Implement MIL-006 and MIL-007: project license, qc submodules, link start, default configuration files #54
@@ -59,13 +59,73 @@ src/create-project.sh --help
|
||||
Nothing has to be installed system-wide: the script runs from the checkout and
|
||||
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
|
||||
|
||||
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
|
||||
are accepted, and anything else stops the run with a message that names the key
|
||||
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
|
||||
cp config.env.example config.env # service addresses, not secret: set GITEA_URL (and GITEA_API_URL)
|
||||
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
|
||||
```
|
||||
|
||||
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,
|
||||
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
|
||||
|
||||
+1
-1
@@ -167,7 +167,7 @@ setup_local_remotes() {
|
||||
# first, then the now empty directories from the bottom up.
|
||||
remove_workdir() {
|
||||
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 {} +
|
||||
fi
|
||||
WORK=""
|
||||
|
||||
+17
-16
@@ -4,7 +4,7 @@
|
||||
# --config and --env, else ./config.env and ./.env, else the checkout's.
|
||||
# Sourced by run-tests.sh.
|
||||
|
||||
# shellcheck disable=SC2016 # snippet and fixture text is literal on purpose
|
||||
# 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.
|
||||
@@ -53,19 +53,19 @@ test_the_framework_checklists_are_fetched() {
|
||||
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 git_in
|
||||
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 "" "init_framework_submodules '$dir'"
|
||||
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 "" "init_framework_submodules '$dir'"
|
||||
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"
|
||||
@@ -78,14 +78,15 @@ test_a_failed_qc_fetch_names_the_command_to_run_by_hand() {
|
||||
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
|
||||
printf '[url "file://%s/nowhere/"]\n\tinsteadOf = ssh://git@git.tirsystem.com:10022/\n' "$WORK" >>"$WORK/gitconfig"
|
||||
GIT_CONFIG_GLOBAL="$WORK/gitconfig" GIT_CONFIG_NOSYSTEM=1 git -C "$dir/framework" config --unset-all submodule.qc.url >/dev/null 2>&1 || true
|
||||
run_lib "" "init_framework_submodules '$dir'"
|
||||
if ((STATUS == 0)); then
|
||||
# The earlier alias still wins in this git; remove the working alias instead.
|
||||
sed -i '/remote\/"\]/,+1d' "$WORK/gitconfig"
|
||||
run_lib "" "init_framework_submodules '$dir'"
|
||||
# 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"
|
||||
@@ -96,7 +97,7 @@ test_a_framework_without_a_submodule_of_its_own_is_not_a_failure() {
|
||||
local dir="$WORK/plain"
|
||||
mkdir -p "$dir"
|
||||
git init -q "$dir"
|
||||
run_lib "" "init_framework_submodules '$dir'"
|
||||
run_lib "" "setup_temp_dir; init_framework_submodules '$dir'"
|
||||
assert_status "nothing to fetch" 0 "$STATUS"
|
||||
}
|
||||
|
||||
@@ -186,7 +187,7 @@ test_the_script_runs_through_a_link_and_creates_the_project_in_the_current_folde
|
||||
make_folder
|
||||
local bin="$WORK/linkbin"
|
||||
mkdir -p "$bin"
|
||||
ln -s "$CHECKOUT/src/create-project.sh" "$bin/repo-foundry" 2>/dev/null || true
|
||||
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
|
||||
@@ -196,7 +197,7 @@ test_the_script_runs_through_a_link_and_creates_the_project_in_the_current_folde
|
||||
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" "Config file : $CHECKOUT/config.env (from the checkout)"
|
||||
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"
|
||||
}
|
||||
@@ -207,12 +208,12 @@ test_a_link_to_a_link_is_followed() {
|
||||
make_folder
|
||||
local bin="$WORK/linkbin"
|
||||
mkdir -p "$bin"
|
||||
ln -s "$CHECKOUT/src/create-project.sh" "$bin/first" 2>/dev/null || true
|
||||
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" && ln -s first second)
|
||||
(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/.env" "$WORK/project/.env"
|
||||
STATUS=0
|
||||
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=$?
|
||||
(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=$?
|
||||
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"
|
||||
# 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
|
||||
(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=$?
|
||||
assert_status "run from another directory" 0 "$STATUS"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user