Skip to content

Iter-9: realign admin deploy with bunny canonical pattern (ghcr.io)#10

Merged
ractive merged 2 commits into
mainfrom
iter-9/admin-go-live-ghcr
May 5, 2026
Merged

Iter-9: realign admin deploy with bunny canonical pattern (ghcr.io)#10
ractive merged 2 commits into
mainfrom
iter-9/admin-go-live-ghcr

Conversation

@ractive
Copy link
Copy Markdown
Owner

@ractive ractive commented May 5, 2026

Summary

  • Switch admin image hosting from bunny container registry → ghcr.io (drops 3 secrets, uses GITHUB_TOKEN automatically). Matches https://docs.bunny.net/magic-containers/deploy-with-github-actions.md.
  • Replace homemade curl trigger with BunnyWay/actions/container-update-image@main for the container roll.
  • Standardise on secrets.BUNNYNET_API_KEY (matches bunny docs and the secret name that already existed in the repo since 2026-04-20). Aligned build-homepage on the same secret so there's one bunny API key, one rotation point.
  • Gate build-admin on vars.APP_ID != '' so it skips cleanly before the admin Magic Container exists.
  • Add permissions: { contents: read, packages: write } for the ghcr push.
  • Dockerfile fix: set placeholder env vars in the builder stage so next build (page-data collection) succeeds when the Dockerfile is run outside the verify-step's env injection. ENV doesn't cross FROM boundaries — runtime image is unaffected.

Pre-merge state set up by the iter-9 cutover

Already provisioned and ready for this PR's deploy:

  • Magic Container app: wardrobe-assistants-admin (id h4vme6Uhod4W3Yu)
  • libSQL DB: wa-admin-prod (id db_01KQV95KJ611YYT48VSZKHC495); migrations applied; full+RO tokens minted
  • Container template env vars set (DATABASE_URL, DATABASE_AUTH_TOKEN, BETTER_AUTH_*, RESEND_API_KEY, EMAIL_FROM, NODE_ENV, PORT, HOSTNAME)
  • DNS: admin.wardrobe-assistants.chmc-r6f39iacv2.b-cdn.net
  • TLS: free Let's Encrypt cert issued, force-SSL enabled
  • GitHub: vars.APP_ID = h4vme6Uhod4W3Yu; BUNNYNET_API_KEY aligned

What happens on merge

  1. verify runs (typecheck + Vitest + both Next builds with placeholders) — green expected.
  2. build-homepage rebuilds + uploads to bunny Storage (already at parity from manual upload, will be a no-op refresh + cache purge).
  3. build-admin builds the admin Docker image, pushes to ghcr.io/ractive/wardrobe-assistants-admin:<sha> and :latest, then BunnyWay/actions/container-update-image rolls the Magic Container to the new tag.
  4. https://admin.wardrobe-assistants.ch transitions from bunny's "We're deploying your app!" placeholder to the admin sign-in page.

Test plan

  • CI: verify, build-homepage, build-admin all pass.
  • After roll: curl -I https://admin.wardrobe-assistants.ch/login → 200.
  • Browser sign-in with seeded user → TOTP enrollment → "Hello, {email}" dashboard.
  • https://wardrobe-assistants.ch continues to serve the homepage from the new Pull Zone (no regression).

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Chores
    • Updated deployment infrastructure to streamline container deployments
    • Refined environment and authentication configuration for build processes

…self-build

Workflow:
- Switch admin image hosting from bunny container registry to ghcr.io
  (drops BUNNY_REGISTRY/USERNAME/PASSWORD secrets; GITHUB_TOKEN suffices)
- Replace curl-based deploy trigger with BunnyWay/actions/container-update-image
- Use secrets.BUNNYNET_API_KEY (matches bunny docs and existing secret name);
  align build-homepage on the same secret for consistency
- Gate build-admin on vars.APP_ID being set so the job skips cleanly before
  the admin Magic Container app is provisioned
- Add packages:write permission for the ghcr push

Dockerfile:
- Set placeholder env in builder stage so Next's build-time page-data
  collection doesn't fail when run outside CI's verify-step env injection.
  ENV doesn't cross FROM boundaries — values stay in builder, runner uses
  real container env vars.

Iteration: iter-9 (go live).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 5, 2026

Warning

Rate limit exceeded

@ractive has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 55 minutes and 7 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 018ca13b-7d14-4410-b608-8bbfaabb73b3

📥 Commits

Reviewing files that changed from the base of the PR and between 4f616d2 and ca45b50.

📒 Files selected for processing (1)
  • apps/admin/scripts/seed-admin.ts
📝 Walkthrough

Walkthrough

This PR updates deployment infrastructure for two services: the homepage now uses the correct BUNNYNET_API_KEY secret for Bunny storage uploads, and the admin service migrates from Bunny Registry to GitHub Container Registry with automated Magic Container rollout via the BunnyWay action. Build-time environment variables are added to the admin Dockerfile.

Changes

Homepage Static Deployment

Layer / File(s) Summary
Bunny Storage API Update
.github/workflows/deploy.yml
Homepage upload step now uses secrets.BUNNYNET_API_KEY instead of secrets.BUNNY_API_KEY.

Admin Container Deployment

Layer / File(s) Summary
Build Configuration
apps/admin/Dockerfile
Builder stage defines placeholder ENV variables (DATABASE_URL, BETTER_AUTH_SECRET, BETTER_AUTH_URL, RESEND_API_KEY, EMAIL_FROM) for Next.js build-time data collection.
Job Conditions & Permissions
.github/workflows/deploy.yml
Admin job now runs only on main branch pushes when vars.APP_ID != ''; permissions grant contents: read and packages: write.
Registry & Authentication
.github/workflows/deploy.yml
Docker login switched from Bunny Registry to GitHub Container Registry (ghcr.io) using docker/login-action with GITHUB_TOKEN.
Image Build & Push
.github/workflows/deploy.yml
Docker build/push tags updated to ghcr.io/${{ github.repository_owner }}/wardrobe-assistants-admin with ${{ github.sha }} and latest tags.
Container Rollout
.github/workflows/deploy.yml
Manual curl-based Magic Container deployment replaced with BunnyWay/actions/container-update-image@main, passing app_id, api_key (BUNNYNET_API_KEY), container name, and image_tag.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

  • ractive/wardrobe-assistants.ch#9: Both PRs update the admin deployment pipeline to bunny.net, including Docker build, registry configuration, and Magic Container rollout via shared secrets and variables.

Poem

🐰 A workflow refined, so shiny and new,
From Bunny's old paths to GitHub's bright view,
Environment whispers in Docker's dark night,
Build-time secrets prepared for the flight,
Deployment made simpler—one push, and we're bright! 🚀

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the main change: realigning the admin deployment to use GitHub Container Registry (ghcr.io) and follow the canonical Bunny deployment pattern.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch iter-9/admin-go-live-ghcr

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
.github/workflows/deploy.yml (1)

91-144: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Update README.md and kb/runbook-go-live.md to match the new secret/variable names.

The workflow now reads secrets.BUNNYNET_API_KEY and vars.APP_ID, but README.md (lines 64-71) still documents secrets.BUNNY_API_KEY and vars.ADMIN_APP_ID, and kb/runbook-go-live.md (line 253) likewise references ADMIN_APP_ID. Per the PR objectives the workflow names are intentional (matching the existing repo secret/variable), so the docs are the side that's stale. Without an update, an operator following the runbook to reprovision secrets/vars after a token rotation or repo migration will set the wrong names and build-admin will silently skip (because of the vars.APP_ID != '' gate at line 93) or the homepage upload will fail authentication.

A vars.APP_ID named that generically also won't scale — if a second Magic Container app is ever added (e.g. a worker), you'll likely want ADMIN_APP_ID to disambiguate. Worth considering renaming the GitHub variable now while there's only one app, but at minimum sync the docs.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/deploy.yml around lines 91 - 144, Docs are out of sync
with the workflow: update README and kb/runbook-go-live.md to reference the
workflow's actual secret/variable names (secrets.BUNNYNET_API_KEY and
vars.APP_ID) instead of the old names (secrets.BUNNY_API_KEY and
vars.ADMIN_APP_ID) so the build-admin job's conditions and the Magic Container
upload authenticate correctly; while editing, optionally add a note suggesting
renaming vars.APP_ID to a more specific name like ADMIN_APP_ID if you plan
multiple Magic Container apps in future to avoid ambiguity.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/deploy.yml:
- Around line 137-144: The workflow uses the third-party action reference
BunnyWay/actions/container-update-image@main which is a moving ref and gives
deploy-time access to secrets (secrets.BUNNYNET_API_KEY) — replace the `@main` tag
with the full 40-character commit SHA for
BunnyWay/actions/container-update-image to pin the exact code used when updating
the wardrobe-assistants-admin container (container: wardrobe-assistants-admin,
image_tag: ${{ github.sha }}); add a short comment like “# vX.Y.Z” for
readability and update the SHA via Dependabot or PRs, and confirm the pinned
commit is compatible with future Node.js runtime changes referenced in
BunnyWay/actions#25.

---

Outside diff comments:
In @.github/workflows/deploy.yml:
- Around line 91-144: Docs are out of sync with the workflow: update README and
kb/runbook-go-live.md to reference the workflow's actual secret/variable names
(secrets.BUNNYNET_API_KEY and vars.APP_ID) instead of the old names
(secrets.BUNNY_API_KEY and vars.ADMIN_APP_ID) so the build-admin job's
conditions and the Magic Container upload authenticate correctly; while editing,
optionally add a note suggesting renaming vars.APP_ID to a more specific name
like ADMIN_APP_ID if you plan multiple Magic Container apps in future to avoid
ambiguity.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 78bbdd43-c6cf-4dee-95dd-cf2fce2d7682

📥 Commits

Reviewing files that changed from the base of the PR and between f9df005 and 4f616d2.

📒 Files selected for processing (2)
  • .github/workflows/deploy.yml
  • apps/admin/Dockerfile

Comment on lines +137 to +144
- name: Roll admin Magic Container to new image
if: steps.changes.outputs.app == 'true'
env:
BUNNY_API_KEY: ${{ secrets.BUNNY_API_KEY }}
ADMIN_APP_ID: ${{ vars.ADMIN_APP_ID }}
run: |
curl -fsS -X POST \
-H "AccessKey: $BUNNY_API_KEY" \
"https://api.bunny.net/magic-containers/apps/$ADMIN_APP_ID/deploy"
uses: BunnyWay/actions/container-update-image@main
with:
app_id: ${{ vars.APP_ID }}
api_key: ${{ secrets.BUNNYNET_API_KEY }}
container: wardrobe-assistants-admin
image_tag: ${{ github.sha }}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

GitHub Actions security hardening pin third-party action commit SHA

💡 Result:

To harden GitHub Actions security, especially for third-party actions, pin them to a full-length (40-character) commit SHA instead of mutable tags or branches. This prevents supply chain attacks where tags are rewritten or repositories compromised to inject malicious code, as SHAs are immutable [1][2][3]. Example workflow change: Before: uses: some/action@v1 After: uses: some/action@692973e3d937129bcbf40652eb9f2f61becf3332 # v1.0.0 [1][3][4] How to find the SHA [3]: 1. Go to the action's repo (e.g., https://github.com/owner/repo for owner/repo). 2. Switch to the tag/branch (e.g., v1). 3. Copy the full commit SHA from the commit page. Or run: git ls-remote --tags https://github.com/owner/repo.git v1 [4]. Or check a workflow run log under "Set up job" for the resolved SHA [3]. Add a comment with the version for reference [5][4][6]. Use Dependabot to update pinned SHAs automatically [1]. Enforce via policies: At repo/org/enterprise level, enable "Require full commit SHA for actions" in Actions settings > General > Policies. Unpinned workflows fail [2][7]. Prefer verified creators from Marketplace [1]. Tools like GitHub Actions Lockfile can generate/verify lockfiles with SHAs [8].

Citations:


🌐 Web query:

BunnyWay/actions container-update-image action GitHub repository

💡 Result:

The BunnyWay/actions/container-update-image is a GitHub Action within the BunnyWay/actions repository [1]. It updates container images for Bunny.net's Magic Containers platform, automating rolling updates after building and pushing new Docker images [2][3]. Repository details: https://github.com/BunnyWay/actions (created 2024-08-09, last push 2026-03-27, 13 stars, JavaScript primary language) [1]. The action is in the container-update-image folder, with its own documentation there [1]. Latest release: container-update-image_0.2.1 (2026-03-27) [1][4]. Usage example from official Bunny.net docs [2]: - name: Update container image on Magic Containers uses: BunnyWay/actions/container-update-image@main with: app_id: ${{ vars.APP_ID }} api_key: ${{ secrets.BUNNYNET_API_KEY }} container: app image_tag: "${{ github.sha }}" Required inputs [2]: - app_id: Magic Containers App ID - api_key: Bunny.net API key (no sub-user support) - container: Container name - image_tag: New image tag Recent updates include support for optional image_digest [5], removing imageDigest when updating imageTag [4], Node.js 20 (issue open for upgrade to 24) [6]. It's used in workflows for Magic Containers deployments [7].

Citations:


🌐 Web query:

GitHub Actions Node.js 20 deprecated June 2026 timeline

💡 Result:

GitHub Actions is deprecating Node.js 20 support on hosted runners. Starting June 2nd, 2026, runners will default to Node.js 24 for JavaScript actions.[1][2] Users can opt out temporarily by setting ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION=true in their workflow or runner environment, but Node 20 will be fully removed later in fall 2026.[1] Node.js 20 reaches upstream EOL on April 30, 2026.[3] Warnings already appear for actions using Node 20, like actions/checkout@v4.[4] Newer runner versions (e.g., v2.328.0+) support both Node 20 and 24, defaulting to 20 currently.[1] To test Node 24 early, set FORCE_JAVASCRIPT_ACTIONS_TO_NODE24=true.[1] Action maintainers should update to Node 24 (via actions configuration).[1] Users should pin latest action versions supporting Node 24. setup-node@v4+ supports Node 22/24.[5] ubuntu-latest (ubuntu-24.04) currently defaults to Node 20.x, discussions ongoing to update post-EOL.[3]

Citations:


🌐 Web query:

GitHub Actions node20 runtime deprecation schedule

💡 Result:

GitHub Actions Node 20 runtime deprecation schedule, as of today's date (2026-05-05): Node.js 20 reaches end-of-life (EOL) on 2026-04-30 [1][2]. Deprecation process started due to EOL. Runners currently default to Node 20 but support Node 24 [1]. Key dates: - Beginning June 2, 2026: Runners will use Node 24 by default for JavaScript actions. Workflows can opt out to continue using Node 20 by setting ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION=true env var (until full removal) [1][3]. - Later in fall 2026 (around September/October): Node 20 fully removed from runners [1][4]. Editor's note on the changelog updated February 25, 2026, confirms migration to June 2026 [1]. To test Node 24 early: Set FORCE_JAVASCRIPT_ACTIONS_TO_NODE24=true [1]. Action maintainers should update actions.yml to 'node24' [1]. Ubuntu-latest currently defaults to Node 20.20.2, discussions ongoing to update post-EOL, ideally aligning with June 2 [2].

Citations:


Pin BunnyWay/actions/container-update-image to a commit SHA instead of @main.

@main is a moving reference: any push to that branch (including a compromised one) immediately runs in your release pipeline with access to secrets.BUNNYNET_API_KEY and the ability to roll the production Magic Container. GitHub's hardening guide and OpenSSF Scorecard both recommend pinning third-party actions to a full 40-character commit SHA. You can leave a # v… comment alongside for readability, and Dependabot can still bump the SHA on its schedule.

A secondary concern: per BunnyWay/actions#25, container-update-image currently declares using: 'node20'. GitHub is forcing Node.js 20 to Node.js 24 starting June 2, 2026 (Node.js 20 reaches EOL April 30, 2026). Pinning to a specific SHA makes that future breakage visible and controllable rather than a surprise mid-deploy.

🔒 Suggested change
-        uses: BunnyWay/actions/container-update-image@main
+        # pin: replace <sha> with the full commit SHA of the desired version
+        uses: BunnyWay/actions/container-update-image@<sha>  # main as of YYYY-MM-DD
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/deploy.yml around lines 137 - 144, The workflow uses the
third-party action reference BunnyWay/actions/container-update-image@main which
is a moving ref and gives deploy-time access to secrets
(secrets.BUNNYNET_API_KEY) — replace the `@main` tag with the full 40-character
commit SHA for BunnyWay/actions/container-update-image to pin the exact code
used when updating the wardrobe-assistants-admin container (container:
wardrobe-assistants-admin, image_tag: ${{ github.sha }}); add a short comment
like “# vX.Y.Z” for readability and update the SHA via Dependabot or PRs, and
confirm the pinned commit is compatible with future Node.js runtime changes
referenced in BunnyWay/actions#25.

Better Auth's enableTwoFactor requires an active session — the CLI
seed script was calling it with empty Headers, getting Unauthorized,
and leaving newly-created users without TOTP enrolled.

Fix: signInEmail to obtain the session cookie, then pass that cookie
to enableTwoFactor.

Tested locally: admin user james@ractive.ch created on the prod DB
with TOTP enrolled. QR + backup codes printed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@ractive ractive merged commit 6be2c29 into main May 5, 2026
4 checks passed
@ractive ractive deleted the iter-9/admin-go-live-ghcr branch May 5, 2026 05:44
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