Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ A workflow file mixes two things. The orchestration (triggers, job graph, matrix

**Run directly.** `run` executes the pipeline through an in-process runner that asks an injected executor to run each command. Nothing is generated. A single job, a step range or one matrix combination can be run alone. Around each step the runner applies the provider's file commands, as a hosted runner does: the step writes to `GITHUB_OUTPUT`, `GITHUB_ENV`, `GITHUB_PATH` and `GITHUB_STEP_SUMMARY`, and its stdout is read for `::add-mask::` and the log-level commands, so outputs reach the step's record, environment and paths reach the job's later steps, and masked values are not echoed.

GitHub's own parts of a pipeline run on the host too. A provider step carrying a `uses:` reference is resolved, a composite action's steps are interpreted and a node action's entry runs under the CLI's own node, with the action's `pre` and `post` phases placed around the job's steps the way the hosted runner places them. `${{ }}` expressions in command text, environments and conditions are evaluated with the run's context: step outputs and environment written by earlier steps, the results of needed jobs, and secrets supplied by `--secret name=value` or `--secret name`, which reads the environment variable of that name and never puts the value on a command line. Local `./` references resolve against the workspace and an `owner/repo@ref` reference is fetched as a tarball and extracted for the run's lifetime; the platform token, wherever the run has one, is supplied to expressions as `secrets.GITHUB_TOKEN` and sent with the fetch. What needs a container stays the provider's: a `docker://` reference is skipped by `--skip-provider-steps` and fails the run without it, and the same flag skips whole actions, for a host that has already supplied the toolchain those steps would install.

**Query the platform.** A separate read-only contract lists runs, checks, logs and the checks a branch requires. It does not depend on the pipeline model.

**Import a native file.** `import` reads a provider's file into a pipeline, as TypeScript or JSON, and `convert` writes another provider's files from it. What the model cannot express is rejected with the file and pointer, never approximated.
**Import a native file.** `import` reads a provider's file into a pipeline, as TypeScript or JSON, and `convert` writes another provider's files from it. What the model cannot express is rejected with the file and pointer, never approximated. A GitHub workflow imported as a pipeline runs through `import` then `run --local`: its actions, expressions, output hand-offs and `if` gates execute on the host for the supported subset, and what the host cannot run is rejected naming the construct rather than approximated.

## Authoring

Expand Down Expand Up @@ -100,7 +102,7 @@ node packages/cli/src/bin.ts check-names --pipeline ci/ci.pipeline.ts --provider
node packages/cli/src/bin.ts platform runs --repo OWNER/REPO --sha SHA
```

`--pipeline` takes a `.ts` module or a `.json` document. `run` takes its context (event, ref, sha and so on) from the environment when it detects GitHub Actions or GitLab CI, or from `--context key=value`. A missing value is an error, not a guess. `--local` supplies the context of a manual run on a branch called `local`.
`--pipeline` takes a `.ts` module or a `.json` document. `run` takes its context (event, ref, sha and so on) from the environment when it detects GitHub Actions or GitLab CI, or from `--context key=value`. A missing value is an error, not a guess. `--local` supplies the context of a manual run on a branch called `local`. `run` takes secrets with `--secret name=value`, or `--secret name` to read the environment variable of that name; a secret an expression reads but the run was not given is an error naming it, and secret values are never printed, logged or stored.

Platform commands read the token from `GH_TOKEN`; the adapter reads no files or other variables for it.

Expand Down
8 changes: 8 additions & 0 deletions docs/providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ A job that names no runner label has an empty `runsOn`, which means the provider

Every adapter can render a job in the wrapper form: orchestration stays native and each contiguous run of portable steps becomes one call to this tool's runner for exactly those step indexes. A run step with `form: "native"` stays in the provider's syntax, which is how an install step runs before the tool exists. A matrix combination is passed as a quoted `--matrix "axis=value"` argument: as a shell-quoted literal where the adapter expands the matrix itself, and through the provider's own substitution (`${{ matrix.node }}`, `$node`, `<< parameters.node >>`) where the provider does. The run context the runner reads comes from the provider's environment variables; a value that provider does not expose (Bitbucket cannot tell a push from a manual run, CircleCI exposes no workflow name) is absent, and the runner fails only if a step or condition reads it. The runner's own file commands (`GITHUB_OUTPUT` and its siblings) apply between the steps of one wrapper call and are not forwarded to the provider's runner, so a native step after the call does not see what an inner step wrote.

## GitHub's own steps on the host

A `run` that reaches a provider step of GitHub's resolves it on the host, in two moments that mirror the hosted runner's own. Before a job runs, every action the job's `uses` steps name, whose step this run will execute, is resolved and its `pre` and `post` phases are placed: `pre` before the job's own steps in the order the steps name their actions, `post` after them in the reverse of that order, which is where the hosted runner runs them. When the run reaches a step, it is resolved against what the job's earlier steps by then produced, so a `with:` mapping or an `if:` reading a step output, or command text reading the environment a step wrote, works as written. A composite action's steps run as commands; a node action's entry runs under the CLI's own node with its inputs in the environment as `INPUT_<NAME>`; a local `./` reference resolves against the workspace and an `owner/repo@ref` reference is fetched as a tarball, extracted once per reference for the run's lifetime.

Expressions evaluate with the run's context: the neutral context values mapped onto the `github` context's own names, the environment the next command inherits, the outputs and conclusions of the job's steps so far (keyed by the `id` the workflow gave them), the results of needed jobs, and secrets from `--secret`. The platform token, wherever the run holds one, is supplied as `secrets.GITHUB_TOKEN`.

What stays provider-owned: a `docker://` reference, an action whose `runs.using` needs a container, a workflow step naming a shell other than `bash` or `sh` or a setting the model has no step form for (`continue-on-error`, `timeout-minutes`), and every step of another provider. Each is skipped by `--skip-provider-steps` and fails the run without it, naming the construct; `--skip-provider-steps` skips whole actions too, for a host that has already supplied the toolchain those steps would install. An action's declared `outputs` are read as data but not evaluated into the steps context, because the model records no outputs an action declares, and a composite action's step conditions see the view the step was reached with, not the outcomes of the composite's own later steps.

## Check names

`checkNames` returns, per job, the names under which the provider reports it, one per matrix combination: `Test (22)` on GitHub, `Test: [22]` on GitLab, and the step name on Bitbucket, which reports one status per pipeline. CircleCI names a matrix job from a templated name that the schema accepts and that was not run on CircleCI.
Expand Down
2 changes: 2 additions & 0 deletions eslint.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ export default defineConfig(
"schemas",
// Golden files are the exact bytes an adapter must emit.
"packages/*/test/golden",
// Action fixtures are files a host run executes as data, like the tarball a fetched action arrives in.
"packages/cli/test/fixtures",
],
},
{
Expand Down
Loading
Loading