diff --git a/.github/workflows/dev-release.yml b/.github/workflows/dev-release.yml index 3c24daf..7904b78 100644 --- a/.github/workflows/dev-release.yml +++ b/.github/workflows/dev-release.yml @@ -210,6 +210,7 @@ jobs: --image-tag "${IMAGE_NAME}:dev" \ --base-url "${dev_base_url}" \ --installer-release-url "${dev_base_url}/release.json" \ + --installer-update-release-url "${dev_base_url}/release.json" \ --installer-compose-url "${dev_base_url}/docker-compose.image.yml" \ --installer-manager-url "${dev_base_url}/subboost-manager" \ --installer-image "${IMAGE_NAME}:dev" diff --git a/local/scripts/install.sh b/local/scripts/install.sh index 1548d12..c4c58a6 100644 --- a/local/scripts/install.sh +++ b/local/scripts/install.sh @@ -4,6 +4,7 @@ set -Eeuo pipefail DEFAULT_HOME="/opt/subboost" DEFAULT_BIN="/usr/local/bin/subboost" DEFAULT_RELEASE_URL="https://github.com/SubBoost/subboost/releases/latest/download/release.json" +DEFAULT_UPDATE_RELEASE_URL="https://github.com/SubBoost/subboost/releases/latest/download/release.json" DEFAULT_COMPOSE_URL="https://github.com/SubBoost/subboost/releases/latest/download/docker-compose.image.yml" DEFAULT_MANAGER_URL="https://github.com/SubBoost/subboost/releases/latest/download/subboost-manager" DEFAULT_IMAGE="ghcr.io/subboost/subboost:latest" @@ -11,6 +12,7 @@ DEFAULT_IMAGE="ghcr.io/subboost/subboost:latest" SUBBOOST_HOME="${SUBBOOST_HOME:-$DEFAULT_HOME}" SUBBOOST_BIN="${SUBBOOST_BIN:-$DEFAULT_BIN}" SUBBOOST_RELEASE_URL="${SUBBOOST_RELEASE_URL:-$DEFAULT_RELEASE_URL}" +SUBBOOST_UPDATE_RELEASE_URL="${SUBBOOST_UPDATE_RELEASE_URL:-$DEFAULT_UPDATE_RELEASE_URL}" SUBBOOST_ASSUME_YES="${SUBBOOST_ASSUME_YES:-0}" SUBBOOST_DRY_RUN="${SUBBOOST_DRY_RUN:-0}" @@ -563,6 +565,7 @@ main() { say "[dry-run] image=$image" say "[dry-run] composeUrl=$compose_url" say "[dry-run] managerUrl=$manager_url" + say "[dry-run] updateReleaseUrl=$SUBBOOST_UPDATE_RELEASE_URL" exit 0 fi @@ -576,7 +579,7 @@ main() { if [ -f "$ENV_FILE" ]; then existing_env="1"; fi set_env_value SUBBOOST_IMAGE "$image" - set_env_value SUBBOOST_RELEASE_URL "$SUBBOOST_RELEASE_URL" + set_env_value SUBBOOST_RELEASE_URL "$SUBBOOST_UPDATE_RELEASE_URL" set_env_value SUBBOOST_COMPOSE_URL "$compose_url" set_env_value SUBBOOST_MANAGER_URL "$manager_url" ensure_env_value POSTGRES_DB "subboost" diff --git a/local/scripts/selfhost-shell.test.ts b/local/scripts/selfhost-shell.test.ts index d9d8d25..cbe416c 100644 --- a/local/scripts/selfhost-shell.test.ts +++ b/local/scripts/selfhost-shell.test.ts @@ -286,6 +286,83 @@ ENV expect(result.stdout).toContain("curl_count=7"); }, 10_000); + it("uses refreshed release metadata before pulling during update", () => { + const script = ` + set -Eeuo pipefail + home="$(mktemp -d)" + trap 'rm -rf "$home"' EXIT + release_dir="$home/release" + mkdir -p "$release_dir" "$home/bin" + cat > "$release_dir/release.json" <<'JSON' +{"image":"new-image","composeUrl":"docker-compose.image.yml","managerUrl":"subboost-manager"} +JSON + printf 'services:\\n app:\\n image: \${SUBBOOST_IMAGE}\\n' > "$release_dir/docker-compose.image.yml" + printf '#!/usr/bin/env bash\\necho manager\\n' > "$release_dir/subboost-manager" + cat > "$home/.env" < "$home/docker-compose.yml" + export SUBBOOST_SCRIPT_SOURCE_ONLY=1 + export SUBBOOST_HOME="$home" + export SUBBOOST_BIN="$home/bin/subboost" + export SUBBOOST_DOCTOR_HEALTH_ATTEMPTS=1 + export SUBBOOST_DOCTOR_HEALTH_INTERVAL_SECONDS=0 + source local/scripts/subboost.sh + sudo_do() { "$@"; } + install_secret_file() { cp "$1" "$2"; } + read_env_file() { cat "$ENV_FILE"; } + docker_log="$home/docker-log" + : > "$docker_log" + docker() { + if [ "$1" = "info" ]; then return 0; fi + if [ "$1" = "compose" ]; then + case "$*" in + "compose version"*) return 0 ;; + *" config") return 0 ;; + *" pull") + printf 'pull_image=%s\\n' "\${SUBBOOST_IMAGE:-}" >> "$docker_log" + return 0 + ;; + *" up -d --remove-orphans") return 0 ;; + *" up -d --no-deps --force-recreate app") return 0 ;; + *" ps -q app") printf 'app-id\\n'; return 0 ;; + *" ps -q db") printf 'db-id\\n'; return 0 ;; + *" ps -q cron") printf 'cron-id\\n'; return 0 ;; + esac + fi + if [ "$1" = "inspect" ]; then + case "$*" in + *".State.Status"*) printf 'running\\n'; return 0 ;; + *".State.Health"*) printf 'healthy\\n'; return 0 ;; + esac + fi + return 0 + } + curl() { return 0; } + update_cmd + cat "$docker_log" + cat "$home/.env" + `; + + const result = runBash(script); + + expect(result.status).toBe(0); + expect(result.stdout).toContain("pull_image=new-image"); + expect(result.stdout).toContain("SUBBOOST_IMAGE=new-image"); + expect(result.stdout).toContain("SUBBOOST_COMPOSE_URL=file://"); + expect(result.stdout).toContain("SUBBOOST_MANAGER_URL=file://"); + }, 10_000); + it("updates exact env keys without removing similarly prefixed names", () => { const script = ` set -Eeuo pipefail diff --git a/local/scripts/subboost.sh b/local/scripts/subboost.sh index 6fd43b5..9adebfa 100644 --- a/local/scripts/subboost.sh +++ b/local/scripts/subboost.sh @@ -131,6 +131,13 @@ write_env_value() { install_secret_file "$tmp" "$ENV_FILE" } +write_runtime_env_value() { + local key="$1" + local value="$2" + write_env_value "$key" "$value" + export "$key=$value" +} + install_file_from_url() { local url="$1" local destination="$2" @@ -278,14 +285,14 @@ update_cmd() { image="$(json_get image "$release_file" || true)" compose_url="$(resolve_url "$release_url" "$(json_get composeUrl "$release_file" || true)")" manager_url="$(resolve_url "$release_url" "$(json_get managerUrl "$release_file" || true)")" - if [ -n "$image" ]; then write_env_value SUBBOOST_IMAGE "$image"; fi + if [ -n "$image" ]; then write_runtime_env_value SUBBOOST_IMAGE "$image"; fi if [ -n "$compose_url" ]; then install_file_from_url "$compose_url" "$COMPOSE_FILE" 644 - write_env_value SUBBOOST_COMPOSE_URL "$compose_url" + write_runtime_env_value SUBBOOST_COMPOSE_URL "$compose_url" fi if [ -n "$manager_url" ]; then install_file_from_url "$manager_url" "${SUBBOOST_BIN:-/usr/local/bin/subboost}" 755 - write_env_value SUBBOOST_MANAGER_URL "$manager_url" + write_runtime_env_value SUBBOOST_MANAGER_URL "$manager_url" fi else say "Release manifest unavailable; updating current image and compose only." diff --git a/scripts/selfhost-release-assets.cjs b/scripts/selfhost-release-assets.cjs index c1e6a3e..5f32392 100644 --- a/scripts/selfhost-release-assets.cjs +++ b/scripts/selfhost-release-assets.cjs @@ -14,6 +14,13 @@ const INSTALLER_DEFAULTS = [ name: "DEFAULT_RELEASE_URL", value: "https://github.com/SubBoost/subboost/releases/latest/download/release.json", }, + { + arg: "--installer-update-release-url", + env: "SUBBOOST_INSTALLER_UPDATE_RELEASE_URL", + key: "installerUpdateReleaseUrl", + name: "DEFAULT_UPDATE_RELEASE_URL", + value: "https://github.com/SubBoost/subboost/releases/latest/download/release.json", + }, { arg: "--installer-compose-url", env: "SUBBOOST_INSTALLER_COMPOSE_URL", @@ -40,7 +47,7 @@ const INSTALLER_DEFAULTS = [ function usage() { return [ "Usage:", - " node scripts/selfhost-release-assets.cjs [--output ] [--image ] [--image-tag ] [--base-url ] [--tag ] [--build-sha ] [--installer-release-url ] [--installer-compose-url ] [--installer-manager-url ] [--installer-image ] [--dry-run]", + " node scripts/selfhost-release-assets.cjs [--output ] [--image ] [--image-tag ] [--base-url ] [--tag ] [--build-sha ] [--installer-release-url ] [--installer-update-release-url ] [--installer-compose-url ] [--installer-manager-url ] [--installer-image ] [--dry-run]", "", "Creates release.json, install.sh, docker-compose.image.yml, and subboost-manager for GitHub Release assets.", ].join("\n");