Merge origin/mil-006-license: .env stays optional, config.env is required

This commit is contained in:
2026-10-07 14:34:04 +08:00
10 changed files with 419 additions and 22 deletions
+31 -9
View File
@@ -90,18 +90,39 @@ if ((BASH_VERSINFO[0] < 4 || (BASH_VERSINFO[0] == 4 && BASH_VERSINFO[1] < 4)));
fi
# Where this script lives; the library files and the project root are found
# from here, never from the current directory.
readonly SCRIPT_FILE="${BASH_SOURCE[0]}"
case "${BASH_SOURCE[0]}" in
*/*) script_path_dir="${BASH_SOURCE[0]%/*}" ;;
# from here, never from the current directory. A link to the script (a
# command in a folder on PATH) is followed to the real file, however many
# links lie on the way; readlink without -f exists on Linux, macOS and Git
# Bash alike.
script_path="${BASH_SOURCE[0]}"
script_link_count=0
while [[ -L $script_path ]]; do
((++script_link_count <= 40)) || {
printf 'error: too many links when following %s\n' "${BASH_SOURCE[0]}" >&2
exit 1
}
script_link_target="$(readlink -- "$script_path")"
case "$script_link_target" in
/*) script_path="$script_link_target" ;;
*)
case "$script_path" in
*/*) script_path="${script_path%/*}/$script_link_target" ;;
*) script_path="$script_link_target" ;;
esac
;;
esac
done
readonly SCRIPT_FILE="$script_path"
case "$script_path" in
*/*) script_path_dir="${script_path%/*}" ;;
*) script_path_dir="." ;;
esac
SCRIPT_DIR="$(cd "$script_path_dir" && pwd)"
SCRIPT_DIR="$(cd -P "$script_path_dir" && pwd -P)"
readonly SCRIPT_DIR
unset script_path_dir
# The script lives in src/; the configuration files live one level up, in
# the project root, next to config.env.example and .env.example.
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
unset script_path script_path_dir script_link_count script_link_target
# The script lives in src/; the checkout is one level up, next to
# config.env.example and .env.example.
PROJECT_ROOT="$(cd -P "$SCRIPT_DIR/.." && pwd -P)"
readonly PROJECT_ROOT
# shellcheck source=lib/constants.sh
@@ -171,6 +192,7 @@ main() {
trap finish EXIT
is_valid_repo_name "$PROJECT_NAME" ||
die "REPOFOUNDRY_NAME is not a valid project name"
WORKING_FOLDER="$(pwd -P)"
parse_args "$@"
check_tools
setup_temp_dir
+2
View File
@@ -9,6 +9,8 @@
usage() {
cat <<EOF
Usage: ${0##*/} [--apply] [--config FILE] [--env FILE]
Without --config and --env, ./config.env and ./.env in the current folder
are read, then the ones in the checkout.
${0##*/} --help | --version
EOF
}
+53 -1
View File
@@ -191,11 +191,63 @@ warn_if_env_unsafe() {
fi
}
# locate_config_file NAME FLAG FILE_VAR ORIGIN_VAR: the file named with FLAG;
# otherwise ./NAME in the working folder; otherwise NAME in the checkout.
# The origin is "named", "folder" or "checkout". With a fifth word, optional,
# a file found nowhere is not an error: the file stays empty and the origin
# is "none".
locate_config_file() {
local name="$1" flag="$2" optional="${5:-}"
local -n file_ref="$3" origin_ref="$4"
if [[ -n $file_ref ]]; then
origin_ref="named"
elif [[ $WORKING_FOLDER != "$PROJECT_ROOT" && -f $WORKING_FOLDER/$name ]]; then
file_ref="$WORKING_FOLDER/$name"
origin_ref="folder"
elif [[ -f $PROJECT_ROOT/$name ]]; then
file_ref="$PROJECT_ROOT/$name"
origin_ref="checkout"
elif [[ -n $optional ]]; then
origin_ref="none"
else
die "$name is not named with $flag and is in neither the working folder ($WORKING_FOLDER) nor the checkout ($PROJECT_ROOT); name it with $flag FILE"
fi
}
# origin_words ORIGIN: how the origin of a configuration file reads.
origin_words() {
case "$1" in
named) printf 'named on the command line' ;;
none) printf 'none found; a missing credential is asked' ;;
folder) printf 'from the working folder' ;;
*) printf 'from the checkout' ;;
esac
}
# confirm_folder_files: a file taken from the working folder may point the
# Gitea address elsewhere and so send the token there, so it is named with
# the address and needs a yes (default no) before the first request.
confirm_folder_files() {
local files=()
[[ $CONFIG_ORIGIN == folder ]] && files+=("$CONFIG_FILE")
[[ $ENV_ORIGIN == folder ]] && files+=("$ENV_FILE")
((${#files[@]} > 0)) || return 0
say "The working folder supplies: ${files[*]}"
say "Gitea would be ${CONFIG[GITEA_URL]}; the token from the credentials file is sent there."
prompt_yes_no "Use ${files[*]}" n
((REPLY)) || die "stopped before any request: choose the files with --config and --env, or run from the checkout"
}
load_configuration() {
locate_config_file config.env --config CONFIG_FILE CONFIG_ORIGIN
locate_config_file .env --env ENV_FILE ENV_ORIGIN optional
say "Config file : $CONFIG_FILE ($(origin_words "$CONFIG_ORIGIN"))"
say "Credentials file: ${ENV_FILE:-(none)} ($(origin_words "$ENV_ORIGIN"))"
parse_env_file "$CONFIG_FILE" CONFIG_KEYS CONFIG
validate_config
confirm_folder_files
# .env is optional: a credential it does not provide is asked.
if [[ -e $ENV_FILE ]]; then
if [[ -n $ENV_FILE && -e $ENV_FILE ]]; then
parse_env_file "$ENV_FILE" CREDENTIAL_KEYS CREDENTIALS
warn_if_env_unsafe "$ENV_FILE"
fi
+8 -2
View File
@@ -39,8 +39,14 @@ readonly CONFIG_KEYS=(GITHUB_API_URL GITHUB_WEB_URL GITEA_URL GITEA_API_URL
GITHUB_OWNER PROJECT_DIRECTORY ENABLE_PLAN_GATE PROJECT_LICENSE)
readonly CREDENTIAL_KEYS=(GITHUB_PAT GITHUB_USER GITEA_TOKEN)
CONFIG_FILE="$PROJECT_ROOT/config.env"
ENV_FILE="$PROJECT_ROOT/.env"
# The configuration files: named by --config and --env, or chosen by
# locate_config_file. The origin is named, folder or checkout.
CONFIG_FILE=""
ENV_FILE=""
CONFIG_ORIGIN=""
ENV_ORIGIN=""
# The folder the Maintainer started the script in.
WORKING_FOLDER=""
TMP_DIR=""
HAS_JQ=0
HTTP_STATUS=0
+15 -1
View File
@@ -4,7 +4,7 @@
#
# 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
# Provides: is_framework_skipped, init_framework_submodules, 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).
@@ -12,6 +12,18 @@ is_framework_skipped() {
[[ ${STATE[skip_framework]:-0} == 1 ]]
}
# init_framework_submodules DIR: fetch the submodules the framework holds
# itself (the qc checklists). Without a submodule of its own this changes
# nothing. A failure names the command to run by hand.
init_framework_submodules() {
local dir="$1" err
make_temp_file
err="$REPLY"
if ! git_project "$dir" submodule update -q --init --recursive 2>"$err"; then
die "the framework was added but git could not fetch its own submodules (the qc checklists). Run in $dir: git submodule update --init --recursive. Git said: $(head -n 2 "$err" | tr '\n' ' ')"
fi
}
# 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() {
@@ -27,6 +39,7 @@ add_framework() {
if [[ $existing != "$url" ]]; then
die "'framework' already exists in $dir and is not the framework submodule ($url)"
fi
init_framework_submodules "$dir"
finish_step "$label" "reused" "$url (already a submodule)"
return 0
fi
@@ -35,6 +48,7 @@ add_framework() {
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
init_framework_submodules "$dir"
finish_step "$label" "created" "$url"
}