Skip to content
1,903 linesCodeBlameRaw
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
22Otherwise g1t doesn't read `.github`, so a repository that lives in both
23places can keep `.github` for GitHub and `.g1t` for g1t, side by side.
24
25### On a mirror
26
27A [mirror](/guides/mirroring/) of a GitHub repository runs nothing while it
28stands by: its workflows run on GitHub. Its owners can change that:
29
30- **CI failover** runs its workflows on g1t for each push copied in from
31 GitHub, `.github/workflows` as well as `.g1t/workflows`. Use it when
32 GitHub's workflows aren't running.
33- **Taking over** runs them for everything pushed to g1t, until it's handed
34 back.
35- **Keep CI warm** runs `.g1t/workflows`, and only those, on each push
36 copied in while it stands by.
37
38When both folders have a workflow of the same `name:`, the one in `.g1t`
39runs. A workflow that deploys (any job names an `environment:`) waits for
40approval during CI failover and takeovers, so it doesn't deploy from two
41places, unless the mirror's settings say otherwise. A push copied in from
42GitHub that changes workflows or local actions waits for approval before
43it can use the repository's secrets.
44
45Whenever g1t runs a mirror's workflows, their results show on the commit on
46GitHub too, as commit statuses named `g1t / <workflow> / <event>`, such as
47`g1t / CI / push`. This needs the Commit statuses permission on g1t's GitHub
48App; turn it off with **Report checks to GitHub** in the mirror's settings.
49See [Checks on GitHub](/guides/mirroring/#checks-on-github).
50
51Then add your [secrets and variables](#secrets-and-variables): GitHub never
52gives their values out, so they cannot be copied across.
53
54## What runs
55
56| On GitHub | On g1t |
57| --- | --- |
58| `on:` `push` (branches, tags, paths), `pull_request`, `pull_request_target`, `issues`, `issue_comment`, `pull_request_review`, `schedule`, `workflow_dispatch`, `workflow_run`, `merge_group`, `create`, `delete`, `repository_dispatch`, `release`, `deployment`, `deployment_status` | The same, from g1t's own pushes, pull requests, issues, comments, [releases](#releases), [deployments](#deployments) and [merge queue](/guides/merge-queue/). `create` starts on each new branch or tag, at its commit; `delete` on each branch or tag a push deletes, on the default branch's latest commit, with `github.event.ref` and `github.event.ref_type` naming what went. As on GitHub, neither takes `branches` or `tags` filters: a workflow on them runs for every branch and tag. `repository_dispatch` starts on [a dispatch event](#repository-dispatch). |
59| `jobs`, `needs`, `if`, `outputs`, `env`, `defaults`, `timeout-minutes`, `continue-on-error` | The same. |
60| `timeout-minutes` and `continue-on-error` on a step | The same, for `run:` and `uses:` steps alike. A `uses:` step's action is stopped at its limit, with every process it started; a step inside a composite action stops at its own limit or the `uses:` step's, whichever comes first. A step stopped this way fails, unless `continue-on-error` lets the job go on. |
61| `strategy.matrix` with `include` and `exclude`, `fail-fast`, `max-parallel`, a matrix from `fromJSON(needs.…)` | The same. |
62| `concurrency` with `cancel-in-progress`, for the workflow or for one job | The same: one run, or one job, of a group at a time. |
63| `permissions:` for the workflow or for one job, `read-all`, `write-all` | The same: they decide what [the job's token](#the-jobs-token) may do. |
64| `${{ }}` expressions: every operator, function and context | The same, including `hashFiles`, `success()`, `failure()`, `always()` and `cancelled()`. |
65| `run:` with `bash`, `sh`, `python`, `pwsh` or a custom shell | The same, with GitHub's file for each: `.sh` for `bash` and `sh`, `.py` for `python`, `.ps1` for PowerShell (stopping at the first error, and exiting with the last program's code), `.cmd` for `cmd`, and no extension for another program. `python` runs `python`, or `python3` on a machine without it. |
66| JavaScript actions (`uses: owner/repo@v7`, `owner/repo/path@v7`) | Fetched from that repository on g1t when g1t has it and your repository may use it, otherwise from GitHub, and run as they are, on Node 24, the runtime current actions declare. An action that declares `node20` runs on Node 20 where the machine has one. See [actions and workflows from other repositories](#actions-and-workflows-from-other-repositories) and [an action's pre and post](#an-actions-pre-and-post). |
67| Composite actions | The same. |
68| Reusable workflows (`jobs.<id>.uses: ./.g1t/workflows/build.yml`, or `owner/repo/.g1t/workflows/build.yml@v1` in another repository) | The same: `with:` inputs checked against their `type`, `secrets:` by name or `secrets: inherit`, `on.workflow_call` outputs, `strategy.matrix` on the calling job (one call for each combination), `concurrency` on the calling job, and up to ten levels of workflows with at most 50 different reusable workflows in a run. `.github/workflows/…` finds the workflow under `.g1t/` after the move. See [actions and workflows from other repositories](#actions-and-workflows-from-other-repositories). |
69| `actions/checkout` | g1t's runner checks out itself, at whatever version the step names, fetching what versions 4 and 5 fetch: the run's commit on its branch (detached for a pull request or a tag); `ref` as a branch, a tag or a commit; `repository`, at its default branch when there is no `ref`; `path`, `token`, `fetch-depth` (`0` fetches every branch and tag), `fetch-tags`, `filter`, `sparse-checkout` and `sparse-checkout-cone-mode`; `submodules` (`true` or `recursive`, with the token sent only to g1t); `persist-credentials`, on unless it is `false`, so later steps' `git fetch` and `git push` are signed in; and `set-safe-directory`. The outputs `ref` and `commit` are set. `lfs`, `ssh-key` and `clean: false` are not read. See [why it is g1t's own](/about/limitations/#actionscheckout-is-g1ts-own). |
70| `GITHUB_OUTPUT`, `GITHUB_ENV`, `GITHUB_PATH`, `GITHUB_STATE`, `GITHUB_STEP_SUMMARY` | The same. Step summaries show on the run's page; see [job summaries](#job-summaries). |
71| `::error::`, `::warning::`, `::notice::`, `::group::`, `::add-mask::`, `::echo::on` | The same: errors and warnings become annotations on the run, with their `file`, `line`, `col`, `endLine` and `endColumn`, and [masked](#masking-secrets) values stay hidden. |
72| Problem matchers (`::add-matcher::`, `::remove-matcher::`) | The same, so `actions/setup-node`, `setup-go` and `setup-python` turn compiler and linter output into annotations. See [problem matchers](#problem-matchers). |
73| `secrets.*`, `vars.*`, `secrets.GITHUB_TOKEN` | The same. `secrets.G1T_TOKEN` is [the job's own token](#the-jobs-token); `GITHUB_TOKEN` is its alias. |
74| `environment:` on a job | The job waits for the environment's [protection rules](#environments), then reads each key's row for that environment, as environment secrets work, and the run records a [deployment](/guides/deployments-api/#deployments-from-g1t-actions) to it. `url` gives the deployment its address; `deployment: false` reads the environment's values without making one. The name may be an expression. |
75| `actions/upload-artifact`, `actions/download-artifact`, `actions/upload-artifact/merge` | The same inputs and outputs as version 4: `retention-days`, `overwrite`, `compression-level`, `include-hidden-files`, `!` exclusions, download by `pattern` with `merge-multiple`, and from another run with `run-id` and `github-token`. Up to 5 GiB each; see [artifacts](#artifacts). |
76| `actions/cache`, `actions/cache/restore`, `actions/cache/save` | The actions themselves, at the version the step names, saving to and restoring from the repository's cache: kept per repository and branch, found by `key` or the newest under a `restore-keys` prefix. Up to 2 GiB each; see [the cache](#the-cache). |
77| Actions that cache through the toolkit, such as `actions/setup-node` with `cache: npm` or `Swatinem/rust-cache` | The same: they save to and restore from the repository's cache. See [actions built on the toolkit](#actions-built-on-the-toolkit). |
78| sccache with `SCCACHE_GHA_ENABLED` | The same: its entries go to the repository's cache. See [caching Rust builds](#caching-rust-builds). |
79| `permissions: id-token: write` | The job can ask for an OIDC token, and trade it for a cloud provider's credentials. See [OIDC tokens](#oidc-tokens). |
80| `docker build`, `push`, `run`, `login`, `compose`, Buildx | The same, with a Docker Engine of the job's own. See [Docker](#docker). |
81| `services:` | The same: each service starts before the steps, health checks are waited for, and it is reached at `localhost` on its port and by its name. |
82| `container:` | The same: every step runs inside the image. |
83| `uses: docker://image`, Docker actions (`runs.using: docker`) | The same: built from the action's Dockerfile or pulled, and run with GitHub's `/github/workspace` layout. |
84| `docker/setup-buildx-action`, `docker/build-push-action`, `docker/login-action` | The same. `setup-buildx-action` picks the job's own Engine as the builder. |
85
86The **Actions** page of a workflow says, under *How this runs on g1t*,
87anything in it that runs differently.
88
89### Not yet
90
91- **Windows and macOS on g1t's machines.** g1t's own runners are Linux; a
92 job with `runs-on: windows-latest` or `macos-latest` fails, and says so.
93 [Self-hosted runners](/guides/self-hosted-runners/) of any OS run them:
94 `runs-on: [self-hosted, windows]`.
95- **Docker's `type=gha` build cache.** Buildx skips it on g1t, and the
96 build runs without a cache. Use a registry cache instead; see
97 [caching image builds](#caching-image-builds).
98- **Actions that upload artifacts with the toolkit's artifact library
99 themselves.** The library refuses to run against any server but
100 github.com. `actions/upload-artifact`, `actions/download-artifact` and
101 `actions/upload-artifact/merge` work, because g1t runs them itself. See
102 [actions built on the toolkit](#actions-built-on-the-toolkit).
103- **`on: label`, and some activity types.** Creating, editing or deleting
104 a label starts no workflows yet, and the workflow's page says so. Nor do
105 the `pull_request` types `unassigned`, `locked` and `unlocked`, `edited`
106 for a new title or description, or the `issues` types `unassigned`,
107 `locked`, `unlocked`, `pinned` and `unpinned`.
108- **The `actions/checkout` from GitHub.** g1t's runner checks out
109 itself, with the action's inputs and outputs but `lfs`, `ssh-key` and
110 `clean: false`, until a pull request's commits are kept in its
111 repository.
112- **Background steps.** A step with `background: true`, or a `wait`,
113 `wait-all`, `cancel` or `parallel` step, is refused: g1t does not run
114 them yet.
115- **`timezone` on a schedule, `concurrency.queue`, `cancel-timeout-minutes`
116 and `snapshot`.** The workflow runs without them, and its page says so:
117 a schedule runs in UTC, a group keeps one run waiting, a cancelled job
118 stops in its usual time, and no runner image is made.
119
120Why each of these is missing, and what to use instead, is on
121[What g1t can't do yet](/about/limitations/#actions-and-runners).
122
123## Skipping a run
124
125A commit can ask not to start the `push` and `pull_request` workflows it
126would, as on GitHub: put `[skip ci]`, `[ci skip]`, `[no ci]`,
127`[skip actions]` or `[actions skip]` anywhere in its message, or end the
128message with a `skip-checks: true` trailer. For a push, the message read
129is the pushed head's; for a pull request, its head commit's. Other
130events, such as `pull_request_target`, `workflow_dispatch` and
131`schedule`, still start their workflows.
132
133```sh
134git commit -m "Fix a typo in the README [skip ci]"
135```
136
137A required check that a skipped workflow would have reported stays
138pending, so a pull request whose merge needs it waits for a push that
139runs it.
140
141## How g1t checks a workflow
142
143g1t reads a workflow as GitHub does, from GitHub's own schema for workflow
144files, so a workflow GitHub refuses is refused on g1t too, and one GitHub
145runs reads on g1t. When a workflow can't be read, its page says what is
146wrong, with the job and step, and no run starts from it.
147
148| You write | g1t says |
149| --- | --- |
150| A key GitHub doesn't have, at any level, such as `runs_on:` or `with:` on a `run:` step | It is unexpected, and lists the keys that place can have. |
151| A job without `runs-on` | The job has no `runs-on`. A job that calls a reusable workflow with `uses:` has no `runs-on` or `steps`. |
152| A job id that doesn't start with a letter or `_`, has characters other than letters, digits, `-` and `_`, or starts with `__` | It is not a job id GitHub accepts. Step ids follow the same rule. Two jobs, or two steps of a job, whose ids differ only in case are the same id. |
153| An event GitHub doesn't have, or a filter the event doesn't take, such as `types` on `push` | GitHub has no such event, or the event doesn't take that key. `branches` and `branches-ignore` (and `tags`, `paths`) can't both be on one event. |
154| A YAML merge key (`<<: *defaults`) | GitHub doesn't read merge keys. Anchors and aliases (`&steps` and `*steps`) work, as on GitHub, for a whole value. |
155| A `permissions` name GitHub's token doesn't have, such as `metadata` | It is unexpected. A name GitHub has that g1t has nothing behind yet reads, and grants nothing. |
156
157### g1t's own settings
158
159A `g1t:` mapping can go at the top of a workflow, on a job and on a step.
160GitHub refuses a workflow with `g1t:` in it, so use it only in
161`.g1t/workflows`. It follows two rules:
162
163- It only adds to a workflow. It never changes what a GitHub key means.
164- Each setting in it has a GitHub equivalent, written here, so that a
165 workflow can be moved back to GitHub without it.
166
167| Setting | Where | What it does | On GitHub |
168| --- | --- | --- | --- |
169| `deployment: false` | A job | The job reads its `environment:` values without the run recording a deployment. | `environment: { name: ..., deployment: false }` |
170
171g1t keeps any other key under `g1t:` as written, and does nothing with it
172yet.
173
174## Managed workflows
175
176Some workflows run in a repository without a file in it: g1t installs
177them for a feature you turn on, such as a project's
178[build with a workflow](/guides/deployments/#build-with-a-workflow). A
179managed workflow runs like any other: on the **Actions** page, with its
180runs, logs, re-runs and checks, on g1t's machines or your
181[self-hosted runners](/guides/self-hosted-runners/). Its page says it is
182managed by g1t.
183
184A workflow of the same `name:` in `.g1t/workflows/` runs in its place, so
185to change one, commit your own version of it.
186
187## Schedules
188
189A workflow with `on: schedule` runs on the default branch's latest commit,
190at each time its cron lines name, in UTC:
191
192```yaml
193on:
194 schedule:
195 - cron: "0 9 * * mon" # Mondays at 09:00 UTC
196 - cron: "*/30 * * * *" # every 30 minutes
197```
198
199Each line has five fields: minute, hour, day of month, month and day of
200the week. A field takes `*`, a number, a range `1-5`, a list `1,15` and a
201step `*/10`; days take `mon` to `sun`, and months `jan` to `dec`.
202
203| Rule | What happens |
204| --- | --- |
205| Every 5 minutes at most | A schedule more frequent than every 5 minutes, such as `* * * * *` or `*/2 * * * *`, runs every 5 minutes instead, on the five-minute marks (:00, :05, :10 and so on), at each mark that ends five minutes in which it would have run. A mark outside the hours, days or months the schedule names never runs: `* 9 * * *` runs from 09:00 to 09:55. |
206| No push for 60 days | Schedules pause in a repository that has had no push for 60 days. The next push to any branch resumes them. Other events and `workflow_dispatch` still start the workflow. |
207| Actions not paid for | When a scheduled run's job could not start because the workspace's plan, spend limit or the open-source pool does not cover it, that run fails and says why, and the workflow's schedule waits an hour before it tries again. |
208| Archived repository | Schedules wait until the repository is unarchived. |
209
210## Actions and workflows from other repositories
211
212A step's `uses: owner/repo@ref` (or `owner/repo/path@ref`) and a job's
213`uses: owner/repo/.g1t/workflows/build.yml@ref` name another repository.
214g1t looks for it on g1t first, but a name means what it means on GitHub
215whenever GitHub has it:
216
217| The repository | What happens |
218| --- | --- |
219| On g1t and public: in your workspace, under `g1t/`, or with a name GitHub has no public repository of | Your workflows use it, from any workspace. |
220| On g1t, private, in your workspace, with **Access** set to *Accessible from repositories in* the workspace | Your private repositories' workflows use it. A job reads it with a read-only token for that repository alone, which ends with the job. |
221| On g1t, private, and not shared that way | The step or job fails, and says why. A private repository's actions are never used by a public repository's workflows, whose logs anyone can read, nor from another workspace. |
222| Not on g1t, or private in a workspace you cannot see | An action comes from GitHub, at the commit its ref names there (see [actions from GitHub](#actions-from-github)); a reusable workflow is read from a public repository on GitHub. |
223| On g1t, public, in another workspace, with a name GitHub has a public repository of | GitHub's is used, as on GitHub: anyone can make a workspace, so a workspace called `actions` never answers for `actions/setup-node`. When g1t can't reach GitHub to tell, GitHub's is used too. |
224
225`ref` is a branch, a tag or a commit. A reusable workflow may be under
226`.g1t/workflows/` or `.github/workflows/`; a `.github/workflows/` path
227also finds the file under `.g1t/workflows/` in a repository moved to
228g1t. A `./.g1t/workflows/…` call inside a workflow from another
229repository reads from that repository, at the same ref.
230
231To share a private repository's actions and workflows with the rest of its
232workspace, an admin chooses **Settings → Actions → Access → Accessible from
233repositories in** the workspace, or calls
234`PUT /repos/{owner}/{repo}/actions/permissions/access` with
235`{"access_level": "organization"}` (`none` to stop).
236
237### Actions from GitHub
238
239g1t keeps the actions your workflows use from GitHub, so a job downloads
240them from g1t rather than from GitHub:
241
2421. Before the first step, the job downloads every action its steps name,
243 as GitHub's runner does in **Set up job**, and fails there if one can't
244 be had. Its log names each one's commit:
245 `Download action repository 'actions/setup-node@v4' (SHA:…) from g1t`.
2462. g1t reads which commit the ref (`v4`, a branch, or a commit) names on
247 GitHub, a tag before a branch of the same name, and looks again at
248 most every 5 minutes, so a tag that moves is followed within minutes.
2493. The first job to use an action at a commit has g1t fetch that commit's
250 archive from GitHub and keep it, which can take up to two minutes.
251 While it waits, **Set up job** says so every 5 seconds
252 (`Waiting for g1t to fetch 'actions/setup-node@v4' (10s)`). Every
253 later job, in any workspace, downloads the same bytes from g1t.
254
255While GitHub doesn't answer, a ref reads as the commit it last named, and
256the job says so in a warning; an action g1t keeps still downloads. An
257action whose archive is larger than 32 MB, or from a private repository
258on GitHub, is downloaded from GitHub by the job itself. An archive no job
259has used for 30 days is deleted, and fetched again when next used.
260Kept archives are g1t's and count toward no workspace's storage.
261
262Actions that a composite action uses are downloaded when it runs.
263
264### g1t's own actions
265
266The `g1t` workspace on g1t.sh holds g1t's own actions, public, with a
267`v1` tag that moves with each compatible release:
268
269| Action | What it does |
270| --- | --- |
271| [`g1t/deploy@v1`](https://g1t.sh/g1t/deploy) | Builds the checkout and puts it on [g1t.page](/guides/deployments/): production from the project's production branch, a preview from a pull request or another branch. The job needs `deployments: write`. See [deploying from a workflow](/guides/deployments/#deploying-from-a-workflow). |
272| [`g1t/detect@v1`](https://g1t.sh/g1t/detect) | Says what that build would find (`kind`, `install`, `build`, `output-dir`) without building anything. |
273
274Both are composite actions around `g1t-runner`, which g1t's machines
275have; on a [self-hosted runner](/guides/self-hosted-runners/) they use
276the one on `PATH` or download the newest release.
277
278### Secrets for a called workflow
279
280A called workflow gets only the secrets its caller passes, plus
281`G1T_TOKEN` (`GITHUB_TOKEN`):
282
283```yaml
284jobs:
285 build:
286 uses: acme/shared/.g1t/workflows/build.yml@v2
287 with:
288 node-version: 24
289 secrets:
290 npm-token: ${{ secrets.NPM_TOKEN }}
291
292 deploy:
293 uses: ./.g1t/workflows/deploy.yml
294 secrets: inherit
295```
296
297- `secrets:` with names passes each as the called workflow names it,
298 read from the caller's `secrets`, `needs`, `inputs`, `matrix`,
299 `github` and `vars`.
300- `secrets: inherit` passes every secret the caller has.
301- A job in the called workflow with its own `environment:` also reads that
302 environment's secrets, over what was passed.
303- A secret the called workflow marks `required: true` under
304 `on.workflow_call.secrets` that the caller does not pass fails the
305 calling job before anything runs.
306
307`vars` are the calling repository's, and a called workflow's jobs run
308with the calling run's `github` context: `actions/checkout` checks out the
309calling repository.
310
311A secret passed by name that the called workflow does not declare under
312`on.workflow_call.secrets` fails the calling job: `Invalid secret: … is
313not defined in …`.
314
315### Inputs for a called workflow
316
317The values under `with:` are checked against the `type` each input has
318under `on.workflow_call.inputs`, before anything runs:
319
320- A `boolean` input takes `true` or `false`, and a `number` input a
321 number. Text such as `'true'` or a job's output (outputs are always
322 text) fails the calling job: pass `${{ fromJSON(needs.build.outputs.count) }}`
323 to give its value.
324- A `string` input takes text; a number or a boolean becomes its text.
325- An input you leave out takes its `default`, else `false`, `0` or `''`
326 by its type. A `required: true` input you leave out fails the calling
327 job.
328- A name under `with:` that the called workflow does not declare fails the
329 calling job: `Invalid input: … is not defined in …`.
330
331### A matrix of calls
332
333A job that calls a reusable workflow may have a `strategy`. Each
334combination of its matrix calls the workflow once, with `matrix` read in
335`with:` and `secrets:`:
336
337```yaml
338jobs:
339 test:
340 strategy:
341 matrix:
342 node: [20, 22]
343 max-parallel: 1
344 uses: ./.g1t/workflows/test.yml
345 with:
346 node-version: ${{ matrix.node }}
347```
348
349The called workflow's jobs are named under the combination's call:
350`test (20) / unit`, `test (22) / unit`. `max-parallel` limits how many
351calls run at a time, and with `fail-fast` (the default) a call that fails
352cancels the calls still running or waiting. `concurrency` on the calling
353job holds its group while the called workflow runs, as a job's own does.
354
355A run connects at most ten levels of workflows: the workflow it started
356from and nine levels of reusable workflows below it. It calls at most 50
357different reusable workflows; a workflow called again, as by each
358combination of a matrix, counts once. A call past either limit fails the
359calling job and says why.
360
361## Releases
362
363Workflows with `on: release` start when a release changes, at the commit
364its tag names (`GITHUB_REF` is `refs/tags/<tag>`). Each change is one or
365more activity types, which `types:` chooses among:
366
367| Change | Activity types |
368| --- | --- |
369| A draft made | `created` |
370| A release made and published | `created`, `published`, and `released` (or `prereleased` for a prerelease) |
371| A draft published | `published`, and `released` or `prereleased` |
372| A prerelease made a full release | `edited` and `released` |
373| Made a draft again | `unpublished` |
374| Title, notes or prerelease changed | `edited`, with `github.event.changes` holding the old title and notes |
375| Deleted (the tag stays) | `deleted` |
376
377```yaml
378on:
379 release:
380 types: [published]
381```
382
383`github.event.release` has `tag_name`, `name`, `body`, `draft`,
384`prerelease`, `target_commitish`, `author` and `html_url`. A release a
385job's own token makes or changes starts no workflows.
386
387## Deployments
388
389`on: deployment` starts when a deployment is made, and
390`on: deployment_status` when one has a new status: one reported through
391the [deployments API](/guides/deployments-api/) or a
392[g1t.page](/guides/deployments/) build. The run is at the commit deployed;
393`GITHUB_REF` is the branch or tag deployed, and empty for a bare commit.
394
395```yaml
396on: deployment_status
397
398jobs:
399 smoke:
400 if: github.event.deployment_status.state == 'success'
401 runs-on: ubuntu-latest
402 steps:
403 - run: curl -fsS "${{ github.event.deployment_status.environment_url }}"
404```
405
406`github.event.deployment` has `environment`, `ref`, `sha`, `task` and
407`payload`; `github.event.deployment_status` has `state`, `environment_url`
408and `log_url`. Deployments a workflow makes, with `environment:` or with
409its job's token, start no workflows, so a workflow cannot set itself off.
410
411## The runner
412
413Jobs run in a fresh sandbox each: Debian with Node 24, Python 3, Go, Rust,
414Java 21, .NET 8, Ruby 3.3, `build-essential`, `git`, `curl`, `jq`, Docker
415(with Buildx and Compose) and passwordless `sudo`, in GitHub's layout
416(`/home/runner/work`, `RUNNER_WORKSPACE`, `RUNNER_TEMP`, `RUNNER_TOOL_CACHE`).
417`runner.os` is `Linux`. Each step's `GITHUB_ACTION` is its `id`, or
418GitHub's name for it: `__run`, `__run_2` and on for `run:` steps,
419`__actions_checkout` for `uses: actions/checkout`. `ImageOS` and
420`ImageVersion` are set when the machine's image sets them. `ubuntu-latest`, `ubuntu-24.04` and other Linux
421labels all run here. A job whose `runs-on` names `self-hosted` waits for one
422of your [self-hosted runners](/guides/self-hosted-runners/) instead. Setup actions such as
423`actions/setup-node` and `actions/setup-python` install other versions as
424they do on GitHub.
425
426### Languages and their setup actions
427
428Each language below is on `PATH` from the job's first step, so a workflow
429that only runs `java`, `dotnet` or `ruby` needs no setup step. When it has
430one, the setup action finds the version that is already there and
431downloads nothing.
432
433| Language | Version | Where | Setup action |
434| --- | --- | --- | --- |
435| Java | Eclipse Temurin 21 (LTS), JDK | `JAVA_HOME` (also `JAVA_HOME_21_X64`), in `RUNNER_TOOL_CACHE` | `actions/setup-java` with `distribution: temurin` and `java-version: 21` uses it. Other versions and distributions are downloaded. |
436| .NET | SDK 8 (LTS) | `DOTNET_ROOT`, `/usr/share/dotnet` | `actions/setup-dotnet` with `dotnet-version: 8.0.x` keeps it when it is the newest 8.0 SDK, and installs other SDKs beside it. |
437| Ruby | 3.3, with Bundler | in `RUNNER_TOOL_CACHE` | `ruby/setup-ruby` with `ruby-version: '3.3'` (or a `.ruby-version` naming 3.3) uses it. |
438| Node | 24 | `/usr/local/bin` | `actions/setup-node` installs other versions. |
439| Python | 3.11 | `/usr/bin/python3` | `actions/setup-python` installs other versions. |
440| Go | 1.27 | `/usr/local/go` | `actions/setup-go` installs other versions. |
441| Rust | stable, with `rustfmt`, `clippy` and the `wasm32-unknown-unknown` target | `~/.cargo/bin` | `rustup` is there to add toolchains and targets. |
442
443```yaml
444steps:
445 - uses: actions/checkout@v5
446 - uses: actions/setup-java@v5
447 with:
448 distribution: temurin
449 java-version: 21
450 - run: ./gradlew build
451```
452
453Because the sandbox runs Debian, `ruby/setup-ruby` treats it as a
454self-hosted runner and uses only the Rubies in `RUNNER_TOOL_CACHE`. A version other than 3.3 fails at that step; install
455it in a `run` step instead (for example with `ruby-build`) or run the job
456in a `container:` with the Ruby you need, such as `ruby:3.4`.
457
458The headers that gems and .NET need to build native code (`libyaml`,
459`libffi`, `zlib`, OpenSSL, ICU) are installed too.
460
461### Calling g1t's API from a job
462
463The `gh` command is not installed: it needs a GraphQL API, and g1t's API
464is REST. Call it with `curl`, using the job's token and the API's address,
465which every job has as `GITHUB_API_URL`:
466
467```yaml
468- name: Comment on the pull request
469 env:
470 TOKEN: ${{ github.token }}
471 run: |
472 curl -fsS -X POST "$GITHUB_API_URL/repos/$GITHUB_REPOSITORY/issues/${{ github.event.number }}/comments" \
473 -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
474 -d '{"body": "Built."}'
475```
476
477The token reaches this repository and does what the job's `permissions:`
478say; see [the job's token](#the-jobs-token). The
479[API reference](/reference/api/) lists every endpoint.
480
481### Machine sizes
482
483A job runs on the standard machine unless its `runs-on` names a larger
484one:
485
486| `runs-on` | vCPUs | Memory | Disk |
487| --- | --- | --- | --- |
488| `ubuntu-latest`, or any other Linux label | 0.5 | 4 GiB | 8 GB |
489| `g1t-2core` | 2 | 8 GiB | 16 GB |
490| `g1t-4core` | 4 | 12 GiB | 20 GB |
491
492```yaml
493jobs:
494 build:
495 runs-on: g1t-4core
496```
497
498The label can come from the matrix or the run's inputs
499(`runs-on: ${{ matrix.big && 'g1t-4core' || 'ubuntu-latest' }}`). A
500larger machine costs what it costs g1t, plus the same margin as all
501sandbox time: see [usage and billing](/guides/usage-and-billing/#workflow-jobs-on-larger-machines).
502Builds that compile, such as Rust or a large TypeScript project, finish
503several times faster on one.
504
505A job on g1t's machines runs for at most 60 minutes, whatever its
506`timeout-minutes`, and the workflow's page warns about a job that asks
507for more. One on a self-hosted runner runs for 360 minutes when it names
508no `timeout-minutes`, as on GitHub, and can ask for up to 24 hours. A job
509stopped at its time cap fails saying so.
510
511g1t's machines are x64. A job whose `runs-on` names an Arm label, such
512as `ubuntu-24.04-arm`, runs on x64, and the workflow's page says so; a
513self-hosted Arm runner runs it on Arm.
514
515### What a job can reach
516
517A job's network is restricted, as an agent's is (see
518[guardrails](/guides/guardrails/)): it reaches the hosts its project's
519guardrails allow, g1t itself, and what builds need, and nothing else.
520What builds need is the package registries (npm, PyPI, crates.io, the Go
521proxy, RubyGems, Packagist, NuGet, Maven and Gradle, Debian's mirrors),
522GitHub, where `uses:` actions and the setup actions' downloads come from,
523the toolchains' download sites (`nodejs.org`, `go.dev`,
524`static.rust-lang.org`), and the public container registries (Docker Hub,
525GitHub's, Quay, and `mirror.gcr.io`, the mirror of Docker Hub that a job's
526Engine asks first). A request anywhere else gets `403` with
527the reason. To reach another host, someone with the Maintain [role](/guides/access-and-roles/) or
528higher adds it to the project's
529allowed domains under **Settings → Guardrails**; a project whose guardrails
530turn the network restriction off runs its jobs with an open network.
531
532A host only workflows should reach, such as the API a deploy uploads to,
533goes in **Workflow-only domains** instead, limited to the workflows and
534environments that need it: `api.cloudflare.com | deploy.yml | production`
535lets only `deploy.yml`'s jobs with `environment: production` reach it.
536Agents never reach those hosts, and neither do runs of pull requests from
537forks. See [workflow-only domains](/guides/guardrails/#workflow-only-domains).
538
539g1t does not run cryptocurrency miners: a step that names one (`xmrig`,
540a `stratum+tcp://` pool, `--donate-level`) is not run, and a job that
541looks like it is mining is stopped. See
542[abuse and mining](/guides/guardrails/#abuse-and-mining).
543
544## Docker
545
546Each job on g1t's machines has a Docker Engine of its own, inside the
547job's sandbox. Nothing runs until the job uses it: the first `docker`
548command, or a job's `services:` or `container:`, starts it, in a second
549or two, and the log says so. It ends with the job, with every image,
550container and build cache in it. No other job, repository or workspace
551ever shares it.
552
553```yaml
554jobs:
555 test:
556 runs-on: ubuntu-latest
557 services:
558 postgres:
559 image: postgres:17
560 env:
561 POSTGRES_PASSWORD: ${{ secrets.DB_PASSWORD }}
562 ports: ["5432:5432"]
563 options: >-
564 --health-cmd pg_isready --health-interval 5s --health-retries 10
565 steps:
566 - uses: actions/checkout@v5
567 - run: docker compose up -d --wait
568 - run: npm test
569 env:
570 DATABASE_URL: postgres://postgres:${{ secrets.DB_PASSWORD }}@localhost:5432/postgres
571```
572
573### What works
574
575| | On g1t's machines |
576| --- | --- |
577| `docker build`, `buildx build`, `run`, `exec`, `pull`, `push`, `login`, `compose` | Work as they do on GitHub's runners. The Engine, Buildx and Compose are current releases. |
578| `services:` | Pulled and started before the first step, with `env`, `ports`, `volumes`, `options` and `credentials`. Services with a health check are waited for; one that turns unhealthy fails the job with its log. Each service's log is printed when the job ends. `job.services.<id>.id`, `.network` and `.ports` are set. |
579| `container:` | Every `run` step and JavaScript action runs inside the image, with its `env`, `options`, `volumes` and `credentials`. The workspace, `RUNNER_TEMP` and the tool cache are mounted at the same paths as on g1t's runner. |
580| `uses: docker://image` | Pulled and run, with `with.args` and `with.entrypoint`. |
581| Docker actions | Built from the action's Dockerfile (or pulled, for `image: docker://…`), and run with its `args`, `env` and `entrypoint`, its inputs as `INPUT_*` variables, and `pre-entrypoint` and `post-entrypoint`. |
582| `docker/setup-buildx-action` | Selects the job's own Engine as the builder (BuildKit). Its `name`, `driver`, `platforms` and `nodes` outputs are set. `driver`, `driver-opts` and `buildkitd-*` are not used, and the log says so. |
583| `docker/build-push-action` | Works, with `push`, `load`, `tags`, `labels`, `build-args`, `secrets`, `target`, `provenance` and `sbom`. |
584| `docker/login-action` | Works, for g1t's registry, Docker Hub, GitHub's registry, Cloudflare's (`registry.cloudflare.com`) and any registry the job can reach. |
585
586### Services and the network
587
588Every container a job starts shares the job's own network, the one its
589[guardrails](/guides/guardrails/) apply to. So:
590
591- **A service is at `localhost`** on its port, from steps and from other
592 containers. `ports: ["5432:5432"]` and `ports: ["5432"]` both mean
593 `localhost:5432`.
594- **A port mapped to another number** (`ports: ["6543:5432"]`, or
595 `docker run -p 8080:80`) is forwarded: `localhost:6543` reaches the
596 service's 5432. `job.services.<id>.ports` says which port to use, and
597 `docker inspect` and `docker port` report it.
598- **A service is also reached by its name**, as it is from a job
599 container on GitHub: `postgres:5432` works from steps, from the job's
600 container and from any container started later. So do the names of
601 containers and Compose services, and their network aliases.
602- **Two containers cannot listen on the same port.** A job with a
603 `redis` service and a Compose file that starts another Redis on 6379
604 gets an error from the second; give one of them another port.
605
606A container that asks for `--network none` gets none, and
607`--network container:<name>` shares that container's.
608
609### Job containers
610
611With `container:`, the steps run inside the image as its default user,
612usually `root`. A few things differ from GitHub's runner:
613
614- The workspace is at the same path as on g1t's runner
615 (`/home/runner/work/…`), not `/__w`. `github.workspace` is correct
616 either way.
617- JavaScript actions run inside the container with g1t's Node 24, which
618 needs an image with glibc and `libstdc++` (Debian, Ubuntu and most
619 language images have both). In an image without them, such as Alpine,
620 they run beside the container, on g1t's runner, with the same files,
621 and the log says so.
622- `actions/checkout` and the artifact actions run on g1t's runner, with
623 the same files.
624
625### Building and pushing images
626
627On g1t's machines, a job is signed in to g1t's container registry from
628the start, with its own `G1T_TOKEN`, so it can push to and pull from its
629repository's images without a login step; images of other repositories
630need this one added under their
631[Manage Actions access](/guides/packages/#manage-actions-access). A run that gets no secrets is
632not signed in. See [container registry](/guides/containers/#in-workflows).
633
634```yaml
635jobs:
636 image:
637 runs-on: g1t-4core
638 steps:
639 - uses: actions/checkout@v5
640 - uses: docker/setup-buildx-action@v3
641 - uses: docker/build-push-action@v6
642 with:
643 push: true
644 tags: g1t.sh/${{ github.repository }}:${{ github.sha }}
645 cache-from: type=registry,ref=g1t.sh/${{ github.repository }}:buildcache
646 cache-to: type=registry,ref=g1t.sh/${{ github.repository }}:buildcache,mode=max
647```
648
649For other registries, sign in with `docker/login-action` or
650`docker login`, as on GitHub. Docker Hub's images are pulled through its
651public mirror first, so jobs are rarely held up by Docker Hub's limits on
652anonymous pulls.
653
654#### Caching image builds
655
656The Engine starts empty in every job, so a build's layers are rebuilt
657unless the job brings a cache:
658
659- **A registry cache** (`cache-to: type=registry,ref=…,mode=max`), in g1t's
660 registry or any other, is the simplest and is shared by every branch.
661- **A local cache** (`cache-to: type=local,dest=/tmp/buildx-cache`) saved
662 and restored with `actions/cache`, within [the cache's limits](#the-cache).
663- **`type=gha`** is not used on g1t yet: Buildx skips it, and the build
664 runs without a cache.
665
666### Limits
667
668- **Machine.** Containers share the job's machine: its vCPUs, memory and
669 disk ([machine sizes](#machine-sizes)). Image builds and databases want
670 `g1t-2core` or `g1t-4core`. `--cpus` and `--memory` limit a container
671 within that.
672- **Disk.** Images take room on the job's disk. On a machine whose disk
673 cannot hold layered images, the Engine stores plain copies, which take
674 more room; the log says when it does.
675- **Linux, amd64.** Images for other platforms need QEMU's emulators,
676 which g1t's machines do not have set up; `docker/setup-qemu-action` is
677 not supported there yet.
678- **Privileged containers** (`--privileged`) run, with no more reach than
679 the job itself has: the job's sandbox is the boundary.
680
681### How Docker is kept safe
682
683- **One Engine per job.** It runs inside the job's own sandbox, a virtual
684 machine of its own, and is gone with it. No Docker socket of g1t's, or of
685 any machine, is shared with a job.
686- **The job's guardrails hold.** Containers use the job's network, so a
687 container, a build step or an image pull reaches only what the job may
688 reach. A host off the list gets `403` with the reason, as any step does.
689- **HTTPS keeps working.** In a job whose network is restricted, every
690 container and build step is given the certificate the job's HTTPS is
691 checked with, in `/dev/g1t-egress`, and `SSL_CERT_FILE`,
692 `NODE_EXTRA_CA_CERTS`, `REQUESTS_CA_BUNDLE`, `CURL_CA_BUNDLE`, `PIP_CERT`,
693 `GIT_SSL_CAINFO` and `CARGO_HTTP_CAINFO` pointing at it, unless the
694 container sets them itself. None of it is written into an image's layers.
695 Tools that keep their own list of certificates, such as Java's, need it
696 added in the build that uses them.
697- **Short-lived credentials.** The registry sign-in uses the run's own
698 token, which ends with the run; `credentials:` for a service or a job
699 container are used for that pull only.
700- **No miners.** A container whose image or command names a miner is not
701 created, as a step's script is not run.
702
703## The cache
704
705`actions/cache` keeps what a job saves for the repository's later jobs:
706
707| | |
708| --- | --- |
709| One entry | Up to 2 GiB, compressed. A larger one is not saved, and the job goes on. |
710| A repository's entries | Up to 10 GiB together. Saving past it removes the entries restored longest ago. |
711| How long | Until it has not been restored for 7 days, and at most 28 days after it was saved. |
712| Which branch | An entry belongs to the branch, tag or pull request whose run saved it. A run restores from its own, then from the branch its pull request merges into, then from the default branch. |
713| Keys | Written once on each branch: saving under a key that exists there does nothing. On each branch in turn, a restore finds its `key` exactly, else the newest entry whose key starts with one of its `restore-keys`. |
714| `path` | Files and folders; globs, `**` included; `~/` is the home folder; a line starting with `!` leaves matching paths out. The same key saved for other paths is another entry. |
715| Compression | zstd. |
716
717So a feature branch can read what `main` saved, but `main` never reads
718what a feature branch saved, and a pull request from outside the
719repository saves where nothing else ever reads it: nobody can plant an
720entry that the default branch's builds restore. Actions that cache through the
721toolkit, such as `actions/setup-node` with `cache: npm`, follow the same
722rules.
723
724```yaml
725- uses: actions/cache@v4
726 with:
727 path: |
728 ~/.cargo/registry/cache
729 target/release
730 !target/**/incremental
731 key: cargo-${{ runner.os }}-${{ hashFiles('Cargo.lock') }}
732 restore-keys: cargo-${{ runner.os }}-
733```
734
735Each restore and save says on the job's log how large the entry was and
736how long it took. A workspace on the plan pays for what its caches and
737[artifacts](#artifacts) hold (`Actions cache storage` on its statement),
738at R2's price plus the margin; see
739[usage and billing](/guides/usage-and-billing/#actions-cache).
740
741## Artifacts
742
743`actions/upload-artifact` keeps files a job made with its run, for later
744jobs, other runs and people:
745
746| | |
747| --- | --- |
748| One artifact | Up to 5 GiB, zipped. |
749| A run's artifacts | Up to 10 GiB together. |
750| How long | The repository's setting: 14 days unless someone with the Maintain role changes it under **Settings → Repository → Artifacts**, from 1 to 90 days. `retention-days` asks for fewer days, never more. |
751| Names | One artifact per name in a run. Uploading a name again fails, unless the upload says `overwrite: true`, which replaces it. A name is up to 256 characters, none of `" : < > \| * ? \ /`. |
752| `path` | Files, folders and globs, `**` included; a line starting with `!` leaves matching paths out. Files and folders whose names start with `.` are left out unless `include-hidden-files: true`. |
753| Compression | `compression-level` 0 (stored) to 9; 6 unless you say. |
754| Outputs | `artifact-id` (a number), `artifact-url` (its run's page) and `artifact-digest` (the SHA-256 of its zip). |
755
756```yaml
757- uses: actions/upload-artifact@v4
758 with:
759 name: web-dist
760 path: |
761 dist/
762 !dist/**/*.map
763 retention-days: 5
764 compression-level: 9
765```
766
767`actions/download-artifact` downloads one by `name` into `path`, or every
768artifact of the run, each into a folder of its name; `pattern` picks them
769by name, and `merge-multiple: true` puts them all in one folder.
770`artifact-ids` picks them by number. With `github-token` and `run-id`, it
771downloads from another run of the same repository, such as the one a
772`workflow_run` workflow follows:
773
774```yaml
775- uses: actions/download-artifact@v4
776 with:
777 name: web-dist
778 github-token: ${{ secrets.GITHUB_TOKEN }}
779 run-id: ${{ github.event.workflow_run.id }}
780```
781
782`actions/upload-artifact/merge` downloads the run's artifacts that match
783`pattern`, uploads them as one artifact (`name`, `merged-artifacts` unless
784you say), and deletes them with `delete-merged: true`.
785
786A run's summary lists its artifacts with their size and when they expire.
787Anyone who can see the run downloads them there; someone with the Write
788role can delete one before it expires.
789
790## Actions built on the toolkit
791
792`actions/cache` and many other actions save to the cache with GitHub's
793toolkit, `@actions/cache`: `actions/setup-node`, `actions/setup-python`,
794`actions/setup-go` and `actions/setup-java` with `cache:`,
795`Swatinem/rust-cache`, and others. They work on g1t as they are: every job gets `ACTIONS_RUNTIME_TOKEN`, `ACTIONS_CACHE_URL` and
796`ACTIONS_RESULTS_URL`, and g1t answers the toolkit's requests from the
797repository's cache.
798
799- Their entries are the repository's, under [the cache's](#the-cache)
800 limits, and are deleted the same way.
801- An entry is found by its key and by the version the toolkit names for
802 it, from its paths and compression, as on GitHub: an entry `setup-node`
803 saved is restored by `actions/cache` with the same key and paths, and
804 the other way round.
805- An entry the toolkit sends whole, which it does below 128 MB, is not
806 saved when it is over 100 MB, the most g1t takes in one request. The
807 step warns and the job goes on.
808- The toolkit's artifact library refuses to run against any server but
809 github.com, so an action that uploads artifacts with it directly fails
810 with its own message. `actions/upload-artifact`,
811 `actions/download-artifact` and `actions/upload-artifact/merge` work:
812 g1t runs those itself.
813
814## An action's pre and post
815
816A JavaScript action's `pre` runs before the job's first step, as on
817GitHub: each one is a step of its own, *Pre* and the step's name, in the
818order of the steps, when its `pre-if` holds (`always()` when it has
819none). Its `post` runs after the last step, *Post* and the step's name,
820last first. What `pre` saves with `GITHUB_STATE` or `::save-state::`
821reaches the step's main as `STATE_<name>`, and what either saves reaches
822`post`.
823
824An action in your repository (`uses: ./…`) is not checked out when the
825job starts, so its `pre` is not run and the step warns, as on GitHub. An
826action inside a composite action runs its `pre` right before its main.
827
828A step that gives an input the action does not declare is warned of:
829*Unexpected input(s) 'foo', valid inputs are ['token', 'path']*. The
830step still runs.
831
832## Problem matchers
833
834`::add-matcher::path/to/matcher.json` adds the matchers in a file of
835GitHub's format, and they read every line of output from then on, for
836the rest of the job. A line one matches becomes an error, warning or
837notice annotation, with the file, line, column and message it found, in
838place of the line in the log.
839
840```json
841{
842 "problemMatcher": [
843 {
844 "owner": "my-linter",
845 "pattern": [
846 {
847 "regexp": "^(.+):(\\d+):(\\d+): (error|warning) (.*)$",
848 "file": 1, "line": 2, "column": 3, "severity": 4, "message": 5
849 }
850 ]
851 }
852 ]
853}
854```
855
856- A pattern of several lines matches them in order. The last one can
857 set `loop` to match each issue after a header line, as ESLint's
858 stylish output has.
859- `severity` is the matcher's own default, `error` when it gives none.
860 A severity other than `error`, `warning` or `notice` is no annotation.
861- A file is kept when it exists in a repository in the workspace, as a
862 path inside it; `fromPath` names the file a relative path is from.
863- A matcher added with an owner already there takes its place.
864 `::remove-matcher owner=my-linter::` removes it.
865- Patterns are regular expressions as Rust's `regex` reads them, which
866 has no lookaround or backreferences. A file whose patterns do not
867 read is refused, with an error in the log.
868
869`::echo::on` shows each workflow command in the log as it is read,
870until `::echo::off`; with [debug logging](#debug-logging) they are shown
871from the start.
872
873## Caching Rust builds
874
875A checkout gives every file a new modification time, so Cargo compiles
876your workspace's own crates again even when `target/` was restored from
877the cache. [sccache](https://github.com/mozilla/sccache) caches each
878compiler call by what it compiles (the source, the flags, the toolchain
879and the dependencies), so a crate that did not change comes back from the
880cache instead. Its GitHub Actions backend works on g1t as it is: it keeps
881its entries in the repository's cache, under [the cache's](#the-cache)
882limits and branch rules.
883
8841. Install sccache, pinned to a release and checked against its checksum.
8852. Set `RUSTC_WRAPPER: sccache` and `SCCACHE_GHA_ENABLED: "true"`. The
886 job's `ACTIONS_RUNTIME_TOKEN` and `ACTIONS_CACHE_URL` are already in
887 every step's environment; no step needs to export them.
8883. Set `CARGO_INCREMENTAL: "0"`: sccache does not cache incremental
889 builds, and a fresh checkout gains nothing from them.
890
891```yaml
892name: CI
893on:
894 pull_request:
895 push:
896 branches: [main]
897
898jobs:
899 test:
900 runs-on: ubuntu-latest
901 env:
902 RUSTC_WRAPPER: sccache
903 SCCACHE_GHA_ENABLED: "true"
904 CARGO_INCREMENTAL: "0"
905 steps:
906 - uses: actions/checkout@v5
907 - name: Install sccache
908 env:
909 VERSION: v0.18.0
910 SHA256: 45f1447fbe231e3037bde351ef70677dd212216c8d62ae7ca409fecc4d6acc89
911 run: |
912 name="sccache-$VERSION-x86_64-unknown-linux-musl"
913 curl -fsSL -o "$RUNNER_TEMP/sccache.tar.gz" \
914 "https://github.com/mozilla/sccache/releases/download/$VERSION/$name.tar.gz"
915 echo "$SHA256 $RUNNER_TEMP/sccache.tar.gz" | sha256sum -c -
916 tar -xzf "$RUNNER_TEMP/sccache.tar.gz" -C "$RUNNER_TEMP"
917 install -m 0755 "$RUNNER_TEMP/$name/sccache" "$HOME/.cargo/bin/sccache"
918 - run: cargo test --workspace --locked
919 - name: sccache's hits and misses
920 if: always()
921 run: |
922 echo '```' >> "$GITHUB_STEP_SUMMARY"
923 sccache --show-stats | tee -a "$GITHUB_STEP_SUMMARY"
924 echo '```' >> "$GITHUB_STEP_SUMMARY"
925```
926
927What to expect:
928
929| | |
930| --- | --- |
931| What is cached | Every library crate (`rlib`): your workspace's crates and their dependencies. |
932| What is compiled each time | What rustc links: binaries, `cdylib` crates, proc macros, build scripts and test harnesses. |
933| Its entries | One per compiled crate, often thousands for a workspace, each a few KB to a few MB. They count toward the repository's 10 GiB like any other entry, and the ones not restored for 7 days are deleted. |
934| Branches | A pull request reads what the default branch saved. Run the workflow on pushes to the default branch too, as above, or each pull request's first run starts with an empty cache. |
935| Starting over | Set `SCCACHE_GHA_VERSION` to any new value. A new sccache release starts over by itself. |
936| If the cache cannot be reached | sccache refuses to start. Set `SCCACHE_IGNORE_SERVER_IO_ERROR: "1"` to have rustc run without it instead. |
937
938sccache adds to `actions/cache` rather than replacing it: keep caching
939`~/.cargo/registry/cache` and `target/` keyed by `Cargo.lock`, so Cargo
940does not call rustc at all for dependencies that did not change, and
941sccache answers the calls it still makes for your own crates.
942`Swatinem/rust-cache` works the same way.
943
944## OIDC tokens
945
946A job can prove which repository, branch and environment it runs for with
947a short-lived OpenID Connect token signed by g1t, and trade it for a cloud
948provider's credentials. Nothing long-lived needs to sit in a secret.
949
9501. Give the job, or the workflow, `permissions: id-token: write`. A job
951 without it gets no token, and neither does a run of a pull request from
952 outside the repository.
9532. Tell your cloud to trust g1t's issuer for your repository (below).
9543. Use the provider's own login action, which asks for the token.
955
956```yaml
957permissions:
958 id-token: write
959 contents: read
960
961jobs:
962 deploy:
963 runs-on: ubuntu-latest
964 environment: production
965 steps:
966 - uses: aws-actions/configure-aws-credentials@v4
967 with:
968 role-to-assume: arn:aws:iam::123456789012:role/acme-web-deploy
969 aws-region: us-east-1
970```
971
972The job gets `ACTIONS_ID_TOKEN_REQUEST_URL` and
973`ACTIONS_ID_TOKEN_REQUEST_TOKEN`, which `core.getIDToken()` reads. To ask
974for a token yourself, name its audience:
975
976```sh
977curl -sS -H "Authorization: Bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
978 "$ACTIONS_ID_TOKEN_REQUEST_URL&audience=https://deploy.example.com" | jq -r .value
979```
980
981| | |
982| --- | --- |
983| Issuer | `https://api.g1t.sh/actions/oidc` |
984| Discovery | `https://api.g1t.sh/actions/oidc/.well-known/openid-configuration` |
985| Keys | `https://api.g1t.sh/actions/oidc/.well-known/jwks`, RS256, each with its `kid` |
986| Lifetime | 5 minutes |
987| Audience | What the job asks for; `https://g1t.sh/<owner>` when it asks for none |
988
989### Claims
990
991Each token carries the claims GitHub's do, so trust policies written for
992those read g1t's the same way.
993
994| Claim | Example |
995| --- | --- |
996| `sub` | `repo:acme/web:environment:production` for a job with an `environment:`; `repo:acme/web:pull_request` for a pull request's run; otherwise `repo:acme/web:ref:refs/heads/main` (or `refs/tags/v1.2.0`) |
997| `repository`, `repository_owner` | `acme/web`, `acme` |
998| `repository_id`, `repository_owner_id` | g1t's ids for them, such as `rep_01kpw0…` (not the numbers `github.repository_id` and `github.repository_owner_id` give, so trust policies already written keep matching) |
999| `repository_visibility` | `public` or `private` |
1000| `ref`, `ref_type`, `ref_protected`, `sha` | `refs/heads/main`, `branch`, `"true"`, the commit |
1001| `head_ref`, `base_ref` | A pull request's branches |
1002| `environment` | The job's environment, when it has one |
1003| `event_name` | `push`, `pull_request`, `workflow_dispatch`, … |
1004| `workflow`, `workflow_ref`, `workflow_sha` | `Deploy`, `acme/web/.g1t/workflows/deploy.yml@refs/heads/main`, the commit |
1005| `job_workflow_ref`, `job_workflow_sha` | The workflow that defines the job: a called workflow's own file |
1006| `run_id`, `run_number`, `run_attempt` | `run_01kq9c…`, `"12"`, `"1"` |
1007| `actor`, `actor_id` | Who started the run |
1008| `runner_environment` | `github-hosted` on g1t's machines, `self-hosted` on yours |
1009| `iss`, `aud`, `jti`, `iat`, `nbf`, `exp` | As in any OIDC token |
1010
1011### AWS
1012
10131. Add g1t as an identity provider, under **IAM → Identity providers**:
1014 provider type **OpenID Connect**, provider URL
1015 `https://api.g1t.sh/actions/oidc`, audience `sts.amazonaws.com`. Or:
1016
1017 ```sh
1018 aws iam create-open-id-connect-provider \
1019 --url https://api.g1t.sh/actions/oidc \
1020 --client-id-list sts.amazonaws.com
1021 ```
1022
10232. Give the role a trust policy for your repository:
1024
1025 ```json
1026 {
1027 "Version": "2012-10-17",
1028 "Statement": [{
1029 "Effect": "Allow",
1030 "Principal": { "Federated": "arn:aws:iam::123456789012:oidc-provider/api.g1t.sh/actions/oidc" },
1031 "Action": "sts:AssumeRoleWithWebIdentity",
1032 "Condition": {
1033 "StringEquals": {
1034 "api.g1t.sh/actions/oidc:aud": "sts.amazonaws.com",
1035 "api.g1t.sh/actions/oidc:sub": "repo:acme/web:environment:production"
1036 }
1037 }
1038 }]
1039 }
1040 ```
1041
10423. Use `aws-actions/configure-aws-credentials@v4` with `role-to-assume`,
1043 as above.
1044
1045### Google Cloud
1046
10471. Make a workload identity pool and a provider for g1t:
1048
1049 ```sh
1050 gcloud iam workload-identity-pools create g1t --location=global
1051 gcloud iam workload-identity-pools providers create-oidc g1t \
1052 --location=global --workload-identity-pool=g1t \
1053 --issuer-uri=https://api.g1t.sh/actions/oidc \
1054 --attribute-mapping="google.subject=assertion.sub,attribute.repository=assertion.repository" \
1055 --attribute-condition="assertion.repository_owner == 'acme'"
1056 ```
1057
10582. Let the repository act as a service account:
1059
1060 ```sh
1061 gcloud iam service-accounts add-iam-policy-binding deploy@acme-prod.iam.gserviceaccount.com \
1062 --role=roles/iam.workloadIdentityUser \
1063 --member="principalSet://iam.googleapis.com/projects/123456789/locations/global/workloadIdentityPools/g1t/attribute.repository/acme/web"
1064 ```
1065
10663. Use `google-github-actions/auth@v2` with
1067 `workload_identity_provider: projects/123456789/locations/global/workloadIdentityPools/g1t/providers/g1t`
1068 and `service_account`. It asks for the provider's own name as the
1069 audience, which the provider accepts unless you change its allowed
1070 audiences.
1071
1072### Azure
1073
10741. On the app registration or user-assigned managed identity, add a
1075 federated credential with the scenario **Other issuer**: issuer
1076 `https://api.g1t.sh/actions/oidc`, subject identifier
1077 `repo:acme/web:environment:production`, audience
1078 `api://AzureADTokenExchange`. Or:
1079
1080 ```sh
1081 az ad app federated-credential create --id <application id> --parameters '{
1082 "name": "g1t-acme-web-production",
1083 "issuer": "https://api.g1t.sh/actions/oidc",
1084 "subject": "repo:acme/web:environment:production",
1085 "audiences": ["api://AzureADTokenExchange"]
1086 }'
1087 ```
1088
10892. Give it a role on what it deploys to, as for any identity.
10903. Use `azure/login@v2` with `client-id`, `tenant-id` and
1091 `subscription-id`, and no secret.
1092
1093A federated credential matches the subject exactly: add one per
1094environment or branch that deploys.
1095
1096### Cloudflare
1097
1098Cloudflare's API takes API tokens, not OIDC tokens. Keep a token scoped
1099to what the workflow deploys in a secret, available to that workflow only
1100(see [workflow-only domains](/guides/guardrails/#workflow-only-domains)
1101for limiting where it can be sent).
1102
1103A Worker of your own can trust g1t's jobs directly, by checking the token
1104a job sends it against g1t's keys:
1105
1106```ts
1107import { createRemoteJWKSet, jwtVerify } from "jose";
1108
1109const keys = createRemoteJWKSet(new URL("https://api.g1t.sh/actions/oidc/.well-known/jwks"));
1110
1111export async function fromG1tJob(request: Request): Promise<boolean> {
1112 const token = request.headers.get("authorization")?.replace(/^Bearer /, "") ?? "";
1113 const { payload } = await jwtVerify(token, keys, {
1114 issuer: "https://api.g1t.sh/actions/oidc",
1115 audience: "https://deploy.example.com",
1116 });
1117 return payload.sub === "repo:acme/web:environment:production";
1118}
1119```
1120
1121### npm
1122
1123npm's trusted publishing and provenance accept OIDC tokens only from the
1124CI services npm lists, and g1t is not one of them yet. Publish with a
1125granular access token in a secret instead:
1126
1127```yaml
1128- uses: actions/setup-node@v4
1129 with:
1130 node-version: 24
1131 registry-url: https://registry.npmjs.org
1132- run: npm publish
1133 env:
1134 NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
1135```
1136
1137See [What g1t can't do yet](/about/limitations/#no-npm-trusted-publishing-or-provenance).
1138
1139## Runs and logs
1140
1141Open a repository's **Actions** page, in its sidebar. Pick a workflow to
1142see its runs, run it by hand if it has `workflow_dispatch`, or turn it off
1143without touching its file.
1144
1145A run's page opens on its **Summary**. Its sidebar lists **All jobs**;
1146pick one to see its steps and their logs as they are written. Groups
1147fold, errors and warnings are marked, and secrets are replaced with
1148`***`. The page keeps itself up to date while the run goes on.
1149
1150### Live logs
1151
1152While a job runs, its log reaches the page within about two seconds of
1153being printed: the runner sends what a step prints every second, and the
1154page asks for more every second while a step is running.
1155
1156- The running step opens by itself, and its timer counts up. Its log
1157 follows the newest line while the end of it is on screen. Scroll up and
1158 it stays where you are, with **Jump to latest** to go back to the end.
1159- A step that prints nothing for a while says so under its last line:
1160 **Running · no output for 2m 10s**, or **no output yet** before its
1161 first line. The job's header names the step it is on.
1162- Colour that tools print is shown as they print it: bold, dim, italic,
1163 underline, the 16 terminal colours, 256 colours and 24-bit colour, for
1164 text and background, in both the light and the dark theme. Tools built
1165 on chalk or supports-color colour their output because the job has
1166 `GITHUB_ACTIONS=true`, as on GitHub; others need asking, such as
1167 `CARGO_TERM_COLOR: always` in the step's `env` for cargo. Other escape codes (moving the cursor, clearing a
1168 line) are left out, and a link keeps its text. Search reads the text
1169 without the colour codes.
1170- Every line is stamped with the time it was printed, as GitHub stamps
1171 it (`2026-10-12T09:41:07.1234567Z`). The clock button beside **Search
1172 logs** shows or hides the times; your browser remembers which.
1173- A download (the job's `log.txt` and the run's zip) has the time at the
1174 start of every line, as GitHub's downloaded logs do.
1175- Each line's number links to it, as on GitHub: `#step:3:12` is line 12
1176 of step 3 (0 is **Set up job**). Opening the link opens the step and
1177 any group the line is in, scrolls to the line and marks it.
1178- A long step's log stays fast. Each new piece of a running step's log is
1179 read once as it arrives, and a step of more than 2,000 lines draws only
1180 the lines on screen and a few around them. Search, groups, line links,
1181 following the newest line and **Jump to latest** work as they do on a
1182 short log. Your browser's own find (Ctrl+F) only sees the lines that are
1183 drawn, so use **Search logs** in a long log.
1184- The runner numbers each batch of the log it sends. When it sends a
1185 batch again because g1t's answer didn't reach it, g1t keeps the batch
1186 once, so a line never shows twice.
1187- If the page cannot reach g1t for a moment, it says **Reconnecting to the
1188 log** and tries again, waiting longer each time up to half a minute.
1189 A tab you are not looking at stops asking, and catches up when you come
1190 back to it.
1191
1192A cache restore, an artifact upload or download, and other transfers say
1193how far they have got every few seconds
1194(`Received 46137344 of 523190823 (8.8%), 43.9 MBs/sec`).
1195
1196A [self-hosted runner](/guides/self-hosted-runners/) stamps its lines and
1197writes these headers once it has updated itself to a release that does;
1198until then its logs show without times.
1199
1200### What a job's log says
1201
1202Each job's log reads as GitHub's does:
1203
1204| Step | What it shows |
1205| --- | --- |
1206| **Set up job** | The runner's version and name; the operating system; the runner image (`ImageOS` and `ImageVersion` when the machine's image sets them, and the software g1t's machines have); the machine (hosted or self-hosted, its architecture, CPUs and memory); what the job's [token](#the-jobs-token) may do, under **G1T_TOKEN Permissions**; the secret source; each action the job uses as it is downloaded, with the commit it resolved to (`Download action repository 'actions/setup-node@v4' (SHA:…)`); and the job's matrix. |
1207| A `run:` step | A folded group, `Run` and the script's first line, holding the whole script, the shell it ran in (`shell: /usr/bin/bash -e {0}`) and its variables under `env:`, secrets masked. Then what it printed. |
1208| A `uses:` step | A folded group, `Run owner/repo@ref`, holding its inputs under `with:` (an action's defaults among them) and its variables under `env:`. |
1209| **Post** steps and **Stop containers** | An action's `post`, saving the cache, then the job's containers stopped, as on GitHub. |
1210| **Complete job** | The job's outputs as they are set (`Set output 'version'`), and **Cleaning up orphan processes**: anything the steps started and left running is stopped (`Terminate orphan process: pid (1234) (node)`). The runner finds those processes by `RUNNER_TRACKING_ID`, as GitHub's does, so a process that unsets it is not found. Only on Linux. |
1211
1212### The run's summary
1213
1214The summary holds, top to bottom:
1215
1216| Part | What it shows |
1217| --- | --- |
1218| The run | What triggered it and when, who, the commit and its branch (or pull request), how the run stands, how long it took in all, and how many artifacts it kept. |
1219| The jobs graph | The workflow file and its event (`deploy.yml · on: push`), then every job and how they depend on each other. See [the jobs graph](#the-jobs-graph). |
1220| Annotations | Every job's errors, warnings and notices, counted by kind, each with the job it came from. Folds away. |
1221| Job summaries | What each job's steps wrote to `$GITHUB_STEP_SUMMARY`, under `<job> summary`. See [job summaries](#job-summaries). |
1222| Artifacts | What the run kept, to download. The count in the run's card jumps here. |
1223
1224Under **Run details** in the sidebar, **Workflow file** opens the
1225workflow as it was at the run's commit.
1226
1227### The jobs graph
1228
1229Jobs are laid out left to right by their `needs:`: a job that needs
1230nothing starts a row on the left, and each job sits one column to the
1231right of the furthest job it needs. Within a column, jobs are ordered so
1232that as few connectors as possible cross.
1233
1234| Node | How it shows |
1235| --- | --- |
1236| A job | Its status, its name and how long it took. Click it to open its steps and logs. |
1237| A job that deploys | Its environment's address (`environment.url`), as a link, under its name. |
1238| A matrix | One node with how many jobs it made (`test · 6 jobs`) and how they stand together. Click it to list each combination; click one to open it. |
1239| A reusable workflow | A box under the job that calls it, named with the called file, holding the called workflow's jobs laid out the same way; a matrix of calls shares one box, each call's jobs named with its combination. The jobs that need the calling job connect to the box. |
1240
1241Connectors run from a job to each job that needs it. They are red from a
1242job that failed, and move while the job they lead to is running (they
1243stay still if your system asks for reduced motion).
1244
1245A wide graph scrolls sideways inside its card, on a phone as on a desktop.
1246
1247The sidebar groups jobs the same way: a matrix's jobs, and a called
1248workflow's jobs, fold under their name.
1249
1250### Job summaries
1251
1252Markdown a step appends to the file in `$GITHUB_STEP_SUMMARY` shows on
1253the run's summary, below the graph, a card per job, in the order its
1254steps wrote it. The card's title opens the job:
1255
1256```yaml
1257- name: Report the tests
1258 if: always()
1259 run: |
1260 echo "### Test results" >> "$GITHUB_STEP_SUMMARY"
1261 echo "| Suite | Passed | Failed |" >> "$GITHUB_STEP_SUMMARY"
1262 echo "| --- | ---: | ---: |" >> "$GITHUB_STEP_SUMMARY"
1263 echo "| unit | 41 | 0 |" >> "$GITHUB_STEP_SUMMARY"
1264```
1265
1266| What | How it works |
1267| --- | --- |
1268| Formatting | GitHub-flavoured Markdown: tables, task lists, alerts such as `> [!WARNING]`, code blocks, and the HTML GitHub allows. Scripts, styles and event handlers are removed. |
1269| Secrets | Masked like the log, before the summary leaves the runner. |
1270| Size | Up to 1 MiB a step. A larger summary is refused with an error in the step's log, as on GitHub. |
1271| Steps | Up to 20 steps of a job keep a summary; later ones are dropped. |
1272| Actions | A JavaScript action's `core.summary` writes to the same file, so it works unchanged. |
1273
1274Summaries belong to their attempt: an earlier attempt keeps its own.
1275
1276### Re-running
1277
1278When a run has finished, someone with the Write role can run it again:
1279
1280| Button | Runs again |
1281| --- | --- |
1282| **Re-run all jobs** | Every job. |
1283| **Re-run failed jobs** | Jobs that did not succeed (failed, cancelled or skipped), and every job that needs one of them. |
1284| The re-run button beside a job's name | That job, and every job that needs it. A job of a matrix runs again with the rest of its matrix; a job of a reusable workflow runs again with the job that calls it. |
1285
1286Jobs that are not run again keep how they ended, and their outputs reach
1287the jobs that need them.
1288
1289Each re-run is a new **attempt**. The run keeps its number, `github.run_attempt`
1290goes up by one, and the attempt before is kept as it ended: its jobs,
1291their steps, logs and summaries. Pick one from **Attempt #** at the top of
1292the page to read it. Its jobs have ids of their own, so a link to an
1293earlier attempt's job keeps showing that job's log.
1294
1295#### Debug logging
1296
1297Each re-run asks whether to **Enable debug logging**. The new attempt's
1298jobs then run with:
1299
1300| Set | Effect |
1301| --- | --- |
1302| `RUNNER_DEBUG=1`, and `runner.debug` is `1` | Actions that check it, such as the toolkit's `core.isDebug()`, log more. |
1303| `ACTIONS_STEP_DEBUG=true` | `::debug::` lines are shown in the log. |
1304| `ACTIONS_RUNNER_DEBUG=true` | Set for actions that read it. |
1305
1306With debug logging, each step's log also says how its `if:` read:
1307`Evaluating condition for step`, the expression, and the result. Setting a
1308secret or variable named `ACTIONS_STEP_DEBUG` to `true` shows `::debug::`
1309lines on every run instead. The attempt picker marks attempts that ran
1310with debug logging.
1311
1312### Cancelling
1313
1314**Cancel run** cancels jobs that have not started at once. A job that is
1315running is stopped the way GitHub stops one:
1316
13171. The step it is on gets `SIGINT`, then `SIGTERM` 7.5 seconds later, and
1318 is killed 2.5 seconds after that. Signals reach the processes the step
1319 started too; in a [job container](#job-containers) they reach only the
1320 `docker exec` that runs the step. The step ends **cancelled**.
13212. Its remaining steps run only if they ask to: `if: always()` or
1322 `if: cancelled()`. Steps without an `if:`, or with `success()` or
1323 `failure()`, are skipped.
13243. Post steps (an action's `post`, saving the cache) run, as their
1325 `post-if` is `always()` unless the action says otherwise.
13264. The job ends **cancelled**, whatever those steps came to.
1327
1328While that happens the run says **Cancelling**. A job still going 5
1329minutes after it was cancelled is stopped outright. **Force cancel**
1330(shown while a run is cancelling) stops every job at once, without
1331waiting for its cleanup steps.
1332
1333A step learns of a cancellation within about 10 seconds, even when it
1334prints nothing. A job on a [self-hosted runner](/guides/self-hosted-runners/)
1335is stopped the same way.
1336
1337### Searching and downloading logs
1338
1339The **Search logs** box above a job's steps shows only the lines that hold
1340what you type, in any case, with each match marked and every step that has
1341one opened. Lines inside folded groups are searched too.
1342
1343| Download | Where |
1344| --- | --- |
1345| One job's whole log, as text | The download button beside the job's name. |
1346| Every job's log of an attempt, as a zip | **Download logs** at the top of the run. The zip holds `1_<job>.txt` with each job's whole log, and a `<job>/` folder with `<step>_<step name>.txt` for each step. |
1347
1348A zip holds up to 24 MiB of logs; jobs past that are listed with a note
1349to download them on their own. Each job keeps up to 4 MB of log.
1350
1351### Status badges
1352
1353A badge shows how a workflow's latest finished run went: **passing**,
1354**failing**, **cancelled**, or **no status** before it has finished one.
1355
13561. Open the repository's **Actions** page and pick the workflow.
13572. Click **Create status badge**.
13583. Choose a branch and an event, if you want them, and copy the Markdown.
1359
1360```markdown
1361[![CI](https://g1t.sh/acme/web/actions/workflows/ci.yml/badge.svg)](https://g1t.sh/acme/web/actions?workflow=ci.yml)
1362```
1363
1364The address is `https://g1t.sh/{workspace}/{repo}/actions/workflows/{file}/badge.svg`,
1365where `{file}` is the workflow's file name in `.g1t/workflows/`. It takes:
1366
1367| Parameter | Shows |
1368| --- | --- |
1369| `branch` | Runs on that branch. Without it, the default branch's runs, or any branch's when the default branch has none. |
1370| `event` | Runs started by that event, such as `push` or `pull_request`. |
1371
1372A public repository's badge loads for anyone and is cached for a minute.
1373A private repository's loads only for someone who can see the repository,
1374so it does not show in a README read anywhere else.
1375
1376### Masking secrets
1377
1378Every secret's value is replaced with `***` wherever a job prints it, and
1379so is every value a step masks with `::add-mask::`. Each is masked in the
1380forms it shows up in:
1381
1382| Form | Example |
1383| --- | --- |
1384| As it is | `echo $API_KEY` |
1385| Each of its lines on its own | `cat key.pem`, which prints a private key a line at a time |
1386| Base64 | `echo -n $API_KEY \| base64`, or an `Authorization: Basic` header |
1387| JSON-escaped | A secret holding quotes or newlines printed inside JSON |
1388
1389Annotations' titles and messages, and step names, are masked the same way.
1390A job output that holds a secret in any of those forms is left out, with
1391a warning in the log, since outputs go to other jobs and to the run's page.
1392A value of one character is not masked: it would hide that character
1393everywhere.
1394
1395## Pull requests
1396
1397A pull request's workflows run on each new head: when it is opened, when
1398a commit is pushed to it, and, for one g1t makes, when g1t
1399marks it ready, which on g1t is when it first has code. Each head runs
1400each workflow once.
1401
1402They also start on the activity types `reopened` (also run by
1403default, as `opened` and `synchronize` are), `converted_to_draft`,
1404`ready_for_review`, `labeled`, `unlabeled`, `milestoned`, `demilestoned`,
1405`assigned`, `review_requested` and `closed`, and `edited` when the branch
1406a pull request merges into changes; `issue_comment` workflows on
1407`created`, `edited` (with `github.event.changes.body.from`) and `deleted`; `issues` workflows on `assigned`, `labeled`, `unlabeled`, `milestoned` and
1408`demilestoned` too. List them under `types:` to run on them. For
1409`labeled` and `unlabeled`, `github.event.label` names the label; for
1410`assigned`, `github.event.assignee` names who was assigned. Assigning
1411several people at once starts one run, which names the first of them. A pull
1412request's `branches` filter, `github.base_ref` and
1413`pull_request.base.ref` are the branch it merges into, which is not
1414always the default branch: see
1415[pull requests into other branches](/guides/base-branches/).
1416
1417### What a pull request's run builds
1418
1419As on GitHub, a `pull_request` run builds the pull request merged into
1420its base branch, not its head alone:
1421
1422| | Is |
1423| --- | --- |
1424| `github.ref`, `GITHUB_REF` | `refs/pull/<number>/merge` |
1425| `github.sha`, `GITHUB_SHA` | The test merge commit at `refs/pull/<number>/merge`: the head merged into the base branch's latest commit, its parents the base's commit and then the head, with the message `Merge <head> into <base>` |
1426| `github.event.pull_request.head.sha` | The head commit |
1427| `github.event.pull_request.merge_commit_sha` | The test merge commit |
1428| `actions/checkout` with no `ref` | The test merge commit, fetched from the repository itself as `+<sha>:refs/remotes/pull/<number>/merge` |
1429
1430The workflow files the run uses are read from the test merge commit too.
1431Its status, its check runs and the run itself stay on the pull request's
1432head, so required checks hold the merge as before. `pull_request_review`
1433runs build the same commit. `pull_request_target` runs are unchanged: they
1434build the base branch, never the pull request's code.
1435
1436g1t keeps two refs in the repository for every open pull request, from a
1437branch, a fork or a pull request g1t made: `refs/pull/<number>/head` at
1438its head (a fork's head commit is copied into the repository), and
1439`refs/pull/<number>/merge` at its test merge commit. Both move each time
1440the head is pushed to, the pull request is opened or reopened, or its
1441base branch changes, and both can be fetched like any branch:
1442
1443```sh
1444git fetch origin refs/pull/12/head refs/pull/12/merge
1445```
1446
1447The merge commit moves when the head does. When only the base branch
1448moves, it moves at the pull request's next event, as on GitHub, where
1449workflows do not run again because the base branch moved.
1450
1451**A pull request with conflicts runs no `pull_request` workflows**, as on
1452GitHub: it has no merge commit to build. Its checks box says so, and the
1453runs start once the conflicts are resolved and the head is pushed again.
1454g1t merges the two the way git does: a file only one side changed is
1455taken from it, and changes both made to the same file merge unless they
1456change the same or neighbouring lines differently, or the file is binary.
1457When g1t cannot make the merge itself (a file one side renamed or deleted
1458while the other changed it, a file where the other side has a directory,
1459more than 300 changed files, or a file over 1 MB that both changed), the
1460run builds the head commit instead, as it did before.
1461
1462A pull request from outside the workspace may wait for approval before
1463its workflows run: see [pull requests from outside](#pull-requests-from-outside).
1464
1465`github.event.pull_request` reads as it does on GitHub. For a pull request
1466g1t made, `pull_request.user` is g1t (`login` `g1t`, `type` `Bot`), and
1467`pull_request.requested_by` names the person who asked for it; it is `null`
1468on anyone else's. `github.event.issue.requested_by` does the same for an
1469issue g1t's agent filed. `sender` is whoever caused the event.
1470
1471## The event payload
1472
1473`github.event`, and the file at `GITHUB_EVENT_PATH`, hold the webhook
1474payload GitHub would send for the event, with the same keys and types.
1475
1476- **Ids are numbers.** `repository.id`, `pull_request.id`, `issue.id`,
1477 `review.id`, `sender.id`, every `user.id` and `owner.id` are whole
1478 numbers, the same for an object wherever it appears and below 2^53, so
1479 JavaScript reads them exactly. `node_id` is g1t's own id, such as
1480 `rep_01kpw0…`. A person's or workspace's number comes from their name,
1481 so it changes when they are renamed. `github.repository_id`,
1482 `github.repository_owner_id`, `github.actor_id` and their
1483 `GITHUB_REPOSITORY_ID`, `GITHUB_REPOSITORY_OWNER_ID` and
1484 `GITHUB_ACTOR_ID` variables are the same numbers, as text.
1485- **What the API takes by id keeps g1t's id.** `comment.id`,
1486 `release.id`, `deployment.id`, `deployment_status.id`,
1487 `workflow_run.id` and `workflow_run.workflow_id` are g1t's ids
1488 (`cmt_…`, `rel_…`, `run_…`, `wfl_…`), so you can pass them straight
1489 back, as in `PATCH /repos/{owner}/{repo}/issues/comments/{comment_id}`
1490 or `run-id: ${{ github.event.workflow_run.id }}`.
1491- **`repository.owner.type`** is `User` for a repository in a person's
1492 own workspace and `Organization` for any other workspace's.
1493- **On `push`,** `created` is true for a new branch or tag, and `forced`
1494 is true when the commit before the push is not in the new commit's
1495 history. g1t reads the 20 latest commits to tell, so a forced push onto
1496 a longer history says `false`. `head_commit` is the commit pushed and
1497 `commits` lists at most 20, oldest first.
1498- **On pull request events,** `pull_request.base.sha` is the tip of the
1499 branch it merges into when the event happened, and
1500 `pull_request.merge_commit_sha` is set only once it is merged, to the
1501 commit it landed as.
1502- **On `workflow_run`,** `workflow_run` has the followed run's `name`,
1503 `path`, `event`, `status`, `conclusion`, `head_branch`, `head_sha`,
1504 `head_commit`, `run_number`, `run_attempt`, `actor`, `created_at`,
1505 `updated_at`, `html_url`, `repository` and `pull_requests` (each with
1506 its `number` and `url`).
1507
1508Labels on `issue` and `pull_request` have their `id` and `name` but no
1509`color`; a `labeled` event's own `label` has its color.
1510
1511## Checks
1512
1513A pull request's checks are its workflows. Each workflow that runs on
1514`pull_request` runs for every pull request, whoever opened it, a
1515person or an agent, and its runs report a check named after the workflow:
1516a workflow with `name: CI` reports `CI`, with the status context
1517`CI / pull_request` (the workflow's name and the event). Each of its jobs
1518is a [check run](/guides/checks/) on the commit, shown as
1519`CI / test (pull_request)` beside it wherever it appears.
1520
1521- **Which checks a merge needs** is up to the [rules](/guides/rules/) of the branch it merges into,
1522 their [required status checks](/guides/pull-requests/#required-status-checks),
1523 under **Settings → Rules**. A required check that failed,
1524 is still running or has not reported holds the merge. Checks that are not
1525 required are shown on the pull request and never hold it.
1526- **In a repository that merges through the [merge queue](/guides/merge-queue/)**,
1527 workflows with `on: merge_group` run on each combined state the queue
1528 builds, on the branch `g1t-queue/<entry>`, and the state lands only if
1529 they and every required check pass on it. A workflow behind a required
1530 check needs `merge_group` in its `on:`.
1531- **A pull request g1t is working on** goes back to g1t when
1532 a check fails, with the end of each failed job's log. The agent reads the
1533 run and its logs with the same tools you have, fixes the cause, and
1534 pushes; the workflows run again. See
1535 [seeing it through](/guides/working-with-g1t/#seeing-it-through).
1536
1537```yaml
1538name: CI
1539
1540on:
1541 pull_request:
1542 push:
1543 branches: [main]
1544 merge_group:
1545```
1546
1547### Add CI
1548
1549A repository with no workflows has nothing that proves a change works, for
1550people or for agents. Its pull requests, its **Branches and merging**
1551settings and its **Actions** page say **This repository has no checks**,
1552with an **Add CI** button. Anyone who can push to the repository can use it:
1553
15541. Choose **Add CI**. g1t looks at the files at the repository's root and
1555 writes a starter workflow with a job for each stack it finds, up to
1556 three: Node (npm, pnpm, Yarn or Bun), Rust, Go, Python (pip or uv), Ruby,
1557 Java (Maven or Gradle), .NET, or Make. Each job installs, lints where
1558 the project says how, builds and tests. When it finds none, the job is a
1559 placeholder that fails until you replace its last step with your own
1560 commands.
15612. The workflow is committed as `.g1t/workflows/ci.yml` on a new branch,
1562 `add-ci`, and opened as a pull request, by you. It is named `CI` and runs
1563 on `pull_request`, on `push` to the default branch, and on `merge_group`.
15643. Change it on the pull request if the steps are not how your project
1565 builds, and merge it.
15664. Once it has run, `CI` is offered under **Require status checks to pass
1567 before merging**.
1568 Require it, so that nothing merges into the default branch unless it
1569 passes.
1570
1571## Secrets and variables
1572
1573Secrets are read as `${{ secrets.KEY }}` and config as `${{ vars.KEY }}`,
1574from the rows under **Settings → Secrets and variables** that are
1575available to Workflows. A job with `environment: production` reads each
1576key's Production row; other jobs read the rows for all environments. A
1577job with an `environment:` also makes a deployment to it; see
1578[deployments from g1t Actions](/guides/deployments-api/#deployments-from-g1t-actions). See
1579[Secrets and variables](/guides/secrets-and-variables/) for how rows,
1580environments and the workspace's rows work.
1581
1582Every job also gets `${{ secrets.G1T_TOKEN }}`, [its own token](#the-jobs-token),
1583with `GITHUB_TOKEN` as its alias. A pull request's runs get secrets only
1584when its author has the Write [role](/guides/access-and-roles/) or higher
1585on the repository, a member or an outside collaborator, or is g1t working
1586on its own. For a pull request g1t made, its author is g1t and the person
1587who asked for it is the one whose role counts. Anyone else's, such as one
1588from a fork or by someone with Read or Triage, runs without secrets and
1589with a token that can only read. See
1590[who gets secrets](/guides/secrets-and-variables/#who-gets-secrets).
1591
1592## The job's token
1593
1594Each job gets a token of its own, `${{ secrets.G1T_TOKEN }}`
1595(`${{ secrets.GITHUB_TOKEN }}` and `${{ github.token }}` are the same).
1596`actions/checkout` uses it, and so can any step that calls the
1597[API](/reference/api/) or pushes with git:
1598
1599- It reaches **this repository only**. Every other repository, even one
1600 in the same workspace, is refused. So are packages: it reaches this
1601 repository's own, and another package only once the package's admins
1602 add this repository under its
1603 [Manage Actions access](/guides/packages/#manage-actions-access).
1604- It can do **what its `permissions:` say**, and nothing more.
1605- It **stops working when the job ends**, however it ends.
1606- Everything it changes is in the [audit log](/guides/audit-log/) as that
1607 job's, under its run.
1608- What it changes **starts no workflows**: a push, a pull request, an issue
1609 or a comment made with it runs nothing, so a workflow cannot set itself
1610 off. `workflow_dispatch` and [`repository_dispatch`](#repository-dispatch)
1611 are the exceptions, for a workflow that means to start another.
1612- It **never puts g1t to work**. A comment it posts that mentions
1613 `@g1t` starts nothing, and it cannot assign an issue or a plan to g1t,
1614 queue one for it, hand it work or ask it for a review. Otherwise a
1615 workflow that asks g1t to fix a failing check would run again on g1t's
1616 push, and ask again, without end. A step that should put g1t to work
1617 uses a token of a person's own, stored as a
1618 [secret](/guides/secrets-and-variables/).
1619
1620`permissions:` goes at the top of the workflow, for every job, or on a job,
1621which then ignores the workflow's. Once either is written, every permission
1622it leaves out is `none`:
1623
1624```yaml
1625permissions:
1626 contents: read
1627
1628jobs:
1629 release:
1630 runs-on: ubuntu-latest
1631 permissions:
1632 contents: write
1633 pull-requests: write
1634 steps:
1635 - uses: actions/checkout@v5
1636 - run: ./scripts/release.sh
1637```
1638
1639| Permission | `read` lets it | `write` also lets it |
1640| --- | --- | --- |
1641| `contents` | Clone and fetch with git, read the repository | Push, publish releases |
1642| `pull-requests` | Read pull requests | Open, review, close and merge them |
1643| `issues` | Read issues | Open, edit, comment on and close them |
1644| `actions` | Read workflows, runs and logs | Run, cancel and re-run them |
1645| `checks`, `statuses` | Read statuses and check runs | Report them |
1646| `deployments`, `pages` | Read deployments | Report them |
1647| `packages` | Pull packages | Push and publish them |
1648| `security-events` | Read security alerts | Upload code scanning results, change alerts |
1649| `metadata` | Always `read` | |
1650| `id-token` | Nothing | Ask for an [OIDC token](#oidc-tokens) |
1651| `discussions`, `attestations`, `models`, `repository-projects` | Nothing on g1t | Nothing on g1t |
1652
1653`read-all` and `write-all` set every permission; `permissions: {}` sets
1654none, so the token cannot even clone a private repository. A reusable
1655workflow's jobs get no more than the job that calls it.
1656
1657There is no `workflows` permission for a job's token: it can never add,
1658change or delete a file under `.g1t/workflows/` or `.github/workflows/`,
1659even with `contents: write`. A push that does is declined, naming the file,
1660so a workflow cannot rewrite the workflows that run with its repository's
1661secrets. To change workflows from a job, push with a
1662[personal access token](/guides/authentication/#workflow-files) that has
1663Workflow files: write, kept as a secret.
1664
1665The job's token is the repository's workspace acting with the Write role
1666at most, never Admin: it cannot manage webhooks, secrets, deploy keys or who
1667has access, whatever it asks for.
1668
1669**Without `permissions:`** a job gets the repository's default, which
1670someone with the Admin role sets under **Settings → Actions**:
1671
1672| Repository | Default until someone chooses |
1673| --- | --- |
1674| Made before restricted tokens came in, in October 2026 | **Read and write**: every permission at `write`, as before |
1675| Made since | The workspace's default for new repositories: **Read repository contents and packages** (`contents: read`, `packages: read`) unless an owner chose otherwise |
1676
1677The workspace's owners set that default, and a **maximum**, under the
1678workspace's **Settings → Actions**: with a maximum of **Read only**, no
1679repository's default goes past `contents: read` and `packages: read`,
1680whatever it chose. Workflows that write `permissions:` get what they
1681write either way, and whatever a workflow asks for, a pull request from
1682outside the repository's writers (a fork, or someone with Read or Triage)
1683gets a token that can only read.
1684
1685**Allow g1t Actions to create and approve pull requests** is off unless a
1686repository's admin turns it on under **Settings → Actions**, and they can
1687only where the workspace's owners allow it. Until then a job's token
1688cannot open a pull request or approve one, whatever its `pull-requests`
1689permission says; it can still read, comment on, review with changes
1690requested, and merge them.
1691
1692The token can never change secrets, variables, environments' rules or
1693the Actions settings, approve runs or deployments, or reach another
1694repository.
1695
1696## Environments
1697
1698A job that names an environment with `environment:` reads that
1699environment's [secrets and variables](/guides/secrets-and-variables/#a-value-per-environment)
1700and records a [deployment](/guides/deployments-api/#deployments-from-g1t-actions)
1701to it. Give the environment protection rules, and such a job waits until
1702they let it through, and only then gets the environment's secrets:
1703
1704| Rule | What it does |
1705| --- | --- |
1706| **Required reviewers** | Up to 6 people or teams. The job waits until one of them approves it. |
1707| **Prevent self-review** | Whoever started the run cannot approve it, even as a reviewer. |
1708| **Wait timer** | Minutes the job waits once it reaches the environment, up to 43,200 (30 days). |
1709| **Deployment branches and tags** | **All branches**; **Protected branches only**, those the repository's [rules](/guides/rules/) protect, the default branch included; or **Selected branches and tags**, by pattern, such as `main`, `release/*` or `v*`. A job on any other ref fails, saying so. A pull request's run is on no branch, so it can deploy only where all branches may. |
1710| **Allow admins to bypass** | On unless you turn it off: someone with the Admin role may approve without being a reviewer, which also skips the wait timer. |
1711
1712To set them:
1713
17141. Open the repository's **Settings → Environments**. It lists every
1715 environment your workflows, secrets and deployments name.
17162. Choose one, or name a new one, and set its rules.
17173. Save. Runs that reach the environment from then on wait by them.
1718
1719A run whose jobs wait shows **Waiting for review** at the top of its page,
1720with the environments, the jobs each holds, its reviewers and when its
1721wait timer runs out. Reviewers are told in their [notifications](/guides/notifications/);
1722on the run's page they choose **Approve and deploy** or **Reject**, with
1723room for a comment. One review covers every job of the run that names the
1724environment. A rejected job fails, and so does anything that needs it. The
1725rest of the run goes on meanwhile: jobs that do not need the waiting ones
1726run.
1727
1728The environment's name may be an expression, such as
1729`environment: ${{ inputs.target }}`: it is read once the job's needs are
1730done, and its rules and secrets are that environment's. Names are matched
1731without regard to case.
1732
1733From the API, `PUT /repos/{owner}/{repo}/environments/{environment}` sets
1734the rules, with `reviewers`, `prevent_self_review`, `wait_timer`,
1735`deployment_branch_policy`, `branch_policies` and `can_admins_bypass`;
1736`GET` on the same route returns them as `protection_rules`; `DELETE`
1737removes them. `POST /repos/{owner}/{repo}/actions/runs/{id}/pending_deployments`
1738approves or rejects a run's waiting jobs:
1739
1740```sh
1741curl -X PUT https://api.g1t.sh/repos/acme/web/environments/production \
1742 -H "Authorization: Bearer $G1T_TOKEN" -H "Content-Type: application/json" \
1743 -d '{"reviewers": [{"type": "Team", "name": "deployers"}], "wait_timer": 10,
1744 "deployment_branch_policy": {"protected_branches": true, "custom_branch_policies": false}}'
1745```
1746
1747A job's own token cannot approve, reject or change any of it.
1748
1749## Pull requests from outside
1750
1751A pull request from someone outside the workspace runs code anyone could
1752have written. By the repository's **approval policy**, its runs wait as
1753**Approval required** until someone with the Write
1754[role](/guides/access-and-roles/) chooses **Approve and run** on the run's
1755page. Nothing runs before then: no job starts, and no token or secret is
1756handed out.
1757
1758| Policy, under **Settings → Actions** | Whose pull requests' runs wait |
1759| --- | --- |
1760| **First-time contributors** | Someone outside the workspace who has not had a pull request merged here yet. |
1761| **Outside contributors** (the default) | Those, and everyone outside the workspace who cannot push here: pull requests from forks, and from people with Read or Triage. |
1762| **All external contributors** | Everyone outside the workspace, [outside collaborators](/guides/access-and-roles/#outside-collaborators) with Write included. |
1763
1764Members' pull requests never wait, nor do pull requests g1t opens on its
1765own. For a pull request g1t made for someone, that person is the one whose
1766policy counts. Each new push to the pull request waits again.
1767`pull_request_target` runs, which run the default branch's workflow and
1768code, never wait.
1769
1770From the API: `POST /repos/{owner}/{repo}/actions/runs/{id}/approve`
1771approves a run, and `GET` and `PUT
1772/repos/{owner}/{repo}/actions/permissions/fork-pr-contributor-approval`
1773read and set the policy, as `approval_policy`.
1774
1775## Repository dispatch
1776
1777`POST /repos/{owner}/{repo}/dispatches` starts the default branch's
1778workflows that run `on: repository_dispatch` for its `event_type`, those
1779listing it under `types:` or listing none. `client_payload` is theirs to
1780read as `github.event.client_payload`:
1781
1782```yaml
1783on:
1784 repository_dispatch:
1785 types: [docs-published]
1786
1787jobs:
1788 announce:
1789 runs-on: ubuntu-latest
1790 steps:
1791 - run: echo "Docs ${{ github.event.client_payload.version }} are out"
1792```
1793
1794```sh
1795curl -X POST https://api.g1t.sh/repos/acme/web/dispatches \
1796 -H "Authorization: Bearer $G1T_TOKEN" -H "Content-Type: application/json" \
1797 -d '{"event_type": "docs-published", "client_payload": {"version": "2.4.0"}}'
1798```
1799
1800It needs the Write role, or a token with `code:write`; a job's own token
1801needs `contents: write`. `client_payload` is a JSON object of at most 10
1802properties and 64 KB.
1803
1804## Who may run workflows
1805
1806What you can do with a repository's workflows follows your
1807[role](/guides/access-and-roles/) on it:
1808
1809| | Needs |
1810| --- | --- |
1811| See workflows, runs and their logs | Read: on a public repository, anyone |
1812| Run a workflow by hand, cancel or re-run a run, approve a pull request's run from outside | Write |
1813| Approve or reject a job waiting for an environment | One of the environment's reviewers |
1814| Enable or disable a workflow | Maintain |
1815| The repository's secrets and variables, seeing them included; environments' rules; **Settings → Actions** | Admin |
1816
1817Jobs run in g1t's sandboxes, so they need the
1818[g1t plan](/guides/usage-and-billing/#the-g1t-plan) or
1819[the trial](/guides/usage-and-billing/#the-trial); jobs on
1820[self-hosted runners](/guides/self-hosted-runners/#billing) need neither.
1821On a public repository,
1822[g1t's open-source pool](/guides/usage-and-billing/#the-open-source-pool)
1823runs them too, after a card check, until the month's pool is spent.
1824
1825Before each job starts, g1t reserves what it may cost (its time limit at
1826the sandbox price) with billing, and settles what it really cost when it
1827ends; each job's sandbox is charged as
1828[sandbox time](/guides/usage-and-billing/#sandbox-time), from the first
1829second. A job billing refuses does not start: it is recorded as failed
1830with "Not started:" and the reason, such as "Workflows run in g1t's
1831sandboxes, which cost real money, so they need the g1t plan ($20 a month)
1832or a card check", and what to do about it. The
1833Actions page tells people with Write on a repository whose workspace
1834cannot run jobs before the first run.
1835
1836## From the API
1837
1838The routes follow the standard Actions REST shape, so existing scripts
1839usually work once they point at `https://api.g1t.sh`.
1840
1841| `workflow` action | Route |
1842| --- | --- |
1843| `list` | `GET /repos/{owner}/{repo}/actions/workflows` |
1844| `list_runs` | `GET /repos/{owner}/{repo}/actions/runs`, with `workflow`, `branch`, `event`, `pull`, `head_sha` |
1845| `get_run` | `GET /repos/{owner}/{repo}/actions/runs/{id}` |
1846| `job_logs` | `GET /repos/{owner}/{repo}/actions/jobs/{job}/logs?after=`, or `?format=text` for the whole log as plain text |
1847| `get_run` with `attempt` | `GET /repos/{owner}/{repo}/actions/runs/{id}/attempts/{attempt}` |
1848| No tool: a download | `GET /repos/{owner}/{repo}/actions/runs/{id}/logs`, or `…/attempts/{attempt}/logs`: every job's log as a zip |
1849| `dispatch` | `POST /repos/{owner}/{repo}/actions/workflows/{workflow}/dispatches` with `ref` and `inputs` |
1850| `cancel` | `POST /repos/{owner}/{repo}/actions/runs/{id}/cancel`; `…/force-cancel`, or `force`, to stop running jobs without their cleanup steps |
1851| `rerun` | `POST …/runs/{id}/rerun`, or `…/rerun-failed-jobs`; one job and those that need it with `POST /repos/{owner}/{repo}/actions/jobs/{job}/rerun`. Each takes `enable_debug_logging` (or `debug`) |
1852| `update` | `PUT …/workflows/{workflow}/enable` and `…/disable` |
1853| `approve_run` | `POST /repos/{owner}/{repo}/actions/runs/{id}/approve` |
1854| `pending_deployments` | `GET /repos/{owner}/{repo}/actions/runs/{id}/pending_deployments` |
1855| `review_deployments` | `POST /repos/{owner}/{repo}/actions/runs/{id}/pending_deployments` with `environment_names`, `state` and `comment` |
1856| `get_environment` | `GET /repos/{owner}/{repo}/environments/{environment}` |
1857| `update_environment` | `PUT /repos/{owner}/{repo}/environments/{environment}` |
1858| `delete_environment` | `DELETE /repos/{owner}/{repo}/environments/{environment}` |
1859| `get_permissions`, `set_permissions` | `GET` and `PUT /repos/{owner}/{repo}/actions/permissions/workflow`, with `default_workflow_permissions` (`read`, `write` or `inherit`) and `can_approve_pull_request_reviews` |
1860| `get_workspace_permissions`, `set_workspace_permissions` | `GET` and `PUT /workspaces/{workspace}/actions/permissions/workflow`, with `default_workflow_permissions`, `max_workflow_permissions` and `can_approve_pull_request_reviews` |
1861| `get_approval_policy`, `set_approval_policy` | `GET` and `PUT /repos/{owner}/{repo}/actions/permissions/fork-pr-contributor-approval` |
1862| `get_access`, `set_access` | `GET` and `PUT /repos/{owner}/{repo}/actions/permissions/access`, with `access_level` (`none` or `organization`) |
1863| `repository_dispatch` | `POST /repos/{owner}/{repo}/dispatches` with `event_type` and `client_payload` |
1864| `list_artifacts` | `GET /repos/{owner}/{repo}/actions/artifacts`, with `name`, `page`, `per_page` |
1865| `run_artifacts` | `GET …/actions/runs/{id}/artifacts`, with `name` |
1866| `get_artifact` | `GET …/actions/artifacts/{artifact_id}` |
1867| `download_artifact` | `GET …/actions/artifacts/{artifact_id}/zip`: a `302` to a link good for 10 minutes |
1868| `delete_artifact` | `DELETE …/actions/artifacts/{artifact_id}` |
1869| `artifact_retention`, `set_artifact_retention` | `GET` and `PUT …/actions/permissions/artifact-and-log-retention` with `days` (it sets artifacts' days only; logs are kept with their run) |
1870
1871Secrets and variables have a tool of their own, `secret`:
1872
1873| `secret` action | Route |
1874| --- | --- |
1875| `list_secrets`, `set_secret`, `delete_secret` | `GET /repos/{owner}/{repo}/actions/secrets`, `PUT` and `DELETE …/secrets/{name}` |
1876| `list_variables`, `set_variable`, `delete_variable` | `GET` and `POST /repos/{owner}/{repo}/actions/variables`, `PATCH` and `DELETE …/variables/{name}` |
1877
1878Workspace secrets and variables are under
1879`/workspaces/{workspace}/actions/secrets` and `…/variables`. The fields
1880g1t adds (environments, who reads a row, linked repositories) are in
1881[Secrets and variables](/guides/secrets-and-variables/#from-the-api).
1882
1883```sh
1884curl -X POST https://api.g1t.sh/repos/acme/web/actions/workflows/ci.yml/dispatches \
1885 -H "Authorization: Bearer $G1T_TOKEN" -H "Content-Type: application/json" \
1886 -d '{"ref": "main", "inputs": {"environment": "staging"}}'
1887```
1888
1889Give only the inputs the workflow declares under
1890`on.workflow_dispatch.inputs`: a run with any other input is refused, and
1891the answer names the inputs it did not expect. A `boolean` input is `true`
1892or `false`, a `number` a number, a `choice` one of its `options`, and an
1893`environment` the name of one of the repository's
1894[environments](#environments). In the run, the `inputs` context keeps
1895booleans and numbers as they are, and `github.event.inputs` has every
1896input as text, `"true"` and `"3"`, as on GitHub.
1897
1898To save an artifact from a script, follow the redirect:
1899
1900```sh
1901curl -L -o web-dist.zip -H "Authorization: Bearer $G1T_TOKEN" \
1902 https://api.g1t.sh/repos/acme/web/actions/artifacts/4182/zip
1903```