fix(installer): replace a stale installer before upgrading (#3235)

This commit is contained in:
Hampus
2026-10-05 21:50:16 +02:00
committed by GitHub
parent 22b8f5454b
commit 6618a6baf4
4 changed files with 163 additions and 0 deletions
@@ -2198,6 +2198,7 @@ async function verifyInstallerExecution(installerRoot: string): Promise<Array<st
env: { env: {
...process.env, ...process.env,
PATH: `${stubBin}${path.delimiter}${process.env.PATH ?? ''}`, PATH: `${stubBin}${path.delimiter}${process.env.PATH ?? ''}`,
FLUXER_INSTALLER_REFRESHED: '1',
...(cwd == null ? {} : {PWD: cwd}), ...(cwd == null ? {} : {PWD: cwd}),
}, },
}); });
@@ -128,6 +128,8 @@ Removing the line from `.env` also lets the upgrade run, and it is the wrong fix
Keep the installer beside the stack files. [Get started](/operator/get-started/#step-4-bring-up-the-instance) has the download and the checksum check for a fresh copy. Keep the installer beside the stack files. [Get started](/operator/get-started/#step-4-bring-up-the-instance) has the download and the checksum check for a fresh copy.
`--update` first compares the installer against the digest at `https://fluxer.dev/install.sh.sha256`. When the copy differs, it downloads the current one, checks it against that digest, and asks before it replaces the copy and runs it with the same options. Answering no, `--non-interactive`, or a run without a terminal stops before anything changes. On Windows the same check reads `https://fluxer.dev/install.ps1.sha256`.
See the plan first: See the plan first:
```bash ```bash
+75
View File
@@ -59,11 +59,17 @@ param(
[string[]]$Rest = @() [string[]]$Rest = @()
) )
$FluxerScriptArguments = @{}
foreach ($entry in $PSBoundParameters.GetEnumerator()) {
$FluxerScriptArguments[$entry.Key] = $entry.Value
}
Set-StrictMode -Version Latest Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop' $ErrorActionPreference = 'Stop'
$ProgressPreference = 'SilentlyContinue' $ProgressPreference = 'SilentlyContinue'
$FluxerRawBase = 'https://raw.githubusercontent.com/fluxerapp/fluxer' $FluxerRawBase = 'https://raw.githubusercontent.com/fluxerapp/fluxer'
$FluxerInstallerUrl = 'https://fluxer.dev/install.ps1'
$FluxerStackPath = 'deploy/self-hosting' $FluxerStackPath = 'deploy/self-hosting'
$FluxerHealthPath = '/_health' $FluxerHealthPath = '/_health'
$FluxerInitService = 'seaweedfs-init' $FluxerInitService = 'seaweedfs-init'
@@ -1980,6 +1986,71 @@ function Assert-FluxerComposeFiles([string]$TargetDir, [string]$EnvPath) {
} }
} }
function Get-FluxerSha256([string]$Path) {
return (Get-FileHash -LiteralPath $Path -Algorithm SHA256).Hash.ToLowerInvariant()
}
function Update-FluxerInstaller {
if ($env:FLUXER_INSTALLER_REFRESHED) {
return
}
$self = $PSCommandPath
if (-not $self) {
return
}
$selfDir = Split-Path -Parent $self
if ($DryRun) {
$staging = New-FluxerStagingDirectory ([System.IO.Path]::GetTempPath())
} else {
$staging = New-FluxerStagingDirectory $selfDir
}
try {
$digestPath = Join-Path $staging 'install.ps1.sha256'
try {
Invoke-WebRequest -Uri "$FluxerInstallerUrl.sha256" -OutFile $digestPath -UseBasicParsing -MaximumRedirection 5 -TimeoutSec 60
} catch {
Write-FluxerLine "Could not reach $FluxerInstallerUrl.sha256, so this run goes on with $self."
return
}
$published = ([System.IO.File]::ReadAllText($digestPath).Trim() -split '\s+')[0].ToLowerInvariant()
if ($published -notmatch '^[0-9a-f]{64}$') {
Stop-Fluxer "$FluxerInstallerUrl.sha256 holds no sha256 digest. Nothing was changed." $FluxerExitDownload
}
if ((Get-FluxerSha256 $self) -eq $published) {
return
}
$fresh = Join-Path $staging 'install.ps1'
try {
Invoke-WebRequest -Uri $FluxerInstallerUrl -OutFile $fresh -UseBasicParsing -MaximumRedirection 5 -TimeoutSec 120
} catch {
Stop-Fluxer "Download failed for $FluxerInstallerUrl. Nothing was changed." $FluxerExitDownload
}
if ((Get-FluxerSha256 $fresh) -ne $published) {
Stop-Fluxer "$FluxerInstallerUrl does not match the digest in $FluxerInstallerUrl.sha256. Nothing was changed." $FluxerExitDownload
}
Write-FluxerLine "$self differs from the installer $FluxerInstallerUrl serves. The stack files an upgrade downloads can require .env keys that only the current installer writes."
if ($DryRun) {
Write-FluxerLine 'The run asks to replace it with the current installer before it changes anything. The plan below is the one this copy would follow.'
return
}
if ($NonInteractive -or [Console]::IsInputRedirected) {
Stop-Fluxer "Nothing was changed. Download the current installer and run it:`n Invoke-WebRequest -Uri $FluxerInstallerUrl -OutFile install.ps1 -UseBasicParsing" $FluxerExitRefused
}
$answer = Read-Host -Prompt "Replace $self with the current installer and run that? [y/N]"
if ($null -eq $answer -or $answer.Trim() -notmatch '^(y|yes)$') {
Stop-Fluxer "Kept $self. Nothing was changed. Read the current installer at $FluxerInstallerUrl and run it once it is in place." $FluxerExitRefused
}
Move-Item -LiteralPath $fresh -Destination $self -Force
} finally {
Remove-FluxerStagingDirectory $staging
}
Write-FluxerLine "Replaced $self. Running it."
$env:FLUXER_INSTALLER_REFRESHED = '1'
$global:LASTEXITCODE = 0
& $self @FluxerScriptArguments
exit $LASTEXITCODE
}
function Invoke-FluxerInstall { function Invoke-FluxerInstall {
if ($Help) { if ($Help) {
Show-FluxerUsage Show-FluxerUsage
@@ -2022,6 +2093,10 @@ function Invoke-FluxerInstall {
Invoke-FluxerPreflight Invoke-FluxerPreflight
if ($Update) {
Update-FluxerInstaller
}
$targetPath = $Dir $targetPath = $Dir
$adoptedCwd = $false $adoptedCwd = $false
$fellBack = $false $fellBack = $false
+85
View File
@@ -51,6 +51,7 @@ LC_ALL=C
export LC_ALL export LC_ALL
FLUXER_RAW_BASE='https://raw.githubusercontent.com/fluxerapp/fluxer' FLUXER_RAW_BASE='https://raw.githubusercontent.com/fluxerapp/fluxer'
FLUXER_INSTALLER_URL='https://fluxer.dev/install.sh'
FLUXER_STACK_PATH='deploy/self-hosting' FLUXER_STACK_PATH='deploy/self-hosting'
FLUXER_MIN_ENGINE='24.0.0' FLUXER_MIN_ENGINE='24.0.0'
# Podman numbers its releases on its own scale, so the Docker Engine floor says # Podman numbers its releases on its own scale, so the Docker Engine floor says
@@ -305,6 +306,18 @@ opt_no_volume_compression=0
opt_skip_backup=0 opt_skip_backup=0
opt_allow_root=0 opt_allow_root=0
fluxer_self=''
if [ -f "$0" ]; then
case $0 in
/*) fluxer_self=$0 ;;
*) fluxer_self="$(pwd)/$0" ;;
esac
fi
fluxer_self_args=''
for fluxer_arg in "$@"; do
fluxer_self_args="$fluxer_self_args '$(printf '%s' "$fluxer_arg" | sed "s/'/'\\\\''/g")'"
done
while [ $# -gt 0 ]; do while [ $# -gt 0 ]; do
case $1 in case $1 in
--domain) --domain)
@@ -651,6 +664,74 @@ fluxer_prompt() {
return 1 return 1
} }
fluxer_sha256() {
openssl dgst -sha256 < "$1" | awk '{print $NF}'
}
fluxer_drop_scratch() {
fluxer_cleanup
fluxer_scratch=''
}
fluxer_refresh_installer() {
[ -z "${FLUXER_INSTALLER_REFRESHED:-}" ] || return 0
[ -n "$fluxer_self" ] || return 0
fluxer_self_dir=$(dirname "$fluxer_self")
if [ "$opt_dry_run" -eq 1 ]; then
fluxer_open_scratch "${TMPDIR:-/tmp}"
elif [ -w "$fluxer_self_dir" ]; then
fluxer_open_scratch "$fluxer_self_dir"
else
fluxer_say "$fluxer_self_dir is not writable, so this run cannot check $fluxer_self against $FLUXER_INSTALLER_URL and goes on with it."
return 0
fi
if ! curl -fsSL --proto '=https' --tlsv1.2 -o "$fluxer_scratch/install.sh.sha256" "$FLUXER_INSTALLER_URL.sha256"; then
fluxer_say "Could not reach $FLUXER_INSTALLER_URL.sha256, so this run goes on with $fluxer_self."
fluxer_drop_scratch
return 0
fi
fluxer_published=$(awk 'NR == 1 {print $1}' "$fluxer_scratch/install.sh.sha256")
case $fluxer_published in
''|*[!0-9a-f]*) fluxer_fail 4 "$FLUXER_INSTALLER_URL.sha256 holds no sha256 digest. Nothing was changed." ;;
esac
if [ "$(fluxer_sha256 "$fluxer_self")" = "$fluxer_published" ]; then
fluxer_drop_scratch
return 0
fi
if ! curl -fsSL --proto '=https' --tlsv1.2 -o "$fluxer_scratch/install.sh" "$FLUXER_INSTALLER_URL"; then
fluxer_fail 4 "Download failed for $FLUXER_INSTALLER_URL. Nothing was changed."
fi
if [ "$(fluxer_sha256 "$fluxer_scratch/install.sh")" != "$fluxer_published" ]; then
fluxer_fail 4 "$FLUXER_INSTALLER_URL does not match the digest in $FLUXER_INSTALLER_URL.sha256. Nothing was changed."
fi
fluxer_say "$fluxer_self differs from the installer $FLUXER_INSTALLER_URL serves. The stack files an upgrade downloads can require .env keys that only the current installer writes."
if [ "$opt_dry_run" -eq 1 ]; then
fluxer_say 'The run asks to replace it with the current installer before it changes anything. The plan below is the one this copy would follow.'
fluxer_drop_scratch
return 0
fi
if [ "$opt_non_interactive" -eq 1 ] || [ ! -t 0 ]; then
fluxer_fail 3 "Nothing was changed. Download the current installer and run it:
curl -fsSLO $FLUXER_INSTALLER_URL"
fi
printf 'Replace %s with the current installer and run that? [y/N] ' "$fluxer_self" >&2
fluxer_answer=''
read -r fluxer_answer || true
case $fluxer_answer in
y|Y|yes|Yes|YES) ;;
*) fluxer_fail 3 "Kept $fluxer_self. Nothing was changed. Read the current installer at $FLUXER_INSTALLER_URL and run it once it is in place." ;;
esac
if [ -x "$fluxer_self" ]; then
chmod +x "$fluxer_scratch/install.sh"
fi
mv "$fluxer_scratch/install.sh" "$fluxer_self"
fluxer_drop_scratch
fluxer_say "Replaced $fluxer_self. Running it."
FLUXER_INSTALLER_REFRESHED=1
export FLUXER_INSTALLER_REFRESHED
eval "exec sh \"\$fluxer_self\" $fluxer_self_args"
}
fluxer_resolve_values() { fluxer_resolve_values() {
if [ "$opt_update" -eq 1 ] || [ "$opt_rollback" -eq 1 ]; then if [ "$opt_update" -eq 1 ] || [ "$opt_rollback" -eq 1 ]; then
return 0 return 0
@@ -2100,6 +2181,10 @@ fluxer_preflight
fluxer_validate_options fluxer_validate_options
fluxer_resolve_values fluxer_resolve_values
if [ "$opt_update" -eq 1 ]; then
fluxer_refresh_installer
fi
if [ "$opt_update" -eq 1 ] || [ "$opt_rollback" -eq 1 ]; then if [ "$opt_update" -eq 1 ] || [ "$opt_rollback" -eq 1 ]; then
fluxer_require_instance fluxer_require_instance
fluxer_resolve_ref fluxer_resolve_ref