How to Configure Custom Lint, Test, and Format Commands per Repository in no-mistakes
Add a commands section to your repository's .no-mistakes.yaml file to override default lint, test, and format commands, and set allow_repo_commands: true only if you need per-branch customization in trusted personal repositories.
The no-mistakes continuous integration pipeline from kunchenguid/no-mistakes provides sane defaults for linting, testing, and formatting, but most projects require tooling specific to their language or framework. You can configure custom lint, test, and format commands per repository by editing the .no-mistakes.yaml file at your repository root. This configuration is read into the Config.Commands struct defined in internal/config/config.go and executed during the respective pipeline steps.
Configuring Custom Commands in .no-mistakes.yaml
Basic Command Overrides
Place a .no-mistakes.yaml file in your repository root with a commands section containing the shell commands for each stage:
commands:
lint: "golangci-lint run ./..."
test: "go test -race ./..."
format: "gofmt -w ."
When allow_repo_commands is omitted or set to false (the default), the daemon reads these values from the default branch only. This ensures that only maintainer-approved commands execute in shared repositories, preventing arbitrary code execution from untrusted pull requests.
Enabling Per-Branch Commands (Security Considerations)
For single-developer workflows or trusted personal repositories where you want feature branches to define their own commands, enable the allow_repo_commands flag:
allow_repo_commands: true
commands:
lint: "my-custom-linter --fix"
test: "bazel test //..."
format: "clang-format -i **/*.cpp"
Security warning: When allow_repo_commands is true, the daemon loads the commands block from the pushed SHA rather than the default branch. Only enable this in repositories where you trust every branch you push, as it allows arbitrary code execution via the configuration file.
How Configuration Merging Works
The no-mistakes daemon merges configuration through a layered hierarchy defined in internal/config/config.go:
- Global configuration (
~/.no-mistakes/config.yaml) parses intoGlobalConfig. - Per-repository configuration (
.no-mistakes.yaml) parses intoRepoConfig. EffectiveRepoConfigmerges the two, honoring theallow_repo_commandsflag.- The final
Configstruct populates theCommandsfield used by pipeline steps.
According to the source code in internal/pipeline/steps/lint.go, the lint step retrieves the command via sctx.Config.Commands.Lint and executes it through runStepShellCommand. The same pattern applies to test and format steps.
Troubleshooting Common Configuration Issues
| Symptom | Cause | Fix |
|---|---|---|
| Lint step prints "no lint command configured" | The commands.lint field is empty in the trusted config |
Add a lint entry in .no-mistakes.yaml on the default branch |
| Lint runs a different command than expected | allow_repo_commands is false and you edited the file on a feature branch |
Commit the command change to the default branch, or enable allow_repo_commands (trusted repos only) |
| Pipeline aborts with "code-executing selection fields are empty" | commands field is present but allow_repo_commands is true and the pushed branch omitted lint/test/format |
Ensure all three keys are present when allow_repo_commands: true |
Summary
- Configure custom commands by adding a
commandssection to.no-mistakes.yamlwithlint,test, andformatkeys. - By default, the daemon only trusts commands from the default branch to prevent arbitrary code execution from untrusted branches.
- Set
allow_repo_commands: trueonly in trusted personal repositories to enable per-branch command customization. - The configuration merges in
internal/config/config.go, producing anEffectiveRepoConfigthat pipeline steps consume viasctx.Config.Commands. - Debug configuration issues by running with
--log-level=debugto see the effective command that will execute.
Frequently Asked Questions
Where does no-mistakes read custom commands from?
The daemon reads custom commands from the .no-mistakes.yaml file located at the repository root. According to internal/config/config.go, these values populate the RepoConfig.Commands struct, which is then merged into the effective configuration used by the pipeline.
Why are my custom commands ignored when pushing to a feature branch?
By default, allow_repo_commands is false, which forces the daemon to use the .no-mistakes.yaml from the repository's default branch rather than your feature branch. This security measure prevents contributors from executing arbitrary commands through pull requests. To use branch-specific commands, you must set allow_repo_commands: true in the configuration.
What happens if I omit the commands section?
If the commands section is missing or empty, the pipeline step will report that no command is configured for that stage. The lint step in internal/pipeline/steps/lint.go checks sctx.Config.Commands.Lint and will fail with a message indicating no lint command is configured unless you provide one in the trusted configuration.
Is it safe to enable allow_repo_commands in team repositories?
No. You should only enable allow_repo_commands: true in single-developer repositories or situations where you completely trust every branch. When enabled, any branch can define arbitrary shell commands in its .no-mistakes.yaml that the daemon will execute, creating a significant security risk in multi-contributor environments.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →