|
|
@@ -0,0 +1,312 @@
|
|
|
+#!/bin/bash
|
|
|
+#
|
|
|
+# execs.bump end-to-end verification — the manifest version-bump hook.
|
|
|
+#
|
|
|
+# Builds USM, then drives scratch fixture packages through the whole
|
|
|
+# bump-hook story. Every fixture lives OUTSIDE the checkout: the bump
|
|
|
+# verb rewrites the CWD's MANIFEST.usm and creates git tags, so it must
|
|
|
+# never run inside the USM source tree.
|
|
|
+#
|
|
|
+# Assertions (each printed PASS/FAIL, exit non-zero on any failure):
|
|
|
+# a. a version bump runs the hook as `<path> <old> <new>` (the hook
|
|
|
+# records its arguments), MANIFEST.usm's version is rewritten, and
|
|
|
+# the version line the hook rewrites in a fixture meson.build is
|
|
|
+# updated
|
|
|
+# b. a failing hook (non-zero exit) aborts the bump with exit 251,
|
|
|
+# leaving MANIFEST.usm and the fixture meson.build untouched
|
|
|
+# c. an explicit bump to the version the manifest already carries
|
|
|
+# does NOT run the hook
|
|
|
+# d. a flags-only bump (--rebuild-dependants, version unchanged) does
|
|
|
+# NOT run the hook, and the flag IS written to the manifest
|
|
|
+# e. a bare --git release (no version argument, no version change)
|
|
|
+# does NOT run the hook, and still commits + tags
|
|
|
+# f. --git with a hook that edits a file: the release commit contains
|
|
|
+# both MANIFEST.usm and the hook-edited file, and tag v<version>
|
|
|
+# exists
|
|
|
+# g. a data-package manifest (flags: ["dataPackage"], acquire + bump
|
|
|
+# execs) may declare the hook; it runs and the version bumps
|
|
|
+#
|
|
|
+# Environment quirks this script works around (keep in mind when
|
|
|
+# running it elsewhere):
|
|
|
+# * The freshly built usm binary links libusm-0.1.so by SONAME, and
|
|
|
+# the host carries an older /opt/usm/lib/libusm-0.1.so — every
|
|
|
+# invocation needs LD_LIBRARY_PATH=<build>/lib or it silently
|
|
|
+# talks to the stale library.
|
|
|
+# * meson only finds the Invercargill libraries through
|
|
|
+# PKG_CONFIG_PATH=/opt/usm/lib/pkgconfig.
|
|
|
+# * The hook is spawned through GLib, which PATH-searches a path
|
|
|
+# with no slash in it: a manifest declaring "bump": "bump.sh"
|
|
|
+# fails to spawn even with the file executable in the CWD. Real
|
|
|
+# manifests place scripts under usm-scripts/, whose slash makes
|
|
|
+# the path CWD-relative — the fixtures do the same.
|
|
|
+# * usm manifest bump --git commits through plain git: without a
|
|
|
+# configured identity it dies with "unable to auto-detect email
|
|
|
+# address". The GIT_AUTHOR_*/GIT_COMMITTER_* exports below give
|
|
|
+# both usm's internal commits and ours one.
|
|
|
+#
|
|
|
+# Usage: scripts/bump-e2e.sh [build-dir]
|
|
|
+# build-dir defaults to a fresh temp directory; pass an existing meson
|
|
|
+# build directory to skip the (slow) compile.
|
|
|
+
|
|
|
+set -e
|
|
|
+set -u
|
|
|
+set -o pipefail
|
|
|
+
|
|
|
+SCRIPT_DIR=$(cd -- "$(dirname -- "$0")" && pwd)
|
|
|
+SOURCE_DIR=$(cd -- "$SCRIPT_DIR/.." && pwd)
|
|
|
+
|
|
|
+WORK=$(mktemp -d /tmp/usm-bump-e2e.XXXXXX)
|
|
|
+trap 'rm -rf "$WORK"' EXIT
|
|
|
+
|
|
|
+PASS_COUNT=0
|
|
|
+FAIL_COUNT=0
|
|
|
+
|
|
|
+report() {
|
|
|
+ if [ "$1" = 0 ]; then
|
|
|
+ printf 'PASS %s\n' "$2"
|
|
|
+ PASS_COUNT=$((PASS_COUNT + 1))
|
|
|
+ else
|
|
|
+ printf 'FAIL %s\n' "$2"
|
|
|
+ FAIL_COUNT=$((FAIL_COUNT + 1))
|
|
|
+ fi
|
|
|
+}
|
|
|
+
|
|
|
+# Asserts <description> against <command...>: the command succeeding is
|
|
|
+# a PASS, failing is a FAIL (set -e must not abort on a failed
|
|
|
+# assertion — the failure is the observation, reported like any other).
|
|
|
+try() {
|
|
|
+ local description=$1
|
|
|
+ shift
|
|
|
+ if "$@" >/dev/null 2>&1; then
|
|
|
+ report 0 "$description"
|
|
|
+ else
|
|
|
+ report 1 "$description"
|
|
|
+ fi
|
|
|
+}
|
|
|
+
|
|
|
+# ---------------------------------------------------------------------------
|
|
|
+# 0. Build
|
|
|
+# ---------------------------------------------------------------------------
|
|
|
+
|
|
|
+BUILD_DIR=${1:-}
|
|
|
+
|
|
|
+if [ -z "$BUILD_DIR" ]; then
|
|
|
+ BUILD_DIR="$WORK/usm-build"
|
|
|
+ printf '== Building USM into %s\n' "$BUILD_DIR"
|
|
|
+ PKG_CONFIG_PATH=/opt/usm/lib/pkgconfig meson setup "$BUILD_DIR" "$SOURCE_DIR/src" >/dev/null 2>&1 \
|
|
|
+ || { echo "meson setup failed"; exit 1; }
|
|
|
+ PKG_CONFIG_PATH=/opt/usm/lib/pkgconfig ninja -C "$BUILD_DIR" >/dev/null 2>&1 \
|
|
|
+ || { echo "ninja failed"; exit 1; }
|
|
|
+fi
|
|
|
+
|
|
|
+USM_BIN="$BUILD_DIR/cli/usm"
|
|
|
+[ -x "$USM_BIN" ] || { echo "no usm binary at $USM_BIN"; exit 1; }
|
|
|
+
|
|
|
+# The stale-host-library workaround described in the header
|
|
|
+export LD_LIBRARY_PATH="$BUILD_DIR/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
|
|
|
+
|
|
|
+# One identity for our git calls and usm's internal release commits
|
|
|
+export GIT_AUTHOR_NAME="USM Bump E2E"
|
|
|
+export GIT_AUTHOR_EMAIL="bump-e2e@example.invalid"
|
|
|
+export GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"
|
|
|
+export GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"
|
|
|
+
|
|
|
+# ---------------------------------------------------------------------------
|
|
|
+# 1. Fixtures
|
|
|
+# ---------------------------------------------------------------------------
|
|
|
+
|
|
|
+# Runs the built usm against one fixture directory; the exit code lands
|
|
|
+# in BUMP_EXIT (never aborts the script — a non-zero bump IS the test).
|
|
|
+BUMP_EXIT=0
|
|
|
+bump() {
|
|
|
+ BUMP_EXIT=0
|
|
|
+ (cd "$1" && "$USM_BIN" manifest bump "${@:2}") || BUMP_EXIT=$?
|
|
|
+}
|
|
|
+
|
|
|
+# A code-package fixture: a manifest declaring the bump hook, the hook
|
|
|
+# itself (records "<old> <new>", rewrites the meson.build version line),
|
|
|
+# a meson.build carrying the version, and the provided resource.
|
|
|
+make_fixture() {
|
|
|
+ # make_fixture <dir> <name> <version> <hook> (hook: record | fail)
|
|
|
+ local dir=$1 name=$2 version=$3 hook=$4
|
|
|
+ rm -rf "$dir"
|
|
|
+ mkdir -p "$dir/usm-scripts"
|
|
|
+ cat > "$dir/MANIFEST.usm" <<EOF
|
|
|
+{
|
|
|
+ "name": "$name",
|
|
|
+ "version": "$version",
|
|
|
+ "summary": "execs.bump end-to-end fixture",
|
|
|
+ "licences": [],
|
|
|
+ "flags": [],
|
|
|
+ "provides": { "res:$name/hello.txt": "source:hello.txt" },
|
|
|
+ "depends": { "runtime": [], "build": [], "manage": ["bin:bash"] },
|
|
|
+ "execs": { "bump": "usm-scripts/bump.sh" }
|
|
|
+}
|
|
|
+EOF
|
|
|
+ printf 'fixture payload\n' > "$dir/hello.txt"
|
|
|
+ printf "project('%s', 'c', version: '%s')\n" "$name" "$version" > "$dir/meson.build"
|
|
|
+ if [ "$hook" = fail ]; then
|
|
|
+ cat > "$dir/usm-scripts/bump.sh" <<'EOF'
|
|
|
+#!/bin/bash
|
|
|
+# Records its arguments, then fails — the bump must abort.
|
|
|
+echo "$1 $2" > hook-args.txt
|
|
|
+echo "deliberate bump-hook failure" >&2
|
|
|
+exit 1
|
|
|
+EOF
|
|
|
+ else
|
|
|
+ cat > "$dir/usm-scripts/bump.sh" <<'EOF'
|
|
|
+#!/bin/bash
|
|
|
+# Records "<old> <new>", then rewrites the meson.build project version.
|
|
|
+echo "$1 $2" > hook-args.txt
|
|
|
+sed -i "s/version: '$1'/version: '$2'/" meson.build
|
|
|
+EOF
|
|
|
+ fi
|
|
|
+ chmod +x "$dir/usm-scripts/bump.sh"
|
|
|
+}
|
|
|
+
|
|
|
+# Wraps a fixture into a throwaway git repository with everything
|
|
|
+# committed, so --git releases have something to tag.
|
|
|
+git_init_fixture() {
|
|
|
+ (cd "$1" && git init -q && git add -A && git commit -qm "fixture base")
|
|
|
+}
|
|
|
+
|
|
|
+FX_A="$WORK/fixture-a"
|
|
|
+FX_B="$WORK/fixture-b"
|
|
|
+FX_C="$WORK/fixture-c"
|
|
|
+FX_E="$WORK/fixture-e"
|
|
|
+FX_F="$WORK/fixture-f"
|
|
|
+FX_G="$WORK/fixture-g"
|
|
|
+
|
|
|
+make_fixture "$FX_A" bump-e2e-demo 1.0.0 record
|
|
|
+make_fixture "$FX_B" bump-e2e-fail 1.0.0 fail
|
|
|
+make_fixture "$FX_C" bump-e2e-same 1.0.1 record
|
|
|
+make_fixture "$FX_E" bump-e2e-release 1.0.1 record
|
|
|
+make_fixture "$FX_F" bump-e2e-git 1.0.1 record
|
|
|
+git_init_fixture "$FX_E"
|
|
|
+git_init_fixture "$FX_F"
|
|
|
+
|
|
|
+# The data-package fixture: the DATA_MANIFEST shape (acquire + bump are
|
|
|
+# the maintainer-side execs a data package may declare) with a VERSION
|
|
|
+# file in place of meson.build for the hook to rewrite.
|
|
|
+mkdir -p "$FX_G/usm-scripts"
|
|
|
+cat > "$FX_G/MANIFEST.usm" <<'EOF'
|
|
|
+{
|
|
|
+ "name": "bump-e2e-fonts",
|
|
|
+ "version": "1.0.0",
|
|
|
+ "summary": "Example fonts",
|
|
|
+ "licences": [],
|
|
|
+ "flags": ["dataPackage"],
|
|
|
+ "provides": { "res:bump-e2e-fonts/Example.ttf": "source:Example.ttf" },
|
|
|
+ "depends": { "runtime": [], "build": [], "manage": ["bin:bash"] },
|
|
|
+ "execs": { "acquire": "usm-scripts/acquire.sh", "bump": "usm-scripts/bump.sh" }
|
|
|
+}
|
|
|
+EOF
|
|
|
+printf 'stub font payload\n' > "$FX_G/Example.ttf"
|
|
|
+printf "data-version: '1.0.0'\n" > "$FX_G/VERSION"
|
|
|
+printf '#!/bin/bash\nexit 0\n' > "$FX_G/usm-scripts/acquire.sh"
|
|
|
+cat > "$FX_G/usm-scripts/bump.sh" <<'EOF'
|
|
|
+#!/bin/bash
|
|
|
+echo "$1 $2" > hook-args.txt
|
|
|
+sed -i "s/data-version: '$1'/data-version: '$2'/" VERSION
|
|
|
+EOF
|
|
|
+chmod +x "$FX_G/usm-scripts/acquire.sh" "$FX_G/usm-scripts/bump.sh"
|
|
|
+
|
|
|
+# ---------------------------------------------------------------------------
|
|
|
+# 2. (a) A version bump runs the hook and rewrites every version line
|
|
|
+# ---------------------------------------------------------------------------
|
|
|
+
|
|
|
+printf '== (a) version bump runs the hook\n'
|
|
|
+bump "$FX_A" 1.0.1
|
|
|
+try "(a) usm manifest bump 1.0.1 exits 0" test "$BUMP_EXIT" = 0
|
|
|
+try '(a) hook received the arguments "1.0.0 1.0.1"' \
|
|
|
+ test "$(cat "$FX_A/hook-args.txt" 2>/dev/null || true)" = "1.0.0 1.0.1"
|
|
|
+try "(a) MANIFEST.usm version rewritten to 1.0.1" \
|
|
|
+ grep -Eq '"version"[[:space:]]*:[[:space:]]*"1\.0\.1"' "$FX_A/MANIFEST.usm"
|
|
|
+try "(a) meson.build version line rewritten to 1.0.1" \
|
|
|
+ grep -q "version: '1.0.1'" "$FX_A/meson.build"
|
|
|
+
|
|
|
+# ---------------------------------------------------------------------------
|
|
|
+# 3. (b) A failing hook aborts the bump, leaving the tree untouched
|
|
|
+# ---------------------------------------------------------------------------
|
|
|
+
|
|
|
+printf '== (b) failing hook aborts the bump\n'
|
|
|
+bump "$FX_B" 1.0.1
|
|
|
+try "(b) bump exits 251 when the hook fails" test "$BUMP_EXIT" = 251
|
|
|
+try '(b) the hook did run (its arguments were recorded)' \
|
|
|
+ test "$(cat "$FX_B/hook-args.txt" 2>/dev/null || true)" = "1.0.0 1.0.1"
|
|
|
+try "(b) MANIFEST.usm untouched (still 1.0.0)" \
|
|
|
+ grep -Eq '"version"[[:space:]]*:[[:space:]]*"1\.0\.0"' "$FX_B/MANIFEST.usm"
|
|
|
+try "(b) meson.build untouched (still 1.0.0)" \
|
|
|
+ grep -q "version: '1.0.0'" "$FX_B/meson.build"
|
|
|
+
|
|
|
+# ---------------------------------------------------------------------------
|
|
|
+# 4. (c)/(d) No version change — no hook
|
|
|
+# ---------------------------------------------------------------------------
|
|
|
+
|
|
|
+printf '== (c) explicit same-version bump\n'
|
|
|
+bump "$FX_C" 1.0.1
|
|
|
+try "(c) bump 1.0.1 on a 1.0.1 manifest exits 0" test "$BUMP_EXIT" = 0
|
|
|
+try "(c) the hook did NOT run" test ! -e "$FX_C/hook-args.txt"
|
|
|
+try "(c) MANIFEST.usm still carries 1.0.1" \
|
|
|
+ grep -Eq '"version"[[:space:]]*:[[:space:]]*"1\.0\.1"' "$FX_C/MANIFEST.usm"
|
|
|
+
|
|
|
+printf '== (d) flags-only bump\n'
|
|
|
+# A flags-only invocation needs the version argument to match the
|
|
|
+# manifest (no version and no --git is a usage error), so this bump
|
|
|
+# changes nothing but the flags array.
|
|
|
+bump "$FX_C" 1.0.1 --rebuild-dependants
|
|
|
+try "(d) bump 1.0.1 --rebuild-dependants exits 0" test "$BUMP_EXIT" = 0
|
|
|
+try "(d) the hook did NOT run" test ! -e "$FX_C/hook-args.txt"
|
|
|
+try "(d) the rebuildDependants flag IS in the manifest" \
|
|
|
+ grep -q '"rebuildDependants"' "$FX_C/MANIFEST.usm"
|
|
|
+
|
|
|
+# ---------------------------------------------------------------------------
|
|
|
+# 5. (e) A bare --git release does not run the hook
|
|
|
+# ---------------------------------------------------------------------------
|
|
|
+
|
|
|
+printf '== (e) bare --git release\n'
|
|
|
+bump "$FX_E" --git
|
|
|
+try "(e) usm manifest bump --git exits 0" test "$BUMP_EXIT" = 0
|
|
|
+try "(e) the hook did NOT run" test ! -e "$FX_E/hook-args.txt"
|
|
|
+try "(e) tag v1.0.1 exists" test -n "$(git -C "$FX_E" tag -l "v1.0.1")"
|
|
|
+try "(e) meson.build untouched (still 1.0.1)" \
|
|
|
+ grep -q "version: '1.0.1'" "$FX_E/meson.build"
|
|
|
+
|
|
|
+# ---------------------------------------------------------------------------
|
|
|
+# 6. (f) --git with a hook: the release commit carries the hook's edits
|
|
|
+# ---------------------------------------------------------------------------
|
|
|
+
|
|
|
+printf '== (f) --git release after a hook run\n'
|
|
|
+bump "$FX_F" patch --git
|
|
|
+try "(f) usm manifest bump patch --git exits 0" test "$BUMP_EXIT" = 0
|
|
|
+try '(f) hook received the arguments "1.0.1 1.0.2"' \
|
|
|
+ test "$(cat "$FX_F/hook-args.txt" 2>/dev/null || true)" = "1.0.1 1.0.2"
|
|
|
+try "(f) tag v1.0.2 exists" test -n "$(git -C "$FX_F" tag -l "v1.0.2")"
|
|
|
+try "(f) the release commit contains MANIFEST.usm" \
|
|
|
+ test -n "$(git -C "$FX_F" show --name-only --format= "v1.0.2" -- MANIFEST.usm)"
|
|
|
+try "(f) the release commit contains the hook-edited meson.build" \
|
|
|
+ test -n "$(git -C "$FX_F" show --name-only --format= "v1.0.2" -- meson.build)"
|
|
|
+try "(f) the tagged meson.build carries the new version" \
|
|
|
+ test -n "$(git -C "$FX_F" show "v1.0.2":meson.build | grep "version: '1.0.2'")"
|
|
|
+
|
|
|
+# ---------------------------------------------------------------------------
|
|
|
+# 7. (g) A data package may declare and run the bump hook
|
|
|
+# ---------------------------------------------------------------------------
|
|
|
+
|
|
|
+printf '== (g) data-package bump hook\n'
|
|
|
+bump "$FX_G" 1.0.1
|
|
|
+try "(g) data-package bump 1.0.1 exits 0" test "$BUMP_EXIT" = 0
|
|
|
+try '(g) hook received the arguments "1.0.0 1.0.1"' \
|
|
|
+ test "$(cat "$FX_G/hook-args.txt" 2>/dev/null || true)" = "1.0.0 1.0.1"
|
|
|
+try "(g) MANIFEST.usm version rewritten to 1.0.1" \
|
|
|
+ grep -Eq '"version"[[:space:]]*:[[:space:]]*"1\.0\.1"' "$FX_G/MANIFEST.usm"
|
|
|
+try "(g) the hook's VERSION file rewritten to 1.0.1" \
|
|
|
+ grep -q "data-version: '1.0.1'" "$FX_G/VERSION"
|
|
|
+
|
|
|
+# ---------------------------------------------------------------------------
|
|
|
+
|
|
|
+printf '\n%d passed, %d failed\n' "$PASS_COUNT" "$FAIL_COUNT"
|
|
|
+if [ "$FAIL_COUNT" -gt 0 ]; then
|
|
|
+ exit 1
|
|
|
+fi
|
|
|
+exit 0
|