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
This commit is contained in:
125
specs/wait-for-version.spec.md
Normal file
125
specs/wait-for-version.spec.md
Normal file
@@ -0,0 +1,125 @@
|
||||
# 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
|
||||
|
||||
```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"
|
||||
```
|
||||
Reference in New Issue
Block a user