openvibers/openvibe.live Commits
Release notification after a deploy (roadmap WS-P task 9): deploy.sh runs `ovhost announce live` once the release went live
Changes
@@@ -21,6 +21,9 @@# deploy/nginx/ never auto-installed (the live config is generated elsewhere);# the script says so#++# After a deploy or rollback that went live: `ovhost announce live` (release notification to open tabs;++# best effort, skipped without an ovhost that has `announce`, never changes the exit code).++## Two layouts:# release /opt/openvibe.live/releases/<id> (code + node_modules), current -> releases/<id>,# shared/data. A deploy builds a new release while the old one serves; switching is an@@@ -221,6 +224,21 @@ restart_and_verify() {$on_fail}++# ── Release notification (WS-P task 9) ───────────────────────────────────────++# OpenVibe.Host publishes host.deploy.activated to OpenVibe.Events, so open tabs check /release.json now++# instead of at their next poll. ovhost announces the release Live's own /release.json reports, once per++# release. Best effort: skipped when ovhost is missing or has no `announce`, 20 s at most, and it never++# fails a deploy (see OpenVibe.Host docs/release-notifications.md).++OVHOST="${OVHOST:-ovhost}"++announce_release() {++ [ "$DRY_RUN" = "1" ] && return 0++ command -v "$OVHOST" >/dev/null 2>&1 || return 0++ local help++ help=$("$OVHOST" --help 2>/dev/null || true)++ case "$help" in *"announce <service>"*) ;; *) say "release notification skipped: this ovhost has no announce"; return 0 ;; esac++ timeout 20 "$OVHOST" announce live 9>&- 2>&1 | sed 's/^/ /' || say "release notification not sent (the deploy stands)"++}++# ═══════════════════════════════════════════════════════════════# Release layout# ═══════════════════════════════════════════════════════════════@@@ -256,6 +274,7 @@ if [ "$LAYOUT" = release ]; then# Keep the release we left so it can be re-selected; just move it out of "newest".touch -h "$PREV"say "rolled back to $(basename "$PREV") ✅"++ announce_releaseexit 0fi@@@ -338,6 +357,7 @@ if [ "$LAYOUT" = release ]; thendonegit -C "$REPO" worktree prune 2>/dev/null || truesay "deployed $CUR_ID → $NEW_ID ✅"++ announce_releaseexit 0fi@@@ -401,6 +421,7 @@ if [ "$DRY_RUN" = "1" ]; then say "dry run complete — nothing was changed. resif [ "$NEEDS_RESTART" != true ]; thensay "change classes:${CATS} — no restart needed; the server re-reads public/ and docs/ from disk."say "deployed ${OLD_HASH:0:8} → ${NEW_HASH:0:8} without interrupting anyone ✅"++ announce_releaseexit 0fi@@@ -421,3 +442,4 @@ curl -sf --max-time 5 "${API_URL}/api/health" >/dev/null || die "health endpointsay "deployed ${OLD_HASH:0:8} → ${NEW_HASH:0:8} (${COMMIT_COUNT} commit(s))"say "note: established WebSocket, WHIP, RTMP and WebRTC sessions were reconnected, not preserved."say "done ✅"++announce_release@@@ -65,6 +65,13 @@ but could not restore previous `node_modules`.- The last 5 releases are kept.- Content-hashed assets from the previous release are still served under their old hashes, so a pagerendered before the switch never loads JavaScript from after it.++- **Release notification.** After a deploy or `--rollback` that went live, the script runs++ `ovhost announce live` (OpenVibe.Host, WS-P task 9). ovhost publishes `host.deploy.activated` for the++ release `/release.json` now reports, once per release, to OpenVibe.Events. Open tabs (openvibe-shared++ 1.17.0 release-watch) then check `/release.json` within about 20 s instead of at their next poll. It is++ best effort: skipped when `ovhost` is missing or has no `announce` (set `OVHOST` for another path), 20 s at++ most, and it never changes the exit code. A static-only switch keeps the running release, so there is++ nothing new to announce. The credentials and the setup are in OpenVibe.Host `docs/release-notifications.md`.`test/deploy-sim.test.js` runs the script against a simulated host (real git, fake systemctl) andchecks each of these behaviours.@@@ -9,7 +9,9 @@* - a release that never becomes ready is rolled back automatically (exit 3), still serving;* - --rollback returns to the previous release, restarting only if server code differs;* - a lockfile change installs into the NEW release, so rolling back gets the OLD node_modules;-- * - the legacy in-place layout also skips the restart for public/-only changes.++ * - the legacy in-place layout also skips the restart for public/-only changes;++ * - every deploy or rollback that went live runs `ovhost announce live` (a fake ovhost records it),++ * one that did not go live or changed nothing does not, and a failing or older ovhost never fails a deploy.** node test/deploy-sim.test.js*/@@@ -89,6 +91,15 @@ esac`, { mode: 0o755 });fs.writeFileSync(path.join(bin, 'npm'), `#!/usr/bin/env bash\nmkdir -p node_modules && echo "lock-$(node -e 'console.log(require(\"./package-lock.json\").v)')" > node_modules/marker\n`, { mode: 0o755 });fs.writeFileSync(path.join(bin, 'journalctl'), '#!/usr/bin/env bash\nexit 0\n', { mode: 0o755 });++ // Fake ovhost: records each call. OVHOST_FAKE_EXIT makes announce fail; OVHOST_FAKE_OLD drops announce from --help.++ const ovhostLog = path.join(tmp, 'ovhost.log');++ fs.writeFileSync(path.join(bin, 'ovhost'), `#!/usr/bin/env bash++if [ "$1" = "--help" ]; then if [ -n "$OVHOST_FAKE_OLD" ]; then echo " deploy <service>"; else echo " announce <service> [--release <id>]"; fi; exit 0; fi++echo "$*" >> "${ovhostLog}"++echo "[ovhost] release notification sent: $2"++exit "\${OVHOST_FAKE_EXIT:-0}"++`, { mode: 0o755 });++ const announced = () => { try { return fs.readFileSync(ovhostLog, 'utf8').split('\n').filter(Boolean); } catch { return []; } };// Release layout, as migrate-to-releases.sh leaves it.fs.mkdirSync(path.join(base, 'releases'), { recursive: true });@@@ -113,6 +124,8 @@ esacconst r = deploy();assert.strictEqual(r.status, 0, r.stdout + r.stderr);assert.match(r.stdout, /without a restart/);++ assert.deepStrictEqual(announced(), ['announce live'], 'the release notification ran once');++ assert.match(r.stdout, /release notification sent: live/);assert.strictEqual((await get('/static')).body, 'static-2');assert.strictEqual(await pid(), before, 'pid changed');});@@@ -122,6 +135,7 @@ esacwrite('server/version.txt', 'v2'); commit('server');const r = deploy();assert.strictEqual(r.status, 0, r.stdout + r.stderr);++ assert.strictEqual(announced().length, 2);await new Promise((res) => setTimeout(res, 300));const code = (await get('/code')).body;assert.ok(code.startsWith('v2'), code);@@@ -138,8 +152,10 @@ esac});await check('--rollback returns code AND node_modules of the previous release', async () => {++ const n = announced().length;const r = deploy('--rollback');assert.strictEqual(r.status, 0, r.stdout + r.stderr);++ assert.strictEqual(announced().length, n + 1, 'a rollback is announced too');await new Promise((res) => setTimeout(res, 500));assert.ok((await get('/code')).body.startsWith('v2'));assert.strictEqual((await get('/deps')).body.trim(), 'lock-1');@@@ -150,8 +166,10 @@ esacwrite('server/version.txt', 'v4'); commit('v4');assert.strictEqual(deploy().status, 0);write('server/BROKEN', '1'); write('server/version.txt', 'v5-broken'); commit('broken');++ const n = announced().length;const r = deploy();assert.strictEqual(r.status, 3, r.stdout + r.stderr);++ assert.strictEqual(announced().length, n, 'a release that did not go live is not announced');await new Promise((res) => setTimeout(res, 500));const code = (await get('/code')).body;assert.ok(code.startsWith('v4'), code);@@@ -162,9 +180,29 @@ esac// origin still has the broken commit; the deploy would try again, so first fix forward.fs.unlinkSync(path.join(work, 'server/BROKEN')); write('server/version.txt', 'v6'); commit('fix');assert.strictEqual(deploy().status, 0);++ const n = announced().length;const r = deploy();assert.strictEqual(r.status, 0);assert.match(r.stdout, /nothing to deploy/);++ assert.strictEqual(announced().length, n, 'nothing new, nothing announced');++ });++++ await check('a failing release notification, or an ovhost without announce, never fails a deploy', async () => {++ write('public/x.txt', 'static-3'); commit('static 3');++ const failing = spawnSync('bash', [DEPLOY], { env: { ...env, OVHOST_FAKE_EXIT: '2' }, encoding: 'utf8' });++ assert.strictEqual(failing.status, 0, failing.stdout + failing.stderr);++ assert.match(failing.stdout, /release notification not sent \(the deploy stands\)/);++ write('public/x.txt', 'static-4'); commit('static 4');++ const n = announced().length;++ const old = spawnSync('bash', [DEPLOY], { env: { ...env, OVHOST_FAKE_OLD: '1' }, encoding: 'utf8' });++ assert.strictEqual(old.status, 0, old.stdout + old.stderr);++ assert.match(old.stdout, /release notification skipped: this ovhost has no announce/);++ assert.strictEqual(announced().length, n);++ write('public/x.txt', 'static-5'); commit('static 5');++ const none = spawnSync('bash', [DEPLOY], { env: { ...env, OVHOST: path.join(tmp, 'no-such-ovhost') }, encoding: 'utf8' });++ assert.strictEqual(none.status, 0, none.stdout + none.stderr);++ assert.strictEqual(announced().length, n, 'no ovhost: nothing to call');++ assert.doesNotMatch(none.stdout, /release notification/);});await check('old releases are pruned to KEEP_RELEASES', async () => {@@@ -184,9 +222,11 @@ esacawait check('legacy layout: public/-only change pulls without restarting', async () => {const before = await pid();write('public/x.txt', 'legacy-static'); commit('legacy static');++ const n = announced().length;const r = spawnSync('bash', [DEPLOY], { env: lenv, encoding: 'utf8' });assert.strictEqual(r.status, 0, r.stdout + r.stderr);assert.match(r.stdout, /no restart needed/);++ assert.strictEqual(announced().length, n + 1, 'the legacy layout announces too');assert.strictEqual((await get('/static')).body, 'legacy-static', r.stdout + r.stderr + ' ready=' + (await get('/api/ready')).status);assert.strictEqual(await pid(), before);});
@@ -21,6 +21,9 @@
@@ -21,6 +21,9 @@
# deploy/nginx/ never auto-installed (the live config is generated elsewhere);
# deploy/nginx/ never auto-installed (the live config is generated elsewhere);
# the script says so
# the script says so
#
#
+# After a deploy or rollback that went live: `ovhost announce live` (release notification to open tabs;
+# best effort, skipped without an ovhost that has `announce`, never changes the exit code).
+#
# Two layouts:
# Two layouts:
# release /opt/openvibe.live/releases/<id> (code + node_modules), current -> releases/<id>,
# release /opt/openvibe.live/releases/<id> (code + node_modules), current -> releases/<id>,
# shared/data. A deploy builds a new release while the old one serves; switching is an
# shared/data. A deploy builds a new release while the old one serves; switching is an
@@ -221,6 +224,21 @@ restart_and_verify() {
@@ -221,6 +224,21 @@ restart_and_verify() {
$on_fail
$on_fail
}
}
+# ── Release notification (WS-P task 9) ───────────────────────────────────────
+# OpenVibe.Host publishes host.deploy.activated to OpenVibe.Events, so open tabs check /release.json now
+# instead of at their next poll. ovhost announces the release Live's own /release.json reports, once per
+# release. Best effort: skipped when ovhost is missing or has no `announce`, 20 s at most, and it never
+# fails a deploy (see OpenVibe.Host docs/release-notifications.md).
+OVHOST="${OVHOST:-ovhost}"
+announce_release() {
+ [ "$DRY_RUN" = "1" ] && return 0
+ command -v "$OVHOST" >/dev/null 2>&1 || return 0
+ local help
+ help=$("$OVHOST" --help 2>/dev/null || true)
+ case "$help" in *"announce <service>"*) ;; *) say "release notification skipped: this ovhost has no announce"; return 0 ;; esac
+ timeout 20 "$OVHOST" announce live 9>&- 2>&1 | sed 's/^/ /' || say "release notification not sent (the deploy stands)"
+}
+
# ═══════════════════════════════════════════════════════════════
# ═══════════════════════════════════════════════════════════════
# Release layout
# Release layout
# ═══════════════════════════════════════════════════════════════
# ═══════════════════════════════════════════════════════════════
@@ -256,6 +274,7 @@ if [ "$LAYOUT" = release ]; then
@@ -256,6 +274,7 @@ if [ "$LAYOUT" = release ]; then
# Keep the release we left so it can be re-selected; just move it out of "newest".
# Keep the release we left so it can be re-selected; just move it out of "newest".
touch -h "$PREV"
touch -h "$PREV"
say "rolled back to $(basename "$PREV") ✅"
say "rolled back to $(basename "$PREV") ✅"
+ announce_release
exit 0
exit 0
fi
fi
@@ -338,6 +357,7 @@ if [ "$LAYOUT" = release ]; then
@@ -338,6 +357,7 @@ if [ "$LAYOUT" = release ]; then
done
done
git -C "$REPO" worktree prune 2>/dev/null || true
git -C "$REPO" worktree prune 2>/dev/null || true
say "deployed $CUR_ID → $NEW_ID ✅"
say "deployed $CUR_ID → $NEW_ID ✅"
+ announce_release
exit 0
exit 0
fi
fi
@@ -401,6 +421,7 @@ if [ "$DRY_RUN" = "1" ]; then say "dry run complete — nothing was changed. res
@@ -401,6 +421,7 @@ if [ "$DRY_RUN" = "1" ]; then say "dry run complete — nothing was changed. res
if [ "$NEEDS_RESTART" != true ]; then
if [ "$NEEDS_RESTART" != true ]; then
say "change classes:${CATS} — no restart needed; the server re-reads public/ and docs/ from disk."
say "change classes:${CATS} — no restart needed; the server re-reads public/ and docs/ from disk."
say "deployed ${OLD_HASH:0:8} → ${NEW_HASH:0:8} without interrupting anyone ✅"
say "deployed ${OLD_HASH:0:8} → ${NEW_HASH:0:8} without interrupting anyone ✅"
+ announce_release
exit 0
exit 0
fi
fi
@@ -421,3 +442,4 @@ curl -sf --max-time 5 "${API_URL}/api/health" >/dev/null || die "health endpoint
@@ -421,3 +442,4 @@ curl -sf --max-time 5 "${API_URL}/api/health" >/dev/null || die "health endpoint
say "deployed ${OLD_HASH:0:8} → ${NEW_HASH:0:8} (${COMMIT_COUNT} commit(s))"
say "deployed ${OLD_HASH:0:8} → ${NEW_HASH:0:8} (${COMMIT_COUNT} commit(s))"
say "note: established WebSocket, WHIP, RTMP and WebRTC sessions were reconnected, not preserved."
say "note: established WebSocket, WHIP, RTMP and WebRTC sessions were reconnected, not preserved."
say "done ✅"
say "done ✅"
+announce_release
@@ -65,6 +65,13 @@ but could not restore previous `node_modules`.
@@ -65,6 +65,13 @@ but could not restore previous `node_modules`.
- The last 5 releases are kept.
- The last 5 releases are kept.
- Content-hashed assets from the previous release are still served under their old hashes, so a page
- Content-hashed assets from the previous release are still served under their old hashes, so a page
rendered before the switch never loads JavaScript from after it.
rendered before the switch never loads JavaScript from after it.
+- **Release notification.** After a deploy or `--rollback` that went live, the script runs
+ `ovhost announce live` (OpenVibe.Host, WS-P task 9). ovhost publishes `host.deploy.activated` for the
+ release `/release.json` now reports, once per release, to OpenVibe.Events. Open tabs (openvibe-shared
+ 1.17.0 release-watch) then check `/release.json` within about 20 s instead of at their next poll. It is
+ best effort: skipped when `ovhost` is missing or has no `announce` (set `OVHOST` for another path), 20 s at
+ most, and it never changes the exit code. A static-only switch keeps the running release, so there is
+ nothing new to announce. The credentials and the setup are in OpenVibe.Host `docs/release-notifications.md`.
`test/deploy-sim.test.js` runs the script against a simulated host (real git, fake systemctl) and
`test/deploy-sim.test.js` runs the script against a simulated host (real git, fake systemctl) and
checks each of these behaviours.
checks each of these behaviours.
@@ -9,7 +9,9 @@
@@ -9,7 +9,9 @@
* - a release that never becomes ready is rolled back automatically (exit 3), still serving;
* - a release that never becomes ready is rolled back automatically (exit 3), still serving;
* - --rollback returns to the previous release, restarting only if server code differs;
* - --rollback returns to the previous release, restarting only if server code differs;
* - a lockfile change installs into the NEW release, so rolling back gets the OLD node_modules;
* - a lockfile change installs into the NEW release, so rolling back gets the OLD node_modules;
- * - the legacy in-place layout also skips the restart for public/-only changes.
+ * - the legacy in-place layout also skips the restart for public/-only changes;
+ * - every deploy or rollback that went live runs `ovhost announce live` (a fake ovhost records it),
+ * one that did not go live or changed nothing does not, and a failing or older ovhost never fails a deploy.
*
*
* node test/deploy-sim.test.js
* node test/deploy-sim.test.js
*/
*/
@@ -89,6 +91,15 @@ esac
@@ -89,6 +91,15 @@ esac
`, { mode: 0o755 });
`, { mode: 0o755 });
fs.writeFileSync(path.join(bin, 'npm'), `#!/usr/bin/env bash\nmkdir -p node_modules && echo "lock-$(node -e 'console.log(require(\"./package-lock.json\").v)')" > node_modules/marker\n`, { mode: 0o755 });
fs.writeFileSync(path.join(bin, 'npm'), `#!/usr/bin/env bash\nmkdir -p node_modules && echo "lock-$(node -e 'console.log(require(\"./package-lock.json\").v)')" > node_modules/marker\n`, { mode: 0o755 });
fs.writeFileSync(path.join(bin, 'journalctl'), '#!/usr/bin/env bash\nexit 0\n', { mode: 0o755 });
fs.writeFileSync(path.join(bin, 'journalctl'), '#!/usr/bin/env bash\nexit 0\n', { mode: 0o755 });
+ // Fake ovhost: records each call. OVHOST_FAKE_EXIT makes announce fail; OVHOST_FAKE_OLD drops announce from --help.
+ const ovhostLog = path.join(tmp, 'ovhost.log');
+ fs.writeFileSync(path.join(bin, 'ovhost'), `#!/usr/bin/env bash
+if [ "$1" = "--help" ]; then if [ -n "$OVHOST_FAKE_OLD" ]; then echo " deploy <service>"; else echo " announce <service> [--release <id>]"; fi; exit 0; fi
+echo "$*" >> "${ovhostLog}"
+echo "[ovhost] release notification sent: $2"
+exit "\${OVHOST_FAKE_EXIT:-0}"
+`, { mode: 0o755 });
+ const announced = () => { try { return fs.readFileSync(ovhostLog, 'utf8').split('\n').filter(Boolean); } catch { return []; } };
// Release layout, as migrate-to-releases.sh leaves it.
// Release layout, as migrate-to-releases.sh leaves it.
fs.mkdirSync(path.join(base, 'releases'), { recursive: true });
fs.mkdirSync(path.join(base, 'releases'), { recursive: true });
@@ -113,6 +124,8 @@ esac
@@ -113,6 +124,8 @@ esac
const r = deploy();
const r = deploy();
assert.strictEqual(r.status, 0, r.stdout + r.stderr);
assert.strictEqual(r.status, 0, r.stdout + r.stderr);
assert.match(r.stdout, /without a restart/);
assert.match(r.stdout, /without a restart/);
+ assert.deepStrictEqual(announced(), ['announce live'], 'the release notification ran once');
+ assert.match(r.stdout, /release notification sent: live/);
assert.strictEqual((await get('/static')).body, 'static-2');
assert.strictEqual((await get('/static')).body, 'static-2');
assert.strictEqual(await pid(), before, 'pid changed');
assert.strictEqual(await pid(), before, 'pid changed');
});
});
@@ -122,6 +135,7 @@ esac
@@ -122,6 +135,7 @@ esac
write('server/version.txt', 'v2'); commit('server');
write('server/version.txt', 'v2'); commit('server');
const r = deploy();
const r = deploy();
assert.strictEqual(r.status, 0, r.stdout + r.stderr);
assert.strictEqual(r.status, 0, r.stdout + r.stderr);
+ assert.strictEqual(announced().length, 2);
await new Promise((res) => setTimeout(res, 300));
await new Promise((res) => setTimeout(res, 300));
const code = (await get('/code')).body;
const code = (await get('/code')).body;
assert.ok(code.startsWith('v2'), code);
assert.ok(code.startsWith('v2'), code);
@@ -138,8 +152,10 @@ esac
@@ -138,8 +152,10 @@ esac
});
});
await check('--rollback returns code AND node_modules of the previous release', async () => {
await check('--rollback returns code AND node_modules of the previous release', async () => {
+ const n = announced().length;
const r = deploy('--rollback');
const r = deploy('--rollback');
assert.strictEqual(r.status, 0, r.stdout + r.stderr);
assert.strictEqual(r.status, 0, r.stdout + r.stderr);
+ assert.strictEqual(announced().length, n + 1, 'a rollback is announced too');
await new Promise((res) => setTimeout(res, 500));
await new Promise((res) => setTimeout(res, 500));
assert.ok((await get('/code')).body.startsWith('v2'));
assert.ok((await get('/code')).body.startsWith('v2'));
assert.strictEqual((await get('/deps')).body.trim(), 'lock-1');
assert.strictEqual((await get('/deps')).body.trim(), 'lock-1');
@@ -150,8 +166,10 @@ esac
@@ -150,8 +166,10 @@ esac
write('server/version.txt', 'v4'); commit('v4');
write('server/version.txt', 'v4'); commit('v4');
assert.strictEqual(deploy().status, 0);
assert.strictEqual(deploy().status, 0);
write('server/BROKEN', '1'); write('server/version.txt', 'v5-broken'); commit('broken');
write('server/BROKEN', '1'); write('server/version.txt', 'v5-broken'); commit('broken');
+ const n = announced().length;
const r = deploy();
const r = deploy();
assert.strictEqual(r.status, 3, r.stdout + r.stderr);
assert.strictEqual(r.status, 3, r.stdout + r.stderr);
+ assert.strictEqual(announced().length, n, 'a release that did not go live is not announced');
await new Promise((res) => setTimeout(res, 500));
await new Promise((res) => setTimeout(res, 500));
const code = (await get('/code')).body;
const code = (await get('/code')).body;
assert.ok(code.startsWith('v4'), code);
assert.ok(code.startsWith('v4'), code);
@@ -162,9 +180,29 @@ esac
@@ -162,9 +180,29 @@ esac
// origin still has the broken commit; the deploy would try again, so first fix forward.
// origin still has the broken commit; the deploy would try again, so first fix forward.
fs.unlinkSync(path.join(work, 'server/BROKEN')); write('server/version.txt', 'v6'); commit('fix');
fs.unlinkSync(path.join(work, 'server/BROKEN')); write('server/version.txt', 'v6'); commit('fix');
assert.strictEqual(deploy().status, 0);
assert.strictEqual(deploy().status, 0);
+ const n = announced().length;
const r = deploy();
const r = deploy();
assert.strictEqual(r.status, 0);
assert.strictEqual(r.status, 0);
assert.match(r.stdout, /nothing to deploy/);
assert.match(r.stdout, /nothing to deploy/);
+ assert.strictEqual(announced().length, n, 'nothing new, nothing announced');
+ });
+
+ await check('a failing release notification, or an ovhost without announce, never fails a deploy', async () => {
+ write('public/x.txt', 'static-3'); commit('static 3');
+ const failing = spawnSync('bash', [DEPLOY], { env: { ...env, OVHOST_FAKE_EXIT: '2' }, encoding: 'utf8' });
+ assert.strictEqual(failing.status, 0, failing.stdout + failing.stderr);
+ assert.match(failing.stdout, /release notification not sent \(the deploy stands\)/);
+ write('public/x.txt', 'static-4'); commit('static 4');
+ const n = announced().length;
+ const old = spawnSync('bash', [DEPLOY], { env: { ...env, OVHOST_FAKE_OLD: '1' }, encoding: 'utf8' });
+ assert.strictEqual(old.status, 0, old.stdout + old.stderr);
+ assert.match(old.stdout, /release notification skipped: this ovhost has no announce/);
+ assert.strictEqual(announced().length, n);
+ write('public/x.txt', 'static-5'); commit('static 5');
+ const none = spawnSync('bash', [DEPLOY], { env: { ...env, OVHOST: path.join(tmp, 'no-such-ovhost') }, encoding: 'utf8' });
+ assert.strictEqual(none.status, 0, none.stdout + none.stderr);
+ assert.strictEqual(announced().length, n, 'no ovhost: nothing to call');
+ assert.doesNotMatch(none.stdout, /release notification/);
});
});
await check('old releases are pruned to KEEP_RELEASES', async () => {
await check('old releases are pruned to KEEP_RELEASES', async () => {
@@ -184,9 +222,11 @@ esac
@@ -184,9 +222,11 @@ esac
await check('legacy layout: public/-only change pulls without restarting', async () => {
await check('legacy layout: public/-only change pulls without restarting', async () => {
const before = await pid();
const before = await pid();
write('public/x.txt', 'legacy-static'); commit('legacy static');
write('public/x.txt', 'legacy-static'); commit('legacy static');
+ const n = announced().length;
const r = spawnSync('bash', [DEPLOY], { env: lenv, encoding: 'utf8' });
const r = spawnSync('bash', [DEPLOY], { env: lenv, encoding: 'utf8' });
assert.strictEqual(r.status, 0, r.stdout + r.stderr);
assert.strictEqual(r.status, 0, r.stdout + r.stderr);
assert.match(r.stdout, /no restart needed/);
assert.match(r.stdout, /no restart needed/);
+ assert.strictEqual(announced().length, n + 1, 'the legacy layout announces too');
assert.strictEqual((await get('/static')).body, 'legacy-static', r.stdout + r.stderr + ' ready=' + (await get('/api/ready')).status);
assert.strictEqual((await get('/static')).body, 'legacy-static', r.stdout + r.stderr + ' ready=' + (await get('/api/ready')).status);
assert.strictEqual(await pid(), before);
assert.strictEqual(await pid(), before);
});
});