Builder v2: local project browser + multi-team ASC keys

Rewrites the builder console to browse local Xcode projects instead of
accepting source uploads or git URLs. Replaces the devices page with a
profiles page that manages ad-hoc provisioning profiles and lists
registered bundle IDs per team.

Adds multi-account support: ASC API keys are now stored in an asc_keys
table keyed by team_id (team_name, key_id, issuer_id, p8_filename). At
build time, the worker reads DEVELOPMENT_TEAM from the Xcode project and
auto-picks the matching key for fastlane sigh + JWT signing. Legacy
single-key settings auto-migrate on first boot.

Fixes storefront IPA parser to handle binary plists produced by Xcode.
Drops the enrollment bridge, device management routes, and direct
ASC API client -- fastlane sigh handles profile lifecycle now.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Trey T
2026-04-16 14:43:16 -05:00
parent 8dbe87da2e
commit 491f3a22ba
24 changed files with 4006 additions and 826 deletions

View File

@@ -40,14 +40,14 @@ async function loadJobs() {
container.innerHTML = `
<table class="table">
<thead>
<tr><th>Status</th><th>Bundle</th><th>Source</th><th>Started</th><th>Duration</th><th></th></tr>
<tr><th>Status</th><th>Bundle</th><th>Project</th><th>Started</th><th>Duration</th><th></th></tr>
</thead>
<tbody>
${jobs.map(j => `
<tr data-id="${j.id}" style="cursor:pointer">
<td>${statusBadge(j.status)}</td>
<td class="mono">${esc(j.bundle_id) || '<span style="color:var(--text-muted)">—</span>'}</td>
<td class="mono">${esc(j.source_kind)}: ${esc((j.source_ref || '').slice(0, 40))}</td>
<td class="mono">${esc((j.source_ref || '').replace(/\.(xcodeproj|xcworkspace)$/, ''))}</td>
<td class="mono">${esc(formatDate(j.started_at))}</td>
<td class="mono">${esc(duration(j.started_at, j.finished_at))}</td>
<td>${j.install_url ? `<a href="${esc(j.install_url)}" class="btn-sm" style="background:var(--accent);color:white;padding:5px 12px;border-radius:14px;text-decoration:none;font-size:12px">Install</a>` : ''}</td>
@@ -72,7 +72,7 @@ async function openJob(id) {
$('#detail-meta').innerHTML = `
<div>bundle: ${esc(job.bundle_id || '—')}</div>
<div>scheme: ${esc(job.scheme || '—')}</div>
<div>source: ${esc(job.source_kind)} ${esc(job.source_ref || '')}</div>
<div>project: ${esc(job.source_ref || job.project_path || '--')}</div>
<div>started: ${esc(formatDate(job.started_at))} · finished: ${esc(formatDate(job.finished_at))}</div>
${job.install_url ? `<div>install: <a href="${esc(job.install_url)}" style="color:var(--accent)">${esc(job.install_url.slice(0, 80))}…</a></div>` : ''}
${job.error ? `<div style="color:var(--danger)">error: ${esc(job.error)}</div>` : ''}