feat(bootstrap): FELIS_RELEASE 按指定 release 的附件安装,回滚文档不再把人带回最新版

This commit is contained in:
Lemon-miaow committed 2026-09-27 01:03:38 +08:00
1 parent 5c58104e09
commit aad72ddb2b
4 files changed
+105 -2

No files matched your search

+28
View File
@@ -90,6 +90,11 @@
# FELIS_GITHUB_TOKEN GitHub token; REQUIRED while the repo is private # FELIS_GITHUB_TOKEN GitHub token; REQUIRED while the repo is private
# FELIS_REF branch/tag/sha — pins the build, overrides the channel, and forces a # FELIS_REF branch/tag/sha — pins the build, overrides the channel, and forces a
# source build (naming a ref asks for that tree, not a published asset) # source build (naming a ref asks for that tree, not a published asset)
# FELIS_RELEASE a published release tag (v1.2.3) the release channel installs, from
# its assets, instead of the newest: the way back to an earlier release.
# Read this script at the same tag. Installers older than this variable
# ignore it and install the newest; for those, FELIS_REF=<tag> builds
# that tag from source
# FELIS_IMAGE control-plane image ref (default: # FELIS_IMAGE control-plane image ref (default:
# registry.felis.svc:5000/felis/felis:<the felis version>, so each # registry.felis.svc:5000/felis/felis:<the felis version>, so each
# release has its own tag and `kubectl rollout undo` returns to the # release has its own tag and `kubectl rollout undo` returns to the
@@ -153,6 +158,8 @@ FELIS_REF="${FELIS_REF:-}"
# built, so it takes the source path even on the release channel. # built, so it takes the source path even on the release channel.
FELIS_REF_PINNED="" FELIS_REF_PINNED=""
if [ -n "$FELIS_REF" ]; then FELIS_REF_PINNED=1; fi if [ -n "$FELIS_REF" ]; then FELIS_REF_PINNED=1; fi
# A published release tag the release channel installs instead of the newest (header).
FELIS_RELEASE="${FELIS_RELEASE:-}"
# The directory of release assets to install from (header); empty installs as the channel says. # The directory of release assets to install from (header); empty installs as the channel says.
FELIS_ARTIFACT_DIR="${FELIS_ARTIFACT_DIR:-}" FELIS_ARTIFACT_DIR="${FELIS_ARTIFACT_DIR:-}"
# Set once a prebuilt felis binary is installed at HOST_BIN, by the TUI hand-off, a release # Set once a prebuilt felis binary is installed at HOST_BIN, by the TUI hand-off, a release
@@ -938,6 +945,18 @@ validate_settings() {
[ -z "$FELIS_REF_PINNED" ] || die "FELIS_ARTIFACT_DIR and FELIS_REF both name what to install; set one" [ -z "$FELIS_REF_PINNED" ] || die "FELIS_ARTIFACT_DIR and FELIS_REF both name what to install; set one"
[ -z "${FELIS_SKIP_FETCH:-}" ] || die "FELIS_ARTIFACT_DIR and FELIS_SKIP_FETCH both name what to install; set one" [ -z "${FELIS_SKIP_FETCH:-}" ] || die "FELIS_ARTIFACT_DIR and FELIS_SKIP_FETCH both name what to install; set one"
fi fi
if [ -n "$FELIS_RELEASE" ]; then
[[ "$FELIS_RELEASE" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]] \
|| die "FELIS_RELEASE must be a release tag like v1.2.3 (got '${FELIS_RELEASE}')"
# Each of these names what to install another way and would silently win, installing
# something other than the release asked for.
[ -z "$FELIS_REF_PINNED" ] || die "FELIS_RELEASE and FELIS_REF both name what to install; set one (FELIS_RELEASE installs a published release's assets, FELIS_REF builds a tree from source)"
[ -z "$FELIS_ARTIFACT_DIR" ] || die "FELIS_RELEASE and FELIS_ARTIFACT_DIR both name what to install; set one"
[ -z "${FELIS_SKIP_FETCH:-}" ] || die "FELIS_RELEASE and FELIS_SKIP_FETCH both name what to install; set one"
[ "$FELIS_VERSION_BOOTSTRAP" = release ] \
|| die "FELIS_RELEASE names a published release, which the ${FELIS_VERSION_BOOTSTRAP} channel does not install; drop FELIS_VERSION_BOOTSTRAP"
! bootstrap_from_tui || die "FELIS_RELEASE does not reach felis setup's install, which installs the binary it runs as; run the installer one-liner with FELIS_RELEASE instead"
fi
[ "$(heap_megabytes "$FELIS_VELOCITY_XMX")" -ge 256 ] \ [ "$(heap_megabytes "$FELIS_VELOCITY_XMX")" -ge 256 ] \
|| die "FELIS_VELOCITY_XMX must be a heap size of at least 256M, written <n>M or <n>G (got '${FELIS_VELOCITY_XMX}')" || die "FELIS_VELOCITY_XMX must be a heap size of at least 256M, written <n>M or <n>G (got '${FELIS_VELOCITY_XMX}')"
case "$FELIS_UPGRADE_DEPS" in case "$FELIS_UPGRADE_DEPS" in
@@ -2567,10 +2586,19 @@ resolve_install_ref() {
fi fi
case "$FELIS_VERSION_BOOTSTRAP" in case "$FELIS_VERSION_BOOTSTRAP" in
release) release)
if [ -n "$FELIS_RELEASE" ]; then
# A named release installs from its own assets, as the newest would: the way back
# to an earlier release (docs/troubleshooting.md §16). validate_settings checked
# the tag's form; this checks it was published.
load_release_json "$FELIS_RELEASE" || die "could not find the published Felis release ${FELIS_RELEASE}.
Check the tag against the repository's releases page. If the repository is private, set FELIS_GITHUB_TOKEN to a token with read access to it."
FELIS_REF="$FELIS_RELEASE"
else
log "resolving the newest published Felis release" log "resolving the newest published Felis release"
FELIS_REF="$(github_latest_tag)" || die "could not resolve the newest Felis release. FELIS_REF="$(github_latest_tag)" || die "could not resolve the newest Felis release.
If the repository is private, set FELIS_GITHUB_TOKEN to a token with read access to it. If the repository is private, set FELIS_GITHUB_TOKEN to a token with read access to it.
If no release has been published yet, set FELIS_VERSION_BOOTSTRAP=dev to build main instead." If no release has been published yet, set FELIS_VERSION_BOOTSTRAP=dev to build main instead."
fi
# A release IS its tag, so the stamp is final here and stamp_version leaves it be. # A release IS its tag, so the stamp is final here and stamp_version leaves it be.
FELIS_VERSION="$FELIS_REF" FELIS_VERSION="$FELIS_REF"
ok "release channel: ${FELIS_REF}" ok "release channel: ${FELIS_REF}"
+51
View File
@@ -3833,6 +3833,57 @@ expect "a FELIS_ARTIFACT_DIR with SHA256SUMS is accepted" "VALID" "$(run_vs "$ad
expect "FELIS_ARTIFACT_DIR and FELIS_REF together are refused" "DIE: FELIS_ARTIFACT_DIR and FELIS_REF both name what to install" "$(run_vs "$adir" 1)" expect "FELIS_ARTIFACT_DIR and FELIS_REF together are refused" "DIE: FELIS_ARTIFACT_DIR and FELIS_REF both name what to install" "$(run_vs "$adir" 1)"
rm -rf "$adir" rm -rf "$adir"
# --- FELIS_RELEASE installs one published release, from its assets --------------------------
# The way back to an earlier release. Without it a rerun resolves the newest release (and
# migrates again), and FELIS_REF builds from source; anything else that names what to install
# would silently win over it.
vrblock="$(awk '/^ if \[ -n "\$FELIS_RELEASE" \]; then$/ { f = 1 } f { print } f && /^ fi$/ { exit }' "$BS")"
case "$vrblock" in *"FELIS_SKIP_FETCH both"*) ;; *) echo "FAIL: the FELIS_RELEASE checks in validate_settings moved"; exit 1 ;; esac
run_vr() { # FELIS_RELEASE, with the other settings from the environment
FELIS_RELEASE="$1" bash -c 'die() { echo "DIE: $*"; exit 1; }
bootstrap_from_tui() { [ "${FELIS_BOOTSTRAP_FROM_TUI:-}" = 1 ]; }
FELIS_REF_PINNED="${FELIS_REF_PINNED:-}" FELIS_ARTIFACT_DIR="${FELIS_ARTIFACT_DIR:-}"
FELIS_VERSION_BOOTSTRAP="${FELIS_VERSION_BOOTSTRAP:-release}"
'"$vrblock"'
echo VALID' 2>&1
}
expect "a release tag is accepted" "VALID" "$(run_vr v1.2.3)"
for bad in 1.2.3 v1.2 main v1.2.3-rc1 'v1.2.3;id'; do
expect "FELIS_RELEASE=$bad is refused" "DIE: FELIS_RELEASE must be a release tag like v1.2.3 (got '$bad')" "$(run_vr "$bad")"
done
expect "FELIS_RELEASE and FELIS_REF together are refused" "DIE: FELIS_RELEASE and FELIS_REF both name what to install" "$(FELIS_REF_PINNED=1 run_vr v1.2.3)"
expect "FELIS_RELEASE and FELIS_ARTIFACT_DIR together are refused" "DIE: FELIS_RELEASE and FELIS_ARTIFACT_DIR both name what to install" "$(FELIS_ARTIFACT_DIR=/srv/assets run_vr v1.2.3)"
expect "FELIS_RELEASE and FELIS_SKIP_FETCH together are refused" "DIE: FELIS_RELEASE and FELIS_SKIP_FETCH both name what to install" "$(FELIS_SKIP_FETCH=1 run_vr v1.2.3)"
expect "FELIS_RELEASE on the dev channel is refused" "DIE: FELIS_RELEASE names a published release, which the dev channel does not install" "$(FELIS_VERSION_BOOTSTRAP=dev run_vr v1.2.3)"
expect "FELIS_RELEASE under felis setup is refused" "DIE: FELIS_RELEASE does not reach felis setup's install" "$(FELIS_BOOTSTRAP_FROM_TUI=1 run_vr v1.2.3)"
riblock="$(bsfn resolve_install_ref)"
[ -n "$riblock" ] && [ "$(printf '%s\n' "$riblock" | wc -l)" -lt 60 ] \
|| { echo "FAIL: no resolve_install_ref in $BS, or its closing brace moved"; exit 1; }
urblock="$(bsfn use_release_binary)"
run_ri() { # FELIS_RELEASE [published tags]
FELIS_RELEASE="$1" PUBLISHED="${2:-v1.2.3 v1.4.0}" bash -c 'die() { echo "DIE: $*"; exit 1; }
log() { :; }
ok() { echo "OK: $*"; }
github_latest_tag() { echo "LOOKED UP THE NEWEST" >&2; echo v1.4.0; }
load_release_json() { case " $PUBLISHED " in *" $1 "*) ;; *) return 1 ;; esac; }
FELIS_REF="" FELIS_REF_PINNED="" FELIS_VERSION_BOOTSTRAP=release
'"$riblock"'
'"$urblock"'
resolve_install_ref
use_release_binary && echo "FROM ASSETS" || echo "FROM SOURCE"
echo "ref=$FELIS_REF version=$FELIS_VERSION"' 2>&1
}
out="$(run_ri v1.2.3)"
expect "a named release is what gets installed" "ref=v1.2.3 version=v1.2.3" "$out"
expect "from its published assets" "FROM ASSETS" "$out"
case "$out" in
*"LOOKED UP THE NEWEST"*) echo "FAIL a named release still looked up the newest"; fails=$((fails + 1)) ;;
*) echo "PASS a named release does not look up the newest" ;;
esac
expect "an unpublished release stops the install" "DIE: could not find the published Felis release v1.3.9" "$(run_ri v1.3.9)"
expect "without one the newest release is installed" "ref=v1.4.0 version=v1.4.0" "$(run_ri "")"
# --- the setup screens' credentials come back from /etc/felis --------------------------- # --- the setup screens' credentials come back from /etc/felis ---------------------------
# `felis setup` keeps the relay password and the uploads bucket's keys in /etc/felis as # `felis setup` keeps the relay password and the uploads bucket's keys in /etc/felis as
# well as in their Secrets. A reinstall, or a host rebuilt from a bundle's state/, starts # well as in their Secrets. A reinstall, or a host rebuilt from a bundle's state/, starts
+4
View File
@@ -110,6 +110,10 @@ Installing from the assets is **[VM-VERIFIED]** on CentOS Stream 9 aarch64 throu
`FELIS_ARTIFACT_DIR`: a fresh install and an upgrade over a release that built on the host `FELIS_ARTIFACT_DIR`: a fresh install and an upgrade over a release that built on the host
pulled no image and built nothing, and a rerun imported and uploaded nothing. Downloading them from a pulled no image and built nothing, and a rerun imported and uploaded nothing. Downloading them from a
release is [SH-TESTED] until a release publishes assets. release is [SH-TESTED] until a release publishes assets.
The release is the newest one unless `FELIS_RELEASE=<tag>` names an earlier one, which
installs from that release's assets the same way: the way back after a bad upgrade
(troubleshooting §16, "Roll back an upgrade that broke the database"), with the installer
read at that tag.
The installer builds on the host instead, installing Docker for it and stopping Docker once The installer builds on the host instead, installing Docker for it and stopping Docker once
the images are in the registry, when: the images are in the registry, when:
+22 -2
View File
@@ -2036,8 +2036,28 @@ kubectl -n felis scale deployment felis-api felis-operator --replicas=1
``` ```
Do **not** run `felis migrate up` here: the host binary is already the new Do **not** run `felis migrate up` here: the host binary is already the new
release and would re-apply the migrations you are rolling back. Re-run the release and would re-apply the migrations you are rolling back. Bring the host
older installer version to bring the host binary back in line. back in line by installing the earlier release from its own assets, with the
installer read at that same tag (`v1.2.3` here):
```
curl -fsSL https://raw.githubusercontent.com/FelisMC/Felis/v1.2.3/deploy/bootstrap.sh \
| sudo FELIS_RELEASE=v1.2.3 bash
```
`FELIS_RELEASE` makes the installer install that release where it would
otherwise resolve the newest one, which would install the new release again and
re-apply its migrations. It is refused together with `FELIS_REF`,
`FELIS_ARTIFACT_DIR`, `FELIS_SKIP_FETCH` or `FELIS_VERSION_BOOTSTRAP=dev`, and
an unpublished tag stops the install before anything changes. [SH-TESTED]
- An installer older than `FELIS_RELEASE` ignores it and installs the newest.
`curl -fsSL <that URL> | grep -c FELIS_RELEASE` prints `0` for one of those;
run it with `FELIS_REF=v1.2.3` instead, which builds that tag from source
(slower, and it needs the build resources of §15c).
- While the repository is private, read the installer through the README's
token'd form with `?ref=v1.2.3` after `contents/deploy/bootstrap.sh`, and run
it as `sudo -E FELIS_RELEASE=v1.2.3 bash`.
### Whole-host disaster recovery: what comes back, and from where ### Whole-host disaster recovery: what comes back, and from where