Implement role-scoped portal deployments with Kamal
Investigated current shared-image Kamal deployment and Forgejo workflows. Recommendation: retain existing service/image initially; use kamal redeploy -d ithaca --roles portal for portal-only rollout, optionally --skip-push --version <explicit SHA> for a previously built image. Installed Kamal 2.12.0 source confirms filtered app boot/stale-container handling; synthetic local config assertion selects only portal and makes it effective primary despite configured primary_role login. redeploy keeps lock/hooks but skips proxy bootstrap and service-wide prune; ordinary deploy prunes stopped containers by service=engines, not role, potentially reducing other engines' rollback history without restarting them. Running containers/images are not pruned. Always use explicit role/version on rollback because latest-image tagging is service-wide. Current .github/workflows/deploy-ithaca.yml triggers full deploy on every main push, so adding a second manual workflow alone does not prevent full engine restarts on merge. Recommend shared CI deployment entry with all/portal scope, manual override and conservative automatic classification: only known portal-only paths can auto-select portal; shared libraries, engine code, Docker/config/workflow changes and unknown paths default to full deployment. Serialize both modes with same Ithaca concurrency group (no cancel-in-progress) plus existing Kamal lock. Skip both CI update-starting warning and pre-app-boot shutdown warnings for portal-only mode; Kamal supplies KAMAL_ROLES=portal (verified locally), but existing hook ignores it. Pin tested Kamal version rather than gem install latest. Keep existing credentials/role secrets and additive embedded portal schema initialization. Add token length preflight before touching running container and public home/status/auth-redirect/API smoke checks. Portal currently proxy:false with no Docker HEALTHCHECK; Kamal fallback only observes running/readiness delay, not HTTP readiness. Add portal-scoped HTTP health check before retiring old instance and validate external routing/persistent sessions (data-protection keys currently container-local). Avoid moving to kamal-proxy or replacing Traefik as part of initial scope. Shared image still builds all projects even for role-filtered deploy; independent rollout is achievable without independent builds. Separate image/service is optional later for build speed and isolated pruning/versioning, with explicit migration of ownership/network labels and retained shared dependency awareness (portal references PlayfieldEngine/Data). Validation: official deploy docs plus installed Kamal 2.12 source for redeploy/boot/role selection/hooks/prune/rollback; synthetic local actual Kamal configuration checks passed for role/primary selection, KAMAL_ROLES metadata and service-wide prune scope. User approved implementing role-scoped deployment and requested inclusion in existing PR #2687 on fix/allow-developers-to-manage-forms targeting main. Implement conservative portal-only selection plus manual all/portal controls, shared workflow runner logic, scoped restart announcements, serialized rollouts, pinned Kamal, preflight/readiness/smoke validation and focused tests/documentation. No production rollout requested. Implemented on existing PR branch: shared deploy-engines.sh runner; auto/portal/all scope for both destinations; actual per-role deployed SHA comparisons, conservative allowlist and stale-queue guards; portal uses role-filtered redeploy with explicit version and lock-wait, full mode retains warnings. Forgejo15 concurrency groups prevent workflow cancellation and serialize destination runs; primary branch checks and existing action URLs/labels preserved. Installed server reports 15.0.9+gitea-1.22.0, verified concurrency/manual choice inputs against versioned Forgejo15 docs. Pinned Kamal2.12.0; pre-app-boot reads KAMAL_ROLES to skip web-only warnings. Portal-only Docker HTTP health options, runtime curl, 120s deploy timeout, API-token preflight and public smoke checks added. Config tests use synthetic secrets and actual installed Kamal objects; Python tests use temporary repositories and mocked commands, never deployment hosts. README documents first full rollout, later portal-only operation, role/version rollback, shared image builds and current session-key limitation. Verified so far: 23 offline tests pass; actual merged Ergo/Ithaca configuration tests pass for selected role/primary/readiness/port/forwarded headers/metrics/secret isolation/hook metadata; shell syntax and ShellCheck pass; actionlint passes with only existing Forgejo absolute-action URL/custom-label incompatibilities excluded; public portal GET smoke checks pass on current deployment. Read-only static feature review found no verified issue but was partial: generated pycache binary (subsequently removed) and external Kamal/Forgejo implementation unavailable to reviewers; no approval claimed. Final local validation passes: 23 offline deployment tests, actual merged Kamal2.12 config assertions for both destinations, shell syntax/ShellCheck, actionlint with only Forgejo URL/label exclusions, and git diff --check. Added durable test-kamal-config.rb to run in both destination workflows after pinned gem installation with synthetic credentials. No full multi-engine image build, real deployment/rollback, or remote workflow parse execution yet. No production rollout requested or performed. Committed as cd13254eb (feature: Add role-scoped portal deployments), pushed to existing fix/allow-developers-to-manage-forms branch and included in PR #2687 targeting main: https://git.project-rk.com/prk/engines/pulls/2687 . PR title/description expanded to cover both form improvements and independent deployments, with rollout limitations explicit. Working tree clean. First rollout intentionally full; subsequent portal-only code/test changes select portal role. User attempted manual portal-only rollout of 40923dee10fc54eda7d7e556f5f6a4b41025ca63. Read-only production check: replacement unhealthy/restart-looping because local AOEMU_DB_URL omitted TrustServerCertificate=True, which is present in the previous working container; Encrypt unspecified in both, SITE_DB empty in both. Credential values not printed. Old cff8ba87 portal remains running; local/public home both HTTP200, confirming readiness failure preserved old service during this attempt. No remote mutation performed. Operator must restore the existing production connection-string trust option in local deployment env and retry; do not disable encryption. New image successfully reached host, but completed rollout remains unverified.
Checklist
2 of 2 complete- Verify Kamal role-filter behavior and current workflow/hook risks; recommend safe portal-only CI and rollback flow.
- Implement and verify automatic/manual deployment scope, portal-only redeploy, warning guards and workflow serialization.
- Area
- Project Rubi-Ka
- Project
- engines
- Kind
- Feature
- Task ID
tsk_pb62mwcdgg- Updated