mirror of
https://github.com/fluxerapp/fluxer
synced 2026-10-07 19:22:14 +09:00
fix(installer): resolve the compose file name Compose loads (#2649)
This commit is contained in:
@@ -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});
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user