fix(deploy): pull images explicitly, and detect api/web drift by commit
The first successful galactus deploy came up all-green while the web tier was running a build from two commits earlier. The registry held web:latest from 3ff56e6; the host still had a web:latest cached from 4ee7ec7; the deploy reported success and served the old one. The API was only current because it had been pulled by hand during earlier debugging. Two independent failures, both fixed here. 1. Images are not pulled. The deploy action's `pull: true` does not reliably refresh an already-cached moving tag on a standalone endpoint. Added a Pull images step (deploy/scripts/pull-images.mjs) that pulls each image through Portainer's Docker API with registry credentials and fails the deploy if a pull fails — note the endpoint answers 200 even when the pull errored, so the stream body has to be inspected, not just the status. 2. The drift check could not see it. Both the verify step and the web footer compared APP_VERSION, but on a branch build BOTH tiers report "master", so equality proved nothing. They now compare gitSha, which is the only field that differs between two builds of the same branch. api and web come from one matrix run, so a difference can only mean an image was not replaced. This needed a /version on the web tier too — previously its build identity was only readable by scraping window.__APP_BUILD__ out of the HTML. pull-images.mjs builds the X-Registry-Auth header as URL-safe base64 WITH padding: Node's "base64url" omits the padding and Portainer's Go decoder rejects it with "Illegal base64 data at input byte N". Verified against galactus: pulls both images, and exits non-zero on a nonexistent tag. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { readBuildInfoFromEnv } from "@/lib/build-info";
|
||||
|
||||
// Read per request, never prerendered — the whole point is to report what THIS
|
||||
// running container is, and a baked answer would defeat that.
|
||||
export const dynamic = "force-dynamic";
|
||||
|
||||
/**
|
||||
* The web tier's counterpart to the API's GET /version.
|
||||
*
|
||||
* Without this, the only way to see what the web container is running was to
|
||||
* scrape window.__APP_BUILD__ out of the HTML. The deploy workflow compares the
|
||||
* two tiers' gitSha to catch a half-applied release, so it needs a stable,
|
||||
* parseable answer from both sides.
|
||||
*/
|
||||
export function GET() {
|
||||
return NextResponse.json({ service: "web", ...readBuildInfoFromEnv() });
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
import { useEffect, useRef, useState, type ReactNode } from "react";
|
||||
import { usePathname, useRouter } from "next/navigation";
|
||||
import Link from "next/link";
|
||||
import { getApiVersion, logout, me, updateUiScale } from "@/lib/api";
|
||||
import { getApiVersion, logout, me, updateUiScale, type ServiceVersion } from "@/lib/api";
|
||||
import { shortSha, webBuildInfo } from "@/lib/build-info";
|
||||
import { AuthContext, can } from "@/lib/abilities";
|
||||
import { ROLE_LABEL } from "@/lib/labels";
|
||||
@@ -190,13 +190,13 @@ function NavMenu({
|
||||
*/
|
||||
function BuildFooter() {
|
||||
const web = webBuildInfo();
|
||||
const [api, setApi] = useState<string | null>(null);
|
||||
const [api, setApi] = useState<ServiceVersion | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
let alive = true;
|
||||
getApiVersion()
|
||||
.then((v) => {
|
||||
if (alive) setApi(v.version);
|
||||
if (alive) setApi(v);
|
||||
})
|
||||
.catch(() => {
|
||||
// The shell already redirects to /login when the API is unreachable;
|
||||
@@ -207,7 +207,12 @@ function BuildFooter() {
|
||||
};
|
||||
}, []);
|
||||
|
||||
const mismatch = api !== null && api !== web.version;
|
||||
// Compare the COMMIT, not the version string. On a branch build both tiers
|
||||
// report APP_VERSION "master", so comparing versions cannot see drift — which
|
||||
// is exactly how a stale web image once sat next to a current API with this
|
||||
// footer showing nothing wrong. The sha is the only field that actually
|
||||
// differs between two builds of the same branch.
|
||||
const mismatch = api !== null && api.gitSha !== web.gitSha;
|
||||
|
||||
return (
|
||||
<footer className="shell-footer">
|
||||
@@ -216,8 +221,8 @@ function BuildFooter() {
|
||||
className="shell-footer-build"
|
||||
title={`web ${web.version} (${shortSha(web.gitSha)}) — ${web.buildDate}`}
|
||||
>
|
||||
v{web.version}
|
||||
{api !== null && (mismatch ? ` · API v${api}` : "")}
|
||||
v{web.version} · {shortSha(web.gitSha)}
|
||||
{mismatch && api ? ` · API ${shortSha(api.gitSha)}` : ""}
|
||||
</span>
|
||||
{mismatch && (
|
||||
<span className="shell-footer-warn" role="status">
|
||||
|
||||
Reference in New Issue
Block a user