Phase 4-5: build pipeline + device enrollment
Builder service (Mac mini): - Build worker: xcodebuild archive + export + fastlane signing + upload to unraid - /api/build/upload (source archive) and /api/build/git (clone) ingest paths - SSE-streamed build logs, builds list UI, live status updates - /api/devices/from-enrollment bridge endpoint (shared-secret auth) Storefront (unraid): - /enroll/ public flow: landing page, mobileconfig generator, callback parser - Forwards extracted UDIDs to the Mac mini builder for ASC registration - docker-compose.yml now passes BUILDER_URL and BUILDER_SHARED_SECRET Updated CLAUDE.md with full architecture, deploy flow, and gotchas.
This commit is contained in:
51
builder/public/js/build.js
Normal file
51
builder/public/js/build.js
Normal file
@@ -0,0 +1,51 @@
|
||||
const $ = (s) => document.querySelector(s);
|
||||
function toast(msg, kind = '') {
|
||||
const t = $('#toast');
|
||||
t.textContent = msg;
|
||||
t.className = 'toast show ' + kind;
|
||||
setTimeout(() => t.classList.remove('show'), 3500);
|
||||
}
|
||||
|
||||
$('#upload-form').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
const fd = new FormData(e.target);
|
||||
const btn = e.target.querySelector('button[type=submit]');
|
||||
btn.disabled = true;
|
||||
btn.textContent = 'Uploading…';
|
||||
try {
|
||||
const r = await fetch('/api/build/upload', { method: 'POST', body: fd });
|
||||
const data = await r.json();
|
||||
if (!r.ok) throw new Error(data.error || 'Upload failed');
|
||||
location.href = `/builds#${data.job_id}`;
|
||||
} catch (err) {
|
||||
toast(err.message, 'error');
|
||||
btn.disabled = false;
|
||||
btn.textContent = 'Queue Build';
|
||||
}
|
||||
});
|
||||
|
||||
$('#git-form').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
const body = {
|
||||
url: e.target.url.value.trim(),
|
||||
branch: e.target.branch.value.trim() || null,
|
||||
scheme: e.target.scheme.value.trim() || null,
|
||||
};
|
||||
const btn = e.target.querySelector('button[type=submit]');
|
||||
btn.disabled = true;
|
||||
btn.textContent = 'Cloning…';
|
||||
try {
|
||||
const r = await fetch('/api/build/git', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
const data = await r.json();
|
||||
if (!r.ok) throw new Error(data.error || 'Clone failed');
|
||||
location.href = `/builds#${data.job_id}`;
|
||||
} catch (err) {
|
||||
toast(err.message, 'error');
|
||||
btn.disabled = false;
|
||||
btn.textContent = 'Queue Build';
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user