diff --git a/prompts/EXISTING_REPO_CHECKLIST.md b/prompts/EXISTING_REPO_CHECKLIST.md index f62e99a..fbc2214 100644 --- a/prompts/EXISTING_REPO_CHECKLIST.md +++ b/prompts/EXISTING_REPO_CHECKLIST.md @@ -214,15 +214,18 @@ with your task. - [ ] `script/install` and `script/update-policy` exist, identical to the model scripts, and `make update-policy` shims to `script/update-policy`. `make update-policy` commits without pushing, so a person reviews and - pushes the commit. Before its first use, the repo's own entries in - `.gitignore`, `.editorconfig` and `.dockerignore` go below the comment - that ends each canonical file. An entry above that comment which the - canonical file has never held makes the script stop before it writes - 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. It also stops on a repo's own `script/install` or - `script/update-policy`. + pushes the commit. A `.gitignore` or `.dockerignore` without the comment + that ends the canonical file needs no preparation. The script moves each + line the canonical file has never held to below the canonical content. + Blank lines and comments are dropped. Before its first use, add that + comment to an `.editorconfig` that has lines of its own, and put those + lines below it. Otherwise the script stops before it writes anything, + naming the file and each line. In a file that has the comment, an entry + above it which the canonical file has never held also stops the script. 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. 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 331be19..842f727 100755 --- a/script/install +++ b/script/install @@ -15,6 +15,12 @@ # - .gitignore, .editorconfig, .dockerignore: the lines below the # canonical file's last line. An entry above that line which the # canonical file has never held stops the script. +# - .gitignore, .dockerignore lacking that last line: each line the +# canonical file has never held, unless it is blank or a comment, +# goes below the canonical content. +# - .editorconfig lacking that last line: it is replaced if it has no +# such line. Otherwise the script stops, because a setting means +# nothing away from its section. # - .golangci.yml: the entries the repo added to the deny list of the # test-support rule. Any other line the canonical file has never # held stops the script, unless it is blank or a comment. @@ -117,12 +123,23 @@ local_only_lines() { # keep_lines_below_last : append to the fetched file # whatever follows its last line in the local file. Stop if the canonical # file has never held a line above that one, unless it is blank or a -# comment: replacing the file would delete it. +# comment: replacing the file would delete it. If the local file lacks +# that last line, append each line the canonical file has never held, +# unless it is blank or a comment. An .editorconfig with such a line +# stops the script instead, because a setting means nothing away from +# its section. keep_lines_below_last() { last="$(tail -n 1 "$1")" number="$(grep -n -F -x -- "$last" "$2" | head -n 1 | cut -d: -f1)" if [ -z "$last" ] || [ -z "$number" ]; then - die "$2 lacks this line; add it and put the repo's own entries below it: $last" + own="$(local_only_lines "$1" "$2")" + [ -n "$own" ] || return 0 + if [ "$2" = .editorconfig ]; then + die "$2 lacks this line and has lines the canonical file has never held; add it and put those lines below it: $last +$own" + fi + printf '%s\n' "$own" >> "$1" + return 0 fi own="$(local_only_lines "$1" "$2" "$number")" if [ -n "$own" ]; then diff --git a/test/update-policy.sh b/test/update-policy.sh index 8a1ac08..4c1c144 100755 --- a/test/update-policy.sh +++ b/test/update-policy.sh @@ -45,7 +45,7 @@ not_found() { serve_file() { [ -f "$canonical/$1" ] || not_found - cp "$canonical/$1" "$output" + cat "$canonical/$1" > "$output" } case "$url" in @@ -703,12 +703,37 @@ test_own_script_stops() { } test_gitignore_lacks_canonical_last_line() { - start_scenario ".gitignore lacking the canonical last line stops" - printf '*.log\n/myapp\n' > "$dir/repo/.gitignore" + start_scenario ".gitignore lacking the canonical last line keeps the repo's own line below the canonical content" + printf '*.log\n\n# A comment of our own.\n/myapp\n' > "$dir/repo/.gitignore" commit_and_push_fixture .gitignore .prettierrc run_update_policy + expect_success + expect_file .gitignore << 'EOF' +*.log +*.tmp + +# Own entries go below this comment. +/myapp +EOF + expect_new_commit_not_pushed +} + +test_dockerignore_lacks_canonical_last_line() { + start_scenario ".dockerignore lacking the canonical last line drops a line an earlier canonical commit held" + echo "**/*.log" > "$dir/repo/.dockerignore" + expect_former_canonical_line_replaced .dockerignore ".git" + expect_same_as_canonical .dockerignore +} + +test_editorconfig_lacks_canonical_last_line() { + start_scenario ".editorconfig lacking the canonical last line with a line of its own stops" + printf 'root = true\n[*.py]\nindent_size = 2\n' > "$dir/repo/.editorconfig" + commit_and_push_fixture .editorconfig .prettierrc + run_update_policy expect_failure - expect_output ".gitignore lacks this line" + expect_output ".editorconfig lacks this line" + expect_output "[*.py]" + expect_output "indent_size = 2" expect_nothing_written } @@ -861,6 +886,8 @@ main() { test_ignored_file_stops test_own_script_stops test_gitignore_lacks_canonical_last_line + test_dockerignore_lacks_canonical_last_line + test_editorconfig_lacks_canonical_last_line test_fetched_golangci_without_deny_list test_own_golangci_setting_stops test_source_repo_stops