Deploy a base demo app from main
A pull request's preview shows what the branch does; it does not show
what main already did. This deploys the same demo from main, so the two
can be read side by side.
The deploy job follows the one in kejadlen/ketchup: it runs after build
on a push to main and hands flyctl the image build just pushed to GHCR,
rather than building the commit a second time, so what the demo runs is
the release. The app is created on the first deploy rather than by hand.
It runs demo.ru, as the per-PR previews do, so the base app is the same
fixture book and the same stand-in login and differs only in what it
calls itself.
The config is fly.base.toml, not fly.toml, and every command names it
with --config: `flyctl launch --copy-config` inside the review-app
action reads whatever fly.toml it finds and would otherwise start a
preview from this app's settings.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Rz9eYTeKgfMPoBpCjGkkaW
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 8dabe86..19145e2 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -85,3 +85,43 @@ jobs:
# --target names the commit explicitly: persist-credentials is
# false, so the tag cannot ride the leftover git credentials.
run: gh release create "${STEPS_META_OUTPUTS_VERSION}" --target "$GITHUB_SHA" --generate-notes
+
+ deploy:
+ name: Deploy base demo
+ needs: build
+ if: github.ref == 'refs/heads/main' && github.event_name == 'push'
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read # checkout, for fly.base.toml
+ environment:
+ name: demo
+ url: https://pro-tacts-base.fly.dev
+ # One deploy at a time, and a newer main is the one worth having.
+ concurrency:
+ group: fly-deploy
+ cancel-in-progress: true
+ env:
+ FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
+ FLY_APP: pro-tacts-base
+ FLY_ORG: personal
+ steps:
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
+ with:
+ persist-credentials: false
+
+ - uses: superfly/flyctl-actions/setup-flyctl@ed8efb33836e8b2096c7fd3ba1c8afe303ebbff1 # 1.6
+
+ # Created here rather than by hand, so the app is something this
+ # repository can lose and get back. `status` is how the review-app
+ # action asks the same question.
+ - name: Create the app if it is not there yet
+ run: flyctl status --app "$FLY_APP" >/dev/null 2>&1 || flyctl apps create "$FLY_APP" --org "$FLY_ORG"
+
+ # The image the build job just pushed, rather than a second build
+ # of the same commit: what the demo runs is then the release.
+ # --config is named because the file is not fly.toml; see its head
+ # for why it is not.
+ - name: Deploy the released image
+ env:
+ NEEDS_BUILD_OUTPUTS_IMAGE: ${{ needs.build.outputs.image }}
+ run: flyctl deploy --app "$FLY_APP" --config fly.base.toml --image "$NEEDS_BUILD_OUTPUTS_IMAGE"
diff --git a/fly.base.toml b/fly.base.toml
new file mode 100644
index 0000000..99f926f
--- /dev/null
+++ b/fly.base.toml
@@ -0,0 +1,40 @@
+# The base demo app, deployed from main by the deploy job in
+# .github/workflows/ci.yml, so a pull request's preview has something to
+# be read against: the same app one commit behind, or one design
+# decision behind. It runs demo.ru rather than the image's own
+# config.ru, exactly as the per-PR previews do -- the fixture book
+# seeded on first boot, and a stand-in login, since nothing writes the
+# identity header in front of it either. No volume, so every deploy
+# starts from the fixtures again.
+#
+# Named fly.base.toml rather than fly.toml, and passed to every command
+# with --config: `flyctl launch --copy-config` inside the review-app
+# action reads whatever fly.toml it finds, so a file by that name would
+# put this app's settings in the middle of building a preview's.
+#
+# The app name is Fly's, not GitHub's, so it has to be unique across all
+# of Fly and a taken one fails the deploy. ci.yml spells it twice more,
+# as FLY_APP to create it on the first deploy and in the environment's
+# URL, so a rename here is a rename in three places.
+app = 'pro-tacts-base'
+primary_region = 'sjc'
+
+[processes]
+ app = 'rackup -o 0.0.0.0 demo.ru'
+
+[http_service]
+ internal_port = 9292
+ force_https = true
+ auto_stop_machines = 'stop'
+ auto_start_machines = true
+ min_machines_running = 0
+ processes = ['app']
+
+[[vm]]
+ memory = '256mb'
+ cpu_kind = 'shared'
+ cpus = 1
+
+[env]
+ PRO_TACTS_INSTANCE_NAME = 'pro-tacts (main)'
+ PRO_TACTS_ACCENT = 'teal'