Compare commits
2
Commits
de78fb195f
...
fb6c439325
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fb6c439325 | ||
|
|
75e8e91f9a |
@@ -1,4 +1,4 @@
|
||||
# RepoFoundry
|
||||
# Repo Foundry
|
||||
|
||||
RepoFoundry (`src/create-project.sh`) sets up a new project in one run:
|
||||
|
||||
@@ -87,6 +87,41 @@ chmod 600 .env # Linux and macOS
|
||||
Every URL must start with `https://` and must not contain a user name,
|
||||
password, query string or fragment. A credential key in this file is rejected.
|
||||
|
||||
### `config.env` (project details, optional)
|
||||
|
||||
Any of the details the script asks for can be set in `config.env` instead.
|
||||
A detail that is set is used and not asked; the summary marks it with
|
||||
`(from config.env)`.
|
||||
|
||||
| Key | Detail | Accepted value |
|
||||
| --- | --- | --- |
|
||||
| `PROJECT_NAME` | repository name | letters, digits, `.`, `_`, `-`; at most 100; not ending in `.git` |
|
||||
| `PROJECT_DESCRIPTION` | description | at most 350 characters; may be empty |
|
||||
| `PROJECT_VISIBILITY` | visibility | `private` or `public` |
|
||||
| `GITEA_OWNER` | Gitea user or organization | letters, digits, `.`, `_`, `-`; at most 39 |
|
||||
| `USE_GITHUB` | also create a GitHub repository | `yes` or `no` |
|
||||
| `GITHUB_OWNER` | GitHub user or organization | letters, digits, `-`; used only when GitHub is used |
|
||||
| `PROJECT_DIRECTORY` | local directory | not empty, not starting with `-` |
|
||||
| `ENABLE_PLAN_GATE` | enable the plan gate | `yes` or `no` |
|
||||
|
||||
- A key that is present counts as set, even when its value is empty. Only
|
||||
`PROJECT_DESCRIPTION` may be empty (no description); an empty value for any
|
||||
other key stops the run. Remove the line to be asked instead.
|
||||
- An invalid value stops the run before any request to a host and names the
|
||||
key. The script never falls back to asking for it.
|
||||
- `USE_GITHUB=no` skips the GitHub owner and every GitHub step; a
|
||||
`GITHUB_OWNER` set at the same time is ignored, with a warning.
|
||||
- Only these eight details can be set. The confirmations stay questions that
|
||||
default to no: create now, reusing an existing repository, an existing
|
||||
directory, `core.hooksPath` and replacing a template file.
|
||||
- These keys are accepted in `config.env` only, never in `.env`.
|
||||
|
||||
With all eight set, a run asks only the confirmations:
|
||||
|
||||
```bash
|
||||
src/create-project.sh --apply # asks only "Create these now (y/n) [n]"
|
||||
```
|
||||
|
||||
### `.env` (credentials)
|
||||
|
||||
| Key | Meaning |
|
||||
|
||||
@@ -30,6 +30,21 @@ GITEA_API_URL=https://<your gitea instance>/api/v1
|
||||
# The server may enforce a minimum. Default: 10m0s.
|
||||
#MIRROR_INTERVAL=10m0s
|
||||
|
||||
# Optional. Project details that are set here are not asked. A key that is
|
||||
# present counts as set, even when empty; only PROJECT_DESCRIPTION may be
|
||||
# empty. An invalid value stops the run and names the key. Remove or comment
|
||||
# out a line to be asked for it. The confirmations ("Create these now" and
|
||||
# the questions about existing repositories, directories and files) are
|
||||
# always asked.
|
||||
#PROJECT_NAME=my-project
|
||||
#PROJECT_DESCRIPTION=What the project is for
|
||||
#PROJECT_VISIBILITY=private # private or public
|
||||
#GITEA_OWNER=my-organization
|
||||
#USE_GITHUB=yes # yes or no
|
||||
#GITHUB_OWNER=my-organization # used only when USE_GITHUB is yes
|
||||
#PROJECT_DIRECTORY=./my-project
|
||||
#ENABLE_PLAN_GATE=no # yes or no
|
||||
|
||||
# Optional. OWNER/NAME of the SQA-QC-Framework repository on the Gitea server;
|
||||
# it is added to the new project as a submodule over SSH.
|
||||
# Default: TirSystem/SQA-QC-Framework.
|
||||
|
||||
+40
-1
@@ -4,7 +4,7 @@
|
||||
#
|
||||
# Part of create-project.sh: sourced by it, never run on its own.
|
||||
#
|
||||
# Provides: unquote_value, parse_env_file, parse_env_entry, validate_config, validate_credentials, require_github_credentials, warn_if_env_unsafe, load_configuration
|
||||
# Provides: unquote_value, parse_env_file, parse_env_entry, validate_config, check_preset, check_preset_choice, validate_project_presets, validate_credentials, require_github_credentials, warn_if_env_unsafe, load_configuration
|
||||
|
||||
# unquote_value RAW: strip matching quotes (or a trailing " # comment" on an
|
||||
# unquoted value) and return the value in REPLY. Fails on unbalanced quotes.
|
||||
@@ -103,6 +103,45 @@ validate_config() {
|
||||
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_project_presets
|
||||
}
|
||||
|
||||
# check_preset KEY VALIDATOR HINT: when KEY is set in config.env its value must
|
||||
# pass VALIDATOR. A key that is present counts as set; only the description
|
||||
# may be empty. The message names the key, never the value.
|
||||
check_preset() {
|
||||
local key="$1" validator="$2" hint="$3"
|
||||
[[ -n ${CONFIG[$key]+set} ]] || return 0
|
||||
if [[ -z ${CONFIG[$key]} && $key != PROJECT_DESCRIPTION ]]; then
|
||||
die "$key in $CONFIG_FILE is empty; remove the line to be asked, or give a value ($hint)"
|
||||
fi
|
||||
"$validator" "${CONFIG[$key]}" ||
|
||||
die "$key in $CONFIG_FILE is not valid: $hint"
|
||||
}
|
||||
|
||||
# check_preset_choice KEY CHOICE...: like check_preset for a fixed list of
|
||||
# words; the value is stored in lower case.
|
||||
check_preset_choice() {
|
||||
local key="$1"
|
||||
shift
|
||||
[[ -n ${CONFIG[$key]+set} ]] || return 0
|
||||
[[ -n ${CONFIG[$key]} ]] ||
|
||||
die "$key in $CONFIG_FILE is empty; remove the line to be asked, or give one of: $*"
|
||||
CONFIG[$key]="${CONFIG[$key],,}"
|
||||
in_list "${CONFIG[$key]}" "$@" ||
|
||||
die "$key in $CONFIG_FILE must be one of: $*"
|
||||
}
|
||||
|
||||
# The optional project details that may be preset in config.env.
|
||||
validate_project_presets() {
|
||||
check_preset PROJECT_NAME is_valid_repo_name "$HINT_REPO_NAME"
|
||||
check_preset PROJECT_DESCRIPTION is_valid_description "$HINT_DESCRIPTION"
|
||||
check_preset_choice PROJECT_VISIBILITY private public
|
||||
check_preset GITEA_OWNER is_valid_gitea_owner "$HINT_GITEA_OWNER"
|
||||
check_preset_choice USE_GITHUB yes no
|
||||
check_preset GITHUB_OWNER is_valid_github_owner "$HINT_GITHUB_OWNER"
|
||||
check_preset PROJECT_DIRECTORY is_valid_directory "$HINT_DIRECTORY"
|
||||
check_preset_choice ENABLE_PLAN_GATE yes no
|
||||
}
|
||||
|
||||
validate_credentials() {
|
||||
|
||||
+12
-1
@@ -19,11 +19,20 @@ readonly DEFAULT_SSH_PORT=10022
|
||||
readonly DEFAULT_FRAMEWORK_REPO="TirSystem/SQA-QC-Framework"
|
||||
readonly AGPL_LICENSE_KEY="AGPL-3.0"
|
||||
readonly DEFAULT_BRANCH="main"
|
||||
# What the prompts and the preset keys in config.env both tell the Maintainer
|
||||
# when a value is refused.
|
||||
readonly HINT_REPO_NAME="use letters, digits, '.', '_' or '-' (at most 100), not ending in .git"
|
||||
readonly HINT_DESCRIPTION="at most $MAX_DESCRIPTION_LENGTH characters and no control characters"
|
||||
readonly HINT_GITEA_OWNER="use letters, digits, '.', '_' or '-' (at most 39)"
|
||||
readonly HINT_GITHUB_OWNER="use letters, digits or '-' (at most 39)"
|
||||
readonly HINT_DIRECTORY="must not be empty, start with '-' or contain control characters"
|
||||
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 FRAMEWORK_REPO)
|
||||
GITEA_SSH_PORT MIRROR_INTERVAL FRAMEWORK_REPO
|
||||
PROJECT_NAME PROJECT_DESCRIPTION PROJECT_VISIBILITY GITEA_OWNER USE_GITHUB
|
||||
GITHUB_OWNER PROJECT_DIRECTORY ENABLE_PLAN_GATE)
|
||||
readonly CREDENTIAL_KEYS=(GITHUB_PAT GITHUB_USER GITEA_TOKEN)
|
||||
|
||||
CONFIG_FILE="$PROJECT_ROOT/config.env"
|
||||
@@ -41,6 +50,8 @@ TEMP_FILES=()
|
||||
declare -A CONFIG=()
|
||||
declare -A CREDENTIALS=()
|
||||
declare -A PROJECT=()
|
||||
# Project details that came from config.env instead of a prompt (PRESET[name]=1).
|
||||
declare -A PRESET=()
|
||||
# Facts found by the preflight checks (logins, owner kinds, repository state).
|
||||
declare -A STATE=()
|
||||
# Outcome of each step in PLAN_STEPS, for the final report.
|
||||
|
||||
+54
-22
@@ -4,34 +4,59 @@
|
||||
#
|
||||
# Part of create-project.sh: sourced by it, never run on its own.
|
||||
#
|
||||
# Provides: collect_project_details, yes_no, credential_state, print_summary
|
||||
# Provides: preset_detail, collect_project_details, collect_github_details, source_note, yes_no, credential_state, print_summary
|
||||
|
||||
# preset_detail KEY NAME: a detail set in config.env is used and not asked;
|
||||
# the value is returned in REPLY and marked in PRESET[NAME].
|
||||
preset_detail() {
|
||||
[[ -n ${CONFIG[$1]+set} ]] || return 1
|
||||
REPLY="${CONFIG[$1]}"
|
||||
PRESET[$2]=1
|
||||
}
|
||||
|
||||
# Ask for each project detail, except those set in config.env.
|
||||
collect_project_details() {
|
||||
prompt_value "Repository name" "" is_valid_repo_name \
|
||||
"use letters, digits, '.', '_' or '-' (at most 100), not ending in .git"
|
||||
preset_detail PROJECT_NAME name ||
|
||||
prompt_value "Repository name" "" is_valid_repo_name "$HINT_REPO_NAME"
|
||||
PROJECT[name]="$REPLY"
|
||||
prompt_value "Description (optional)" "" is_valid_description \
|
||||
"at most $MAX_DESCRIPTION_LENGTH characters and no control characters"
|
||||
preset_detail PROJECT_DESCRIPTION description ||
|
||||
prompt_value "Description (optional)" "" is_valid_description "$HINT_DESCRIPTION"
|
||||
PROJECT[description]="$REPLY"
|
||||
preset_detail PROJECT_VISIBILITY visibility ||
|
||||
prompt_choice "Visibility" private private public
|
||||
PROJECT[visibility]="$REPLY"
|
||||
prompt_value "Gitea owner (user or organization)" "" is_valid_gitea_owner \
|
||||
"use letters, digits, '.', '_' or '-' (at most 39)"
|
||||
preset_detail GITEA_OWNER gitea_owner ||
|
||||
prompt_value "Gitea owner (user or organization)" "" is_valid_gitea_owner "$HINT_GITEA_OWNER"
|
||||
PROJECT[gitea_owner]="$REPLY"
|
||||
collect_github_details
|
||||
preset_detail PROJECT_DIRECTORY directory ||
|
||||
prompt_value "Local directory" "./${PROJECT[name]}" is_valid_directory "$HINT_DIRECTORY"
|
||||
PROJECT[directory]="$REPLY"
|
||||
if preset_detail ENABLE_PLAN_GATE is_plan_gate_enabled; then
|
||||
[[ $REPLY == yes ]] && REPLY=1 || REPLY=0
|
||||
else
|
||||
prompt_yes_no "Enable the plan gate" n
|
||||
fi
|
||||
PROJECT[is_plan_gate_enabled]="$REPLY"
|
||||
}
|
||||
|
||||
# Whether GitHub is used, and its owner. A GITHUB_OWNER set while GitHub is
|
||||
# not used is ignored, with a warning.
|
||||
collect_github_details() {
|
||||
if preset_detail USE_GITHUB has_github; then
|
||||
[[ $REPLY == yes ]] && REPLY=1 || REPLY=0
|
||||
else
|
||||
prompt_yes_no "Also create a GitHub repository (applies the AGPL license)" y
|
||||
fi
|
||||
PROJECT[has_github]="$REPLY"
|
||||
PROJECT[github_owner]=""
|
||||
if ((PROJECT[has_github])); then
|
||||
prompt_value "GitHub owner (user or organization)" \
|
||||
"${CREDENTIALS[GITHUB_USER]:-}" is_valid_github_owner \
|
||||
"use letters, digits or '-' (at most 39)"
|
||||
preset_detail GITHUB_OWNER github_owner ||
|
||||
prompt_value "GitHub owner (user or organization)" "${CREDENTIALS[GITHUB_USER]:-}" is_valid_github_owner "$HINT_GITHUB_OWNER"
|
||||
PROJECT[github_owner]="$REPLY"
|
||||
elif [[ -n ${CONFIG[GITHUB_OWNER]+set} ]]; then
|
||||
warn "GITHUB_OWNER in $CONFIG_FILE is ignored because GitHub is not used"
|
||||
fi
|
||||
prompt_value "Local directory" "./${PROJECT[name]}" is_valid_directory \
|
||||
"must not be empty, start with '-' or contain control characters"
|
||||
PROJECT[directory]="$REPLY"
|
||||
prompt_yes_no "Enable the plan gate" n
|
||||
PROJECT[is_plan_gate_enabled]="$REPLY"
|
||||
}
|
||||
|
||||
yes_no() {
|
||||
@@ -50,20 +75,27 @@ credential_state() {
|
||||
fi
|
||||
}
|
||||
|
||||
# source_note NAME: the marker shown after a value that came from config.env.
|
||||
source_note() {
|
||||
if [[ -n ${PRESET[$1]:-} ]]; then
|
||||
printf ' (from config.env)'
|
||||
fi
|
||||
}
|
||||
|
||||
print_summary() {
|
||||
say ""
|
||||
say "$PROJECT_NAME $VERSION"
|
||||
say "Collected details:"
|
||||
say " Repository : ${PROJECT[name]} (${PROJECT[visibility]})"
|
||||
say " Description : ${PROJECT[description]:-(none)}"
|
||||
say " Gitea : ${CONFIG[GITEA_URL]}/${PROJECT[gitea_owner]}/${PROJECT[name]}"
|
||||
say " Repository : ${PROJECT[name]}$(source_note name) (${PROJECT[visibility]}$(source_note visibility))"
|
||||
say " Description : ${PROJECT[description]:-(none)}$(source_note description)"
|
||||
say " Gitea : ${CONFIG[GITEA_URL]}/${PROJECT[gitea_owner]}/${PROJECT[name]}$(source_note gitea_owner)"
|
||||
if ((PROJECT[has_github])); then
|
||||
say " GitHub : ${CONFIG[GITHUB_WEB_URL]}/${PROJECT[github_owner]}/${PROJECT[name]} (AGPL license applied)"
|
||||
say " GitHub : ${CONFIG[GITHUB_WEB_URL]}/${PROJECT[github_owner]}/${PROJECT[name]} (AGPL license applied)$(source_note github_owner)"
|
||||
else
|
||||
say " GitHub : not used"
|
||||
say " GitHub : not used$(source_note has_github)"
|
||||
fi
|
||||
say " Directory : ${PROJECT[directory]}"
|
||||
say " Plan gate : $(yes_no "${PROJECT[is_plan_gate_enabled]}")"
|
||||
say " Directory : ${PROJECT[directory]}$(source_note directory)"
|
||||
say " Plan gate : $(yes_no "${PROJECT[is_plan_gate_enabled]}")$(source_note is_plan_gate_enabled)"
|
||||
say "Credentials : GITEA_TOKEN $(credential_state GITEA_TOKEN)," \
|
||||
"GITHUB_PAT $(credential_state GITHUB_PAT)"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,264 @@
|
||||
#!/usr/bin/env bash
|
||||
# test-presets.sh - tests for the project details preset in config.env
|
||||
# (MIL-004): a detail that is set there is not asked, an invalid one stops
|
||||
# the run, and the confirmations stay interactive. Sourced by run-tests.sh.
|
||||
|
||||
# shellcheck disable=SC2016 # snippet and fixture text is literal on purpose
|
||||
|
||||
# The eight keys with a value that differs from the answer given when asked.
|
||||
readonly PRESET_ALL='PROJECT_NAME=preset-app
|
||||
PROJECT_DESCRIPTION=From the config
|
||||
PROJECT_VISIBILITY=public
|
||||
GITEA_OWNER=PresetOrg
|
||||
USE_GITHUB=yes
|
||||
GITHUB_OWNER=preset-gh
|
||||
PROJECT_DIRECTORY=./preset-dir
|
||||
ENABLE_PLAN_GATE=yes'
|
||||
|
||||
# The same details as ANSWERS_GITHUB, so a run with all of them preset is the
|
||||
# same run with no answers.
|
||||
readonly PRESET_LIKE_ANSWERS='PROJECT_NAME=my-app
|
||||
PROJECT_DESCRIPTION=A test app
|
||||
PROJECT_VISIBILITY=private
|
||||
GITEA_OWNER=TirSystem
|
||||
USE_GITHUB=yes
|
||||
GITHUB_OWNER=acme-org
|
||||
PROJECT_DIRECTORY=./my-app
|
||||
ENABLE_PLAN_GATE=no'
|
||||
|
||||
# collect_with PRESET_LINES INPUT: parse a config holding the preset lines,
|
||||
# collect the details and print them one per line.
|
||||
collect_with() {
|
||||
printf '%s\n' "$1" >"$WORK/preset.env"
|
||||
run_lib "$2" 'parse_env_file "'"$WORK"'/preset.env" CONFIG_KEYS CONFIG
|
||||
validate_project_presets
|
||||
collect_project_details
|
||||
for k in name description visibility gitea_owner has_github github_owner directory is_plan_gate_enabled; do
|
||||
printf "%s=%s\n" "$k" "${PROJECT[$k]}"
|
||||
done'
|
||||
}
|
||||
|
||||
# Each key, the answers a run gives for the other seven details, and the
|
||||
# prompt that must not be shown for the key.
|
||||
test_each_key_is_used_and_not_asked() {
|
||||
local key value field label line input
|
||||
local -A answer=([PROJECT_NAME]=asked-app [PROJECT_DESCRIPTION]="Asked description"
|
||||
[PROJECT_VISIBILITY]=private [GITEA_OWNER]=AskedOrg [USE_GITHUB]=y
|
||||
[GITHUB_OWNER]=asked-gh [PROJECT_DIRECTORY]=./asked-dir [ENABLE_PLAN_GATE]=n)
|
||||
local order=(PROJECT_NAME PROJECT_DESCRIPTION PROJECT_VISIBILITY GITEA_OWNER
|
||||
USE_GITHUB GITHUB_OWNER PROJECT_DIRECTORY ENABLE_PLAN_GATE)
|
||||
while IFS='|' read -r key value field label; do
|
||||
input=""
|
||||
for line in "${order[@]}"; do
|
||||
if [[ $line != "$key" ]]; then
|
||||
input+="${answer[$line]}"$'\n'
|
||||
fi
|
||||
done
|
||||
collect_with "$key=$value" "$input"
|
||||
assert_status "$key preset" 0 "$STATUS"
|
||||
assert_contains "$key value used" "$OUT" "$field"
|
||||
assert_not_contains "$key not asked" "$ERR" "$label"
|
||||
assert_contains "other details still asked" "$OUT" "asked"
|
||||
done <<'EOF'
|
||||
PROJECT_NAME|preset-app|name=preset-app|Repository name
|
||||
PROJECT_DESCRIPTION|From the config|description=From the config|Description
|
||||
PROJECT_VISIBILITY|public|visibility=public|Visibility
|
||||
GITEA_OWNER|PresetOrg|gitea_owner=PresetOrg|Gitea owner
|
||||
USE_GITHUB|yes|has_github=1|Also create a GitHub
|
||||
GITHUB_OWNER|preset-gh|github_owner=preset-gh|GitHub owner
|
||||
PROJECT_DIRECTORY|./preset-dir|directory=./preset-dir|Local directory
|
||||
ENABLE_PLAN_GATE|yes|is_plan_gate_enabled=1|Enable the plan gate
|
||||
EOF
|
||||
}
|
||||
|
||||
test_all_keys_set_asks_nothing() {
|
||||
collect_with "$PRESET_ALL" ""
|
||||
assert_status "no input needed" 0 "$STATUS"
|
||||
assert_eq "every value from the config" $'name=preset-app\ndescription=From the config\nvisibility=public\ngitea_owner=PresetOrg\nhas_github=1\ngithub_owner=preset-gh\ndirectory=./preset-dir\nis_plan_gate_enabled=1' "$OUT"
|
||||
assert_eq "no prompt text at all" "" "$ERR"
|
||||
}
|
||||
|
||||
test_absent_keys_are_asked_as_before() {
|
||||
collect_with "PROJECT_NAME=preset-app" $'\n\nTirSystem\nn\n\nn\n'
|
||||
assert_status "mixed" 0 "$STATUS"
|
||||
assert_contains "preset name" "$OUT" "name=preset-app"
|
||||
assert_contains "default directory from the preset name" "$OUT" "directory=./preset-app"
|
||||
assert_contains "other details asked" "$ERR" "Gitea owner"
|
||||
}
|
||||
|
||||
test_values_are_taken_in_any_case() {
|
||||
collect_with $'PROJECT_VISIBILITY=PUBLIC\nUSE_GITHUB=No\nENABLE_PLAN_GATE=YES' $'x-app\n\nTirSystem\n\n'
|
||||
assert_status "case ignored" 0 "$STATUS"
|
||||
assert_contains "visibility" "$OUT" "visibility=public"
|
||||
assert_contains "no GitHub" "$OUT" "has_github=0"
|
||||
assert_contains "plan gate" "$OUT" "is_plan_gate_enabled=1"
|
||||
}
|
||||
|
||||
# ------------------------------------------------------- empty and invalid
|
||||
|
||||
test_empty_value_counts_as_set_only_for_the_description() {
|
||||
collect_with "PROJECT_DESCRIPTION=" $'my-app\npublic\nTirSystem\nn\n\nn\n'
|
||||
assert_status "empty description accepted" 0 "$STATUS"
|
||||
assert_contains "description empty" "$OUT" "description="
|
||||
assert_not_contains "description not asked" "$ERR" "Description"
|
||||
local key
|
||||
for key in PROJECT_NAME PROJECT_VISIBILITY GITEA_OWNER USE_GITHUB GITHUB_OWNER \
|
||||
PROJECT_DIRECTORY ENABLE_PLAN_GATE; do
|
||||
printf '%s=\n' "$key" >"$WORK/preset.env"
|
||||
run_lib "" 'parse_env_file "'"$WORK"'/preset.env" CONFIG_KEYS CONFIG
|
||||
validate_project_presets'
|
||||
assert_status "$key empty" 1 "$STATUS"
|
||||
assert_contains "$key named" "$ERR" "$key in"
|
||||
assert_contains "$key says empty" "$ERR" "is empty"
|
||||
done
|
||||
}
|
||||
|
||||
test_invalid_values_are_refused_naming_the_key() {
|
||||
local key value
|
||||
while IFS='|' read -r key value; do
|
||||
printf '%s=%s\n' "$key" "$value" >"$WORK/preset.env"
|
||||
run_lib "" 'parse_env_file "'"$WORK"'/preset.env" CONFIG_KEYS CONFIG
|
||||
validate_project_presets'
|
||||
assert_status "$key=$value" 1 "$STATUS"
|
||||
assert_contains "$key=$value named" "$ERR" "$key in"
|
||||
done <<'EOF'
|
||||
PROJECT_NAME|bad name
|
||||
PROJECT_NAME|x.git
|
||||
PROJECT_VISIBILITY|internal
|
||||
GITEA_OWNER|-lead
|
||||
USE_GITHUB|maybe
|
||||
USE_GITHUB|1
|
||||
GITHUB_OWNER|octo_user
|
||||
PROJECT_DIRECTORY|-rf
|
||||
ENABLE_PLAN_GATE|true
|
||||
EOF
|
||||
# A description over the limit (350 characters) is refused too.
|
||||
printf 'PROJECT_DESCRIPTION=%s\n' "$(printf 'a%.0s' $(seq 1 351))" >"$WORK/preset.env"
|
||||
run_lib "" 'parse_env_file "'"$WORK"'/preset.env" CONFIG_KEYS CONFIG
|
||||
validate_project_presets'
|
||||
assert_status "long description" 1 "$STATUS"
|
||||
assert_contains "named" "$ERR" "PROJECT_DESCRIPTION in"
|
||||
}
|
||||
|
||||
test_invalid_value_stops_before_any_request_and_never_asks() {
|
||||
setup_hosts
|
||||
printf 'PROJECT_NAME=bad name\n' >>"$WORK/config.env"
|
||||
run_apply ""
|
||||
assert_status "stopped" 1 "$STATUS"
|
||||
assert_contains "key named" "$ERR" "PROJECT_NAME in"
|
||||
assert_not_contains "no value asked instead" "$ERR" "Repository name:"
|
||||
assert_eq "no request made" "" "$(calls)"
|
||||
}
|
||||
|
||||
test_new_keys_are_rejected_in_env_and_credentials_in_config() {
|
||||
setup_hosts
|
||||
printf 'PROJECT_NAME=my-app\n' >>"$WORK/.env"
|
||||
run_dry ""
|
||||
assert_status ".env with a project key" 1 "$STATUS"
|
||||
assert_contains "unknown in .env" "$ERR" "unknown key 'PROJECT_NAME'"
|
||||
setup_hosts
|
||||
printf 'GITEA_TOKEN=abcdefgh12345\n' >>"$WORK/config.env"
|
||||
run_dry ""
|
||||
assert_status "config.env with a credential" 1 "$STATUS"
|
||||
assert_contains "credential refused" "$ERR" "is a credential"
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------- GitHub
|
||||
|
||||
test_use_github_no_skips_the_owner_and_warns_about_a_stray_one() {
|
||||
collect_with $'USE_GITHUB=no\nGITHUB_OWNER=acme-org' $'my-app\n\n\nTirSystem\n\nn\n'
|
||||
assert_status "GitHub off" 0 "$STATUS"
|
||||
assert_contains "no GitHub" "$OUT" "has_github=0"
|
||||
assert_contains "no owner" "$OUT" "github_owner="
|
||||
assert_not_contains "owner not asked" "$ERR" "GitHub owner ("
|
||||
assert_contains "ignored with a warning" "$ERR" "GITHUB_OWNER in"
|
||||
assert_contains "says why" "$ERR" "ignored because GitHub is not used"
|
||||
collect_with "USE_GITHUB=no" $'my-app\n\n\nTirSystem\n\nn\n'
|
||||
assert_not_contains "no warning without the key" "$ERR" "ignored"
|
||||
}
|
||||
|
||||
test_github_owner_preset_is_used_when_github_is_asked_for() {
|
||||
collect_with "GITHUB_OWNER=preset-gh" $'my-app\n\n\nTirSystem\ny\n\nn\n'
|
||||
assert_contains "owner from the config" "$OUT" "github_owner=preset-gh"
|
||||
assert_not_contains "owner not asked" "$ERR" "GitHub owner ("
|
||||
collect_with "GITHUB_OWNER=preset-gh" $'my-app\n\n\nTirSystem\nn\n\nn\n'
|
||||
assert_contains "ignored when answered no" "$ERR" "ignored because GitHub is not used"
|
||||
}
|
||||
|
||||
test_use_github_no_makes_no_github_call() {
|
||||
setup_hosts
|
||||
printf '%s\n' "$PRESET_LIKE_ANSWERS" | sed 's/^USE_GITHUB=.*/USE_GITHUB=no/' >>"$WORK/config.env"
|
||||
run_dry ""
|
||||
assert_status "dry run" 0 "$STATUS"
|
||||
assert_contains "GitHub not used" "$OUT" "GitHub repository : not used"
|
||||
assert_not_contains "no GitHub call" "$(calls)" "api.github.com"
|
||||
assert_not_contains "no GitHub owner asked" "$ERR" "GitHub owner ("
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------ the summary
|
||||
|
||||
test_summary_marks_the_values_from_config_env() {
|
||||
setup_hosts
|
||||
printf '%s\n' "$PRESET_LIKE_ANSWERS" >>"$WORK/config.env"
|
||||
run_dry ""
|
||||
assert_status "all preset" 0 "$STATUS"
|
||||
assert_contains "name" "$OUT" "Repository : my-app (from config.env) (private (from config.env))"
|
||||
assert_contains "description" "$OUT" "Description : A test app (from config.env)"
|
||||
assert_contains "Gitea" "$OUT" "/TirSystem/my-app (from config.env)"
|
||||
assert_contains "GitHub" "$OUT" "(AGPL license applied) (from config.env)"
|
||||
assert_contains "directory" "$OUT" "Directory : ./my-app (from config.env)"
|
||||
assert_contains "plan gate" "$OUT" "Plan gate : no (from config.env)"
|
||||
}
|
||||
|
||||
test_summary_marks_nothing_when_everything_is_asked() {
|
||||
setup_hosts
|
||||
run_dry "$ANSWERS_GITHUB"
|
||||
assert_status "all asked" 0 "$STATUS"
|
||||
assert_not_contains "no marker" "$OUT" "(from config.env)"
|
||||
}
|
||||
|
||||
# ----------------------------------------------------- the confirmations
|
||||
|
||||
test_with_every_detail_set_only_the_confirmations_are_asked() {
|
||||
setup_hosts
|
||||
run_apply "$ANSWERS_GITHUB"y$'\n'
|
||||
local asked_calls
|
||||
asked_calls="$(calls)"
|
||||
remove_workdir
|
||||
new_workdir
|
||||
setup_hosts
|
||||
printf '%s\n' "$PRESET_LIKE_ANSWERS" >>"$WORK/config.env"
|
||||
run_apply $'y\n'
|
||||
assert_status "run with presets" 0 "$STATUS"
|
||||
assert_eq "same requests as the run that was asked" "$asked_calls" "$(calls)"
|
||||
assert_contains "created" "$OUT" "This is what exists now"
|
||||
}
|
||||
|
||||
test_with_every_detail_set_create_now_is_still_asked_and_defaults_to_no() {
|
||||
setup_hosts
|
||||
printf '%s\n' "$PRESET_LIKE_ANSWERS" >>"$WORK/config.env"
|
||||
run_apply $'n\n'
|
||||
assert_status "declined" 0 "$STATUS"
|
||||
assert_contains "says so" "$OUT" "Nothing was created."
|
||||
assert_not_contains "nothing created" "$(calls)" "POST"
|
||||
setup_hosts
|
||||
printf '%s\n' "$PRESET_LIKE_ANSWERS" >>"$WORK/config.env"
|
||||
run_apply $'\n'
|
||||
assert_contains "empty answer means no" "$OUT" "Nothing was created."
|
||||
assert_not_contains "no POST on the default" "$(calls)" "POST"
|
||||
setup_hosts
|
||||
printf '%s\n' "$PRESET_LIKE_ANSWERS" >>"$WORK/config.env"
|
||||
run_apply ""
|
||||
assert_contains "no answer means no" "$OUT" "Nothing was created."
|
||||
assert_not_contains "nothing created without an answer" "$(calls)" "POST"
|
||||
}
|
||||
|
||||
test_with_every_detail_set_an_existing_directory_is_not_replaced() {
|
||||
setup_hosts
|
||||
printf '%s\n' "$PRESET_LIKE_ANSWERS" >>"$WORK/config.env"
|
||||
mkdir -p "$WORK/my-app"
|
||||
printf 'keep\n' >"$WORK/my-app/mine.txt"
|
||||
run_apply $'y\n\n'
|
||||
assert_file_exists "existing file kept" "$WORK/my-app/mine.txt"
|
||||
assert_eq "content kept" "keep" "$(cat "$WORK/my-app/mine.txt")"
|
||||
}
|
||||
Reference in New Issue
Block a user