Unify container build workflows (#306)
All checks were successful
Build Container / detect (push) Successful in 3s
All checks were successful
Build Container / detect (push) Successful in 3s
## Summary - Merges `build-container.yaml` and `build-container-nix.yaml` into a single workflow - Detect job classifies each changed container by presence of `Dockerfile` and/or `default.nix` - Dockerfile containers build on `k8s` (indri) via Dagger; Nix containers build on `nix-container-builder` (ringtail) via nix-build + skopeo - Containers with both build files (alloy, nettest, ntfy) get built on both runners ## Test plan - [ ] Push a change to a Dockerfile-only container (e.g. grafana) — verify it builds on k8s only - [ ] Push a change to a nix-only container (e.g. jobsync) — verify it builds on nix-container-builder only - [ ] Push a change to a dual container (e.g. ntfy) — verify it builds on both runners - [ ] Test workflow_dispatch with a specific container name 🤖 Generated with [Claude Code](https://claude.com/claude-code) Reviewed-on: #306
This commit is contained in:
parent
4cc26ed5eb
commit
bd0ff30d3f
12 changed files with 124 additions and 365 deletions
|
|
@ -1,147 +0,0 @@
|
||||||
# Nix container build workflow
|
|
||||||
# Triggers on pushes to main that modify containers/*, or via manual dispatch.
|
|
||||||
# Detects which containers changed, builds from default.nix, and pushes via
|
|
||||||
# skopeo with commit-SHA-based tags: vX.Y.Z-<sha>-nix
|
|
||||||
name: Build Container (Nix)
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [main]
|
|
||||||
paths: ['containers/**']
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
container:
|
|
||||||
description: 'Container name (directory under containers/)'
|
|
||||||
required: true
|
|
||||||
type: string
|
|
||||||
ref:
|
|
||||||
description: 'Commit SHA to build (defaults to current HEAD)'
|
|
||||||
required: false
|
|
||||||
type: string
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
detect:
|
|
||||||
runs-on: nix-container-builder
|
|
||||||
outputs:
|
|
||||||
containers: ${{ steps.list.outputs.containers }}
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
|
||||||
with:
|
|
||||||
fetch-depth: 2
|
|
||||||
|
|
||||||
- name: Detect changed containers
|
|
||||||
id: list
|
|
||||||
run: |
|
|
||||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
|
||||||
CONTAINERS='["${{ inputs.container }}"]'
|
|
||||||
else
|
|
||||||
CONTAINERS=$(git diff --name-only HEAD~1 HEAD -- containers/ \
|
|
||||||
| cut -d/ -f2 | sort -u \
|
|
||||||
| jq -R -s -c 'split("\n") | map(select(length > 0))')
|
|
||||||
fi
|
|
||||||
echo "containers=$CONTAINERS" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "Containers to build: $CONTAINERS"
|
|
||||||
|
|
||||||
build:
|
|
||||||
needs: detect
|
|
||||||
if: needs.detect.outputs.containers != '[]'
|
|
||||||
runs-on: nix-container-builder
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
container: ${{ fromJson(needs.detect.outputs.containers) }}
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
|
||||||
with:
|
|
||||||
ref: ${{ inputs.ref || github.sha }}
|
|
||||||
|
|
||||||
- name: Check for default.nix
|
|
||||||
id: check
|
|
||||||
run: |
|
|
||||||
if [ -f "containers/${{ matrix.container }}/default.nix" ]; then
|
|
||||||
echo "exists=true" >> "$GITHUB_OUTPUT"
|
|
||||||
else
|
|
||||||
echo "No default.nix for ${{ matrix.container }} — skipping"
|
|
||||||
echo "exists=false" >> "$GITHUB_OUTPUT"
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Extract version and SHA
|
|
||||||
if: steps.check.outputs.exists == 'true'
|
|
||||||
id: meta
|
|
||||||
run: |
|
|
||||||
CONTAINER="${{ matrix.container }}"
|
|
||||||
NIX_FILE="containers/$CONTAINER/default.nix"
|
|
||||||
|
|
||||||
# Try extracting version = "..." from the nix file (e.g. ntfy)
|
|
||||||
VERSION=$(grep -m1 '^\s*version\s*=\s*"' "$NIX_FILE" \
|
|
||||||
| sed 's/.*"\(.*\)".*/\1/' || true)
|
|
||||||
|
|
||||||
# Fall back to CONTAINER_APP_VERSION from Dockerfile (e.g. nettest)
|
|
||||||
if [ -z "$VERSION" ] && [ -f "containers/$CONTAINER/Dockerfile" ]; then
|
|
||||||
VERSION=$(grep -m1 '^ARG CONTAINER_APP_VERSION=' \
|
|
||||||
"containers/$CONTAINER/Dockerfile" \
|
|
||||||
| sed 's/^ARG CONTAINER_APP_VERSION=//')
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Last resort: nix eval for nixpkgs packages (e.g. authentik)
|
|
||||||
if [ -z "$VERSION" ]; then
|
|
||||||
VERSION=$(nix --extra-experimental-features "nix-command flakes" \
|
|
||||||
eval --raw "nixpkgs#${CONTAINER}.version")
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -z "$VERSION" ]; then
|
|
||||||
echo "Error: Could not determine version for $CONTAINER"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
REF="${{ inputs.ref }}"
|
|
||||||
if [ -z "$REF" ]; then
|
|
||||||
REF="${GITHUB_SHA}"
|
|
||||||
fi
|
|
||||||
SHORT_SHA=$(echo "$REF" | head -c 7)
|
|
||||||
|
|
||||||
# Ensure version starts with 'v'
|
|
||||||
case "$VERSION" in
|
|
||||||
v*) ;;
|
|
||||||
*) VERSION="v${VERSION}" ;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "sha=$SHORT_SHA" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "Version: $VERSION, SHA: $SHORT_SHA"
|
|
||||||
|
|
||||||
- name: Resolve nixpkgs
|
|
||||||
if: steps.check.outputs.exists == 'true'
|
|
||||||
id: nixpkgs
|
|
||||||
run: |
|
|
||||||
NIXPKGS_PATH=$(nix flake metadata nixpkgs --json | jq -r '.path')
|
|
||||||
echo "Resolved nixpkgs: $NIXPKGS_PATH"
|
|
||||||
echo "path=$NIXPKGS_PATH" >> "$GITHUB_OUTPUT"
|
|
||||||
|
|
||||||
- name: Build with nix
|
|
||||||
if: steps.check.outputs.exists == 'true'
|
|
||||||
env:
|
|
||||||
NIX_PATH: "nixpkgs=${{ steps.nixpkgs.outputs.path }}"
|
|
||||||
run: |
|
|
||||||
echo "Building containers/${{ matrix.container }}/default.nix"
|
|
||||||
echo "NIX_PATH=$NIX_PATH"
|
|
||||||
nix-build "containers/${{ matrix.container }}/default.nix" -o result
|
|
||||||
echo "Build complete: $(readlink result)"
|
|
||||||
|
|
||||||
- name: Push to registry
|
|
||||||
if: steps.check.outputs.exists == 'true'
|
|
||||||
env:
|
|
||||||
ZOT_CI_API_KEY: ${{ secrets.ZOT_CI_API_KEY }}
|
|
||||||
run: |
|
|
||||||
CONTAINER="${{ matrix.container }}"
|
|
||||||
VERSION="${{ steps.meta.outputs.version }}"
|
|
||||||
SHORT_SHA="${{ steps.meta.outputs.sha }}"
|
|
||||||
IMAGE="registry.ops.eblu.me/blumeops/$CONTAINER:${VERSION}-${SHORT_SHA}-nix"
|
|
||||||
|
|
||||||
echo "Pushing to $IMAGE"
|
|
||||||
skopeo copy \
|
|
||||||
--dest-creds="zot-ci:$ZOT_CI_API_KEY" \
|
|
||||||
"docker-archive:result" \
|
|
||||||
"docker://$IMAGE"
|
|
||||||
echo "Push complete: $IMAGE"
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
# Dockerfile container build workflow
|
# Unified container build workflow
|
||||||
# Triggers on pushes to main that modify containers/*, or via manual dispatch.
|
# Triggers on pushes to main that modify containers/*, or via manual dispatch.
|
||||||
# Detects which containers changed, extracts version from CONTAINER_APP_VERSION,
|
# Detects which containers changed and routes to the correct runner:
|
||||||
# and publishes with commit-SHA-based tags: vX.Y.Z-<sha>
|
# - Dockerfile containers build on k8s (indri) via Dagger
|
||||||
|
# - Nix containers build on nix-container-builder (ringtail) via nix-build + skopeo
|
||||||
name: Build Container
|
name: Build Container
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
|
@ -23,52 +24,64 @@ jobs:
|
||||||
detect:
|
detect:
|
||||||
runs-on: k8s
|
runs-on: k8s
|
||||||
outputs:
|
outputs:
|
||||||
containers: ${{ steps.list.outputs.containers }}
|
dockerfile: ${{ steps.classify.outputs.dockerfile }}
|
||||||
|
nix: ${{ steps.classify.outputs.nix }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
||||||
with:
|
with:
|
||||||
fetch-depth: 2
|
fetch-depth: 2
|
||||||
|
|
||||||
- name: Detect changed containers
|
- name: Detect and classify changed containers
|
||||||
id: list
|
id: classify
|
||||||
run: |
|
run: |
|
||||||
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
|
||||||
CONTAINERS='["${{ inputs.container }}"]'
|
CHANGED='["${{ inputs.container }}"]'
|
||||||
else
|
else
|
||||||
# Diff against parent commit to find changed container dirs
|
CHANGED=$(git diff --name-only HEAD~1 HEAD -- containers/ \
|
||||||
CONTAINERS=$(git diff --name-only HEAD~1 HEAD -- containers/ \
|
|
||||||
| cut -d/ -f2 | sort -u \
|
| cut -d/ -f2 | sort -u \
|
||||||
| jq -R -s -c 'split("\n") | map(select(length > 0))')
|
| jq -R -s -c 'split("\n") | map(select(length > 0))')
|
||||||
fi
|
fi
|
||||||
echo "containers=$CONTAINERS" >> "$GITHUB_OUTPUT"
|
|
||||||
echo "Containers to build: $CONTAINERS"
|
|
||||||
|
|
||||||
build:
|
echo "Changed containers: $CHANGED"
|
||||||
|
|
||||||
|
# Classify each container by build type (a container can appear in both)
|
||||||
|
DOCKERFILE='[]'
|
||||||
|
NIX='[]'
|
||||||
|
for name in $(echo "$CHANGED" | jq -r '.[]'); do
|
||||||
|
has_any=false
|
||||||
|
if [ -f "containers/$name/Dockerfile" ]; then
|
||||||
|
DOCKERFILE=$(echo "$DOCKERFILE" | jq -c --arg n "$name" '. + [$n]')
|
||||||
|
has_any=true
|
||||||
|
fi
|
||||||
|
if [ -f "containers/$name/default.nix" ]; then
|
||||||
|
NIX=$(echo "$NIX" | jq -c --arg n "$name" '. + [$n]')
|
||||||
|
has_any=true
|
||||||
|
fi
|
||||||
|
if [ "$has_any" = "false" ]; then
|
||||||
|
echo "Warning: $name has neither Dockerfile nor default.nix — skipping"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "dockerfile=$DOCKERFILE" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "nix=$NIX" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "Dockerfile builds: $DOCKERFILE"
|
||||||
|
echo "Nix builds: $NIX"
|
||||||
|
|
||||||
|
build-dockerfile:
|
||||||
needs: detect
|
needs: detect
|
||||||
if: needs.detect.outputs.containers != '[]'
|
if: needs.detect.outputs.dockerfile != '[]'
|
||||||
runs-on: k8s
|
runs-on: k8s
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
container: ${{ fromJson(needs.detect.outputs.containers) }}
|
container: ${{ fromJson(needs.detect.outputs.dockerfile) }}
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
||||||
with:
|
with:
|
||||||
ref: ${{ inputs.ref || github.sha }}
|
ref: ${{ inputs.ref || github.sha }}
|
||||||
|
|
||||||
- name: Check for Dockerfile
|
|
||||||
id: check
|
|
||||||
run: |
|
|
||||||
if [ -f "containers/${{ matrix.container }}/Dockerfile" ]; then
|
|
||||||
echo "exists=true" >> "$GITHUB_OUTPUT"
|
|
||||||
else
|
|
||||||
echo "No Dockerfile for ${{ matrix.container }} — skipping"
|
|
||||||
echo "exists=false" >> "$GITHUB_OUTPUT"
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Extract version and SHA
|
- name: Extract version and SHA
|
||||||
if: steps.check.outputs.exists == 'true'
|
|
||||||
id: meta
|
id: meta
|
||||||
run: |
|
run: |
|
||||||
VERSION=$(grep -m1 '^ARG CONTAINER_APP_VERSION=' \
|
VERSION=$(grep -m1 '^ARG CONTAINER_APP_VERSION=' \
|
||||||
|
|
@ -80,7 +93,6 @@ jobs:
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Use dispatch input ref if provided, otherwise current commit
|
|
||||||
REF="${{ inputs.ref }}"
|
REF="${{ inputs.ref }}"
|
||||||
if [ -z "$REF" ]; then
|
if [ -z "$REF" ]; then
|
||||||
REF="${GITHUB_SHA}"
|
REF="${GITHUB_SHA}"
|
||||||
|
|
@ -89,7 +101,7 @@ jobs:
|
||||||
|
|
||||||
# Ensure version starts with 'v'
|
# Ensure version starts with 'v'
|
||||||
case "$VERSION" in
|
case "$VERSION" in
|
||||||
v*) ;; # already has v prefix
|
v*) ;;
|
||||||
*) VERSION="v${VERSION}" ;;
|
*) VERSION="v${VERSION}" ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
|
@ -98,7 +110,6 @@ jobs:
|
||||||
echo "Version: $VERSION, SHA: $SHORT_SHA"
|
echo "Version: $VERSION, SHA: $SHORT_SHA"
|
||||||
|
|
||||||
- name: Publish
|
- name: Publish
|
||||||
if: steps.check.outputs.exists == 'true'
|
|
||||||
env:
|
env:
|
||||||
ZOT_CI_API_KEY: ${{ secrets.ZOT_CI_API_KEY }}
|
ZOT_CI_API_KEY: ${{ secrets.ZOT_CI_API_KEY }}
|
||||||
run: |
|
run: |
|
||||||
|
|
@ -108,3 +119,79 @@ jobs:
|
||||||
--version=${{ steps.meta.outputs.version }} \
|
--version=${{ steps.meta.outputs.version }} \
|
||||||
--commit-sha=${{ steps.meta.outputs.sha }} \
|
--commit-sha=${{ steps.meta.outputs.sha }} \
|
||||||
--registry-password=env:ZOT_CI_API_KEY
|
--registry-password=env:ZOT_CI_API_KEY
|
||||||
|
|
||||||
|
build-nix:
|
||||||
|
needs: detect
|
||||||
|
if: needs.detect.outputs.nix != '[]'
|
||||||
|
runs-on: nix-container-builder
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
container: ${{ fromJson(needs.detect.outputs.nix) }}
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
|
||||||
|
with:
|
||||||
|
ref: ${{ inputs.ref || github.sha }}
|
||||||
|
|
||||||
|
- name: Extract version and SHA
|
||||||
|
id: meta
|
||||||
|
run: |
|
||||||
|
CONTAINER="${{ matrix.container }}"
|
||||||
|
NIX_FILE="containers/$CONTAINER/default.nix"
|
||||||
|
|
||||||
|
# Extract version = "..." from the nix file
|
||||||
|
VERSION=$(grep -m1 '^\s*version\s*=\s*"' "$NIX_FILE" \
|
||||||
|
| sed 's/.*"\(.*\)".*/\1/' || true)
|
||||||
|
|
||||||
|
if [ -z "$VERSION" ]; then
|
||||||
|
echo "Error: No version declaration found in $NIX_FILE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
REF="${{ inputs.ref }}"
|
||||||
|
if [ -z "$REF" ]; then
|
||||||
|
REF="${GITHUB_SHA}"
|
||||||
|
fi
|
||||||
|
SHORT_SHA=$(echo "$REF" | head -c 7)
|
||||||
|
|
||||||
|
# Ensure version starts with 'v'
|
||||||
|
case "$VERSION" in
|
||||||
|
v*) ;;
|
||||||
|
*) VERSION="v${VERSION}" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo "version=$VERSION" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "sha=$SHORT_SHA" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "Version: $VERSION, SHA: $SHORT_SHA"
|
||||||
|
|
||||||
|
- name: Resolve nixpkgs
|
||||||
|
id: nixpkgs
|
||||||
|
run: |
|
||||||
|
NIXPKGS_PATH=$(nix flake metadata nixpkgs --json | jq -r '.path')
|
||||||
|
echo "Resolved nixpkgs: $NIXPKGS_PATH"
|
||||||
|
echo "path=$NIXPKGS_PATH" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Build with nix
|
||||||
|
env:
|
||||||
|
NIX_PATH: "nixpkgs=${{ steps.nixpkgs.outputs.path }}"
|
||||||
|
run: |
|
||||||
|
echo "Building containers/${{ matrix.container }}/default.nix"
|
||||||
|
echo "NIX_PATH=$NIX_PATH"
|
||||||
|
nix-build "containers/${{ matrix.container }}/default.nix" -o result
|
||||||
|
echo "Build complete: $(readlink result)"
|
||||||
|
|
||||||
|
- name: Push to registry
|
||||||
|
env:
|
||||||
|
ZOT_CI_API_KEY: ${{ secrets.ZOT_CI_API_KEY }}
|
||||||
|
run: |
|
||||||
|
CONTAINER="${{ matrix.container }}"
|
||||||
|
VERSION="${{ steps.meta.outputs.version }}"
|
||||||
|
SHORT_SHA="${{ steps.meta.outputs.sha }}"
|
||||||
|
IMAGE="registry.ops.eblu.me/blumeops/$CONTAINER:${VERSION}-${SHORT_SHA}-nix"
|
||||||
|
|
||||||
|
echo "Pushing to $IMAGE"
|
||||||
|
skopeo copy \
|
||||||
|
--dest-creds="zot-ci:$ZOT_CI_API_KEY" \
|
||||||
|
"docker-archive:result" \
|
||||||
|
"docker://$IMAGE"
|
||||||
|
echo "Push complete: $IMAGE"
|
||||||
|
|
|
||||||
|
|
@ -1,27 +0,0 @@
|
||||||
# Network connectivity test container for blumeops CI/CD debugging
|
|
||||||
#
|
|
||||||
# This container tests connectivity to tailnet services from various environments:
|
|
||||||
# - Docker on indri (during CI build)
|
|
||||||
# - Minikube pods (manual testing)
|
|
||||||
|
|
||||||
ARG CONTAINER_APP_VERSION=0.1.0
|
|
||||||
|
|
||||||
FROM alpine:3.22
|
|
||||||
|
|
||||||
ARG CONTAINER_APP_VERSION
|
|
||||||
LABEL org.opencontainers.image.title="nettest"
|
|
||||||
LABEL org.opencontainers.image.description="Network connectivity test container for CI/CD debugging"
|
|
||||||
LABEL org.opencontainers.image.version="${CONTAINER_APP_VERSION}"
|
|
||||||
LABEL org.opencontainers.image.source="https://forge.eblu.me/eblume/blumeops"
|
|
||||||
LABEL org.opencontainers.image.vendor="blumeops"
|
|
||||||
|
|
||||||
RUN apk add --no-cache \
|
|
||||||
curl \
|
|
||||||
ca-certificates \
|
|
||||||
jq \
|
|
||||||
bind-tools
|
|
||||||
|
|
||||||
COPY test-connectivity.sh /test-connectivity.sh
|
|
||||||
RUN chmod +x /test-connectivity.sh
|
|
||||||
|
|
||||||
ENTRYPOINT ["/test-connectivity.sh"]
|
|
||||||
|
|
@ -1,39 +0,0 @@
|
||||||
# Nix-built nettest container
|
|
||||||
# Equivalent to the Dockerfile: curl, jq, bind (nslookup), ca-certs, bash
|
|
||||||
# Built with dockerTools.buildLayeredImage for efficient layer caching
|
|
||||||
{ pkgs ? import <nixpkgs> { } }:
|
|
||||||
|
|
||||||
let
|
|
||||||
testScript = ./test-connectivity.sh;
|
|
||||||
|
|
||||||
tools = pkgs.buildEnv {
|
|
||||||
name = "nettest-tools";
|
|
||||||
paths = [
|
|
||||||
pkgs.curl
|
|
||||||
pkgs.jq
|
|
||||||
pkgs.dnsutils # provides nslookup, dig
|
|
||||||
pkgs.cacert
|
|
||||||
pkgs.coreutils
|
|
||||||
pkgs.hostname
|
|
||||||
pkgs.bashInteractive
|
|
||||||
];
|
|
||||||
};
|
|
||||||
in
|
|
||||||
pkgs.dockerTools.buildLayeredImage {
|
|
||||||
name = "blumeops/nettest";
|
|
||||||
tag = "latest";
|
|
||||||
|
|
||||||
contents = [ tools ];
|
|
||||||
|
|
||||||
extraCommands = ''
|
|
||||||
cp ${testScript} test-connectivity.sh
|
|
||||||
chmod +x test-connectivity.sh
|
|
||||||
'';
|
|
||||||
|
|
||||||
config = {
|
|
||||||
Entrypoint = [ "/bin/bash" "/test-connectivity.sh" ];
|
|
||||||
Env = [
|
|
||||||
"SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
@ -1,115 +0,0 @@
|
||||||
#!/bin/ash
|
|
||||||
# shellcheck shell=dash
|
|
||||||
# Network connectivity test script for blumeops
|
|
||||||
# Tests access to tailnet services from within the container
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
echo "========================================"
|
|
||||||
echo "BlumeOps Network Connectivity Test"
|
|
||||||
echo "========================================"
|
|
||||||
echo ""
|
|
||||||
echo "Timestamp: $(date -Iseconds)"
|
|
||||||
echo "Hostname: $(hostname)"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
# Test targets
|
|
||||||
FORGE_HOST="forge.ops.eblu.me"
|
|
||||||
REGISTRY_HOST="registry.ops.eblu.me"
|
|
||||||
|
|
||||||
test_dns() {
|
|
||||||
local host="$1"
|
|
||||||
echo "--- DNS: $host ---"
|
|
||||||
if nslookup "$host" 2>/dev/null; then
|
|
||||||
echo "DNS: OK"
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
echo "DNS: FAILED"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
test_https() {
|
|
||||||
local url="$1"
|
|
||||||
local name="$2"
|
|
||||||
echo ""
|
|
||||||
echo "--- HTTPS: $name ---"
|
|
||||||
echo "URL: $url"
|
|
||||||
|
|
||||||
# Try to fetch with verbose output
|
|
||||||
http_code=$(curl -s -o /dev/null -w "%{http_code}" --max-time 10 "$url" 2>&1) || true
|
|
||||||
|
|
||||||
if [ "$http_code" = "200" ] || [ "$http_code" = "401" ] || [ "$http_code" = "302" ]; then
|
|
||||||
echo "HTTP Status: $http_code"
|
|
||||||
echo "Result: OK (service reachable)"
|
|
||||||
return 0
|
|
||||||
elif [ -n "$http_code" ] && [ "$http_code" != "000" ]; then
|
|
||||||
echo "HTTP Status: $http_code"
|
|
||||||
echo "Result: OK (service reachable, status $http_code)"
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
echo "HTTP Status: $http_code"
|
|
||||||
echo "Result: FAILED (could not connect)"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
test_registry_api() {
|
|
||||||
local host="$1"
|
|
||||||
echo ""
|
|
||||||
echo "--- Registry API: $host ---"
|
|
||||||
|
|
||||||
# Try to query the registry API
|
|
||||||
response=$(curl -sf --max-time 10 "https://$host/v2/_catalog" 2>/dev/null) || true
|
|
||||||
|
|
||||||
if [ -n "$response" ]; then
|
|
||||||
echo "Response: $response"
|
|
||||||
repo_count=$(echo "$response" | jq -r '.repositories | length' 2>/dev/null) || repo_count="unknown"
|
|
||||||
echo "Repository count: $repo_count"
|
|
||||||
echo "Result: OK"
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
echo "Result: FAILED (no response from /v2/_catalog)"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
echo "========================================"
|
|
||||||
echo "Testing DNS Resolution"
|
|
||||||
echo "========================================"
|
|
||||||
dns_ok=0
|
|
||||||
test_dns "$FORGE_HOST" && dns_ok=$((dns_ok + 1)) || true
|
|
||||||
echo ""
|
|
||||||
test_dns "$REGISTRY_HOST" && dns_ok=$((dns_ok + 1)) || true
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "========================================"
|
|
||||||
echo "Testing HTTPS Connectivity"
|
|
||||||
echo "========================================"
|
|
||||||
https_ok=0
|
|
||||||
test_https "https://$FORGE_HOST" "Forgejo" && https_ok=$((https_ok + 1)) || true
|
|
||||||
test_https "https://$REGISTRY_HOST/v2/" "Zot Registry" && https_ok=$((https_ok + 1)) || true
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "========================================"
|
|
||||||
echo "Testing Registry API"
|
|
||||||
echo "========================================"
|
|
||||||
api_ok=0
|
|
||||||
test_registry_api "$REGISTRY_HOST" && api_ok=1 || true
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "========================================"
|
|
||||||
echo "Summary"
|
|
||||||
echo "========================================"
|
|
||||||
echo "DNS tests passed: $dns_ok/2"
|
|
||||||
echo "HTTPS tests passed: $https_ok/2"
|
|
||||||
echo "Registry API: $([ $api_ok -eq 1 ] && echo 'OK' || echo 'FAILED')"
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
if [ "$dns_ok" -eq 2 ] && [ "$https_ok" -eq 2 ] && [ "$api_ok" -eq 1 ]; then
|
|
||||||
echo "OVERALL: ALL TESTS PASSED"
|
|
||||||
exit 0
|
|
||||||
else
|
|
||||||
echo "OVERALL: SOME TESTS FAILED"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
1
docs/changelog.d/unify-container-workflows.infra.md
Normal file
1
docs/changelog.d/unify-container-workflows.infra.md
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Unified Dockerfile and Nix container build workflows into a single workflow that auto-classifies containers by build type and routes to the correct runner (k8s for Dockerfile, nix-container-builder for Nix). Removed nettest container (outgrown). Nix builds now require an explicit `version = "..."` declaration — no implicit nixpkgs fallback.
|
||||||
|
|
@ -117,7 +117,7 @@ Existing containers demonstrate several build approaches:
|
||||||
| Multi-stage with Node + Go | [[#navidrome]] | Separate UI and backend build stages |
|
| Multi-stage with Node + Go | [[#navidrome]] | Separate UI and backend build stages |
|
||||||
| Multi-stage Elixir | [[#teslamate]] | Elixir release with Node assets |
|
| Multi-stage Elixir | [[#teslamate]] | Elixir release with Node assets |
|
||||||
| Runtime tarball download | [[#kiwix-serve]] | Download pre-built binary with arch detection |
|
| Runtime tarball download | [[#kiwix-serve]] | Download pre-built binary with arch detection |
|
||||||
| Nix `dockerTools` | [[#nettest-nix]] | `buildLayeredImage` with nixpkgs tools |
|
| Nix `dockerTools` | [[#ntfy-nix]] | `buildLayeredImage` with nix-built app |
|
||||||
|
|
||||||
### transmission
|
### transmission
|
||||||
|
|
||||||
|
|
@ -139,9 +139,9 @@ Existing containers demonstrate several build approaches:
|
||||||
|
|
||||||
`containers/kiwix-serve/Dockerfile` — Downloads a pre-built binary from upstream, with architecture detection for cross-platform support.
|
`containers/kiwix-serve/Dockerfile` — Downloads a pre-built binary from upstream, with architecture detection for cross-platform support.
|
||||||
|
|
||||||
### nettest (nix)
|
### ntfy (nix)
|
||||||
|
|
||||||
`containers/nettest/default.nix` — Uses `dockerTools.buildLayeredImage` with `buildEnv` to merge nixpkgs tools (curl, jq, dnsutils, bash). Runs alongside the existing Dockerfile; the nix variant is tagged `:version-nix` in the registry.
|
`containers/ntfy/default.nix` — Builds ntfy from source using `buildGoModule` and packages it with `dockerTools.buildLayeredImage`. Runs alongside the existing Dockerfile; the nix variant is tagged `:version-nix` in the registry.
|
||||||
|
|
||||||
## Related
|
## Related
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ A typer-based uv-script that iterates over `containers/*/` and validates five ru
|
||||||
|
|
||||||
Scoping: by default only checks containers changed vs main. `--all-files` checks everything. If `service-versions.yaml` itself changed, all containers are checked.
|
Scoping: by default only checks containers changed vs main. `--all-files` checks everything. If `service-versions.yaml` itself changed, all containers are checked.
|
||||||
|
|
||||||
Blacklisted containers (utility images, not tracked services): `kubectl`, `nettest`.
|
Blacklisted containers (utility images, not tracked services): `kubectl`.
|
||||||
|
|
||||||
Container-to-service name mapping: `quartz` → `docs`, `kiwix-serve` → `kiwix`.
|
Container-to-service name mapping: `quartz` → `docs`, `kiwix-serve` → `kiwix`.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ Add Dagger functions for building nix container images and extracting version in
|
||||||
|
|
||||||
## Context
|
## Context
|
||||||
|
|
||||||
Discovered during analysis of [[adopt-commit-based-container-tags]]: nix containers (authentik, ntfy, nettest) derive their bundled app version from the nixpkgs pin, not from an explicit declaration. To validate that a VERSION file matches the actual nix-built version, we need a way to query the version from nix.
|
Discovered during analysis of [[adopt-commit-based-container-tags]]: nix containers (authentik, ntfy) derive their bundled app version from the nixpkgs pin, not from an explicit declaration. To validate that a VERSION file matches the actual nix-built version, we need a way to query the version from nix.
|
||||||
|
|
||||||
Currently, nix containers can only be built on ringtail (the `nix-container-builder` runner). There is no local build path for developers — the only option is to push and wait for CI. Adding a Dagger-based nix build gives both local evaluation and version extraction.
|
Currently, nix containers can only be built on ringtail (the `nix-container-builder` runner). There is no local build path for developers — the only option is to push and wait for CI. Adding a Dagger-based nix build gives both local evaluation and version extraction.
|
||||||
|
|
||||||
|
|
@ -84,7 +84,7 @@ The `flake_lock` function already demonstrates running nix inside Dagger using `
|
||||||
|
|
||||||
## Verification
|
## Verification
|
||||||
|
|
||||||
- [ ] `dagger call build-nix --src=. --container-name=nettest` produces a valid docker-archive tarball
|
- [ ] `dagger call build-nix --src=. --container-name=ntfy` produces a valid docker-archive tarball
|
||||||
- [ ] `dagger call nix-version --src=. --package=ntfy-sh` returns the correct version string
|
- [ ] `dagger call nix-version --src=. --package=ntfy-sh` returns the correct version string
|
||||||
- [ ] `dagger call nix-version --src=. --package=authentik` returns the Authentik version
|
- [ ] `dagger call nix-version --src=. --package=authentik` returns the Authentik version
|
||||||
- [ ] Tarball from `build-nix` can be loaded with `docker load` and run locally
|
- [ ] Tarball from `build-nix` can be loaded with `docker load` and run locally
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,6 @@ Specific changes:
|
||||||
- **devpi**: Pinned devpi-server==6.19.1 and devpi-web==5.0.1
|
- **devpi**: Pinned devpi-server==6.19.1 and devpi-web==5.0.1
|
||||||
- **cv**: `CONTAINER_APP_VERSION=1.0.3` (matches latest Forgejo package release)
|
- **cv**: `CONTAINER_APP_VERSION=1.0.3` (matches latest Forgejo package release)
|
||||||
- **quartz**: `CONTAINER_APP_VERSION=1.28.2` (pinned nginx:1.28.2-alpine base)
|
- **quartz**: `CONTAINER_APP_VERSION=1.28.2` (pinned nginx:1.28.2-alpine base)
|
||||||
- **nettest**: `CONTAINER_APP_VERSION=0.1.0` (internal, no upstream)
|
|
||||||
- **All others**: Existing versions carried forward with new uniform ARG pattern
|
- **All others**: Existing versions carried forward with new uniform ARG pattern
|
||||||
|
|
||||||
## Key Files
|
## Key Files
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ dagger call --interactive build --src=. --container-name=devpi
|
||||||
dagger call publish --src=. --container-name=devpi --version=v1.1.0
|
dagger call publish --src=. --container-name=devpi --version=v1.1.0
|
||||||
|
|
||||||
# Build a nix container (no local nix required)
|
# Build a nix container (no local nix required)
|
||||||
dagger call build-nix --src=. --container-name=nettest export --path=./nettest.tar.gz
|
dagger call build-nix --src=. --container-name=ntfy export --path=./ntfy.tar.gz
|
||||||
|
|
||||||
# Check a nixpkgs package version
|
# Check a nixpkgs package version
|
||||||
dagger call nix-version --package=authentik
|
dagger call nix-version --package=authentik
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ CONTAINERS_DIR = REPO_ROOT / "containers"
|
||||||
SERVICE_VERSIONS_FILE = REPO_ROOT / "service-versions.yaml"
|
SERVICE_VERSIONS_FILE = REPO_ROOT / "service-versions.yaml"
|
||||||
|
|
||||||
# Containers that are utility/test images, not tracked services
|
# Containers that are utility/test images, not tracked services
|
||||||
BLACKLIST = {"kubectl", "nettest"}
|
BLACKLIST = {"kubectl"}
|
||||||
|
|
||||||
# Container dir name → service-versions.yaml name (when they differ)
|
# Container dir name → service-versions.yaml name (when they differ)
|
||||||
CONTAINER_TO_SERVICE = {
|
CONTAINER_TO_SERVICE = {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue