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:
Generated
+18
@@ -145,6 +145,23 @@
|
|||||||
"url": "https://code.m3ta.dev/m3tam3re/AGENTS"
|
"url": "https://code.m3ta.dev/m3tam3re/AGENTS"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"atrocore-docker": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1786515722,
|
||||||
|
"narHash": "sha256-17KU/c6l4SEoqM13vdvplI0ENxCHJ65SyG0gpzbhyqg=",
|
||||||
|
"owner": "atrocore",
|
||||||
|
"repo": "docker",
|
||||||
|
"rev": "e1e9bed1f6ae983d1aac474657cbeba1c004e313",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "atrocore",
|
||||||
|
"repo": "docker",
|
||||||
|
"rev": "e1e9bed1f6ae983d1aac474657cbeba1c004e313",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"azess": {
|
"azess": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
@@ -1480,6 +1497,7 @@
|
|||||||
"inputs": {
|
"inputs": {
|
||||||
"agenix": "agenix",
|
"agenix": "agenix",
|
||||||
"agents": "agents",
|
"agents": "agents",
|
||||||
|
"atrocore-docker": "atrocore-docker",
|
||||||
"azess": "azess",
|
"azess": "azess",
|
||||||
"azion-scheduler": "azion-scheduler",
|
"azion-scheduler": "azion-scheduler",
|
||||||
"disko": "disko",
|
"disko": "disko",
|
||||||
|
|||||||
@@ -70,6 +70,12 @@
|
|||||||
url = "git+https://git.az-gruppe.com/AZ-Intec-GmbH/phishboard.git";
|
url = "git+https://git.az-gruppe.com/AZ-Intec-GmbH/phishboard.git";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
atrocore-docker = {
|
||||||
|
# Rev-pinned vendor Dockerfiles for the AtroPIM image pipeline (AZ-NIX-ava.1)
|
||||||
|
url = "github:atrocore/docker/e1e9bed1f6ae983d1aac474657cbeba1c004e313";
|
||||||
|
flake = false;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = {
|
outputs = {
|
||||||
@@ -90,8 +96,11 @@
|
|||||||
];
|
];
|
||||||
forAllSystems = nixpkgs.lib.genAttrs systems;
|
forAllSystems = nixpkgs.lib.genAttrs systems;
|
||||||
in {
|
in {
|
||||||
packages =
|
packages = forAllSystems (system:
|
||||||
forAllSystems (system: import ./pkgs nixpkgs.legacyPackages.${system});
|
import ./pkgs {
|
||||||
|
pkgs = nixpkgs.legacyPackages.${system};
|
||||||
|
atrocore-docker = inputs.atrocore-docker;
|
||||||
|
});
|
||||||
overlays = let
|
overlays = let
|
||||||
all = import ./overlays {inherit inputs;};
|
all = import ./overlays {inherit inputs;};
|
||||||
in
|
in
|
||||||
@@ -116,6 +125,7 @@
|
|||||||
opencode
|
opencode
|
||||||
# pi-coding-agent
|
# pi-coding-agent
|
||||||
inputs.agenix.packages.${system}.default
|
inputs.agenix.packages.${system}.default
|
||||||
|
outputs.packages.${system}.atropim-tools
|
||||||
];
|
];
|
||||||
|
|
||||||
shellHook = ''
|
shellHook = ''
|
||||||
@@ -140,6 +150,19 @@
|
|||||||
echo " - ls .opencode-rules/ Browse available rules"
|
echo " - ls .opencode-rules/ Browse available rules"
|
||||||
echo " - nix develop Re-enter this shell"
|
echo " - nix develop Re-enter this shell"
|
||||||
echo ""
|
echo ""
|
||||||
|
echo "🐘 AtroPIM Image Pipeline (AZ-NIX-ava)"
|
||||||
|
echo " Commands:"
|
||||||
|
echo " atropim-build Build secret-free AtroPIM image (podman, 2 stages)"
|
||||||
|
echo " atropim-push [version] Tag + push to Gitea registry (default tag: YYYYMMDD)"
|
||||||
|
echo " Build parameters (pkgs/atropim-image/default.nix):"
|
||||||
|
echo " SKELETON_VARIANT=pim-no-demo BUILD_VARIANT=pdf PRODUCTION_STABILITY=stable"
|
||||||
|
echo " PRODUCTION_DOMAIN=pim.l.az-gruppe.com DB build args deliberately EMPTY"
|
||||||
|
echo " Runtime ENV (written to data/config.php at container start):"
|
||||||
|
echo " ATRO_DB_HOST ATRO_DB_NAME ATRO_DB_USER ATRO_DB_PASSWORD"
|
||||||
|
echo " Registry: git.az-gruppe.com/az-intec-gmbh/atrocore-web (podman login git.az-gruppe.com)"
|
||||||
|
echo " Quick test:"
|
||||||
|
echo " podman run -d --name atropim -p 127.0.0.1:8080:80 <image>"
|
||||||
|
echo ""
|
||||||
echo "Remember to add to .gitignore:"
|
echo "Remember to add to .gitignore:"
|
||||||
echo " .opencode-rules"
|
echo " .opencode-rules"
|
||||||
echo " coding-rules.json"
|
echo " coding-rules.json"
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
{inputs, ...}: {
|
{inputs, ...}: {
|
||||||
# This one brings our custom packages from the 'pkgs' directory
|
# This one brings our custom packages from the 'pkgs' directory
|
||||||
additions = final: prev:
|
additions = final: prev:
|
||||||
(import ../pkgs {pkgs = final;})
|
(import ../pkgs {
|
||||||
|
pkgs = final;
|
||||||
|
atrocore-docker = inputs.atrocore-docker;
|
||||||
|
})
|
||||||
// {
|
// {
|
||||||
zugferd-service = inputs.zugferd-service.packages.${prev.stdenv.hostPlatform.system}.default;
|
zugferd-service = inputs.zugferd-service.packages.${prev.stdenv.hostPlatform.system}.default;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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;
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -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
@@ -1,4 +1,9 @@
|
|||||||
{pkgs, ...}: {
|
{
|
||||||
|
pkgs,
|
||||||
|
atrocore-docker,
|
||||||
|
...
|
||||||
|
}: {
|
||||||
# Define your custom packages here
|
# Define your custom packages here
|
||||||
online3dviewer = pkgs.callPackage ./online3dviewer {};
|
online3dviewer = pkgs.callPackage ./online3dviewer {};
|
||||||
|
atropim-tools = pkgs.callPackage ./atropim-image {inherit atrocore-docker;};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user