@@ -0,0 +1,89 @@ |
| 1 | +# Actions workflows |
| 2 | + |
| 3 | +Read-only access to the workflow files in a repo plus the |
| 4 | +`workflow_dispatch` trigger that the HTML "Run workflow" button |
| 5 | +fires. Workflow runs themselves live at the |
| 6 | +[Actions workflow runs](./actions-runs.md) surface. |
| 7 | + |
| 8 | +Scopes: |
| 9 | + |
| 10 | +- `repo:read` on the list / single-workflow GETs |
| 11 | +- `repo:write` on the dispatch POST (same trust boundary as the |
| 12 | + HTML UI, where any pusher can run a workflow) |
| 13 | + |
| 14 | +## Endpoints |
| 15 | + |
| 16 | +``` |
| 17 | +GET /api/v1/repos/{owner}/{repo}/actions/workflows list discovered workflows |
| 18 | +GET /api/v1/repos/{owner}/{repo}/actions/workflows/{id_or_file} single workflow |
| 19 | +POST /api/v1/repos/{owner}/{repo}/actions/workflows/{file}/dispatches workflow_dispatch |
| 20 | +``` |
| 21 | + |
| 22 | +## Discovery model |
| 23 | + |
| 24 | +shithub does not keep a `workflows` table. The list endpoint walks |
| 25 | +`.shithub/workflows/` in the git tree at the repo's default-branch |
| 26 | +HEAD (or `?ref=` when provided) and parses each `*.yml`/`*.yaml` |
| 27 | +file to extract its `name:`. Files that fail to parse still appear |
| 28 | +in the listing — with their basename as the name — so the listing |
| 29 | +reflects ground truth rather than silently dropping broken |
| 30 | +workflows. |
| 31 | + |
| 32 | +The `id` field on each entry is a deterministic 64-bit hash of the |
| 33 | +file path so gh-shaped clients that pass `workflow_id` still work: |
| 34 | +both `ci.yml` (basename), `.shithub/workflows/ci.yml` (full path), |
| 35 | +and the numeric id are accepted on the single-workflow GET. The |
| 36 | +dispatch endpoint accepts only the basename or full path (the |
| 37 | +numeric id is rejected with 400 to keep dispatch a single |
| 38 | +round-trip; clients with only an id should hit the list first to |
| 39 | +resolve the path). |
| 40 | + |
| 41 | +## List response |
| 42 | + |
| 43 | +```json |
| 44 | +[ |
| 45 | + { |
| 46 | + "id": 7142839129843290000, |
| 47 | + "name": "CI", |
| 48 | + "path": ".shithub/workflows/ci.yml", |
| 49 | + "file": "ci.yml", |
| 50 | + "state": "active" |
| 51 | + } |
| 52 | +] |
| 53 | +``` |
| 54 | + |
| 55 | +`state` is always `"active"` for now — enable/disable knobs land |
| 56 | +in a follow-up PR alongside a `workflow_disabled` table. |
| 57 | + |
| 58 | +## Dispatch request |
| 59 | + |
| 60 | +``` |
| 61 | +POST /api/v1/repos/alice/demo/actions/workflows/ci.yml/dispatches |
| 62 | +Content-Type: application/json |
| 63 | + |
| 64 | +{ |
| 65 | + "ref": "trunk", |
| 66 | + "inputs": { "env": "prod", "debug": "true" } |
| 67 | +} |
| 68 | +``` |
| 69 | + |
| 70 | +- `ref` defaults to the repo's default branch when omitted. |
| 71 | +- `inputs` must match the workflow's declared |
| 72 | + `on.workflow_dispatch.inputs`: |
| 73 | + - Unknown inputs → `400 invalid_request`. |
| 74 | + - Required (non-boolean) inputs missing → 400. |
| 75 | + - Booleans must be `"true"` / `"false"` strings. |
| 76 | + - Choices must match one of the declared options. |
| 77 | +- A workflow without `on.workflow_dispatch` returns 400. |
| 78 | +- Successful dispatch returns `204 No Content`. The run shows up in |
| 79 | + the [actions runs](./actions-runs.md) feed shortly after the |
| 80 | + trigger pipeline enqueues it. |
| 81 | + |
| 82 | +## Errors |
| 83 | + |
| 84 | +| Status | Cause | |
| 85 | +|------:|--------------------------------------------------------------| |
| 86 | +| 400 | Workflow has no `on.workflow_dispatch`, or inputs malformed. | |
| 87 | +| 400 | Numeric id passed to dispatch (use basename or full path). | |
| 88 | +| 403 | PAT lacks `repo:write`. | |
| 89 | +| 404 | Repo, ref, or workflow file unknown. | |