From 2cf29c030a59261fef7dbe1104f5ee83d5e00493 Mon Sep 17 00:00:00 2001 From: Qiufeng Date: Tue, 18 Aug 2026 09:33:06 +0800 Subject: [PATCH] fix: recover stalled update requests --- README.md | 2 +- deploy/systemd/kaidi-update.service | 2 +- deploy/update.sh | 18 +++++++++++++++--- scripts/test-install-fixture.sh | 2 ++ scripts/test-update-fixture.sh | 21 +++++++++++++++++++-- 5 files changed, 38 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 601af5e..ee1b7e5 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ PostgreSQL 18 兼容工作继续冻结。 先在宝塔面板停止并删除当前错误的 Java 项目,再执行: ```bash -curl -fsSL https://git.awaioi.com/ERP-Team/kaidi/releases/download/v1.0.0-preview.28/install.sh | sudo env KAIDI_APP_PORT=18080 bash +curl -fsSL https://git.awaioi.com/ERP-Team/kaidi/releases/download/v1.0.0-preview.29/install.sh | sudo env KAIDI_APP_PORT=18080 bash ``` 该命令只安装程序运行所需的 systemd 单元,自动创建 `kaidi` 用户并检测现有 Java 17(包括 diff --git a/deploy/systemd/kaidi-update.service b/deploy/systemd/kaidi-update.service index 3b88c45..3f2f82a 100644 --- a/deploy/systemd/kaidi-update.service +++ b/deploy/systemd/kaidi-update.service @@ -18,5 +18,5 @@ IOSchedulingPriority=6 PrivateTmp=true ProtectHome=true ProtectSystem=full -ReadWritePaths=/opt/kaidi /www/wwwroot/kaidi /var/lib/kaidi /var/lib/kaidi-update /var/log/kaidi /etc/systemd/system +ReadWritePaths=/opt/kaidi -/www/wwwroot/kaidi /var/lib/kaidi /var/lib/kaidi-update /var/log/kaidi /etc/systemd/system UMask=0077 diff --git a/deploy/update.sh b/deploy/update.sh index 2e1c45d..aa29063 100755 --- a/deploy/update.sh +++ b/deploy/update.sh @@ -62,6 +62,9 @@ case "$RUNTIME_ENV_FILE" in esac bootstrap_die() { + if [ -f "$PROCESSING_FILE" ] && [ ! -L "$PROCESSING_FILE" ]; then + fail "$1" + fi printf '%s\n' "$1" >&2 exit 1 } @@ -174,6 +177,15 @@ prepare_update_layout() { || bootstrap_die "Private update directories could not be prepared" } +prepare_queue_layout() { + if [ -d "$PROCESSING_DIR" ] && [ ! -L "$PROCESSING_DIR" ] \ + && [ -d "$FAILED_REQUEST_ROOT" ] && [ ! -L "$FAILED_REQUEST_ROOT" ]; then + return 0 + fi + install -d -o root -g root -m 0700 "$PROCESSING_DIR" "$FAILED_REQUEST_ROOT" \ + || bootstrap_die "Private update queue directories could not be prepared" +} + secure_release_tree() { release_dir=$1 chown -R root:"$SERVICE_GROUP" "$release_dir" || return 1 @@ -197,9 +209,6 @@ verify_release_access() { fi } -prepare_update_layout -load_runtime_database_env - # shellcheck disable=SC2329 # Invoked by the EXIT trap below. cleanup() { rc=$? @@ -616,6 +625,7 @@ validate_release_operations() { "$WORK_DIR/extracted/ops/kaidi-update.path" >/dev/null || return 1 } +prepare_queue_layout exec 9>"$LOCK_FILE" flock -n 9 || exit 0 recover_interrupted_transaction @@ -650,6 +660,8 @@ TARGET_VERSION=$REQUESTED_VERSION REQUEST_ACTION=$(jq -er '(.action // "INSTALL") | strings | ascii_upcase | select(. == "DOWNLOAD" or . == "INSTALL")' "$PROCESSING_FILE") \ || fail "Update request action is invalid" +prepare_update_layout +load_runtime_database_env [ -z "$RELEASE_TOKEN" ] || { [ "${#RELEASE_TOKEN}" -le 512 ] \ && ! printf '%s' "$RELEASE_TOKEN" | grep -q '[[:cntrl:]]'; } \ || fail "UPDATE_RELEASE_TOKEN is invalid" diff --git a/scripts/test-install-fixture.sh b/scripts/test-install-fixture.sh index 1aee987..47a6c45 100755 --- a/scripts/test-install-fixture.sh +++ b/scripts/test-install-fixture.sh @@ -319,6 +319,8 @@ grep -Fqx 'StartLimitBurst=3' "$ROOT/deploy/systemd/kaidi-finance.service" \ || fail 'application service no longer has a bounded restart burst' grep -Fqx 'Restart=no' "$ROOT/deploy/systemd/kaidi-update.service" \ || fail 'update service can automatically repeat a failed switching transaction' +grep -Fq -- '-/www/wwwroot/kaidi' "$ROOT/deploy/systemd/kaidi-update.service" \ + || fail 'update service requires the Baota application path on a systemd-only installation' ! grep -Fq '/var/lib/kaidi-update/processing' "$ROOT/deploy/systemd/kaidi-update.path" \ || fail 'update path can automatically repeat a claimed switching transaction' # shellcheck disable=SC2016 # Match literal installer source. diff --git a/scripts/test-update-fixture.sh b/scripts/test-update-fixture.sh index 8ec48da..1b47dc4 100755 --- a/scripts/test-update-fixture.sh +++ b/scripts/test-update-fixture.sh @@ -26,18 +26,35 @@ for version in 01.2.3 1.02.3 1.2.03 1.2.3-01 1.2.3-alpha..1; do done missing_service_user="kaidi-fixture-missing-$$" +mkdir -p "$WORK/bootstrap/app" "$WORK/bootstrap/state/inbox" \ + "$WORK/bootstrap/state/processing" "$WORK/bootstrap/state/failed" "$WORK/bootstrap/log" \ + "$WORK/bootstrap/bin" +cat > "$WORK/bootstrap/bin/flock" <<'SH' +#!/bin/sh +exit 0 +SH +chmod 0755 "$WORK/bootstrap/bin/flock" +jq -n \ + '{action:"DOWNLOAD",version:"1.0.0-preview.2",reason:"bootstrap fixture"}' \ + > "$WORK/bootstrap/state/inbox/request.json" if KAIDI_APP_ROOT="$WORK/bootstrap/app" \ KAIDI_UPDATE_STATE_ROOT="$WORK/bootstrap/state" \ KAIDI_LOG_ROOT="$WORK/bootstrap/log" \ KAIDI_SERVICE_USER="$missing_service_user" \ KAIDI_SERVICE_GROUP="$missing_service_user" \ + PATH="$WORK/bootstrap/bin:$PATH" \ sh "$ROOT/deploy/update.sh" > "$WORK/bootstrap.log" 2>&1; then fail 'updater accepted a missing service identity during bootstrap' fi grep -Fq "Service user $missing_service_user is missing" "$WORK/bootstrap.log" \ || fail 'updater bootstrap failure did not preserve its diagnostic' -! grep -Eq 'No such file|nonexistent directory|cannot create' "$WORK/bootstrap.log" \ - || fail 'updater bootstrap failure was masked by an unavailable status directory' +[ "$(jq -r '.state' "$WORK/bootstrap/state/status.json")" = FAILED ] \ + || fail 'updater bootstrap failure did not persist FAILED' +[ ! -e "$WORK/bootstrap/state/inbox/request.json" ] \ + && [ ! -e "$WORK/bootstrap/state/processing/request.json" ] \ + || fail 'updater bootstrap failure left a request permanently queued' +find "$WORK/bootstrap/state/failed" -type f -name 'request-*.json' -print -quit | grep -q . \ + || fail 'updater bootstrap failure did not archive the claimed request' write_mock_commands() { local mock_bin=$1