Skip to content

g1t/apps/docs/src/content/docs/guides/actions.md

346 lines17,521 bytesCodeBlame
1---
2title: GitHub Actions
3description: Your GitHub Actions workflows run on g1t as they are. Rename .github to .g1t and push.
4---
5
6g1t runs GitHub Actions workflows. They are written exactly as on GitHub,
7and kept in `.g1t/workflows/` instead of `.github/workflows/`.
8
9## Moving from GitHub
10
11```sh
12git mv .github .g1t
13git commit -m "Run our workflows on g1t"
14git push g1t main
15```
16
17That is the whole move. Everything in the folder comes along: workflows,
18local actions under `.g1t/actions/` and anything else you keep there.
19Workflows that still say `uses: ./.github/actions/setup` find it under
20`.g1t/` once `.github` is gone.
21
22g1t never reads `.github`. A repository mirrored to both places can keep
23`.github` for GitHub and `.g1t` for g1t, side by side.
24
25Then add your [secrets and variables](#secrets-and-variables): GitHub never
26gives their values out, so they cannot be copied across.
27
28## What runs
29
30| On GitHub | On g1t |
31| --- | --- |
32| `on:` `push` (branches, tags, paths), `pull_request`, `pull_request_target`, `issues`, `issue_comment`, `pull_request_review`, `schedule`, `workflow_dispatch`, `workflow_run`, `merge_group` | The same, from g1t's own pushes, pull requests, issues, comments and [merge queue](/guides/merge-queue/). |
33| `jobs`, `needs`, `if`, `outputs`, `env`, `defaults`, `timeout-minutes`, `continue-on-error` | The same. |
34| `strategy.matrix` with `include` and `exclude`, `fail-fast`, `max-parallel`, a matrix from `fromJSON(needs.…)` | The same. |
35| `concurrency` with `cancel-in-progress` | The same. |
36| `${{ }}` expressions: every operator, function and context | The same, including `hashFiles`, `success()`, `failure()`, `always()` and `cancelled()`. |
37| `run:` with `bash`, `sh`, `python` or a custom shell | The same. |
38| JavaScript actions (`uses: owner/repo@v7`) | Fetched from GitHub and run as they are, on Node 24, the runtime current actions declare. |
39| Composite actions | The same. |
40| Reusable workflows in the repository (`jobs.<id>.uses: ./.g1t/workflows/build.yml`) | The same: `with:` inputs, `on.workflow_call` outputs, and nesting up to four deep. `./.github/workflows/…` finds the workflow under `.g1t/` after the move. Their jobs read the repository's secrets and variables. |
41| `actions/checkout` | Checks out from g1t, with `ref`, `fetch-depth`, `path`, `repository`, `token` and `submodules`. |
42| `GITHUB_OUTPUT`, `GITHUB_ENV`, `GITHUB_PATH`, `GITHUB_STATE`, `GITHUB_STEP_SUMMARY` | The same. |
43| `::error::`, `::warning::`, `::notice::`, `::group::`, `::add-mask::` | The same: errors and warnings become annotations on the run. |
44| `secrets.*`, `vars.*`, `secrets.GITHUB_TOKEN` | The same. `secrets.G1T_TOKEN` is the workspace's own token for the run; `GITHUB_TOKEN` is its alias. |
45| `environment:` on a job | The job reads each key's row for that environment, as GitHub's environment secrets work. |
46| `actions/upload-artifact`, `actions/download-artifact` | Kept with the run for 14 days, passed between its jobs, and downloadable from the run's page. Up to 60 MB each. |
47| `actions/cache`, `actions/cache/restore`, `actions/cache/save` | Kept per repository, found by `key` or the newest under a `restore-keys` prefix. `path` takes globs and `!` exclusions. Up to 2 GiB each; see [the cache](#the-cache). |
48
49The **Actions** page of a workflow says, under *How this runs on g1t*,
50anything in it that runs differently.
51
52### Not yet
53
54- **Windows and macOS on g1t's machines.** g1t's own runners are Linux; a
55 job with `runs-on: windows-latest` or `macos-latest` fails, and says so.
56 [Self-hosted runners](/guides/self-hosted-runners/) of any OS run them:
57 `runs-on: [self-hosted, windows]`.
58- **Docker** container actions, `services:` containers and `container:` on
59 g1t's machines. A job's `container:` is ignored there and its steps run on
60 g1t's image; a [self-hosted runner](/guides/self-hosted-runners/#what-a-job-gets)
61 that runs jobs in Docker uses it.
62- **Reusable workflows from other repositories** (`uses: owner/repo/.github/workflows/x.yml@v1`); ones in the same repository work.
63- **The toolkit's own cache.** Actions that cache through GitHub's service
64 themselves, such as `actions/setup-node` with `cache: npm`, run without
65 it. Use `actions/cache` for the same effect.
66- **Environments' protection rules** (required reviewers, wait timers,
67 branch limits). A job with `environment:` gets that environment's
68 [values](/guides/secrets-and-variables/#a-value-per-environment), and runs
69 without waiting.
70
71Why each of these is missing, and what to use instead, is on
72[What g1t can't do yet](/about/limitations/#actions-and-runners).
73
74## The runner
75
76Jobs run in a fresh sandbox each: Debian with Node 24, Python 3, Go, Rust,
77`build-essential`, `git`, `curl`, `jq` and passwordless `sudo`, in GitHub's
78layout (`/home/runner/work`, `RUNNER_TEMP`, `RUNNER_TOOL_CACHE`).
79`runner.os` is `Linux`. `ubuntu-latest`, `ubuntu-24.04` and other Linux
80labels all run here. A job whose `runs-on` names `self-hosted` waits for one
81of your [self-hosted runners](/guides/self-hosted-runners/) instead. Setup actions such as
82`actions/setup-node` and `actions/setup-python` install other versions as
83they do on GitHub.
84
85### Machine sizes
86
87A job runs on the standard machine unless its `runs-on` names a larger
88one:
89
90| `runs-on` | vCPUs | Memory | Disk |
91| --- | --- | --- | --- |
92| `ubuntu-latest`, or any other Linux label | 0.5 | 4 GiB | 8 GB |
93| `g1t-2core` | 2 | 8 GiB | 16 GB |
94| `g1t-4core` | 4 | 12 GiB | 20 GB |
95
96```yaml
97jobs:
98 build:
99 runs-on: g1t-4core
100```
101
102The label can come from the matrix or the run's inputs
103(`runs-on: ${{ matrix.big && 'g1t-4core' || 'ubuntu-latest' }}`). A
104larger machine costs what it costs g1t, plus the same margin as all
105sandbox time: see [usage and billing](/guides/usage-and-billing/#workflow-jobs-on-larger-machines).
106Builds that compile, such as Rust or a large TypeScript project, finish
107several times faster on one.
108
109A job on g1t's machines runs for at most 60 minutes, whatever its
110`timeout-minutes`; one on a self-hosted runner can run for up to 24 hours.
111A job stopped at its time cap fails saying so.
112
113### What a job can reach
114
115A job's network is restricted, as an agent's is (see
116[guardrails](/guides/guardrails/)): it reaches the hosts its project's
117guardrails allow, g1t itself, and what builds need, and nothing else.
118What builds need is the package registries (npm, PyPI, crates.io, the Go
119proxy, RubyGems, Packagist, NuGet, Maven and Gradle, Debian's mirrors),
120GitHub, where `uses:` actions and the setup actions' downloads come from,
121and the toolchains' download sites (`nodejs.org`, `go.dev`,
122`static.rust-lang.org`). A request anywhere else gets `403` with
123the reason. To reach another host, someone with the Maintain [role](/guides/access-and-roles/) or
124higher adds it to the project's
125allowed domains under **Settings → Guardrails**; a project whose guardrails
126turn the network restriction off runs its jobs with an open network.
127
128A host only workflows should reach, such as the API a deploy uploads to,
129goes in **Workflow-only domains** instead, limited to the workflows and
130environments that need it: `api.cloudflare.com | deploy.yml | production`
131lets only `deploy.yml`'s jobs with `environment: production` reach it.
132Agents never reach those hosts, and neither do runs of pull requests from
133forks. See [workflow-only domains](/guides/guardrails/#workflow-only-domains).
134
135g1t does not run cryptocurrency miners: a step that names one (`xmrig`,
136a `stratum+tcp://` pool, `--donate-level`) is not run, and a job that
137looks like it is mining is stopped. See
138[abuse and mining](/guides/guardrails/#abuse-and-mining).
139
140## The cache
141
142`actions/cache` keeps what a job saves for the repository's later jobs:
143
144| | |
145| --- | --- |
146| One entry | Up to 2 GiB, compressed. A larger one is not saved, and the job goes on. |
147| A repository's entries | Up to 10 GiB together. Saving past it removes the entries restored longest ago. |
148| How long | Until it has not been restored for 7 days, and at most 28 days after it was saved. |
149| Keys | Written once: saving under a key that exists does nothing. A restore finds its `key` exactly, else the newest entry whose key starts with one of its `restore-keys`. |
150| `path` | Files and folders; globs, `**` included; `~/` is the home folder; a line starting with `!` leaves matching paths out. |
151| Compression | zstd. |
152
153```yaml
154- uses: actions/cache@v4
155 with:
156 path: |
157 ~/.cargo/registry/cache
158 target/release
159 !target/**/incremental
160 key: cargo-${{ runner.os }}-${{ hashFiles('Cargo.lock') }}
161 restore-keys: cargo-${{ runner.os }}-
162```
163
164Each restore and save says on the job's log how large the entry was and
165how long it took. A workspace on the plan pays for what its caches hold
166(`Actions cache storage` on its statement), at R2's price plus the margin;
167see [usage and billing](/guides/usage-and-billing/#actions-cache).
168
169## Runs and logs
170
171Open a repository's **Actions** page, in its sidebar. Pick a workflow to
172see its runs, run it by hand if it has `workflow_dispatch`, or turn it off
173without touching its file.
174
175A run's page shows its jobs, each job's steps, and their logs as they are
176written. Groups fold, errors and warnings are marked, and secrets are
177replaced with `***`. **Cancel**, **Re-run all jobs** and **Re-run failed
178jobs** do what they say.
179
180## Pull requests
181
182A pull request's workflows run on each new head: when it is opened, when
183a commit is pushed to it, and, for one g1t makes, when g1t
184marks it ready, which on g1t is when it first has code. Each head runs
185each workflow once.
186
187They also start on the activity types `labeled`, `unlabeled`,
188`milestoned`, `demilestoned`, `assigned`, `review_requested` and
189`closed`, and `edited` when the branch a pull request merges into
190changes; `issues` workflows on `labeled`, `unlabeled`, `milestoned` and
191`demilestoned` too. List them under `types:` to run on them. For
192`labeled` and `unlabeled`, `github.event.label` names the label. A pull
193request's `branches` filter, `github.base_ref` and
194`pull_request.base.ref` are the branch it merges into, which is not
195always the default branch: see
196[pull requests into other branches](/guides/base-branches/).
197
198`github.event.pull_request` reads as it does on GitHub. For a pull request
199g1t made, `pull_request.user` is g1t (`login` `g1t`, `type` `Bot`), and
200`pull_request.requested_by` names the person who asked for it; it is `null`
201on anyone else's. `github.event.issue.requested_by` does the same for an
202issue g1t's agent filed. `sender` is whoever caused the event.
203
204## Checks
205
206A pull request's checks are its workflows. Each workflow that runs on
207`pull_request` runs on every pull request's head, whoever opened it, a
208person or an agent, and its runs report a check named after the workflow:
209a workflow with `name: CI` reports `CI`, with the status context
210`CI / pull_request` (the workflow's name and the event).
211
212- **Which checks a merge needs** is up to the [rules](/guides/rules/) of the branch it merges into,
213 their [required status checks](/guides/pull-requests/#required-status-checks),
214 under **Settings → Rules**. A required check that failed,
215 is still running or has not reported holds the merge. Checks that are not
216 required are shown on the pull request and never hold it.
217- **In a repository that merges through the [merge queue](/guides/merge-queue/)**,
218 workflows with `on: merge_group` run on each combined state the queue
219 builds, on the branch `g1t-queue/<entry>`, and the state lands only if
220 they and every required check pass on it. A workflow behind a required
221 check needs `merge_group` in its `on:`.
222- **A pull request g1t is working on** goes back to g1t when
223 a check fails, with the end of each failed job's log. The agent reads the
224 run and its logs with the same tools you have, fixes the cause, and
225 pushes; the workflows run again. See
226 [seeing it through](/guides/working-with-g1t/#seeing-it-through).
227
228```yaml
229name: CI
230
231on:
232 pull_request:
233 push:
234 branches: [main]
235 merge_group:
236```
237
238### Add CI
239
240A repository with no workflows has nothing that proves a change works, for
241people or for agents. Its pull requests, its **Branches and merging**
242settings and its **Actions** page say **This repository has no checks**,
243with an **Add CI** button. Anyone who can push to the repository can use it:
244
2451. Choose **Add CI**. g1t looks at the files at the repository's root and
246 writes a starter workflow with a job for each stack it finds, up to
247 three: Node (npm, pnpm, Yarn or Bun), Rust, Go, Python (pip or uv), Ruby,
248 Java (Maven or Gradle), .NET, or Make. Each job installs, lints where
249 the project says how, builds and tests. When it finds none, the job is a
250 placeholder that fails until you replace its last step with your own
251 commands.
2522. The workflow is committed as `.g1t/workflows/ci.yml` on a new branch,
253 `add-ci`, and opened as a pull request, by you. It is named `CI` and runs
254 on `pull_request`, on `push` to the default branch, and on `merge_group`.
2553. Change it on the pull request if the steps are not how your project
256 builds, and merge it.
2574. Once it has run, `CI` is offered under **Require status checks to pass
258 before merging**.
259 Require it, so that nothing merges into the default branch unless it
260 passes.
261
262## Secrets and variables
263
264Secrets are read as `${{ secrets.KEY }}` and config as `${{ vars.KEY }}`,
265from the rows under **Settings → Secrets and variables** that are
266available to Workflows. A job with `environment: production` reads each
267key's Production row; other jobs read the rows for all environments. See
268[Secrets and variables](/guides/secrets-and-variables/) for how rows,
269environments and the workspace's rows work.
270
271Every trusted job also gets `${{ secrets.G1T_TOKEN }}`, the workspace's own
272token for the run, with `GITHUB_TOKEN` as its alias. A pull request's runs
273get secrets and the token only when its author has the Write
274[role](/guides/access-and-roles/) or higher on the repository, a member or
275an outside collaborator, or is g1t working on its own. For a pull request
276g1t made, its author is g1t and the person who asked for it is the one
277whose role counts. Anyone else's, such as one
278from a fork or by someone with Read or Triage, runs without secrets and
279with an empty token. See
280[who gets secrets](/guides/secrets-and-variables/#who-gets-secrets).
281
282## Who may run workflows
283
284What you can do with a repository's workflows follows your
285[role](/guides/access-and-roles/) on it:
286
287| | Needs |
288| --- | --- |
289| See workflows, runs and their logs | Read: on a public repository, anyone |
290| Run a workflow by hand, cancel or re-run a run | Write |
291| Enable or disable a workflow | Maintain |
292| The repository's secrets and variables, seeing them included | Admin |
293
294Jobs run in g1t's sandboxes, so they need the
295[g1t plan](/guides/usage-and-billing/#the-g1t-plan) or
296[the trial](/guides/usage-and-billing/#the-trial); jobs on
297[self-hosted runners](/guides/self-hosted-runners/#billing) need neither.
298On a public repository,
299[g1t's open-source pool](/guides/usage-and-billing/#the-open-source-pool)
300runs them too, after a card check, until the month's pool is spent.
301
302Before each job starts, g1t reserves what it may cost (its time limit at
303the sandbox price) with billing, and settles what it really cost when it
304ends; each job's sandbox is charged as
305[sandbox time](/guides/usage-and-billing/#sandbox-time), from the first
306second. A job billing refuses does not start: it is recorded as failed
307with "Not started:" and the reason, such as "Workflows run in g1t's
308sandboxes, which cost real money, so they need the g1t plan ($20 a month)
309or a card check", and what to do about it. The
310Actions page tells people with Write on a repository whose workspace
311cannot run jobs before the first run.
312
313## From the API
314
315The routes follow the standard Actions REST shape, so existing scripts
316usually work once they point at `https://api.g1t.sh`.
317
318| `workflow` action | Route |
319| --- | --- |
320| `list` | `GET /repos/{owner}/{repo}/actions/workflows` |
321| `list_runs` | `GET /repos/{owner}/{repo}/actions/runs`, with `workflow`, `branch`, `event`, `pull`, `head_sha` |
322| `get_run` | `GET /repos/{owner}/{repo}/actions/runs/{id}` |
323| `job_logs` | `GET /repos/{owner}/{repo}/actions/jobs/{job}/logs?after=` |
324| `dispatch` | `POST /repos/{owner}/{repo}/actions/workflows/{workflow}/dispatches` with `ref` and `inputs` |
325| `cancel` | `POST /repos/{owner}/{repo}/actions/runs/{id}/cancel` |
326| `rerun` | `POST …/runs/{id}/rerun`, or `…/rerun-failed-jobs` |
327| `update` | `PUT …/workflows/{workflow}/enable` and `…/disable` |
328
329Secrets and variables have a tool of their own, `secret`:
330
331| `secret` action | Route |
332| --- | --- |
333| `list_secrets`, `set_secret`, `delete_secret` | `GET /repos/{owner}/{repo}/actions/secrets`, `PUT` and `DELETE …/secrets/{name}` |
334| `list_variables`, `set_variable`, `delete_variable` | `GET` and `POST /repos/{owner}/{repo}/actions/variables`, `PATCH` and `DELETE …/variables/{name}` |
335
336Workspace secrets and variables are under
337`/workspaces/{workspace}/actions/secrets` and `…/variables`. The fields
338g1t adds (environments, who reads a row, linked repositories) are in
339[Secrets and variables](/guides/secrets-and-variables/#from-the-api).
340
341```sh
342curl -X POST https://api.g1t.sh/repos/acme/web/actions/workflows/ci.yml/dispatches \
343 -H "Authorization: Bearer $G1T_TOKEN" -H "Content-Type: application/json" \
344 -d '{"ref": "main", "inputs": {"environment": "staging"}}'
345```
346