Skip to content

pre-commit: detect-secrets runs in parallel batches that race on .secrets.baseline, so large commits and base merges can't commit #16343

Description

@mrveiss

Problem

The detect-secrets pre-commit hook added in #16300 (.pre-commit-config.yaml:615-619) doesn't set require_serial: true, and neither does upstream's hook manifest (Yelp/detect-secrets v1.5.0 .pre-commit-hooks.yaml: id, name, entry: detect-secrets-hook, language: python, files: .*, nothing more).

pre-commit therefore runs detect-secrets-hook in parallel batches. Each batch process reads .secrets.baseline, updates the line numbers for its files, and writes the whole file back. The writers race, the last one wins, and every other batch's updates are lost. The hook then fails with "Please git add .secrets.baseline", and each retry keeps only a fraction of the needed updates.

Evidence (2026-09-11)

A base merge into #16199's branch, touching hundreds of files after #16300 regenerated the baseline, failed on this hook on every commit attempt:

  • The pending rewrite shrank on each retry, from 167 lines to 111 lines, instead of settling in one pass. That's the signature of partial writes.
  • Every rewrite changed only line_number fields relative to base's baseline: no hashed secret, filename or type. So the rewrites were correct, just lost to the race.
  • Any commit touching many baselined files, including every base merge, hits it. Smaller commits usually land in a single batch and pass.

Fix

Add require_serial: true to the detect-secrets hook entry in .pre-commit-config.yaml, which overrides upstream's manifest. It's one line, and it costs a little speed on large commits.

Acceptance criteria

  • The detect-secrets hook entry has require_serial: true.
  • A commit touching many baselined files updates .secrets.baseline in a single pass. Show it with a base merge that commits on its first attempt after staging the baseline once.
  • A guard test pins require_serial: true on any hook that rewrites a shared file (at least detect-secrets), so the setting can't be dropped silently.

Activity

  1. mrveiss commented on Sep 11, 2026

    @mrveiss
    OwnerAuthor

    A second cause, found independently while merging base into #16273: base's own .secrets.baseline has stale line numbers. Later PRs shifted lines in files with baselined entries after #16300 regenerated the baseline. So any commit that stages those files, and every base merge does, gets a baseline rewrite from the hook, even after the race is fixed. For now, PR authors either take that unrelated churn or work around it by merging in the other direction (a branch off base that merges the PR, so only the PR's files are staged), which is how #16273 is handling it.

    Acceptance criteria added:

    • The same PR refreshes .secrets.baseline's line_number fields to base's current content, in one commit whose diff changes only line_number fields (no hashed_secret, filename, type or is_secret change). That's verified by a field-level diff against base's baseline.
    • After it lands, a base merge into any open PR produces no .secrets.baseline change.
  2. added this to the v0.9.0 milestone on Sep 12, 2026
  3. github-actions commented on Sep 12, 2026

    @github-actions
    Contributor

    PR #16349 (merged to Dev_new_gui) references this issue with a close keyword.

    fix(pre-commit): run detect-secrets serially and refresh the baseline's line numbers (#16343)

    If this issue is fully resolved, close it manually. If work remains, no action is needed.

  4. mrveiss commented on Sep 12, 2026

    @mrveiss
    OwnerAuthor

    Closing: all 3 criteria met on Dev_new_gui (4a7e76e28), delivered by #16349.

    AC Evidence
    1. require_serial: true on the hook .pre-commit-config.yaml:624-627 id: detect-secrets with a comment explaining upstream sets none
    2. A base merge commits in one pass Mechanism fixed by AC1; the guard test below pins it structurally
    3. A guard test pins it, can't drop silently repo_tests/precommit_baseline_hooks_run_serially_16343_test.py:62,72,82 — presence, serial-running, and the detector's own accept/reject self-test
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions