- Added a comprehensive plan for history rewrites in `docs/plans/history_rewrite.md`, including backup requirements and a checklist for destructive operations. - Created a QA report for history-rewrite scripts in `docs/reports/qa_report.md`, summarizing tests, findings, and recommendations. - Introduced `check_refs.sh` script to list branches and tags, saving a tarball of tag references. - Updated `clean_history.sh` to include non-interactive mode and improved error handling for backup branch pushes. - Enhanced `preview_removals.sh` to support JSON output format and added shallow clone detection. - Added Bats tests for `clean_history.sh` and `validate_after_rewrite.sh` to ensure functionality and error handling. - Implemented pre-commit hook to block commits to `data/backups/` directory. - Improved validation script to check for backup branch existence and run pre-commit checks. - Created temporary test scripts for validating `clean_history.sh` and `validate_after_rewrite.sh` functionality.
19 KiB
History Rewrite: Address Copilot Suggestions (PR #336)
Summary
- PR #336 introduced history-rewrite tooling, documentation, and a CI dry-run workflow to detect unwanted large blobs and CodeQL DB artifacts in repository history.
- Copilot left suggestions on the PR asserting a number of robustness, testing, validation, and safety improvements.
- This spec documents how to resolve those suggestions, lists the impacted files and functions, and provides an implementation & QA plan.
Copilot Suggestions (Short Summary)
- Improve
validate_after_rewrite.shto use a definedbackup_branchvariable and fail gracefully when missing. - Harden
clean_history.shandpreview_removals.shto handle shallow clones, tags, and refs, validategit-filter-repoargs, and double-check backups (include tags & annotated refs). - Add automated script unit tests (shell) for the scripts (preview/dry-run/validate) to make them testable and CI-friendly.
- Add a CI job to run these script tests (e.g.,
bats-core) and trap shallow clones early. - Expand pre-commit and
.gitignorecoverage (includedata/backups), validatebackup_branchpush, and refuse running filter-repo onmain/masteror non-existent remotes. - Add more detailed PR checklist validation (tags, backup branch pushed) and update docs/examples.
Files Changed / Impacted
Core scripts and CI currently touched by PR #336 and Copilot suggestions (primary targets):
- scripts/history-rewrite/clean_history.sh
- Functions:
check_requirements,timestamp,preview_removalsblock, localbackup_branchcreation. - Behaviors to harden: shallow clone handling; ensure backup branch pushed to remote and tags backed up; refuse to run on
main/master; confirmgit-filter-repoargs are validated; ensure remote tag backup.
- Functions:
- scripts/history-rewrite/preview_removals.sh
- Behaviors to add: more structured preview output (json or delimited), detect shallow clone and warn, add checks for tags & refs.
- scripts/history-rewrite/validate_after_rewrite.sh
- Fix bug:
backup_branchreferenced but not set, add env variable or accept--backup-branchargument; verify pre-commit location; exit non-zero on failures.
- Fix bug:
- scripts/ci/dry_run_history_rewrite.sh
- Add shallow clone detection and early fail with instructions to fetch full history; ensure
git rev-listdoes not grow too large on very large repositories (timeout or cap); fail on conditions.
- Add shallow clone detection and early fail with instructions to fetch full history; ensure
- .github/workflows/dry-run-history-rewrite.yml
- Behavior: run the new tests; ensure fetch-depth 0; add
batsrunner step orshellcheckrunner.
- Behavior: run the new tests; ensure fetch-depth 0; add
- .github/workflows/pr-checklist.yml
- Behavior: enhance validation of PR body for additional checklist items: ensure
data/backupslog is attached,tagsbackup statement, and maintainers ack for forced rewrite.
- Behavior: enhance validation of PR body for additional checklist items: ensure
- .github/PULL_REQUEST_TEMPLATE/history-rewrite.md
- Behavior: update the checklist with new checks for tags and
data/backups/and notevalidate_after_rewrite.shwill fail if not present.
- Behavior: update the checklist with new checks for tags and
- .gitignore
- Add
data/backups/to.gitignoreto ensure backup logs are not accidentally committed.
- Add
- .pre-commit-config.yaml
- Add a new
block-data-backups-commithook to prevent accidental commits todata/backups.
- Add a new
Potential Secondary Impact (best-guess; confirm):
- scripts/pre-commit-hooks/block-codeql-db-commits.sh (might need to be more strict): extend to check codeql-db-* and codeql-*.sarif patterns.
- scripts/ci/dry_run_history_rewrite.sh invocation in
.github/workflows/dry-run-history-rewrite.yml: adjust to ensurefetch-depth: 0is set and thatgitis non-shallow.
Implementation Plan (Phases)
PHASE 1 — Script Hardening (2-4 days)
- Goals: fix functional bugs, add validation checks, handle edge cases (shallow clones, tag preservation), make scripts idempotent and testable.
- Tasks:
- Update
scripts/history-rewrite/validate_after_rewrite.sh:- Add a command-line argument or
ENVfor--backup-branchand fallback to readingbackup_branchfrom the log indata/backupsif present. - Ensure it sets
backup_branchcorrectly or exits with a clear message. - Ensure it currently fails the build on any reported issues (non-zero exit when pre-commit fails in CI mode).
- Add a command-line argument or
- Update
scripts/history-rewrite/clean_history.sh:- Detect shallow clones (if
git rev-parse --is-shallow-repositoryreturns true) and fail with instructions togit fetch --unshallow. - When creating
backup_branch, also include tag backups:git tag -l | xargs -n1 -I{} git tag -l -n {}...and push tags tooriginintobackup/tags/history-YYYY...namespace OR save them todata/backups/tags-*.tar. - Validate
git-filter-repoargs are valid—usegit filter-repo --helpto confirm that provided--strip-blobs-bigger-thanargs are numbers and--pathsexist in repo for the dry-run case. - Ensure
backup_branchis pushed successfully, otherwise abort. - Make
read -r confirmationexplicit with--or a short timeout to avoid interactive hang; in scripts launched via terminal, interactive fallback is acceptable, but in CI this should not be used. Add--non-interactiveto skip confirmation in CI with an explicit flag and require maintainers to passFORCE=1in env to proceed.
- Detect shallow clones (if
- Update
scripts/history-rewrite/preview_removals.sh:- Add structured
--formatoption withtext(default) andjsonfor CI parsing; include commit oids, paths, and sizes in the output. - Detect & warn if the repo is shallow.
- Add structured
- Add a
scripts/history-rewrite/check_refs.shhelper:- Print current branches, tags, and any remotes pointing to objects in the paths to be removed.
- Output a tarball
data/backups/tags-YYYYMMDD.tarwith tag refs.
- Update
PHASE 2 — Testing & Automation (2-3 days)
- Goals: Add script unit tests and CI steps to run them; add a validation pipeline for maintainers to use.
- Tasks:
- Add
bats-coretest harness insidescripts/history-rewrite/tests/.scripts/history-rewrite/tests/preview_removals.bats— tests ensuring the preview prints commits and objects for specified paths.scripts/history-rewrite/tests/clean_history.dryrun.bats— tests that--dry-runexits non-zero when repo contains banned paths and that--forcerequires confirmation.scripts/history-rewrite/tests/validate_after_rewrite.bats— tests thatvalidate_after_rewrite.shuses--backup-branchand fails with the correct non-zero codes whenbackup_branchis missing.
- Add a
ci/scripts/test-history-rewrite.ymlworkflow to run bats tests in CI and to fail early on shallow clones or missing tools. - Add a script-level
shellcheckpass and abashminimal lint step; useshellcheckGitHub Action or pre-commit hook.
- Add
PHASE 3 — PR Pipeline & Pre-commit (1-2 days)
- Goals: Prevent accidental destructive runs and accidental commits of generated backups.
- Tasks:
- Update the PR template
.github/PULL_REQUEST_TEMPLATE/history-rewrite.mdadding checklist items: tag backups, confirmdata/backupstarball included, confirm remote pushed backup branch and tags, optionalCI verification outputfrompreview_removals --format json. - Update
.github/workflows/pr-checklist.ymlto validate: presence ofpreview_removalsoutput in PR body, a check thatdata/backupsis attached, and additional keywords liketag backupandbackup branch pushed. - Add
.pre-commit-config.yamlhook to block commits todata/backupsand ensuredata/backupsis added to.gitignore. - Add
scripts/pre-commit-hooks/validate-backup-branch.shwhich verifies thatbackup_branchexists and points to the expected ref(s).
- Update the PR template
PHASE 4 — Docs, QA & Rollout (1-2 days)
- Goals: Update docs, add reproducible tests, and provide QA instructions and rollback strategies.
- Tasks:
- Update
docs/plans/history_rewrite.mdto include:backup_branchnaming and tagging policydata/backupslayout, e.g.,metadata.json,tags.tar.gz,logpaths- Example
preview_removals --format jsonoutput for PR inclusion
- Add
docs/plans/current_spec.md(this file) containing the execution plan and timeline estimate. - QA steps: run
clean_history.sh --dry-run,preview_removals.shwith--format jsonfor PR attachments, then proceed with--forceonly after maintainers confirm window; verify viavalidate_after_rewrite.shand CI.
- Update
PHASE 5 — Post-Deploy & Maintenance (1 day)
- Run
git gcand prune on mirrors; notify downstream consumers; update CI mirrors and caches. Verify repository size decreased within expected tolerance.
Unit & Integration Tests (Files & Functions)
Add these test files to scripts/history-rewrite/tests/.
Unit test harness: bats-core recommended; tests should run without network and create ephemeral local repositories.
-
scripts/history-rewrite/tests/preview_removals.bats:- test_preview_detects_banned_commits()
- test_preview_detects_large_blob_sizes()
- test_preview_outputs_json_when_requested()
-
scripts/history-rewrite/tests/clean_history.dryrun.bats:- test_dry_run_exits_success_when_no_banned_paths()
- test_dry_run_reports_banned_commits()
- test_force_requires_confirmation() — simulate interactive confirmation or set
FORCE=1with--non-interactiveflag to test non-interactive usage. - test_refuse_on_main_branch() — ensures script refuses to run on
main/master.
-
scripts/history-rewrite/tests/validate_after_rewrite.bats:- test_validate_fails_when_backup_branch_missing()
- test_validate_passes_when_backup_branch_provided_and_all_checks_clear()
- test_validate_populates_log_and_error_when_precommit_fails()
Integration test (bash / simulated repository): a test that acts as a small git repo containing a backend/codeql-db folder and a large fake blob.
scripts/history-rewrite/tests/integration_clean_history.bats:- test_integration_end_to_end_preview_then_dry_run(): create a local repo, add a large file under
backend/codeql-db, commit it, runpreview_removalsto capture output, ensureclean_history.sh --dry-rundetects it, then runclean_history.sh --forcebut only after backing up repo; verifygit rev-listno longer returns commits for that path.
- test_integration_end_to_end_preview_then_dry_run(): create a local repo, add a large file under
Exact tests & names (for maintainers' convenience):
scripts/history-rewrite/tests/preview_removals.bats::test_preview_detects_banned_commitsscripts/history-rewrite/tests/preview_removals.bats::test_preview_outputs_jsonscripts/history-rewrite/tests/clean_history.dryrun.bats::test_dry_run_reports_banned_commitsscripts/history-rewrite/tests/clean_history.dryrun.bats::test_force_requires_confirmationscripts/history-rewrite/tests/validate_after_rewrite.bats::test_validate_fails_when_backup_branch_missingscripts/history-rewrite/tests/integration_clean_history.bats::test_integration_end_to_end_preview_then_dry_run
CI & Pre-commit Changes
- Add
data/backups/to.gitignore(to avoid accidental commits of backup logs) and ensurescriptsproduce readabledata/backupslogs that can be attached to PRs. - Add a new pre-commit hook
scripts/pre-commit-hooks/block-data-backups-commit.shto block user commits ofdata/backupsanddata/backups/*(mirrorblock-codeql-db-commits.sh). - Add
shellcheckto the pre-commit config or add ascripts/ci/shellcheck_history_rewrite.ymlworkflow that ensures scripts pass style checks. - Create a new CI workflow:
.github/workflows/history-rewrite-tests.yml- Steps: Checkout with
fetch-depth: 0, install bats-core via apt or package manager, run thebatstests, runshellcheckfor scripts, and runscripts/ci/dry_run_history_rewrite.sh.
- Steps: Checkout with
- Update existing
.github/workflows/dry-run-history-rewrite.ymlto:- Ensure
fetch-depth: 0inactions/checkoutis set (already the case), and fail early for shallow clones; add ashellcheckstep andbatstests step.
- Ensure
Potential Regressions & Rollback Strategies
-
Regressions:
- Accidental removal of unrelated history entries due to incorrect
--pathsor--invert-pathsusage. - Loss of tags or refs if not properly backed up and pushed to a safe place before rewrite.
- CI breakage from new pre-commit hooks or failing
batstests. - Developer pipelines or forks could break from forced
--all --forcepush if they do not follow the rollback steps.
- Accidental removal of unrelated history entries due to incorrect
-
Mitigations & Rollback:
- Always create backups:
backup_branchandbackup/tags/history-YYYYMMDDtarball stored outside the working repo (S3/GitHub release) prior to any--forcepush. - Maintain a simple rollback command sequence in the docs:
git checkout -b restore/DATE backup/history-YYYYMMDD-HHMMSSgit push origin restore/DATEand create a PR to restore the history (or directly replace refs on the remote as maintainers decide)
- Keep the
data/backups/tarball outside the repo in a known remote location (this will also help recovery if thebackup_branchis not visible). - Ensure CI
dry-runworkflow is fully functional and fails on shallow clones so maintainers must re-run with a proper clone. - Add a section in
docs/plans/history_rewrite.mdto show commands to restore tags if they were mistakenly deleted.
- Always create backups:
Backwards Compatibility & Maintainers' Notes
- The scripts must remain POSIX-compliant where pragmatic; use
/bin/shfor portability. - Avoid automatic
git push --all --forcefrom scripts; maintainers must perform final coordinated push. - Scripts will remain safe by default (
--dry-runor interactive) with--forceand explicitI UNDERSTANDconfirmation for destructive operations.
Timeline Estimate (Rough)
- Script hardening: 2-4 days
- Tests & CI: 2-3 days
- PR pipeline updates & pre-commit hooks: 1-2 days
- Docs, QA & rollout ( manual coordination): 1-2 days
- Total: 6-11 business days (one-to-two weeks), may vary with availability of maintainers and CR feedback.
Deployment Checklist for Maintainers
Before scheduling a destructive rewrite:
- Verify all
batstests inscripts/history-rewrite/testspass on CI. - Ensure backup branches and tags are pushed to
origin(and optionally exported to external storage like an S3 bucket). - Confirm the PR uses
.github/PULL_REQUEST_TEMPLATE/history-rewrite.mdand the PR automation passes. - Run full
scripts/history-rewrite/clean_history.sh --dry-runandscripts/history-rewrite/preview_removals.sh --format jsonlocally and attach outputs to the PR. - Have at least two maintainers approve the destructive rewrite before pushing
git push --all --force.
Development checklist
- Implement described script and validation changes.
- Add
batstests andhistory-rewritetest CI workflow. - Add
data/backups/to.gitignoreand add pre-commit hooks to block accidental commits. - Update
pr-checklist.ymlto include tag-backup checks, backup logs, and PR content checks. - Add maintainers' docs and rollback examples.
Follow-ups / Outstanding Questions (ask maintainers)
- Should
data/backupsremain inside repo (but ignored) or be offloaded to a remote store before the rewrite? - Should
clean_history.shcreate an optional tarball ofrefsandtagsand push toorigin/backups/or an alternate remote repository for longer term storage? - For CI (bats) tests: do we want to install
bats-corein the main CI image, or depend on an apt install in thehistory-rewrite-testsworkflow? - Is
git-filter-repopresent on official runner images or should we install it in the CI workflow each time? (script currently exits withPlease install git-filter-repoadvisory.)
Appendix: Example bats Test Skeleton (preview_removals)
You can start implementing the tests with bats like the following skeleton:
#!/usr/bin/env bats
setup() {
repo_dir="$(mktemp -d)"
cd "$repo_dir"
git init -q
mkdir -p backend/codeql-db
echo "largefile" > backend/codeql-db/big.txt
git add -A
git commit -m "feat: add dummy codeql-db file" || exit 1
}
teardown() {
rm -rf "$repo_dir"
}
@test "preview_removals reports commits in path" {
run sh /workspace/scripts/history-rewrite/preview_removals.sh --paths 'backend/codeql-db' --strip-size 1
[ "$status" -eq 0 ]
[[ "$output" == *"Commits touching specified paths"* ]]
}
This same pattern can be reused to spawn a test repository and run clean_history.sh --dry-run, validate_after_rewrite.sh and assert expected outputs and exit codes.
Done.
Investigation and Remediation Plan: CI Failures on feature/beta-release
1. Incident Summary
Issue: CI builds failing on feature/beta-release.
Symptoms:
- Frontend build fails due to missing module
../data/crowdsecPresets. - Backend coverage check fails (likely due to missing tests or artifacts).
- Docker build fails. Root Cause Identified:
- The file
frontend/src/data/crowdsecPresets.tsexists locally but was ignored by git due to an overly broad pattern in.gitignore. - The pattern
data/in.gitignore(intended for the rootdata/directory) accidentally matchedfrontend/src/data/.
2. Diagnosis Details
- Local Environment: The file
frontend/src/data/crowdsecPresets.tswas present, so localnpm run buildandnpm run test:cipassed. - CI Environment: The file was missing because it was not committed.
- Git Ignore Analysis:
.gitignorecontaineddata/under "Caddy Runtime Data".- This pattern matches any directory named
dataanywhere in the tree. - It matched
frontend/src/data/, causingcrowdsecPresets.tsto be ignored.
3. Remediation Steps
- Fix
.gitignore:- Change
data/to/data/to anchor it to the project root. - Change
frontend/frontend/to/frontend/frontend/for safety.
- Change
- Add Missing File:
- Force add or add
frontend/src/data/crowdsecPresets.tsafter fixing.gitignore.
- Force add or add
- Verify:
- Run
git check-ignoreto ensure the file is no longer ignored. - Run local build/test to ensure no regressions.
- Run
4. Verification Results
- Local Tests:
- Backend Coverage: 85.4% (Pass)
- Frontend Tests: 70 files passed (Pass)
- Frontend Coverage: 85.97% (Pass)
- Build: Passed
- Git Status:
frontend/src/data/crowdsecPresets.tsis now staged for commit..gitignoreis modified and staged.
5. Next Actions
- Commit the changes with message:
fix: resolve CI failures by unignoring frontend data files. - Push to
feature/beta-release. - Monitor the next CI run.
6. Future Prevention
- Use anchored paths (starting with
/) in.gitignorefor root-level directories. - Check
git statusfor unexpected ignored files when adding new directories. - Add a pre-commit check or CI step to verify that all imported modules exist in the git tree (though
tscin CI does this, the issue was the discrepancy between local and CI).