dsec-discord-bot/.github/workflows/deploy.yml
Clupai8o0 4bcfb8668f OPS-04: make the deploy assertion catch a crash-looping bot
Codex review: with `restart: unless-stopped` a bot that panics at boot is
restarted, so a single `docker inspect` still reads Running while it crash-loops
— the deploy went green while broken. The assertion now waits for the bot to
reach READY (it logs "Logged in as ..." from the Ready handler) within a
stability window AND requires RestartCount == 0, breaking early and failing the
moment a restart is observed, dumping container logs on any failure.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017XrE7F9ZuBWdQnS8CZvYDE
2026-08-30 16:00:58 +10:00

96 lines
3.5 KiB
YAML

name: Docker Compose Deploy
on:
push:
branches:
- main
# Manual re-run, for redeploying after a rollback or re-creating the
# workspace .env that the cleanup step below now removes.
workflow_dispatch:
permissions:
contents: read
jobs:
build_and_deploy:
runs-on: [self-hosted, linux]
# SEC-01: this gates nothing until required reviewers are configured on the
# "production" environment itself. See the pull request description.
environment: production
steps:
- name: Checkout code
uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
clean: true
fetch-depth: 1
- name: Create environment file
working-directory: ${{ github.workspace }}
env:
DOT_ENV: ${{ secrets.DOT_ENV }}
run: |
if [ -z "$DOT_ENV" ]; then
echo "DOT_ENV secret is missing or empty."
exit 1
fi
umask 077
printf '%s\n' "$DOT_ENV" > .env
- name: Build and deploy
working-directory: ${{ github.workspace }}
run: |
sudo docker compose up -d --build --force-recreate
echo "Container created."
# `docker compose up -d` exits 0 once the container is created, not once the
# program inside is working. And with `restart: unless-stopped` a container
# that panics at boot is restarted, so a crash loop still reads Running at any
# single instant — "Running" alone is not proof of a healthy deploy. Assert
# the bot actually reached READY (it logs "Logged in as ..." from the Ready
# handler) within a stability window AND has not restarted, and dump its logs
# and fail otherwise (OPS-04).
- name: Verify the bot came up cleanly
working-directory: ${{ github.workspace }}
run: |
id=$(sudo docker compose ps -q dsec_bot)
if [ -z "$id" ]; then
echo "::error::dsec_bot container was not created"
sudo docker compose logs --tail=200 dsec_bot || true
exit 1
fi
ready=""
for _ in $(seq 1 30); do
sleep 2
running=$(sudo docker inspect -f '{{.State.Running}}' "$id" 2>/dev/null || echo "false")
restarts=$(sudo docker inspect -f '{{.RestartCount}}' "$id" 2>/dev/null || echo "0")
# A non-zero restart count means it has already crashed at least once:
# stop waiting and fail rather than let a later restart look healthy.
if [ "$restarts" != "0" ]; then
break
fi
if [ "$running" = "true" ] \
&& sudo docker compose logs dsec_bot 2>&1 | grep -q "Logged in as"; then
ready="yes"
break
fi
done
running=$(sudo docker inspect -f '{{.State.Running}}' "$id" 2>/dev/null || echo "false")
restarts=$(sudo docker inspect -f '{{.RestartCount}}' "$id" 2>/dev/null || echo "0")
if [ "$ready" != "yes" ] || [ "$running" != "true" ] || [ "$restarts" != "0" ]; then
echo "::error::dsec_bot did not come up cleanly (running=$running restarts=$restarts ready=${ready:-no})"
sudo docker compose logs --tail=200 dsec_bot
exit 1
fi
echo "Deployment complete: dsec_bot reached READY with no restarts."
- name: Remove environment file
if: always()
working-directory: ${{ github.workspace }}
run: rm -f .env
- name: Clean up old Docker images
run: sudo docker image prune -f