Goal
Replace the manual deploy/consolidated/deploy.sh scp/ssh workflow with a GitHub-release-triggered deploy pipeline:
- CI bundles deploy assets into a versioned tarball and publishes a GH release tagged
release-deploy-assets/<sha>
- A small deployer service running on the consolidated host polls for new releases, downloads the tarball, and applies the deploy — replacing what
deploy.sh does today
Part 1: CI — release-deploy-assets step in publish.yml
Add a final step to the test-and-publish job (after images are pushed and SHA-tagged) that:
- Collects the following files into a staging directory:
- Creates a tarball:
deploy-assets-<sha>.tar.gz
- Publishes a GH release tagged
release-deploy-assets/<sha> (pre-release, auto-generated notes) with the tarball as an asset
The release tag scheme release-deploy-assets/<sha> lets the deployer service unambiguously identify new deploy-asset releases vs. other release types (microgpt, wordchains, etc.).
Note: use POSIX-compatible shell in run: steps — the CI container default shell is sh, not bash.
Part 2: Deployer service (deploy/consolidated/deployer/)
A small long-running service (suggest: Go or shell + systemd, or a minimal Docker container already in compose.yaml) that:
- Polls the GH Releases API for new
release-deploy-assets/* releases (e.g. every 60s)
- Persists the last-applied release tag to a state file (e.g.
~/.deployer-state) to avoid re-applying on restart
- On new release detected:
- Downloads the tarball asset via the GH API
- Extracts to a working directory
- Copies config files into place (mirrors current
deploy.sh logic):
- Runs
docker compose -f compose.yaml -f docker-compose.observability.yml pull
- Runs
docker compose -f compose.yaml -f docker-compose.observability.yml up -d --remove-orphans
- Runs
docker compose exec caddy caddy reload --config /etc/caddy/Caddyfile
- Writes the new release tag to the state file
- Logs each deploy attempt with timestamp and outcome
- Needs a GH token (read:packages + contents) in the environment — sourced from
.env or a secrets file on the host
The deployer should be added to compose.yaml so it is itself managed by Docker Compose. On first boot / initialize_host.sh, it gets started with the rest of the stack.
Out of scope (v2)
- Per-service granular deploys (only restart the service whose image changed)
- Rollback automation
- Slack/webhook deploy notifications
Related
Goal
Replace the manual
deploy/consolidated/deploy.shscp/ssh workflow with a GitHub-release-triggered deploy pipeline:release-deploy-assets/<sha>deploy.shdoes todayPart 1: CI —
release-deploy-assetsstep inpublish.ymlAdd a final step to the
test-and-publishjob (after images are pushed and SHA-tagged) that:deploy/consolidated/compose.yamldeploy/consolidated/Caddyfiledeploy/consolidated/docker-compose.observability.ymldeploy/consolidated/forgejo/app.inideploy/consolidated/o11y/otel-collector.ymldeploy/consolidated/o11y/prometheus.ymldomains/r3dr/apps/r3dr_web/(static assets — until feat(r3dr): migrate UI to Cloudflare Workers with TypeScript/React/Vite #1106 lands)deploy-assets-<sha>.tar.gzrelease-deploy-assets/<sha>(pre-release, auto-generated notes) with the tarball as an assetThe release tag scheme
release-deploy-assets/<sha>lets the deployer service unambiguously identify new deploy-asset releases vs. other release types (microgpt, wordchains, etc.).Note: use POSIX-compatible shell in
run:steps — the CI container default shell issh, notbash.Part 2: Deployer service (
deploy/consolidated/deployer/)A small long-running service (suggest: Go or shell + systemd, or a minimal Docker container already in
compose.yaml) that:release-deploy-assets/*releases (e.g. every 60s)~/.deployer-state) to avoid re-applying on restartdeploy.shlogic):compose.yaml,Caddyfile,docker-compose.observability.yml→~/o11y/*→~/o11y/forgejo/app.ini→/etc/forgejo/app.ini(chown 1000:1000)r3dr_web/*→/var/www/r3dr/(until feat(r3dr): migrate UI to Cloudflare Workers with TypeScript/React/Vite #1106)docker compose -f compose.yaml -f docker-compose.observability.yml pulldocker compose -f compose.yaml -f docker-compose.observability.yml up -d --remove-orphansdocker compose exec caddy caddy reload --config /etc/caddy/Caddyfile.envor a secrets file on the hostThe deployer should be added to
compose.yamlso it is itself managed by Docker Compose. On first boot /initialize_host.sh, it gets started with the rest of the stack.Out of scope (v2)
Related