Files
small-scripts/specs/wait-for-version.spec.md
Paul O'Reilly f50561ea78 feat(mm-worktree-task): run from any planner-backed repo — cp_cli dir + context resolution (--context, MM_WORKTREE_TASK_CP_*), empty-docs guard, tests + spec
cp_cli lives in agent-runtimes; previously every planner call ran 'python3 -m lib.cp_cli' from the current repo with no --context, so the script only worked inside agent-runtimes. The context now resolves from ~/.cpconfig by matching project_id to the repo directory name. Verified live from hatchmate (item + parent + documents fetched via the hatchmate context).

Claude-Session: https://claude.ai/code/session_01JgJbWzVJioVznVTFyQFEfx
2026-09-11 16:27:08 +12:00

5.2 KiB

wait-for-version

Purpose

Poll a Gitea repository's Actions runs until the CI workflow for HEAD of the main branch completes successfully, then print the latest release tag. Solves the race condition where a dependency's main branch has been updated but CI has not yet produced a release.

Usage

wait-for-version [OPTIONS] REPO

Arguments

Argument Description
REPO Gitea repo in owner/name form (e.g. skynet/hugo-accelerator).

Flags

Flag Description
-t, --token TOKEN Gitea access token. Fallback order: $GITEA_TOKEN, $GITHUB_TOKEN.
--workflow NAME Name of the CI workflow to wait for (default: version). Matched against the name field of each workflow run.
--timeout SECONDS Max total wait time (default: 300). 0 means poll once then give up.
--interval SECONDS Polling interval (default: 15; min: 1).
--server URL Gitea base URL. Fallback order: $GITHUB_SERVER_URL, https://gitea.oreillyit.nz.
-v, --verbose Emit polling progress to stderr.
-n, --dryrun Print resolved configuration and exit; make no API calls.
-h, --help Show usage.

Behaviour

  1. Validate inputs: REPO must be owner/name; --timeout is a non-negative integer; --interval is an integer ≥ 1.
  2. Compute API_BASE = <server>/api/v1/repos/<owner>/<name>.
  3. Compute DEADLINE = now + timeout.
  4. Resolve HEAD SHA: GET <API_BASE>/branches/main. Extract .commit.id. On HTTP 401/403 exit 3; on HTTP 404 exit 3; on any other failure exit 3.
  5. Emit Waiting for '<workflow>' CI on <repo> @ <sha[0:8]>... to stderr.
  6. Loop:
    1. GET <API_BASE>/actions/runs?head_sha=<SHA>&limit=50. Add Authorization: token <TOKEN> when a token is available.
    2. From the JSON response, filter .workflow_runs[] where .name == <workflow>. Take the first (index 0) result.
    3. If no matching run: treat as pending, continue polling.
    4. If status == "completed" and conclusion == "success": fetch GET <API_BASE>/releases/latest, extract .tag_name, print to stdout, exit 0.
    5. If status == "completed" and conclusion != "success": print error to stderr (include conclusion value), exit 1.
    6. Otherwise (queued / in_progress / waiting / etc.): continue polling.
    7. On HTTP 401 or 403: print auth error to stderr, exit 3.
    8. On HTTP 404: print not-found error to stderr, exit 3.
    9. On any other HTTP code or network error: keep retrying (log to stderr when --verbose).
  7. Between polls, sleep for --interval seconds, capped so sleep never overshoots the deadline.
  8. When the deadline passes without success, print timeout error to stderr, exit 1.

Dryrun Behaviour

When --dryrun is passed, no HTTP requests are made. Output (to stdout):

[dryrun] Server: https://gitea.oreillyit.nz
[dryrun] Repo: skynet/hugo-accelerator
[dryrun] Workflow: version
[dryrun] Timeout: 300s
[dryrun] Interval: 15s
[dryrun] Auth: yes
[dryrun] Actions API: https://gitea.oreillyit.nz/api/v1/repos/skynet/hugo-accelerator/actions/runs

Auth: yes when a token was supplied or present in the environment; otherwise Auth: no.

Exit Codes

Code Meaning
0 CI succeeded; latest release tag printed to stdout.
1 Timeout elapsed, CI concluded with a non-success result, or no release tag found.
2 Usage error (missing / invalid arguments).
3 Terminal API error (401, 403, 404, or failure resolving HEAD SHA).

Edge Cases

Case Handling
REPO missing the slash Error, exit 2.
No matching workflow run found Treated as pending; continue polling until timeout.
CI run fails (non-success conclusion) Print error including conclusion value, exit 1.
Latest release has no tag_name Error to stderr, exit 1.
No token for a private repo API returns 401/403 → exit 3.
Invalid token HTTP 401 → exit 3.
Repo doesn't exist HTTP 404 → exit 3.
Transient 5xx or network error Retry each --interval seconds until timeout.
--timeout 0 Resolve SHA, poll once, exit 1 if not yet complete.
--interval 0 Error, exit 2.

Examples

# Wait for the version CI workflow to complete on HEAD of main
wait-for-version skynet/hugo-accelerator

# Capture version and update a consumer project
VERSION=$(wait-for-version skynet/hugo-accelerator)
sed -i "s/hugo-accelerator: .*/hugo-accelerator: $VERSION/" config.yaml

# With explicit token for a private repo
wait-for-version -t "$MY_TOKEN" skynet/private-lib

# Longer timeout for slow CI (10 minutes)
wait-for-version --timeout 600 skynet/hugo-accelerator

# Verbose polling progress
wait-for-version -v skynet/hugo-accelerator

# Custom workflow name
wait-for-version --workflow release skynet/hugo-accelerator

# Dryrun preview
wait-for-version --dryrun skynet/hugo-accelerator

In a Claude Code session

Invoke via the Bash tool with run_in_background=true. The session is notified when the script exits. On success stdout holds the new version tag.

VERSION=$(wait-for-version skynet/hugo-accelerator) || exit 1
echo "Dependency released as $VERSION"