From ec8e32726bb03a77eb0060437ab7d703ee6ce01d Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Fri, 24 Jul 2026 14:36:25 -0500 Subject: [PATCH] Use Kaniko for Docker builds --- .forgejo/workflows/build-base.yaml | 131 ++++------------------------- 1 file changed, 16 insertions(+), 115 deletions(-) diff --git a/.forgejo/workflows/build-base.yaml b/.forgejo/workflows/build-base.yaml index 3edac9f0..e5145aa0 100644 --- a/.forgejo/workflows/build-base.yaml +++ b/.forgejo/workflows/build-base.yaml @@ -1,13 +1,5 @@ name: Build base image -# Path-filtered so it fires when the base Dockerfile (or this workflow) -# changes, weekly via cron so Debian security updates land without us -# remembering to bump anything, and on-demand via workflow_dispatch. -# -# The weekly cron passes CACHE_BUST= as a build-arg so the -# apt layers in Dockerfile.base re-execute (otherwise BuildKit's layer -# cache would short-circuit `apt-get update` on a deterministic input -# and silently skip security patches). on: push: branches: @@ -16,16 +8,9 @@ on: - 'k8s/Dockerfile.base' - '.forgejo/workflows/build-base.yaml' schedule: - # Sundays 06:00 UTC — runs on a quiet day-of-week so a long base - # rebuild doesn't collide with mid-week deploys, and matches the - # cadence that Debian security advisories typically aggregate at. - cron: '0 6 * * 0' workflow_dispatch: -# Image hosted on Codeberg's container registry. The shared -# REGISTRY_USER / REGISTRY_PASSWORD secrets carry Codeberg credentials; -# login URL is the hardcoded env.REGISTRY rather than secrets.REGISTRY_URL -# so a stale URL secret can't push to the wrong registry. env: REGISTRY: git.mcintire.me IMAGE_NAME: graham/towerops-base @@ -43,118 +28,34 @@ jobs: - name: Checkout code uses: https://github.com/actions/checkout@v4 - # Static docker + buildx download — same dance as production.yaml. - # The runner image's bookworm apt repos have broken GPG signatures, - # so we bypass apt entirely. - - name: Install Docker CLI and buildx - run: | - curl -fsSL -o /tmp/docker.tgz \ - https://download.docker.com/linux/static/stable/x86_64/docker-27.5.1.tgz - tar xzf /tmp/docker.tgz -C /tmp - install -m 0755 /tmp/docker/docker /usr/local/bin/docker - rm -rf /tmp/docker /tmp/docker.tgz - - mkdir -p /usr/libexec/docker/cli-plugins - curl -fsSL -o /usr/libexec/docker/cli-plugins/docker-buildx \ - https://github.com/docker/buildx/releases/download/v0.19.3/buildx-v0.19.3.linux-amd64 - chmod +x /usr/libexec/docker/cli-plugins/docker-buildx - - docker --version - docker buildx version - - - name: Wait for Docker daemon - run: | - sock="${DOCKER_HOST:-unix:///var/run/docker.sock}" - echo "Using DOCKER_HOST=${sock}" - - for i in $(seq 1 30); do - if docker info >/dev/null 2>&1; then - echo "Docker daemon reachable after ${i}s" - exit 0 - fi - echo "Waiting for Docker daemon... ($i/30)" - sleep 1 - done - - echo "Docker daemon never became reachable — diagnostics:" >&2 - ls -l /var/run/docker.sock 2>&1 || true - env | grep -i docker 2>&1 || true - docker info 2>&1 || true - exit 1 - - name: Generate image tag id: tag run: | - # Embed the Debian release in the tag so prior bases remain - # pullable for hotfix rollbacks if a debian bump regresses - # something at runtime. DEBIAN=$(grep -E '^ARG DEBIAN_VERSION=' k8s/Dockerfile.base | head -1 | cut -d= -f2) TIMESTAMP=$(date +%s) TAG="debian-${DEBIAN}-${TIMESTAMP}" echo "tag=${TAG}" >> $GITHUB_OUTPUT - - name: Log in to Codeberg container registry - run: | - # Reuses the shared REGISTRY_USER / REGISTRY_PASSWORD - # Forgejo Actions secrets — already set with Codeberg - # credentials (token must have `write:package` scope). - attempt=1 - max_attempts=3 - while : ; do - if echo "${{ secrets.FORGEJO_TOKEN }}" | \ - docker login "${{ env.REGISTRY }}" \ - -u "graham" \ - --password-stdin; then - exit 0 - fi - if [ "$attempt" -ge "$max_attempts" ]; then - echo "docker login failed after $attempt attempts" >&2 - exit 1 - fi - delay=$((2 ** attempt)) - echo "docker login attempt $attempt failed; retrying in ${delay}s" - sleep "$delay" - attempt=$((attempt + 1)) - done + - name: Build and push base image via Kaniko + uses: graham/infra/.forgejo/actions/kaniko-build@main + with: + dockerfile: k8s/Dockerfile.base + token: ${{ secrets.FORGEJO_TOKEN }} + image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tag: ${{ steps.tag.outputs.tag }} + extra_args: "--build-arg CACHE_BUST=$(date -u +%G-W%V)" - - name: Build and push base image - run: | - IMAGE="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}" - TAG="${{ steps.tag.outputs.tag }}" - # ISO year+week (e.g. 2026-W18). Same value all week, so a - # cron-triggered rebuild on day 1 vs day 7 reuses the layer - # cache; the next ISO week boundary forces apt-get update + - # install to re-execute and pull fresh security patches. - CACHE_BUST=$(date -u +%G-W%V) - echo "CACHE_BUST=${CACHE_BUST}" - - attempt=1 - max_attempts=3 - while : ; do - if docker buildx build \ - -f k8s/Dockerfile.base \ - --build-arg CACHE_BUST="${CACHE_BUST}" \ - -t "${IMAGE}:${TAG}" \ - -t "${IMAGE}:latest" \ - --push \ - .; then - exit 0 - fi - if [ "$attempt" -ge "$max_attempts" ]; then - echo "docker buildx build failed after $attempt attempts" >&2 - exit 1 - fi - delay=$((2 ** attempt)) - echo "docker buildx build attempt $attempt failed; retrying in ${delay}s" - sleep "$delay" - attempt=$((attempt + 1)) - done + - name: Tag as latest + uses: graham/infra/.forgejo/actions/kaniko-build@main + with: + dockerfile: k8s/Dockerfile.base + token: ${{ secrets.FORGEJO_TOKEN }} + image: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tag: latest + extra_args: "" - name: Build summary run: | echo "### ✅ Base Image Built and Pushed" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "**Image:** \`${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ steps.tag.outputs.tag }}\`" >> $GITHUB_STEP_SUMMARY - echo "**Also tagged:** \`${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest\`" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "Next push to \`main\` will pick up this base in the app build." >> $GITHUB_STEP_SUMMARY