diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index c68b05f2a9..efa5a5ccb0 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -42,6 +42,9 @@ jobs: with: persist-credentials: false + - name: Test image stamp + run: sh e2e/stamp_test.sh + - name: Set up Go uses: actions/setup-go@v7 with: diff --git a/e2e/stamp.sh b/e2e/stamp.sh index 73fb5591d0..9c4ed355be 100755 --- a/e2e/stamp.sh +++ b/e2e/stamp.sh @@ -27,16 +27,24 @@ digest() { } { - # the content of those paths, not the commit: an e2e-only commit cannot change the image, and - # keying on HEAD would rebuild the stack for every one of them - # shellcheck disable=SC2086 # the path list is deliberately split into arguments - for path in $sources; do - git rev-parse "HEAD:$path" - done + # Hash the files on disk, not HEAD plus a diff: staging or committing the same + # content must not invalidate a running stack. Skip deleted files, including + # staged deletions, so committing their removal leaves the stamp unchanged too. + # shellcheck disable=SC2086,SC2016 # split paths; expand $file in the child shell + git ls-files -z -- $sources | xargs -0 sh -c ' + for file do + if [ -f "$file" ]; then + if command -v sha256sum >/dev/null 2>&1; then + sha256sum "$file" + else + shasum -a 256 "$file" + fi + fi + done + ' sh + # Only untracked names belong here; porcelain also records index state. # shellcheck disable=SC2086 - git diff HEAD -- $sources - # shellcheck disable=SC2086 - git status --porcelain -- $sources + git ls-files --others --exclude-standard -z -- $sources # an instrumented build is a different binary from the same sources, so it has to be a # different stamp: without this a coverage stack is accepted for a plain run, and a plain # stack for a coverage run, which reports no coverage at all and looks like untested code diff --git a/e2e/stamp_test.sh b/e2e/stamp_test.sh new file mode 100644 index 0000000000..b62e3fdee1 --- /dev/null +++ b/e2e/stamp_test.sh @@ -0,0 +1,80 @@ +#!/bin/sh +# Exercise the stamp without building an image or starting the browser stack. +set -eu + +stamp_script=$(cd "$(dirname "$0")" && pwd)/stamp.sh +work=$(mktemp -d "${TMPDIR:-/tmp}/remark42-stamp.XXXXXX") +trap 'rm -rf "$work"' EXIT HUP INT TERM +mkdir -p "$work/e2e/telegramstub" "$work/backend" "$work/frontend" +cp "$stamp_script" "$work/e2e/stamp.sh" +cd "$work" +git init -q +git config user.name w3lld1 +git config user.email w3lld1@users.noreply.github.com +printf 'original\n' > backend/source +printf 'frontend\n' > 'frontend/file with spaces' +printf 'stub\n' > e2e/telegramstub/source +printf 'image\n' > Dockerfile +printf 'init\n' > docker-init.sh +git add . +git commit -qm initial + +stamp() { E2E_COVERAGE='' sh e2e/stamp.sh; } +same() { + if [ "$1" != "$2" ]; then + printf 'FAIL: %s (%s != %s)\n' "$3" "$1" "$2" >&2 + exit 1 + fi + printf 'PASS: %s\n' "$3" +} +different() { + if [ "$1" = "$2" ]; then + printf 'FAIL: %s (%s == %s)\n' "$3" "$1" "$2" >&2 + exit 1 + fi + printf 'PASS: %s\n' "$3" +} + +original=$(stamp) +printf 'edited\n' > backend/source +edited=$(stamp) +different "$original" "$edited" 'tracked content changes the stamp' +git add backend/source +same "$edited" "$(stamp)" 'staging an edit preserves the stamp' +git commit -qm edit +same "$edited" "$(stamp)" 'committing an edit preserves the stamp' + +rm backend/source +deleted=$(stamp) +different "$edited" "$deleted" 'deleting a tracked file changes the stamp' +git add backend/source +same "$deleted" "$(stamp)" 'staging a deletion preserves the stamp' +git commit -qm deletion +same "$deleted" "$(stamp)" 'committing a deletion preserves the stamp' + +printf 'changed\n' > 'frontend/file with spaces' +spaced=$(stamp) +different "$deleted" "$spaced" 'content in a path with spaces is hashed' +git add frontend +git commit -qm spaces +same "$spaced" "$(stamp)" 'committing a path with spaces preserves the stamp' + +printf 'new\n' > frontend/untracked +untracked=$(stamp) +different "$spaced" "$untracked" 'untracked names change the stamp' +printf 'later edit\n' > frontend/untracked +same "$untracked" "$(stamp)" 'untracked contents retain the name-only behavior' +mv frontend/untracked frontend/renamed +different "$untracked" "$(stamp)" 'renaming an untracked file changes the stamp' +rm frontend/renamed +same "$spaced" "$(stamp)" 'removing an untracked file restores the stamp' + +printf 'suite only\n' > e2e/test.txt +git add e2e/test.txt +git commit -qm suite +same "$spaced" "$(stamp)" 'a suite-only commit preserves the stamp' +printf 'edited stub\n' > e2e/telegramstub/source +different "$spaced" "$(stamp)" 'the telegram image sources are included' +plain=$(stamp) +same "$plain" "$(E2E_COVERAGE=0 sh e2e/stamp.sh)" 'coverage=0 is a plain build' +different "$plain" "$(E2E_COVERAGE=1 sh e2e/stamp.sh)" 'coverage=1 is a separate build'