feat(docker): prepare portable images with runtime hotel configuration
CI / check (push) Successful in 1m6s
CI / deploy (push) Successful in 1m23s

This commit is contained in:
Simo committed 2026-09-07 22:37:53 +02:00
1 parent 745d0b7247
commit c4496e710b
28 files changed
+622 -104

No files matched your search

+54 -15
View File
@@ -200,24 +200,63 @@ are preserved and retried on later updates. Untracked historical images, build c
other services and persistent volumes are not pruned. Retain the history file between
updates. Before production migrations, retain your normal database backup.
### Shared prebuilt image audit
### Portable images on the Gitea registry
The current Docker build is installation-specific. A shared registry image needs
these changes before it can safely replace local builds:
Docker builds no longer load your installation's `.env`. The builder uses disposable
fixture values; the runtime reads `HOTEL_NAME`, `APP_URL`, `AUTH_SECRET`, database and
asset settings when the container starts. Missing/invalid required runtime values
stop startup with the setting names, without printing credentials.
| Configuration | Current source | Required preparation |
| --- | --- | --- |
| Avatar imager URL | `src/lib/imager.ts`, direct `NEXT_PUBLIC_IMAGER_URL` access | Supply public runtime configuration; remove the Epicnabbo-specific fallback for other installations. |
| Badge URL | Admin user badge components, `NEXT_PUBLIC_BADGE_URL` | Pass runtime configuration from the server to client components. |
| Public application URL | Diagnostics and photo helpers, `NEXT_PUBLIC_APP_URL` fallback | Use server runtime `APP_URL`; audit any generated absolute URLs. |
| Release identity | `next.config.ts`, `NEXT_PUBLIC_CMS_RELEASE` | Keep baked into the image: one commit identifies the same application binary everywhere. |
| Environment validation | `src/env.ts` requires database URL, hotel name and production auth secret | Build with isolated fixture configuration, then validate each installation at startup. Do not publish production environment files or builder images. |
| Build context | `.dockerignore` currently includes `.env`; Dockerfile copies the context into the builder | Separate build inputs from runtime secrets; inspect final image and build layers before registry publication. |
Configure `IMAGER_URL` with the actual avatar renderer endpoint and `BADGE_URL` with
the badge directory URL (or a local path). The legacy `NEXT_PUBLIC_IMAGER_URL` and
`NEXT_PUBLIC_BADGE_URL` names remain supported at runtime. If the imager points back
to the CMS `/imaging` proxy, set `IMAGING_UPSTREAM_URL` to the actual renderer to avoid
a loop. With no imager configured, the CMS uses Habbo's public renderer. Client
requests use the existing `/api/imaging/avatar` proxy. Admin badges preserve their
local `/swf/c_images/album1584` fallback. Public URL resolution uses runtime `APP_URL`.
After changing `.env`, recreate the container; an image rebuild is not required.
`NEXT_PUBLIC_CMS_RELEASE` deliberately remains compiled into the image.
This is a source audit, not a validated portable image. The next verification is to
run the **same image digest** for two installations with different names, domains,
imager/badge URLs and credentials, then check rendered pages and browser requests.
The registry publishing workflow is intentionally not enabled yet.
To publish from Gitea:
1. In the repository's Actions secrets, configure `CONTAINER_REGISTRY_USER` and
`CONTAINER_REGISTRY_TOKEN`. Use a Gitea access token with package read/write
permission belonging to an account allowed to publish under the repository owner.
2. Run **Publish portable container** manually on the commit/branch to distribute.
The workflow runs checks, builds from committed source only, and verifies the same
application image with two runtime avatar/badge configurations before pushing.
It does not deploy to production or move a `latest` tag.
3. The images are `<gitea-host>/<owner>/<repository-lowercase>:<full-commit>` and
`:<full-commit>-migrations`. Only the application image runs the website; the
migrations image is used temporarily for the matching database migrations.
For this repository the image base is
`gitlab.epicnabbo.nl/remco/epicnext-cms`. Package access is controlled by Gitea.
For private packages, run `docker login gitlab.epicnabbo.nl` on the installation
with a token that can read packages. Then update with:
```bash
CMS_IMAGE_REPOSITORY=gitlab.epicnabbo.nl/remco/epicnext-cms \
CMS_PUBLIC_URL=https://your-hotel.example \
bash scripts/docker-update.sh
```
The updater pulls the configured Git upstream and requires both images for that
exact commit. A missing image or failed login stops before replacing the running
CMS. Local builds remain the default when `CMS_IMAGE_REPOSITORY` is unset. Both
paths retain the existing image/HTTP checks and automatic application rollback.
Migration secrets are mounted read-only for the temporary migration container;
they are never copied into its image. Registry images currently target the Linux
architecture of the self-hosted build runner; this is not a multi-architecture release.
The portability gate checks release identity, runtime avatar/badge routing and
absence of installation environment files in the application image. It uses an
unreachable fixture database and does not replace a full live database/site smoke
test. See `scripts/verify-portable-image.mjs`. Production deployment remains verified
separately by the existing CI workflow.
References: [Gitea container registry](https://docs.gitea.com/usage/packages/container/)
and [Next.js runtime environment variables](https://nextjs.org/docs/app/guides/self-hosting).
### Diagnose an update that is not visible