Fix the defects found by the live end-to-end run, add RC-017

- Close stdin for ssh, git, curl and the framework scripts, so a real ssh
  no longer swallows answers meant for later prompts.
- Count LICENSE and LICENSE + README.md (what Gitea creates) as content
  the script made, so a partly created repository can be reused.
- Document the observed token scopes (write:user for user-owned Gitea
  repositories) and the Gitea README.md in the README.
- Correct criterion 2 of MIL-002 (new Proposed version row).
- Record the run and the final security review as RC-017.
- Tests: stdin regression, initial_only reuse, other files count as
  content, example config needs its address edited.

Task: MIL-003#6
Task: MIL-002#1
Refs #20

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-05 21:41:47 +08:00
co-authored by Claude Sonnet 5.5
parent 0030334e5e
commit 613a288dea
13 changed files with 203 additions and 33 deletions
+3
View File
@@ -244,6 +244,9 @@ STUB
write_ssh_stub() {
cat >"$WORK/bin/ssh" <<STUB
#!/usr/bin/env bash
# The real ssh reads standard input until it ends; so does this stub. Whatever
# is left on the caller's stdin (the answers to later prompts) is lost to it.
cat >/dev/null
printf '%s\n' "\$@" >>"\$STUB_DIR/ssh.args"
if [[ ${1:-0} == 0 ]]; then
echo "Hi there, gitea-user! You've successfully authenticated, but Gitea does not provide shell access."
+12 -1
View File
@@ -158,10 +158,21 @@ test_example_files_hold_placeholders_only() {
fail ".env.example has a value for ${line%%=*}; it must be empty"
fi
done < <(grep -E '^[A-Z_]+=' "$REPO_ROOT/.env.example")
# The example holds a placeholder for the Gitea address, so it must be
# edited before use: as it is, it is refused with a clear message...
run_lib "" "parse_env_file \"$REPO_ROOT/config.env.example\" CONFIG_KEYS CONFIG
validate_config
echo parsed"
assert_contains "config.env.example is valid" "$OUT" "parsed"
assert_status "placeholder address is refused" 1 "$STATUS"
assert_contains "names the key" "$ERR" "GITEA_URL"
# ...and with a real address in its place the rest of the file is valid.
sed -e 's|^GITEA_URL=.*|GITEA_URL=https://git.example.test/|' \
-e 's|^GITEA_API_URL=.*|GITEA_API_URL=https://git.example.test/api/v1|' \
"$REPO_ROOT/config.env.example" >"$WORK/example.env"
run_lib "" "parse_env_file \"$WORK/example.env\" CONFIG_KEYS CONFIG
validate_config
echo parsed"
assert_contains "config.env.example is valid once the address is set" "$OUT" "parsed"
run_lib "" "parse_env_file \"$REPO_ROOT/.env.example\" CREDENTIAL_KEYS CREDENTIALS
echo parsed"
assert_contains ".env.example parses" "$OUT" "parsed"
+39
View File
@@ -476,3 +476,42 @@ mirror_field '$WORK/m.json' https://github.com/o/b.git sync_on_commit"
assert_eq "the right mirror is chosen with jq" "true" "$OUT"
fi
}
# ------------------------------------------------- found by the live e2e run
test_a_repository_this_script_created_earlier_can_be_reused() {
# Seen on a real Gitea: creating a repository with a license also adds a
# README.md. After a failed mirror step the next run must still reuse it.
setup_hosts
prepend_route 'GET|/api/v1/repos/TirSystem/my-app|200|{"empty":false}'
prepend_route 'GET|/api/v1/repos/TirSystem/my-app/contents|200|[{"name":"README.md","type":"file"},{"name":"LICENSE","type":"file"}]'
run_apply "$ANSWERS_GITHUB"$'y\ny\n'
assert_status "LICENSE and README.md" 0 "$STATUS"
assert_not_contains "not created again" "$(calls)" "$GITEA_REPO_CALL"
assert_contains "reported" "$OUT" "Gitea repository : reused"
}
test_other_files_still_count_as_content() {
local listing
for listing in '[{"name":"README.md","type":"file"}]' \
'[{"name":"LICENSE","type":"file"},{"name":"README.md","type":"file"},{"name":"main.c","type":"file"}]' \
'[{"name":"LICENSE","type":"file"},{"name":"src","type":"dir"}]'; do
setup_hosts
prepend_route 'GET|/api/v1/repos/TirSystem/my-app|200|{"empty":false}'
prepend_route "GET|/api/v1/repos/TirSystem/my-app/contents|200|$listing"
run_dry "$ANSWERS_GITHUB"
assert_status "content: $listing" 1 "$STATUS"
assert_contains "refused" "$ERR" "already exists and has content"
done
}
test_children_never_eat_the_answers_meant_for_later_prompts() {
# The real ssh reads standard input until it ends; the stub does too. The
# script closes stdin for ssh, git, curl and the framework scripts, so the
# answers that follow the SSH test still reach the later prompts.
setup_hosts
run_apply "$ANSWERS_GITHUB"$'y\n'
assert_status "the final question is still answered" 0 "$STATUS"
assert_contains "created" "$OUT" "Done. This is what exists now:"
assert_not_contains "no lost input" "$ERR" "no input available"
}
+3 -3
View File
@@ -217,13 +217,13 @@ test_an_existing_hooks_path_is_not_replaced_without_a_yes() {
test_a_global_hooks_path_is_reported_not_changed() {
setup_hosts
git config --file "$WORK/gitconfig" core.hooksPath /somewhere/global-hooks
git config --file "$WORK/gitconfig" core.hooksPath global-hooks-dir
local dir="$WORK/project"
local_answers "$dir" y n
run_apply "$LOCAL_ANSWERS"$'y\n'
assert_status "apply" 0 "$STATUS"
assert_contains "warns" "$ERR" "your global core.hooksPath is '/somewhere/global-hooks'"
assert_eq "the global setting is untouched" "/somewhere/global-hooks" "$(git config --file "$WORK/gitconfig" --get core.hooksPath)"
assert_contains "warns" "$ERR" "your global core.hooksPath is 'global-hooks-dir'"
assert_eq "the global setting is untouched" "global-hooks-dir" "$(git config --file "$WORK/gitconfig" --get core.hooksPath)"
assert_eq "the project has its own" "framework/githooks" "$(project_git "$dir" config --local --get core.hooksPath)"
}