Skip to content
Source

Run CI jobs on your machine

A CI job failed on the forge. Each try of a fix costs a push, and a wait for a container.

src ci run runs the same job on your machine: the same workflow file, steps, matrix cell and needs. You fix the job there, and push once.

The commands are in the npm package @tanstack-source/cli, which has the src binary. src ci plan and src ci run work without the forge.

Run a job

  1. Plan the workflows. The command shows each workflow and its jobs, or the errors with the file to fix.

    src ci plan
  2. Run one job:

    src ci run ci test
  3. Read the output. The command exits with 0 when the job passed, and with 1 when it failed.

These flags change what runs:

FlagWhat it does
--matrix <key>=<value>Runs only the matrix cells with this value. Repeat it for more keys. Without it, every cell runs, one after the other.
--with-needsRuns the jobs that the job needs first, and gives it their results and outputs.
--input <name>=<value>Gives a value to an input of manual(). Repeat it for more inputs. The run then has the event manual.
src ci run e2e test --matrix shard=2
src ci run ci gate --with-needs
src ci run release publish --input channel=next

If a job has needs and you leave out --with-needs, the job gets no results or outputs of them. The command then warns you:

Job gate needs test, and this run gives it no results or outputs of them. Add --with-needs to run them first.

How a local run differs

  • The trust level is local.
  • The event is the type of the first trigger of the workflow. With --input, the event is manual.
  • ctx.head is the commit that you checked out, ctx.base is its parent, and ctx.ref is your branch.
  • The steps run in bash, in the root of your checkout, with your environment. On Windows, the steps use the bash of Git for Windows.
  • Your machine has its own tools. To use the tools of the forge, use --docker.

Give the job its secrets

A job gets each secret that it declares from your environment, then from .env in the root of the checkout. On your machine the job gets the value itself, because no proxy sits between the job and the network.

echo 'NPM_TOKEN=<your own token>' >> .env
src ci run release publish
  • The run reads from .env only the secrets that the jobs declare.

  • If a required secret has no value, the command stops before any job runs:

    src ci run: Job publish needs the secret NPM_TOKEN. Set it in the environment or in .env.

Keep .env out of git. The forge refuses a push that adds a .env file.

Where the cache, artifacts and fixups go

A local run keeps them in a folder of your machine: $XDG_CACHE_HOME/tanstack-source/, or ~/.cache/tanstack-source/ when XDG_CACHE_HOME is not set.

  • ci/: the entries of cache(). Delete this folder to start with an empty cache.
  • artifacts/: the archives of artifact(). The step prints the path of each one.
  • fixups/: the patches of fixup(). The step prints the git apply command for each one.
  • tools/: the Node, bun and pnpm that the setup steps download.

report() prints its findings in the log of the step.

Send real writes to the forge: --live

Without --live, each forge() write is a dry run. The step prints the write and sends nothing:

[dry run] POST /repos/acme/app/issues/7/comments {"body":"Released"}

With --live, the job also gets TSS_FORGE_URL and TSS_TOKEN, and forge() sends each write. --live needs TSS_FORGE_URL, TSS_TOKEN and TSS_REPO.

export TSS_FORGE_URL=https://forge.example/api/v1
export TSS_TOKEN=<your personal access token>
export TSS_REPO=acme/app
src ci run release publish --live

CAUTION: Use --live only when you want the writes on the forge. The writes use your own token, so the permissions of the job do not limit them.

Use the image of the forge: --docker

A job can pass on your machine and fail on the forge, because the tools are different. --docker runs the job in its CI image (node or browser) with Docker.

  1. Build the two images once, from a checkout of the TanStack Source repository. The images are for linux/amd64.

    docker build --platform linux/amd64 -t tanstack-source-ci-node -f apps/web/containers/node/Dockerfile packages/ci
    docker build --platform linux/amd64 -t tanstack-source-ci-browser -f apps/web/containers/browser/Dockerfile packages/ci
  2. Run the job with --docker:

    src ci run ci test --docker
  • The command mounts your checkout at /workspace, and runs the job in the container. It uses tanstack-source-ci-browser when a job to run has image: 'browser'.

  • Secrets and the --live variables go into the container by name, so their values never show in the command line.

  • To see the docker command without running it, add --print:

    docker run --rm --mount 'type=bind,source=/home/you/app,target=/workspace' --workdir /workspace tanstack-source-ci-node node /opt/tanstack-source/ci/src/cli.ts run ci test

In Docker, the cache, the artifacts and the fixup patches stay in the container, and Docker deletes the container after the run. Changes to the files of your checkout stay, because the checkout is mounted.

Run a job of a forge run again: --repro

Each job of a run on the forge has a repro command. The run page shows it under "Run this job on your machine". The command runs that job cell again, with the matrix cell, inputs and needs of that run.

  1. Set TSS_FORGE_URL, TSS_TOKEN and TSS_REPO, as for --live.

  2. Check out the commit of the run:

    git fetch origin <commit>
    git checkout --detach <commit>
  3. Run the command that you copied from the run page:

    src ci run --repro '<run>/test (shard: 2)'

What to know about --repro:

  • A job key has its matrix cell, like test (shard: 2). Put it in quotes in the shell.
  • If your checkout is at another commit, the command stops and prints the two git commands.
  • Secrets come from your environment and .env, as in any local run. You can add --live.
  • The run gives the matrix cell, inputs and needs, so --repro takes no --matrix, --input, --with-needs or --docker.

From red to green

The test job of your pull request failed on the forge. Run it on your machine:

src ci run ci test

The end of the output shows the failed test, with its file and line:

--- Test
 FAIL  src/sum.test.ts > sum > adds two numbers
error src/sum.test.ts:5:22 sum > adds two numbers: expected 3 to be 4 // Object.is equality
Error: vitest exited with code 1
...
--- Test: failed (9.9 s): vitest exited with code 1
--- Save cache pnpm-3b1f2a9c8d7e6f50: skipped
Job test failed: Step "Test" failed: vitest exited with code 1

Fix src/sum.ts, then run the job again:

--- Test
 ✓ src/sum.test.ts (2 tests) 3ms
No findings
--- Test: passed (6.0 s)
--- Save cache pnpm-3b1f2a9c8d7e6f50
Saved the cache pnpm-3b1f2a9c8d7e6f50 in /home/you/.cache/tanstack-source/ci
--- Save cache pnpm-3b1f2a9c8d7e6f50: passed (2.2 s)
Job test passed.

Commit the fix and push it once. Then list the runs of your pull request:

src ci runs --pr 12
ID                                    STATUS   WORKFLOW  EVENT         REF      COMMIT
d4e5f6a7-8b9c-4d0e-9f1a-2b3c4d5e6f70  success  ci        pull_request  fix-sum  5d6e7f8
b51f0e2a-6c7d-4e8f-9a1b-2c3d4e5f6a7b  failure  ci        pull_request  fix-sum  1a2b3c4

The run of your fix is green, after one push.