fix(ops): treat a CI-managed host as a skip, not a failed updater run
CI / check (push) Successful in 32s
CI / tests-unit (push) Successful in 1m57s
CI / tests-integration (push) Successful in 2m5s
CI / tests-imaging-browsers (push) Successful in 1m37s
CI / tests-ui (push) Failing after 3m13s
CI / preflight (push) Skipped
CI / deploy (push) Skipped

Commit 3c8abb6f made docker-update.sh exit 0 (SKIP) on a host owned by CI so
the cron does not report a failed deployment. The tests still expected a
nonzero exit for the ci-active/ci-green/ci-upstream scenarios, so five cases
failed.

- docker-update.test.ts: move the CI scenarios out of the non-zero group and
  assert the skip: exit 0, 'This host is managed by CI' in the output and no
  'compose up'. All three CI scenarios (both slot containers and the nginx
  upstream) now share one test.
- README.md: document the exit-0 skip.
This commit is contained in:
openhands committed 2026-10-14 21:59:42 +02:00
1 parent 3c8abb6fbf
commit ac23d7d1d9
2 files changed
+16 -15

No files matched your search

+5 -2
View File
@@ -191,7 +191,9 @@ it does not fetch changes to this Git-built CMS. Use the updater for releases.
A clone does **not** install an automatic scheduler. If desired, explicitly add a A clone does **not** install an automatic scheduler. If desired, explicitly add a
cron entry with the absolute path of **your** checkout; keep logs in its `logs` cron entry with the absolute path of **your** checkout; keep logs in its `logs`
directory. Do not configure both CI and Compose updates for the same instance. directory. Do not configure both CI and Compose updates for the same instance.
The updater refuses an active `epicnext-cms-app` CI-managed container. On a CI-managed host (either slot container running, or the nginx upstream
pointing at a blue/green slot) the updater skips with exit 0, so a cron run
that legitimately has nothing to do does not look like a failed deployment.
Logs: `logs/docker-update.log`. After cutover, a startup, image or HTTP check Logs: `logs/docker-update.log`. After cutover, a startup, image or HTTP check
failure automatically recreates the CMS with its previous image and verifies its failure automatically recreates the CMS with its previous image and verifies its
@@ -898,7 +900,8 @@ that:
- `scripts/docker-update.sh` refuses to run on a CI host. It used to test only - `scripts/docker-update.sh` refuses to run on a CI host. It used to test only
`epicnext-cms-app`, but after a cutover to the green slot that container is `epicnext-cms-app`, but after a cutover to the green slot that container is
stopped and deleted, so the guard stopped firing while the host stayed stopped and deleted, so the guard stopped firing while the host stayed
CI-managed. It now checks both slot containers and the nginx upstream. CI-managed. It now checks both slot containers and the nginx upstream, and
reports the skip with exit 0 instead of failing the cron.
- `ci-deploy.sh` retires a compose replica of *this* checkout - `ci-deploy.sh` retires a compose replica of *this* checkout
(`com.docker.compose.project.config_files`) from the candidate port before (`com.docker.compose.project.config_files`) from the candidate port before
starting the candidate — but never a slot container, and never the port nginx starting the candidate — but never a slot container, and never the port nginx
+11 -13
View File
@@ -188,29 +188,27 @@ describe("Docker clone updates", () => {
expect(r.calls).not.toContain("docker volume prune"); expect(r.calls).not.toContain("docker volume prune");
expect(r.calls).not.toContain("docker system prune"); expect(r.calls).not.toContain("docker system prune");
}); });
it.each([ it.each(["dirty", "pull-failure", "build-failure", "migration-failure"])(
"dirty", "does not replace the container after %s",
"ci-active", (scenario) => {
"ci-green",
"ci-upstream",
"pull-failure",
"build-failure",
"migration-failure",
])("does not replace the container after %s", (scenario) => {
const r = simulate(scenario); const r = simulate(scenario);
expect(r.status, r.output).not.toBe(0); expect(r.status, r.output).not.toBe(0);
expect(r.calls).not.toContain("compose up"); expect(r.calls).not.toContain("compose up");
}); },
);
// Regressie: na een cutover naar het groene slot bestaat epicnext-cms-app // Regressie: na een cutover naar het groene slot bestaat epicnext-cms-app
// niet meer en zag deze controle alleen een vrijgekomen blauwe container. De // niet meer en zag deze controle alleen een vrijgekomen blauwe container. De
// dagelijkse `docker-update.sh` startte toen een compose-replica op poort // dagelijkse `docker-update.sh` startte toen een compose-replica op poort
// 3002 en blokkeerde elke volgende release. De blauwe container alleen // 3002 en blokkeerde elke volgende release. De blauwe container alleen
// controleren is dus geen bewijs dat de host niet door CI beheerd wordt. // controleren is dus geen bewijs dat de host niet door CI beheerd wordt.
it.each(["ci-green", "ci-upstream"])( //
"refuses a compose deployment while CI owns the host (%s)", // A host owned by CI is a legitimate no-op, not an error: the cron run must
// exit 0 so it does not look like a failed deployment.
it.each(["ci-active", "ci-green", "ci-upstream"])(
"skips the compose deployment while CI owns the host (%s)",
(scenario) => { (scenario) => {
const r = simulate(scenario); const r = simulate(scenario);
expect(r.status, r.output).not.toBe(0); expect(r.status, r.output).toBe(0);
expect(r.output).toContain("This host is managed by CI"); expect(r.output).toContain("This host is managed by CI");
expect(r.calls).not.toContain("compose up"); expect(r.calls).not.toContain("compose up");
}, },