script/install: write every listed file the repo lacks
check / check (push) Successful in 48s

A listed file the repo lacks is now written whole, with no keep rule.
A prompts/NAME.md goes to docs/NAME.md unless the repo keeps it at
NAME.md, and any other file goes to its own path. A language's files
are skipped when git tracks no source file of that language.

A listed path git ignores now stops the script before it writes.

README bullets split into short sentences, and a false sentence about
template repos deleted.

Model: opus-5-5
This commit is contained in:
clawbot committed 2026-11-09 19:38:41 +00:00
1 parent f4275b9ac0
commit 76b75fa79f
4 files changed
+166 -45

No files matched your search

+112 -15
View File
@@ -117,13 +117,20 @@ EOF
echo '{ "tabWidth": 4 }' > "$dir/canonical/.prettierrc"
echo "new policy" > "$dir/canonical/prompts/REPO_POLICIES.md"
echo "new api policy" > "$dir/canonical/prompts/API_POLICIES.md"
for name in CODE_STYLEGUIDE CODE_STYLEGUIDE_GO CODE_STYLEGUIDE_JS \
CODE_STYLEGUIDE_PYTHON EXISTING_REPO_CHECKLIST \
GO_HTTP_SERVER_CONVENTIONS LLM_PROSE_TELLS NEW_REPO_CHECKLIST; do
echo "new $name" > "$dir/canonical/prompts/$name.md"
done
mkdir -p "$dir/canonical/.gitea/workflows"
echo "name: check" > "$dir/canonical/.gitea/workflows/check.yml"
}
# commit_canonical: commit the canonical files as they stand. The stub
# curl serves only the newest commit; every other commit gets a 404.
commit_canonical() {
git -C "$dir/canonical" add .gitignore .dockerignore .editorconfig \
.golangci.yml .prettierrc prompts script
.golangci.yml .prettierrc .gitea prompts script
git -C "$dir/canonical" commit -q -m "Canonical"
HASH="$(git -C "$dir/canonical" rev-parse HEAD)"
}
@@ -174,7 +181,8 @@ EOF
}
# start_scenario <name>: make a new temp directory holding a repo with
# the outdated files, not yet committed, and an empty origin.
# a Go source file and the outdated files, not yet committed, and an
# empty origin.
start_scenario() {
scenario="$1"
rm -rf "$dir"
@@ -203,18 +211,46 @@ start_scenario() {
cp "$INSTALL" "$UPDATE_POLICY" "$dir/repo/script/"
printf '#!/bin/sh\necho example\n' > "$dir/repo/script/projectname"
chmod +x "$dir/repo/script/projectname"
mkdir -p "$dir/repo/cmd/app"
echo "package main" > "$dir/repo/cmd/app/main.go"
write_outdated_files
}
# commit_and_push_fixture <paths...>: commit those paths, remove the
# rest of the outdated files, and record the commit as $before.
# commit_and_push_fixture <paths...>: commit those paths as they stand
# and every other shared file as the canonical file has it, so the
# script changes only those paths. Record the commit as $before.
commit_and_push_fixture() {
(
cd "$dir/repo"
git add script "$@"
git add script cmd "$@"
git clean -q -f -x
for file in $(git -C "$dir/canonical" ls-files); do
case "$file" in
script/*) continue ;;
prompts/REPO_POLICIES.md) path="REPO_POLICIES.md" ;;
prompts/*) path="docs/${file#prompts/}" ;;
*) path="$file" ;;
esac
if [ -z "$(git ls-files -- "$path")" ]; then
mkdir -p "$(dirname "$path")"
cp "$dir/canonical/$file" "$path"
git add "$path"
fi
done
git commit -q -m "Fixture"
git push -q origin HEAD
git clean -q -f -x
)
before="$(git -C "$dir/repo" rev-parse HEAD)"
}
# remove_from_fixture <paths...>: delete those paths from the repo and
# its origin, and record the commit as $before.
remove_from_fixture() {
(
cd "$dir/repo"
git rm -q -r "$@"
git commit -q -m "Remove from fixture"
git push -q origin HEAD
)
before="$(git -C "$dir/repo" rev-parse HEAD)"
}
@@ -365,25 +401,55 @@ EOF
[ -z "$(ls -A "$dir/tmp")" ] || fail "a temporary directory is left"
}
test_files_not_carried_are_skipped() {
start_scenario "files the repo does not carry skipped"
test_files_the_repo_lacks_are_written_whole() {
start_scenario "files the repo lacks are written whole"
mkdir "$dir/repo/docs"
mv "$dir/repo/REPO_POLICIES.md" "$dir/repo/docs/REPO_POLICIES.md"
commit_and_push_fixture docs/REPO_POLICIES.md
echo "print()" > "$dir/repo/tool.py"
echo "export {}" > "$dir/repo/web.ts"
commit_and_push_fixture docs/REPO_POLICIES.md tool.py web.ts
remove_from_fixture REPO_POLICIES.md .gitignore .golangci.yml .gitea \
docs/API_POLICIES.md docs/CODE_STYLEGUIDE_GO.md \
docs/CODE_STYLEGUIDE_JS.md docs/CODE_STYLEGUIDE_PYTHON.md
run_update_policy
expect_success
expect_same_as_canonical .gitignore
expect_same_as_canonical .golangci.yml
expect_same_as_canonical .gitea/workflows/check.yml
expect_file docs/REPO_POLICIES.md << 'EOF'
new policy
EOF
expect_absent REPO_POLICIES.md
expect_file docs/API_POLICIES.md << 'EOF'
new api policy
EOF
expect_absent API_POLICIES.md
expect_absent docs/API_POLICIES.md
expect_absent .gitignore
expect_absent .dockerignore
expect_absent .editorconfig
expect_file docs/CODE_STYLEGUIDE_GO.md << 'EOF'
new CODE_STYLEGUIDE_GO
EOF
expect_file docs/CODE_STYLEGUIDE_JS.md << 'EOF'
new CODE_STYLEGUIDE_JS
EOF
expect_file docs/CODE_STYLEGUIDE_PYTHON.md << 'EOF'
new CODE_STYLEGUIDE_PYTHON
EOF
expect_new_commit_not_pushed
}
test_language_files_need_a_source_file() {
start_scenario "a repo with no source file of a language gets none of its files"
commit_and_push_fixture .prettierrc
remove_from_fixture cmd .golangci.yml docs/CODE_STYLEGUIDE_GO.md \
docs/GO_HTTP_SERVER_CONVENTIONS.md docs/CODE_STYLEGUIDE_JS.md \
docs/CODE_STYLEGUIDE_PYTHON.md
run_update_policy
expect_success
expect_absent .golangci.yml
expect_absent .prettierrc
expect_absent docs/CODE_STYLEGUIDE_GO.md
expect_absent docs/GO_HTTP_SERVER_CONVENTIONS.md
expect_absent docs/CODE_STYLEGUIDE_JS.md
expect_absent docs/CODE_STYLEGUIDE_PYTHON.md
expect_new_commit_not_pushed
}
@@ -463,6 +529,19 @@ EOF
[ -z "$(ls -A "$dir/tmp")" ] || fail "a temporary directory is left"
}
test_install_without_script_directory() {
start_scenario "script/install with a path writes both scripts into a clone with no script directory"
commit_and_push_fixture .prettierrc
remove_from_fixture script
run_install "$dir/repo"
expect_success
expect_same_as_canonical script/install
expect_same_as_canonical script/update-policy
expect_executable script/install
expect_executable script/update-policy
expect_no_commit
}
test_install_without_argument_updates_own_repo() {
start_scenario "script/install with no argument updates the repo it sits in"
commit_and_push_fixture .prettierrc
@@ -580,6 +659,21 @@ EOF
expect_no_commit
}
test_ignored_file_stops() {
start_scenario "an ignored file at a listed path is not overwritten"
commit_and_push_fixture .prettierrc
remove_from_fixture docs/API_POLICIES.md
echo "docs/API_POLICIES.md" >> "$dir/repo/.git/info/exclude"
echo "our own notes" > "$dir/repo/docs/API_POLICIES.md"
run_install "$dir/repo"
expect_failure
expect_output "docs/API_POLICIES.md is ignored by git"
expect_file docs/API_POLICIES.md << 'EOF'
our own notes
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"
@@ -731,11 +825,13 @@ test_line_removed_from_another_file_still_stops() {
main() {
test_own_entries_kept
test_files_not_carried_are_skipped
test_files_the_repo_lacks_are_written_whole
test_language_files_need_a_source_file
test_nothing_changed
test_target_lacking_install_gets_both_scripts
test_update_policy_replaces_itself
test_install_with_path_commits_nothing
test_install_without_script_directory
test_install_without_argument_updates_own_repo
test_install_bad_target
test_failed_fetch_stops
@@ -744,6 +840,7 @@ main() {
test_own_entry_above_closing_comment
test_uncommitted_change_stops
test_untracked_script_stops
test_ignored_file_stops
test_own_script_stops
test_gitignore_lacks_canonical_last_line
test_fetched_golangci_without_deny_list