Skip to content

build: Docker base images built here and pushed on release - #21

Merged
tanhtm merged 3 commits into
main2.0from
feat/daisyui-multi-theme
Oct 14, 2026
Merged

tanhtm merged 3 commits into
main2.0from
feat/daisyui-multi-theme

Conversation

@tanhtm

@tanhtm tanhtm commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Summary

The base images WN-Sense builds FROM are anhneuron/django-whiteneuron-{alpine,debian}:v<version>. Until now they were built in WN-Sense (docker/build-base.sh, push-base.sh, Dockerfile.base-*) from a git+https://…@tag install. This PR moves that flow into this repository and publishes the images automatically when a GitHub Release is published.

Image names, tag format (v<version>), user appuser (home /app) and TZ are unchanged, so WN-Sense only needs BASE=v0.4.0.0 in docker/.versions.

What's added

  • docker/Dockerfile.base-{alpine,debian}
    • Install the package from the build context through a bind mount, so no source layer ends up in the image.
    • Dependencies are pinned from uv.lock, the same set CI tests. Without pinning, today's build would get 49 of 97 packages at untested versions, e.g. openai 2 → 3 and django-debug-toolbar 7 → 8.
    • The build fails if the installed version differs from the tag (compared as normalized versions).
    • appuser is pinned to uid 100 / gid 101, the ids earlier images got, so existing volumes stay writable. USER is numeric so runAsNonRoot can verify it.
    • Alpine: the build tools (gcc musl-dev) and AUTOBAHN_USE_NVX=0 only exist while installing. Debian: DEBIAN_FRONTEND is an ARG, so it doesn't leak into downstream images.
  • docker/build-base.sh (and push-base.sh = PUSH=1) is the only build path; CI uses it too.
    • The source is git archive of GIT_REF, so local files (db.sqlite3, dist/, node_modules) never reach an image.
    • Dependencies come from that ref's uv.lock, via uv export.
    • The Dockerfiles come from the checkout, so older tags can be built too.
    • Pushing requires GIT_REF to be the release tag v<version> and a clean docker/. A published tag can't be overwritten with other code: pushing HEAD, or v0.3.5.3 (whose pyproject says 0.3.5.2), is refused with a clear message.
    • Variants are validated before the first build. PUSH=1 creates a docker-container builder when the current builder can't attach provenance/SBOM.
    • Settings use WN_PYTHON_VERSION / WN_PLATFORM, because python images export their own PYTHON_VERSION.
  • .github/workflows/docker-base.yml
    • Release published: builds and pushes both images with provenance and SBOM, alongside python-publish.yml (PyPI).
    • Manual run: any ref; pushing is opt-in and only for a release tag.
    • Pull requests touching docker/, pyproject.toml or uv.lock: build only. This PR runs it, which checks the apk/apt steps.
  • .dockerignore allowlist; README (Building & Releasing → Docker base images), CHANGELOG and v0.4.0.0 release notes.

Setup needed once

Add the repository secrets DOCKERHUB_USERNAME and DOCKERHUB_TOKEN (a Docker Hub access token with write access). The optional repository variable DOCKERHUB_NAMESPACE changes the namespace (default anhneuron).

Validation

  • Image builds in the sandbox:
    • Built both images for real. The sandbox's network policy blocks the Alpine/Debian package mirrors, so the apk/apt lines were dropped for that run; PyPI was reachable. This PR's CI covers the full Dockerfiles.
    • Inside both images: django-whiteneuron 0.4.0.0, Django 6.0.8, django-unfold 0.109.0 and openai 2.44.0, matching uv.lock. pip check is clean. The process runs as uid=100(appuser) gid=101, the compiled CSS, monitoring templates and .mo files are present, and there is no /tmp/src or db.sqlite3.
    • With a wrong WHITENEURON_VERSION, the build fails with "… but the source is 0.4.0.0".
  • Script guards (dry runs with a stubbed docker):
    • pushing HEAD is refused;
    • pushing v0.3.5.3 is refused (tag vs. pyproject mismatch);
    • a typo in a variant fails before anything is built;
    • a dirty docker/ blocks a push;
    • v0.3.8.0 builds with its own uv.lock (Django 6.0.8, django-unfold 0.99.1);
    • an inherited PYTHON_VERSION is ignored.
  • actionlint reports no issues in either workflow. The rebuilt styles.css is unchanged.

Follow-up in WN-Sense (not part of this PR)

Once this has published v0.4.0.0:

  • set BASE=v0.4.0.0 in docker/.versions;
  • remove build-base.sh, push-base.sh and Dockerfile.base-*;
  • in docker-push.sh, drop the push-base.sh call that PUSH_MODE=attested makes for TARGET=all.

The base images WN-Sense builds FROM (django-whiteneuron-{alpine,debian}
:v<version>) were built in WN-Sense from a git+https install of a tag.
They now live with the package:

- docker/Dockerfile.base-{alpine,debian}: install the package from the
  build context (bind mount, no source layer), run as appuser (/app) like
  before, and fail if WHITENEURON_VERSION != the installed version.
  Alpine build tools are a virtual package removed in the same layer.
- docker/build-base.sh / push-base.sh: same variables as WN-Sense
  (IMAGE_NAMESPACE, PYTHON_VERSION, PLATFORM) plus GIT_REF and VARIANTS;
  the context is 'git archive' of the ref, so local files (db.sqlite3,
  dist/, node_modules) never reach an image; the tag defaults to
  v<pyproject version at that ref>.
- .github/workflows/docker-base.yml: on a published GitHub Release, build
  and push both images with provenance + SBOM (tag must equal the
  pyproject version); manual runs for any ref; PRs touching docker/
  build only. Needs DOCKERHUB_USERNAME / DOCKERHUB_TOKEN secrets.
- .dockerignore allowlist; README / CHANGELOG / release notes.
- Dependencies pinned from the ref's uv.lock (uv export ->
  docker-requirements.txt), so images match what CI tests and a tag
  always rebuilds the same set (unpinned, 49 of 97 packages drifted,
  e.g. openai 2 -> 3).
- Pushing requires GIT_REF to be the release tag v<version> (and a
  clean docker/), so a published tag can't be overwritten with other
  code; the version check compares normalized versions.
- The workflow runs docker/build-base.sh (one build path; Dockerfiles
  from the checkout, so older tags build too) and no longer uses a
  write-only GHA cache.
- Variants validated before the first build; PUSH=1 uses a
  docker-container builder when the current one can't attest.
- WN_PYTHON_VERSION / WN_PLATFORM (python images export PYTHON_VERSION);
  the tag is always v<version> (no WHITENEURON_VERSION override).
- AUTOBAHN_USE_NVX=0 only while installing; DEBIAN_FRONTEND as ARG; no
  redundant ca-certificates; Alpine build deps reduced to gcc musl-dev;
  appuser pinned to uid 100 / gid 101 (the ids earlier images got) and
  USER is numeric.
@tanhtm
tanhtm merged commit 6c3fce3 into main2.0 Oct 14, 2026
4 checks passed
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