ci(e2e): 按 README 原样安装的 readme 阶段走 release 附件下载,升级阶段的旧版安装器不再被 FELIS_REF 逼去源码构建

This commit is contained in:
Lemon-miaow committed 2026-09-27 01:13:29 +08:00
1 parent aad72ddb2b
commit 4fe58ef6f5
6 files changed
+336 -14

No files matched your search

+92
View File
@@ -0,0 +1,92 @@
#!/bin/bash
# The newest published release, for the e2e workflow's readme and upgrade jobs, and what
# their installer logs must show about how that release reached the host:
#
# bash deploy/e2e_release.sh find # tag, binary, sums into $GITHUB_OUTPUT
# bash deploy/e2e_release.sh check-own LOG # the release's own installer (upgrade)
# bash deploy/e2e_release.sh check-readme LOG # this commit's installer on its default
# # channel, the README's command (readme)
#
# The checks read TAG, BINARY and SUMS from the environment, as find wrote them. The
# runners are x86_64, so the binary asset is felis-linux-amd64. deploy/e2e_release_test.sh
# holds this script's own checks, against bootstrap.sh's own messages.
set -euo pipefail
ASSET=felis-linux-amd64
HOST_BIN=/usr/local/bin/felis
fails=0
pass() { printf 'PASS %s\n' "$*"; }
fail() { printf 'FAIL %s\n' "$*"; fails=$((fails + 1)); }
has() { # label fixed-string log
if grep -qF -- "$2" "$3"; then pass "$1"; else fail "$1: no line with <$2> in $3"; fi
}
has_re() { # label regex log
if grep -qE -- "$2" "$3"; then pass "$1"; else fail "$1: no line matching <$2> in $3"; fi
}
lacks_re() { # label regex log
local hit
if hit="$(grep -E -m 1 -- "$2" "$3")"; then fail "$1: ${hit}"; else pass "$1"; fi
}
# find_release: `gh release view` with no tag answers with the newest release that is not a
# prerelease, the one the installer's release channel resolves.
find_release() {
local lines tag names binary="" sums=""
lines="$(gh release view --repo "$GITHUB_REPOSITORY" --json tagName,assets --jq '.tagName, .assets[].name' 2>/dev/null || true)"
tag="$(printf '%s\n' "$lines" | head -n 1)"
names="$(printf '%s\n' "$lines" | tail -n +2)"
if [ -z "$tag" ]; then
echo "::notice::no published release yet; the readme and upgrade jobs have nothing to install"
fi
if printf '%s\n' "$names" | grep -qxF "$ASSET"; then binary=yes; fi
if printf '%s\n' "$names" | grep -qxF SHA256SUMS; then sums=yes; fi
printf 'tag=%s\nbinary=%s\nsums=%s\n' "$tag" "$binary" "$sums" >> "${GITHUB_OUTPUT:-/dev/stdout}"
}
# check_own: a release's installer, however old, downloads the release's binary when the
# release publishes one, and says so in the same words.
check_own() {
local log="$1"
if [ "${BINARY:-}" != yes ]; then
echo "::notice::release ${TAG} publishes no ${ASSET}; its installer builds it from source"
return 0
fi
has "the release's installer installed the release's binary" "installed ${ASSET} ${TAG} at ${HOST_BIN}" "$log"
}
# check_readme: this commit's installer takes everything from a release that publishes its
# SHA256SUMS and builds nothing on the host; from one without, it builds the tag from source
# and says why.
check_readme() {
local log="$1" role
if [ "${BINARY:-}" = yes ] && [ "${SUMS:-}" = yes ]; then
has "the binary is the release's" "installed ${ASSET} ${TAG} at ${HOST_BIN}" "$log"
has "the images and plugin come from the release" "release ${TAG}'s prebuilt images and Velocity plugin are installed as published" "$log"
for role in felis limbo lobby paper; do
has_re "felis/${role} is the release's" "felis/${role}:[^ ]* is the release's" "$log"
done
has_re "the registry image is the release's" "docker.io/library/registry@sha256:[0-9a-f]* is the release's" "$log"
has_re "the postgres image is the release's" "docker.io/library/postgres@sha256:[0-9a-f]* is the release's" "$log"
has "felis-velocity.jar is the release's" "felis-velocity.jar is the release's" "$log"
lacks_re "nothing fell back to a build or a pull" "on this host instead|from Docker Hub instead|building felis from source" "$log"
lacks_re "Docker was left alone" "docker already installed|installing docker|docker running" "$log"
elif [ "${BINARY:-}" = yes ]; then
has "the source build says why" "release ${TAG} publishes no SHA256SUMS, so ${ASSET} cannot be verified; building ${TAG} from source on this host instead" "$log"
echo "::warning::release ${TAG} publishes no SHA256SUMS, so the README's install builds ${TAG} from source on the host, Docker included; a release cut by release.yml puts it on the assets"
else
has "the source build says why" "release ${TAG} publishes no usable ${ASSET}; building ${TAG} from source on this host instead" "$log"
echo "::warning::release ${TAG} publishes no ${ASSET}, so the README's install builds ${TAG} from source on the host, Docker included; a release cut by release.yml puts it on the assets"
fi
}
case "${1:-}" in
find) find_release ;;
check-own) check_own "${2:?usage: e2e_release.sh check-own LOG}" ;;
check-readme) check_readme "${2:?usage: e2e_release.sh check-readme LOG}" ;;
*)
echo "usage: e2e_release.sh find | check-own LOG | check-readme LOG" >&2
exit 2
;;
esac
exit "$fails"
+168
View File
@@ -0,0 +1,168 @@
#!/bin/bash
# Checks for deploy/e2e_release.sh. Run it as: bash deploy/e2e_release_test.sh
#
# The installer logs it reads are built from bootstrap.sh's own ok/warn messages, expanded
# with a release's values, so rewording one of them there fails here rather than in a
# two-hour e2e run. gh is a stub that prints what a release listing would.
set -u
here="$(dirname "$0")"
ER="${1:-${here}/e2e_release.sh}"
BS="${2:-${here}/bootstrap.sh}"
[ -f "$ER" ] || { echo "no such script: $ER"; exit 1; }
[ -f "$BS" ] || { echo "no such script: $BS"; exit 1; }
fails=0
expect() { # label needle haystack
case "$3" in
*"$2"*) echo "PASS $1" ;;
*) echo "FAIL $1: expected <$2> in:"; echo "$3"; fails=$((fails + 1)) ;;
esac
}
status() { # label want got
if [ "$2" = "$3" ]; then echo "PASS $1"; else echo "FAIL $1: exit $3, want $2"; fails=$((fails + 1)); fi
}
root="$(mktemp -d)"
trap 'rm -rf "$root"' EXIT
# msg <marker> prints bootstrap.sh's first ok/warn message holding <marker>, expanded with
# the variables below the way the installer expands it. They are read only through that eval.
# shellcheck disable=SC2034
{
tag=v1.2.3
FELIS_REF="$tag"
v="$tag"
asset=felis-linux-amd64
name="$asset"
HOST_BIN=/usr/local/bin/felis
digest=sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
}
msg() {
local line body
line="$(grep -F -- "$1" "$BS" | grep -E '^[[:space:]]*(ok|warn) "' | head -n 1)"
if [ -z "$line" ]; then
echo "FAIL bootstrap.sh prints no message holding <$1>" >&2
fails=$((fails + 1))
return
fi
body="${line#*\"}"
body="${body%\"*}"
eval "printf '%s\n' \"${body}\""
}
# unusable <marker> prints the warning of bootstrap.sh's first artifact_unusable call holding
# <marker>; artifact_unusable joins its two arguments with "; ".
unusable() {
local line
line="$(grep -F -- "$1" "$BS" | grep -E '^[[:space:]]*artifact_unusable "' | head -n 1)"
if [ -z "$line" ]; then
echo "FAIL bootstrap.sh has no artifact_unusable call holding <$1>" >&2
fails=$((fails + 1))
return
fi
artifact_unusable() { printf '%s; %s\n' "$1" "$2"; }
eval "$line"
}
image_line() { # target
# shellcheck disable=SC2034 # read by msg's eval
local target="$1"
msg 'is the release'"'"'s (${digest:7:12})'
}
# What an install from a complete release prints, in the installer's order.
{
msg 'ok "installed ${asset} ${FELIS_REF} at ${HOST_BIN}"'
msg 'matches release ${tag}'"'"'s SHA256SUMS'
msg 'prebuilt images and Velocity plugin are installed as published'
for t in felis/felis:v1.2.3 felis/limbo:v1.2.3 felis/lobby:v1.2.3 felis/paper:v1.2.3 \
docker.io/library/registry@sha256:aa docker.io/library/postgres@sha256:bb; do
image_line "$t"
done
msg 'felis-velocity.jar is the release'"'"'s"'
} > "$root/complete.log"
readme() { # log binary sums
TAG="$tag" BINARY="$2" SUMS="$3" bash "$ER" check-readme "$1" 2>&1
}
own() { # log binary
TAG="$tag" BINARY="$2" bash "$ER" check-own "$1" 2>&1
}
out="$(readme "$root/complete.log" yes yes)"
status "a complete release's install passes" 0 $?
expect " and every image is checked" "PASS felis/paper is the release's" "$out"
grep -v 'felis/limbo:' "$root/complete.log" > "$root/nolimbo.log"
out="$(readme "$root/nolimbo.log" yes yes)"
status "an image missing from the log fails" 1 $?
expect " and names it" "FAIL felis/limbo is the release's" "$out"
{
cat "$root/complete.log"
name=felis-images-linux-amd64.txt unusable '} cannot be used" "building its images on this host instead"'
} > "$root/fallback.log"
out="$(readme "$root/fallback.log" yes yes)"
status "an image built on the host fails" 1 $?
expect " and quotes the fallback" "building its images on this host instead" "$out"
{ cat "$root/complete.log"; msg 'ok "docker already installed"'; } > "$root/docker.log"
out="$(readme "$root/docker.log" yes yes)"
status "Docker touched fails" 1 $?
sed 's/installed felis-linux-amd64 v1.2.3/installed felis-linux-amd64 v1.2.2/' "$root/complete.log" > "$root/other.log"
out="$(readme "$root/other.log" yes yes)"
status "another release's binary fails" 1 $?
# A release that publishes its binary but no SHA256SUMS: this commit's installer builds the
# tag from source and says so.
msg 'publishes no SHA256SUMS, so ${name} cannot be verified' > "$root/nosums.log"
out="$(readme "$root/nosums.log" yes "")"
status "a release without SHA256SUMS passes when the fallback is announced" 0 $?
expect " and warns in the run" "::warning::release v1.2.3 publishes no SHA256SUMS" "$out"
out="$(readme "$root/complete.log" yes "")"
status "a release without SHA256SUMS fails when nothing announced the fallback" 1 $?
msg 'publishes no usable ${asset}' > "$root/nobinary.log"
out="$(readme "$root/nobinary.log" "" "")"
status "a release without a binary passes when the fallback is announced" 0 $?
out="$(readme "$root/nosums.log" "" "")"
status "a release without a binary fails when the log says otherwise" 1 $?
# check-own: the release's own installer, which may predate SHA256SUMS.
out="$(own "$root/complete.log" yes)"
status "the release's installer downloading its binary passes" 0 $?
out="$(own "$root/nobinary.log" yes)"
status "the release's installer building from source fails" 1 $?
out="$(own "$root/other.log" yes)"
status "the release's installer downloading another release fails" 1 $?
out="$(own "$root/nobinary.log" "")"
status "a release without a binary asks nothing of its installer" 0 $?
# find: the listing gh answers with, in the step's outputs, exactly.
mkdir -p "$root/bin"
cat > "$root/bin/gh" <<'STUB'
#!/bin/sh
[ -n "${GH_LISTING:-}" ] || exit 1
printf '%s\n' $GH_LISTING
STUB
chmod +x "$root/bin/gh"
find_run() { # listing: prints what find says; its outputs land in $root/out
: > "$root/out"
GH_LISTING="$1" GITHUB_REPOSITORY=FelisMC/Felis GITHUB_OUTPUT="$root/out" PATH="$root/bin:$PATH" bash "$ER" find 2>&1
}
same() { # label want got
if [ "$2" = "$3" ]; then echo "PASS $1"; else printf 'FAIL %s: got\n%s\nwant\n%s\n' "$1" "$3" "$2"; fails=$((fails + 1)); fi
}
find_run "v1.2.3 felis-linux-amd64 felis-linux-arm64 SHA256SUMS felis-velocity.jar" >/dev/null
same "find: a complete release" "$(printf 'tag=v1.2.3\nbinary=yes\nsums=yes')" "$(cat "$root/out")"
find_run "v0.1.0 felis-linux-amd64 felis-linux-arm64" >/dev/null
same "find: a release without SHA256SUMS" "$(printf 'tag=v0.1.0\nbinary=yes\nsums=')" "$(cat "$root/out")"
find_run "v0.1.0 felis-linux-arm64 felis-linux-amd64.cdx.json SHA256SUMS.sig" >/dev/null
same "find: only exact asset names count" "$(printf 'tag=v0.1.0\nbinary=\nsums=')" "$(cat "$root/out")"
out="$(find_run "")"
same "find: no release" "$(printf 'tag=\nbinary=\nsums=')" "$(cat "$root/out")"
expect " and says so" "::notice::no published release yet" "$out"
echo
if [ "$fails" -eq 0 ]; then echo "ALL PASS"; else echo "${fails} FAILED"; fi
exit "$fails"