feat!: installation only from a release, into an empty folder; upstream merge/verify and private-instance.md removed, dist adopt, shell-neutral instructions (#153)
CI / verify (push) Successful in 5m19s
CI / pwsh (push) Successful in 1m55s
Release / release (push) Successful in 36s

Files changed:
- .gitea/workflows/ci.yml
- .gitea/workflows/release.yml
- AGENTS.md
- CHANGES.md
- DEVELOPMENT.md
- EVALS.md
- INSTALL.md
- README.md
- VERSION
- docs/ownership-and-templates.md
- instructions/CONTRACT.md
- instructions/bootstrap.md
- instructions/dev/dev-setup.md
- instructions/dev/stack-dev/SKILL.md
- instructions/gates.md
- instructions/ingest-large-tree.md
- instructions/kb-profiles.md
- instructions/mcp-read-server.md
- instructions/migrations/3.0.0-authoring-conventions.md
- instructions/preflight.md
- instructions/private-instance.md
- instructions/session-setup.md
- instructions/setup-instance.md
- instructions/upgrade-instance.md
- tools/CONTRACT.md
- tools/README.md
- tools/chemenu/cli.py
- tools/chemenu/cli_contract.py
- tools/chemenu/commands/dist_cmd.py
- tools/chemenu/commands/docs_verify.py
- tools/chemenu/commands/doctor.py
- tools/chemenu/commands/git_publish.py
- tools/chemenu/commands/upstream_cmd.py
- tools/chemenu/commands/work_cmd.py
- tools/chemenu/config.py
- tools/chemenu/ownership.py
- tools/chemenu/tests/test_cli.py
- tools/chemenu/tests/test_dist_cmd.py
- tools/chemenu/tests/test_instructions_shell.py
- tools/chemenu/tests/test_preflight.py
- tools/chemenu/tests/test_preflight_pwsh.py
- tools/chemenu/tests/test_run_budget.py
- tools/chemenu/tests/test_upstream_cmd.py
- tools/chemenu/toc.py
- tools/preflight.ps1
- tools/preflight.sh
This commit is contained in:
torben committed 2026-10-01 22:12:09 +02:00
1 parent d0f08d1fba
commit a6d07f97c4
46 files changed
+1314 -1936

No files matched your search

+47 -15
View File
@@ -8,8 +8,10 @@
# As the release asset `preflight.sh` - a copy with no tools/prerequisites.txt next
# to it - the script is the first install step instead: it downloads the stack
# release named in RELEASE_ARCHIVE_URL / RELEASE_CHECKSUM_URL, checks the sha256,
# unpacks it into <script folder>/chemenu (or --into <path>), and runs the copy of
# this script inside the unpacked tree, which does everything above.
# unpacks it into the folder it lies in (or --into <path>), removes itself there, and
# runs the copy of this script inside the unpacked tree, which does everything above.
# That folder has to be empty apart from this script and a .git (an empty clone of the
# instance's own repository).
#
# preflight.sh [--into <path>] [--archive <tarball>] [--set <tool>=<path>]...
#
@@ -59,8 +61,8 @@ in .wikitool-tools.json and sets up tools/.venv. Exit 0 means ready; exit 42
means the user has to act - the output says how.
The second form is the release asset: it downloads the release (or takes
--archive), checks its sha256, unpacks it into <script folder>/chemenu or --into,
and runs the preflight inside it.
--archive), checks its sha256, unpacks it into its own folder or --into (empty,
or holding only .git), and runs the preflight inside it.
EOF
}
@@ -207,6 +209,23 @@ sha256_of() { # <file>
fi
}
# Whether the install folder holds anything besides this script and a .git - the
# two things an empty folder may already carry: the script was downloaded into it, and
# the user may have cloned the instance's own, still empty repository there.
target_is_occupied() {
[ -d "$TARGET" ] || return 1
self_name=${0##*/}
target_real=$(CDPATH='' cd -- "$TARGET" && pwd -P)
for entry in "$TARGET"/* "$TARGET"/.[!.]* "$TARGET"/..?*; do
[ -e "$entry" ] || [ -L "$entry" ] || continue
name=${entry##*/}
[ "$name" = .git ] && continue
[ "$target_real" = "$DIR" ] && [ "$name" = "$self_name" ] && continue
return 0
done
return 1
}
asset_mode() {
# GNU tar reads `C:` as a host name; Git Bash has to hand it `/c/...`.
if [ "$PLATFORM" = windows ] && command -v cygpath >/dev/null 2>&1; then
@@ -216,11 +235,14 @@ asset_mode() {
if [ -n "$INTO" ]; then
case "$INTO" in /*) TARGET=$INTO ;; *) TARGET="$(pwd)/$INTO" ;; esac
else
TARGET="$DIR/chemenu"
TARGET=$DIR
fi
TARGET=${TARGET%/}
if [ -e "$TARGET" ] || [ -L "$TARGET" ]; then
asset_fail "$(native_path "$TARGET") already exists - nothing was unpacked. Choose another folder with --into <path>, or move the existing one away."
if { [ -e "$TARGET" ] || [ -L "$TARGET" ]; } && [ ! -d "$TARGET" ]; then
asset_fail "$(native_path "$TARGET") exists and is not a folder - nothing was unpacked."
fi
if target_is_occupied; then
asset_fail "$(native_path "$TARGET") is not empty - nothing was unpacked. The wiki installs into an empty folder (an empty git clone is fine): put this script into one and run it there, or pass --into <path>."
fi
if [ -z "$ARCHIVE" ] && { [ -z "$RELEASE_ARCHIVE_URL" ] || [ -z "$RELEASE_CHECKSUM_URL" ]; }; then
@@ -287,21 +309,31 @@ asset_mode() {
if [ "$length" -gt "$limit" ]; then
problem "The folder this wiki would be installed in is too long ($length characters, at most $limit): $folder" \
"Windows on this computer only allows paths of up to 259 characters, and the wiki's own files need the rest" \
"Run this again with a shorter folder, for example: --into C:\\\\Chemenu
"Use a shorter folder such as C:\\Chemenu - put this script there and run it again,
or pass --into C:\\Chemenu.
Alternatively, someone with administrator rights can turn on long paths in Windows."
stop
fi
fi
parent=$(dirname -- "$TARGET")
mkdir -p "$parent" || asset_fail "could not create $parent."
UNPACK="$parent/.chemenu-unpack.$$"
mkdir "$UNPACK" || asset_fail "could not create a temporary folder next to the target."
tar -xzf "$archive" -C "$UNPACK" || asset_fail "unpacking failed - the target was not created."
[ -d "$UNPACK/$top" ] || asset_fail "unpacking produced no $top folder - the target was not created."
mv "$UNPACK/$top" "$TARGET" || asset_fail "could not move the unpacked stack to $(native_path "$TARGET")."
# Unpacked inside the target rather than beside it: the folder above may be a drive
# root nobody can write to. Only then are the entries moved up, one by one.
mkdir -p "$TARGET" || asset_fail "could not create $(native_path "$TARGET")."
UNPACK="$TARGET/.chemenu-unpack.$$"
mkdir "$UNPACK" || asset_fail "could not create a temporary folder in the target."
tar -xzf "$archive" -C "$UNPACK" || asset_fail "unpacking failed - nothing was installed."
[ -d "$UNPACK/$top" ] || asset_fail "unpacking produced no $top folder - nothing was installed."
for entry in "$UNPACK/$top"/* "$UNPACK/$top"/.[!.]* "$UNPACK/$top"/..?*; do
[ -e "$entry" ] || [ -L "$entry" ] || continue
mv "$entry" "$TARGET/" || asset_fail "could not move ${entry##*/} into $(native_path "$TARGET") - the folder holds part of the stack now; empty it (keep .git) and run this again."
done
rm -rf "$UNPACK"
UNPACK=""
# The release asset is not part of the stack; left in the folder, it would end up in
# the instance's first commit beside the tree's own tools/preflight.sh.
if [ "$(CDPATH='' cd -- "$TARGET" && pwd -P)" = "$DIR" ]; then
rm -f -- "$DIR/${0##*/}"
fi
[ -f "$TARGET/tools/preflight.sh" ] || asset_fail "the unpacked stack has no tools/preflight.sh."
echo "Unpacked into $(native_path "$TARGET")."