# How to Configure Custom Lint, Test, and Format Commands per Repository in no-mistakes

> Learn to configure custom lint, test, and format commands per repository in no-mistakes by adding a commands section to your .no-mistakes.yaml. Control your workflows efficiently.

- Repository: [Kun Chen/no-mistakes](https://github.com/kunchenguid/no-mistakes)
- Tags: how-to-guide
- Published: 2026-06-27

---

**Add a `commands` section to your repository's [`.no-mistakes.yaml`](https://github.com/kunchenguid/no-mistakes/blob/main/.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`](https://github.com/kunchenguid/no-mistakes/blob/main/.no-mistakes.yaml) file at your repository root. This configuration is read into the `Config.Commands` struct defined in [`internal/config/config.go`](https://github.com/kunchenguid/no-mistakes/blob/main/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`](https://github.com/kunchenguid/no-mistakes/blob/main/.no-mistakes.yaml) file in your repository root with a `commands` section containing the shell commands for each stage:

```yaml
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:

```yaml
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`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/config/config.go):

1. **Global configuration** (`~/.no-mistakes/config.yaml`) parses into `GlobalConfig`.
2. **Per-repository configuration** ([`.no-mistakes.yaml`](https://github.com/kunchenguid/no-mistakes/blob/main/.no-mistakes.yaml)) parses into `RepoConfig`.
3. **`EffectiveRepoConfig`** merges the two, honoring the `allow_repo_commands` flag.
4. The final `Config` struct populates the `Commands` field used by pipeline steps.

According to the source code in [`internal/pipeline/steps/lint.go`](https://github.com/kunchenguid/no-mistakes/blob/main/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`](https://github.com/kunchenguid/no-mistakes/blob/main/.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 `commands` section to [`.no-mistakes.yaml`](https://github.com/kunchenguid/no-mistakes/blob/main/.no-mistakes.yaml) with `lint`, `test`, and `format` keys.
- By default, the daemon only trusts commands from the default branch to prevent arbitrary code execution from untrusted branches.
- Set `allow_repo_commands: true` only in trusted personal repositories to enable per-branch command customization.
- The configuration merges in [`internal/config/config.go`](https://github.com/kunchenguid/no-mistakes/blob/main/internal/config/config.go), producing an `EffectiveRepoConfig` that pipeline steps consume via `sctx.Config.Commands`.
- Debug configuration issues by running with `--log-level=debug` to 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`](https://github.com/kunchenguid/no-mistakes/blob/main/.no-mistakes.yaml) file located at the repository root. According to [`internal/config/config.go`](https://github.com/kunchenguid/no-mistakes/blob/main/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`](https://github.com/kunchenguid/no-mistakes/blob/main/.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`](https://github.com/kunchenguid/no-mistakes/blob/main/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`](https://github.com/kunchenguid/no-mistakes/blob/main/.no-mistakes.yaml) that the daemon will execute, creating a significant security risk in multi-contributor environments.