| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312 |
- #!/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
|