Accept MIL-009 and add the Git excludes step (task 2) #71
@@ -19,6 +19,7 @@ create_all() {
|
|||||||
add_framework
|
add_framework
|
||||||
install_framework
|
install_framework
|
||||||
copy_templates
|
copy_templates
|
||||||
|
exclude_framework_files
|
||||||
create_env_file
|
create_env_file
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -30,8 +30,12 @@ readonly HINT_LICENSE="use a Gitea license key (letters, digits, '.', '+' or '-'
|
|||||||
readonly HINT_DIRECTORY="must not be empty, start with '-' or contain control characters"
|
readonly HINT_DIRECTORY="must not be empty, start with '-' or contain control characters"
|
||||||
readonly HINT_TOKEN="8 to 255 letters, digits or _ . ~ + / = -"
|
readonly HINT_TOKEN="8 to 255 letters, digits or _ . ~ + / = -"
|
||||||
readonly ENV_FILE_NAME=".env"
|
readonly ENV_FILE_NAME=".env"
|
||||||
|
# What the framework installs into the new project that git must not list: the
|
||||||
|
# folders of the skills and the copy of AGENTS.md, anchored to the project
|
||||||
|
# root. They go into .git/info/exclude of the new project.
|
||||||
|
readonly FRAMEWORK_EXCLUDES=(/.claude /.agents /AGENTS.md)
|
||||||
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" "Project .env")
|
"Local project" "Framework" "Skills and hooks" "Templates" "Git excludes" "Project .env")
|
||||||
# shellcheck disable=SC2034 # read through namerefs (parse_env_file)
|
# shellcheck disable=SC2034 # read through namerefs (parse_env_file)
|
||||||
readonly CONFIG_KEYS=(GITHUB_API_URL GITHUB_WEB_URL GITEA_URL GITEA_API_URL
|
readonly CONFIG_KEYS=(GITHUB_API_URL GITHUB_WEB_URL GITEA_URL GITEA_API_URL
|
||||||
GITEA_SSH_PORT MIRROR_INTERVAL FRAMEWORK_REPO
|
GITEA_SSH_PORT MIRROR_INTERVAL FRAMEWORK_REPO
|
||||||
|
|||||||
+2
-13
@@ -26,19 +26,8 @@ env_file_key_list() {
|
|||||||
# file: the entry goes into .git/info/exclude, which is never committed. It
|
# file: the entry goes into .git/info/exclude, which is never committed. It
|
||||||
# does nothing when .env is already ignored.
|
# does nothing when .env is already ignored.
|
||||||
exclude_env_file() {
|
exclude_env_file() {
|
||||||
local dir="$1" gitdir exclude
|
local dir="$1"
|
||||||
if git_project "$dir" check-ignore -q -- "$ENV_FILE_NAME"; then
|
exclude_from_git "$dir" "RepoFoundry: the credentials file of this project" "$ENV_FILE_NAME" ||
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
gitdir="$(git_project "$dir" rev-parse --absolute-git-dir)"
|
|
||||||
exclude="$gitdir/info/exclude"
|
|
||||||
mkdir -p -- "$gitdir/info"
|
|
||||||
# Start on a fresh line when the file does not end with one.
|
|
||||||
if [[ -s $exclude && -n "$(tail -c 1 -- "$exclude")" ]]; then
|
|
||||||
printf '\n' >>"$exclude"
|
|
||||||
fi
|
|
||||||
printf '%s\n' "# RepoFoundry: the credentials file of this project" "$ENV_FILE_NAME" >>"$exclude"
|
|
||||||
git_project "$dir" check-ignore -q -- "$ENV_FILE_NAME" ||
|
|
||||||
die "could not make git ignore $ENV_FILE_NAME in $dir; nothing was written to it"
|
die "could not make git ignore $ENV_FILE_NAME in $dir; nothing was written to it"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+37
-1
@@ -4,7 +4,7 @@
|
|||||||
#
|
#
|
||||||
# Part of create-project.sh: sourced by it, never run on its own.
|
# Part of create-project.sh: sourced by it, never run on its own.
|
||||||
#
|
#
|
||||||
# Provides: is_framework_skipped, init_framework_submodules, add_framework, run_framework_script, install_skills, install_hooks, install_framework, copy_template, copy_templates
|
# Provides: is_framework_skipped, init_framework_submodules, add_framework, run_framework_script, install_skills, install_hooks, install_framework, copy_template, copy_templates, exclude_framework_files
|
||||||
|
|
||||||
# is_framework_skipped: succeed when the framework steps are left out because
|
# is_framework_skipped: succeed when the framework steps are left out because
|
||||||
# SSH to Gitea is not available (the Maintainer agreed to that).
|
# SSH to Gitea is not available (the Maintainer agreed to that).
|
||||||
@@ -165,3 +165,39 @@ copy_templates() {
|
|||||||
notes="$notes; ${STATE[template_note]}"
|
notes="$notes; ${STATE[template_note]}"
|
||||||
finish_step "$label" "created" "($notes)"
|
finish_step "$label" "created" "($notes)"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# exclude_framework_files: make git ignore .claude, .agents and AGENTS.md in
|
||||||
|
# the new project, through its .git/info/exclude (never .gitignore, so no
|
||||||
|
# tracked file changes and nothing is committed). Only when the framework
|
||||||
|
# steps ran. A path git already tracks stays tracked: an exclusion does not
|
||||||
|
# apply to it, so the step names it.
|
||||||
|
exclude_framework_files() {
|
||||||
|
local label="Git excludes" dir="${PROJECT[directory]}" entry path tracked=() note written
|
||||||
|
if is_framework_skipped; then
|
||||||
|
finish_step "$label" "skipped" "(no SSH access to Gitea)"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
begin_step "$label"
|
||||||
|
exclude_from_git "$dir" "RepoFoundry: the files installed from the framework" "${FRAMEWORK_EXCLUDES[@]}" ||
|
||||||
|
die "could not make git ignore the framework files in $dir; check $dir/.git/info/exclude"
|
||||||
|
written="$REPLY"
|
||||||
|
for entry in "${FRAMEWORK_EXCLUDES[@]}"; do
|
||||||
|
path="${entry#/}"
|
||||||
|
if [[ -n "$(git_project "$dir" ls-files -- "$path")" ]]; then
|
||||||
|
tracked+=("$path")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if ((written == 0)); then
|
||||||
|
note="${FRAMEWORK_EXCLUDES[*]} already excluded"
|
||||||
|
else
|
||||||
|
note="${FRAMEWORK_EXCLUDES[*]} added to .git/info/exclude"
|
||||||
|
fi
|
||||||
|
if ((${#tracked[@]})); then
|
||||||
|
note="$note; git tracks ${tracked[*]}, so it is not ignored"
|
||||||
|
fi
|
||||||
|
if ((written == 0)); then
|
||||||
|
finish_step "$label" "reused" "($note)"
|
||||||
|
else
|
||||||
|
finish_step "$label" "created" "($note)"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|||||||
+35
-1
@@ -4,7 +4,7 @@
|
|||||||
#
|
#
|
||||||
# Part of create-project.sh: sourced by it, never run on its own.
|
# Part of create-project.sh: sourced by it, never run on its own.
|
||||||
#
|
#
|
||||||
# Provides: git_project, fetch_origin
|
# Provides: git_project, exclude_from_git, fetch_origin
|
||||||
|
|
||||||
# git_project DIR ARGS...: run git in DIR. Prompts are switched off and stdin
|
# git_project DIR ARGS...: run git in DIR. Prompts are switched off and stdin
|
||||||
# is closed (git must not eat the answers meant for later prompts), so a
|
# is closed (git must not eat the answers meant for later prompts), so a
|
||||||
@@ -16,6 +16,40 @@ git_project() {
|
|||||||
git -C "$dir" "$@" </dev/null
|
git -C "$dir" "$@" </dev/null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# exclude_from_git DIR COMMENT ENTRY...: make git ignore each ENTRY in DIR
|
||||||
|
# without touching a tracked file: the entries go into .git/info/exclude,
|
||||||
|
# which is never committed. An entry that already takes effect (checked by its
|
||||||
|
# pattern, so a tracked path counts) is left out, and with none left nothing
|
||||||
|
# is written. The comment line goes above the entries that are written. The
|
||||||
|
# number of entries written is left in REPLY; the return status is 1 when an
|
||||||
|
# entry still does not take effect afterwards.
|
||||||
|
exclude_from_git() {
|
||||||
|
local dir="$1" comment="$2" gitdir exclude entry path missing=()
|
||||||
|
shift 2
|
||||||
|
for entry in "$@"; do
|
||||||
|
path="${entry#/}"
|
||||||
|
if ! git_project "$dir" check-ignore --no-index -q -- "${path%/}"; then
|
||||||
|
missing+=("$entry")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
REPLY="${#missing[@]}"
|
||||||
|
if ((REPLY == 0)); then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
gitdir="$(git_project "$dir" rev-parse --absolute-git-dir)"
|
||||||
|
exclude="$gitdir/info/exclude"
|
||||||
|
mkdir -p -- "$gitdir/info"
|
||||||
|
# Start on a fresh line when the file does not end with one.
|
||||||
|
if [[ -s $exclude && -n "$(tail -c 1 -- "$exclude")" ]]; then
|
||||||
|
printf '\n' >>"$exclude"
|
||||||
|
fi
|
||||||
|
printf '%s\n' "# $comment" "${missing[@]}" >>"$exclude"
|
||||||
|
for entry in "${missing[@]}"; do
|
||||||
|
path="${entry#/}"
|
||||||
|
git_project "$dir" check-ignore --no-index -q -- "${path%/}" || return 1
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
# fetch_origin DIR: fetch the Gitea repository into the project. Over SSH the
|
# 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
|
# 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
|
# GIT_ASKPASS helper and the environment of this one command: it is never part
|
||||||
|
|||||||
@@ -51,6 +51,7 @@ print_plan() {
|
|||||||
say "$(printf ' %-18s: %s' "Framework" "add $(framework_url) as a submodule")"
|
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' "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)")"
|
say "$(printf ' %-18s: %s' "Templates" "AGENTS.md and docs/artifact-registry.md (you are asked before a file is replaced)")"
|
||||||
|
say "$(printf ' %-18s: %s' "Git excludes" "${FRAMEWORK_EXCLUDES[*]} go into .git/info/exclude (no tracked file changes)")"
|
||||||
else
|
else
|
||||||
say "$(printf ' %-18s: %s' "Framework" "NOT possible without SSH to Gitea; you will be asked whether to go on without it")"
|
say "$(printf ' %-18s: %s' "Framework" "NOT possible without SSH to Gitea; you will be asked whether to go on without it")"
|
||||||
fi
|
fi
|
||||||
|
|||||||
Reference in New Issue
Block a user