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:
+22
-1
@@ -4,7 +4,7 @@
|
||||
#
|
||||
# Part of create-project.sh: sourced by it, never run on its own.
|
||||
#
|
||||
# Provides: create_all, confirm_reuse, apply_plan
|
||||
# Provides: create_all, confirm_reuse, confirm_framework_access, apply_plan
|
||||
|
||||
create_all() {
|
||||
IS_CREATION_STARTED=1
|
||||
@@ -15,6 +15,25 @@ create_all() {
|
||||
if ((PROJECT[has_github])); then
|
||||
configure_mirror
|
||||
fi
|
||||
create_local_project
|
||||
add_framework
|
||||
install_framework
|
||||
copy_templates
|
||||
}
|
||||
|
||||
# confirm_framework_access: the framework comes over SSH. Without SSH the
|
||||
# Maintainer can still go on, without the framework steps, after a yes.
|
||||
confirm_framework_access() {
|
||||
if ((${STATE[is_ssh_ok]:-0})); then
|
||||
STATE[skip_framework]=0
|
||||
return 0
|
||||
fi
|
||||
warn "SSH to Gitea ($(gitea_host) port ${CONFIG[GITEA_SSH_PORT]}) did not work, so the framework cannot be added: ${STATE[ssh_note]}"
|
||||
prompt_yes_no "Create the repositories and the local project without the framework" n
|
||||
if ! ((REPLY)); then
|
||||
die "stopped: set up SSH access to Gitea (see the README) and run again"
|
||||
fi
|
||||
STATE[skip_framework]=1
|
||||
}
|
||||
|
||||
confirm_reuse() {
|
||||
@@ -42,5 +61,7 @@ apply_plan() {
|
||||
return 0
|
||||
fi
|
||||
confirm_reuse
|
||||
confirm_local_directory
|
||||
confirm_framework_access
|
||||
create_all
|
||||
}
|
||||
|
||||
@@ -100,6 +100,9 @@ validate_config() {
|
||||
CONFIG[MIRROR_INTERVAL]="${CONFIG[MIRROR_INTERVAL]:-$DEFAULT_MIRROR_INTERVAL}"
|
||||
is_valid_interval "${CONFIG[MIRROR_INTERVAL]}" ||
|
||||
die "MIRROR_INTERVAL in $CONFIG_FILE must look like 10m0s or 8h0m0s"
|
||||
CONFIG[FRAMEWORK_REPO]="${CONFIG[FRAMEWORK_REPO]:-$DEFAULT_FRAMEWORK_REPO}"
|
||||
is_valid_framework_repo "${CONFIG[FRAMEWORK_REPO]}" ||
|
||||
die "FRAMEWORK_REPO in $CONFIG_FILE must look like OWNER/NAME"
|
||||
}
|
||||
|
||||
validate_credentials() {
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
# shellcheck disable=SC2034 # read and written by the other library files
|
||||
|
||||
readonly PROJECT_NAME="${REPOFOUNDRY_NAME:-RepoFoundry}"
|
||||
readonly VERSION="0.2.0"
|
||||
readonly VERSION="0.3.0"
|
||||
readonly EXIT_FAILURE=1
|
||||
readonly EXIT_USAGE=2
|
||||
readonly MAX_VALUE_LENGTH=2048
|
||||
@@ -16,12 +16,14 @@ readonly MAX_DESCRIPTION_LENGTH=350
|
||||
readonly HTTP_TIMEOUT_SECONDS=30
|
||||
readonly DEFAULT_MIRROR_INTERVAL="10m0s"
|
||||
readonly DEFAULT_SSH_PORT=10022
|
||||
readonly DEFAULT_FRAMEWORK_REPO="TirSystem/SQA-QC-Framework"
|
||||
readonly AGPL_LICENSE_KEY="AGPL-3.0"
|
||||
readonly DEFAULT_BRANCH="main"
|
||||
readonly PLAN_STEPS=("GitHub repository" "Gitea repository" "Push mirror")
|
||||
readonly PLAN_STEPS=("GitHub repository" "Gitea repository" "Push mirror"
|
||||
"Local project" "Framework" "Skills and hooks" "Templates")
|
||||
# shellcheck disable=SC2034 # read through namerefs (parse_env_file)
|
||||
readonly CONFIG_KEYS=(GITHUB_API_URL GITHUB_WEB_URL GITEA_URL GITEA_API_URL
|
||||
GITEA_SSH_PORT MIRROR_INTERVAL)
|
||||
GITEA_SSH_PORT MIRROR_INTERVAL FRAMEWORK_REPO)
|
||||
readonly CREDENTIAL_KEYS=(GITHUB_PAT GITHUB_USER GITEA_TOKEN)
|
||||
|
||||
CONFIG_FILE="$PROJECT_ROOT/config.env"
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
# shellcheck shell=bash
|
||||
# shellcheck disable=SC2004,SC2034,SC2154 # shared state and arrays are declared in constants.sh
|
||||
# framework.sh - The SQA-QC-Framework in the new project: submodule, skills, hooks and templates.
|
||||
#
|
||||
# Part of create-project.sh: sourced by it, never run on its own.
|
||||
#
|
||||
# Provides: is_framework_skipped, add_framework, run_framework_script, install_skills, install_hooks, install_framework, copy_template, copy_templates
|
||||
|
||||
# is_framework_skipped: succeed when the framework steps are left out because
|
||||
# SSH to Gitea is not available (the Maintainer agreed to that).
|
||||
is_framework_skipped() {
|
||||
[[ ${STATE[skip_framework]:-0} == 1 ]]
|
||||
}
|
||||
|
||||
# add_framework: git submodule add of the framework as "framework". SSH is
|
||||
# needed for it; a failure says how to test the access.
|
||||
add_framework() {
|
||||
local label="Framework" dir="${PROJECT[directory]}" url err existing
|
||||
url="$(framework_url)"
|
||||
if is_framework_skipped; then
|
||||
finish_step "$label" "skipped" "(no SSH access to Gitea)"
|
||||
return 0
|
||||
fi
|
||||
begin_step "$label"
|
||||
if [[ -e $dir/framework ]]; then
|
||||
existing="$(git_project "$dir" config -f .gitmodules --get submodule.framework.url || true)"
|
||||
if [[ $existing != "$url" ]]; then
|
||||
die "'framework' already exists in $dir and is not the framework submodule ($url)"
|
||||
fi
|
||||
finish_step "$label" "reused" "$url (already a submodule)"
|
||||
return 0
|
||||
fi
|
||||
make_temp_file
|
||||
err="$REPLY"
|
||||
if ! git_project "$dir" submodule add -q "$url" framework 2>"$err"; then
|
||||
die "git could not add the framework from $url. Check the SSH access first: ssh -p ${CONFIG[GITEA_SSH_PORT]} -T git@$(gitea_host). Git said: $(head -n 2 "$err" | tr '\n' ' ')"
|
||||
fi
|
||||
finish_step "$label" "created" "$url"
|
||||
}
|
||||
|
||||
# run_framework_script DIR SCRIPT [ARG...]: run one of the framework's own
|
||||
# scripts inside the project. PROJECT_ROOT is set explicitly so that a
|
||||
# PROJECT_ROOT in the caller's environment cannot point it elsewhere.
|
||||
run_framework_script() {
|
||||
local dir="$1" script="$2" out abs
|
||||
shift 2
|
||||
abs="$(cd "$dir" && pwd)"
|
||||
make_temp_file
|
||||
out="$REPLY"
|
||||
if ! (cd "$abs" && env PROJECT_ROOT="$abs" bash "framework/scripts/$script" "$@") >"$out" 2>&1; then
|
||||
die "the framework script $script failed: $(tail -n 3 "$out" | tr '\n' ' ')"
|
||||
fi
|
||||
}
|
||||
|
||||
# install_skills DIR: install the framework's skills once. The installer is
|
||||
# safe to repeat but copies everything again, so a project that already has
|
||||
# them is left alone.
|
||||
install_skills() {
|
||||
local dir="$1"
|
||||
if [[ -f $dir/.agents/skills/.framework-skills && -f $dir/.claude/skills/.framework-skills ]]; then
|
||||
STATE[skills_note]="skills already installed"
|
||||
return 0
|
||||
fi
|
||||
run_framework_script "$dir" install-skills.sh
|
||||
STATE[skills_note]="skills installed"
|
||||
}
|
||||
|
||||
# install_hooks DIR: point core.hooksPath at the framework's hooks, and turn
|
||||
# on the plan gate if chosen. A different hooks path that is already set is
|
||||
# only replaced after a yes.
|
||||
install_hooks() {
|
||||
local dir="$1" current global gate=() gate_enabled
|
||||
if ((PROJECT[is_plan_gate_enabled])); then
|
||||
gate=(--enable-plan-gate)
|
||||
fi
|
||||
# Both settings are normally unset; git config exits 1 then.
|
||||
current="$(git_project "$dir" config --local --get core.hooksPath || true)"
|
||||
global="$(git_project "$dir" config --global --get core.hooksPath || true)"
|
||||
gate_enabled="$(git_project "$dir" config --local --get planGate.enabled || true)"
|
||||
if [[ -z $current && -n $global ]]; then
|
||||
warn "your global core.hooksPath is '$global'; this project sets its own, which takes precedence here"
|
||||
fi
|
||||
if [[ -z $current ]]; then
|
||||
run_framework_script "$dir" install-git-hooks.sh "${gate[@]}"
|
||||
STATE[hooks_note]="hooks installed"
|
||||
elif [[ $current == framework/githooks ]]; then
|
||||
STATE[hooks_note]="hooks already installed"
|
||||
if ((PROJECT[is_plan_gate_enabled])) && [[ $gate_enabled != true ]]; then
|
||||
run_framework_script "$dir" install-git-hooks.sh "${gate[@]}"
|
||||
fi
|
||||
else
|
||||
prompt_yes_no "core.hooksPath is already '$current'. Replace it with framework/githooks" n
|
||||
if ((REPLY)); then
|
||||
run_framework_script "$dir" install-git-hooks.sh "${gate[@]}"
|
||||
STATE[hooks_note]="hooks installed (replaced '$current')"
|
||||
else
|
||||
STATE[hooks_note]="kept the existing hooks path '$current'"
|
||||
if ((PROJECT[is_plan_gate_enabled])); then
|
||||
warn "the plan gate is not enabled because the framework hooks were not installed"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# install_framework: skills, then hooks (and the plan gate). Each is done once.
|
||||
install_framework() {
|
||||
local label="Skills and hooks" dir="${PROJECT[directory]}" gate_state="plan gate off"
|
||||
if is_framework_skipped; then
|
||||
finish_step "$label" "skipped" "(no SSH access to Gitea)"
|
||||
return 0
|
||||
fi
|
||||
begin_step "$label"
|
||||
install_skills "$dir"
|
||||
install_hooks "$dir"
|
||||
if [[ $(git_project "$dir" config --local --get planGate.enabled || true) == true ]]; then
|
||||
gate_state="plan gate on"
|
||||
fi
|
||||
finish_step "$label" "created" "(${STATE[skills_note]}; ${STATE[hooks_note]}; $gate_state)"
|
||||
}
|
||||
|
||||
# copy_template DIR SOURCE TARGET: copy a framework template. An existing
|
||||
# target is only replaced after a yes. The result goes to STATE[template_note].
|
||||
copy_template() {
|
||||
local dir="$1" source="$2" target="$3"
|
||||
if [[ ! -f $dir/$source ]]; then
|
||||
die "the framework has no $source; is the submodule complete?"
|
||||
fi
|
||||
if [[ -e $dir/$target ]]; then
|
||||
prompt_yes_no "$target already exists. Replace it with the framework template" n
|
||||
if ! ((REPLY)); then
|
||||
STATE[template_note]="kept existing $target"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
cp -- "$dir/$source" "$dir/$target"
|
||||
STATE[template_note]="copied $target"
|
||||
}
|
||||
|
||||
# copy_templates: AGENTS.md and docs/artifact-registry.md from the framework.
|
||||
copy_templates() {
|
||||
local label="Templates" dir="${PROJECT[directory]}" notes=""
|
||||
if is_framework_skipped; then
|
||||
finish_step "$label" "skipped" "(no SSH access to Gitea)"
|
||||
return 0
|
||||
fi
|
||||
begin_step "$label"
|
||||
mkdir -p -- "$dir/docs"
|
||||
copy_template "$dir" framework/templates/AGENTS-template.md AGENTS.md
|
||||
notes="${STATE[template_note]}"
|
||||
copy_template "$dir" framework/templates/artifact-registry-template.md docs/artifact-registry.md
|
||||
notes="$notes; ${STATE[template_note]}"
|
||||
finish_step "$label" "created" "($notes)"
|
||||
}
|
||||
@@ -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"
|
||||
}
|
||||
+38
-1
@@ -4,7 +4,7 @@
|
||||
#
|
||||
# Part of create-project.sh: sourced by it, never run on its own.
|
||||
#
|
||||
# Provides: is_reused, repo_owner, repo_url
|
||||
# Provides: is_reused, repo_owner, repo_url, gitea_host, origin_protocol, origin_url, github_remote_url, framework_url
|
||||
|
||||
# is_reused HOST: succeed if the existing repository on HOST will be reused.
|
||||
is_reused() {
|
||||
@@ -26,3 +26,40 @@ repo_url() {
|
||||
printf '%s/%s/%s' "${CONFIG[GITHUB_WEB_URL]}" "${PROJECT[github_owner]}" "${PROJECT[name]}"
|
||||
fi
|
||||
}
|
||||
|
||||
# gitea_host: the host name of the Gitea server, from GITEA_URL.
|
||||
gitea_host() {
|
||||
local host="${CONFIG[GITEA_URL]#https://}"
|
||||
host="${host%%/*}"
|
||||
printf '%s' "${host%%:*}"
|
||||
}
|
||||
|
||||
# origin_protocol: SSH when the SSH test passed, otherwise HTTPS.
|
||||
origin_protocol() {
|
||||
if ((${STATE[is_ssh_ok]:-0})); then
|
||||
printf 'SSH'
|
||||
else
|
||||
printf 'HTTPS'
|
||||
fi
|
||||
}
|
||||
|
||||
# origin_url: the address of the Gitea repository for the origin remote. It
|
||||
# never holds a credential: "git@" is the SSH user name, not a secret.
|
||||
origin_url() {
|
||||
if [[ $(origin_protocol) == SSH ]]; then
|
||||
printf 'ssh://git@%s:%s/%s/%s.git' "$(gitea_host)" "${CONFIG[GITEA_SSH_PORT]}" \
|
||||
"${PROJECT[gitea_owner]}" "${PROJECT[name]}"
|
||||
else
|
||||
printf '%s/%s/%s.git' "${CONFIG[GITEA_URL]}" "${PROJECT[gitea_owner]}" "${PROJECT[name]}"
|
||||
fi
|
||||
}
|
||||
|
||||
github_remote_url() {
|
||||
printf '%s/%s/%s.git' "${CONFIG[GITHUB_WEB_URL]}" "${PROJECT[github_owner]}" "${PROJECT[name]}"
|
||||
}
|
||||
|
||||
# framework_url: where the framework submodule comes from (always SSH).
|
||||
framework_url() {
|
||||
printf 'ssh://git@%s:%s/%s.git' "$(gitea_host)" "${CONFIG[GITEA_SSH_PORT]}" \
|
||||
"${CONFIG[FRAMEWORK_REPO]}"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
# shellcheck shell=bash
|
||||
# shellcheck disable=SC2004,SC2034,SC2154 # shared state and arrays are declared in constants.sh
|
||||
# localproject.sh - The local project: its directory, its git repository and its remotes.
|
||||
#
|
||||
# Part of create-project.sh: sourced by it, never run on its own.
|
||||
#
|
||||
# Provides: inspect_local_directory, confirm_local_directory, ensure_remote, checkout_gitea_history, create_local_project
|
||||
|
||||
# inspect_local_directory: record in STATE[local_dir] whether the project
|
||||
# directory is missing, empty or not_empty. It creates nothing.
|
||||
inspect_local_directory() {
|
||||
local dir="${PROJECT[directory]}"
|
||||
if [[ ! -e $dir ]]; then
|
||||
STATE[local_dir]="missing"
|
||||
elif [[ ! -d $dir ]]; then
|
||||
die "$dir already exists and is not a directory"
|
||||
elif [[ -z "$(find "$dir" -mindepth 1 -maxdepth 1 -print -quit)" ]]; then
|
||||
STATE[local_dir]="empty"
|
||||
else
|
||||
STATE[local_dir]="not_empty"
|
||||
fi
|
||||
}
|
||||
|
||||
# confirm_local_directory: an existing directory is only used after a yes.
|
||||
confirm_local_directory() {
|
||||
local dir="${PROJECT[directory]}" what="is empty"
|
||||
if [[ ${STATE[local_dir]} == missing ]]; then
|
||||
return 0
|
||||
fi
|
||||
if [[ ${STATE[local_dir]} == not_empty ]]; then
|
||||
what="already has files"
|
||||
fi
|
||||
prompt_yes_no "The directory $dir already exists and $what. Use it" n
|
||||
if ! ((REPLY)); then
|
||||
die "stopped: choose another directory or remove this one"
|
||||
fi
|
||||
}
|
||||
|
||||
# ensure_remote DIR NAME URL: add the remote, or accept one that already has
|
||||
# exactly this address. A different address is never overwritten.
|
||||
ensure_remote() {
|
||||
local dir="$1" name="$2" url="$3" existing
|
||||
# git config exits 1 when the remote is not set; that is the normal case.
|
||||
existing="$(git_project "$dir" config --get "remote.$name.url" || true)"
|
||||
if [[ -z $existing ]]; then
|
||||
git_project "$dir" remote add "$name" "$url"
|
||||
elif [[ $existing != "$url" ]]; then
|
||||
die "the remote '$name' in $dir already points to $existing; remove it or choose another directory"
|
||||
fi
|
||||
}
|
||||
|
||||
# checkout_gitea_history DIR: when the Gitea repository holds the license
|
||||
# commit, fetch it and start the local branch from it, so the local history
|
||||
# begins with that commit. Existing files are never overwritten: git refuses.
|
||||
checkout_gitea_history() {
|
||||
local dir="$1" err
|
||||
if ! fetch_origin "$dir" 2>/dev/null; then
|
||||
die "could not fetch the Gitea repository from $(origin_url); check your SSH key (or, over HTTPS, the token) and run the same command again"
|
||||
fi
|
||||
if ! git_project "$dir" rev-parse --verify -q refs/remotes/origin/main >/dev/null; then
|
||||
return 0
|
||||
fi
|
||||
if git_project "$dir" rev-parse --verify -q HEAD >/dev/null 2>&1; then
|
||||
warn "$dir already has history: the Gitea content was fetched but not checked out"
|
||||
return 0
|
||||
fi
|
||||
make_temp_file
|
||||
err="$REPLY"
|
||||
if ! git_project "$dir" checkout -q -b main --track origin/main 2>"$err"; then
|
||||
die "git would overwrite files in $dir with the Gitea content; move them away and run again. Git said: $(head -n 2 "$err" | tr '\n' ' ')"
|
||||
fi
|
||||
}
|
||||
|
||||
# create_local_project: the directory, the git repository on main, the
|
||||
# remotes and, when GitHub is chosen, the license history. No commit is made.
|
||||
create_local_project() {
|
||||
local label="Local project" dir="${PROJECT[directory]}"
|
||||
begin_step "$label"
|
||||
mkdir -p -- "$dir"
|
||||
if [[ ! -e $dir/.git ]]; then
|
||||
git_project "$dir" init -q
|
||||
git_project "$dir" symbolic-ref HEAD "refs/heads/$DEFAULT_BRANCH"
|
||||
fi
|
||||
ensure_remote "$dir" origin "$(origin_url)"
|
||||
if ((PROJECT[has_github])); then
|
||||
ensure_remote "$dir" github "$(github_remote_url)"
|
||||
checkout_gitea_history "$dir"
|
||||
fi
|
||||
finish_step "$label" "created" "$dir (origin over $(origin_protocol))"
|
||||
}
|
||||
+20
-2
@@ -4,7 +4,17 @@
|
||||
#
|
||||
# Part of create-project.sh: sourced by it, never run on its own.
|
||||
#
|
||||
# Provides: print_plan
|
||||
# Provides: local_plan_note, print_plan
|
||||
|
||||
# local_plan_note: what will happen to the project directory.
|
||||
local_plan_note() {
|
||||
local dir="${PROJECT[directory]}"
|
||||
case "${STATE[local_dir]}" in
|
||||
missing) printf 'create %s (new directory), git on %s, no commit' "$dir" "$DEFAULT_BRANCH" ;;
|
||||
empty) printf 'use the existing empty directory %s (you will be asked)' "$dir" ;;
|
||||
*) printf 'use the existing directory %s, which has files (you will be asked)' "$dir" ;;
|
||||
esac
|
||||
}
|
||||
|
||||
print_plan() {
|
||||
local gitea_action github_action origin_note
|
||||
@@ -35,5 +45,13 @@ print_plan() {
|
||||
else
|
||||
origin_note="HTTPS (SSH test: ${STATE[ssh_note]})"
|
||||
fi
|
||||
say "$(printf ' %-18s: %s' "Local origin" "will use $origin_note, in a later phase")"
|
||||
say "$(printf ' %-18s: %s' "Local project" "$(local_plan_note)")"
|
||||
say "$(printf ' %-18s: %s' "Local origin" "will use $origin_note")"
|
||||
if ((STATE[is_ssh_ok])); then
|
||||
say "$(printf ' %-18s: %s' "Framework" "add $(framework_url) as a submodule")"
|
||||
say "$(printf ' %-18s: %s' "Skills and hooks" "install once; plan gate $(yes_no "${PROJECT[is_plan_gate_enabled]}")")"
|
||||
say "$(printf ' %-18s: %s' "Templates" "AGENTS.md and docs/artifact-registry.md (you are asked before a file is replaced)")"
|
||||
else
|
||||
say "$(printf ' %-18s: %s' "Framework" "NOT possible without SSH to Gitea; you will be asked whether to go on without it")"
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -108,9 +108,7 @@ preflight_github() {
|
||||
# or without access it is simply "not passed"; it never stops the run.
|
||||
test_gitea_ssh() {
|
||||
local host port output status=0
|
||||
host="${CONFIG[GITEA_URL]#https://}"
|
||||
host="${host%%/*}"
|
||||
host="${host%%:*}"
|
||||
host="$(gitea_host)"
|
||||
port="${CONFIG[GITEA_SSH_PORT]}"
|
||||
STATE[is_ssh_ok]=0
|
||||
STATE[ssh_note]="failed (check your SSH key and that $host:$port is reachable)"
|
||||
@@ -163,5 +161,6 @@ run_preflight() {
|
||||
fi
|
||||
test_gitea_ssh
|
||||
decide_existing_repositories
|
||||
inspect_local_directory
|
||||
say "All checks passed."
|
||||
}
|
||||
|
||||
+3
-2
@@ -42,10 +42,11 @@ report_outcome() {
|
||||
say "$(printf ' %-18s: %s' "$label" "${STEP_STATUS[$label]}${STEP_DETAIL[$label]:+ ${STEP_DETAIL[$label]}}")"
|
||||
done
|
||||
if ((code == 0)); then
|
||||
say "The local project with the framework is created by a later phase."
|
||||
say "The project is in ${PROJECT[directory]}. Nothing was committed there: review it, then work on a branch."
|
||||
else
|
||||
say "To continue: fix the problem named above and run the same command again with --apply."
|
||||
say "A repository this run created is still empty (or holds only the license), so the next run offers to reuse it."
|
||||
say "Nothing is deleted automatically. To start over, delete the repositories above in the web interface."
|
||||
say "A directory, remotes and submodule created so far are used again by the next run; you are asked before an existing directory or file is touched."
|
||||
say "Nothing is deleted automatically. To start over, delete the repositories above in the web interface and the project directory by hand."
|
||||
fi
|
||||
}
|
||||
|
||||
+6
-1
@@ -4,7 +4,7 @@
|
||||
#
|
||||
# Part of create-project.sh: sourced by it, never run on its own.
|
||||
#
|
||||
# Provides: is_valid_repo_name, is_valid_gitea_owner, is_valid_github_owner, is_valid_description, is_valid_directory, is_valid_base_url, is_valid_request_url, is_valid_token, is_valid_port, is_valid_interval, normalize_url
|
||||
# Provides: is_valid_framework_repo, is_valid_repo_name, is_valid_gitea_owner, is_valid_github_owner, is_valid_description, is_valid_directory, is_valid_base_url, is_valid_request_url, is_valid_token, is_valid_port, is_valid_interval, normalize_url
|
||||
|
||||
is_valid_repo_name() {
|
||||
local name="$1"
|
||||
@@ -49,6 +49,11 @@ is_valid_token() {
|
||||
[[ $1 =~ ^[A-Za-z0-9_.~+/=-]{8,255}$ ]]
|
||||
}
|
||||
|
||||
# OWNER/NAME of the framework repository on Gitea.
|
||||
is_valid_framework_repo() {
|
||||
[[ $1 =~ ^[A-Za-z0-9._-]+/[A-Za-z0-9._-]+$ && $1 != */.. && $1 != ../* && $1 != ./* && $1 != */. ]]
|
||||
}
|
||||
|
||||
is_valid_port() {
|
||||
[[ $1 =~ ^[0-9]{1,5}$ ]] && ((10#$1 >= 1 && 10#$1 <= 65535))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user