# Workflows gitfor runs GitHub Actions–style workflows from `.gitfor/workflows/*.yml`. ```yaml # .gitfor/workflows/ci.yml name: CI on: push: branches: [main] pull_request: jobs: test: runs-on: cloudflare strategy: matrix: node: [22, 24] steps: - uses: actions/checkout@v4 - run: bun install && bun test - uses: actions/upload-artifact@v4 with: name: coverage-${{ matrix.node }} path: coverage deploy: needs: test if: github.ref == 'refs/heads/main' runs-on: [self-hosted, linux] steps: - run: ./deploy.sh env: TOKEN: ${{ secrets.DEPLOY_TOKEN }} ``` ## Triggers `push` (branches, tags and paths filters), `pull_request` (types, branches, paths), `release` and `workflow_dispatch` (with inputs). ## Steps - `run` scripts in bash, sh, python, node or bun - Built-in `checkout`, `cache` and `upload-artifact` - `setup-node` and `setup-bun` (built in unless a version is requested) - JavaScript actions from public github.com repositories, or from the repository itself (`uses: ./path`) Job status is published as commit statuses named ` / `, which branch protection can require. CI jobs fetch only the commit they run on (depth 1). Set `GITFOR_FETCH_DEPTH` in a workflow's `env` to fetch more history, or to `0` for a full fetch. ## Third-party actions - A tag or branch is resolved to its commit when the run is scheduled. That SHA is stored with the run, shown in the log ("Download action repository … (SHA: …)") and used for every retry. - A full commit SHA is accepted only if a branch or tag of the named repository reaches it, so a commit that only exists in a fork can't pose as the repository. Short SHAs are refused. - Code is fetched over git and every object is checked against its SHA. The archive is stored once per commit and the runner verifies its sha256 before extracting. Actions run only inside the job's runner, never in the Worker. - **Repository settings → Allowed actions** chooses between any public action, only SHA-pinned actions, or only built-in ones. - `${{ github.token }}` written in a step's `with:` reads the `GITHUB_COM_TOKEN` secret (a github.com token, for actions that call the GitHub API). An action's own `${{ github.token }}` defaults get an empty value, so no action receives that secret unless the workflow passes it. Fork pull request runs get no secrets. - Composite and Docker actions are not supported yet. A pin covers the action's code, not what it downloads while it runs. ## Self-hosted runners Create a registration token under **Repository settings → Runners** (or `/orgs//runners` for owner-wide runners), choosing the labels and the isolation. Then, on a host with Docker or Podman: ```sh gfd runner register --url https://gitfor.dev --token gitfor_reg_... gfd runner run ``` Each job runs in a fresh, non-root container with a read-only root filesystem and an egress firewall that blocks cloud metadata and private networks. Fork pull request jobs never reach self-hosted runners unless a repository admin opts in, and then only runners whose container isolation the admin declared on the token. See the gfd README for what is and isn't isolated. The old `runner/gitfor-runner.ts` is retired.