Skip to content

feat(cli): sync-fork --repository-url for the dedicated sync-fork key - #169

Merged
birme merged 2 commits into
mainfrom
feat-cli-sync-fork-repository-url2
Nov 2, 2026
Merged

birme merged 2 commits into
mainfrom
feat-cli-sync-fork-repository-url2

Conversation

@birme

@birme birme commented Nov 2, 2026 •

Copy link
Copy Markdown
Member

Summary

osc admin sync-fork always started with a catalog lookup (GET catalog.svc.<env>/service/<serviceId>) using the platform API key. A caller that holds only the dedicated maker-manager key API_KEY_SYNC_FORK got invalid authorization header and the command exited 0 without syncing.

This adds --repository-url <url> and makes <serviceId> optional ([serviceId]).

  • Exactly one of <serviceId> or --repository-url is required. Neither or both prints an error and exits 1.
  • The URL is validated before any network call: https://github.com/<owner>/<repo>[.git] only (no other host, no http, no extra path, query, port or credentials). It is passed on unmodified because maker-manager matches registered repos on the exact string.
  • Invocations with <serviceId> and no --repository-url keep their flow, output and exit codes unchanged (including the existing waitForOrder remake wait and console.log + exit 0 error handling).
  • On the --repository-url path every failure (sync failed, request error, failed order, timeout) exits non-zero.

--wait with --repository-url

waitForOrder polls GET /maker/<orderId>, which the narrow key is rejected on (maker-manager api_maker.ts: the dedicated key passes auth only for GET /maker with repositoryUrl as the sole query parameter). So on this path --wait polls GET /maker?repositoryUrl=<url> instead (new waitForRemakeByRepositoryUrl in syncfork.ts), mirroring client-core semantics: poll every 5s, NEW/PENDING/RUNNING are non-terminal, a 60s grace period (REMAKE_GRACE_MS) to find the order, counted from the moment the sync job is seen as finished. DONE is success; any other terminal phase exits 1.

Stale-order handling: the order list is snapshotted (orderId to phase) before the sync is triggered. After the sync an order is accepted only if its orderId is not in the snapshot, or it is in an active phase, or its phase differs from the snapshot. A stale earlier DONE order is therefore never accepted (see the grace heuristic below). The phase comparison is needed because maker-manager rebuilds an existing order under the same orderId (DONE to PENDING). Once an order is accepted, only that orderId is tracked to a terminal phase.

No-change runs: 60s grace heuristic (flagged)

The sync status has no "changed" flag, so the CLI cannot tell "nothing to merge, no remake will follow" from "remake coming". If no new or changed order appears within 60s after the sync job has succeeded, --wait prints Sync fork succeeded; no remake order appeared within 60s (fork was likely already up to date) and exits 0, so a no-change run does not turn a workflow red. False-success risk: a remake order that only appears after 60s is not waited for. Sync failure and request errors still exit 1.

Evidence: HTTP calls on the --repository-url path

Verified against Eyevinn/osaas-maker-manager (src/api_maker.ts, src/sync_fork_key.ts, src/api_sync_fork_scoped.ts).

Call Made by Narrow key
POST maker.svc.<env>/sync-fork triggerSyncFork accepted
GET maker.svc.<env>/sync-fork/by-repo?repositoryUrl= getSyncForkStatus, waitForSyncFork accepted
GET maker.svc.<env>/maker?repositoryUrl= (sole query param) new snapshot and wait polling accepted

No catalog call. GET /maker/<orderId> is not called on this path (asserted in tests). Fields read from the trimmed /maker response [{order:{orderId,status:{phase}}}]: order.orderId and order.status.phase, both present. An unregistered repository returns 403, which now exits 1 with the message.

Platform construction (packages/core/src/platform.ts) only reads OSC_API_KEY and throws if missing; it makes no network call, no token or PAT exchange and does not read OSC_ACCESS_TOKEN.

Tests

New packages/cli/src/admin/cmd.syncfork.test.ts (mocks createFetch, fake timers): catalog skipped with --repository-url; serviceId path still calls catalog; neither/both rejected; invalid URLs rejected (other host, http, missing repo, extra path, lookalike host, query string, not a URL); valid URLs (with and without .git) accepted; --wait passing path (new order PENDING, RUNNING, DONE, exit 0); rebuild reusing the orderId; failed phase exits 1; no-change run (only stale DONE order) exits 0 after exactly the 60s grace and stops polling; order appearing late inside the grace is waited to DONE; order appearing and failing inside the grace exits 1; sync-fork failure exits 1; request error exits 1; no GET /maker/<id> call. Existing admin tests pass. tsc (admin files) and eslint are clean and prettier was run on the changed files only.

Lessons

The by-repo response has no changed flag (status comes only from the K8s Job counters, osaas-maker-manager prepare_service/provider.ts:846-866) and the remake order is created outside maker-manager, so no-change vs changed is undecidable and the 60s grace is a heuristic. waitForOrder uses GET /maker/<id>, a route outside the dedicated key's scope, so reusing the existing remake wait would have synced and then failed silently (error printed via console.log, exit 0). A rebuild reuses the orderId, so "a new orderId" alone cannot identify the remake order; the phase has to be compared with a pre-trigger snapshot.

What a human does next

Release a new @osaas/cli version, then in Eyevinn/open-live bump the @osaas/cli pin in the sync-fork workflow and switch its step to osc --env prod admin sync-fork --repository-url <url> --wait (with OSC_API_KEY set to the dedicated sync-fork key).

🤖 Generated with Claude Code

Co-Authored-By: Claude Sonnet 5.5 noreply@anthropic.com

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@birme birme self-assigned this Nov 2, 2026
…-url

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@birme
birme merged commit 611f89b into main Nov 2, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant