Skip to content

Guardrails ​

A guardrail is a reusable check that runs in your pipeline and produces a verdict. To enable one, list it in buildnote.json and run buildnote guardrails. The CLI fetches the guardrail, runs its check on the runner, records the verdict in Buildnote as a guardrail event, comments the result on the pull request, and can fail the build.

Guardrails cover the rules your team agrees on but has no good place to enforce, such as a commit message format, no merge commits, a dependency policy or a minimum coverage level. You configure guardrails instead of writing them, and every run is recorded. That way you can see how a rule trends across builds, not just whether today's build passed.

Browse the library ​

Buildnote ships 125 guardrails. You can filter them by name, by what they check, by category or by severity. Open a guardrail to see its inputs, an example configuration and how to fix what it finds.

125 guardrails. Showing the first 40.

azure1

build4

claude3

clojure4

commands1

cpp4

docker5

docs7

dotnet4

elixir3

git4

Categories ​

Each category is one directory in the library. Its guardrails share a version and any inputs they have in common. A category's page lists all of its guardrails and their shared inputs.

CategoryGuardrails
azure1
build4
claude3
clojure4
commands1
cpp4
docker5
docs7
dotnet4
elixir3
git7
github11
gitlab7
golang4
java3
jenkins2
kotlin4
maven4
nodejs4
php3
python4
ruby4
rust4
scala4
secrets4
security4
supply-chain6
terraform6
tests4

To write your own, see Writing a guardrail. A guardrail rarely gathers its own evidence. Instead, it declares a collector, which gathers information about the repository, the build and the files once per run, and passes the same document to every guardrail that asked for it.

Configuration ​

Configuring guardrails describes every option.

Running ​

bash
buildnote guardrails --org company --project web --module ci --build 1234

In a CI environment that Buildnote recognises, the organisation, project, module, build, commit and ref are detected automatically, so usually all you need is:

bash
buildnote guardrails

Use --only <id> to run a single guardrail, --dry-run to print the verdicts without recording or reporting anything, and --fail-on never to report results without failing the build. The command reference lists every option.

Verdicts ​

Every guardrail run ends with one of three verdicts.

VerdictMeaningEvent status
passedThe check ran and found nothing over the guardrail's threshold.successful
failedThe check ran and found more violations than the guardrail allows.failed
skippedThe check couldn't gather its evidence, so it can't give a result.skipped

A skip is a normal, expected result. If a guardrail can't see what it needs, it never fails your build. The CLI reports skipped when:

  • the check reports that it can't run, for example because a base ref doesn't resolve on a shallow clone
  • the check produces no readable result
  • the interpreter is missing on the runner, or the check runs past its timeout
  • the guardrail's source can't be reached, so its definition or script couldn't be read
  • the pinned sha256 doesn't match the script that was fetched (the CLI prints a warning instead of passing quietly)

After a check runs, the exemptions in your check entry are applied to the violations it reported. The remaining violations are compared with the guardrail's maxViolations, and that decides between failed and passed.

Failing the build ​

failOn decides which verdicts fail the build.

ValueThe build fails when
error (default)a guardrail with severity error fails
warninga guardrail with severity error or warning fails
nevernever

The command always records the verdicts and posts the pull request comment before it exits, so a failed build still reports everything it found. A guardrail can still fail the build even if its events can't be submitted.

A guardrail that can't be loaded at all is not a verdict, and failOn doesn't apply to it. If a definition is malformed, declares an id other than the one referenced, or names a script whose extension doesn't match an interpreter, that's a configuration error. The command reports it, runs every other guardrail, and then exits with a non zero code whatever failOn says. A source that simply can't be reached is different: it counts as missing evidence, so it's recorded as skipped.

Reporting ​

Guardrails use the same reporters as buildnote report. You configure them in the same report section of buildnote.json and adjust them with the same --slack-*, --discord-*, --github-* and --html-* options. Every reporter receives the same verdict table, most severe first, with the evidence and remediation for each failed guardrail. The verdict table is the whole report, so components and template have no effect.

ReporterWhat it does
githubWrites the markdown to the job summary and posts it on the pull request as a comment titled Guardrails. Every report.github option works the same as for buildnote report: enabled and condition decide whether anything is reported to GitHub, commentEnabled and commentUpdates decide whether the comment is posted and updated, and commentTitle replaces the default title. A re-run updates the existing comment instead of adding a new one, and the default title never clashes with the test summary comment.
htmlWrites a standalone Guardrails document. Without an outputFile, the file is buildnote-guardrails.html. With one, it's written next to it with a -guardrails suffix, so build/report.html gives build/report-guardrails.html and your test report isn't overwritten.
slackPosts a notification with one line and a status icon per guardrail.
discordPosts the same notification as a Discord message.

A reporter runs when it's enabled and its condition passes, just like with buildnote report. The condition is evaluated against the guardrail run instead of the test run, so the same expressions refer to the guardrail results:

VariableMeaning
successful failed skippedBooleans for the run's overall verdict.
successful.count failed.count skipped.countHow many guardrails reached each verdict.
totalHow many guardrails ran.
violationsViolations across the whole run.
statussuccessful, failed, skipped or unknown.
always nevertrue and false.
org project module build sha refThe build coordinates.

If an expression can't be resolved, it becomes empty instead of stopping the run. If a reporter can't be resolved at all, the command reports the problem and skips that reporter. Neither changes the exit code.

The pull request comment needs a GitHub token, from report.github.token or the --github-token option. If there's no token, no pull request, or "commentEnabled": false is set, the command skips the comment and carries on. The GitHub reporter is enabled unless report.github says otherwise, so a repository with no report block still gets the job summary and the comment. Set "github": { "enabled": false } to turn both off. A reporter failure never changes the exit code. Only the verdicts decide that.

Querying verdicts ​

Every run is recorded as two kinds of guardrail event, which you can query through the guardrails table. One run event summarises the whole command. It holds the total violation count and, as attachments, the facts every collector gathered. Each guardrail also gets a check event with its own verdict, linked back to the run:

sql
SELECT guardrail, status, violations FROM guardrails WHERE build = '1234' AND category = 'check'
sql
SELECT guardrail, COUNT(*) AS failures FROM guardrails WHERE status = 'failed' AND category = 'check' GROUP BY guardrail ORDER BY failures DESC

Running a guardrail again in the same build replaces its earlier verdict, so each build has one row per guardrail. The entity_id stays the same across builds, so you can track one guardrail over time.

Buildnote Limited
Registered in England and Wales, Reg: 16140412