Add the local project, framework submodule, hooks and templates

After the repositories and the mirror, the script now creates the local
project: the directory, git on main, credential-free origin (and github)
remotes, and the license history from Gitea. Then it adds the framework as
a submodule over SSH, installs its skills and git hooks once (plan gate
optional), and copies the AGENTS.md and artifact registry templates.

Nothing is overwritten without a yes: an existing directory, core.hooksPath,
AGENTS.md or docs/artifact-registry.md each ask first (default no). Without
SSH the framework steps can only be skipped, after a yes. No commit is made
in the new project. New optional config key FRAMEWORK_REPO.

New library files git.sh, localproject.sh and framework.sh. Tests run real
git against local bare repositories that stand in for Gitea and the
framework, with a private git configuration (769 checks). The README is now
the full guide.

Task: MIL-003#1
Task: MIL-003#2
Task: MIL-003#3
Task: MIL-003#4
Task: MIL-003#5
Refs #15
Refs #16
Refs #17
Refs #18
Refs #19

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-05 15:54:02 +08:00
co-authored by Claude Sonnet 5.5
parent 736cdb42fc
commit 0030334e5e
19 changed files with 1185 additions and 68 deletions
+48
View File
@@ -0,0 +1,48 @@
# shellcheck shell=bash
# shellcheck disable=SC2004,SC2034,SC2154 # shared state and arrays are declared in constants.sh
# git.sh - Running git for the new project: no prompts, no token on a command line.
#
# Part of create-project.sh: sourced by it, never run on its own.
#
# Provides: git_project, fetch_origin
# git_project DIR ARGS...: run git in DIR. Prompts are switched off, so a
# missing credential or SSH key fails at once instead of waiting for input.
git_project() {
local dir="$1"
shift
GIT_TERMINAL_PROMPT=0 GIT_SSH_COMMAND="${GIT_SSH_COMMAND:-ssh} -o BatchMode=yes" \
git -C "$dir" "$@"
}
# fetch_origin DIR: fetch the Gitea repository into the project. Over SSH the
# user's key is used. Over HTTPS the token reaches git through a private
# GIT_ASKPASS helper and the environment of this one command: it is never part
# of a URL, of the remote configuration or of a command line.
fetch_origin() {
local dir="$1" url askpass
url="$(git_project "$dir" config --get remote.origin.url)"
if [[ $url != https://* ]]; then
git_project "$dir" fetch -q origin
return
fi
make_temp_file
askpass="$REPLY"
# shellcheck disable=SC2016 # the helper is written out literally: it expands $1 and its environment itself
{
printf '%s\n' '#!/usr/bin/env bash'
printf '%s\n' 'case "$1" in'
printf '%s\n' ' *sername*) printf "%s\n" "$REPOFOUNDRY_ASKPASS_USER" ;;'
printf '%s\n' ' *) printf "%s\n" "$REPOFOUNDRY_ASKPASS_TOKEN" ;;'
printf '%s\n' 'esac'
} >"$askpass"
chmod 700 "$askpass"
# Not "cmd; rm": a failed fetch must still be reported to the caller.
if ! GIT_ASKPASS="$askpass" REPOFOUNDRY_ASKPASS_USER="${STATE[gitea_login]}" \
REPOFOUNDRY_ASKPASS_TOKEN="${CREDENTIALS[GITEA_TOKEN]}" \
git_project "$dir" fetch -q origin; then
rm -f -- "$askpass"
return 1
fi
rm -f -- "$askpass"
}