Contents
CI/CD integration for teams
Make the gitta check required in your pipeline, so the build fails when agent changes break together. This page shows how a team with many developers, agents and pull requests puts gitta into its pipelines. It builds on Pull requests and CI, which explains the check itself.
Test combined work from several agents
Normal CI tests each pull request on its own, against the base branch. With parallel agents, that leaves a gap. Two agent pull requests can each pass, and still fail once both are on main. The gitta check closes that gap. It merges each pair of open pull requests on top of the base branch and runs your gate on the result. A pair that fails gets a Breaks together with line, with the test output, and the newer pull request fails the check.
The same test runs on a developer machine before any pull request exists. gitta check runs the gate on one agent's work combined with each other agent's work, and gitta land runs it on the work merged with main. See Test a combination.
The setup we recommend
- Add the gitta check to every repository where agents open pull requests.
- Start with
fail-on: never. The check only comments. Let your team read the comments for a week or two. - Set
fail-on: breaksand make the check required in branch protection. From now on, a pull request that breaks with an older one cannot merge. - Give each CI runner and cloud agent its own runner token, so agents that run in CI report to the team server while they work. See Agents in CI.
- Add a scheduled job that checks the audit trail. See Audit evidence in CI.
How much the gate costs
Without a gate, the check only reads code. It takes seconds and needs no build. With a gate, the check runs your test command once for each combination of two pull requests.
| Event | Mode | Test runs, with N open pull requests into the same branch |
|---|---|---|
| A pull request is opened or updated | current | N − 1 |
| main moves | all | N × (N − 1) |
With 20 open pull requests, one update costs 19 test runs, and one push to main costs 380. For a large team, do one or more of these:
| To | Do |
|---|---|
| Make each run cheaper | Use a fast gate, for example the unit tests and the type check. Your normal CI still runs the full suite on each pull request. |
| Check all pull requests less often | Run mode: all on a schedule, for example every hour, instead of on each push to main. |
| Limit the comparison | Set max-prs. The default is 30. |
| Skip the gate | Leave gate empty. gitta still reports the warnings and the merges that fail. |
Only pull requests into the same branch are compared. Long-lived release branches do not add cost to main.
Check all pull requests every hour
# .github/workflows/gitta-all.yml
name: Gitta, all open pull requests
on:
schedule: [{ cron: "0 * * * *" }]
workflow_dispatch: {}
permissions: { contents: read, pull-requests: write }
concurrency: { group: gitta-pr-check, cancel-in-progress: false }
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with: { fetch-depth: 0 }
- uses: gitta-io/gitta/gitta-action@v1
with:
version: 0.1.0
gate: "npm ci && npm run test:unit"
mode: all
Keep the workflow from Pull requests and CI for the pull_request event, and remove the push event from it.
Agents in CI
Many teams run agents inside CI, for example Claude Code or Codex in a GitHub Actions job. Without a team server, those agents only meet the others at the pull request. With a runner token, they report while they work, and the agents on laptops hear about their changes at once.
# a step before the agent runs
env:
GITTA_SERVER: https://gitta-1.example.com,https://gitta-2.example.com
GITTA_SERVER_TOKEN: ${{ secrets.GITTA_RUNNER_TOKEN }}
GITTA_MACHINE: ci-${{ github.run_id }}
| Setting | Why |
|---|---|
| One runner token for each pipeline | The audit trail then shows which pipeline started each agent. Make it with gitta team token create --runner. See Tokens and sign-in. |
A unique GITTA_MACHINE for each run | Two runs on the same runner image do not look like one machine. |
| A token that expires | Use --days, and renew the secret before it expires. Revoke a token at once if it leaks. |
Other CI systems
GitLab and Azure Repos are coming next, see Pull requests and CI. In Jenkins, Buildkite, CircleCI or any other system, run gitta pr-check yourself and read its JSON output.
# fetch the pull request under test and the other open ones, then:
gitta pr-check --pr refs/pr/124 --base origin/main \
--other "refs/pr/119=#119" --other "refs/pr/121=#121" \
--gate "make test-fast" --json > gitta.json
# fail the build when a combination breaks or does not merge
jq -e 'all(.[]; .merges and .tests != "FAIL")' gitta.json
gitta pr-check exits with 0 when it finds clashes, so your pipeline decides what fails, as in the jq line above. It exits with 3 when the branch removes tests or weakens checks and weakened_checks is block. Use the same rule as the GitHub check, so two incompatible pull requests do not block each other: only fail for a clash with an older pull request. See Which pull request goes first.
IBM DBB pipelines
For COBOL on z/OS with gitta Enterprise, run gitta pr-check on a Linux or macOS CI agent before the DBB build step. A pull request whose new code uses a copybook field that another pull request changed then stops before it uses z/OS build time. See Mainframe guide.
Audit evidence in CI
A scheduled job can check the audit trail and keep an export with each run, so your auditor gets evidence without asking anyone.
# weekly job
gitta audit verify --team
gitta audit --since "$(date -d '-7 days' +%F)" --csv > gitta-audit.csv # keep as a build artifact
See Audit trail and Compliance.
Monorepos
gitta compares pull requests by the code they change, so pull requests in unrelated parts of a monorepo get no warnings about each other. The gate still runs your whole test command. Make the gate a script that tests only the packages the two pull requests touch, for example with your build tool's affected-packages command.
Merge queues
With a merge queue, make the gitta check required on the pull request. A pull request then enters the queue only after gitta passed it, and the queue runs your full CI as usual.