fix(installer): resolve the compose file name Compose loads (#2649)

This commit is contained in:
Hampus
2026-09-11 00:38:09 +02:00
committed by GitHub
parent 0421c86039
commit f97841a58f
3 changed files with 277 additions and 50 deletions
+75 -3
View File
@@ -1314,6 +1314,33 @@ console.log('self-hosting guide against deploy/self-hosting');
}
}
const COMPOSE_DEFAULT_NAMES = ['compose.yaml', 'compose.yml', 'docker-compose.yml', 'docker-compose.yaml'];
const shellComposeNames = shellRows('fluxer_compose_names', 'NAMES', 'the install.sh compose name list');
const powershellComposeNames: Array<string> = [];
for (const row of powershellRows('FluxerComposeNames', 'the install.ps1 compose name list')) {
const parsed = row.match(/^\s*'([^']+)'\s*$/u);
if (parsed == null) {
problems.push(`the install.ps1 compose name list carries \`${row.trim()}\`, which is not a quoted file name`);
continue;
}
powershellComposeNames.push(parsed[1]);
}
for (const [script, list] of [
['install.sh', shellComposeNames],
['install.ps1', powershellComposeNames],
] as const) {
if (list.join(', ') !== COMPOSE_DEFAULT_NAMES.join(', ')) {
problems.push(
`${script} resolves compose files as [${list.join(', ')}] and Compose resolves them as [${COMPOSE_DEFAULT_NAMES.join(', ')}]`,
);
}
}
if (!shellStackFiles.includes('docker-compose.yml') || !COMPOSE_DEFAULT_NAMES.includes('docker-compose.yml')) {
problems.push(
'the installers no longer download docker-compose.yml, so the name they map onto an instance is unclear',
);
}
const PIPE_TO_SHELL =
/(?:curl|wget|iwr|Invoke-WebRequest)[^\n|]*\|\s*(?:sudo\s+)?(?:sh|bash|zsh|iex|Invoke-Expression)\b/iu;
const docsPages = await walk(DOCS_ROOT);
@@ -1419,10 +1446,15 @@ console.log('self-hosting guide against deploy/self-hosting');
await writeFile(path.join(instance, '.env'), 'FLUXER_DOMAIN=x.example\nFLUXER_IMAGE_TAG=2026.813.205040\n');
await writeFile(path.join(instance, 'docker-compose.yml'), 'name: fluxer\nservices:\n api:\n image: stub\n');
const plannedRef = (label: string, args: ReadonlyArray<string>): string | null => {
const planned = (label: string, args: ReadonlyArray<string>, cwd?: string): string | null => {
const run = spawnSync('sh', [path.join(INSTALLER_ROOT, 'install.sh'), ...args], {
cwd,
encoding: 'utf8',
env: {...process.env, PATH: `${stubBin}${path.delimiter}${process.env.PATH ?? ''}`},
env: {
...process.env,
PATH: `${stubBin}${path.delimiter}${process.env.PATH ?? ''}`,
...(cwd == null ? {} : {PWD: cwd}),
},
});
if (run.error != null) {
problems.push(`install.sh ${label} could not run: ${run.error.message}`);
@@ -1432,7 +1464,15 @@ console.log('self-hosting guide against deploy/self-hosting');
problems.push(`install.sh ${label} exited ${String(run.status)}: ${run.stderr.trim()}`);
return null;
}
const line = run.stdout.match(/^ {2}ref\s+(\S+)$/mu);
return run.stdout;
};
const plannedRef = (label: string, args: ReadonlyArray<string>): string | null => {
const stdout = planned(label, args);
if (stdout == null) {
return null;
}
const line = stdout.match(/^ {2}ref\s+(\S+)$/mu);
if (line == null) {
problems.push(`install.sh ${label} printed no ref line`);
return null;
@@ -1472,6 +1512,38 @@ console.log('self-hosting guide against deploy/self-hosting');
problems.push(`install.sh ${label} plans ref ${resolved}, and the image tag it pairs with wants ${expected}`);
}
}
const composeYmlInstance = path.join(sandbox, 'compose-yml-instance');
await mkdir(composeYmlInstance, {recursive: true});
await writeFile(
path.join(composeYmlInstance, '.env'),
'FLUXER_DOMAIN=x.example\nFLUXER_IMAGE_TAG=2026.813.205040\n',
);
await writeFile(path.join(composeYmlInstance, 'compose.yml'), 'name: fluxer\nservices:\n api:\n image: stub\n');
const COMPOSE_NAME_CASES: ReadonlyArray<readonly [string, ReadonlyArray<string>, string | undefined]> = [
[
'under --update against a compose.yml instance',
['--update', '--dry-run', '--allow-root', '--dir', composeYmlInstance],
undefined,
],
[
'under --update standing in a compose.yml instance',
['--update', '--dry-run', '--allow-root'],
composeYmlInstance,
],
];
for (const [label, args, cwd] of COMPOSE_NAME_CASES) {
const stdout = planned(label, args, cwd);
if (stdout == null) {
continue;
}
if (!/^ {4}compose\.yml is unchanged$/mu.test(stdout)) {
problems.push(`install.sh ${label} does not plan the refreshed stack file onto compose.yml`);
}
if (/^ {4}docker-compose\.yml is new$/mu.test(stdout)) {
problems.push(`install.sh ${label} plans a docker-compose.yml that Compose would never load there`);
}
}
} finally {
await rm(sandbox, {recursive: true, force: true});
}
+100 -19
View File
@@ -108,6 +108,32 @@ $FluxerStackFiles = @(
'.env.example'
)
$FluxerComposeNames = @(
'compose.yaml'
'compose.yml'
'docker-compose.yml'
'docker-compose.yaml'
)
$script:FluxerComposeBase = 'docker-compose.yml'
$script:FluxerComposeBaseFrom = ''
function Get-FluxerComposeNameIn([string]$Directory) {
foreach ($name in $FluxerComposeNames) {
if (Test-Path -LiteralPath (Join-Path $Directory $name)) {
return $name
}
}
return ''
}
function Get-FluxerPlacedName([string]$Name) {
if ($Name -eq 'docker-compose.yml') {
return $script:FluxerComposeBase
}
return $Name
}
# The file Compose bind-mounts from the working directory, with the service that mounts it.
# Compose decides whether to recreate a container by comparing its configuration, and the
# contents of a bind-mounted file are not part of that comparison, so a changed Caddyfile survives
@@ -641,7 +667,7 @@ function Get-FluxerStackFiles([string]$StagingDir, [string]$RefValue) {
# for an edit.
function Move-FluxerStackFiles([string]$StagingDir, [string]$TargetDir) {
foreach ($name in $FluxerStackFiles) {
Move-Item -LiteralPath (Join-Path $StagingDir $name) -Destination (Join-Path $TargetDir $name) -Force
Move-Item -LiteralPath (Join-Path $StagingDir $name) -Destination (Join-Path $TargetDir (Get-FluxerPlacedName $name)) -Force
}
}
@@ -840,7 +866,7 @@ function Get-FluxerComposeProject([string]$TargetDir) {
if ($null -ne $env:COMPOSE_PROJECT_NAME -and $env:COMPOSE_PROJECT_NAME.Length -gt 0) {
return $env:COMPOSE_PROJECT_NAME
}
foreach ($line in [System.IO.File]::ReadAllText((Join-Path $TargetDir 'docker-compose.yml')).Split("`n")) {
foreach ($line in [System.IO.File]::ReadAllText((Join-Path $TargetDir $script:FluxerComposeBase)).Split("`n")) {
if ($line -match '^name:\s*(\S+)') {
return $Matches[1]
}
@@ -1046,7 +1072,7 @@ function Restart-FluxerMounts($Entries, [string]$TargetDir) {
$services = @(Get-FluxerComposeServices)
foreach ($entry in $Entries) {
if ($services -notcontains $entry.Service) {
Write-FluxerLine "Skipping the restart of $($entry.Service), because the docker-compose.yml in $TargetDir defines no service by that name."
Write-FluxerLine "Skipping the restart of $($entry.Service), because the $($script:FluxerComposeBase) in $TargetDir defines no service by that name."
continue
}
Write-FluxerLine "Restarting $($entry.Service), because $($entry.Name) is mounted into it and up -d does not reload a mounted file."
@@ -1225,7 +1251,7 @@ function Save-FluxerCurrentFiles([string]$Record, [string]$TargetDir, [string]$E
Copy-Item -LiteralPath $EnvPath -Destination $envCopy -Force
Set-FluxerPrivateFile $envCopy
foreach ($name in $FluxerStackFiles) {
$source = Join-Path $TargetDir $name
$source = Join-Path $TargetDir (Get-FluxerPlacedName $name)
$length = Get-FluxerFileLength $source
if ($length -gt 0) {
Copy-Item -LiteralPath $source -Destination (Join-Path $Record $name) -Force
@@ -1427,7 +1453,7 @@ function Backup-FluxerInstance([string]$Record, [string]$TargetDir, [string]$Pro
# The refreshed file is still staged when this runs, so a refusal here leaves the instance exactly
# as it was.
function Assert-FluxerPostgresMajor([string]$TargetDir, [string]$StagingDir) {
$old = Get-FluxerPostgresMajor (Join-Path $TargetDir 'docker-compose.yml')
$old = Get-FluxerPostgresMajor (Join-Path $TargetDir $script:FluxerComposeBase)
$new = Get-FluxerPostgresMajor (Join-Path $StagingDir 'docker-compose.yml')
if ($old.Length -eq 0 -or $new.Length -eq 0 -or $old -eq $new) {
return
@@ -1515,21 +1541,22 @@ function Show-FluxerUpdatePlan([string]$TargetDir, [string]$EnvPath, [string]$Ba
Write-FluxerLine ' File changes:'
$changed = 0
foreach ($name in $FluxerStackFiles) {
$current = Join-Path $TargetDir $name
$placed = Get-FluxerPlacedName $name
$current = Join-Path $TargetDir $placed
if (-not (Test-Path -LiteralPath $current)) {
Write-FluxerLine " $name is new"
Write-FluxerLine " $placed is new"
$changed++
} elseif (Test-FluxerSameFile $current (Join-Path $staging $name)) {
Write-FluxerLine " $name is unchanged"
Write-FluxerLine " $placed is unchanged"
} else {
Write-FluxerLine " $name changes"
Write-FluxerLine " $placed changes"
$changed++
}
}
if ($changed -eq 0) {
Write-FluxerLine " Note: ref $Ref moves no stack file"
}
$old = Get-FluxerPostgresMajor (Join-Path $TargetDir 'docker-compose.yml')
$old = Get-FluxerPostgresMajor (Join-Path $TargetDir $script:FluxerComposeBase)
$new = Get-FluxerPostgresMajor (Join-Path $staging 'docker-compose.yml')
if ($old.Length -gt 0 -and $new.Length -gt 0 -and $old -ne $new) {
Write-FluxerLine " Refusal: postgres moves from $old to $new, which this script does not do"
@@ -1706,7 +1733,7 @@ function Invoke-FluxerRollback([string]$TargetDir, [string]$EnvPath, [string]$Ba
$source = Join-Path $record $name
$length = Get-FluxerFileLength $source
if ($length -gt 0) {
Copy-Item -LiteralPath $source -Destination (Join-Path $TargetDir $name) -Force
Copy-Item -LiteralPath $source -Destination (Join-Path $TargetDir (Get-FluxerPlacedName $name)) -Force
} elseif (Test-Path -LiteralPath $source) {
Stop-Fluxer "$source is empty, so restoring it would replace a working file with nothing. Nothing was restored. Take the file from another record or from the ref the record names." $FluxerExitRefused
}
@@ -1739,13 +1766,20 @@ function Assert-FluxerInstance([string]$TargetDir, [string]$EnvPath) {
if (-not (Test-Path -LiteralPath $EnvPath)) {
Stop-Fluxer "No .env in $TargetDir. That directory holds no instance. Run install.ps1 with neither -Update nor -Rollback to set one up." $FluxerExitPrerequisite
}
$compose = Join-Path $TargetDir 'docker-compose.yml'
Resolve-FluxerComposeBase $TargetDir $EnvPath
$compose = Join-Path $TargetDir $script:FluxerComposeBase
if (-not (Test-Path -LiteralPath $compose)) {
Stop-Fluxer "No docker-compose.yml in $TargetDir. That directory does not hold an instance." $FluxerExitPrerequisite
if ($script:FluxerComposeBaseFrom.Length -gt 0) {
Stop-Fluxer "$($script:FluxerComposeBaseFrom) names $($script:FluxerComposeBase) first, and $compose is not there. Put that file back, or name the file the instance runs on first in COMPOSE_FILE." $FluxerExitPrerequisite
}
Stop-Fluxer "No compose file in $TargetDir. Compose looks for compose.yaml, compose.yml, docker-compose.yml and docker-compose.yaml there, and that directory holds none of them, so it does not hold an instance." $FluxerExitPrerequisite
}
if ((Get-FluxerFileLength $compose) -eq 0) {
Stop-Fluxer "$compose is empty. A redirect that captured a failed download leaves that, and Compose refuses an empty compose file. Put the file back from a backup or from the record of the last upgrade, then run this again." $FluxerExitPrerequisite
}
if ($script:FluxerComposeBase -ne 'docker-compose.yml') {
Write-FluxerLine "Compose loads $($script:FluxerComposeBase) in $TargetDir, so the stack's docker-compose.yml is written to that name."
}
}
# Compose reads COMPOSE_FILE from .env and loads every file it names before it answers anything, so
@@ -1776,7 +1810,7 @@ function Get-FluxerEnvScalar([string]$EnvPath, [string]$Name) {
return $raw
}
function Assert-FluxerComposeFiles([string]$TargetDir, [string]$EnvPath) {
function Get-FluxerComposeSetting([string]$EnvPath) {
$value = ''
$source = 'the environment'
if ($null -ne $env:COMPOSE_FILE) {
@@ -1786,9 +1820,6 @@ function Assert-FluxerComposeFiles([string]$TargetDir, [string]$EnvPath) {
$value = Get-FluxerEnvScalar $EnvPath 'COMPOSE_FILE'
$source = $EnvPath
}
if ($value.Length -eq 0) {
return
}
$separator = ''
if ($null -ne $env:COMPOSE_PATH_SEPARATOR) {
$separator = [string]$env:COMPOSE_PATH_SEPARATOR
@@ -1799,6 +1830,50 @@ function Assert-FluxerComposeFiles([string]$TargetDir, [string]$EnvPath) {
if ($separator.Length -eq 0) {
$separator = [System.IO.Path]::PathSeparator
}
return [pscustomobject]@{Value = $value; Source = $source; Separator = $separator}
}
function Resolve-FluxerComposeBase([string]$TargetDir, [string]$EnvPath) {
$setting = Get-FluxerComposeSetting $EnvPath
$script:FluxerComposeBaseFrom = ''
foreach ($name in $setting.Value.Split([string[]]$setting.Separator, [System.StringSplitOptions]::None)) {
if ($name.Length -eq 0) {
continue
}
$base = $name
foreach ($prefix in @('./', '.\')) {
if ($base.StartsWith($prefix, [System.StringComparison]::Ordinal)) {
$base = $base.Substring($prefix.Length)
}
}
foreach ($root in @("$TargetDir/", "$TargetDir\")) {
if ($base.StartsWith($root, [System.StringComparison]::OrdinalIgnoreCase)) {
$base = $base.Substring($root.Length)
}
}
$script:FluxerComposeBaseFrom = "COMPOSE_FILE from $($setting.Source)"
if ($base.Contains('/') -or $base.Contains('\')) {
Stop-Fluxer "$($script:FluxerComposeBaseFrom) names $name first, and that file is not directly in $TargetDir. This script refreshes only the files in the directory it acts on, so the upgrade would leave the file Compose loads on the old stack. Move it into $TargetDir and name it there, or upgrade by hand." $FluxerExitPrerequisite
}
$script:FluxerComposeBase = $base
return
}
$found = Get-FluxerComposeNameIn $TargetDir
if ($found.Length -gt 0) {
$script:FluxerComposeBase = $found
} else {
$script:FluxerComposeBase = 'docker-compose.yml'
}
}
function Assert-FluxerComposeFiles([string]$TargetDir, [string]$EnvPath) {
$setting = Get-FluxerComposeSetting $EnvPath
$value = $setting.Value
$source = $setting.Source
$separator = $setting.Separator
if ($value.Length -eq 0) {
return
}
foreach ($name in $value.Split([string[]]$separator, [System.StringSplitOptions]::None)) {
if ($name.Length -eq 0) {
continue
@@ -1855,15 +1930,18 @@ function Invoke-FluxerInstall {
$targetPath = $Dir
$adoptedCwd = $false
$fellBack = $false
if ($targetPath.Length -eq 0) {
$here = (Get-Location).Path
$hereEnv = Join-Path $here '.env'
$hereIsFluxer = (Test-Path -LiteralPath $hereEnv) -and (@(Get-FluxerEnvLines $hereEnv | Where-Object {$_.StartsWith('FLUXER_')}).Count -gt 0)
if (($Update -or $Rollback) -and (Get-FluxerFileLength (Join-Path $here 'docker-compose.yml')) -gt 0 -and $hereIsFluxer) {
$hereCompose = Get-FluxerComposeNameIn $here
if (($Update -or $Rollback) -and $hereCompose.Length -gt 0 -and (Get-FluxerFileLength (Join-Path $here $hereCompose)) -gt 0 -and $hereIsFluxer) {
$targetPath = $here
$adoptedCwd = $true
} else {
$targetPath = Join-Path $HOME 'fluxer'
$fellBack = $Update -or $Rollback
}
}
$targetDir = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($targetPath)
@@ -1876,6 +1954,9 @@ function Invoke-FluxerInstall {
if ($adoptedCwd) {
Write-FluxerLine "Acting on the instance in $targetDir, the working directory. Pass -Dir to name another."
}
if ($fellBack) {
Write-FluxerLine "The working directory holds no instance, so this acts on $targetDir. Pass -Dir to name another."
}
if ($Update -or $Rollback) {
Assert-FluxerInstance $targetDir $envPath
@@ -1886,7 +1967,7 @@ function Invoke-FluxerInstall {
Assert-FluxerComposeFiles $targetDir $envPath
$project = Get-FluxerComposeProject $targetDir
if ($project.Length -eq 0) {
Stop-Fluxer "docker-compose.yml in $targetDir declares no project name, so the volume names cannot be derived." $FluxerExitPrerequisite
Stop-Fluxer "$($script:FluxerComposeBase) in $targetDir declares no project name, so the volume names cannot be derived." $FluxerExitPrerequisite
}
Push-Location -LiteralPath $targetDir
try {
+100 -26
View File
@@ -131,6 +131,36 @@ Caddyfile
FILES
}
fluxer_compose_names() {
cat <<'NAMES'
compose.yaml
compose.yml
docker-compose.yml
docker-compose.yaml
NAMES
}
fluxer_compose_name_in() {
for fluxer_candidate in $(fluxer_compose_names); do
if [ -e "$1/$fluxer_candidate" ]; then
printf '%s' "$fluxer_candidate"
return 0
fi
done
return 1
}
fluxer_compose_base='docker-compose.yml'
fluxer_compose_base_from=''
fluxer_placed_name() {
if [ "$1" = 'docker-compose.yml' ]; then
printf '%s' "$fluxer_compose_base"
else
printf '%s' "$1"
fi
}
# The file Compose bind-mounts from the working directory, with the service that
# mounts it.
#
@@ -372,16 +402,22 @@ done
#
# An install writes a new instance, so it never adopts the working directory.
if [ -z "$opt_dir" ]; then
fluxer_here_compose=$(fluxer_compose_name_in "$(pwd)" || true)
if { [ "$opt_update" -eq 1 ] || [ "$opt_rollback" -eq 1 ]; } &&
[ -s "$(pwd)/docker-compose.yml" ] && [ -e "$(pwd)/.env" ] &&
grep -q '^FLUXER_' "$(pwd)/.env" 2>/dev/null; then
[ -n "$fluxer_here_compose" ] && [ -s "$(pwd)/$fluxer_here_compose" ] &&
[ -e "$(pwd)/.env" ] && grep -q '^FLUXER_' "$(pwd)/.env" 2>/dev/null; then
opt_dir="$(pwd)"
fluxer_say "Acting on the instance in $opt_dir, the working directory. Pass --dir to name another."
elif [ -n "${HOME:-}" ]; then
else
if [ -n "${HOME:-}" ]; then
opt_dir="$HOME/fluxer"
else
opt_dir="$(pwd)/fluxer"
fi
if [ "$opt_update" -eq 1 ] || [ "$opt_rollback" -eq 1 ]; then
fluxer_say "The working directory holds no instance, so this acts on $opt_dir. Pass --dir to name another."
fi
fi
fi
case $opt_dir in
/*) ;;
@@ -749,7 +785,7 @@ fluxer_fetch_stack() {
fluxer_place_stack() {
while read -r fluxer_file; do
[ -n "$fluxer_file" ] || continue
mv "$fluxer_scratch/$fluxer_file.part" "$opt_dir/$fluxer_file"
mv "$fluxer_scratch/$fluxer_file.part" "$opt_dir/$(fluxer_placed_name "$fluxer_file")"
done < "$fluxer_scratch/files"
fluxer_say "Stack files in $opt_dir are at ref $opt_ref."
}
@@ -759,7 +795,7 @@ fluxer_compose_project() {
printf '%s' "$COMPOSE_PROJECT_NAME"
return 0
fi
sed -n 's/^name: *//p' "$opt_dir/docker-compose.yml" | head -n 1
sed -n 's/^name: *//p' "$opt_dir/$fluxer_compose_base" | head -n 1
}
fluxer_project=''
@@ -767,7 +803,7 @@ fluxer_project=''
fluxer_set_project() {
fluxer_project=$(fluxer_compose_project)
if [ -z "$fluxer_project" ]; then
fluxer_fail 2 "docker-compose.yml in $opt_dir declares no project name, so the volume names cannot be derived."
fluxer_fail 2 "$fluxer_compose_base in $opt_dir declares no project name, so the volume names cannot be derived."
fi
}
@@ -1104,11 +1140,18 @@ fluxer_require_instance() {
if [ ! -e "$opt_dir/.env" ]; then
fluxer_fail 2 "No .env in $opt_dir. That directory holds no instance. Run install.sh with neither --update nor --rollback to set one up."
fi
if [ ! -e "$opt_dir/docker-compose.yml" ]; then
fluxer_fail 2 "No docker-compose.yml in $opt_dir. That directory does not hold an instance."
fluxer_resolve_compose_base
if [ ! -e "$opt_dir/$fluxer_compose_base" ]; then
if [ -n "$fluxer_compose_base_from" ]; then
fluxer_fail 2 "$fluxer_compose_base_from names $fluxer_compose_base first, and $opt_dir/$fluxer_compose_base is not there. Put that file back, or name the file the instance runs on first in COMPOSE_FILE."
fi
if [ ! -s "$opt_dir/docker-compose.yml" ]; then
fluxer_fail 2 "$opt_dir/docker-compose.yml is empty. A redirect that captured a failed download leaves that, and Compose refuses an empty compose file. Put the file back from a backup or from the record of the last upgrade, then run this again."
fluxer_fail 2 "No compose file in $opt_dir. Compose looks for compose.yaml, compose.yml, docker-compose.yml and docker-compose.yaml there, and that directory holds none of them, so it does not hold an instance."
fi
if [ ! -s "$opt_dir/$fluxer_compose_base" ]; then
fluxer_fail 2 "$opt_dir/$fluxer_compose_base is empty. A redirect that captured a failed download leaves that, and Compose refuses an empty compose file. Put the file back from a backup or from the record of the last upgrade, then run this again."
fi
if [ "$fluxer_compose_base" != 'docker-compose.yml' ]; then
fluxer_say "Compose loads $fluxer_compose_base in $opt_dir, so the stack's docker-compose.yml is written to that name."
fi
}
@@ -1141,14 +1184,13 @@ fluxer_env_scalar() {
printf '%s' "$fluxer_scalar"
}
fluxer_require_compose_files() {
fluxer_read_compose_setting() {
fluxer_compose_file=${COMPOSE_FILE:-}
fluxer_compose_from="the environment"
if [ -z "$fluxer_compose_file" ]; then
fluxer_compose_file=$(fluxer_env_scalar COMPOSE_FILE)
fluxer_compose_from="$opt_dir/.env"
fi
[ -n "$fluxer_compose_file" ] || return 0
fluxer_path_sep=${COMPOSE_PATH_SEPARATOR:-}
if [ -z "$fluxer_path_sep" ]; then
fluxer_path_sep=$(fluxer_env_scalar COMPOSE_PATH_SEPARATOR)
@@ -1156,6 +1198,36 @@ fluxer_require_compose_files() {
if [ -z "$fluxer_path_sep" ]; then
fluxer_path_sep=':'
fi
}
fluxer_resolve_compose_base() {
fluxer_read_compose_setting
fluxer_compose_base_from=''
fluxer_rest=$fluxer_compose_file
while [ -n "$fluxer_rest" ]; do
fluxer_name=${fluxer_rest%%"$fluxer_path_sep"*}
case $fluxer_rest in
*"$fluxer_path_sep"*) fluxer_rest=${fluxer_rest#*"$fluxer_path_sep"} ;;
*) fluxer_rest='' ;;
esac
[ -n "$fluxer_name" ] || continue
fluxer_compose_base=${fluxer_name#./}
fluxer_compose_base=${fluxer_compose_base#"$opt_dir"/}
fluxer_compose_base_from="COMPOSE_FILE from $fluxer_compose_from"
break
done
if [ -z "$fluxer_compose_base_from" ]; then
fluxer_compose_base=$(fluxer_compose_name_in "$opt_dir" || printf '%s' 'docker-compose.yml')
return 0
fi
case $fluxer_compose_base in
*/*) fluxer_fail 2 "$fluxer_compose_base_from names $fluxer_name first, and that file is not directly in $opt_dir. This script refreshes only the files in the directory it acts on, so the upgrade would leave the file Compose loads on the old stack. Move it into $opt_dir and name it there, or upgrade by hand." ;;
esac
}
fluxer_require_compose_files() {
fluxer_read_compose_setting
[ -n "$fluxer_compose_file" ] || return 0
fluxer_compose_count=0
fluxer_rest=$fluxer_compose_file
while [ -n "$fluxer_rest" ]; do
@@ -1185,7 +1257,7 @@ Leave the COMPOSE_FILE line as it is. Without $fluxer_name the edge container bi
done
if [ "$fluxer_compose_count" -gt 1 ] &&
! fluxer_version_ge "$fluxer_compose_version" "$FLUXER_MIN_COMPOSE_OVERLAY"; then
fluxer_fail 2 "COMPOSE_FILE from $fluxer_compose_from loads $fluxer_compose_count files and this host runs Compose $fluxer_compose_version. Every overlay this script downloads uses the !override tag, which needs Compose $FLUXER_MIN_COMPOSE_OVERLAY or newer. Upgrade Compose, or load only docker-compose.yml."
fluxer_fail 2 "COMPOSE_FILE from $fluxer_compose_from loads $fluxer_compose_count files and this host runs Compose $fluxer_compose_version. Every overlay this script downloads uses the !override tag, which needs Compose $FLUXER_MIN_COMPOSE_OVERLAY or newer. Upgrade Compose, or load only $fluxer_compose_base."
fi
}
@@ -1315,10 +1387,11 @@ fluxer_save_current_files() {
chmod 600 "$fluxer_record/.env"
while read -r fluxer_file; do
[ -n "$fluxer_file" ] || continue
if [ -s "$opt_dir/$fluxer_file" ]; then
cp -p "$opt_dir/$fluxer_file" "$fluxer_record/$fluxer_file"
elif [ -e "$opt_dir/$fluxer_file" ]; then
fluxer_fail 7 "$opt_dir/$fluxer_file is empty, so the record would hold a file a rollback could not use. Put the file back before upgrading."
fluxer_placed="$opt_dir/$(fluxer_placed_name "$fluxer_file")"
if [ -s "$fluxer_placed" ]; then
cp -p "$fluxer_placed" "$fluxer_record/$fluxer_file"
elif [ -e "$fluxer_placed" ]; then
fluxer_fail 7 "$fluxer_placed is empty, so the record would hold a file a rollback could not use. Put the file back before upgrading."
fi
done < "$fluxer_scratch/files"
}
@@ -1499,7 +1572,7 @@ fluxer_postgres_major() {
# The refreshed file is still in the scratch directory when this runs, so a
# refusal here leaves the instance exactly as it was.
fluxer_guard_postgres_major() {
fluxer_old_major=$(fluxer_postgres_major "$opt_dir/docker-compose.yml")
fluxer_old_major=$(fluxer_postgres_major "$opt_dir/$fluxer_compose_base")
fluxer_new_major=$(fluxer_postgres_major "$fluxer_scratch/docker-compose.yml.part")
if [ -z "$fluxer_old_major" ] || [ -z "$fluxer_new_major" ]; then
return 0
@@ -1550,7 +1623,7 @@ $(fluxer_compose_error ' ')"
while read -r fluxer_file fluxer_service; do
[ -n "$fluxer_service" ] || continue
if ! grep -qxF "$fluxer_service" "$fluxer_scratch/services"; then
fluxer_say "Skipping the restart of $fluxer_service, because the docker-compose.yml in $opt_dir defines no service by that name."
fluxer_say "Skipping the restart of $fluxer_service, because the $fluxer_compose_base in $opt_dir defines no service by that name."
continue
fi
fluxer_say "Restarting $fluxer_service, because $fluxer_file is mounted into it and up -d does not reload a mounted file."
@@ -1656,20 +1729,21 @@ fluxer_plan_update() {
fluxer_changed=0
while read -r fluxer_file; do
[ -n "$fluxer_file" ] || continue
if [ ! -e "$opt_dir/$fluxer_file" ]; then
fluxer_say " $fluxer_file is new"
fluxer_placed=$(fluxer_placed_name "$fluxer_file")
if [ ! -e "$opt_dir/$fluxer_placed" ]; then
fluxer_say " $fluxer_placed is new"
fluxer_changed=1
elif cmp -s "$opt_dir/$fluxer_file" "$fluxer_scratch/$fluxer_file.part"; then
fluxer_say " $fluxer_file is unchanged"
elif cmp -s "$opt_dir/$fluxer_placed" "$fluxer_scratch/$fluxer_file.part"; then
fluxer_say " $fluxer_placed is unchanged"
else
fluxer_say " $fluxer_file changes"
fluxer_say " $fluxer_placed changes"
fluxer_changed=1
fi
done < "$fluxer_scratch/files"
if [ "$fluxer_changed" -eq 0 ]; then
fluxer_say " note ref $opt_ref moves no stack file"
fi
fluxer_old_major=$(fluxer_postgres_major "$opt_dir/docker-compose.yml")
fluxer_old_major=$(fluxer_postgres_major "$opt_dir/$fluxer_compose_base")
fluxer_new_major=$(fluxer_postgres_major "$fluxer_scratch/docker-compose.yml.part")
if [ -n "$fluxer_old_major" ] && [ -n "$fluxer_new_major" ] && [ "$fluxer_old_major" != "$fluxer_new_major" ]; then
fluxer_say " refusal postgres moves from $fluxer_old_major to $fluxer_new_major, which this script does not do"
@@ -1862,7 +1936,7 @@ fluxer_run_rollback() {
while read -r fluxer_file; do
[ -n "$fluxer_file" ] || continue
if [ -s "$fluxer_rollback_dir/$fluxer_file" ]; then
cp -p "$fluxer_rollback_dir/$fluxer_file" "$opt_dir/$fluxer_file"
cp -p "$fluxer_rollback_dir/$fluxer_file" "$opt_dir/$(fluxer_placed_name "$fluxer_file")"
elif [ -e "$fluxer_rollback_dir/$fluxer_file" ]; then
fluxer_fail 3 "$fluxer_rollback_dir/$fluxer_file is empty, so restoring it would replace a working file with nothing. Nothing was restored. Take the file from another record or from the ref the record names."
fi