distribute prebuilt channel images via GHCR instead of building on pilots

Pilots behind the inspecting proxy cannot reliably build the frontend from source; npm's many small fetches get reset mid-install. Build the image once in CI on the clean runners and have pilots pull the finished channel image. One resumable image pull survives the proxy where hundreds of npm requests do not, and a pilot now needs only Docker.
This commit is contained in:
benjamin.baker
2026-09-02 09:34:40 -04:00
parent a299e751dc
commit d0e9f80287
4 changed files with 224 additions and 25 deletions

View File

@@ -11,12 +11,15 @@
silently). Python is optional and only checked when -CheckPython is
passed, because the app itself runs entirely in the container.
2. Picks a channel:
stable -> builds from the 'main' branch, no DEV badge.
edge -> builds from the 'dev-be' branch, red DEV badge in the UI.
stable -> pulls the 'stable' image (built from main), no DEV badge.
edge -> pulls the 'edge' image (built from dev-be), red DEV badge.
The two channels use different compose project names, host ports, and
named volumes, so they run side by side without colliding.
3. Generates a .env for the channel (dev-grade secrets, NOT for production)
if one does not already exist, then builds and brings up the stack.
if one does not already exist, then PULLS the prebuilt channel image
from GHCR and brings up the stack. Nothing is built on the host: the
image is produced by CI, so a pilot needs only Docker (no Node/npm and
no source compile behind the corporate proxy).
Windows-only by design: pre-release audience is Windows. Not a production
installer - see deploy/windows/installer/shopdb-install.ps1 for that.
@@ -26,7 +29,8 @@
.PARAMETER Ref
Override the git ref to build. Defaults to 'main' for stable, 'dev-be' for
edge. Useful for testing a feature branch through the edge plumbing.
edge. Retained for compatibility; the pull flow selects the image by channel
tag, so this now only affects which branch the working tree is prepared on.
.PARAMETER CheckPython
Also verify a local Python 3.11+ (only needed if you run tests/tooling on
@@ -67,6 +71,7 @@ $channels = @{
ApiPort = 5001
MysqlPort = 3306
EnvLabel = ''
ImageTag = 'stable'
}
edge = @{
Ref = 'dev-be'
@@ -74,9 +79,15 @@ $channels = @{
ApiPort = 5011
MysqlPort = 3316
EnvLabel = 'DEV'
ImageTag = 'edge'
}
}
# GHCR repository that CI (.github/workflows/publish-images.yml) publishes the
# per-channel images to. Pilots PULL this instead of building from source,
# because building behind an inspecting proxy is unreliable.
$imageRepo = 'ghcr.io/ge-aero/shopdb-flask'
function Write-Step {
param([string]$Message)
Write-Host ''
@@ -259,6 +270,15 @@ try {
$envPath = Join-Path $repoRoot ".env.$($config.Project)"
if (Test-Path $envPath) {
Write-Ok "Reusing existing $([System.IO.Path]::GetFileName($envPath))."
# A .env written before the pull-based flow existed will not carry a
# SHOPDB_IMAGE line, and the pull compose file requires it. Top it up in
# place rather than force the pilot to delete and re-key the file.
$image = "$imageRepo`:$($config.ImageTag)"
$envText = Get-Content -Path $envPath
if (-not ($envText | Where-Object { $_ -match '^SHOPDB_IMAGE=' })) {
Add-Content -Path $envPath -Value "SHOPDB_IMAGE=$image" -Encoding ASCII
Write-Ok "Added SHOPDB_IMAGE=$image to the existing .env."
}
} else {
# Dev-grade secrets: generated locally, never for production. A real
# deploy fills .env from the wiki runbook, not from this script.
@@ -268,6 +288,7 @@ try {
$rootPass = [Convert]::ToBase64String((1..18 | ForEach-Object { Get-Random -Maximum 256 })) -replace '[^A-Za-z0-9]', 'x'
$apiPort = $config.ApiPort
$mysqlPort = $config.MysqlPort
$image = "$imageRepo`:$($config.ImageTag)"
$lines = @(
"# Auto-generated by scripts/dev-bootstrap.ps1 for the $Channel channel.",
@@ -283,28 +304,29 @@ try {
"MYSQL_PASSWORD=$dbPass",
"MYSQL_PORT=$mysqlPort",
"API_PORT=$apiPort",
"SHOPDB_IMAGE=$image",
"SHOPDB_ENV_LABEL=$($config.EnvLabel)"
)
Set-Content -Path $envPath -Value $lines -Encoding ASCII
Write-Ok "Wrote $([System.IO.Path]::GetFileName($envPath)) (API on $apiPort, MySQL on $mysqlPort)."
}
# --- 4. Build and start ---------------------------------------------------
# --- 4. Pull and start ----------------------------------------------------
Write-Step 'Detecting corporate TLS-inspection CA for the Docker build'
Set-CorpRootCa -RepoRoot $repoRoot
Write-Step "Building and starting the '$($config.Project)' stack"
# No local build: the image is produced by CI and published to GHCR (see
# .github/workflows/publish-images.yml). Pilots may have only Docker
# installed, and building the frontend from source behind an inspecting
# proxy is unreliable, so we pull the finished channel image instead.
Write-Step "Pulling and starting the '$($config.Project)' stack"
$composeArgs = @(
'compose',
'-p', $config.Project,
'--env-file', $envPath,
'-f', 'docker-compose.yml',
'-f', 'docker-compose.channel.yml'
'-f', 'docker-compose.pull.yml'
)
& docker @composeArgs build api
if ($LASTEXITCODE -ne 0) { throw "docker compose build failed ($LASTEXITCODE)." }
& docker @composeArgs pull
if ($LASTEXITCODE -ne 0) { throw "docker compose pull failed ($LASTEXITCODE). If this is an auth error, run: docker login ghcr.io" }
& docker @composeArgs up -d
if ($LASTEXITCODE -ne 0) { throw "docker compose up failed ($LASTEXITCODE)." }