CI Integration
Cursus is designed to work seamlessly in CI. The ci subcommand auto-detects your repository state and runs the appropriate action, and the verify subcommand ensures PRs include changesets.
The ci subcommand
Section titled “The ci subcommand”cursus ci --no-interactiveThis inspects the repository and decides what to do:
| State | Action |
|---|---|
| Pending changeset files exist | Runs prepare |
| No changesets, but packages have versions without matching Git tags | Runs publish |
| Neither condition | No-op (exits successfully) |
This makes your CI pipeline simple — just run cursus ci on every push to your main branch and it does the right thing.
Verifying changesets on PRs
Section titled “Verifying changesets on PRs”Use the verify subcommand to enforce that every PR includes at least one changeset:
cursus verify --no-interactiveExit codes:
- 0 — changeset(s) found
- 1 — error
- 2 — no changesets found
By default, verify compares against origin/HEAD. To use a different base:
cursus verify --no-interactive --base origin/mainExample GitHub Actions workflow
Section titled “Example GitHub Actions workflow”name: Releaseon: push: branches: [main]
jobs: release: runs-on: ubuntu-latest permissions: contents: write # create tags and GitHub Releases id-token: write # trusted publishing steps: - uses: actions/checkout@v6 with: fetch-depth: 0 # Exchange the GitHub Actions OIDC token for a short-lived CARGO_REGISTRY_TOKEN. # Only consumed when cursus ci dispatches to publish; harmless on prepare runs. - uses: rust-lang/crates-io-auth-action@v1 - run: cursus ci --no-interactive env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}For projects not using trusted publishing, you can omit the id-token: write permission and the exchange step, and set login tokens such as CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} in the env block instead. See the publishing guide for details on trusted publishing setup.
For PR changeset verification:
name: CIon: pull_request:
jobs: verify-changeset: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 with: fetch-depth: 0 - run: cursus verify --no-interactiveVerified commits with a GitHub App
Section titled “Verified commits with a GitHub App”By default, commits made by the release workflow appear as Unverified on GitHub because they are produced by the local git binary on the runner. To get the green Verified badge on your release commits, use a GitHub App installation token instead of secrets.GITHUB_TOKEN.
When Cursus detects it is running on GitHub Actions with a token available, it automatically routes the prepare commit through the GitHub Git Data API (signed_commits = "auto" is the default). GitHub signs any commit created this way using its web-flow GPG key — but this signing only activates when the request is authenticated as a GitHub App, not a personal access token or the default GITHUB_TOKEN.
Setting up a GitHub App
Section titled “Setting up a GitHub App”- Go to Settings → Developer settings → GitHub Apps → New GitHub App.
- Give it a name (e.g.
my-org-release-bot) and uncheck Webhook active. - Under Repository permissions, set Contents and Pull Requests to Read and write.
- Install the app on your repository.
- Note the App ID from the app’s settings page.
- Generate a private key (downloaded as a
.pemfile). - Find the app’s user ID: visit
https://api.github.com/users/{app-name}[bot]and note theidfield.
Store these in your repository:
| Item | Where |
|---|---|
| App ID | Repository variable APP_ID |
Private key (.pem contents) | Repository secret APP_PRIVATE_KEY |
| User ID | Repository variable APP_USER_ID |
Updated release workflow
Section titled “Updated release workflow”name: Releaseon: push: branches: [main]
jobs: release: runs-on: ubuntu-latest permissions: id-token: write # trusted publishing steps: - name: Generate GitHub App token id: app-token uses: actions/create-github-app-token@v3 with: app-id: ${{ vars.APP_ID }} private-key: ${{ secrets.APP_PRIVATE_KEY }}
- uses: actions/checkout@v6 with: token: ${{ steps.app-token.outputs.token }} fetch-depth: 0
# Tags are still created via the local git binary during publish, so set # the identity here to attribute them to the App bot rather than the # default runner identity. The release commit itself does not need this — # its committer is set by GitHub when the API commit is created. - name: Configure git identity run: | git config user.name "${{ steps.app-token.outputs.app-slug }}[bot]" git config user.email "${{ vars.APP_USER_ID }}+${{ steps.app-token.outputs.app-slug }}[bot]@users.noreply.github.com"
- uses: rust-lang/crates-io-auth-action@v1
- run: cursus ci --no-interactive env: GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}The contents: write permission is no longer needed in the workflow because the App token carries it on behalf of the installation. No changes to .cursus/config.toml are required — signed_commits = "auto" is the default.
Automating dependency update changesets
Section titled “Automating dependency update changesets”For PRs created by tools like Renovate or Dependabot, you can automatically derive a changeset from the Conventional Commit message. This works best with git integration enabled, which lets Cursus commit and push the changeset back to the PR branch without any extra steps:
name: Auto Changeseton: pull_request: types: [opened, synchronize]
jobs: changeset: if: contains(github.event.pull_request.labels.*.name, 'dependencies') runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - run: cursus change --no-interactive --auto