Is your feature request related to a problem? Please describe.
Production releases and deploys are manual. Someone has to remember to tag, the existing release.yml workflow is non-functional (references a dist/ directory that doesn't exist, uses deprecated actions/create-release@v1), and there's no formal process for merging staging into main or gating a production deploy. Docker images for production still go through Docker Hub. Self-hosters have no reliable way to pull the same artifact that runs in production.
Describe the solution you'd like
Two new workflows that replace the current release.yml and the production path in build_and_deploy.yml:
Monthly release workflow (cron + manual trigger):
- Runs on the 1st of each month via cron, and on-demand via workflow_dispatch for off-cycle releases.
- Checks whether staging has commits ahead of main. If not, skips — no empty releases.
- Fast-forward merges staging into main. If fast-forward isn't possible, the workflow fails (indicates main has diverged and needs manual intervention).
- Determines the next semver minor bump from the latest existing tag (e.g., v2.2.0 → v2.3.0).
- Builds Docker image from main, tags it with the semver version and latest, pushes to ghcr.io/open5e/open5e-api.
- Creates a GitHub Release with auto-generated release notes from merged PRs since the last tag, and attaches a data artifact (format TBD — likely the built SQLite DB).
- Does not deploy. The release is an artifact checkpoint. Basically "this is everything written and merged to staging in the last month!"
Approval-gated production deploy workflow:
- Triggers on GitHub Release published.
- Targets a production GitHub environment configured with protection rules requiring approval from a defined set of repo admins.
- Pulls the already-built, already-tagged image from GHCR. No rebuild.
- Deploys to the production DigitalOcean app.
- Runs the existing smoke test against the production URL.
The approval flow: the release is created automatically on the 1st. Admins get a notification, review the release notes in GitHub, then approve the pending deploy in the Actions tab. If they don't approve, production stays on the previous release. We can probably wire this up to discord too for alerting, but the UI layer will likely be within Github.
Describe alternatives you've considered
- calver instead of semver - probably not a good time to change as we just released v2.
- Draft release requiring publish as the approval gate - not quite right, seems to conflate "release existence" with "push this code" - two different thing.
- Rebuild during deploy - seems wasteful and can result in provenance problems.
Additional context
Depends on #926.
Docker hub CAN be completely removed, but I think we should generally tend towards mirroring instead. We'll get a decision on that before moving forward on this.
Is your feature request related to a problem? Please describe.
Production releases and deploys are manual. Someone has to remember to tag, the existing release.yml workflow is non-functional (references a dist/ directory that doesn't exist, uses deprecated actions/create-release@v1), and there's no formal process for merging staging into main or gating a production deploy. Docker images for production still go through Docker Hub. Self-hosters have no reliable way to pull the same artifact that runs in production.
Describe the solution you'd like
Two new workflows that replace the current release.yml and the production path in build_and_deploy.yml:
Monthly release workflow (cron + manual trigger):
Approval-gated production deploy workflow:
The approval flow: the release is created automatically on the 1st. Admins get a notification, review the release notes in GitHub, then approve the pending deploy in the Actions tab. If they don't approve, production stays on the previous release. We can probably wire this up to discord too for alerting, but the UI layer will likely be within Github.
Describe alternatives you've considered
Additional context
Depends on #926.
Docker hub CAN be completely removed, but I think we should generally tend towards mirroring instead. We'll get a decision on that before moving forward on this.