deploy.yml: /opt/drb/.last_good_tag reads empty (not missing), so PREV_TAG is blank and every deploy's Main Deploy step fails — rollback safety net has been dead since at least run #602 #156
Closed
opened 2026-09-20 14:38:54 -04:00 by logan
·
1 comment
No Branch/Tag Specified
Labels
Clear labels
area/ai-pipeline
area/auth
area/billing
area/correlation
area/discord
area/frontend
area/governance
area/growth
area/infra-deploy
area/legal
area/map
area/mqtt
area/node-ota
area/osint
area/recording-audio
area/sdr-radio
area/transcription
board
board:agenda
minutes:draft
minutes:final
role:ceo
role:cfo
role:cio
role:ciso
role:cmo
role:coo
role:cto
Scene/entity extraction, summarizer, AI providers, embeddings
AuthN/Z, roles, tenancy/org scoping, API keys, credentials
Plans, entitlements, AI spend accounting and caps
Incident correlation, linking, lifecycle, severity, corr_debug
Discord bot, radio bots, voice streams, bot tokens, webhooks
Web UI: Next.js app and the edge-node local dashboard
Board process, minutes, unattended runner, agent tooling
Go-to-market, outreach, cold email, discovery conversations
CI/CD, VM, registry, Firestore rules/indexes deploy, Terraform/Caddy
ToS, retention, licences/DAAs, CAN-SPAM, E&O, name suppression
Live map: pins, paths, tiles, legend, geocoding
Node-C2 MQTT, checkins, commands, node sweeper, dynsec
Node install, enrollment, OTA updater, image skew
External feeds: cameras, 511, ADS-B, AIS, outages, and their licences
Call capture, boundaries, PulseAudio, ring buffer, trimming, attribution
OP25 decode, SDR hardware/tuning, conventional, second SDR, mesh radio
STT, transcript correction, place verification, name accuracy
Board process: minutes and board-filed items
Needs board scoping at the next sitting; CEO removes the label once scoped
One exec pre-read, closed by CEO
CEO ruling; open until enacted
Filed by the CEO
Filed by the CFO
Filed by the CIO
Filed by the CISO
Filed by the CMO
Filed by the COO
Filed by the CTO
kind
behavior-bug
Wrong product behavior; the instrument reads it correctly
kind
measurement-bug
Corrupts the recording/instrument (debug data, evals, health checks), not behavior
priority
p0
Drop everything: prod broken, live exposure, or owner-ruled P0
priority
p1
Next up: blocks a gate, a customer, or core incident quality
priority
p2
Normal: real defect or planned work, schedule in turn
priority
p3
Low: minor, cosmetic, or someday
status
blocked
Waiting on an external party, spend, or another issue
status
in-progress
Branch or PR open against it
status
needs-decision
Waiting on an owner or board ruling before work
status
needs-info
Root cause or repro unknown; needs a reading first
type
bug
Something built is wrong: code, data, or a measurement
type
chore
Maintenance, refactor, tests, pinning, cleanup: no user-visible change
type
decision
A ruling to make or record: board minutes, owner calls, legal reviews
type
docs
Docs, runbooks, CLAUDE.md, skills, SOPs
type
feature
New capability or an extension of an existing one
type
security
Security, privacy, credential or tenant-isolation exposure
type
tracking
Tracker, checklist, initiative or punch-list spanning several changes
Milestone
No items
No Milestone
Projects
Clear projects
No projects
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: logan/server-26#156
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Found while deploy-checking commit
fb0bb15(server-26#155's incident-clearance fix). Prod IS correctly running the new code (/healthconfirmsgit_sha: fb0bb15..., healthy) — this is a CI/rollback-tooling bug, not a prod outage.Evidence (job 820, run 606, full log pulled): line ~122 of the Main Deploy step:
Logged output:
PREV_TAG=— completely empty, not even thelatestfallback.git pull, the image pulls, andcompose up -dall ran and succeeded afterward (confirmed: new images pulled, containers came up,/healthnow serves the new SHA). But becausePREV_TAGcame back empty, the step at the end (if [ -z "$PREV_TAG" ]; then ... exit 1; fi) fails the whole job with "Could not determine the previous tag from deploy output - rollback target unknown," which in turn triggers "Rollback on failed health check" — which itself immediately no-ops ("No previous tag was captured ... cannot roll back automatically") since it also has no PREV_TAG to roll back to.Root cause:
cat /opt/drb/.last_good_tag 2>/dev/null || echo latestonly falls back tolatestwhencatfails (file missing, permission denied, etc). If the file EXISTS but is EMPTY (zero bytes),catsucceeds with exit 0 and empty stdout — the||never fires, andPREV_TAGis silently set to the empty string. The health-check step (deploy.yml ~line 228-234) is the only writer of this file, viaecho '${{ gitea.sha }}' > /opt/drb/.last_good_tag— if that SSH write was ever interrupted mid-flight (network hiccup, runner timeout) after truncating but before writing the SHA, the file is left permanently empty, and every subsequent deploy reads the same empty value forever, since nothing ever rewrites it back to non-empty (the read happens before the write in each run).Impact, ongoing: checked runs 602 through 606 (5 most recent, 2026-09-13 through 2026-09-20) — every single one shows
Deploy to VM: failureat the job level. The underlyingdocker compose pull && up -dappears to succeed regardless (spot-confirmed for 605 and 606 via direct/healthSHA match), so prod itself is not stuck — but:deploy-checkskill users have to manually verify/healthevery time rather than trusting the Actions conclusion, which defeats part of the point of having it..last_good_tagneeds to be manually repaired on the VM (echo '<known-good-sha>' > /opt/drb/.last_good_tag, e.g. the current live SHA) before the fallback will readlatestagain on any FUTURE empty-file case — that alone doesn't fix the underlying bug, since a future interrupted write reproduces the same empty-file state.Suggested fix: treat an empty (not just missing) file as "no previous tag" — e.g.
PREV_TAG=$(cat /opt/drb/.last_good_tag 2>/dev/null); [ -n "$PREV_TAG" ] || PREV_TAG=latest. Separately, consider writing the new SHA to a temp file andmving it into place atomically, so an interrupted write can never leave a truncated/empty file in.last_good_tag's place.Not fixed this session — out of scope for the incident-clearance work server-26#155 was tracking; filing so it doesn't get lost.
cioterritory per CLAUDE.md (infra/deploy truth).Filed by Claude Code, 2026-09-20, while deploy-checking #155.
Fixed in
f91d455, pushed to main. Verified on run 612: "Deploy to VM" reports success (first green deploy since the lock started), and /health confirms git_shaf91d4559matches HEAD. The self-perpetuating lock is broken -- .last_good_tag is now correctly populated for future deploys.