Skip to content

Refactor deployment and migration handling for droplets#21

Merged
getBoolean merged 7 commits into
prodfrom
master
Jun 22, 2026
Merged

Refactor deployment and migration handling for droplets#21
getBoolean merged 7 commits into
prodfrom
master

Conversation

@getBoolean

Copy link
Copy Markdown
Owner

No description provided.

getBoolean and others added 7 commits June 21, 2026 04:46
…firewall.sh

Co-authored-by: Cursor <cursoragent@cursor.com>
The deploy bind-mounts ./data over /app/data, which shadowed the
data/migrations baked into the image and crashed the bot at startup
(Can't find meta/_journal.json). Relocate migrations to repo-root
migrations/ (code, in image) and keep data/ purely for runtime state,
mirroring dlsite's code-vs-state separation. Legacy CSV drop dir moves
to data/legacy-export (stays on the mounted volume).

Co-authored-by: Cursor <cursoragent@cursor.com>
Revert the migrations relocation. Keep migrations at data/migrations
(shipped in the image) and instead bind-mount only the persistent state
(data/main.sqlite and data/backups) so the image's data/migrations is no
longer shadowed by the host mount. The deploy pre-creates the sqlite file
and backups dir so Docker mounts a file/dir rather than creating a
directory in place of the db file.

Co-authored-by: Cursor <cursoragent@cursor.com>
@cursor

cursor Bot commented Jun 22, 2026

Copy link
Copy Markdown

PR Summary

High Risk
This rewrites production deploy topology (shared droplet, pull-based releases, and SQLite bind-mount behavior), so a bad deploy or mount mistake could affect both prod and dev bots or migration application.

Overview
Deployment pipeline now builds and pushes the bot image to GHCR in a new build-and-push job, then deploys by pulling that image instead of rsyncing the repo and running docker compose up --build on the VPS. The deploy step only syncs docker-compose.app.yml, writes .env (including CONTAINER_NAME and IMAGE_TAG), optionally logs the droplet into GHCR, and invokes deploy-event-queue-bot with a branch-specific app path.

Prod and dev share one DigitalOcean droplet with isolated state: prod uses /opt/event-queue-bot / queue-bot / image tag prod; dev uses /opt/event-queue-bot-nightly / queue-bot-nightly / tag master. Deploy jobs use a shared concurrency group so prod and dev deploys do not overlap on the same host.

docker-compose.app.yml runs the GHCR image with bind mounts only for main.sqlite, backups, and .env so baked-in data/migrations in the image are not shadowed by a full data/ directory mount. Cloud-init and the deploy script pre-create the sqlite file and backup dir before compose up.

Firewall reconciliation is extracted to scripts/ensure-firewall.sh and run from provision and deploy (with doctl on deploy), so SSH rules can update when provision is skipped. restart-bot.yml restarts via docker-compose.app.yml and the environment-specific container name.

Docs (INFRA.md, README-dev.md) describe the single-droplet model, GHCR setup (GHCR_PULL_TOKEN), and CLI re-provision flow; .gitattributes normalizes shell script line endings.

Reviewed by Cursor Bugbot for commit a34d8ae. Bugbot is set up for automated code reviews on this repo. Configure here.

@getBoolean getBoolean merged commit f66bee6 into prod Jun 22, 2026
7 checks passed

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default effort and found 3 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit a34d8ae. Configure here.

- name: Deploy bot
run: |
ssh -i ~/.ssh/bot_deploy_key deploy@"${BOT_HOST}" "sudo /usr/local/bin/deploy-event-queue-bot"
ssh -i ~/.ssh/bot_deploy_key deploy@"${BOT_HOST}" "sudo /usr/local/bin/deploy-event-queue-bot '${APP_PATH}'"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GHCR login user mismatch

High Severity

The deploy workflow runs docker login over SSH as the deploy user, but sudo /usr/local/bin/deploy-event-queue-bot runs docker compose pull as root. Registry credentials live per Docker client user, so a private GHCR image pull during deploy fails even when GHCR_PULL_TOKEN is set.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit a34d8ae. Configure here.

run: |
if [ -n "${GHCR_PULL_TOKEN}" ]; then
ssh -i ~/.ssh/bot_deploy_key deploy@"${BOT_HOST}" \
"echo '${GHCR_PULL_TOKEN}' | docker login ghcr.io -u '${{ github.actor }}' --password-stdin"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wrong GHCR login username

Medium Severity

When GHCR_PULL_TOKEN is used, docker login on the VPS uses ${{ github.actor }} as the username. GHCR expects the GitHub username that owns the PAT, which often differs from whoever triggered the workflow, so authentication can fail for private packages.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit a34d8ae. Configure here.

env:
DO_DROPLET_NAME: ${{ vars.DO_DROPLET_NAME || 'event-queue-bot' }}
APP_PATH: ${{ vars.APP_PATH || '/opt/event-queue-bot' }}
APP_PATH: ${{ vars.APP_PATH || (inputs.environment == 'prod' && '/opt/event-queue-bot' || '/opt/event-queue-bot-nightly') }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Restart bypasses deploy lock

Medium Severity

Prod and dev now share one droplet, and the deploy job serializes changes with concurrency group provision-and-deploy-bot-droplet. The Restart Bot workflow still uses provision-and-deploy-bot-${{ inputs.environment }}, so a manual restart can run concurrently with an in-flight deploy against the same app path and container.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit a34d8ae. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant