Publish from CI/CD
Keep your project in Git and let a pipeline publish it: every pull request is validated and its tool fixtures run, and every merge to main is evaluated and published. Nobody publishes from a laptop, and the release history matches your commit history.
In CI there is nobody to log in, so the CLI authenticates with an API key instead. See API keys for automation for how keys and scopes work.
Set up the key
Section titled “Set up the key”-
Log in as a workspace Owner or Admin, so you can grant agent scopes, and select the workspace you publish to.
-
Create an API client with the scopes a publish needs, and send its key straight into your repository’s secrets without printing it. With the GitHub CLI:
Terminal window caplo workspace api-client create github-deploy \--scope agents:write,agents:publish,tools:write,tools:publish \| gh secret set CAPLO_API_KEYAdd
channels:writeif an environment deploys channels. Leave out thetoolsscopes if the project has no tool packages. -
Store the workspace ID as
CAPLO_WORKSPACE. It is not secret, but keeping it next to the key keeps them in step:Terminal window caplo workspace get -o jsongh secret set CAPLO_WORKSPACE --body <WORKSPACE_ID>
With an API key, the workspace must be given by ID, never by name.
GitHub Actions
Section titled “GitHub Actions”Save this as .github/workflows/caplo.yml. It pins the CLI version, so a new CLI release never changes your pipeline until you update the pin.
name: Caplo
on: pull_request: push: branches: [main]
permissions: contents: read
env: CAPLO_VERSION: v0.4.0
jobs: check: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: 24 - name: Install the Caplo CLI run: | curl -fsSL "https://raw.githubusercontent.com/getcaplo/homebrew-tap/$CAPLO_VERSION/install.sh" | sh echo "$HOME/.local/bin" >> "$GITHUB_PATH" - name: Install tool package dependencies run: | for dir in tools/*/; do if [ -f "$dir/package.json" ]; then npm ci --prefix "$dir"; fi done - run: caplo validate - run: caplo test
publish: if: github.event_name == 'push' && github.ref == 'refs/heads/main' needs: check runs-on: ubuntu-latest concurrency: caplo-publish env: CAPLO_API_KEY: ${{ secrets.CAPLO_API_KEY }} CAPLO_WORKSPACE: ${{ secrets.CAPLO_WORKSPACE }} steps: - uses: actions/checkout@v4 - name: Install the Caplo CLI run: | curl -fsSL "https://raw.githubusercontent.com/getcaplo/homebrew-tap/$CAPLO_VERSION/install.sh" | sh echo "$HOME/.local/bin" >> "$GITHUB_PATH" - run: caplo auth status - run: caplo eval - run: caplo publish --yesWhat each part does:
checkruns on every pull request and push.caplo validateandcaplo testneed no key and call no workspace, so they also run for pull requests from forks.npm cineeds each package’spackage-lock.jsoncommitted.publishruns only on pushes tomain.caplo auth statusprints the API client and its scopes, which makes a wrong key easy to spot in the log.caplo evalpushes the drafts and runs every agent’s eval cases, so a failing case stops the job before anything is published.caplo publish --yesthen publishes bottom up, reusing a passing eval run wherever the draft has not changed since.--yesis required: without a terminal, a publish that would ask for confirmation stops with exit code 2. It also covers the project’s first publish to the workspace.concurrencystops two publishes from running against the same workspace at once.
The install script checks the archive’s SHA-256 checksum before installing. To also gate publishing on a reviewer, put the publish job in a GitHub environment with required reviewers.
Show the plan on pull requests
Section titled “Show the plan on pull requests”To see what a merge would change, add a step to check with a key that has only agents:read and tools:read:
- run: caplo diff env: CAPLO_API_KEY: ${{ secrets.CAPLO_READ_KEY }} CAPLO_WORKSPACE: ${{ secrets.CAPLO_WORKSPACE }}caplo diff -o json prints the same plan as one JSON document, to post as a comment or check with a policy; see Plans as JSON.
Staging and production
Section titled “Staging and production”With environments in caplo.yaml, give each its own API client and secret, and select the environment per job:
- run: caplo publish --env staging --yes env: CAPLO_STAGING_KEY: ${{ secrets.CAPLO_STAGING_KEY }}For production, run a separate job, for example on workflow_dispatch or a tag, that sets both keys. With promoteFrom: staging, the CLI reads staging with CAPLO_STAGING_KEY and refuses any agent staging has not released from the same source:
- run: caplo publish --env prod --yes env: CAPLO_PROD_KEY: ${{ secrets.CAPLO_PROD_KEY }} CAPLO_STAGING_KEY: ${{ secrets.CAPLO_STAGING_KEY }}Do not also set CAPLO_WORKSPACE in these jobs unless it matches the environment’s workspace exactly; a different value stops the command with exit code 2.
Publish my Caplo project from GitHub Actions
Adds a GitHub Actions workflow that validates and tests every change and publishes the Caplo project from main with an API key.
Show prompt
Goal: add a GitHub Actions workflow to this repository that runs `caplo validate` and `caplo test` on every pull request and push, and runs `caplo eval` and `caplo publish --yes` on pushes to `main`, authenticated with a Caplo API key. Read these pages first: - https://docs.caplo.si/cli/ci-cd.md - https://docs.caplo.si/cli/authentication.md - https://docs.caplo.si/cli/projects.md - https://docs.caplo.si/cli/scripting.md Steps: 1. Find the Caplo project: the `caplo.yaml` with `kind: Project`. If it is not at the repository root, run every `caplo` command in the workflow with `-C <PROJECT_DIR>` and adjust the `tools/*/` path. Read its `tools` glob and `environments`, if any. 2. Run `caplo validate` locally and fix any problem it reports before you write the workflow. Do not change agent behavior to make it pass without asking me. 3. Check that every tool package directory has a committed `package-lock.json`. If one is missing, run `npm install` in that package and include the lock file in your change. 4. Create `.github/workflows/caplo.yml` from the GitHub Actions example on the CI/CD page: - pin the CLI with `CAPLO_VERSION: <CLI_VERSION>` and install it with the install script from that version's tag; - a `check` job with Node.js 24 that installs package dependencies with `npm ci` and runs `caplo validate` and `caplo test`; - a `publish` job that runs only on pushes to `main`, needs `check`, uses `concurrency: caplo-publish`, sets `CAPLO_API_KEY` and `CAPLO_WORKSPACE` from repository secrets, and runs `caplo auth status`, `caplo eval` and `caplo publish --yes`. If `caplo.yaml` defines environments, use `caplo publish --env <ENVIRONMENT> --yes` and that environment's `apiKeyEnv` secret instead, as the page describes. 5. Do not run `caplo push`, `caplo eval` or `caplo publish` on pull requests. 6. Tell me the commands to create the API client and store the secrets myself, with the scopes the project needs (`agents:write`, `agents:publish`, plus `tools:write` and `tools:publish` if it has tool packages, plus `channels:write` if an environment lists channels): `caplo workspace api-client create <CLIENT_NAME> --scope <SCOPES> | gh secret set CAPLO_API_KEY` and `gh secret set CAPLO_WORKSPACE --body <WORKSPACE_ID>`. Verify: - `caplo validate` and `caplo test` pass locally. - The workflow file is valid YAML and every `caplo` command and flag in it appears in https://docs.caplo.si/reference/cli/project.md. - Open a pull request so the `check` job runs, and report its result. Rules: - Never create, print, paste or commit an API key, and never put one in the workflow file. Keys go only into GitHub secrets, and I create them. - Do not run `caplo push`, `caplo eval` or `caplo publish` yourself, and do not merge to `main`, without asking me first: on `main` the workflow publishes to the workspace customers use. - Do not edit agent files or tool code beyond what validation requires, and ask before any such change.
GitLab CI
Section titled “GitLab CI”Add CAPLO_API_KEY and CAPLO_WORKSPACE under Settings, CI/CD, Variables in GitLab, masked and protected so only protected branches can read them. Then in .gitlab-ci.yml:
stages: [check, publish]
variables: CAPLO_VERSION: v0.4.0
default: image: node:24 before_script: - curl -fsSL "https://raw.githubusercontent.com/getcaplo/homebrew-tap/$CAPLO_VERSION/install.sh" | sh - export PATH="$HOME/.local/bin:$PATH"
check: stage: check script: - for dir in tools/*/; do if [ -f "$dir/package.json" ]; then npm ci --prefix "$dir"; fi; done - caplo validate - caplo test
publish: stage: publish resource_group: caplo-publish rules: - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH script: - caplo eval - caplo publish --yesExit codes
Section titled “Exit codes”Pipelines stop on any non-zero exit code. The code tells you where to look:
| Code | Meaning in CI | Typical fix |
|---|---|---|
0 |
Success, including nothing to publish | |
1 |
An error, such as a refused API key, a missing scope, a network failure or a failed build | Read the error line; check scopes with caplo auth status |
2 |
A usage error: a missing --yes, a workspace given by name with an API key, --workspace disagreeing with --env, or channels:write missing for a channel deploy |
Fix the command or the variables |
3 |
A failed gate: validation, tool fixtures, eval cases, or a promotion with no matching release | Fix the files, or publish to the promoteFrom environment first |
130, 143 |
The job was cancelled | Rerun it |
A publish that fails partway keeps what it already published, such as tool versions or child agent releases. Fix the cause and run it again; unchanged resources are skipped. See Scripting for output formats and environment variables.