Merge pull request 'Implement MIL-007: qc submodules fetched, start through a link, default configuration files' (#53) from mil-007-impl into mil-006-license
Reviewed-on: #53
This commit was merged in pull request #53.
This commit is contained in:
@@ -59,13 +59,73 @@ src/create-project.sh --help
|
|||||||
Nothing has to be installed system-wide: the script runs from the checkout and
|
Nothing has to be installed system-wide: the script runs from the checkout and
|
||||||
loads its own files from `src/lib/`.
|
loads its own files from `src/lib/`.
|
||||||
|
|
||||||
|
### Run it from the folder where the project is to be created
|
||||||
|
|
||||||
|
The new project is created under the folder you start the script in: the
|
||||||
|
default directory is `./<repository name>`. Go to the folder that should hold
|
||||||
|
the project, then start the script from there, by its path or by a global
|
||||||
|
command (below):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/work # the folder that will hold my-app
|
||||||
|
~/src/RepoFoundry/src/create-project.sh # creates ~/work/my-app
|
||||||
|
```
|
||||||
|
|
||||||
|
### Make it a global command
|
||||||
|
|
||||||
|
Put a link to the script in a folder that is on your `PATH`. The script
|
||||||
|
follows the link to the checkout, so it still finds its own files there.
|
||||||
|
|
||||||
|
Linux, macOS and Git Bash on Windows (run this once, from the checkout):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir -p ~/bin
|
||||||
|
ln -s "$PWD/src/create-project.sh" ~/bin/repo-foundry
|
||||||
|
```
|
||||||
|
|
||||||
|
If `~/bin` is not on your `PATH` yet, add it and open a new shell:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
echo 'export PATH="$HOME/bin:$PATH"' >> ~/.bashrc # ~/.zshrc on macOS
|
||||||
|
```
|
||||||
|
|
||||||
|
Check that it works, from any folder:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd ~/work
|
||||||
|
repo-foundry --version # prints the name and version
|
||||||
|
repo-foundry # a dry run that creates nothing
|
||||||
|
```
|
||||||
|
|
||||||
|
On Windows, Git Bash makes a *copy* instead of a link unless symbolic links
|
||||||
|
are allowed (Developer Mode, or an administrator shell). Either allow them and
|
||||||
|
run `export MSYS=winsymlinks:nativestrict` before the `ln -s`, or use an alias
|
||||||
|
in `~/.bashrc`, which works because the script finds its own folder:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
alias repo-foundry='bash /c/Users/me/RepoFoundry/src/create-project.sh'
|
||||||
|
```
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
The script reads two plain files from the project root. They are **parsed,
|
The script reads two plain files. They are **parsed,
|
||||||
never executed** (`source` is not used): only `KEY=VALUE` lines with known keys
|
never executed** (`source` is not used): only `KEY=VALUE` lines with known keys
|
||||||
are accepted, and anything else stops the run with a message that names the key
|
are accepted, and anything else stops the run with a message that names the key
|
||||||
and the line, never the value.
|
and the line, never the value.
|
||||||
|
|
||||||
|
Each file is chosen on its own, in this order:
|
||||||
|
|
||||||
|
1. the file named with `--config` or `--env`;
|
||||||
|
2. `./config.env` or `./.env` in the folder you start the script in;
|
||||||
|
3. `config.env` or `.env` in the checkout (next to `src/`).
|
||||||
|
|
||||||
|
The script names the files it uses before it contacts any host. A file taken
|
||||||
|
from the folder you started in is also confirmed: the script shows the file
|
||||||
|
names and the Gitea address and asks for a yes (default no) before the first
|
||||||
|
request, because a `config.env` in a folder you do not control could point
|
||||||
|
Gitea at another host and so send your token there. A file you name with
|
||||||
|
`--config` or `--env`, or the checkout's own, is not asked about.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cp config.env.example config.env # service addresses, not secret: set GITEA_URL (and GITEA_API_URL)
|
cp config.env.example config.env # service addresses, not secret: set GITEA_URL (and GITEA_API_URL)
|
||||||
cp .env.example .env # credentials: keep private
|
cp .env.example .env # credentials: keep private
|
||||||
@@ -151,6 +211,11 @@ src/create-project.sh --apply # creates everything after a final yes
|
|||||||
src/create-project.sh --config /path/to/config.env --env /path/to/.env
|
src/create-project.sh --config /path/to/config.env --env /path/to/.env
|
||||||
```
|
```
|
||||||
|
|
||||||
|
With a global command (see [Installation](#installation)) the same commands are
|
||||||
|
`repo-foundry`, `repo-foundry --apply` and so on, started from the folder that
|
||||||
|
should hold the project. Without `--config` and `--env` the files are looked
|
||||||
|
for as described under [Configuration](#configuration).
|
||||||
|
|
||||||
The script asks for, in this order: repository name, description, visibility,
|
The script asks for, in this order: repository name, description, visibility,
|
||||||
Gitea owner, whether to also create a GitHub repository (and its owner), the
|
Gitea owner, whether to also create a GitHub repository (and its owner), the
|
||||||
local directory and whether to enable the plan gate (a detail set in
|
local directory and whether to enable the plan gate (a detail set in
|
||||||
|
|||||||
@@ -9,8 +9,8 @@
|
|||||||
## Version History
|
## Version History
|
||||||
| Date | Status | Author | Reviewer | Change | Commit |
|
| Date | Status | Author | Reviewer | Change | Commit |
|
||||||
| --- | --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- | --- |
|
||||||
| 2026-10-07 | Proposed | Jens Tirsvad Nielsen | S02 | Task 4 renamed so its issue title is unique (the sync matches issues by title) | [be759e3] |
|
| 2026-10-07 | Deprecated | Jens Tirsvad Nielsen | S02 | Task 4 renamed so its issue title is unique (the sync matches issues by title) | [be759e3] |
|
||||||
| 2026-10-07 | Proposed | Jens Tirsvad Nielsen | S02 | The configuration files default to the working folder's, then the checkout's, confirmed and named (deliverable 3, criteria 9 and 10, tasks 2 to 4) | [0ab5006] |
|
| 2026-10-07 | Accepted | Jens Tirsvad Nielsen | S02 | The configuration files default to the working folder's, then the checkout's, confirmed and named (deliverable 3, criteria 9 and 10, tasks 2 to 4) | [0ab5006] |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+31
-9
@@ -86,18 +86,39 @@ if ((BASH_VERSINFO[0] < 4 || (BASH_VERSINFO[0] == 4 && BASH_VERSINFO[1] < 4)));
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
# Where this script lives; the library files and the project root are found
|
# Where this script lives; the library files and the project root are found
|
||||||
# from here, never from the current directory.
|
# from here, never from the current directory. A link to the script (a
|
||||||
readonly SCRIPT_FILE="${BASH_SOURCE[0]}"
|
# command in a folder on PATH) is followed to the real file, however many
|
||||||
case "${BASH_SOURCE[0]}" in
|
# links lie on the way; readlink without -f exists on Linux, macOS and Git
|
||||||
*/*) script_path_dir="${BASH_SOURCE[0]%/*}" ;;
|
# 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="." ;;
|
*) script_path_dir="." ;;
|
||||||
esac
|
esac
|
||||||
SCRIPT_DIR="$(cd "$script_path_dir" && pwd)"
|
SCRIPT_DIR="$(cd -P "$script_path_dir" && pwd -P)"
|
||||||
readonly SCRIPT_DIR
|
readonly SCRIPT_DIR
|
||||||
unset script_path_dir
|
unset script_path script_path_dir script_link_count script_link_target
|
||||||
# The script lives in src/; the configuration files live one level up, in
|
# The script lives in src/; the checkout is one level up, next to
|
||||||
# the project root, next to config.env.example and .env.example.
|
# config.env.example and .env.example.
|
||||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
PROJECT_ROOT="$(cd -P "$SCRIPT_DIR/.." && pwd -P)"
|
||||||
readonly PROJECT_ROOT
|
readonly PROJECT_ROOT
|
||||||
|
|
||||||
# shellcheck source=lib/constants.sh
|
# shellcheck source=lib/constants.sh
|
||||||
@@ -163,6 +184,7 @@ main() {
|
|||||||
trap finish EXIT
|
trap finish EXIT
|
||||||
is_valid_repo_name "$PROJECT_NAME" ||
|
is_valid_repo_name "$PROJECT_NAME" ||
|
||||||
die "REPOFOUNDRY_NAME is not a valid project name"
|
die "REPOFOUNDRY_NAME is not a valid project name"
|
||||||
|
WORKING_FOLDER="$(pwd -P)"
|
||||||
parse_args "$@"
|
parse_args "$@"
|
||||||
check_tools
|
check_tools
|
||||||
setup_temp_dir
|
setup_temp_dir
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
usage() {
|
usage() {
|
||||||
cat <<EOF
|
cat <<EOF
|
||||||
Usage: ${0##*/} [--apply] [--config FILE] [--env FILE]
|
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
|
${0##*/} --help | --version
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -198,9 +198,56 @@ warn_if_env_unsafe() {
|
|||||||
fi
|
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".
|
||||||
|
locate_config_file() {
|
||||||
|
local name="$1" flag="$2"
|
||||||
|
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"
|
||||||
|
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' ;;
|
||||||
|
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() {
|
load_configuration() {
|
||||||
|
locate_config_file config.env --config CONFIG_FILE CONFIG_ORIGIN
|
||||||
|
locate_config_file .env --env ENV_FILE ENV_ORIGIN
|
||||||
|
say "Config file : $CONFIG_FILE ($(origin_words "$CONFIG_ORIGIN"))"
|
||||||
|
say "Credentials file: $ENV_FILE ($(origin_words "$ENV_ORIGIN"))"
|
||||||
parse_env_file "$CONFIG_FILE" CONFIG_KEYS CONFIG
|
parse_env_file "$CONFIG_FILE" CONFIG_KEYS CONFIG
|
||||||
validate_config
|
validate_config
|
||||||
|
confirm_folder_files
|
||||||
parse_env_file "$ENV_FILE" CREDENTIAL_KEYS CREDENTIALS
|
parse_env_file "$ENV_FILE" CREDENTIAL_KEYS CREDENTIALS
|
||||||
validate_credentials
|
validate_credentials
|
||||||
warn_if_env_unsafe "$ENV_FILE"
|
warn_if_env_unsafe "$ENV_FILE"
|
||||||
|
|||||||
@@ -37,8 +37,14 @@ readonly CONFIG_KEYS=(GITHUB_API_URL GITHUB_WEB_URL GITEA_URL GITEA_API_URL
|
|||||||
GITHUB_OWNER PROJECT_DIRECTORY ENABLE_PLAN_GATE PROJECT_LICENSE)
|
GITHUB_OWNER PROJECT_DIRECTORY ENABLE_PLAN_GATE PROJECT_LICENSE)
|
||||||
readonly CREDENTIAL_KEYS=(GITHUB_PAT GITHUB_USER GITEA_TOKEN)
|
readonly CREDENTIAL_KEYS=(GITHUB_PAT GITHUB_USER GITEA_TOKEN)
|
||||||
|
|
||||||
CONFIG_FILE="$PROJECT_ROOT/config.env"
|
# The configuration files: named by --config and --env, or chosen by
|
||||||
ENV_FILE="$PROJECT_ROOT/.env"
|
# 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=""
|
TMP_DIR=""
|
||||||
HAS_JQ=0
|
HAS_JQ=0
|
||||||
HTTP_STATUS=0
|
HTTP_STATUS=0
|
||||||
|
|||||||
+15
-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, 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
|
# 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).
|
||||||
@@ -12,6 +12,18 @@ is_framework_skipped() {
|
|||||||
[[ ${STATE[skip_framework]:-0} == 1 ]]
|
[[ ${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
|
# add_framework: git submodule add of the framework as "framework". SSH is
|
||||||
# needed for it; a failure says how to test the access.
|
# needed for it; a failure says how to test the access.
|
||||||
add_framework() {
|
add_framework() {
|
||||||
@@ -27,6 +39,7 @@ add_framework() {
|
|||||||
if [[ $existing != "$url" ]]; then
|
if [[ $existing != "$url" ]]; then
|
||||||
die "'framework' already exists in $dir and is not the framework submodule ($url)"
|
die "'framework' already exists in $dir and is not the framework submodule ($url)"
|
||||||
fi
|
fi
|
||||||
|
init_framework_submodules "$dir"
|
||||||
finish_step "$label" "reused" "$url (already a submodule)"
|
finish_step "$label" "reused" "$url (already a submodule)"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
@@ -35,6 +48,7 @@ add_framework() {
|
|||||||
if ! git_project "$dir" submodule add -q "$url" framework 2>"$err"; then
|
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' ' ')"
|
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
|
fi
|
||||||
|
init_framework_submodules "$dir"
|
||||||
finish_step "$label" "created" "$url"
|
finish_step "$label" "created" "$url"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+6
-2
@@ -120,12 +120,15 @@ write_gitconfig() {
|
|||||||
insteadOf = https://git.example.test/
|
insteadOf = https://git.example.test/
|
||||||
[url "file://$WORK/remote/"]
|
[url "file://$WORK/remote/"]
|
||||||
insteadOf = ssh://git@git.example.test:10022/
|
insteadOf = ssh://git@git.example.test:10022/
|
||||||
|
[url "file://$WORK/remote/"]
|
||||||
|
insteadOf = ssh://git@git.tirsystem.com:10022/
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
# ensure_shared_remotes: build, once per run of the suite, the local bare
|
# ensure_shared_remotes: build, once per run of the suite, the local bare
|
||||||
# repositories that stand in for Gitea (TirSystem/my-app.git, holding the
|
# repositories that stand in for Gitea (TirSystem/my-app.git, holding the
|
||||||
# license commit) and for the framework (a copy of the real one).
|
# license commit), for the framework and for the checklists the framework
|
||||||
|
# holds as its own submodule (copies of the real ones).
|
||||||
ensure_shared_remotes() {
|
ensure_shared_remotes() {
|
||||||
if [[ -n $SHARED_REMOTES && -d $SHARED_REMOTES ]]; then
|
if [[ -n $SHARED_REMOTES && -d $SHARED_REMOTES ]]; then
|
||||||
return 0
|
return 0
|
||||||
@@ -133,6 +136,7 @@ ensure_shared_remotes() {
|
|||||||
SHARED_REMOTES="$(mktemp -d "${TMPDIR:-/tmp}/repofoundry-remotes.XXXXXX")"
|
SHARED_REMOTES="$(mktemp -d "${TMPDIR:-/tmp}/repofoundry-remotes.XXXXXX")"
|
||||||
mkdir -p "$SHARED_REMOTES/TirSystem"
|
mkdir -p "$SHARED_REMOTES/TirSystem"
|
||||||
git clone -q --bare "$REPO_ROOT/framework" "$SHARED_REMOTES/TirSystem/SQA-QC-Framework.git"
|
git clone -q --bare "$REPO_ROOT/framework" "$SHARED_REMOTES/TirSystem/SQA-QC-Framework.git"
|
||||||
|
git clone -q --bare "$REPO_ROOT/framework/qc" "$SHARED_REMOTES/TirSystem/SQA-QC-Checklists.git"
|
||||||
git init -q --bare "$SHARED_REMOTES/TirSystem/my-app.git"
|
git init -q --bare "$SHARED_REMOTES/TirSystem/my-app.git"
|
||||||
git init -q "$SHARED_REMOTES/seed"
|
git init -q "$SHARED_REMOTES/seed"
|
||||||
git -C "$SHARED_REMOTES/seed" symbolic-ref HEAD refs/heads/main
|
git -C "$SHARED_REMOTES/seed" symbolic-ref HEAD refs/heads/main
|
||||||
@@ -163,7 +167,7 @@ setup_local_remotes() {
|
|||||||
# first, then the now empty directories from the bottom up.
|
# first, then the now empty directories from the bottom up.
|
||||||
remove_workdir() {
|
remove_workdir() {
|
||||||
if [[ -n $WORK && -d $WORK ]]; then
|
if [[ -n $WORK && -d $WORK ]]; then
|
||||||
find "$WORK" \( -type f -o -type p \) -delete
|
find "$WORK" \( -type f -o -type p -o -type l \) -delete
|
||||||
find "$WORK" -depth -type d -exec rmdir {} +
|
find "$WORK" -depth -type d -exec rmdir {} +
|
||||||
fi
|
fi
|
||||||
WORK=""
|
WORK=""
|
||||||
|
|||||||
@@ -0,0 +1,219 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# test-launch.sh - tests for MIL-007: the framework's own submodules (qc) are
|
||||||
|
# fetched, the script starts through a link, and the configuration files are
|
||||||
|
# --config and --env, else ./config.env and ./.env, else the checkout's.
|
||||||
|
# Sourced by run-tests.sh.
|
||||||
|
|
||||||
|
# shellcheck disable=SC2016,SC2153 # snippet and fixture text is literal on purpose; SCRIPT comes from lib.sh
|
||||||
|
|
||||||
|
# make_checkout: a copy of the script's own files with config.env and .env
|
||||||
|
# beside them, standing in for the checkout; the path is in CHECKOUT.
|
||||||
|
make_checkout() {
|
||||||
|
CHECKOUT="$(cd "$WORK" && pwd -P)/checkout"
|
||||||
|
mkdir -p "$CHECKOUT"
|
||||||
|
cp -R "$SRC_DIR" "$CHECKOUT/src"
|
||||||
|
cp "$WORK/config.env" "$CHECKOUT/config.env"
|
||||||
|
printf 'GITEA_TOKEN=%s\n' "$FAKE_GITEA_TOKEN" >"$CHECKOUT/.env"
|
||||||
|
}
|
||||||
|
|
||||||
|
# make_folder: an empty working folder; the path is in FOLDER.
|
||||||
|
make_folder() {
|
||||||
|
FOLDER="$(cd "$WORK" && pwd -P)/folder"
|
||||||
|
mkdir -p "$FOLDER"
|
||||||
|
}
|
||||||
|
|
||||||
|
# run_from SCRIPT_PATH DIR INPUT ARGS...: run the script with DIR as the
|
||||||
|
# current folder.
|
||||||
|
run_from() {
|
||||||
|
local script="$1" dir="$2" input="$3"
|
||||||
|
shift 3
|
||||||
|
STATUS=0
|
||||||
|
(cd "$dir" && PATH="$WORK/bin:$PATH" STUB_DIR="$WORK" TMPDIR="$WORK/tmp" \
|
||||||
|
REPOFOUNDRY_SYNC_WAIT=0 GIT_CONFIG_GLOBAL="$WORK/gitconfig" GIT_CONFIG_NOSYSTEM=1 \
|
||||||
|
"$BASH" "$script" "$@" <<<"$input" >"$WORK/out.txt" 2>"$WORK/err.txt") ||
|
||||||
|
STATUS=$?
|
||||||
|
OUT="$(cat "$WORK/out.txt")"
|
||||||
|
ERR="$(cat "$WORK/err.txt")"
|
||||||
|
}
|
||||||
|
|
||||||
|
# --------------------------------------------------- the framework's qc
|
||||||
|
|
||||||
|
test_the_framework_checklists_are_fetched() {
|
||||||
|
setup_hosts
|
||||||
|
printf 'GITEA_TOKEN=%s\n' "$FAKE_GITEA_TOKEN" >"$WORK/.env"
|
||||||
|
local dir="$WORK/project" answers
|
||||||
|
printf -v answers 'my-app\n\n\nTirSystem\nn\n%s\nn\n' "$dir"
|
||||||
|
run_apply "$answers"$'y\n'
|
||||||
|
assert_status "apply" 0 "$STATUS"
|
||||||
|
assert_file_exists "the framework" "$dir/framework/README.md"
|
||||||
|
assert_file_exists "qc is filled" "$dir/framework/qc/qc-business-case.md"
|
||||||
|
assert_eq "no submodule is left uninitialised" "" "$(GIT_CONFIG_GLOBAL="$WORK/gitconfig" git -C "$dir" submodule status --recursive | grep '^-' || true)"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_an_empty_qc_is_filled_by_a_second_run_and_a_complete_one_is_not_changed() {
|
||||||
|
setup_hosts
|
||||||
|
printf 'GITEA_TOKEN=%s\n' "$FAKE_GITEA_TOKEN" >"$WORK/.env"
|
||||||
|
local dir="$WORK/project" answers
|
||||||
|
printf -v answers 'my-app\n\n\nTirSystem\nn\n%s\nn\n' "$dir"
|
||||||
|
run_apply "$answers"$'y\n'
|
||||||
|
assert_status "first run" 0 "$STATUS"
|
||||||
|
git_in() { GIT_CONFIG_GLOBAL="$WORK/gitconfig" GIT_CONFIG_NOSYSTEM=1 git -C "$dir/framework" "$@"; }
|
||||||
|
git_in submodule deinit -f qc >/dev/null 2>&1
|
||||||
|
assert_file_missing "qc emptied" "$dir/framework/qc/qc-business-case.md"
|
||||||
|
run_lib "" "setup_temp_dir; init_framework_submodules '$dir'"
|
||||||
|
assert_status "repair" 0 "$STATUS"
|
||||||
|
assert_file_exists "qc filled again" "$dir/framework/qc/qc-business-case.md"
|
||||||
|
local before after
|
||||||
|
before="$(git_in status --porcelain)"
|
||||||
|
run_lib "" "setup_temp_dir; init_framework_submodules '$dir'"
|
||||||
|
after="$(git_in status --porcelain)"
|
||||||
|
assert_status "second call" 0 "$STATUS"
|
||||||
|
assert_eq "nothing else changed" "$before" "$after"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_a_failed_qc_fetch_names_the_command_to_run_by_hand() {
|
||||||
|
setup_hosts
|
||||||
|
printf 'GITEA_TOKEN=%s\n' "$FAKE_GITEA_TOKEN" >"$WORK/.env"
|
||||||
|
local dir="$WORK/project" answers
|
||||||
|
printf -v answers 'my-app\n\n\nTirSystem\nn\n%s\nn\n' "$dir"
|
||||||
|
run_apply "$answers"$'y\n'
|
||||||
|
GIT_CONFIG_GLOBAL="$WORK/gitconfig" GIT_CONFIG_NOSYSTEM=1 git -C "$dir/framework" submodule deinit -f qc >/dev/null 2>&1
|
||||||
|
# Drop the cached copy of the checklists and refuse local fetches, so they
|
||||||
|
# cannot be fetched again.
|
||||||
|
local cache="$dir/.git/modules/framework/modules/qc"
|
||||||
|
if [[ -d $cache ]]; then
|
||||||
|
find "$cache" \( -type f -o -type l \) -delete
|
||||||
|
find "$cache" -depth -type d -exec rmdir {} +
|
||||||
|
fi
|
||||||
|
printf '[protocol "file"]\n\tallow = never\n' >>"$WORK/gitconfig"
|
||||||
|
run_lib "" "setup_temp_dir; init_framework_submodules '$dir'"
|
||||||
|
assert_status "fetch fails" 1 "$STATUS"
|
||||||
|
assert_contains "the command" "$ERR" "git submodule update --init --recursive"
|
||||||
|
assert_not_contains "no token" "$ERR" "$FAKE_GITEA_TOKEN"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_a_framework_without_a_submodule_of_its_own_is_not_a_failure() {
|
||||||
|
setup_hosts
|
||||||
|
local dir="$WORK/plain"
|
||||||
|
mkdir -p "$dir"
|
||||||
|
git init -q "$dir"
|
||||||
|
run_lib "" "setup_temp_dir; init_framework_submodules '$dir'"
|
||||||
|
assert_status "nothing to fetch" 0 "$STATUS"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ------------------------------------------------ the configuration files
|
||||||
|
|
||||||
|
test_files_in_the_working_folder_are_used_after_a_yes() {
|
||||||
|
setup_hosts
|
||||||
|
make_checkout
|
||||||
|
make_folder
|
||||||
|
cp "$WORK/config.env" "$FOLDER/config.env"
|
||||||
|
printf 'GITEA_TOKEN=%s\n' "$FAKE_GITEA_TOKEN" >"$FOLDER/.env"
|
||||||
|
run_from "$SCRIPT" "$FOLDER" $'y\n'"$ANSWERS_GITEA_ONLY"
|
||||||
|
assert_status "folder files" 0 "$STATUS"
|
||||||
|
assert_contains "config named" "$OUT" "Config file : $FOLDER/config.env (from the working folder)"
|
||||||
|
assert_contains "credentials named" "$OUT" "Credentials file: $FOLDER/.env (from the working folder)"
|
||||||
|
assert_contains "the address is named" "$OUT" "Gitea would be https://git.example.test"
|
||||||
|
assert_contains "asked" "$ERR" "Use $FOLDER/config.env $FOLDER/.env (y/n) [n]"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_files_in_the_working_folder_are_not_used_without_a_yes() {
|
||||||
|
setup_hosts
|
||||||
|
make_checkout
|
||||||
|
make_folder
|
||||||
|
cp "$WORK/config.env" "$FOLDER/config.env"
|
||||||
|
printf 'GITEA_TOKEN=%s\n' "$FAKE_GITEA_TOKEN" >"$FOLDER/.env"
|
||||||
|
rm -f "$WORK/curl.calls"
|
||||||
|
run_from "$SCRIPT" "$FOLDER" $'\n'"$ANSWERS_GITEA_ONLY"
|
||||||
|
assert_status "default no" 1 "$STATUS"
|
||||||
|
assert_contains "stopped" "$ERR" "stopped before any request"
|
||||||
|
assert_eq "no request to any host" "" "$(calls)"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_named_files_win_and_are_not_confirmed() {
|
||||||
|
setup_hosts
|
||||||
|
make_checkout
|
||||||
|
make_folder
|
||||||
|
cp "$WORK/config.env" "$FOLDER/config.env"
|
||||||
|
printf 'GITEA_TOKEN=%s\n' "$FAKE_GITEA_TOKEN" >"$FOLDER/.env"
|
||||||
|
printf 'GITEA_TOKEN=%s\n' "$FAKE_GITEA_TOKEN" >"$WORK/.env"
|
||||||
|
run_from "$SCRIPT" "$FOLDER" "$ANSWERS_GITEA_ONLY" --config "$WORK/config.env" --env "$WORK/.env"
|
||||||
|
assert_status "named files" 0 "$STATUS"
|
||||||
|
assert_contains "named" "$OUT" "Config file : $WORK/config.env (named on the command line)"
|
||||||
|
assert_not_contains "no confirmation" "$OUT" "The working folder supplies"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_the_checkouts_files_are_used_when_the_folder_has_none() {
|
||||||
|
setup_hosts
|
||||||
|
make_checkout
|
||||||
|
make_folder
|
||||||
|
run_from "$CHECKOUT/src/create-project.sh" "$FOLDER" "$ANSWERS_GITEA_ONLY"
|
||||||
|
assert_status "checkout files" 0 "$STATUS"
|
||||||
|
assert_contains "config named" "$OUT" "Config file : $CHECKOUT/config.env (from the checkout)"
|
||||||
|
assert_contains "credentials named" "$OUT" "Credentials file: $CHECKOUT/.env (from the checkout)"
|
||||||
|
assert_not_contains "no confirmation" "$OUT" "The working folder supplies"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_each_file_is_chosen_on_its_own() {
|
||||||
|
setup_hosts
|
||||||
|
make_checkout
|
||||||
|
make_folder
|
||||||
|
printf 'GITEA_TOKEN=%s\n' "$FAKE_GITEA_TOKEN" >"$FOLDER/.env"
|
||||||
|
run_from "$CHECKOUT/src/create-project.sh" "$FOLDER" $'y\n'"$ANSWERS_GITEA_ONLY"
|
||||||
|
assert_status "mixed" 0 "$STATUS"
|
||||||
|
assert_contains "config from the checkout" "$OUT" "Config file : $CHECKOUT/config.env (from the checkout)"
|
||||||
|
assert_contains "credentials from the folder" "$OUT" "Credentials file: $FOLDER/.env (from the working folder)"
|
||||||
|
assert_contains "asked about the folder file only" "$ERR" "Use $FOLDER/.env (y/n) [n]"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_files_found_nowhere_stop_before_any_request_and_name_both_places() {
|
||||||
|
setup_hosts
|
||||||
|
make_checkout
|
||||||
|
make_folder
|
||||||
|
rm -f "$CHECKOUT/.env" "$WORK/curl.calls"
|
||||||
|
run_from "$CHECKOUT/src/create-project.sh" "$FOLDER" "$ANSWERS_GITEA_ONLY"
|
||||||
|
assert_status "no .env anywhere" 1 "$STATUS"
|
||||||
|
assert_contains "working folder named" "$ERR" "working folder ($FOLDER)"
|
||||||
|
assert_contains "checkout named" "$ERR" "checkout ($CHECKOUT)"
|
||||||
|
assert_contains "the option" "$ERR" "--env FILE"
|
||||||
|
assert_eq "no request to any host" "" "$(calls)"
|
||||||
|
}
|
||||||
|
|
||||||
|
# ----------------------------------------------------------- a link to it
|
||||||
|
|
||||||
|
test_the_script_runs_through_a_link_and_creates_the_project_in_the_current_folder() {
|
||||||
|
setup_hosts
|
||||||
|
make_checkout
|
||||||
|
make_folder
|
||||||
|
local bin="$WORK/linkbin"
|
||||||
|
mkdir -p "$bin"
|
||||||
|
MSYS=winsymlinks:nativestrict ln -s "$CHECKOUT/src/create-project.sh" "$bin/repo-foundry" 2>/dev/null || true
|
||||||
|
if [[ ! -L $bin/repo-foundry ]]; then
|
||||||
|
printf 'skipped: this shell cannot make symbolic links\n'
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
run_from "$bin/repo-foundry" "$FOLDER" "" --version
|
||||||
|
assert_status "version through the link" 0 "$STATUS"
|
||||||
|
assert_contains "found its files" "$OUT" "RepoFoundry"
|
||||||
|
run_from "$bin/repo-foundry" "$FOLDER" "$ANSWERS_GITEA_ONLY"$'y\n' --apply
|
||||||
|
assert_status "apply through the link" 0 "$STATUS"
|
||||||
|
assert_contains "the checkout's files" "$OUT" "/checkout/config.env (from the checkout)"
|
||||||
|
assert_file_exists "the project is in the current folder" "$FOLDER/my-app/.git"
|
||||||
|
assert_file_missing "not in the checkout" "$CHECKOUT/my-app"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_a_link_to_a_link_is_followed() {
|
||||||
|
setup_hosts
|
||||||
|
make_checkout
|
||||||
|
make_folder
|
||||||
|
local bin="$WORK/linkbin"
|
||||||
|
mkdir -p "$bin"
|
||||||
|
MSYS=winsymlinks:nativestrict ln -s "$CHECKOUT/src/create-project.sh" "$bin/first" 2>/dev/null || true
|
||||||
|
[[ -L $bin/first ]] || {
|
||||||
|
printf 'skipped: this shell cannot make symbolic links\n'
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
(cd "$bin" && MSYS=winsymlinks:nativestrict ln -s first second)
|
||||||
|
run_from "$bin/second" "$FOLDER" "" --version
|
||||||
|
assert_status "second link" 0 "$STATUS"
|
||||||
|
}
|
||||||
@@ -201,13 +201,15 @@ test_default_files_are_in_the_project_root() {
|
|||||||
cp "$WORK/config.env" "$WORK/project/config.env"
|
cp "$WORK/config.env" "$WORK/project/config.env"
|
||||||
cp "$WORK/.env" "$WORK/project/.env"
|
cp "$WORK/.env" "$WORK/project/.env"
|
||||||
STATUS=0
|
STATUS=0
|
||||||
PATH="$WORK/bin:$PATH" STUB_DIR="$WORK" TMPDIR="$WORK/tmp" "$BASH" "$WORK/project/src/create-project.sh" \
|
(cd "$WORK/project" && PATH="$WORK/bin:$PATH" STUB_DIR="$WORK" TMPDIR="$WORK/tmp" "$BASH" "$WORK/project/src/create-project.sh" \
|
||||||
<<<"$ANSWERS_GITHUB" >"$WORK/out.txt" 2>"$WORK/err.txt" || STATUS=$?
|
<<<"$ANSWERS_GITHUB" >"$WORK/out.txt" 2>"$WORK/err.txt") || STATUS=$?
|
||||||
assert_status "run with the default files" 0 "$STATUS"
|
assert_status "run with the default files" 0 "$STATUS"
|
||||||
assert_contains "found config.env in the project root" "$(cat "$WORK/out.txt")" "https://git.example.test/TirSystem/my-app"
|
assert_contains "found config.env in the project root" "$(cat "$WORK/out.txt")" "https://git.example.test/TirSystem/my-app"
|
||||||
# Run from another directory: the defaults follow the script, not the cwd.
|
# Run from another folder that holds no files of its own: the checkout's
|
||||||
|
# files are used; the defaults follow the script, not the current folder.
|
||||||
|
mkdir -p "$WORK/elsewhere"
|
||||||
STATUS=0
|
STATUS=0
|
||||||
(cd "$WORK" && PATH="$WORK/bin:$PATH" STUB_DIR="$WORK" TMPDIR="$WORK/tmp" "$BASH" "$WORK/project/src/create-project.sh" \
|
(cd "$WORK/elsewhere" && PATH="$WORK/bin:$PATH" STUB_DIR="$WORK" TMPDIR="$WORK/tmp" "$BASH" "$WORK/project/src/create-project.sh" \
|
||||||
<<<"$ANSWERS_GITEA_ONLY" >"$WORK/out.txt" 2>"$WORK/err.txt") || STATUS=$?
|
<<<"$ANSWERS_GITEA_ONLY" >"$WORK/out.txt" 2>"$WORK/err.txt") || STATUS=$?
|
||||||
assert_status "run from another directory" 0 "$STATUS"
|
assert_status "run from another directory" 0 "$STATUS"
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user