* ci: publish amd64 images under a lowercase name and deploy only after green CI Image publishing never succeeded: GHCR rejects the capitalized repository slug, and the arm64 jobs failed on the macOS runners, which have no Docker buildx. Resolve the namespace in lowercase and build amd64 only until an arm64 runner with buildx exists; the manifest check expects one digest. The Evo deploy ran on every push to main regardless of the result of Product CI, so red commits went live. Trigger it from the completed ci workflow instead and deploy the head it verified; manual dispatch stays. The Playwright report publish failed every nightly run because the S3 settings are not configured. The script now skips the gallery with a notice when they are missing instead of failing the job. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u * ci: give each runner on a shared host its own E2E ports Two Linux runners live on the same host. Every Web E2E job first frees "stale" listeners on the fixed ports 3110, 3111, 5180 and 4321 with fuser -k, so a job starting on the second runner killed the Vite server of the job already running on the first one. The victim died with SIGKILL (exit 137), and its remaining tests failed on reloads that could no longer reach a server. This is what made the Web E2E run on this PR red, and it explains the recurring reload failures on main whenever two runs overlapped. The workflow now derives API_PORT, WEB_PORT and WWW_PORT from the runner name (suffix -N means an offset of N times 100), and the port-freeing script follows those settings instead of a hard-coded list. The harness, Vite, the API and the marketing site already read these variables. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u * test(testkit): cover the env-driven E2E ports instead of a fixed list The ports test parsed a hard-coded array that no longer exists. It now runs the script on a fake host whose fuser finds nothing, and checks the default ports, the per-runner override, the rejection of a non-numeric setting, and that the workflow picks the ports before freeing them. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u * test(updater): expect the amd64-only publish and the lowercase namespace The workflow test still described the multi-arch publish. It now checks that only amd64 builds natively, that arm64 is documented as paused, that one digest feeds the manifest, and that every image name comes from the lowercase namespace step. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u * ci: keep every runner off the default E2E ports Branches that still free the fixed ports 3110, 3111, 5180 and 4321 keep running on the shared host (main, the nightly run, other open PRs) until they carry the per-runner ports. A job of ours on the runner without a numeric suffix used exactly those defaults, so such a neighbour could still kill it, and its own port freeing could kill the neighbour. The unsuffixed runner now starts at offset 100, so no job from this workflow touches the defaults in either direction. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u * fix(web): recover lazy routes and overlays from a failed module fetch When the browser aborts a module request while a page loads, the lazy import rejects and the route throws, leaving a blank page. Chromium does this whenever a network interface appears or disappears mid-request (net::ERR_NETWORK_CHANGED), which happens on a laptop that changes networks and on the CI host every time a container starts or stops. A Web E2E run hit exactly this: two modules of the onboarding page failed to load and the suite waited on an empty document. Lazy routes and overlays now load through a helper that retries the import once and, when the browser keeps rejecting it from its module map, reloads the page a single time before surfacing the error. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u (cherry picked from commit d5dacb12f14380071ef14ba8845d18c9d39e3fac) * fix(web): reload once when the module graph is aborted while booting The lazy-route recovery cannot help when the browser aborts a module of the static import graph: nothing of the app runs, the root stays empty, and the only trace is a console error. A Web E2E run hit this right after a page reload, when four modules of the shell failed with net::ERR_NETWORK_CHANGED and the shell never rendered. An inline script in the HTML entry now listens for the error event of the module script and reloads the page once, guarded by a session flag that the entry module clears after a successful boot. The existing preload recovery stays in place; it only covers Vite's chunk preloads of production builds. A new E2E spec aborts one module request during boot and one lazy route request, and expects the sign-in page to render in both cases. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u (cherry picked from commit e5b7e036e1a60e7e270592c4b9382ee23ed45ccb) --------- Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
58 lines
2.3 KiB
HTML
58 lines
2.3 KiB
HTML
<!doctype html>
|
|
<html lang="de">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<meta name="description" content="Deine Oos — immer da, immer wach." />
|
|
<meta name="theme-color" content="#07080a" />
|
|
<meta name="apple-mobile-web-app-title" content="evOo" />
|
|
<link rel="icon" href="/favicon.ico" sizes="any" />
|
|
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
|
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
|
<!-- Fonts loaded locally via @fontsource-variable (Inter + Geist) — no external CDN -->
|
|
<link rel="stylesheet" href="/evoo-brand.css" />
|
|
<title>evOo</title>
|
|
<script>
|
|
(() => {
|
|
const key = "rakazo.uiAppearance";
|
|
let preference = "system";
|
|
try {
|
|
const stored = localStorage.getItem(key);
|
|
if (stored === "system" || stored === "light" || stored === "dark") preference = stored;
|
|
} catch {}
|
|
const systemLight =
|
|
typeof matchMedia === "function" && matchMedia("(prefers-color-scheme: light)").matches;
|
|
const theme = preference === "system" ? (systemLight ? "light" : "dark") : preference;
|
|
document.documentElement.dataset.theme = theme;
|
|
document.documentElement.style.colorScheme = theme;
|
|
const meta = document.querySelector('meta[name="theme-color"]');
|
|
if (meta) meta.setAttribute("content", theme === "light" ? "#fafaf8" : "#07080a");
|
|
})();
|
|
</script>
|
|
<script>
|
|
// Reload once when the browser aborts part of the module graph while the app
|
|
// boots (a network interface changed mid-request). main.tsx clears the flag
|
|
// after a successful boot; a second failure in a row is left to the browser.
|
|
addEventListener(
|
|
"error",
|
|
(event) => {
|
|
const target = event.target;
|
|
if (!(target instanceof HTMLScriptElement) || target.type !== "module") return;
|
|
try {
|
|
if (sessionStorage.getItem("rakazo:boot-reload")) return;
|
|
sessionStorage.setItem("rakazo:boot-reload", "1");
|
|
} catch {
|
|
return;
|
|
}
|
|
location.reload();
|
|
},
|
|
true,
|
|
);
|
|
</script>
|
|
</head>
|
|
<body>
|
|
<div id="root"></div>
|
|
<script type="module" src="/src/main.tsx"></script>
|
|
</body>
|
|
</html>
|