feat: support Baota manual deployment and managed updates
Release / release (push) Canceled after 0s

This commit is contained in:
Qiufeng
2026-08-18 03:08:28 +08:00
parent fa4517c263
commit cfdc0fed44
13 changed files with 765 additions and 269 deletions
+3 -1
View File
@@ -92,13 +92,15 @@ cp -R "$ROOT/frontend/dist/." "$STAGE/public/"
printf '%s\n' "$VERSION" > "$STAGE/VERSION"
cp "$ROOT/deploy/update.sh" "$STAGE/ops/update.sh"
cp "$ROOT/deploy/baota-start.sh" "$STAGE/ops/baota-start.sh"
cp "$ROOT/deploy/baota-init.sh" "$STAGE/ops/baota-init.sh"
cp "$ROOT/deploy/release-public.pem" "$STAGE/ops/release-public.pem"
cp "$ROOT/deploy/baota.env.example" "$STAGE/ops/baota.env.example"
# Kept in the archive so Preview.9's updater can complete the one-time transition.
cp "$ROOT/deploy/nginx/kaidi-finance.conf" "$STAGE/ops/kaidi-finance.conf"
cp "$ROOT/deploy/systemd/kaidi-finance.service" "$STAGE/ops/kaidi-finance.service"
cp "$ROOT/deploy/systemd/kaidi-update.service" "$STAGE/ops/kaidi-update.service"
cp "$ROOT/deploy/systemd/kaidi-update.path" "$STAGE/ops/kaidi-update.path"
chmod 0755 "$STAGE/ops/update.sh" "$STAGE/ops/baota-start.sh"
chmod 0755 "$STAGE/ops/update.sh" "$STAGE/ops/baota-start.sh" "$STAGE/ops/baota-init.sh"
ARTIFACT="kaidi-finance-$VERSION.tar.gz"
COPYFILE_DISABLE=1 tar --format=ustar -czf "$OUTPUT_DIR/$ARTIFACT" -C "$STAGE" .
+74 -20
View File
@@ -3,21 +3,51 @@ set -Eeuo pipefail
ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
WORK=$(mktemp -d)
trap 'rm -rf "$WORK"' EXIT
EXECUTION_MARKER=/tmp/kaidi-baota-fixture-executed-$$
trap 'rm -rf "$WORK" "$EXECUTION_MARKER"' EXIT
fail() {
printf 'Baota start fixture failed: %s\n' "$1" >&2
[ ! -f "$WORK/java.log" ] || sed -n '1,40p' "$WORK/java.log" >&2
exit 1
}
RELEASE="$WORK/release"
mkdir -p "$RELEASE/ops" "$RELEASE/public" "$WORK/files" "$WORK/tmp"
mkdir -p "$RELEASE/ops" "$RELEASE/public" "$WORK/files" "$WORK/tmp" "$WORK/state" "$WORK/mock-bin"
RELEASE_PHYSICAL=$(cd "$RELEASE" && pwd -P)
printf 'fixture jar\n' > "$RELEASE/app.jar"
printf '<!doctype html>\n' > "$RELEASE/public/index.html"
printf '1.0.0-preview.24\n' > "$RELEASE/VERSION"
printf '1.0.0-preview.25\n' > "$RELEASE/VERSION"
cp "$ROOT/deploy/baota-start.sh" "$RELEASE/ops/baota-start.sh"
chmod 0755 "$RELEASE/ops/baota-start.sh"
cat > "$WORK/mock-bin/awk" <<'SH'
#!/usr/bin/env bash
case "${2:-}" in
/proc/*/stat) printf '424242\n' ;;
*) exec /usr/bin/awk "$@" ;;
esac
SH
chmod 0755 "$WORK/mock-bin/awk"
cat > "$WORK/base.env" <<EOF
SPRING_PROFILES_ACTIVE="production"
SERVER_ADDRESS="127.0.0.1"
SERVER_PORT="18080"
FIELD_ENCRYPTION_KEY="fixture-field-key"
FILE_STORAGE_ROOT="$WORK/files"
FILE_STORAGE_TEMP="$WORK/tmp"
FINANCE_UPDATE_ENABLED="true"
KAIDI_PID_FILE="$WORK/state/kaidi.pid"
EOF
cat > "$WORK/runtime.env" <<EOF
FINANCE_SETUP_ENABLED="false"
SERVER_PORT="19090"
DB_URL="jdbc:mysql://db.fixture/kaidi_finance"
DB_USERNAME="fixture-user"
DB_PASSWORD='\$(touch $EXECUTION_MARKER)'
EOF
cat > "$WORK/java" <<'SH'
#!/usr/bin/env bash
set -Eeuo pipefail
@@ -29,39 +59,63 @@ set -Eeuo pipefail
printf 'port=%s\n' "$SERVER_PORT"
printf 'update=%s\n' "$FINANCE_UPDATE_ENABLED"
printf 'static=%s\n' "$FINANCE_STATIC_LOCATIONS"
printf 'username=%s\n' "$DB_USERNAME"
printf 'password=%s\n' "$DB_PASSWORD"
printf 'pid-record=%s\n' "$(cat "$KAIDI_PID_FILE")"
printf 'self-pid=%s\n' "$$"
} > "$MOCK_JAVA_LOG"
SH
chmod 0755 "$WORK/java"
MOCK_JAVA_LOG="$WORK/java.log" \
PATH="$WORK/mock-bin:$PATH" \
KAIDI_JAVA_BIN="$WORK/java" \
DB_URL='jdbc:mysql://db.fixture/kaidi_finance' \
DB_USERNAME=fixture \
DB_PASSWORD='fixture-password' \
FIELD_ENCRYPTION_KEY='fixture-field-key' \
FILE_STORAGE_ROOT="$WORK/files" \
FILE_STORAGE_TEMP="$WORK/tmp" \
KAIDI_CONFIG_FILE="$WORK/base.env" \
KAIDI_RUNTIME_ENV_FILE="$WORK/runtime.env" \
DB_URL='' DB_USERNAME='' DB_PASSWORD='' \
"$RELEASE/ops/baota-start.sh"
grep -Fqx "cwd=$RELEASE" "$WORK/java.log" || fail 'launcher did not use the release working directory'
grep -Fqx "args=-XX:MaxRAMPercentage=70 -Dfile.encoding=UTF-8 -jar $RELEASE/app.jar" "$WORK/java.log" \
|| fail 'launcher did not execute the packaged Spring Boot jar'
grep -Fqx 'version=1.0.0-preview.24' "$WORK/java.log" || fail 'launcher did not read VERSION'
grep -Fqx "cwd=$RELEASE_PHYSICAL" "$WORK/java.log" || fail 'launcher did not use the physical release directory'
grep -Fqx "args=-XX:MaxRAMPercentage=70 -Dfile.encoding=UTF-8 -Dkaidi.release.path=$RELEASE_PHYSICAL -Dkaidi.release.version=1.0.0-preview.25 -jar $RELEASE_PHYSICAL/app.jar" "$WORK/java.log" \
|| fail 'launcher did not identify and execute the packaged Spring Boot release'
grep -Fqx 'version=1.0.0-preview.25' "$WORK/java.log" || fail 'launcher did not read VERSION'
grep -Fqx 'address=127.0.0.1' "$WORK/java.log" || fail 'launcher did not default to loopback'
grep -Fqx 'port=18080' "$WORK/java.log" || fail 'launcher did not default to port 18080'
grep -Fqx 'update=false' "$WORK/java.log" || fail 'launcher did not disable the systemd updater'
grep -Fqx "static=file:$RELEASE/public/" "$WORK/java.log" \
grep -Fqx 'port=19090' "$WORK/java.log" || fail 'runtime environment did not override the base port'
grep -Fqx 'update=true' "$WORK/java.log" || fail 'launcher did not keep online update enabled'
grep -Fqx "static=file:$RELEASE_PHYSICAL/public/" "$WORK/java.log" \
|| fail 'launcher did not bind static resources to the release directory'
grep -Fqx 'username=fixture-user' "$WORK/java.log" || fail 'launcher did not load setup database values'
grep -Fqx "password=\$(touch $EXECUTION_MARKER)" "$WORK/java.log" \
|| fail 'launcher changed a literal password while parsing the protected environment'
[ ! -e "$EXECUTION_MARKER" ] || fail 'launcher executed command syntax from a database password'
PID_RECORD=$(sed -n 's/^pid-record=//p' "$WORK/java.log")
SELF_PID=$(sed -n 's/^self-pid=//p' "$WORK/java.log")
[ "$PID_RECORD" = "$SELF_PID 424242" ] || fail 'PID file did not bind the PID to its proc start time'
MOCK_JAVA_LOG="$WORK/explicit.log" \
PATH="$WORK/mock-bin:$PATH" \
KAIDI_JAVA_BIN="$WORK/java" \
KAIDI_CONFIG_FILE="$WORK/base.env" \
KAIDI_RUNTIME_ENV_FILE="$WORK/runtime.env" \
SERVER_PORT=20000 \
"$RELEASE/ops/baota-start.sh"
grep -Fqx 'port=20000' "$WORK/explicit.log" || fail 'non-empty Baota environment did not take precedence'
cat > "$WORK/missing-db.env" <<EOF
FIELD_ENCRYPTION_KEY="fixture-field-key"
FILE_STORAGE_ROOT="$WORK/files"
FILE_STORAGE_TEMP="$WORK/tmp"
KAIDI_PID_FILE="$WORK/state/missing.pid"
EOF
if KAIDI_JAVA_BIN="$WORK/java" \
DB_USERNAME=fixture DB_PASSWORD=fixture FIELD_ENCRYPTION_KEY=fixture \
FILE_STORAGE_ROOT="$WORK/files" FILE_STORAGE_TEMP="$WORK/tmp" \
KAIDI_CONFIG_FILE="$WORK/missing-db.env" \
KAIDI_RUNTIME_ENV_FILE="$WORK/absent.env" \
MOCK_JAVA_LOG="$WORK/missing-db.log" \
"$RELEASE/ops/baota-start.sh" > "$WORK/missing-db.out" 2>&1; then
fail 'launcher accepted a production configuration without DB_URL'
fi
grep -Fq 'DB_URL must be configured' "$WORK/missing-db.out" \
grep -Fq 'DB_URL is missing from the protected Kaidi configuration' "$WORK/missing-db.out" \
|| fail 'launcher did not report the missing database URL'
[ ! -e "$WORK/missing-db.log" ] || fail 'launcher started Java after configuration validation failed'
printf 'Baota Spring Boot launcher fixture passed\n'
printf 'Baota Spring Boot launcher configuration, PID identity, and injection fixtures passed\n'
+8 -4
View File
@@ -378,13 +378,17 @@ grep -Fq 'SERVER_ADDRESS "$SERVER_ADDRESS"' "$ROOT/deploy/install.sh" \
grep -Fq 'EnvironmentFile=-/var/lib/kaidi/setup/application.env' \
"$ROOT/deploy/systemd/kaidi-finance.service" \
|| fail 'application service no longer loads the setup-completion environment'
grep -Fq 'EnvironmentFile=-/var/lib/kaidi/setup/application.env' \
! grep -Fq 'EnvironmentFile=-/var/lib/kaidi/setup/application.env' \
"$ROOT/deploy/systemd/kaidi-update.service" \
|| fail 'update service no longer loads setup database overrides'
|| fail 'root update service must not load an application-owned environment file'
grep -Fq 'load_runtime_database_env' "$ROOT/deploy/update.sh" \
|| fail 'update script no longer imports only the setup database fields'
# shellcheck disable=SC2016 # Match literal installer source.
grep -Fq 'install -d -o root -g "$SERVICE_GROUP" -m 0750 "$UPDATE_STATE_ROOT"' "$ROOT/deploy/install.sh" \
|| fail 'update state parent is not group-accessible to the application user'
grep -Fq 'install.sh | sudo bash' "$ROOT/README.md" \
|| fail 'README does not document the public one-line setup-wizard install path'
grep -Fq 'kaidi-finance-1.0.0-preview.25.tar.gz' "$ROOT/README.md" \
|| fail 'README does not document the public manual-deployment artifact'
grep -Fq 'ops/baota-init.sh' "$ROOT/README.md" \
|| fail 'README does not document the local Baota initialization command'
printf 'Install configuration, custom port, public Gitea, optional private token, i686, setup wizard, and MySQL 8.4 fixtures passed\n'
+9 -5
View File
@@ -174,13 +174,20 @@ build_release() {
local unit_prefix=${3:-new}
local stage="$fixture/stage"
mkdir -p "$fixture/release" "$stage/public" "$stage/ops"
"$REAL_OPENSSL" genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 \
-out "$fixture/private.pem" >/dev/null 2>&1
"$REAL_OPENSSL" pkey -in "$fixture/private.pem" -pubout \
-out "$fixture/release-public.pem" >/dev/null 2>&1
printf 'new application\n' > "$stage/app.jar"
printf '<!doctype html><title>new</title>\n' > "$stage/public/index.html"
printf '%s\n' "$version" > "$stage/VERSION"
printf '#!/bin/sh\nprintf "new updater\\n"\n' > "$stage/ops/update.sh"
printf '#!/usr/bin/env bash\nexit 0\n' > "$stage/ops/baota-start.sh"
printf '#!/usr/bin/env bash\nexit 0\n' > "$stage/ops/baota-init.sh"
cp "$fixture/release-public.pem" "$stage/ops/release-public.pem"
chmod 0755 "$stage/ops/update.sh"
chmod 0755 "$stage/ops/baota-start.sh"
chmod 0755 "$stage/ops/baota-init.sh"
for name in kaidi-finance.service kaidi-update.service kaidi-update.path; do
printf '%s %s\n' "$unit_prefix" "$name" > "$stage/ops/$name"
done
@@ -192,10 +199,6 @@ build_release() {
jq -n --arg version "$version" --arg artifact "$artifact" --arg sha "$sha" \
'{version:$version,artifact:$artifact,sha256:$sha,publishedAt:"2026-08-16T00:00:00Z",releaseNotes:"fixture"}' \
> "$fixture/release/release-manifest.json"
"$REAL_OPENSSL" genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 \
-out "$fixture/private.pem" >/dev/null 2>&1
"$REAL_OPENSSL" pkey -in "$fixture/private.pem" -pubout \
-out "$fixture/release-public.pem" >/dev/null 2>&1
"$REAL_OPENSSL" dgst -sha256 -sign "$fixture/private.pem" \
-out "$fixture/release/release-manifest.sig" "$fixture/release/release-manifest.json"
}
@@ -284,6 +287,7 @@ run_update() {
KAIDI_LOG_ROOT="$fixture/log" \
KAIDI_SYSTEMD_ROOT="$fixture/systemd" \
KAIDI_UPDATER_PATH="$fixture/app/bin/update.sh" \
KAIDI_RUNTIME_ENV_FILE="$fixture/runtime.env" \
KAIDI_SKIP_DB_BACKUP="$skip_backup" \
KAIDI_UPDATE_HEALTH_ATTEMPTS=1 \
KAIDI_UPDATE_HEALTH_INTERVAL_SECONDS=0 \
@@ -424,7 +428,7 @@ assert_rollback_case() {
if run_update "$fixture" fail-new > "$fixture/update.log" 2>&1; then
fail 'rollback case unexpectedly succeeded'
fi
grep -q 'previous release was restored and verified' "$fixture/update.log" \
grep -q 'previous application release was restored and verified' "$fixture/update.log" \
|| fail 'rollback case did not report a complete restoration'
[ "$(readlink "$fixture/app/current")" = "$fixture/app/releases/1.0.0-preview.1" ] \
|| fail 'rollback case did not restore the previous application'
+5
View File
@@ -169,6 +169,8 @@ compare_archive_file() {
compare_archive_file ./ops/update.sh deploy/update.sh
compare_archive_file ./ops/baota-start.sh deploy/baota-start.sh
compare_archive_file ./ops/baota-init.sh deploy/baota-init.sh
compare_archive_file ./ops/release-public.pem deploy/release-public.pem
compare_archive_file ./ops/baota.env.example deploy/baota.env.example
compare_archive_file ./ops/kaidi-update.path deploy/systemd/kaidi-update.path
compare_archive_file ./ops/kaidi-update.service deploy/systemd/kaidi-update.service
@@ -176,7 +178,10 @@ compare_archive_file ./ops/kaidi-finance.service deploy/systemd/kaidi-finance.se
compare_archive_file ./ops/kaidi-finance.conf deploy/nginx/kaidi-finance.conf
tar -tvzf "$ARTIFACT" ./ops/baota-start.sh | grep -Eq '^-.{2}x.{2}x.{2}x' \
|| { printf 'Baota Spring Boot launcher is not executable\n' >&2; exit 1; }
tar -tvzf "$ARTIFACT" ./ops/baota-init.sh | grep -Eq '^-.{2}x.{2}x.{2}x' \
|| { printf 'Baota initializer is not executable\n' >&2; exit 1; }
bash -n "$ROOT/deploy/baota-start.sh"
bash -n "$ROOT/deploy/baota-init.sh"
cmp install.sh "$ROOT/deploy/install.sh" \
|| { printf 'Release installer does not match deploy/install.sh\n' >&2; exit 1; }
bash -n "$ROOT/deploy/purge.sh"