Skip to content
Source

CI reference

The options, steps, limits, commands and routes of CI on TanStack Source. To write a workflow step by step, start with Your first workflow.

Workflow files

  • A workflow is a .ts file directly in .source/workflows/. Its default export is defineWorkflow(...) with its jobs.
  • At plan time, a workflow file can import only @tanstack-source/ci, node: built-ins, other files in .source/, and src: modules (Share steps). Import the code of your repository inside a step.
  • The forge plans in a container with no network and no secrets, for at most 30 s.
  • .source/ can hold at most 2 MiB of files, and at most 500 files and folders.

defineWorkflow(name, options)

OptionDefaultWhat it does
nameUnique in the repository. Letters, digits, _, . and -, up to 64 characters. The first character is a letter or a digit.
onThe triggers. At least one, and at most one manual().
env{}Environment variables of every job. It cannot replace a variable that the forge sets: see Environment variables.
cancelOlderRunstrueA new run cancels the older runs of this workflow on the same pull request or branch.

Add each job with .job(name, options). A job name has the rules of a workflow name, and is unique in the workflow.

When the plan fails

A plan fails when a workflow file does not load or breaks a rule. It also fails when a src: import cannot be used, or when it is over one of the limits above. Then no job runs, and a push or a pull request records the errors as failed runs:

  • Each workflow file with errors gets a failed run with no jobs, named after the file, for at most 10 files. One run named plan gets all other errors.
  • The errors are the warnings of the run, at most 20 for each run.
  • The runs list shows the first error. In GET /ci/runs and list_ci_runs, planErrors holds the errors of the run. The run page and the checks of the pull request show them too.
  • A required check of the workflow stays pending. Such a run cannot run again: fix the errors, and push.
  • When the plan of the hidden workflows fails, the runs are hidden runs, for admins only.

A manual start (Run workflow, src ci start) is refused with the first error. On your machine, src ci plan shows the errors of your checkout.

Triggers

TriggerOptionsA run starts when
pullRequest({ branches, paths })branches: patterns of the base branch. paths: patterns of the changed files.A pull request opens, and at each push to its head branch.
push({ branches, tags, paths, skipCiPushes })branches, tags, paths: patterns. skipCiPushes (default false): ignore the pushes that a CI job made.A push moves a branch or a tag that matches. Without branches and tags, every push matches. With only one of them, only that kind of ref matches.
schedule(cron)Five fields, in UTC: minute, hour, day of the month, month, day of the week.The time of the schedule comes.
manual({ inputs })inputs: see Manual inputs.A person with write access starts it: Run workflow on the CI tab, src ci start, REST or MCP.
after(workflow, { conclusion })conclusion: 'success' (default), 'failure' or 'any'.A run of the other workflow ends with that conclusion.

Patterns:

  • * matches any characters except /. ** matches any characters. ? matches one character that is not /.
  • A leading ! removes the values that it matches. The last pattern that matches a value decides.
  • A list needs at least one pattern without !. A pattern has at most 256 characters.
  • A paths filter matches when one changed file matches. A push of a tag ignores paths.
  • A change of more than 1,000 files matches every paths filter.

Rules of schedule() and after():

  • schedule(): only the plan of the default branch counts. The plan refuses a schedule that runs more often than every 5 minutes. A schedule that missed several times runs once. When the plan of the default branch fails, the repository has no schedules until a push fixes the plan.
  • after(): the name must be another workflow of the repository. The plan refuses an unknown name.
  • An after() run uses the same commit and ref as the run that ended.
  • A cancelled run starts no after() run. A run of a pull request from a fork starts none either.
  • A workflow runs at most once in one chain of after() runs.

Manual inputs

FieldInput typesWhat it does
typeall'string', 'boolean' or 'choice'.
descriptionallThe text of the input in the form.
requiredallA manual run must give a value.
defaultallThe value when a run gives none. For 'choice', one of the options.
options'choice'The values to pick from. At least one.

The steps get the values in ctx.inputs, with their types. An input with a default always has a value: runs of other events get the default.

Trust levels

The forge sets the level of each run. A workflow file cannot change it. Trust levels and secrets explains each rule.

EventLevel
Pull requesttrusted when the author has write access to the base repository, else untrusted.
Pushprotected on a protected ref, when the person has write access. trusted on any other branch. untrusted without write access.
manual()protected on a protected ref, else trusted. It needs write access.
schedule()protected.
after()The level of the run that ended.

A protected ref is the default branch, a tag, or a branch with a branch rule. A run that a CI push starts gets the lower of two levels: the level of the run that pushed, and the level of the target. A local run (src ci run) has the level local.

Job options

What runs, and where:

OptionDefaultWhat it does
needs[]Jobs declared before this one. They end first.
when'success''success': runs when all its needs passed, else is skipped. 'always': runs when all its needs ended, with any result. 'on-failure': runs only when one of its needs failed.
ifnone(ctx) => boolean. It runs in the job's container before the first step, with the ctx of the steps: the event, the ref, the inputs and the outputs of its needs. When it returns false, no step runs and the job is skipped. Its check run then shows success, with "skipped" in its title.
image'node''node' or 'browser'. See Images and sizes.
size'large''small', 'medium' or 'large'.
timeoutMinutes60A whole number from 1 to 120.
checkout{ depth: 1, lfs: false }depth: 0 checks out all the history. lfs: true checks out the LFS files.
matrixnoneRuns the job once for each combination of the values. At most 256 cells.
warmnone{ files, through }. Later jobs with the same files mount the checkout and the home folder of this job, as they were after the step through. See Warm starts.

What the job can reach, and what it gives:

OptionDefaultWhat it does
secrets{}Secret names. {} is a required secret: with no value at the trust level of the run, the job is skipped. { optional: true } gives undefined there.
vars[]Names of CI variables, at most 100. The job gets each one that the repository has as an environment variable.
egress[]Exact host names that the job reaches, on port 443. No other host.
permissionsnoneWhat the run token can write. See Permissions.
outputs[]Names that the steps set with ctx.setOutput. The jobs that need this job read them.
env{}Environment variables of the job, on top of the env of the workflow. It cannot replace a variable that the forge sets either.
stepsAt least one step.

Steps

KindFieldsHow it runs
Commandname, run, env, workingDirectory, ifbash --noprofile --norc -eo pipefail, in the workspace or in workingDirectory.
Functionname, do, ifThe runner awaits do(ctx). The step fails when it throws or rejects.
  • if(ctx) returns true or false, or a promise of one. With false, the step is skipped. Any other value fails the step.
  • When a step fails, the job fails, and the steps after it are skipped.
  • After the last step, the functions that steps gave to ctx.afterJob run, the last one first. When the job failed, only those with always: true run.

ctx

A function step gets ctx, typed from the job and the workflow.

Values:

MemberWhat it holds
eventThe trigger type: 'pull_request', 'push', 'schedule', 'manual' or 'after'.
trust'untrusted', 'trusted', 'protected' or 'local'.
matrixThe values of the matrix cell of this job, with their exact types.
inputsThe values of the manual inputs.
needsFor each needed job, its result, and its outputs. For a matrix job, result and cells, each with matrix, result and outputs.
secretsA placeholder for each declared secret. The forge swaps it for the value only in requests to the hosts of the secret.
varsThe value of each CI variable that the job declares, or undefined when the repository does not have it.
envThe environment of the job.
base, head, refThe commit to compare with, the commit that the run tests, and the full ref of the run.
workspaceThe path of the checked-out repository.

What base is, by event:

  • Pull request: the head commit of the base branch when the run starts.
  • Push: the head of the last run of the same workflow that passed on that ref. If the workflow never passed there, the commit of the ref before the push. A new branch compares with the head of the default branch.
  • Push of a new tag, first push of the default branch, schedule() and manual(): empty.
  • after(): the base of the run that ended.

Functions:

MemberWhat it does
sh`...`Runs a command in bash, in the workspace. Each value in ${} is one shell word, and an array is one word for each item. A plain string runs as it is. Throws when the exit code is not 0.
sh.nothrow`...`The same, but returns { code, stdout, stderr } for any exit code.
setOutput(name, value)Sets a declared output. All outputs of a job take at most 1 MiB.
summary(markdown)Adds markdown to the job summary, at most 1 MiB.
afterJob(fn, { name, always })Runs fn after the last step, as a step with its own log. With always: true, it runs also when the job failed.
addPath(dir)Puts a folder first on the PATH of the steps after this one.

Environment variables

Each job also gets these environment variables:

VariableValue
CItrue
FORCE_COLOR1, so tools print colors to the log, and the run page shows them. Set it to 0 in env to turn colors off.
TSS_TOKENThe placeholder of the run token. The forge swaps it only for its own git host and REST API.
TSS_FORGE_URL, TSS_REPOThe REST base for forge(), and the repository as owner/name.
NX_HEAD, NX_BASEctx.head, and ctx.base when it is not empty.
TANSTACK_SOURCE_JOB_ID, TANSTACK_SOURCE_SHA, TANSTACK_SOURCE_REF, TANSTACK_SOURCE_REPOSITORYThe job, the commit, the ref and the repository. Each one also has a GITFLARE_ name.

The env of a workflow or a job cannot replace a variable that the forge sets: CI, NX_BASE, NX_HEAD, and every TSS_*, TANSTACK_SOURCE_* and GITFLARE_* variable.

  • The job keeps the value of the forge, and runs.
  • The log of the first step starts with a warning that names the variables that the env tried to set.
  • The env of a command step can still set them, for that step only.

Built-in steps

Import them from @tanstack-source/ci. Each one returns a function step. Use it in steps, or call its do(ctx) in your own step.

Setup:

StepWhat it does
setup.node({ version })Puts the Node of version, else of .nvmrc or .node-version, first on the PATH. When the Node on the PATH is another version, it downloads Node from nodejs.org. A version can be 24, 24.3, lts/*, lts/<name> or node.
setup.pnpm()Puts the pnpm that packageManager in package.json names (like pnpm@10.4.1) first on the PATH, through corepack. Fails when packageManager names no pnpm.
setup.bun({ version })Puts the bun of version, else of .bun-version, first on the PATH. When the bun on the PATH is another version, it downloads that exact version from the GitHub release of bun.

Files and results:

StepWhat it does
cache({ key, paths, restoreKeys })Restores key, else the newest entry of the first prefix in restoreKeys that has one. When the job passes and no entry had exactly key, it saves paths as key after the last step.
artifact({ name, paths, retentionDays })Uploads the files as one tar.gz artifact of the run. With no matching file, it uploads nothing and passes.
fixup({ message })Sends what the steps before it changed, compared with the tested commit. The forge commits it to the head branch of the pull request, or shows it as a suggested patch.
report({ file, text, format })Reads the output of a tool: a file of the workspace, or text. Adds its errors and warnings to the findings of the run.

The rules of each:

  • cache(): a key has 1 to 512 printable ASCII characters. At most 10 restore keys. A path is relative to the workspace, under ~, or a glob, with no !. An entry is written once. A restore or save that fails is logged and does not fail the job. The cache scopes depend on the trust level. In a job container, an entry is a tar.zst that the step packs on every core, and a restore reads a large entry in several parts at once, straight into tar. In a local run, an entry is a tar.gz.
  • artifact(): a name has 1 to 200 characters, with no /, \ or control character. A path outside the workspace fails the step. retentionDays is a whole number from 1, and can only make the time shorter.
  • fixup(): the step fails when a change is in .source/, when the fixup is over 5 MiB, or when a submodule changed. The job needs no push permission. Pull requests from a fork lists when the forge commits it.
  • report(): as a step, it runs only when the steps before it passed. To report a tool that failed, call await report({ text, format }).do(ctx) in your step before the step fails.

report() formats. Without format, the step detects it.

formatReads
vitestThe JSON reporter of vitest (vitest run --reporter=json).
jestThe JSON output of jest (jest --json).
junitJUnit XML, from vitest, jest-junit, node --test, pytest and other test runners.
tscThe output of tsc, with --pretty false or without it.
eslintThe JSON output of eslint (eslint -f json).
playwrightThe JSON reporter of Playwright (playwright test --reporter=json).
logThe ::error, ::warning and ::notice lines of a log.

One report keeps at most 1,000 findings, and a message keeps at most 4,000 characters.

forge(ctx)

A client of the REST API of the forge, for the repository of the run. It reads TSS_FORGE_URL, TSS_REPO and TSS_TOKEN from ctx.env.

MethodWhat it doesPermission
pulls.list({ head, state, label })Lists the pull requests, every page. head is owner:branch for a fork.none
pulls.create({ title, head, base, body })Opens a pull request.pulls: 'create'
comments.create(number, body)Comments on an issue or a pull request.comment
issues.list({ label, state })Lists the issues, every page.none
issues.create({ title, body, labels })Opens an issue, then adds the labels.issues: 'write'
labels.ensure({ name, color, description })Gives the label with that name, or creates it.issues: 'write' to create
labels.add(number, names)Adds labels to an issue or a pull request, and creates the missing ones.issues: 'write'
  • A refused request rejects with a ForgeError, with the HTTP status and the message of the forge.
  • A read is tried again after a network error, a 5xx or a 429 (after 0.25 s, 1 s and 4 s). A write is sent once.
  • Without TSS_FORGE_URL, each write is a dry run: the step prints it, and sends nothing. Reads find nothing.

Warm starts

warm: { files, through } makes later jobs mount the checkout and the home folder of an earlier job, as they were when its step through passed. Start jobs warm shows it step by step.

warm: { files: ['pnpm-lock.yaml', 'package.json', '.nvmrc'], through: 'Install dependencies' }

The rules:

  • files: 1 to 20 paths in the repository. A path has no empty, . or .. folder, and does not start with /. A path that is not in the commit is part of the key too.
  • through: the name of a step of the job. With two steps of that name, the first one.
  • The key: warm- and a hash of the CI image (node or browser), the workflow file, the job, its matrix cell, and the git id of each of files at the commit that the run tests. For a pull request from a fork, the commit is in the fork. No code of the repository runs for the key.
  • The image is the cache entry of the key. It has the cache scopes, counts in the cache size of the repository, and goes when the cache evicts it. An untrusted run without a pull request saves none.
  • A hidden workflow takes no warm start and makes none.
  • In a local run (src ci run), warm does nothing: every step runs.

What an image holds:

  • The checkout and the home folder (~) when the step through passed, packed by mksquashfs with zstd. Files in other folders, like the packages of apt-get install, are not in it.
  • The commit that the job tested, and the folders that the steps put on the PATH with ctx.addPath, like the Node of setup.node().

A job whose key has no image:

  • Runs every step. When through passes, it packs the image. A step at the end of the job, Save the warm start, saves it, also when a later step failed.

A job whose key has an image:

  • Before its checkout, downloads the image and mounts it read-only, with an overlay that takes the writes of the job, at the checkout and at the home folder. Nothing is extracted.
  • Moves the checkout of the image to the commit of its run. The files that the .gitignore of the commit ignores stay, like node_modules. Every other file of the earlier checkout goes.
  • Does not run the steps up to and including through, or the ctx.afterJob functions that those steps register. So a cache() step before through saves nothing.
  • Runs the steps after through as usual.

What the log and the step list show:

WhenWhat you see
The key has no imageThe first line of the log: No warm start for these files yet: this job installs, and saves one for the next jobs. A run with no write scope says ...and its run saves none.
The job packed the imageThe last line of the log of through: Packed the warm start: an image of <size> MiB. The job saves it at its end. Then the step Save the warm start says Saved the cache warm-<hash>.
The job could not pack the imageThat line says why, like No warm start was packed: .... The job goes on.
The job mounted an imageEach skipped step has (warm start) after its name, a time of 0 s, and one log line: Taken from the warm start that a job of commit <commit> packed. This step did not run.
The image did not mountThe first line of the log: The warm start did not mount (<why>): this job installs.

A warm start never makes a job fail. When the image does not mount, the job runs every step.

Permissions

A job declares permissions. The default is none: the run token only reads. The trust level of the run sets a ceiling.

PermissionWhat it allowsuntrustedtrustedprotected
noneRead the repository.yesyesyes
comment: 'own-pr'Comment on the pull request of the run.noyesyes
comment: 'any-pr'Comment on any pull request.nogets 'own-pr'yes
push: 'own-pr'Push to the head branch of the pull request of the run.noyesyes
push: ['<pattern>', ...]Push to the branches that match. 'tags' means all tags.noyes, but not to a protected refyes
issues: 'write'Create and edit issues, and change their labels.nonoyes
pulls: 'create'Open pull requests.nonoyes

The run shows a warning for each permission that its level takes away. The forge audits each write with the run, the job and the person who started the run.

Images and sizes

ImageWhat it has
nodeDebian 13, Node 24, corepack (pnpm, yarn), bun, git, git-lfs, jq, python3, build-essential, curl, unzip, xz-utils and zstd.
browserThe node image, plus Playwright 1.57.0 with Chromium, Firefox and WebKit in /ms-playwright.

The images are for linux/amd64, and the steps run as root.

In both images, fetch in Node 22.19 and later goes through undici 8. The fetch of Node 24 (undici 7) can stop the whole process with assert(!this.paused) when a server closes the connection during a slow download, as when corepack unpacks pnpm. A step whose env sets NODE_OPTIONS keeps the fetch of its Node.

In both images, npm, pnpm and bun reach the npm registry over plain http inside the container, 64 requests at a time. /root/.npmrc and /root/.bunfig.toml set this. The job's egress still fetches from the registry over https. A project's own .npmrc or bunfig.toml wins over these files. A job opens a new connection for each request, so a large install with https spends much of its time on TLS handshakes. With http and 64 requests at a time, a cold install of TanStack/ai took 68 s, against 140 s.

SizevCPUMemoryDisk
small16 GiB12 GB
medium28 GiB16 GB
large412 GiB20 GB

A job is large by default: 4 vCPU, as a GitHub runner has. A package install links thousands of files, and more vCPU makes it faster. Give a job that only runs a short script size: 'small' or size: 'medium'.

Where jobs run

The CI jobs of a repository run near the repository's data, which is near the place where the repository was made. So a team in Europe gets jobs in Europe.

Each job reads its checkout and its cache from storage in the repository's region:

  • Eastern Europe or Western Europe;
  • western or eastern North America;
  • Asia-Pacific or Oceania.

At the first CI run of a repository, the forge measures which region is nearest to the repository's data, and keeps that region.

Limits

Jobs:

LimitValue
Matrix cells of a job256
timeoutMinutes1 to 120. The default is 60.
Log of a job128 MiB over all its steps. The forge keeps no log bytes past it.
Outputs, and the summary, of a job1 MiB each
Outbound requests of a job50,000. A request past it gets 429, and the job error says why. The forge audits the first 500 requests one by one, and counts the rest by host. See The egress audit.
CI pushes in a row3. A CI push past that starts no run.

When the platform stops a job's container while the job runs, the job ends with the error "The job lost its container", and the forge runs its cell once more, in a new job. When that job loses its container too, the cell ends as an error.

Storage:

LimitValue
Artifact1 GiB each, and 5 GiB for each run
Artifact retention30 days, and 90 days for a protected run
Cache of a repository10 GB. The least recently used entries go first.
Cache entry5 GiB less 5 MiB
fixup()5 MiB

The queue: each account has a queue of its own, and its repositories take turns. A job past a limit waits in the queue. It does not fail. The jobs of software factory runs wait in the same queue, and these limits count them too. These are the defaults, and the operator of the forge can change them. No limit covers the whole forge.

Containers at the same timeDefault
For one account (the owner of the repositories)20
For one repository20. Lower it with src ci settings --containers <n>.
For the untrusted jobs of one fork author, in all accounts4, and 4 hours of container time each UTC day. The factory jobs on the items of an author without write access count here too.

Settings:

LimitValue
CI secrets100 for each repository, 16 KiB for each value, 10 hosts for each secret
CI variables100 for each repository, 48 KiB for each value, 256 KiB for the variables of one job
Branch rules100 for each repository, 50 required checks for each rule
Approval gates50 for each repository
Allowed workflow sources100 for each repository
src: modules in one plan1 MiB of files, 100 files, 8 imports deep

gh commands for CI

src has gh's run, workflow, cache, secret, variable and agent-task commands. They use your src login and the repository of your clone. For another repository, add -R <owner>/<repo>.

Runs:

CommandWhat it does
src run list [-w <workflow>] [-b <branch>] [-s <status>] [-e <event>] [-u <user>] [-c <commit>] [-L <n>] [--all]Lists the runs, 20 by default. A status is one of gh's, like in_progress or failure. With --all, -w can name a disabled workflow.
src run view <run> [-j <job>] [--log | --log-failed] [-v] [--exit-status]Shows a run and its jobs, one job, or the logs. -j takes the job key, like test (shard: 1), or the job id.
src run watch <run> [-i <seconds>] [--compact] [--exit-status]Shows the run again every 3 seconds, until it ends.
src run rerun <run> [--failed], src run cancel <run>Runs a run again, or only its jobs that did not pass, with the jobs that need them. Or cancels a run.
src run download <run> [-D <dir>] [-n <name>]... [-p <pattern>]...Saves the artifacts of a run. Each artifact is one file. src never replaces a file.
src run delete <run>Deletes a run that ended. See Delete a run.

Workflows, cache, secrets, variables and agent tasks:

CommandWhat it does
src workflow list [--all], src workflow view <workflow> [--yaml] [-r <ref>]Lists the workflows of the default branch, or shows one with its recent runs. --yaml prints the workflow file.
src workflow run <workflow> [-r <ref>] [-f <name>=<text>]... [-F <name>=<value>]... [--json]Starts a workflow that has a manual() trigger. --json reads the inputs from stdin, as a JSON object.
src workflow enable <workflow>, src workflow disable <workflow>Turns a workflow on or off. See Turn a workflow off.
src cache list [-k <prefix>] [-r <ref>] [-S <field>] [-O asc|desc] [-L <n>]Lists the cache entries, 30 by default.
src cache delete <key> [-r <ref>], src cache delete --all [-r <ref>]Deletes the entry of a key in every scope, or only in the scope of -r. --all deletes every entry.
src secret list, src secret set <name>, src secret delete <name>See Set a secret from the terminal.
src variable list, src variable get <name>, src variable set <name>, src variable delete <name>See CI variables. People with write access list and get the variables. Admins set and delete them.
src agent-task create <task> -a <agent>, src agent-task list [-L <n>], src agent-task view <id>Gives a task to an agent, lists the agent jobs of the repository (newest first), or shows one. list takes no -R: it uses the repository of your clone.
  • A workflow is its file name, like nightly.ts, or its name.
  • A -r of the cache commands is refs/heads/<branch>, refs/pull/<n>/merge or protected.
  • --json, --jq and --template work with run list, run view, workflow list, cache list, secret list, variable list and variable get. See Use src in scripts and agents.

Turn a workflow off

src workflow disable nightly.ts stops a workflow from starting runs. src workflow enable nightly.ts turns it on again.

  • A disabled workflow starts no run: not from its triggers, and not by hand. A manual start is refused.
  • It still plans, so a required check of it stays pending.
  • Only an admin of the repository turns a workflow on or off. On the web, admins use the switch of the workflow under Workflows on the CI settings page.
  • src workflow list shows a disabled workflow only with --all.

Delete a run

src run delete <run> deletes a run with its logs, its artifacts and the records of its jobs.

  • Only an admin of the repository deletes a run.
  • The run must have ended. If it has not ended, cancel it first with src run cancel <run>.
  • A run of a pull request cannot be deleted, because the required checks of the pull request read it. A run of a failed plan cannot be deleted either.
  • The run goes at once. Its jobs, logs and artifacts go in the background after that.
  • The check results of the run on its commit stay.

src ci commands

The src binary is in the npm package @tanstack-source/cli. src ci help shows the same list.

The commands that act on the forge use your src login and the repository of your clone, like the other src commands:

  • To use another repository of the same forge, set TSS_REPO=<owner>/<repo>.
  • To use another token, set TSS_TOKEN. It wins over the token of your login.
  • To use another forge, set TSS_FORGE_URL (the REST base, like https://forge.example/api/v1), TSS_TOKEN and TSS_REPO. When TSS_FORGE_URL is set, src ci uses only these three variables.
  • src ci run --live gives its steps only a TSS_TOKEN that you set, never the token of your login.

On your machine. plan, run and convert work without the forge. sync, update and --repro act on the forge:

CommandWhat it does
src ci plan [--json]Plans the workflows, and shows their jobs or the errors. --json prints the plan as JSON.
src ci run <workflow> <job>Runs one job. Flags: --matrix <key>=<value>, --input <name>=<value>, --with-needs, --docker, --print (with --docker), --live. See Run CI jobs on your machine.
src ci run --repro <run>/<job>Runs a job of a forge run again, with its matrix cell, inputs and needs.
src ci convert [<file>...] [--force]Converts GitHub Actions workflows into .source/workflows/*.ts. See Move from GitHub Actions.
src ci syncDownloads the src: modules that src.lock pins into .source/.modules/.
src ci update [<owner>/<repo>@<ref>...]Pins src: imports to the commits of their refs now, shows what changed, and syncs.

Runs and jobs. These, and every command below, act on the forge:

CommandWhat it does
src ci runs [--workflow <name>] [--pr <n>] [--branch <name>] [--status <status>] [--limit <n>] [--cursor <next>]Lists the runs, the newest first. A status is planning, waiting_approval, queued, running, completed or cancelled.
src ci view <run> [--job <job>] [--log | --log-failed]Shows a run and its jobs, one job, or the logs of its jobs.
src ci logs <run> <job> [--step <n>] [--from <line>] [--to <line>]Prints the log of a job, or some of its lines.
src ci findings <run> <job>Shows the errors and warnings that a job found.
src ci rerun <run> [--failed]Runs a run again, or only the jobs that did not pass.
src ci cancel <run>, src ci approve <run>Cancels a run, or approves a run that waits for a person.
src ci start <workflow> [--ref <ref>] [-f <name>=<text>]... [-F <name>=<true|false>]...Starts a workflow that has a manual() trigger, with its inputs.

Artifacts, pull requests and agent jobs:

CommandWhat it does
src ci artifacts <run> <job>Lists the artifacts of a job.
src ci download <run> <job> [--name <name>]... [--dir <dir>]Downloads the artifacts of a job into a folder. The default is the current folder.
src ci artifact <id> [--dir <dir>]Downloads one artifact by its id.
src ci checks <pr>Shows the required checks of a pull request, and whether it can merge.
src ci check-runs [--sha <commit>]Lists the check runs of a commit. The default is the commit that you checked out.
src ci fixups <pr>, src ci fixup apply <id>Lists the fixups of a pull request, or applies a suggested one.
src ci maintainer-edits <pr> [--on | --off]Shows or sets whether maintainers and CI fixups can change the pull request.
src ci job start --agent <name> --prompt <text> [--ref <ref>] [--pr <n>] [--egress <host>]..., src ci job view <id>Starts an agent job, or shows a job.

Settings of the repository:

CommandWhat it does
src ci secret list, src ci secret delete <name>Lists the CI secrets (levels and hosts, never values), or deletes one.
src ci secret set <name> [--untrusted <value>] [--trusted <value>] [--protected <value>] [--unset <level>]... [--host <host>]...Sets the values and the hosts of a secret. A value of - comes from stdin.
src ci rule list, src ci rule delete <pattern>Lists the branch rules, or deletes one.
src ci rule set <pattern> [--require-pull] [--check <workflow>/<job>]... [--up-to-date] [--skipped-passes]Sets the branch rule of a pattern. A flag that you leave out is off.
src ci source list, src ci source add <owner>/<name>, src ci source remove <owner>/<name>Lists, adds or removes the allowed workflow sources.
src ci cache list, src ci cache delete --scope <scope> | --key <key> | --allLists the cache entries, or deletes them. A scope is protected, branch:<name> or pr:<n>.
src ci settings [--containers <n> | --clear-containers] [--gate <job>]... [--no-gates]Shows the CI settings, or sets the container limit and the approval gates. A gate is <job> or <workflow>/<job>.
  • --json prints the answer of the forge as it is. It works with runs, view, findings, artifacts, job view, check-runs, checks, fixups, maintainer-edits and each list command.
  • Short flags: -w (--workflow), -b (--branch), -s (--status), -L (--limit), -j (--job), -r (--ref), -n (--name), -D (--dir).
  • The exit code is 0 when the command did its work, and 1 when it failed. It is 2 when the command line is wrong. For the commands of the forge, it is 2 also when src finds no login or no repository, and a TSS_* variable is missing.

REST and MCP

The REST routes are under /api/v1/repos/<owner>/<repo>. Each MCP tool calls the same method as its route. A job key has its matrix cell, like test (shard: 1).

Runs and jobs:

RESTMCP tool
GET /ci/runs?workflow=&pull=&ref=&status=&limit=&cursor=list_ci_runs
GET /ci/runs/<run>get_ci_run
POST /ci/runs/<run>/approve, /cancel, /rerun ({ "failedOnly": true })approve_ci_run, cancel_ci_run, rerun_ci_run
DELETE /ci/runs/<run>delete_ci_run
GET /ci/workflows, GET /ci/workflows/<workflow>list_ci_workflows, get_ci_workflow
PUT or DELETE /ci/workflows/<workflow>/enabledset_ci_workflow_enabled
POST /ci/workflows/<workflow>/runs ({ "ref", "inputs" })start_ci_workflow
GET /ci/runs/<run>/jobs/<job>/logs?step=&from=&to= (1,000 lines by default)read_ci_job_log
GET /ci/runs/<run>/jobs/<job>/findingsget_ci_job_findings
GET /ci/runs/<run>/jobs/<job>/artifactslist_ci_job_artifacts
GET /ci/runs/<run>/jobs/<job>/reproget_ci_job_repro
GET /artifacts/<id> (with Range)get_artifact

The X-Log-Steps header of the logs route, and steps in read_ci_job_log, give each step of the log:

  • step (from 0), name, and lines.
  • finished: the log of the step ended.
  • conclusion (success, failure or skipped) and durationMs: null until the step ends.

A step skipped because an earlier step failed has no lines. name is null for a step that the runner did not report. The header escapes each character that is not ASCII, like é.

The run lists read an index of the runs, which can lag behind a run by a few seconds:

  • GET /ci/runs, list_ci_runs, src ci runs and the CI tab show a new run, or a change of a run, after those seconds.
  • Until the index has a new run, the routes of that run answer 404. This includes a run that POST /ci/workflows/<workflow>/runs started.
  • When the index has a run, GET /ci/runs/<run> gives its current state.

Pull requests, check runs and agent jobs:

RESTMCP tool
GET /pulls/<n>/checksget_pull_request_checks
GET or PUT /pulls/<n>/maintainer-edits ({ "allowed" })get_maintainer_edits, set_maintainer_edits
GET /pulls/<n>/fixupslist_pull_request_fixups
POST /ci/fixups/<id>/applyapply_ci_fixup
GET /check-runs?sha=list_check_runs
POST /jobs, GET /jobs/<id>start_job, get_job
GET /jobs?kind=agent&limit=&cursor=list_agent_jobs

Settings:

RESTMCP tool
GET /ci/secrets, PUT or DELETE /ci/secrets/<name>list_ci_secrets, set_ci_secret, delete_ci_secret
GET /ci/variables, GET, PUT or DELETE /ci/variables/<name> ({ "value" })list_ci_variables, get_ci_variable, set_ci_variable, delete_ci_variable
GET /ci/rules, PUT or DELETE /ci/rules/<pattern>list_branch_rules, set_branch_rule, delete_branch_rule
GET or POST /ci/sources ({ "source": "owner/name" }), DELETE /ci/sources/<owner>/<name>list_ci_sources, add_ci_source, delete_ci_source
GET /ci/cache, DELETE /ci/cache?scope=&key=list_ci_cache, delete_ci_cache
GET or PUT /ci/settings ({ "containers", "approvalGates" })get_ci_settings, set_ci_settings

Who can:

  • Everyone who can read the runs lists the workflows and the agent jobs.
  • People with write access read the CI variables. Admins set and delete them.
  • Admins turn a workflow on or off, and delete a run. A run of a pull request, or a run that has not ended, gets 422.

Two MCP prompts help agents: fix_ci_run fixes a failed run, and convert_github_actions moves workflows from GitHub Actions.