# Run your GitHub Actions workflows on GitCafe

GitCafe runs the workflows in your repository's `.github/workflows/`
directory with the same YAML you use on GitHub. Jobs run on the official
GitHub Actions runner: on `ubuntu-latest` they get a fresh GitCafe-hosted
Linux VM, and on your own Linux, macOS or Windows machines, including
hardware-in-the-loop rigs, they run on [self-hosted runners](https://git.cafe/docs/ci/self-hosted-runners.md).
Results show on the repository's **CI** tab and as checks on pull
requests.

GitCafe reads workflows with GitHub's own parser and expression engine. Most
workflows run unchanged; [what's supported](https://git.cafe/docs/ci/workflow-syntax.md) lists the
exceptions, and [the table below](https://git.cafe/docs/ci/actions.md#what-works-differently) the actions that
need GitHub itself.

## Turn on Actions

Coming from GitHub? Follow [Move CI from GitHub](https://git.cafe/docs/ci/migrate-from-github.md),
including how to avoid duplicate runs while syncing.

Jobs run on your [self-hosted runners](https://git.cafe/docs/ci/self-hosted-runners.md).
GitCafe-hosted runners are in beta for organizations GitCafe admits; their
setting appears in **Settings → CI** once they're available to yours.

Actions is off for every repository until a repository admin turns it on:

1. Open the repository's **Settings → CI**.
2. Check **Enable Actions**.
3. If **Use GitCafe-hosted runners** is shown, keep it checked for
   `ubuntu-latest` jobs, or uncheck it to run only on your own runners.
4. Choose **Save Actions settings**.

```sh
cafe ci enable            # --no-hosted to use only your own runners
cafe ci settings
```

Turning Actions off stops runs that started before: running jobs are
cancelled and jobs that had not started don't start, even if you turn Actions
on again. Rerun them afterwards. Turning Actions on does not run anything by
itself: the next push, pull request update or dispatch does.

## Your first workflow

Add `.github/workflows/ci.yml`, or keep the one you already have:

```yaml
name: CI

on:
  push:
    branches: [main]
  pull_request:

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 22
      - run: npm ci
      - run: npm test
```

Push it: the run appears on the **CI** tab within a minute. If the
workflow has a mistake, you get a failed run whose page lists each problem
and where it is, instead of silence.

## Where jobs run

`runs-on` picks a runner by its **labels**: a job runs on a runner that has
every label it lists (letter case doesn't matter).

- **GitCafe-hosted runners** serve `ubuntu-latest`, `ubuntu-24.04` and
  `ubuntu-22.04`. Each job gets a new Linux x64 VM with Docker and the
  official runner, which is deleted after the job. The image has Node,
  Python, Git and the usual build tools, but not everything GitHub's images
  have; `setup-*` actions download what's missing. A repository uses them
  while **Use GitCafe-hosted runners** is on, if the server offers them:
  **Settings → CI** lists the labels it serves.
- **Self-hosted runners** are your machines. Each has the labels
  `self-hosted`, its operating system (`Linux`, `macOS` or `Windows`), its
  architecture (`X64` or `ARM64`) and any you add when you configure it, so
  `runs-on: [self-hosted, macOS, ARM64]` or `runs-on: [self-hosted, hil]`
  work as on GitHub. A repository can use the runners registered on it and
  those of its organization's [runner groups](https://git.cafe/docs/ci/self-hosted-runners.md#runner-groups)
  that it has access to. Labels choose a runner; they never grant access.

**Settings → CI** shows every runner the repository can use, with its
labels and status. A job that no runner matches waits, marked as waiting for
a runner, until one with the right labels comes online. A run waits at most
24 hours for runners, on top of its jobs' time limits, then ends as **Timed
out**.

## Triggers

GitCafe reads the workflow files at the commit being tested, so a change to a
workflow applies to the commit that makes it. Three events run workflows:

- **`push`** runs for each branch a push creates or updates, whether you push
  with Git or GitCafe updates the branch, such as when you merge a pull
  request. Deleting a branch and pushing tags run nothing. `branches`,
  `branches-ignore`, `paths` and `paths-ignore` filters work as on GitHub.
- **`pull_request`** runs when a pull request is opened or reopened, and each
  time its head branch gets a new commit. It tests the **head commit** of the
  pull request, not a merge with the base branch: `GITHUB_SHA` is the head
  commit and `GITHUB_REF` is `refs/pull/<number>/head`. `branches` filters
  match the base branch and `paths` filters the changed files. GitCafe
  reports every pull request event as the `synchronize` activity type, so a
  `types` list must include `synchronize` (the default list does).
- **`workflow_dispatch`** runs a workflow on demand, at the commit a branch or
  tag points to now. You need write access to the repository. Dispatch it
  with the Cafe CLI; the web UI has no **Run workflow** button yet:

  ```sh
  cafe ci dispatch .github/workflows/ci.yml --ref main
  ```

  Inputs take their `default` values; you can't type input values yet.

A workflow may list other events, such as `schedule` or `release`: GitCafe
accepts the file and runs it only for the three events above.

## Checks on pull requests

Every run reports checks on its commit, next to Buildkite checks and commit
statuses ([read CI results](https://git.cafe/docs/ci/checks-and-logs.md)):

- one check per job, named `<workflow name> / <job name>`, such as
  `CI / test` or, for a matrix, `CI / test (22)`;
- one check per run, named `<workflow name>`, which also reports when the
  workflow could not be read.

Each check links to its run. To require a check before merging, add its exact
name to a [ruleset](https://git.cafe/docs/repositories/repository-rulesets.md)'s required status
checks. When you rerun a workflow, the new run's checks replace that run's
earlier ones.

## Secrets and variables

`${{ secrets.NAME }}` and `${{ vars.NAME }}` work as on GitHub: add them in
**Settings → Secrets and variables** of a repository or an organization
([secrets and variables](https://git.cafe/docs/ci/secrets-and-variables.md)). Every job also gets a
`GITHUB_TOKEN` that can read its own repository, valid while the job runs;
`actions/checkout` uses it to clone from GitCafe.

## Follow runs and read logs

The repository's **CI** tab lists workflow runs and Buildkite builds
together, newest first, with filters for source, status and branch (and event
for workflow runs). A run's page shows its jobs by stage, with a
matrix's combinations together under their job, each job's steps with their
results, and its log, which updates while the job runs.

```sh
cafe ci list --branch main               # workflow runs and Buildkite builds
cafe ci list --source actions --pr 42    # workflow runs at a pull request's head
cafe ci view <run-id>
cafe ci logs <run-id>                    # the first failed job
cafe ci logs <run-id> --job test --tail 50
```

Anyone who can read the repository can see its runs and logs, including
anonymous visitors to a public repository. GitCafe keeps up to 4 MiB of each
job attempt's log; past that, the oldest output is dropped.

## Cancel or rerun a run

You need write access to the repository.

- **Cancel** stops a run that hasn't finished. Jobs that haven't started are
  cancelled at once; running jobs are told to stop.
- **Rerun** starts a new run of a finished run at the same commit, with the
  same workflow, and runs every job again. To run a changed workflow, push a
  new commit instead.

```sh
cafe ci cancel <run-id>
cafe ci rerun <run-id>
```

GitCafe never reruns a job by itself. If a runner disappears mid-job, the job
fails once its runner has been silent for a few minutes; rerun it when you're
ready.

## Coming from the GitHub CLI

`cafe ci` covers the repository's CI: GitCafe Actions workflow runs and
Buildkite builds, like the **CI** tab (`cafe actions` is an alias). Runs are
addressed by id (`actwfrun_…` for a workflow run, from `cafe ci list`), not by
run number.

| `gh`                                     | `cafe`                                                                                 |
| ---------------------------------------- | -------------------------------------------------------------------------------------- |
| `gh run list`                            | `cafe ci list --source actions` (`--branch`, `--event`, `--status`, `--pr`, `--limit`) |
| `gh run view <run>`                      | `cafe ci view <run-id>`                                                                |
| `gh run view <run> --log`                | `cafe ci logs <run-id> --job <job> --full` (one job at a time)                         |
| `gh run view <run> --log-failed`         | `cafe ci logs <run-id>` (the first failed job, last 200 lines)                         |
| `gh run watch <run>`                     | `cafe ci watch <run-id>` (exits 8 unless the run succeeded)                            |
| `gh run cancel <run>`                    | `cafe ci cancel <run-id>`                                                              |
| `gh run rerun <run>`                     | `cafe ci rerun <run-id>` (every job)                                                   |
| `gh workflow run <workflow> --ref <ref>` | `cafe ci dispatch <workflow>.yml --ref <ref>`                                          |
| `gh secret set NAME [--org ORG]`         | `cafe ci secret set NAME [--org ORG]` (value from `--body`, `--env` or stdin)          |
| `gh variable set NAME [--org ORG]`       | `cafe ci variable set NAME [--org ORG]` (value from `--body`, `--env` or stdin)        |
| `gh pr checks <pr>`                      | `cafe pr status <pr>` (every check: workflow jobs, Buildkite and commit statuses)      |

What's missing:

- No rerun of only the failed jobs (`gh run rerun --failed`, `--job`): `rerun` runs
  every job again.
- No dispatch inputs (`gh workflow run -f`/`--json`): inputs take their defaults.
- `cafe ci list` has no `--workflow` or `--user` filter. `--branch` matches
  runs on that branch's ref; pull request runs are on
  `refs/pull/<number>/head`, so find them with `cafe ci list --pr <pr>` (the
  runs at the pull request's current head) or `cafe ci status --pr <pr>`.
- For organization secrets and variables, `--visibility` and `--repos` take
  repository names of that organization (`name` or `org/name`); without them,
  `set` keeps an existing one's access and creates a new one as `private`.

## Pull requests from forks

Workflows do not run for pull requests from forks, and `pull_request_target`
is not one of the events GitCafe runs. A fork's run is recorded with the
result **Action required** and its check says that workflows do not run for
pull requests from forks; it cannot be rerun. This applies whatever the
repository's fork setting is.

## What works differently

Some actions and features depend on github.com or on services GitCafe doesn't
offer yet:

| What                                                                                         | On GitCafe                                                                                                                                                                                  |
| -------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `actions/upload-artifact@v4`, `actions/download-artifact@v4`                                 | Fail: their own code refuses servers other than github.com. Artifacts are not supported yet; publish files elsewhere, such as a release or object storage.                                  |
| `actions/cache`                                                                              | No cache service yet: the cache step finds nothing to restore.                                                                                                                              |
| OIDC (`permissions: id-token: write`)                                                        | Not supported yet: jobs get no OIDC token, so cloud login actions that use one fail. Use a secret instead.                                                                                  |
| Pull requests from forks, `pull_request_target`                                              | Don't run ([above](https://git.cafe/docs/ci/actions.md#pull-requests-from-forks)).                                                                                                                                             |
| Actions that call the GitHub REST API (`actions/github-script`, release and comment actions) | `GITHUB_TOKEN` and `github.api_url` point at GitCafe, which implements only a few endpoints (reading a repository), and the token can only read. Calls to anything else fail.               |
| Third-party runners (Depot, Namespace, Blacksmith, ...)                                      | Their `runs-on` labels never match: those services serve github.com only. See [runner providers](https://git.cafe/docs/ci/runner-providers.md).                                                                      |
| Deployment environments (`environment:`)                                                     | The environment's name is shown on the job, but there are no protection rules, environment secrets or variables: the job runs at once.                                                      |
| Actions from repositories                                                                    | `uses: owner/repo@ref` downloads the action from public github.com. An action whose `owner/repo` is also a GitCafe repository is refused for now. Private github.com actions can't be used. |

## Limits

- A job runs for at most its `timeout-minutes` (default and maximum 360),
  then ends as **Timed out**.
- A run waits at most 24 hours for runners, on top of its jobs' time limits.
- More in [what's supported](https://git.cafe/docs/ci/workflow-syntax.md#limits).
