Release weekly, and publish an image (#5)
INBUXA Admin had CI and nothing after it: twelve tags inherited from upstream's numbering, no GitHub releases at all, and no image. Deploying it meant building the tree yourself. This adds the three workflows ihasmail already runs -- weekly release, publish, prune -- and the Dockerfile they need. Monday 09:37 UTC, and nothing on a quiet week. Staggered twenty minutes behind ihasmail-inbuxa's and twenty ahead of the server's, so three releases do not compete for runners and a bad Monday names one repository rather than three. The version is the difference from ihasmail. ihasmail derives its version from the commit it builds, so its release only reads. INBUXA Admin keeps its version in inbuxa-version.json, so the release writes it: the bump is committed to main and the tag names that commit. The tree a tag points at therefore reports the version the tag claims, which a tag placed beside an unbumped file cannot promise. The bump is written with a JSON parser rather than sed, because a version substituted into JSON as a string is one stray quote from a file nothing can read. The image is nginx serving the built files and nothing else -- the interface talks to the mail server from the browser, never from the container. It is built from source in the image rather than copied from dist/, so an image always matches the commit it claims. One image serves any installation: API_BASE_URL writes the `<meta name="api-base-url">` tag that README already documents as the deploy-time way to point the interface at its server. Set nothing and the container still starts, for a build that was given VITE_API_BASE_URL instead. Two things the smoke test found rather than review. Unprivileged nginx runs as uid 101, so the copied files are chowned to it or the tag can never be written. And the directory stays root's, so the entrypoint writes back through the existing file instead of `sed -i`, which replaces the file and needs to create a temp file in the directory. Verified by running it: the tag lands, a deep route falls back to index.html, hashed assets come back immutable while index.html is no-cache, it runs as uid 101, and it starts with no API_BASE_URL set.
This commit is contained in:
+31
@@ -0,0 +1,31 @@
|
||||
# INBUXA Admin as an image: the built interface and a static server for it.
|
||||
#
|
||||
# The interface is static files and nothing else -- it talks to the mail server
|
||||
# from the browser, never from here -- so this is nginx with a SPA fallback and
|
||||
# no back end of its own.
|
||||
#
|
||||
# It is built here rather than copied from `dist/`, which is committed for the
|
||||
# convenience of people serving the tree directly. An image built from a stale
|
||||
# `dist/` would be a build nobody can reproduce from the commit it claims.
|
||||
FROM docker.io/node:26-alpine AS build
|
||||
WORKDIR /build
|
||||
# The lockfile alone first, so a commit that changes no dependency reuses this
|
||||
# layer instead of resolving the tree again.
|
||||
COPY package.json package-lock.json ./
|
||||
RUN npm ci
|
||||
COPY . .
|
||||
# The version comes from inbuxa-version.json, which the release commits before
|
||||
# this builds, so there is nothing to pass in here.
|
||||
RUN npm run build
|
||||
|
||||
FROM docker.io/nginxinc/nginx-unprivileged:1.29-alpine
|
||||
# Unprivileged nginx, which runs as uid 101 and cannot bind 80. 8080 is the
|
||||
# port it listens on and the one to publish.
|
||||
EXPOSE 8080
|
||||
# Owned by the nginx user (uid 101 in this image), not root: the entrypoint
|
||||
# below rewrites index.html, and cannot if the file is root's. The directory
|
||||
# stays root's, which is why the entrypoint writes through the file rather
|
||||
# than replacing it.
|
||||
COPY --from=build --chown=101:101 /build/dist /usr/share/nginx/html
|
||||
COPY docker/nginx.conf /etc/nginx/conf.d/default.conf
|
||||
COPY --chmod=0755 docker/entrypoint.sh /docker-entrypoint.d/40-api-base-url.sh
|
||||
Reference in New Issue
Block a user