From f4275b9ac0720fe45d72ad95c49096a11da1df40 Mon Sep 17 00:00:00 2001 From: clawbot Date: Mon, 9 Nov 2026 18:49:16 +0000 Subject: [PATCH] script/install: stop on a repo's own script of the same name script/install replaced any tracked script/install or script/update-policy in the target, including a repo's own unrelated script. It now stops when such a file has a line the canonical file has never held. An older shared copy is still replaced. The padded file in the self-replacement test is now an earlier canonical version, so the new stop does not catch it. Model: opus-5-5 --- prompts/EXISTING_REPO_CHECKLIST.md | 3 ++- script/install | 16 ++++++++++++++++ test/update-policy.sh | 26 +++++++++++++++++++++++++- 3 files changed, 43 insertions(+), 2 deletions(-) diff --git a/prompts/EXISTING_REPO_CHECKLIST.md b/prompts/EXISTING_REPO_CHECKLIST.md index 286cfcf..92d68e1 100644 --- a/prompts/EXISTING_REPO_CHECKLIST.md +++ b/prompts/EXISTING_REPO_CHECKLIST.md @@ -221,7 +221,8 @@ with your task. anything, naming the file and the entry. It stops the same way on a line in `.golangci.yml` that the canonical file has never held, apart from comments and the repo's entries in the `deny` list of the `test-support` - rule. + rule. It also stops on a repo's own `script/install` or + `script/update-policy`. - [ ] README has an **Entrypoints** section documenting the `script/` entrypoints and linking the standard - [ ] `script/lint` and `script/test` each run diff --git a/script/install b/script/install index 788219f..d7667db 100755 --- a/script/install +++ b/script/install @@ -174,6 +174,19 @@ $own" fi } +# stop_on_own_script : stop if the local script has a +# line the canonical file has never held, unless it is blank or a +# comment: the repo wrote that script itself, and replacing it would +# delete it. A repo without the script is fine. +stop_on_own_script() { + [ -e "$2" ] || return 0 + own="$(local_only_lines "$1" "$2")" + if [ -n "$own" ]; then + die "$2 has lines the canonical file has never held; rename the repo's own script to keep it: +$own" + fi +} + main() { [ "$#" -le 1 ] || die "usage: script/install [path of a repo clone]" @@ -221,6 +234,9 @@ main() { .golangci.yml) keep_deny_entries "$tmp/$path" "$path" ;; + script/*) + stop_on_own_script "$tmp/$path" "$path" + ;; esac done diff --git a/test/update-policy.sh b/test/update-policy.sh index 70e1094..c847283 100755 --- a/test/update-policy.sh +++ b/test/update-policy.sh @@ -415,7 +415,8 @@ test_target_lacking_install_gets_both_scripts() { # The shell reads a script in blocks, so the padding makes it read the # last line only after main has returned. It finds that line only if -# script/install left the old file in place under a new name. +# script/install left the old file in place under a new name. The old +# file is an earlier canonical version, so script/install replaces it. test_update_policy_replaces_itself() { start_scenario "script/update-policy replaces its own file and still exits 0" { @@ -427,6 +428,10 @@ test_update_policy_replaces_itself() { done echo "echo 'the old file ran to its end'" } > "$dir/merged" + cp "$dir/merged" "$dir/canonical/script/update-policy" + commit_canonical + cp "$UPDATE_POLICY" "$dir/canonical/script/update-policy" + commit_canonical mv "$dir/merged" "$dir/repo/script/update-policy" chmod 755 "$dir/repo/script/update-policy" commit_and_push_fixture @@ -575,6 +580,17 @@ EOF expect_no_commit } +test_own_script_stops() { + start_scenario "a tracked script/install with a line the canonical file has never held stops" + echo "echo our own installer" >> "$dir/repo/script/install" + commit_and_push_fixture .prettierrc + run_update_policy + expect_failure + expect_output "script/install has lines" + expect_output "echo our own installer" + expect_nothing_written +} + test_gitignore_lacks_canonical_last_line() { start_scenario ".gitignore lacking the canonical last line stops" printf '*.log\n/myapp\n' > "$dir/repo/.gitignore" @@ -688,6 +704,12 @@ test_former_canonical_gitignore_line_replaced() { expect_former_canonical_line_replaced .gitignore "*.old" } +test_former_canonical_script_line_replaced() { + start_scenario "a script/install line an earlier canonical commit held is replaced" + expect_former_canonical_line_replaced script/install "echo an older install" + expect_same_as_canonical script/install +} + # A line another file's history removed does not count as held by this # one. test_line_removed_from_another_file_still_stops() { @@ -722,6 +744,7 @@ main() { test_own_entry_above_closing_comment test_uncommitted_change_stops test_untracked_script_stops + test_own_script_stops test_gitignore_lacks_canonical_last_line test_fetched_golangci_without_deny_list test_own_golangci_setting_stops @@ -730,6 +753,7 @@ main() { test_update_policy_takes_no_argument test_former_canonical_golangci_line_replaced test_former_canonical_gitignore_line_replaced + test_former_canonical_script_line_replaced test_line_removed_from_another_file_still_stops echo "update-policy tests passed" }