# Workflow syntax

The keys of a workflow file in `.rebasely/workflows/`. It is close to GitHub Actions' syntax, with a smaller and stricter set of keys: a file with a key Rebasely does not know fails with the line and column, before any job starts.

> **Not GitHub Actions:** Differences from GitHub Actions: only Rebasely's built-in actions (`duragit/*@v1`) can be used, `${{ }}` is never allowed inside `run:` (pass values through `env:`), steps always run as `bash -eo pipefail` in `/workspace`, and the context is `duragit`, not `github`.

## The file

| Key | What it is |
| --- | --- |
| `name` | The workflow's name, shown in the Actions tab |
| `on` | What starts it (required): an event, a list of events or a map of them |
| `env` | Variables for every job |
| `concurrency` | A group name, or `{group, cancel-in-progress}`: one run of the group at a time |
| `jobs` | The jobs, by id (at least one) |

Plain YAML only: anchors, aliases, tags and duplicate keys are refused. At most 20 files per repository, 64 KiB each.

## on

```yaml
on:
  push:
    branches: [main, "release/**"]
    tags: ["v*"]
    paths: ["src/**", "!src/**/*.md"]
    checkpoints: false        # live-branch checkpoints, at most once every 5 minutes
  pull_request:
    branches: [main]          # the base branch
    types: [opened, synchronize, reopened]
    rerun-on-base-change: true
  schedule:
    - cron: "30 4 * * 1-5"    # UTC
  workflow_dispatch:
    inputs:
      target: { type: choice, options: [staging, production], default: staging }
  sandbox:
    paths: ["src/**"]
```

- Patterns: `*` within one part of a path, `**` across parts, `!` to exclude; later patterns win. Use `branches-ignore`, `tags-ignore` and `paths-ignore` instead of `!` if you prefer, but not both for the same filter.
- `pull_request` types are `opened`, `synchronize` and `reopened`; `closed` is refused for now.
- Schedules: at most 5 per workflow, at most every 5 minutes. A scheduled run uses the default branch's latest commit.
- `workflow_dispatch` inputs are `string`, `choice`, `boolean` or `number`, at most 25. Steps see them as `DURAGIT_INPUT_<NAME>`.

## Jobs

```yaml
jobs:
  test:
    runs-on: { image: node24, instance: standard-1 }
    timeout-minutes: 30
    strategy:
      matrix:
        node-env: [development, production]
    env:
      NODE_ENV: ${{ matrix.node-env }}
    steps:
      - uses: duragit/checkout@v1
      - run: npm ci && npm test
  deploy:
    needs: test
    if: duragit.ref == 'refs/heads/main'
    environment: production
    runs-on: { image: deploy, instance: standard-1 }
    steps:
      - uses: duragit/checkout@v1
      - uses: duragit/wrangler@v1
        with:
          args: [deploy]
        env:
          CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
```

| Key | What it is |
| --- | --- |
| `runs-on` | An image, or `{image, instance}`; default `node24` on `basic` |
| `needs` | Jobs that must finish first |
| `if` | A condition; the job is skipped when it is false |
| `timeout-minutes` | 1 to 350, default 60, and at most the plan allows |
| `strategy.matrix` | Axes, `include` and `exclude`; at most 64 combinations |
| `environment` | A deployment environment, with its rules and approvers |
| `concurrency` | One job of the group at a time |
| `permissions` | `contents: read\|write` (write lets a trusted job push) and `statuses: write\|none` |
| `egress` | More hostnames the job may reach |
| `env`, `outputs`, `steps` | Variables, values for jobs that need this one, and the steps (at most 100) |

Not supported: `services`, `container`, `defaults`, reusable workflows, and `fail-fast` or `max-parallel` in a strategy.

## Steps

| Key | What it is |
| --- | --- |
| `run` | A bash script, run with `bash -eo pipefail` in `/workspace` |
| `uses`, `with` | A built-in action and its inputs |
| `id`, `name`, `if`, `env` | An id for its outputs, a name, a condition, variables |
| `timeout-minutes`, `continue-on-error` | A shorter deadline; keep going when it fails |

A step sets outputs by appending `name=value` lines to `$DURAGIT_OUTPUT`; later steps read them as `steps.<id>.outputs.<name>`.

## Built-in actions

| Action | What it does |
| --- | --- |
| `duragit/checkout@v1` | Checks out the commit. `fetch-depth` (1 by default, 0 for all history), `filter`, `path`, `submodules`, `ref` (a full SHA of this repository). |
| `duragit/cache@v1` | Restores and saves directories by `key` and `key-files`; `paths` is required. Output `cache-hit`. |
| `duragit/upload-artifact@v1` | Keeps files from the run: `name`, `path`, `if-no-files-found`. |
| `duragit/download-artifact@v1` | Gets an artifact of the same run. |
| `duragit/wrangler@v1` | Runs wrangler with `args`, on the `deploy` image. |

## Expressions

`${{ }}` works in `if`, `env` and `with` values, `name`, `outputs`, `concurrency` and the image of `runs-on`. The contexts are `duragit`, `inputs`, `matrix`, `needs`, `steps`, `env` and `secrets` (in `env` and `with` only). Functions: `contains`, `startsWith`, `endsWith`, `format`, `join`, `toJSON`, `fromJSON`, `success`, `failure`, `always` and `cancelled`.

`duragit` has `event`, `ref`, `ref_name`, `ref_type`, `sha`, `before`, `repository`, `org`, `actor`, `workflow`, `default_branch`, `run_id` and, for pull requests, `pull_request` (`number`, `head_ref`, `head_sha`, `base_ref`, `base_sha`).

## Variables a step sees

`CI=true`, `DURAGIT_ACTIONS=true`, `DURAGIT_EVENT`, `DURAGIT_REF`, `DURAGIT_SHA`, `DURAGIT_BEFORE`, `DURAGIT_ACTOR`, `DURAGIT_REPOSITORY`, `DURAGIT_WORKFLOW`, `DURAGIT_JOB`, `DURAGIT_RUN_ID`, `DURAGIT_ATTEMPT`, `DURAGIT_WORKSPACE`, `DURAGIT_OUTPUT` and `DURAGIT_MASK`; on pull requests also `DURAGIT_PR_NUMBER`, `DURAGIT_HEAD_REF`, `DURAGIT_HEAD_SHA`, `DURAGIT_BASE_REF` and `DURAGIT_BASE_SHA`. Names starting with `DURAGIT_` are reserved.

---

Source: https://rebasely.com/docs/workflow-syntax
