CI gate¶
tingle check is stat --diff with an opinion. It measures the
same branch impact, then exits 1 if the branch made things worse — so a
pull request that takes on debt fails the build.
Output is trimmed to what CI needs: the metrics that grew, and only the lines the branch added.
$ tingle check
noqa-comment (regex_count): +2
+ src/api/views.py:23
+ src/api/views.py:41
ruff-ignores (toml_list_length): +1
+ pyproject.toml: D203
$ echo $?
1
A branch that worsens nothing prints nothing and exits 0.
Policies¶
Two policies decide what "worse" means:
| Policy | Fails when |
|---|---|
sum (default) |
the metrics grow in total — paying off debt in one metric offsets taking it on in another |
any |
any single metric grows, whatever else improved |
--policy sum|any overrides the config for a single run.
Under sum, a metric can grow and the branch still pass. Its added lines
are still printed, because the trade is worth seeing even when it is
allowed.
Ignoring metrics¶
ignore names metrics that are expected to grow — lines of code, say. They
take no part in the check: they neither move the total nor fail the build,
and they stay out of the output.
A name that matches no configured metric is a config error, so a typo cannot silently ignore nothing.
Exit codes¶
| Code | Meaning |
|---|---|
0 |
the branch worsened nothing |
1 |
the branch is a regression under the active policy (or a metric function failed) |
2 |
config or usage error |
GitHub Actions¶
A complete workflow that fails a pull request which takes on debt:
name: Metrics
on: [pull_request]
jobs:
tingle:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # check needs history for the merge-base
- uses: actions/setup-python@v5
with:
python-version: "3.13"
- run: pip install tingle
- run: tingle check
fetch-depth: 0 is not optional
actions/checkout clones shallow by default, and check needs history
to find the merge-base with the base branch. Without it the job fails on
every run.
Advisory: report without blocking the merge¶
To see the branch's debt on every pull request without gating it, put
check in its own job with continue-on-error: true. The job goes red when
the branch worsens the metrics — so the number is visible in the checks list
— but the workflow's overall conclusion stays green, so the merge is not
blocked. This is how tingle's own CI runs it.
tingle:
runs-on: ubuntu-latest
continue-on-error: true # report, but never block the merge
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- run: pip install tingle
- run: tingle check
Branch protection overrides this
continue-on-error keeps the workflow from failing, but it does not
make a check non-required. If the job is listed as a required status
check in a branch protection rule, it will still block the merge. Leave
it out of that list.
This pairs well with [check] ignore and policy = "sum": start advisory
to learn what your branches actually do to the numbers, then drop
continue-on-error once the baseline is honest.
Publishing the numbers¶
check only judges. To also record the values, add stat --json — it is a
separate command and does not affect the exit code:
- run: tingle stat --json > metrics.json
- run: jq -r '.metrics[] | "\(.name)\t\(.value)"' metrics.json
- uses: actions/upload-artifact@v4
with:
name: metrics
path: metrics.json
GitLab¶
Set GIT_DEPTH: 0 so the merge-base is reachable. tingle report
--cobertura emits Cobertura XML in which each occurrence line is an
uncovered line, which the MR widget consumes directly.