feat: AtroPIM image pipeline with secret-free build (AZ-NIX-ava.1)

- flake input atrocore-docker rev-pinned (e1e9bed)
- pkgs/atropim-image: atropim-build/atropim-push scripts + 2-stage
  podman build (vendor pdf target + entrypoint wrapper)
- entrypoint writes ATRO_DB_* to data/config.php at runtime,
  guarded by isInstalled (idempotent); no DB credentials in layers
- devShell documents build/push commands and ENV variables
This commit is contained in:
2026-08-17 13:00:06 +02:00
parent 5132a4de2f
commit 705702abee
8 changed files with 217 additions and 4 deletions
+95
View File
@@ -0,0 +1,95 @@
{
lib,
pkgs,
atrocore-docker,
registryHost ? "git.az-gruppe.com",
registryNamespace ? "az-intec-gmbh",
imageName ? "atrocore-web",
skeletonVariant ? "pim-no-demo",
buildVariant ? "pdf",
productionDomain ? "pim.l.az-gruppe.com",
productionStability ? "stable",
}: let
imageRef = "${registryHost}/${registryNamespace}/${imageName}";
baseTag = "localhost/${imageName}-base:build";
entrypointContext = ./entrypoint;
# The vendor Dockerfile uses the unqualified FROM "php:8.4-apache-bookworm";
# resolve it against docker.io without touching the host's registries.conf.
registriesConf = pkgs.writeText "atropim-registries.conf" ''
unqualified-search-registries = ["docker.io"]
'';
podman = lib.getExe pkgs.podman;
atropim-build = pkgs.writeShellApplication {
name = "atropim-build";
runtimeInputs = with pkgs; [coreutils];
text = ''
# Two-stage build: stage 1 builds the untouched vendor image from the
# rev-pinned atrocore/docker flake input, stage 2 layers the secret-free
# entrypoint wrapper on top. All DB build args stay empty on purpose:
# no credentials are ever baked into any layer.
export CONTAINERS_REGISTRIES_CONF="${registriesConf}"
echo "[atropim-build] stage 1: vendor image from ${atrocore-docker} (target ${buildVariant})"
${podman} build \
--target "${buildVariant}" \
--build-arg "SKELETON_VARIANT=${skeletonVariant}" \
--build-arg "PRODUCTION_DOMAIN=${productionDomain}" \
--build-arg "PRODUCTION_STABILITY=${productionStability}" \
--build-arg "PRODUCTION_DB=" \
--build-arg "DB_USER=" \
--build-arg "DB_PASSWORD=" \
--tag "${baseTag}" \
--file "${atrocore-docker}/.docker/php/Dockerfile" \
"${atrocore-docker}/.docker"
echo "[atropim-build] stage 2: entrypoint wrapper -> ${imageRef}:latest"
${podman} build \
--build-arg "BASE_IMAGE=${baseTag}" \
--label "org.opencontainers.image.title=${imageName}" \
--label "org.opencontainers.image.description=AtroPIM (${skeletonVariant}) web image, secret-free build with runtime DB config" \
--tag "${imageRef}:latest" \
--file "${entrypointContext}/Dockerfile" \
"${entrypointContext}"
echo "[atropim-build] done: ${imageRef}:latest"
echo "[atropim-build] verify the image is secret-free:"
echo " podman run --rm ${imageRef}:latest ls /var/www/${productionDomain}/data"
'';
};
atropim-push = pkgs.writeShellApplication {
name = "atropim-push";
runtimeInputs = with pkgs; [coreutils];
text = ''
VERSION="''${1:-$(date +%Y%m%d)}"
if ! ${podman} image exists "${imageRef}:latest"; then
echo "error: ${imageRef}:latest not found - run atropim-build first" >&2
exit 1
fi
if ! ${podman} login --get-login "${registryHost}" >/dev/null 2>&1; then
echo "not logged in to ${registryHost} - run: podman login ${registryHost}" >&2
exit 1
fi
echo "[atropim-push] pushing ${imageRef}:{latest,''${VERSION}}"
${podman} tag "${imageRef}:latest" "${imageRef}:''${VERSION}"
${podman} push "${imageRef}:''${VERSION}"
${podman} push "${imageRef}:latest"
echo "[atropim-push] done - package visible at https://${registryHost}/${registryNamespace}/-/packages"
'';
};
in
pkgs.symlinkJoin {
name = "atropim-tools";
paths = [atropim-build atropim-push];
meta = {
description = "Build/push tooling for the secret-free AtroPIM image (${imageRef})";
platforms = lib.platforms.linux;
};
}
+13
View File
@@ -0,0 +1,13 @@
# Stage 2 of the AtroPIM image pipeline: takes the vendor-built base image
# (atrocore/docker, target "pdf") and layers the secret-free entrypoint
# wrapper on top. The base image reference is injected via BASE_IMAGE so the
# vendor Dockerfile stays untouched (pinned flake input).
ARG BASE_IMAGE
FROM ${BASE_IMAGE}
COPY entrypoint.sh /entrypoint.sh
COPY entrypoint-prepare-pim.php /entrypoint-prepare-pim.php
RUN chmod +x /entrypoint.sh
CMD ["/entrypoint.sh"]
@@ -0,0 +1,32 @@
<?php
/* Runtime counterpart of the vendor's prepare-pim.php (atrocore/docker). */
if (empty($argv[5])) {
exit("Usage: php entrypoint-prepare-pim.php <instance-dir> <db-host> <db-name> <db-user> <db-password>\n");
}
$instanceDir = $argv[1];
chdir($instanceDir);
set_include_path($instanceDir);
require_once 'vendor/autoload.php';
$app = new \Atro\Core\Application();
$config = $app->getContainer()->get('config');
if ($config->get('isInstalled')) {
exit("[entrypoint] instance already installed - keeping existing data/config.php\n");
}
$config->set('database', [
'driver' => 'pdo_pgsql',
'host' => $argv[2],
'port' => '',
'charset' => 'utf8',
'dbname' => $argv[3],
'user' => $argv[4],
'password' => $argv[5],
]);
$config->set('useChromeNoSandbox', true);
$config->save();
@@ -0,0 +1,24 @@
#!/bin/sh
# Secret-free AtroPIM image entrypoint.
#
# Before cron/apache start, the ATRO_DB_* environment variables are written
# into data/config.php of the instance directory (same pattern as the vendor's
# prepare-pim.php). The PHP side skips the write once the instance is
# installed (isInstalled), which makes the wrapper idempotent: a config
# completed by the web installer is never overwritten.
set -e
INSTANCE_DIR="/var/www/${ATRO_INSTANCE_DIR:-pim.l.az-gruppe.com}"
if [ -n "${ATRO_DB_HOST:-}" ] && [ -n "${ATRO_DB_NAME:-}" ] && [ -n "${ATRO_DB_USER:-}" ] && [ -n "${ATRO_DB_PASSWORD:-}" ]; then
echo "[entrypoint] applying ATRO_DB_* variables to ${INSTANCE_DIR}/data/config.php"
mkdir -p "${INSTANCE_DIR}/data"
php /entrypoint-prepare-pim.php "${INSTANCE_DIR}" "${ATRO_DB_HOST}" "${ATRO_DB_NAME}" "${ATRO_DB_USER}" "${ATRO_DB_PASSWORD}"
# The web installer (running as www-data) must stay able to update the config later.
chown www-data:www-data "${INSTANCE_DIR}/data/config.php"
else
echo "[entrypoint] no ATRO_DB_* variables set - starting web installer"
fi
exec /startup.sh
+6 -1
View File
@@ -1,4 +1,9 @@
{pkgs, ...}: {
{
pkgs,
atrocore-docker,
...
}: {
# Define your custom packages here
online3dviewer = pkgs.callPackage ./online3dviewer {};
atropim-tools = pkgs.callPackage ./atropim-image {inherit atrocore-docker;};
}