markdown · 4264 bytes Raw Blame History

Actions runner smoke runbook

This runbook validates the runner-facing Actions path. shithubd-runner now claims jobs and executes containerized run: steps through Docker or Podman. The curl flow below remains useful for token/replay debugging.

For host provisioning and the systemd/Ansible path, see runner-deploy.md.

Prereqs:

  • Database migrations are current through 0053_runner_jwt_used.sql.
  • SHITHUB_TOTP_KEY or auth.totp_key_b64 is set on the web process.
  • Object storage is configured if testing artifact upload.
  • Docker or Podman is installed on the runner host.
  • A repo has a workflow under .shithub/workflows/*.yml with runs-on: ubuntu-latest, and a push/dispatch has enqueued a run. S41d PR1 supports run: steps; checkout and artifact aliases land in the following S41d slices.

runs-on is a runner-label selector, not a hard-coded image name. A workflow that says runs-on: ubuntu-latest can be claimed by any runner advertising the ubuntu-latest label. The container image is selected by the runner host's engine.default_image setting; the reproducible Nix-built image is the default, but operators can point it at another OCI image when they need closer Ubuntu parity.

Register a runner:

shithubd admin runner register \
  --name runner-1 \
  --labels self-hosted,linux,ubuntu-latest \
  --capacity 1

Save the printed token:

export RUNNER_TOKEN='<printed-token>'
export BASE='https://shithub.example'

Run the binary:

shithubd-runner run \
  --server-url "$BASE" \
  --token "$RUNNER_TOKEN" \
  --labels self-hosted,linux,ubuntu-latest \
  --workspace-root /var/lib/shithubd-runner/workspaces

Equivalent config file:

[server]
base_url = "https://shithub.example"

[runner]
token = "<printed-token>"
labels = ["self-hosted", "linux", "ubuntu-latest"]
capacity = 1
poll_interval = "5s"
workspace_root = "/var/lib/shithubd-runner/workspaces"
workspace_ttl = "24h"
network_allowlist = [
  "api.github.com",
  "auth.docker.io",
  "codeload.github.com",
  "github.com",
  "objects.githubusercontent.com",
  "production.cloudflare.docker.com",
  "registry-1.docker.io",
  "*.githubusercontent.com",
]

[engine]
kind = "docker"
default_image = "ghcr.io/shithub/runner-nix:1.0"
network = "bridge"
memory = "2g"
cpus = "2"
seccomp_profile = "/etc/shithubd-runner/seccomp.json"
user = "65534:65534"
pids_limit = 512
dns_servers = []

The config path defaults to /etc/shithubd-runner/config.toml. Environment variables use the SHITHUB_RUNNER_ prefix, for example SHITHUB_RUNNER_TOKEN or SHITHUB_RUNNER_SERVER__BASE_URL.

Curl token smoke

Claim a job:

curl -fsS "$BASE/api/v1/runners/heartbeat" \
  -H "Authorization: Bearer $RUNNER_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"labels":["self-hosted","linux","ubuntu-latest"],"capacity":1}' \
  | tee /tmp/shithub-claim.json

Extract the job token and id:

export JOB_ID="$(jq -r '.job.id' /tmp/shithub-claim.json)"
export JOB_TOKEN="$(jq -r '.token' /tmp/shithub-claim.json)"

Append a log chunk:

curl -fsS "$BASE/api/v1/jobs/$JOB_ID/logs" \
  -H "Authorization: Bearer $JOB_TOKEN" \
  -H "Content-Type: application/json" \
  -d "{\"seq\":0,\"chunk\":\"$(printf 'hello from curl\n' | base64)\"}" \
  | tee /tmp/shithub-log.json

export JOB_TOKEN="$(jq -r '.next_token' /tmp/shithub-log.json)"

Complete the job:

curl -fsS "$BASE/api/v1/jobs/$JOB_ID/status" \
  -H "Authorization: Bearer $JOB_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"status":"completed","conclusion":"success"}'

Replay check: reusing the log token after the log call must fail with 401 because its jti is already present in runner_jwt_used.

curl -i "$BASE/api/v1/jobs/$JOB_ID/status" \
  -H "Authorization: Bearer $(jq -r '.next_token' /tmp/shithub-log.json)" \
  -H "Content-Type: application/json" \
  -d '{"status":"running"}'

Expected results:

  • workflow_jobs.status = completed and conclusion success.
  • The parent workflow_runs row rolls up to completed/success when all jobs are terminal.
  • The PR Checks tab shows the matching check run as success.
  • /metrics includes runner registration, heartbeat, and JWT counters.
View source
1 # Actions runner smoke runbook
2
3 This runbook validates the runner-facing Actions path. `shithubd-runner`
4 now claims jobs and executes containerized `run:` steps through Docker or
5 Podman. The curl flow below remains useful for token/replay debugging.
6
7 For host provisioning and the systemd/Ansible path, see
8 [runner-deploy.md](./runner-deploy.md).
9
10 Prereqs:
11
12 - Database migrations are current through `0053_runner_jwt_used.sql`.
13 - `SHITHUB_TOTP_KEY` or `auth.totp_key_b64` is set on the web process.
14 - Object storage is configured if testing artifact upload.
15 - Docker or Podman is installed on the runner host.
16 - A repo has a workflow under `.shithub/workflows/*.yml` with
17 `runs-on: ubuntu-latest`, and a push/dispatch has enqueued a run.
18 S41d PR1 supports `run:` steps; checkout and artifact aliases land in
19 the following S41d slices.
20
21 `runs-on` is a runner-label selector, not a hard-coded image name.
22 A workflow that says `runs-on: ubuntu-latest` can be claimed by any
23 runner advertising the `ubuntu-latest` label. The container image is
24 selected by the runner host's `engine.default_image` setting; the
25 reproducible Nix-built image is the default, but operators can point it
26 at another OCI image when they need closer Ubuntu parity.
27
28 Register a runner:
29
30 ```sh
31 shithubd admin runner register \
32 --name runner-1 \
33 --labels self-hosted,linux,ubuntu-latest \
34 --capacity 1
35 ```
36
37 Save the printed token:
38
39 ```sh
40 export RUNNER_TOKEN='<printed-token>'
41 export BASE='https://shithub.example'
42 ```
43
44 Run the binary:
45
46 ```sh
47 shithubd-runner run \
48 --server-url "$BASE" \
49 --token "$RUNNER_TOKEN" \
50 --labels self-hosted,linux,ubuntu-latest \
51 --workspace-root /var/lib/shithubd-runner/workspaces
52 ```
53
54 Equivalent config file:
55
56 ```toml
57 [server]
58 base_url = "https://shithub.example"
59
60 [runner]
61 token = "<printed-token>"
62 labels = ["self-hosted", "linux", "ubuntu-latest"]
63 capacity = 1
64 poll_interval = "5s"
65 workspace_root = "/var/lib/shithubd-runner/workspaces"
66 workspace_ttl = "24h"
67 network_allowlist = [
68 "api.github.com",
69 "auth.docker.io",
70 "codeload.github.com",
71 "github.com",
72 "objects.githubusercontent.com",
73 "production.cloudflare.docker.com",
74 "registry-1.docker.io",
75 "*.githubusercontent.com",
76 ]
77
78 [engine]
79 kind = "docker"
80 default_image = "ghcr.io/shithub/runner-nix:1.0"
81 network = "bridge"
82 memory = "2g"
83 cpus = "2"
84 seccomp_profile = "/etc/shithubd-runner/seccomp.json"
85 user = "65534:65534"
86 pids_limit = 512
87 dns_servers = []
88 ```
89
90 The config path defaults to `/etc/shithubd-runner/config.toml`.
91 Environment variables use the `SHITHUB_RUNNER_` prefix, for example
92 `SHITHUB_RUNNER_TOKEN` or `SHITHUB_RUNNER_SERVER__BASE_URL`.
93
94 ## Curl token smoke
95
96 Claim a job:
97
98 ```sh
99 curl -fsS "$BASE/api/v1/runners/heartbeat" \
100 -H "Authorization: Bearer $RUNNER_TOKEN" \
101 -H "Content-Type: application/json" \
102 -d '{"labels":["self-hosted","linux","ubuntu-latest"],"capacity":1}' \
103 | tee /tmp/shithub-claim.json
104 ```
105
106 Extract the job token and id:
107
108 ```sh
109 export JOB_ID="$(jq -r '.job.id' /tmp/shithub-claim.json)"
110 export JOB_TOKEN="$(jq -r '.token' /tmp/shithub-claim.json)"
111 ```
112
113 Append a log chunk:
114
115 ```sh
116 curl -fsS "$BASE/api/v1/jobs/$JOB_ID/logs" \
117 -H "Authorization: Bearer $JOB_TOKEN" \
118 -H "Content-Type: application/json" \
119 -d "{\"seq\":0,\"chunk\":\"$(printf 'hello from curl\n' | base64)\"}" \
120 | tee /tmp/shithub-log.json
121
122 export JOB_TOKEN="$(jq -r '.next_token' /tmp/shithub-log.json)"
123 ```
124
125 Complete the job:
126
127 ```sh
128 curl -fsS "$BASE/api/v1/jobs/$JOB_ID/status" \
129 -H "Authorization: Bearer $JOB_TOKEN" \
130 -H "Content-Type: application/json" \
131 -d '{"status":"completed","conclusion":"success"}'
132 ```
133
134 Replay check: reusing the log token after the log call must fail with
135 401 because its `jti` is already present in `runner_jwt_used`.
136
137 ```sh
138 curl -i "$BASE/api/v1/jobs/$JOB_ID/status" \
139 -H "Authorization: Bearer $(jq -r '.next_token' /tmp/shithub-log.json)" \
140 -H "Content-Type: application/json" \
141 -d '{"status":"running"}'
142 ```
143
144 Expected results:
145
146 - `workflow_jobs.status = completed` and conclusion `success`.
147 - The parent `workflow_runs` row rolls up to completed/success when all
148 jobs are terminal.
149 - The PR Checks tab shows the matching check run as success.
150 - `/metrics` includes runner registration, heartbeat, and JWT counters.