Compare commits

..

2 Commits

Author SHA1 Message Date
peptiderepo
b1685d239e ci(monitor): only flag drift when undeployed commits touch deployable files
Compare prod-deployment SHA to main and ignore changes confined to .github/,
tests/, and docs/build files (which the deploy excludes) — so infra-only commits
don't trip a false 'prod behind main' alert every day.

Agent-Session: cto monitor-drift-filter
2026-06-09 07:15:24 +00:00
peptiderepo
afb43e7a41 ci: harden deploy + add health monitor (#5)
* ci(deploy): retry rsync on Hostinger runner-IP throttle + best-effort inventory guard

Pilot surfaced an exit-255 SSH connection timeout (documented Hostinger
rate-limiting of concurrent runner IPs). rsync now retries 4x/25s; the plugin
count guard is best-effort so a throttled check can't fail a good deploy.

Agent-Session: cto harden-deploy

* ci: add daily Deploy Health Monitor (drift + deploy-failure)

Scheduled GitHub Actions check (runs on GitHub infra, not the dead local cron):
for each app repo, compares the latest production deployment SHA to main and
flags failed deploy runs. Opens one de-duplicated 'deploy-health' issue when
something is off — the alarm that was missing when deploys silently broke.

Agent-Session: cto deploy-health-monitor

---------

Co-authored-by: peptiderepo <peptiderepo@users.noreply.github.com>
2026-06-09 14:11:28 +07:00

View File

@@ -45,7 +45,23 @@ jobs:
if (sts.length) prodState = sts[0].state;
}
if (runConc === 'failure') problems.push(`**${repo}**: latest deploy run failed (\`${short(runSha)}\`)`);
if (prodSha && prodSha !== main) problems.push(`**${repo}**: prod behind main — prod=\`${short(prodSha)}\` vs main=\`${short(main)}\` (undeployed commits)`);
if (prodSha && prodSha !== main) {
// Only real drift if undeployed commits touch DEPLOYABLE files
// (ignore .github/, tests/, and docs/build files the deploy excludes).
try {
const cmp = (await github.rest.repos.compareCommits({owner, repo, base: prodSha, head: main})).data;
const deployable = (cmp.files || []).some(f => {
const x = f.filename;
if (x.startsWith('.github/') || x.startsWith('tests/')) return false;
if (/^(ARCHITECTURE|CONVENTIONS|CHANGELOG|README)\.md$/.test(x)) return false;
if (/^(composer\.(json|lock)|phpunit\.xml(\.dist)?|\.?phpcs\.xml\.dist)$/.test(x)) return false;
return true;
});
if (deployable) problems.push(`**${repo}**: prod behind main — prod=\`${short(prodSha)}\` vs main=\`${short(main)}\` (undeployed code changes)`);
} catch (e) {
problems.push(`**${repo}**: prod sha != main sha (could not diff: ${e.message})`);
}
}
if (prodSha && prodState !== 'success') problems.push(`**${repo}**: last production deployment state is \`${prodState}\``);
core.info(`${repo}: main=${short(main)} lastRun=${runConc} prodDeploy=${short(prodSha)}/${prodState}`);
} catch (e) {