The deployer image, plus Node and emailmd, so GitLab runners can render the maintenance mails into email-client-safe HTML.
The vnv_maintenance role in
tools/ansible/automation
converts its markdown templates to HTML through emailmd, an npm package that needs
Node >= 22 (through its dependencies). The deployer image ships no Node and has no
reason to, for projects that only deploy — hence this derived image, reserved for the
maintenance pipelines.
vnvsa/deployer:new-latest provides (Ansible, ansible-lint, ssh, rsync, wkhtmltopdf, …);nodejs (Node 22.x from the NodeSource repository, npm included);emailmd, installed globally, at the version pinned by the EMAILMD_VERSION build arg.Dockerfile linksEMAILMD_VERSION (default 0.10.0) fixes the globally installed version, and this image
is the authority on which version the pipelines use.
On the Ansible side, vnv_maintenance_mail_html_emailmd_bin is npx --yes emailmd,
with no version. That is deliberate: npx --yes emailmd@<version> queries
registry.npmjs.org even when the package is already installed globally, and therefore
fails on a runner with no npm access. Without a version, npx resolves the binary from
PATH, i.e. this image's. So changing the emailmd version means rebuilding this image, not
editing the role.
Debian 13 caps out at Node 20.19, while a transitive emailmd dependency (commander@15)
requires Node >= 22.12. On the Debian package npm install still goes through, but emits
an EBADENGINE — that holds by luck, not by contract. The image therefore installs Node 22
from NodeSource, through NODE_MAJOR (default 22).
The key and the repository file are laid down by hand, without running the remote
setup_22.x script as root, and the /usr/share/keyrings/nodesource.gpg keyring is
authorised for that one repository only (signed-by=).
The point of this image: anyone can render the maintenance mails with Docker alone — no Node,
no npm, not even uv, since the image carries its own Ansible. From a checkout of
tools/ansible/automation:
make docker-emails-preview
which is this, with the flags that keep it painless:
docker run --rm \
--user "$(id -u):$(id -g)" \
--env HOME=/tmp \
--env "USER=$(id -un)" \
-v "$(pwd):/workspace" \
-v "$HOME/.vault_pass.txt:/tmp/.vault_pass.txt:ro" \
--workdir=/workspace \
vnvsa/updater:latest \
ansible-playbook playbooks/devops/mail_preview.yml -i localhost,
Three of those flags are not optional:
--user keeps the rendered files owned by you instead of root.HOME=/tmp because ansible.cfg resolves the vault password file through ~, and a
non-root user cannot traverse /root. The vault file is mounted to match.USER because the host uid has no passwd entry in the image, and Ansible refuses to
start without a resolvable username.To render a single markdown file rather than the whole playbook, the CLI is on the PATH:
docker run --rm -v "$(pwd):/w" -w /w vnvsa/updater:latest emailmd mail.md -o mail.html
docker run --rm -v "$(pwd):/w" -w /w vnvsa/updater:latest emailmd lint mail.md
./local-build.sh
update:
image: vnvsa/updater:latest
script:
- uv run ansible-playbook playbooks/os/minor_update.yml -i environments/prod \
--extra-vars vnv_maintenance_mail_html_enabled=true
Content type
Image
Digest
sha256:bb01c438a…
Size
672.2 MB
Last updated
24 days ago
docker pull vnvsa/updater