mirror of
https://github.com/dsec-hub/dsec-discord-bot.git
synced 2026-09-22 15:53:56 +00:00
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
96 lines
3.5 KiB
YAML
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
|