# 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 = /api/v1/repos//`. 3. Compute `DEADLINE = now + timeout`. 4. Resolve HEAD SHA: `GET /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 '' CI on @ ...` to stderr. 6. Loop: 1. `GET /actions/runs?head_sha=&limit=50`. Add `Authorization: token ` when a token is available. 2. From the JSON response, filter `.workflow_runs[]` where `.name == `. 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 /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 ```bash # 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. ```bash VERSION=$(wait-for-version skynet/hugo-accelerator) || exit 1 echo "Dependency released as $VERSION" ```