From 20c1efb8a3b1ea8252daa1146f0b5434e89c443a Mon Sep 17 00:00:00 2001 From: Butler Agent Date: Fri, 11 Sep 2026 08:10:38 +0000 Subject: [PATCH 1/4] Scope documentation CI and isolate delivery job resources --- .gitea/workflows/build-and-push.yml | 15 +++++++++++ scripts/ci-select.sh | 39 +++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 scripts/ci-select.sh diff --git a/.gitea/workflows/build-and-push.yml b/.gitea/workflows/build-and-push.yml index 46c9939..dd3e25a 100644 --- a/.gitea/workflows/build-and-push.yml +++ b/.gitea/workflows/build-and-push.yml @@ -20,13 +20,26 @@ jobs: - name: Checkout uses: actions/checkout@v4 + - name: Select relevant delivery work + id: delivery + env: + DELIVERY_EVENT: ${{ github.event_name }} + DELIVERY_REF: ${{ github.ref }} + DELIVERY_SHA: ${{ github.sha }} + DELIVERY_BASE: ${{ github.event.pull_request.base.sha || github.event.before }} + run: | + cd . + bash scripts/ci-select.sh - name: Set up QEMU + if: ${{ steps.delivery.outputs.run == 'true' }} uses: docker/setup-qemu-action@v3 - name: Set up Docker Buildx + if: ${{ steps.delivery.outputs.run == 'true' }} uses: docker/setup-buildx-action@v3 - name: Log in to Gitea registry + if: ${{ steps.delivery.outputs.run == 'true' }} uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} @@ -34,6 +47,7 @@ jobs: password: ${{ secrets.REGISTRY_PASSWORD }} - name: Prepare image tags + if: ${{ steps.delivery.outputs.run == 'true' }} id: prep shell: bash run: | @@ -56,6 +70,7 @@ jobs: } >> "$GITHUB_OUTPUT" - name: Build and push image + if: ${{ steps.delivery.outputs.run == 'true' }} uses: docker/build-push-action@v6 with: context: . diff --git a/scripts/ci-select.sh b/scripts/ci-select.sh new file mode 100644 index 0000000..7260b1b --- /dev/null +++ b/scripts/ci-select.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +# Keep required jobs alive; only skip reviewed non-runtime documentation paths. +set -euo pipefail +base="${DELIVERY_BASE:-}" +head="$(git rev-parse HEAD)" +run=true +if [[ "${DELIVERY_REF:-}" != refs/tags/* && "$head" == "${DELIVERY_SHA:-}" && "${DELIVERY_EVENT:-}" =~ ^(push|pull_request)$ && "$base" =~ ^[0-9a-f]{40}$ && "$base" != 0000000000000000000000000000000000000000 ]]; then + if ! git cat-file -e "$base^{commit}" 2>/dev/null; then + if [[ -n "${DELIVERY_FETCH_TOKEN:-}" ]]; then + git -c "http.extraHeader=Authorization: token ${DELIVERY_FETCH_TOKEN}" fetch --quiet --depth=1 origin "$base" >/dev/null 2>&1 || true + else + git fetch --quiet --depth=1 origin "$base" >/dev/null 2>&1 || true + fi + fi + if git cat-file -e "$base^{commit}" 2>/dev/null; then + paths="$(mktemp)" + trap 'rm -f -- "$paths"' EXIT + # Snapshot comparison is conservative when main advanced during a PR. + # Both rename sides remain visible, including names containing newlines. + if git diff --no-renames --name-only -z "$base" "$head" > "$paths"; then + run=false + while IFS= read -r -d '' path; do + case "$path" in + README.md|CHANGELOG.md|LICENSE|LICENSE.md) ;; + *) run=true; break ;; + esac + done < "$paths" + fi + fi +fi +if [[ "$run" == false ]]; then + echo 'Only reviewed non-runtime documentation changed; build and validation are not applicable.' +else + echo 'Runtime, unknown inputs, or unavailable event history: run required validation.' +fi +if [[ -n "${GITHUB_OUTPUT:-}" ]]; then + echo "run=$run" >> "$GITHUB_OUTPUT" +fi +printf '%s\n' "$run" From 8502885a9d104589e9e8c966101c7c78ca7b15dd Mon Sep 17 00:00:00 2001 From: Butler Agent Date: Fri, 11 Sep 2026 08:47:07 +0000 Subject: [PATCH 2/4] Cancel superseded pull request validation without cancelling releases --- .gitea/workflows/build-and-push.yml | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/.gitea/workflows/build-and-push.yml b/.gitea/workflows/build-and-push.yml index dd3e25a..657a811 100644 --- a/.gitea/workflows/build-and-push.yml +++ b/.gitea/workflows/build-and-push.yml @@ -3,19 +3,26 @@ name: Build and Push Container on: push: branches: - - master - - main + - main tags: - - v* - workflow_dispatch: + - v* + workflow_dispatch: null + pull_request: + branches: + - main env: REGISTRY: gitea.wayfinderak.com IMAGE_NAME: wayfinderak/prepbot +concurrency: + group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.event.pull_request.number + || github.run_id }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: docker: - runs-on: ubuntu-latest + runs-on: butler-ci steps: - name: Checkout uses: actions/checkout@v4 @@ -39,7 +46,7 @@ jobs: uses: docker/setup-buildx-action@v3 - name: Log in to Gitea registry - if: ${{ steps.delivery.outputs.run == 'true' }} + if: ${{ (steps.delivery.outputs.run == 'true') && github.event_name != 'pull_request' }} uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} @@ -74,6 +81,6 @@ jobs: uses: docker/build-push-action@v6 with: context: . - push: true + push: ${{ github.event_name != 'pull_request' }} platforms: linux/amd64,linux/arm64 tags: ${{ steps.prep.outputs.tags }} From 9ddab5f1fa050ddc1c2ba4403b815749d0003054 Mon Sep 17 00:00:00 2001 From: Butler Agent Date: Fri, 11 Sep 2026 08:53:23 +0000 Subject: [PATCH 3/4] feat(delivery): publish immutable source images and declare release --- .butler/README.md | 5 +++++ .butler/deployment.json | 12 ++++++++++++ .gitea/workflows/build-and-push.yml | 2 ++ 3 files changed, 19 insertions(+) create mode 100644 .butler/README.md create mode 100644 .butler/deployment.json diff --git a/.butler/README.md b/.butler/README.md new file mode 100644 index 0000000..a70684e --- /dev/null +++ b/.butler/README.md @@ -0,0 +1,5 @@ +# Service release contract + +Butler resolves this repository's protected source revision to verified immutable images. The platform updates only image references in the existing production Compose file and restarts only the already running application services. It preserves current environment, storage, routes, and provider configuration. It retains previous application images and restores them if readiness fails. + +Migration and maintenance images are pinned when declared, but this release operation does not run database migrations or maintenance jobs. A release requiring a schema change needs the corresponding explicit platform migration first. No homelab image-pin commit is required for an ordinary compatible application release. diff --git a/.butler/deployment.json b/.butler/deployment.json new file mode 100644 index 0000000..03c6951 --- /dev/null +++ b/.butler/deployment.json @@ -0,0 +1,12 @@ +{ + "version": 1, + "deployment": "prepbot-release", + "environment": "production", + "images": { + "prepbot_release_prepbot_image": { + "name": "prepbot", + "tag": "sha-{revision}" + } + }, + "revision_variable": "prepbot_release_revision" +} diff --git a/.gitea/workflows/build-and-push.yml b/.gitea/workflows/build-and-push.yml index 657a811..bba21c2 100644 --- a/.gitea/workflows/build-and-push.yml +++ b/.gitea/workflows/build-and-push.yml @@ -60,6 +60,7 @@ jobs: run: | short_sha="${GITHUB_SHA::7}" tags="${REGISTRY}/${IMAGE_NAME}:sha-${short_sha}" + tags+=$'\n'"${REGISTRY}/${IMAGE_NAME}:sha-${GITHUB_SHA}" if [[ "${GITHUB_REF_TYPE}" == "branch" && ("${GITHUB_REF_NAME}" == "master" || "${GITHUB_REF_NAME}" == "main") ]]; then tags+=$'\n'"${REGISTRY}/${IMAGE_NAME}:latest" @@ -84,3 +85,4 @@ jobs: push: ${{ github.event_name != 'pull_request' }} platforms: linux/amd64,linux/arm64 tags: ${{ steps.prep.outputs.tags }} + labels: org.opencontainers.image.revision=${{ github.sha }} From 5e6f5bd518c5d093b1ca41241468bca08b865729 Mon Sep 17 00:00:00 2001 From: Butler Agent Date: Fri, 11 Sep 2026 09:14:35 +0000 Subject: [PATCH 4/4] Resolve upstream Actions explicitly and check out the exact event revision --- .gitea/workflows/build-and-push.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.gitea/workflows/build-and-push.yml b/.gitea/workflows/build-and-push.yml index bba21c2..1046c38 100644 --- a/.gitea/workflows/build-and-push.yml +++ b/.gitea/workflows/build-and-push.yml @@ -25,7 +25,7 @@ jobs: runs-on: butler-ci steps: - name: Checkout - uses: actions/checkout@v4 + uses: https://github.com/actions/checkout@v4 - name: Select relevant delivery work id: delivery @@ -39,15 +39,15 @@ jobs: bash scripts/ci-select.sh - name: Set up QEMU if: ${{ steps.delivery.outputs.run == 'true' }} - uses: docker/setup-qemu-action@v3 + uses: https://github.com/docker/setup-qemu-action@v3 - name: Set up Docker Buildx if: ${{ steps.delivery.outputs.run == 'true' }} - uses: docker/setup-buildx-action@v3 + uses: https://github.com/docker/setup-buildx-action@v3 - name: Log in to Gitea registry if: ${{ (steps.delivery.outputs.run == 'true') && github.event_name != 'pull_request' }} - uses: docker/login-action@v3 + uses: https://github.com/docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ secrets.REGISTRY_USERNAME }} @@ -79,7 +79,7 @@ jobs: - name: Build and push image if: ${{ steps.delivery.outputs.run == 'true' }} - uses: docker/build-push-action@v6 + uses: https://github.com/docker/build-push-action@v6 with: context: . push: ${{ github.event_name != 'pull_request' }}