Files
RepoFoundry/src/lib/envfile.sh
T
Tirsvad 4058ab4e3e Exclude .claude, .agents and AGENTS.md from git in the new project
After the templates are copied, create-project.sh adds /.claude, /.agents and
/AGENTS.md to .git/info/exclude of the new project as its own step, "Git
excludes". No .gitignore or tracked file changes and nothing is committed.
The step is skipped when the framework steps are skipped, and a path git
already tracks stays tracked and is named in the step.

The .git/info/exclude code of exclude_env_file moves into the shared helper
exclude_from_git (git.sh), which both now use; the .env behavior is unchanged.

Refs #66
Task: MIL-009#2
2026-10-08 14:06:37 +08:00

87 lines
3.3 KiB
Bash

# shellcheck shell=bash
# shellcheck disable=SC2004,SC2034,SC2154 # shared state and arrays are declared in constants.sh
# envfile.sh - The .env file of the new project: the one place a credential is written.
#
# Part of create-project.sh: sourced by it, never run on its own.
#
# Provides: env_file_keys, env_file_key_list, exclude_env_file, write_env_file, create_env_file
# env_file_keys: the credentials the new project needs, one per line: the
# Gitea token, and the GitHub token and account name when GitHub was chosen.
env_file_keys() {
printf '%s\n' GITEA_TOKEN
if ((PROJECT[has_github])); then
printf '%s\n' GITHUB_PAT GITHUB_USER
fi
}
# env_file_key_list: the same keys on one line, for messages.
env_file_key_list() {
local keys
keys="$(env_file_keys | tr '\n' ' ')"
printf '%s' "${keys% }"
}
# exclude_env_file DIR: make git ignore .env in DIR without touching a tracked
# file: the entry goes into .git/info/exclude, which is never committed. It
# does nothing when .env is already ignored.
exclude_env_file() {
local dir="$1"
exclude_from_git "$dir" "RepoFoundry: the credentials file of this project" "$ENV_FILE_NAME" ||
die "could not make git ignore $ENV_FILE_NAME in $dir; nothing was written to it"
}
# write_env_file DIR IS_REPLACE: write the credentials to DIR/.env. The file is
# created private (mode 600) from the start, never readable by others, even
# for a moment: it is written under umask 077 as a temporary file next to the
# target and moved into place. An existing file is only replaced when
# IS_REPLACE is 1, and a file that appears in the meantime is never replaced.
write_env_file() {
local dir="$1" is_replace="$2" target tmp key
target="$dir/$ENV_FILE_NAME"
tmp="$(umask 077 && mktemp "$dir/$ENV_FILE_NAME.XXXXXX")"
TEMP_FILES+=("$tmp")
{
while IFS= read -r key; do
printf '%s=%s\n' "$key" "${CREDENTIALS[$key]}"
done < <(env_file_keys)
} >"$tmp"
if ((is_replace)); then
mv -f -- "$tmp" "$target"
else
mv -n -- "$tmp" "$target"
if [[ -e $tmp ]]; then
die "$target appeared while it was being written; it was not replaced"
fi
fi
}
# create_env_file: the last step. Only after a yes (default no) is the .env
# written, and an existing one is only replaced after another yes. Nothing
# printed names a value, only the keys.
create_env_file() {
local label="Project .env" dir="${PROJECT[directory]}" keys is_replace=0
keys="$(env_file_key_list)"
begin_step "$label"
prompt_yes_no "Create a $ENV_FILE_NAME file in the project with the credentials it needs ($keys); only you can read it and git ignores it" n
if ! ((REPLY)); then
finish_step "$label" "skipped" "(you declined)"
return 0
fi
if git_project "$dir" ls-files --error-unmatch -- "$ENV_FILE_NAME" >/dev/null 2>&1; then
finish_step "$label" "skipped" "($ENV_FILE_NAME is tracked by git; it was not written)"
return 0
fi
if [[ -e $dir/$ENV_FILE_NAME || -L $dir/$ENV_FILE_NAME ]]; then
prompt_yes_no "$ENV_FILE_NAME already exists in the project. Replace it" n
if ! ((REPLY)); then
finish_step "$label" "kept" "(the existing $ENV_FILE_NAME was left as it was)"
return 0
fi
is_replace=1
fi
exclude_env_file "$dir"
write_env_file "$dir" "$is_replace"
finish_step "$label" "created" "($keys; only you can read it, git ignores it)"
}