feat: add managed online updates
Signed Release / release (push) Successful in 9m24s

This commit is contained in:
Qiufeng
2026-08-04 20:05:50 +08:00
parent 4f2ea26e8e
commit d3892320dd
20 changed files with 1365 additions and 111 deletions
+46 -1
View File
@@ -202,6 +202,44 @@ test_stable_channel_rejects_prerelease_tag() (
! select_release "$tmp/release.json" '' "$tmp/selection" >/dev/null 2>&1
)
test_update_helper_preserves_release_metadata() (
local tmp
tmp="$(mktemp -d "${TMPDIR:-/tmp}/erp-update-metadata.XXXXXX")" || return 1
trap 'rm -rf "$tmp"' EXIT
mkdir -p "$tmp/state"
printf 'OA_UPDATE_GITEA_BASE_URL=https://example.test\nOA_UPDATE_STATE_FILE=%q\n' \
"$tmp/state/update.json" > "$tmp/erp.env"
printf '%s\n' \
'{"phase":"AVAILABLE","releaseNotes":"Important fixes","publishedAt":"2026-08-04T10:00:00Z",' \
'"assets":[{"name":"SHA256SUMS","downloadUrl":"https://example.test/sums","size":64}]}' \
> "$tmp/state/update.json"
ERP_INSTALL_ROOT="$tmp" ERP_CONFIG_FILE="$tmp/erp.env" source "$PROJECT_ROOT/distribution/bin/erp-update"
trap 'rm -rf "$tmp"' EXIT
write_state DOWNLOADING 25 'downloading' 1.2.3
python3 - "$tmp/state/update.json" <<'PY'
import json, sys
with open(sys.argv[1], encoding="utf-8") as handle:
state = json.load(handle)
assert state["phase"] == "DOWNLOADING"
assert state["releaseNotes"] == "Important fixes"
assert state["publishedAt"] == "2026-08-04T10:00:00Z"
assert state["assets"][0]["name"] == "SHA256SUMS"
PY
)
test_installer_builds_public_setup_urls() (
source "$PROJECT_ROOT/install.sh"
local normalized setup ipv6
normalized="$(normalize_public_url 'https://erp.example.com:8443/setup?tenant=kaidi#start')" || return 1
setup="$(append_setup_token "$normalized" 'one-time-token')" || return 1
ipv6="$(ip_setup_base_url '2001:db8::8' 8091)" || return 1
[[ "$setup" == 'https://erp.example.com:8443/setup?tenant=kaidi&token=one-time-token#start' ]] || return 1
[[ "$ipv6" == 'http://[2001:db8::8]:8091/' ]]
)
prepare_update_release_archive() {
local tmp="$1" version="$2"
local root="$tmp/package/kaidi-erp-$version"
@@ -316,6 +354,7 @@ PY
check_and_install_dependencies() { JAVA_BIN="$(command -v java)"; return 0; }
start_service() { return 0; }
wait_for_installer() { return 0; }
detect_public_address() { printf '203.0.113.10'; }
main > "$tmp/install.log" 2>&1 || return 1
@@ -328,7 +367,9 @@ PY
[[ "$OA_UPDATE_GITEA_BASE_URL" == "http://127.0.0.1:$port" ]]
[[ "$ERP_SETUP_TOKEN" =~ ^[0-9a-f]{64}$ ]]
[[ -z "${OA_DB_URL:-}" ]]
)
) || return 1
grep -Eq '^\[ERP Install\] Setup URL: http://203\.0\.113\.10:8091/\?token=[0-9a-f]{64}$' \
"$tmp/install.log"
)
run_update_scenario() (
@@ -683,6 +724,8 @@ test_release_workflow_uses_scoped_job_token() (
grep -Fq 'GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}' "$workflow"
grep -Fq 'uses: actions/setup-java@v4' "$workflow"
grep -Fq 'java-version: "17"' "$workflow"
grep -Fq "git log --no-merges --format='- %s (\`%h\`)'" "$workflow"
grep -Fq 'release-notes.md' "$workflow"
! grep -Fq 'RELEASE_GITEA_TOKEN' "$workflow"
! grep -Fq 'vars.GITEA_BASE_URL' "$workflow"
)
@@ -718,6 +761,8 @@ run_test 'erp-run preserves a failed pending installation' test_erp_run_preserve
run_test 'installer accepts a correctly signed archive' test_installer_verifies_signed_safe_archive
run_test 'installer rejects symlinks in release archives' test_installer_rejects_archive_symlinks
run_test 'stable update channel rejects prerelease tags' test_stable_channel_rejects_prerelease_tag
run_test 'update helper preserves release metadata' test_update_helper_preserves_release_metadata
run_test 'installer builds public setup URLs safely' test_installer_builds_public_setup_urls
run_test 'installer downloads and installs a signed release end to end' test_installer_downloads_and_installs_signed_release
run_test 'update helper installs a healthy release end to end' test_update_helper_installs_healthy_release
run_test 'update helper rolls back an unhealthy release end to end' test_update_helper_rolls_back_unhealthy_release