From 4a3219648d782b51bc90654b750f7749c95d92a9 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Fri, 23 Jan 2026 19:42:47 -0800 Subject: [PATCH 01/30] Add container build workflows with composite action - Create composite action: .forgejo/actions/build-push-image - Add build-runner.yaml workflow (triggers on Dockerfile changes) - Add build-devpi.yaml workflow (triggers on Dockerfile/start.sh changes) - Mount Docker socket in runner deployment for container builds - Run runner as root to access Docker socket Co-Authored-By: Claude Opus 4.5 --- .forgejo/actions/build-push-image/action.yaml | 47 +++++++++++++++++++ .forgejo/workflows/build-devpi.yaml | 23 +++++++++ .forgejo/workflows/build-runner.yaml | 23 +++++++++ .../manifests/forgejo-runner/deployment.yaml | 9 ++++ 4 files changed, 102 insertions(+) create mode 100644 .forgejo/actions/build-push-image/action.yaml create mode 100644 .forgejo/workflows/build-devpi.yaml create mode 100644 .forgejo/workflows/build-runner.yaml diff --git a/.forgejo/actions/build-push-image/action.yaml b/.forgejo/actions/build-push-image/action.yaml new file mode 100644 index 0000000..40b53a1 --- /dev/null +++ b/.forgejo/actions/build-push-image/action.yaml @@ -0,0 +1,47 @@ +name: 'Build and Push Image' +description: 'Build a container image and push to zot registry' + +inputs: + context: + description: 'Build context path' + required: true + dockerfile: + description: 'Dockerfile path (relative to context)' + required: false + default: 'Dockerfile' + image_name: + description: 'Image name (without registry, e.g. blumeops/devpi)' + required: true + tag: + description: 'Image tag' + required: false + default: 'latest' + registry: + description: 'Registry URL' + required: false + default: 'registry.tail8d86e.ts.net' + +runs: + using: 'composite' + steps: + - name: Build image + shell: bash + run: | + docker build \ + -t ${{ inputs.registry }}/${{ inputs.image_name }}:${{ inputs.tag }} \ + -t ${{ inputs.registry }}/${{ inputs.image_name }}:${{ github.sha }} \ + -f ${{ inputs.context }}/${{ inputs.dockerfile }} \ + ${{ inputs.context }} + + - name: Push to registry + shell: bash + run: | + docker push ${{ inputs.registry }}/${{ inputs.image_name }}:${{ inputs.tag }} + docker push ${{ inputs.registry }}/${{ inputs.image_name }}:${{ github.sha }} + + - name: Verify push + shell: bash + run: | + echo "✅ Pushed: ${{ inputs.registry }}/${{ inputs.image_name }}:${{ inputs.tag }}" + echo "✅ Pushed: ${{ inputs.registry }}/${{ inputs.image_name }}:${{ github.sha }}" + curl -sf "https://${{ inputs.registry }}/v2/${{ inputs.image_name }}/tags/list" | jq . diff --git a/.forgejo/workflows/build-devpi.yaml b/.forgejo/workflows/build-devpi.yaml new file mode 100644 index 0000000..9937bc9 --- /dev/null +++ b/.forgejo/workflows/build-devpi.yaml @@ -0,0 +1,23 @@ +name: Build devpi Image + +on: + push: + paths: + - 'argocd/manifests/devpi/Dockerfile' + - 'argocd/manifests/devpi/start.sh' + - '.forgejo/workflows/build-devpi.yaml' + branches: [main] + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Build and push + uses: ./.forgejo/actions/build-push-image + with: + context: argocd/manifests/devpi + image_name: blumeops/devpi diff --git a/.forgejo/workflows/build-runner.yaml b/.forgejo/workflows/build-runner.yaml new file mode 100644 index 0000000..110f43d --- /dev/null +++ b/.forgejo/workflows/build-runner.yaml @@ -0,0 +1,23 @@ +name: Build Runner Image + +on: + push: + paths: + - 'argocd/manifests/forgejo-runner/Dockerfile' + - '.forgejo/actions/build-push-image/**' + - '.forgejo/workflows/build-runner.yaml' + branches: [main] + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Build and push + uses: ./.forgejo/actions/build-push-image + with: + context: argocd/manifests/forgejo-runner + image_name: blumeops/forgejo-runner diff --git a/argocd/manifests/forgejo-runner/deployment.yaml b/argocd/manifests/forgejo-runner/deployment.yaml index 0848e4a..dc535e2 100644 --- a/argocd/manifests/forgejo-runner/deployment.yaml +++ b/argocd/manifests/forgejo-runner/deployment.yaml @@ -48,6 +48,8 @@ spec: mountPath: /data - name: runner-config mountPath: /config + - name: docker-sock + mountPath: /var/run/docker.sock resources: requests: memory: "256Mi" @@ -55,9 +57,16 @@ spec: limits: memory: "1Gi" cpu: "1000m" + securityContext: + # Run as root to access Docker socket + runAsUser: 0 volumes: - name: runner-data emptyDir: {} - name: runner-config configMap: name: forgejo-runner-config + - name: docker-sock + hostPath: + path: /var/run/docker.sock + type: Socket -- 2.50.1 (Apple Git-155) From 4c249ff116358a4c2043aeb886db68bb8176f7d1 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Fri, 23 Jan 2026 19:44:43 -0800 Subject: [PATCH 02/30] Add docker group (GID 999) to runner security context --- argocd/manifests/forgejo-runner/deployment.yaml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/argocd/manifests/forgejo-runner/deployment.yaml b/argocd/manifests/forgejo-runner/deployment.yaml index dc535e2..57f1f05 100644 --- a/argocd/manifests/forgejo-runner/deployment.yaml +++ b/argocd/manifests/forgejo-runner/deployment.yaml @@ -58,8 +58,14 @@ spec: memory: "1Gi" cpu: "1000m" securityContext: - # Run as root to access Docker socket + # Run as root with docker group to access Docker socket runAsUser: 0 + runAsGroup: 0 + securityContext: + # Add docker group (GID 999 in minikube) for socket access + fsGroup: 999 + supplementalGroups: + - 999 volumes: - name: runner-data emptyDir: {} -- 2.50.1 (Apple Git-155) From 9f5dae5707dc4cb149578f82c79ee528002ef9da Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Fri, 23 Jan 2026 20:14:03 -0800 Subject: [PATCH 03/30] Switch to Buildah for container builds (no Docker socket needed) - Replace docker-cli with buildah/podman in runner image - Configure buildah for overlay storage with fuse-overlayfs - Add registry config for insecure local registry - Remove Docker socket mount and root security context from deployment - Update composite action to use buildah bud/push instead of docker Buildah is daemonless - no Docker socket required, cleaner security model. Co-Authored-By: Claude Opus 4.5 --- .forgejo/actions/build-push-image/action.yaml | 16 +++++------ argocd/manifests/forgejo-runner/Dockerfile | 27 ++++++++++++++++--- .../manifests/forgejo-runner/deployment.yaml | 15 ----------- 3 files changed, 31 insertions(+), 27 deletions(-) diff --git a/.forgejo/actions/build-push-image/action.yaml b/.forgejo/actions/build-push-image/action.yaml index 40b53a1..1d5676b 100644 --- a/.forgejo/actions/build-push-image/action.yaml +++ b/.forgejo/actions/build-push-image/action.yaml @@ -1,5 +1,5 @@ name: 'Build and Push Image' -description: 'Build a container image and push to zot registry' +description: 'Build a container image with Buildah and push to registry' inputs: context: @@ -24,20 +24,20 @@ inputs: runs: using: 'composite' steps: - - name: Build image + - name: Build image with Buildah shell: bash run: | - docker build \ - -t ${{ inputs.registry }}/${{ inputs.image_name }}:${{ inputs.tag }} \ - -t ${{ inputs.registry }}/${{ inputs.image_name }}:${{ github.sha }} \ - -f ${{ inputs.context }}/${{ inputs.dockerfile }} \ + buildah bud \ + --tag ${{ inputs.registry }}/${{ inputs.image_name }}:${{ inputs.tag }} \ + --tag ${{ inputs.registry }}/${{ inputs.image_name }}:${{ github.sha }} \ + --file ${{ inputs.context }}/${{ inputs.dockerfile }} \ ${{ inputs.context }} - name: Push to registry shell: bash run: | - docker push ${{ inputs.registry }}/${{ inputs.image_name }}:${{ inputs.tag }} - docker push ${{ inputs.registry }}/${{ inputs.image_name }}:${{ github.sha }} + buildah push ${{ inputs.registry }}/${{ inputs.image_name }}:${{ inputs.tag }} + buildah push ${{ inputs.registry }}/${{ inputs.image_name }}:${{ github.sha }} - name: Verify push shell: bash diff --git a/argocd/manifests/forgejo-runner/Dockerfile b/argocd/manifests/forgejo-runner/Dockerfile index e511440..5bbdef5 100644 --- a/argocd/manifests/forgejo-runner/Dockerfile +++ b/argocd/manifests/forgejo-runner/Dockerfile @@ -18,12 +18,31 @@ RUN apk add --no-cache \ gcc \ g++ \ musl-dev \ - # For container builds - ca-certificates \ - docker-cli + # For container builds (daemonless, no Docker socket needed) + buildah \ + podman \ + fuse-overlayfs \ + shadow \ + ca-certificates + +# Configure buildah for rootless operation +RUN mkdir -p /etc/containers && \ + echo '[storage]' > /etc/containers/storage.conf && \ + echo 'driver = "overlay"' >> /etc/containers/storage.conf && \ + echo 'runroot = "/tmp/containers-run"' >> /etc/containers/storage.conf && \ + echo 'graphroot = "/tmp/containers-storage"' >> /etc/containers/storage.conf && \ + echo '[storage.options.overlay]' >> /etc/containers/storage.conf && \ + echo 'mount_program = "/usr/bin/fuse-overlayfs"' >> /etc/containers/storage.conf + +# Configure registries (allow insecure for local registry) +RUN mkdir -p /etc/containers && \ + echo 'unqualified-search-registries = ["docker.io"]' > /etc/containers/registries.conf && \ + echo '[[registry]]' >> /etc/containers/registries.conf && \ + echo 'location = "registry.tail8d86e.ts.net"' >> /etc/containers/registries.conf && \ + echo 'insecure = true' >> /etc/containers/registries.conf # Verify tools are available -RUN node --version && npm --version && docker --version +RUN node --version && npm --version && buildah --version # Switch back to non-root user USER 1000 diff --git a/argocd/manifests/forgejo-runner/deployment.yaml b/argocd/manifests/forgejo-runner/deployment.yaml index 57f1f05..0848e4a 100644 --- a/argocd/manifests/forgejo-runner/deployment.yaml +++ b/argocd/manifests/forgejo-runner/deployment.yaml @@ -48,8 +48,6 @@ spec: mountPath: /data - name: runner-config mountPath: /config - - name: docker-sock - mountPath: /var/run/docker.sock resources: requests: memory: "256Mi" @@ -57,22 +55,9 @@ spec: limits: memory: "1Gi" cpu: "1000m" - securityContext: - # Run as root with docker group to access Docker socket - runAsUser: 0 - runAsGroup: 0 - securityContext: - # Add docker group (GID 999 in minikube) for socket access - fsGroup: 999 - supplementalGroups: - - 999 volumes: - name: runner-data emptyDir: {} - name: runner-config configMap: name: forgejo-runner-config - - name: docker-sock - hostPath: - path: /var/run/docker.sock - type: Socket -- 2.50.1 (Apple Git-155) From c2be742094b69cc5dde92ff407ce95dcf5babd18 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Fri, 23 Jan 2026 21:03:53 -0800 Subject: [PATCH 04/30] Add imagePullPolicy: Always to ensure fresh image pulls --- argocd/manifests/forgejo-runner/deployment.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/argocd/manifests/forgejo-runner/deployment.yaml b/argocd/manifests/forgejo-runner/deployment.yaml index 0848e4a..427d414 100644 --- a/argocd/manifests/forgejo-runner/deployment.yaml +++ b/argocd/manifests/forgejo-runner/deployment.yaml @@ -17,6 +17,7 @@ spec: containers: - name: runner image: registry.tail8d86e.ts.net/blumeops/forgejo-runner:latest + imagePullPolicy: Always env: # Use internal k8s service via Tailscale operator egress - name: FORGEJO_INSTANCE_URL -- 2.50.1 (Apple Git-155) From 6d8e6ea4c0eadadc88c9ed1b60a3382d11ee6168 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Fri, 23 Jan 2026 21:05:40 -0800 Subject: [PATCH 05/30] Update test workflow to verify buildah/podman instead of docker --- .forgejo/workflows/test.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.forgejo/workflows/test.yaml b/.forgejo/workflows/test.yaml index 134992b..f336ac9 100644 --- a/.forgejo/workflows/test.yaml +++ b/.forgejo/workflows/test.yaml @@ -26,8 +26,9 @@ jobs: make --version | head -1 gcc --version | head -1 echo "" - echo "=== Docker ===" - docker --version + echo "=== Container tools (Buildah) ===" + buildah --version + podman --version echo "" echo "=== Other tools ===" curl --version | head -1 -- 2.50.1 (Apple Git-155) From a3a61146a3ffaefdcf02f15c689486eff04cc0bf Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Fri, 23 Jan 2026 21:14:02 -0800 Subject: [PATCH 06/30] Fix SIGPIPE in test workflow by adding || true to piped commands --- .forgejo/workflows/test.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.forgejo/workflows/test.yaml b/.forgejo/workflows/test.yaml index f336ac9..1db41ee 100644 --- a/.forgejo/workflows/test.yaml +++ b/.forgejo/workflows/test.yaml @@ -23,15 +23,15 @@ jobs: git --version echo "" echo "=== Build tools ===" - make --version | head -1 - gcc --version | head -1 + make --version 2>&1 | head -1 || true + gcc --version 2>&1 | head -1 || true echo "" echo "=== Container tools (Buildah) ===" buildah --version podman --version echo "" echo "=== Other tools ===" - curl --version | head -1 + curl --version 2>&1 | head -1 || true jq --version - name: Show repo info -- 2.50.1 (Apple Git-155) From b2967817d618c42bfe66054a1ab2f29588ea7bc0 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Fri, 23 Jan 2026 21:15:49 -0800 Subject: [PATCH 07/30] Add comment to test buildah workflow --- argocd/manifests/forgejo-runner/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/argocd/manifests/forgejo-runner/Dockerfile b/argocd/manifests/forgejo-runner/Dockerfile index 5bbdef5..7316438 100644 --- a/argocd/manifests/forgejo-runner/Dockerfile +++ b/argocd/manifests/forgejo-runner/Dockerfile @@ -46,3 +46,4 @@ RUN node --version && npm --version && buildah --version # Switch back to non-root user USER 1000 +# Built with Buildah -- 2.50.1 (Apple Git-155) From 3702e7eec24b55d1d68b33d79db8ac19090b5a7a Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Fri, 23 Jan 2026 21:34:33 -0800 Subject: [PATCH 08/30] Add tag-based container release workflow - Workflows trigger on git tags (e.g. runner-v1.0.0, devpi-v1.0.0) - Composite action takes explicit version, tags image with version + SHA - Add mise-tasks/container-list to enumerate containers and recent tags - Add mise-tasks/container-release to create release tags - Update CLAUDE.md with container release commands - TODO: investigate zot tag immutability Co-Authored-By: Claude Opus 4.5 --- .forgejo/actions/build-push-image/action.yaml | 29 ++++--- .forgejo/workflows/build-devpi.yaml | 26 +++++-- .forgejo/workflows/build-runner.yaml | 26 +++++-- CLAUDE.md | 7 ++ mise-tasks/container-list | 53 +++++++++++++ mise-tasks/container-release | 75 +++++++++++++++++++ 6 files changed, 193 insertions(+), 23 deletions(-) create mode 100755 mise-tasks/container-list create mode 100755 mise-tasks/container-release diff --git a/.forgejo/actions/build-push-image/action.yaml b/.forgejo/actions/build-push-image/action.yaml index 1d5676b..b278e02 100644 --- a/.forgejo/actions/build-push-image/action.yaml +++ b/.forgejo/actions/build-push-image/action.yaml @@ -1,5 +1,8 @@ name: 'Build and Push Image' -description: 'Build a container image with Buildah and push to registry' +description: 'Build a container image with Buildah and push to zot registry' + +# TODO: Investigate zot tag immutability to prevent overwriting released versions +# See: https://zotregistry.dev/v2.1.1/articles/immutable-tags/ inputs: context: @@ -12,10 +15,9 @@ inputs: image_name: description: 'Image name (without registry, e.g. blumeops/devpi)' required: true - tag: - description: 'Image tag' - required: false - default: 'latest' + version: + description: 'Version tag (e.g. v1.0.0)' + required: true registry: description: 'Registry URL' required: false @@ -27,8 +29,9 @@ runs: - name: Build image with Buildah shell: bash run: | + echo "Building ${{ inputs.image_name }}:${{ inputs.version }}" buildah bud \ - --tag ${{ inputs.registry }}/${{ inputs.image_name }}:${{ inputs.tag }} \ + --tag ${{ inputs.registry }}/${{ inputs.image_name }}:${{ inputs.version }} \ --tag ${{ inputs.registry }}/${{ inputs.image_name }}:${{ github.sha }} \ --file ${{ inputs.context }}/${{ inputs.dockerfile }} \ ${{ inputs.context }} @@ -36,12 +39,16 @@ runs: - name: Push to registry shell: bash run: | - buildah push ${{ inputs.registry }}/${{ inputs.image_name }}:${{ inputs.tag }} + echo "Pushing ${{ inputs.image_name }}:${{ inputs.version }}" + buildah push ${{ inputs.registry }}/${{ inputs.image_name }}:${{ inputs.version }} buildah push ${{ inputs.registry }}/${{ inputs.image_name }}:${{ github.sha }} - - name: Verify push + - name: Summary shell: bash run: | - echo "✅ Pushed: ${{ inputs.registry }}/${{ inputs.image_name }}:${{ inputs.tag }}" - echo "✅ Pushed: ${{ inputs.registry }}/${{ inputs.image_name }}:${{ github.sha }}" - curl -sf "https://${{ inputs.registry }}/v2/${{ inputs.image_name }}/tags/list" | jq . + echo "✅ Built and pushed:" + echo " ${{ inputs.registry }}/${{ inputs.image_name }}:${{ inputs.version }}" + echo " ${{ inputs.registry }}/${{ inputs.image_name }}:${{ github.sha }}" + echo "" + echo "Registry tags:" + curl -sf "https://${{ inputs.registry }}/v2/${{ inputs.image_name }}/tags/list" | jq -r '.tags[]' | sort -V | tail -10 diff --git a/.forgejo/workflows/build-devpi.yaml b/.forgejo/workflows/build-devpi.yaml index 9937bc9..89318b6 100644 --- a/.forgejo/workflows/build-devpi.yaml +++ b/.forgejo/workflows/build-devpi.yaml @@ -1,13 +1,14 @@ -name: Build devpi Image +name: Build devpi on: push: - paths: - - 'argocd/manifests/devpi/Dockerfile' - - 'argocd/manifests/devpi/start.sh' - - '.forgejo/workflows/build-devpi.yaml' - branches: [main] + tags: + - 'devpi-v*' workflow_dispatch: + inputs: + version: + description: 'Version (e.g. v1.0.0)' + required: true jobs: build: @@ -16,8 +17,21 @@ jobs: - name: Checkout uses: actions/checkout@v4 + - name: Extract version from tag + id: version + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + VERSION="${{ github.event.inputs.version }}" + else + # Extract version from tag: devpi-v1.0.0 -> v1.0.0 + VERSION="${GITHUB_REF_NAME#devpi-}" + fi + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + echo "Building version: $VERSION" + - name: Build and push uses: ./.forgejo/actions/build-push-image with: context: argocd/manifests/devpi image_name: blumeops/devpi + version: ${{ steps.version.outputs.version }} diff --git a/.forgejo/workflows/build-runner.yaml b/.forgejo/workflows/build-runner.yaml index 110f43d..54162b6 100644 --- a/.forgejo/workflows/build-runner.yaml +++ b/.forgejo/workflows/build-runner.yaml @@ -1,13 +1,14 @@ -name: Build Runner Image +name: Build forgejo-runner on: push: - paths: - - 'argocd/manifests/forgejo-runner/Dockerfile' - - '.forgejo/actions/build-push-image/**' - - '.forgejo/workflows/build-runner.yaml' - branches: [main] + tags: + - 'runner-v*' workflow_dispatch: + inputs: + version: + description: 'Version (e.g. v1.0.0)' + required: true jobs: build: @@ -16,8 +17,21 @@ jobs: - name: Checkout uses: actions/checkout@v4 + - name: Extract version from tag + id: version + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + VERSION="${{ github.event.inputs.version }}" + else + # Extract version from tag: runner-v1.0.0 -> v1.0.0 + VERSION="${GITHUB_REF_NAME#runner-}" + fi + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + echo "Building version: $VERSION" + - name: Build and push uses: ./.forgejo/actions/build-push-image with: context: argocd/manifests/forgejo-runner image_name: blumeops/forgejo-runner + version: ${{ steps.version.outputs.version }} diff --git a/CLAUDE.md b/CLAUDE.md index 82ed044..1399f9a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -134,6 +134,13 @@ When migrating a service from indri to k8s, the Tailscale hostname must be freed Use `ssh indri 'tailscale serve status --json'` to check current serve entries (the non-JSON output may be empty even when entries exist). +## Container Image Releases + +```fish +mise run container-list # Show containers and recent tags +mise run container-release runner v1.0.0 # Tag and trigger build workflow +``` + ## Third-Party Projects When a task requires cloning or using a third-party git repository (e.g., for building from source), **ask the user to mirror it on forge first**, then clone from the mirror: diff --git a/mise-tasks/container-list b/mise-tasks/container-list new file mode 100755 index 0000000..21a2ad9 --- /dev/null +++ b/mise-tasks/container-list @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +#MISE description="List available containers and their recent tags" + +set -euo pipefail + +REGISTRY="registry.tail8d86e.ts.net" +WORKFLOW_DIR=".forgejo/workflows" + +echo "Container Images" +echo "================" +echo "" + +# Find all build-*.yaml workflows +for workflow in "$WORKFLOW_DIR"/build-*.yaml; do + [[ -f "$workflow" ]] || continue + + # Extract container name from filename: build-runner.yaml -> runner + filename=$(basename "$workflow") + container="${filename#build-}" + container="${container%.yaml}" + + # Skip if not a container build workflow (check for image_name) + if ! grep -q "image_name:" "$workflow" 2>/dev/null; then + continue + fi + + # Extract image name from workflow + image=$(grep -E "^\s+image_name:" "$workflow" | head -1 | awk '{print $2}') + + echo "📦 $container" + echo " Image: $REGISTRY/$image" + echo " Workflow: $workflow" + + # Query zot for recent tags + tags=$(curl -sf "https://$REGISTRY/v2/$image/tags/list" 2>/dev/null | jq -r '.tags // [] | .[]' | grep -E '^v[0-9]' | sort -V | tail -4 || true) + + if [[ -n "$tags" ]]; then + echo " Recent tags:" + echo "$tags" | while read -r tag; do + echo " - $tag" + done + else + echo " Recent tags: (none)" + fi + echo "" +done + +echo "---" +echo "To release a new version:" +echo " mise run container-release " +echo "" +echo "Example:" +echo " mise run container-release runner v1.0.0" diff --git a/mise-tasks/container-release b/mise-tasks/container-release new file mode 100755 index 0000000..9e8802b --- /dev/null +++ b/mise-tasks/container-release @@ -0,0 +1,75 @@ +#!/usr/bin/env bash +#MISE description="Release a container image by creating a git tag" + +set -euo pipefail + +CONTAINER="${1:-}" +VERSION="${2:-}" + +if [[ -z "$CONTAINER" || -z "$VERSION" ]]; then + echo "Usage: mise run container-release " + echo "" + echo "Run 'mise run container-list' to see available containers and recent tags." + exit 1 +fi + +# Validate version format +if [[ ! "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + echo "Error: Version must be in format vX.Y.Z (e.g. v1.0.0)" + exit 1 +fi + +TAG="${CONTAINER}-${VERSION}" + +echo "Creating release tag: $TAG" +echo "" + +# Check if tag already exists +if git rev-parse "$TAG" >/dev/null 2>&1; then + echo "Error: Tag '$TAG' already exists" + echo "Existing tags for $CONTAINER:" + git tag -l "${CONTAINER}-v*" | sort -V | tail -5 + exit 1 +fi + +# Find the workflow file to determine image name +WORKFLOW_FILE=".forgejo/workflows/build-${CONTAINER}.yaml" +if [[ ! -f "$WORKFLOW_FILE" ]]; then + echo "Error: No workflow found for container '$CONTAINER'" + echo "" + echo "Run 'mise run container-list' to see available containers." + exit 1 +fi + +# Extract image name from workflow +IMAGE=$(grep -E "^\s+image_name:" "$WORKFLOW_FILE" | head -1 | awk '{print $2}') +if [[ -z "$IMAGE" ]]; then + echo "Error: Could not determine image name from $WORKFLOW_FILE" + exit 1 +fi + +echo "Container: $CONTAINER" +echo "Workflow: $WORKFLOW_FILE" +echo "Image: registry.tail8d86e.ts.net/$IMAGE:$VERSION" +echo "" + +# Confirm +read -p "Create tag and push? [y/N] " -n 1 -r +echo +if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo "Aborted." + exit 0 +fi + +# Create and push tag +git tag "$TAG" +git push origin "$TAG" + +echo "" +echo "✅ Tag '$TAG' created and pushed" +echo "" +echo "The workflow will now build and push:" +echo " registry.tail8d86e.ts.net/$IMAGE:$VERSION" +echo "" +echo "Monitor the build at:" +echo " https://forge.tail8d86e.ts.net/eblume/blumeops/actions" -- 2.50.1 (Apple Git-155) From 0c1a3bf0cfd224b30a540feb95fb4b3b08a3ea98 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Fri, 23 Jan 2026 21:41:01 -0800 Subject: [PATCH 09/30] Remove test comment from Dockerfile Co-Authored-By: Claude Opus 4.5 --- argocd/manifests/forgejo-runner/Dockerfile | 1 - 1 file changed, 1 deletion(-) diff --git a/argocd/manifests/forgejo-runner/Dockerfile b/argocd/manifests/forgejo-runner/Dockerfile index 7316438..5bbdef5 100644 --- a/argocd/manifests/forgejo-runner/Dockerfile +++ b/argocd/manifests/forgejo-runner/Dockerfile @@ -46,4 +46,3 @@ RUN node --version && npm --version && buildah --version # Switch back to non-root user USER 1000 -# Built with Buildah -- 2.50.1 (Apple Git-155) From 4e0767b4d9d7689e6c425cbcb3eca47d6bec42b2 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Fri, 23 Jan 2026 22:00:19 -0800 Subject: [PATCH 10/30] Build forgejo-runner from source with proper user setup - Multi-stage build from mirrored forgejo-runner source - Create proper runner user with passwd entry (fixes buildah) - Use named user instead of numeric UID Co-Authored-By: Claude Opus 4.5 --- argocd/manifests/forgejo-runner/Dockerfile | 58 ++++++++++++++-------- 1 file changed, 37 insertions(+), 21 deletions(-) diff --git a/argocd/manifests/forgejo-runner/Dockerfile b/argocd/manifests/forgejo-runner/Dockerfile index 5bbdef5..64bf571 100644 --- a/argocd/manifests/forgejo-runner/Dockerfile +++ b/argocd/manifests/forgejo-runner/Dockerfile @@ -1,19 +1,37 @@ -FROM code.forgejo.org/forgejo/runner:3.5.1 +# Build forgejo-runner from source +# Source: https://forge.tail8d86e.ts.net/eblume/forgejo-runner (mirror of code.forgejo.org/forgejo/runner) -# Switch to root to install packages -USER root +FROM golang:1.24-alpine AS builder -# The base image is Alpine Linux -# Install tools needed for GitHub Actions and builds +ARG FORGEJO_RUNNER_VERSION=v3.5.1 + +RUN apk add --no-cache git make build-base + +WORKDIR /src +RUN git clone --depth 1 --branch ${FORGEJO_RUNNER_VERSION} \ + https://forge.tail8d86e.ts.net/eblume/forgejo-runner.git . + +RUN make clean && make build + +# Runtime image +FROM alpine:3.21 + +# Create runner user with proper passwd entry (required by buildah) +RUN addgroup -g 1000 runner && \ + adduser -D -u 1000 -G runner -h /data runner + +# Install runtime dependencies RUN apk add --no-cache \ # Required for actions/checkout and other Node-based actions nodejs \ npm \ - # Build essentials + # Core tools git \ + bash \ curl \ wget \ jq \ + # Build essentials make \ gcc \ g++ \ @@ -22,27 +40,25 @@ RUN apk add --no-cache \ buildah \ podman \ fuse-overlayfs \ - shadow \ ca-certificates +# Copy runner binary from builder +COPY --from=builder /src/forgejo-runner /bin/forgejo-runner + # Configure buildah for rootless operation RUN mkdir -p /etc/containers && \ - echo '[storage]' > /etc/containers/storage.conf && \ - echo 'driver = "overlay"' >> /etc/containers/storage.conf && \ - echo 'runroot = "/tmp/containers-run"' >> /etc/containers/storage.conf && \ - echo 'graphroot = "/tmp/containers-storage"' >> /etc/containers/storage.conf && \ - echo '[storage.options.overlay]' >> /etc/containers/storage.conf && \ - echo 'mount_program = "/usr/bin/fuse-overlayfs"' >> /etc/containers/storage.conf + printf '[storage]\ndriver = "overlay"\nrunroot = "/tmp/containers-run"\ngraphroot = "/tmp/containers-storage"\n[storage.options.overlay]\nmount_program = "/usr/bin/fuse-overlayfs"\n' \ + > /etc/containers/storage.conf # Configure registries (allow insecure for local registry) -RUN mkdir -p /etc/containers && \ - echo 'unqualified-search-registries = ["docker.io"]' > /etc/containers/registries.conf && \ - echo '[[registry]]' >> /etc/containers/registries.conf && \ - echo 'location = "registry.tail8d86e.ts.net"' >> /etc/containers/registries.conf && \ - echo 'insecure = true' >> /etc/containers/registries.conf +RUN printf 'unqualified-search-registries = ["docker.io"]\n[[registry]]\nlocation = "registry.tail8d86e.ts.net"\ninsecure = true\n' \ + > /etc/containers/registries.conf # Verify tools are available -RUN node --version && npm --version && buildah --version +RUN node --version && npm --version && buildah --version && /bin/forgejo-runner --version -# Switch back to non-root user -USER 1000 +ENV HOME=/data +WORKDIR /data +USER runner + +CMD ["/bin/forgejo-runner"] -- 2.50.1 (Apple Git-155) From a979ddaf0cc1c5c9d6fecf9d6a0b39377f174704 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Fri, 23 Jan 2026 22:07:06 -0800 Subject: [PATCH 11/30] Use versioned runner image v1.0.1 - Remove imagePullPolicy: Always (rely on immutable tags) - Use explicit version tag instead of :latest Co-Authored-By: Claude Opus 4.5 --- argocd/manifests/forgejo-runner/deployment.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/argocd/manifests/forgejo-runner/deployment.yaml b/argocd/manifests/forgejo-runner/deployment.yaml index 427d414..ef35053 100644 --- a/argocd/manifests/forgejo-runner/deployment.yaml +++ b/argocd/manifests/forgejo-runner/deployment.yaml @@ -16,8 +16,7 @@ spec: serviceAccountName: forgejo-runner containers: - name: runner - image: registry.tail8d86e.ts.net/blumeops/forgejo-runner:latest - imagePullPolicy: Always + image: registry.tail8d86e.ts.net/blumeops/forgejo-runner:v1.0.1 env: # Use internal k8s service via Tailscale operator egress - name: FORGEJO_INSTANCE_URL -- 2.50.1 (Apple Git-155) From 8d2e180d5d6c4ac44bf5c5640e05beeea6dfa41a Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Fri, 23 Jan 2026 22:13:03 -0800 Subject: [PATCH 12/30] Add subuid/subgid for rootless buildah Buildah needs UID/GID remapping to extract images with files owned by different users (root, shadow, etc). Configure subordinate UID/GID ranges for the runner user. Co-Authored-By: Claude Opus 4.5 --- argocd/manifests/forgejo-runner/Dockerfile | 5 ++++- argocd/manifests/forgejo-runner/deployment.yaml | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/argocd/manifests/forgejo-runner/Dockerfile b/argocd/manifests/forgejo-runner/Dockerfile index 64bf571..862f531 100644 --- a/argocd/manifests/forgejo-runner/Dockerfile +++ b/argocd/manifests/forgejo-runner/Dockerfile @@ -17,8 +17,11 @@ RUN make clean && make build FROM alpine:3.21 # Create runner user with proper passwd entry (required by buildah) +# Also configure subuid/subgid for rootless container builds RUN addgroup -g 1000 runner && \ - adduser -D -u 1000 -G runner -h /data runner + adduser -D -u 1000 -G runner -h /data runner && \ + echo "runner:100000:65536" >> /etc/subuid && \ + echo "runner:100000:65536" >> /etc/subgid # Install runtime dependencies RUN apk add --no-cache \ diff --git a/argocd/manifests/forgejo-runner/deployment.yaml b/argocd/manifests/forgejo-runner/deployment.yaml index ef35053..79c70d3 100644 --- a/argocd/manifests/forgejo-runner/deployment.yaml +++ b/argocd/manifests/forgejo-runner/deployment.yaml @@ -16,7 +16,7 @@ spec: serviceAccountName: forgejo-runner containers: - name: runner - image: registry.tail8d86e.ts.net/blumeops/forgejo-runner:v1.0.1 + image: registry.tail8d86e.ts.net/blumeops/forgejo-runner:v1.0.3 env: # Use internal k8s service via Tailscale operator egress - name: FORGEJO_INSTANCE_URL -- 2.50.1 (Apple Git-155) From 676c1782d1e137f779399af8970d4734e9d601c7 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Fri, 23 Jan 2026 22:28:44 -0800 Subject: [PATCH 13/30] Add forgejo_runner Ansible role for indri Run forgejo-runner directly on indri using Docker container mode instead of trying to build containers inside k8s pods. This avoids nested containerization complexity. Features: - Build from source using mise + Go - Docker container mode for job isolation - Can build containers via Docker socket - Labels: docker-builder (distinct from k8s runner) Co-Authored-By: Claude Opus 4.5 --- ansible/playbooks/indri.yml | 19 +++++ .../roles/forgejo_runner/defaults/main.yml | 19 +++++ .../roles/forgejo_runner/handlers/main.yml | 11 +++ ansible/roles/forgejo_runner/tasks/main.yml | 83 +++++++++++++++++++ .../forgejo_runner/templates/config.yaml.j2 | 15 ++++ .../templates/forgejo-runner.plist.j2 | 26 ++++++ 6 files changed, 173 insertions(+) create mode 100644 ansible/roles/forgejo_runner/defaults/main.yml create mode 100644 ansible/roles/forgejo_runner/handlers/main.yml create mode 100644 ansible/roles/forgejo_runner/tasks/main.yml create mode 100644 ansible/roles/forgejo_runner/templates/config.yaml.j2 create mode 100644 ansible/roles/forgejo_runner/templates/forgejo-runner.plist.j2 diff --git a/ansible/playbooks/indri.yml b/ansible/playbooks/indri.yml index 6e962f1..4366eb0 100644 --- a/ansible/playbooks/indri.yml +++ b/ansible/playbooks/indri.yml @@ -61,6 +61,23 @@ no_log: true tags: [forgejo] + # Forgejo runner token (for indri-based runner) + - name: Fetch forgejo runner token + ansible.builtin.command: + cmd: op --vault vg6xf6vvfmoh5hqjjhlhbeoaie item get w3663ffnvkewbftncqxtcpeavy --fields runner-token --reveal + delegate_to: localhost + register: _forgejo_runner_token + changed_when: false + no_log: true + check_mode: false + tags: [forgejo_runner] + + - name: Set forgejo runner token fact + ansible.builtin.set_fact: + forgejo_runner_token: "{{ _forgejo_runner_token.stdout }}" + no_log: true + tags: [forgejo_runner] + roles: - role: alloy tags: alloy @@ -82,3 +99,5 @@ tags: plex_metrics - role: tailscale_serve tags: tailscale-serve + - role: forgejo_runner + tags: forgejo_runner diff --git a/ansible/roles/forgejo_runner/defaults/main.yml b/ansible/roles/forgejo_runner/defaults/main.yml new file mode 100644 index 0000000..643f3a2 --- /dev/null +++ b/ansible/roles/forgejo_runner/defaults/main.yml @@ -0,0 +1,19 @@ +--- +forgejo_runner_repo_dir: /Users/erichblume/code/3rd/forgejo-runner +forgejo_runner_binary: "{{ forgejo_runner_repo_dir }}/forgejo-runner" +forgejo_runner_data_dir: /Users/erichblume/.forgejo-runner +forgejo_runner_config_dir: /Users/erichblume/.config/forgejo-runner +forgejo_runner_log_dir: /Users/erichblume/Library/Logs + +# Runner registration +forgejo_runner_instance_url: "http://localhost:3001" +forgejo_runner_name: "indri-docker-runner" +forgejo_runner_labels: "docker-builder:docker" + +# Runner config +forgejo_runner_capacity: 2 +forgejo_runner_timeout: 3h + +# Docker container settings for jobs +forgejo_runner_docker_network: bridge +forgejo_runner_privileged: true # Needed for container builds diff --git a/ansible/roles/forgejo_runner/handlers/main.yml b/ansible/roles/forgejo_runner/handlers/main.yml new file mode 100644 index 0000000..9cad7d1 --- /dev/null +++ b/ansible/roles/forgejo_runner/handlers/main.yml @@ -0,0 +1,11 @@ +--- +- name: Restart forgejo-runner + block: + - name: Unload forgejo-runner LaunchAgent + ansible.builtin.command: launchctl unload ~/Library/LaunchAgents/mcquack.eblume.forgejo-runner.plist + failed_when: false + changed_when: true + + - name: Load forgejo-runner LaunchAgent + ansible.builtin.command: launchctl load ~/Library/LaunchAgents/mcquack.eblume.forgejo-runner.plist + changed_when: true diff --git a/ansible/roles/forgejo_runner/tasks/main.yml b/ansible/roles/forgejo_runner/tasks/main.yml new file mode 100644 index 0000000..d7106c1 --- /dev/null +++ b/ansible/roles/forgejo_runner/tasks/main.yml @@ -0,0 +1,83 @@ +--- +# Forgejo Runner on indri +# +# Uses Docker container mode for job isolation. +# Can build containers using Docker (via socket). +# +# ONE-TIME SETUP (before running ansible): +# +# 1. Clone forgejo-runner from forge mirror: +# ssh indri 'git clone https://forge.tail8d86e.ts.net/eblume/forgejo-runner.git ~/code/3rd/forgejo-runner' +# +# 2. Set up Go via mise: +# ssh indri 'cd ~/code/3rd/forgejo-runner && mise use go@1.24' +# +# 3. Build: +# ssh indri 'cd ~/code/3rd/forgejo-runner && mise x -- make build' +# +# 4. Run ansible to deploy config and LaunchAgent + +- name: Verify forgejo-runner binary exists + ansible.builtin.stat: + path: "{{ forgejo_runner_binary }}" + register: forgejo_runner_binary_stat + +- name: Fail if forgejo-runner binary not found + ansible.builtin.fail: + msg: | + Forgejo-runner binary not found at {{ forgejo_runner_binary }}. + Please build from source first: + ssh indri 'cd ~/code/3rd/forgejo-runner && mise x -- make build' + when: not forgejo_runner_binary_stat.stat.exists + +- name: Ensure forgejo-runner directories exist + ansible.builtin.file: + path: "{{ item }}" + state: directory + mode: '0755' + loop: + - "{{ forgejo_runner_data_dir }}" + - "{{ forgejo_runner_config_dir }}" + +- name: Deploy forgejo-runner config + ansible.builtin.template: + src: config.yaml.j2 + dest: "{{ forgejo_runner_config_dir }}/config.yaml" + mode: '0644' + notify: Restart forgejo-runner + +- name: Check if runner is registered + ansible.builtin.stat: + path: "{{ forgejo_runner_data_dir }}/.runner" + register: forgejo_runner_registered + +- name: Register runner with Forgejo + ansible.builtin.command: + cmd: > + {{ forgejo_runner_binary }} register + --instance "{{ forgejo_runner_instance_url }}" + --token "{{ forgejo_runner_token }}" + --name "{{ forgejo_runner_name }}" + --labels "{{ forgejo_runner_labels }}" + --no-interactive + chdir: "{{ forgejo_runner_data_dir }}" + when: not forgejo_runner_registered.stat.exists + changed_when: true + +- name: Deploy forgejo-runner LaunchAgent plist + ansible.builtin.template: + src: forgejo-runner.plist.j2 + dest: ~/Library/LaunchAgents/mcquack.eblume.forgejo-runner.plist + mode: '0644' + notify: Restart forgejo-runner + +- name: Check if forgejo-runner LaunchAgent is loaded + ansible.builtin.command: launchctl list mcquack.eblume.forgejo-runner + register: forgejo_runner_launchctl_check + changed_when: false + failed_when: false + +- name: Load forgejo-runner LaunchAgent if not loaded + ansible.builtin.command: launchctl load ~/Library/LaunchAgents/mcquack.eblume.forgejo-runner.plist + when: forgejo_runner_launchctl_check.rc != 0 + changed_when: true diff --git a/ansible/roles/forgejo_runner/templates/config.yaml.j2 b/ansible/roles/forgejo_runner/templates/config.yaml.j2 new file mode 100644 index 0000000..7de5cc0 --- /dev/null +++ b/ansible/roles/forgejo_runner/templates/config.yaml.j2 @@ -0,0 +1,15 @@ +# {{ ansible_managed }} +log: + level: info + +runner: + file: {{ forgejo_runner_data_dir }}/.runner + capacity: {{ forgejo_runner_capacity }} + timeout: {{ forgejo_runner_timeout }} + +container: + network: "{{ forgejo_runner_docker_network }}" + privileged: {{ forgejo_runner_privileged | lower }} + # Mount Docker socket so jobs can build containers + valid_volumes: + - /var/run/docker.sock diff --git a/ansible/roles/forgejo_runner/templates/forgejo-runner.plist.j2 b/ansible/roles/forgejo_runner/templates/forgejo-runner.plist.j2 new file mode 100644 index 0000000..4bac25f --- /dev/null +++ b/ansible/roles/forgejo_runner/templates/forgejo-runner.plist.j2 @@ -0,0 +1,26 @@ + + + + + + Label + mcquack.eblume.forgejo-runner + ProgramArguments + + {{ forgejo_runner_binary }} + daemon + --config + {{ forgejo_runner_config_dir }}/config.yaml + + WorkingDirectory + {{ forgejo_runner_data_dir }} + RunAtLoad + + KeepAlive + + StandardOutPath + {{ forgejo_runner_log_dir }}/mcquack.forgejo-runner.out.log + StandardErrorPath + {{ forgejo_runner_log_dir }}/mcquack.forgejo-runner.err.log + + -- 2.50.1 (Apple Git-155) From 7a637d2ebf62b442df43ca1ae9bbc924cba22195 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Fri, 23 Jan 2026 22:31:06 -0800 Subject: [PATCH 14/30] Fix 1Password field name for runner token Use runner_reg field (matching existing k8s secret template) Co-Authored-By: Claude Opus 4.5 --- ansible/playbooks/indri.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansible/playbooks/indri.yml b/ansible/playbooks/indri.yml index 4366eb0..b12e905 100644 --- a/ansible/playbooks/indri.yml +++ b/ansible/playbooks/indri.yml @@ -64,7 +64,7 @@ # Forgejo runner token (for indri-based runner) - name: Fetch forgejo runner token ansible.builtin.command: - cmd: op --vault vg6xf6vvfmoh5hqjjhlhbeoaie item get w3663ffnvkewbftncqxtcpeavy --fields runner-token --reveal + cmd: op --vault vg6xf6vvfmoh5hqjjhlhbeoaie item get w3663ffnvkewbftncqxtcpeavy --fields runner_reg --reveal delegate_to: localhost register: _forgejo_runner_token changed_when: false -- 2.50.1 (Apple Git-155) From 8b75b696f00725148c82d9b7f16b877597cd03d8 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Sat, 24 Jan 2026 08:44:23 -0800 Subject: [PATCH 15/30] Fix forgejo_runner handler (no nested blocks) Co-Authored-By: Claude Opus 4.5 --- ansible/roles/forgejo_runner/handlers/main.yml | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/ansible/roles/forgejo_runner/handlers/main.yml b/ansible/roles/forgejo_runner/handlers/main.yml index 9cad7d1..a1798f4 100644 --- a/ansible/roles/forgejo_runner/handlers/main.yml +++ b/ansible/roles/forgejo_runner/handlers/main.yml @@ -1,11 +1,7 @@ --- - name: Restart forgejo-runner - block: - - name: Unload forgejo-runner LaunchAgent - ansible.builtin.command: launchctl unload ~/Library/LaunchAgents/mcquack.eblume.forgejo-runner.plist - failed_when: false - changed_when: true - - - name: Load forgejo-runner LaunchAgent - ansible.builtin.command: launchctl load ~/Library/LaunchAgents/mcquack.eblume.forgejo-runner.plist - changed_when: true + listen: Restart forgejo-runner + ansible.builtin.shell: | + launchctl unload ~/Library/LaunchAgents/mcquack.eblume.forgejo-runner.plist 2>/dev/null || true + launchctl load ~/Library/LaunchAgents/mcquack.eblume.forgejo-runner.plist + changed_when: true -- 2.50.1 (Apple Git-155) From 2c284ed0cf2897736f9c0f636a0b7b247e45d5ea Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Sat, 24 Jan 2026 08:49:02 -0800 Subject: [PATCH 16/30] Switch container builds to indri docker-builder runner - Use Docker instead of buildah in composite action - Build workflows now run on docker-builder label - Add actionlint config for custom runner labels - Avoids nested containerization complexity in k8s Co-Authored-By: Claude Opus 4.5 --- .forgejo/actions/build-push-image/action.yaml | 14 +++++++------- .forgejo/workflows/build-devpi.yaml | 2 +- .forgejo/workflows/build-runner.yaml | 2 +- .github/actionlint.yaml | 5 +++++ .pre-commit-config.yaml | 1 + 5 files changed, 15 insertions(+), 9 deletions(-) create mode 100644 .github/actionlint.yaml diff --git a/.forgejo/actions/build-push-image/action.yaml b/.forgejo/actions/build-push-image/action.yaml index b278e02..ac6f711 100644 --- a/.forgejo/actions/build-push-image/action.yaml +++ b/.forgejo/actions/build-push-image/action.yaml @@ -1,5 +1,5 @@ name: 'Build and Push Image' -description: 'Build a container image with Buildah and push to zot registry' +description: 'Build a container image with Docker and push to zot registry' # TODO: Investigate zot tag immutability to prevent overwriting released versions # See: https://zotregistry.dev/v2.1.1/articles/immutable-tags/ @@ -26,11 +26,11 @@ inputs: runs: using: 'composite' steps: - - name: Build image with Buildah + - name: Build image with Docker shell: bash run: | echo "Building ${{ inputs.image_name }}:${{ inputs.version }}" - buildah bud \ + docker build \ --tag ${{ inputs.registry }}/${{ inputs.image_name }}:${{ inputs.version }} \ --tag ${{ inputs.registry }}/${{ inputs.image_name }}:${{ github.sha }} \ --file ${{ inputs.context }}/${{ inputs.dockerfile }} \ @@ -40,15 +40,15 @@ runs: shell: bash run: | echo "Pushing ${{ inputs.image_name }}:${{ inputs.version }}" - buildah push ${{ inputs.registry }}/${{ inputs.image_name }}:${{ inputs.version }} - buildah push ${{ inputs.registry }}/${{ inputs.image_name }}:${{ github.sha }} + docker push ${{ inputs.registry }}/${{ inputs.image_name }}:${{ inputs.version }} + docker push ${{ inputs.registry }}/${{ inputs.image_name }}:${{ github.sha }} - name: Summary shell: bash run: | - echo "✅ Built and pushed:" + echo "Built and pushed:" echo " ${{ inputs.registry }}/${{ inputs.image_name }}:${{ inputs.version }}" echo " ${{ inputs.registry }}/${{ inputs.image_name }}:${{ github.sha }}" echo "" echo "Registry tags:" - curl -sf "https://${{ inputs.registry }}/v2/${{ inputs.image_name }}/tags/list" | jq -r '.tags[]' | sort -V | tail -10 + curl -sf "https://${{ inputs.registry }}/v2/${{ inputs.image_name }}/tags/list" | jq -r '.tags[]' | sort -V | tail -10 || true diff --git a/.forgejo/workflows/build-devpi.yaml b/.forgejo/workflows/build-devpi.yaml index 89318b6..5329b77 100644 --- a/.forgejo/workflows/build-devpi.yaml +++ b/.forgejo/workflows/build-devpi.yaml @@ -12,7 +12,7 @@ on: jobs: build: - runs-on: ubuntu-latest + runs-on: docker-builder steps: - name: Checkout uses: actions/checkout@v4 diff --git a/.forgejo/workflows/build-runner.yaml b/.forgejo/workflows/build-runner.yaml index 54162b6..44be98f 100644 --- a/.forgejo/workflows/build-runner.yaml +++ b/.forgejo/workflows/build-runner.yaml @@ -12,7 +12,7 @@ on: jobs: build: - runs-on: ubuntu-latest + runs-on: docker-builder steps: - name: Checkout uses: actions/checkout@v4 diff --git a/.github/actionlint.yaml b/.github/actionlint.yaml new file mode 100644 index 0000000..12f3259 --- /dev/null +++ b/.github/actionlint.yaml @@ -0,0 +1,5 @@ +self-hosted-runner: + labels: + - docker-builder + - ubuntu-latest + - ubuntu-22.04 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 421de65..d673cc3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -86,4 +86,5 @@ repos: rev: v1.7.10 hooks: - id: actionlint-system + args: ['-config-file', '.github/actionlint.yaml'] files: ^\.forgejo/workflows/ -- 2.50.1 (Apple Git-155) From 6b4e0961ed1c0fd69b3ee91828edc32e2b71e22b Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Sat, 24 Jan 2026 08:52:14 -0800 Subject: [PATCH 17/30] Add README explaining .github vs .forgejo directories Co-Authored-By: Claude Opus 4.5 --- .github/README.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .github/README.md diff --git a/.github/README.md b/.github/README.md new file mode 100644 index 0000000..bea6b15 --- /dev/null +++ b/.github/README.md @@ -0,0 +1,9 @@ +# .github directory + +This directory contains configuration for GitHub-ecosystem tooling only. + +**Workflows and actions belong in `.forgejo/`** - this repository uses Forgejo Actions, not GitHub Actions. + +## Contents + +- `actionlint.yaml` - Configuration for actionlint pre-commit hook (custom runner labels) -- 2.50.1 (Apple Git-155) From f4178fce7d0db9d6c2c15c4ef18651ce350da7c3 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Sat, 24 Jan 2026 08:59:13 -0800 Subject: [PATCH 18/30] Add ubuntu-latest labels to indri runner Now handles all workflows (test and build) Co-Authored-By: Claude Opus 4.5 --- ansible/roles/forgejo_runner/defaults/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ansible/roles/forgejo_runner/defaults/main.yml b/ansible/roles/forgejo_runner/defaults/main.yml index 643f3a2..c5dc8e6 100644 --- a/ansible/roles/forgejo_runner/defaults/main.yml +++ b/ansible/roles/forgejo_runner/defaults/main.yml @@ -8,7 +8,7 @@ forgejo_runner_log_dir: /Users/erichblume/Library/Logs # Runner registration forgejo_runner_instance_url: "http://localhost:3001" forgejo_runner_name: "indri-docker-runner" -forgejo_runner_labels: "docker-builder:docker" +forgejo_runner_labels: "docker-builder:docker,ubuntu-latest:docker,ubuntu-22.04:docker" # Runner config forgejo_runner_capacity: 2 -- 2.50.1 (Apple Git-155) From bcdee225e507ab15f0a8fc3b6fd9e0141643014f Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Sat, 24 Jan 2026 09:23:24 -0800 Subject: [PATCH 19/30] Replace k8s runner with ci-base image for local builds - Remove forgejo-runner k8s manifests and ArgoCD app (runner now on indri) - Remove build-runner workflow (no longer needed) - Add ci-base image with Ubuntu 22.04 + common CI tools - Add build-ci-base workflow to build the image - Update test workflow to check docker instead of buildah - Document bootstrap vs production mode for runner labels - Configure host.docker.internal:5050 for zot access from job containers Co-Authored-By: Claude Opus 4.5 --- .../{build-runner.yaml => build-ci-base.yaml} | 12 ++-- .forgejo/workflows/test.yaml | 17 +++-- .../roles/forgejo_runner/defaults/main.yml | 14 +++- argocd/apps/forgejo-runner.yaml | 23 ------- argocd/manifests/ci-base/Dockerfile | 58 ++++++++++++++++ argocd/manifests/forgejo-runner/Dockerfile | 67 ------------------- .../manifests/forgejo-runner/configmap.yaml | 13 ---- .../manifests/forgejo-runner/deployment.yaml | 63 ----------------- .../forgejo-runner/kustomization.yaml | 8 --- .../manifests/forgejo-runner/namespace.yaml | 4 -- .../forgejo-runner/secret-token.yaml.tpl | 10 --- .../forgejo-runner/serviceaccount.yaml | 5 -- 12 files changed, 85 insertions(+), 209 deletions(-) rename .forgejo/workflows/{build-runner.yaml => build-ci-base.yaml} (73%) delete mode 100644 argocd/apps/forgejo-runner.yaml create mode 100644 argocd/manifests/ci-base/Dockerfile delete mode 100644 argocd/manifests/forgejo-runner/Dockerfile delete mode 100644 argocd/manifests/forgejo-runner/configmap.yaml delete mode 100644 argocd/manifests/forgejo-runner/deployment.yaml delete mode 100644 argocd/manifests/forgejo-runner/kustomization.yaml delete mode 100644 argocd/manifests/forgejo-runner/namespace.yaml delete mode 100644 argocd/manifests/forgejo-runner/secret-token.yaml.tpl delete mode 100644 argocd/manifests/forgejo-runner/serviceaccount.yaml diff --git a/.forgejo/workflows/build-runner.yaml b/.forgejo/workflows/build-ci-base.yaml similarity index 73% rename from .forgejo/workflows/build-runner.yaml rename to .forgejo/workflows/build-ci-base.yaml index 44be98f..7a0d5f8 100644 --- a/.forgejo/workflows/build-runner.yaml +++ b/.forgejo/workflows/build-ci-base.yaml @@ -1,9 +1,9 @@ -name: Build forgejo-runner +name: Build CI base image on: push: tags: - - 'runner-v*' + - 'ci-base-v*' workflow_dispatch: inputs: version: @@ -23,8 +23,8 @@ jobs: if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then VERSION="${{ github.event.inputs.version }}" else - # Extract version from tag: runner-v1.0.0 -> v1.0.0 - VERSION="${GITHUB_REF_NAME#runner-}" + # Extract version from tag: ci-base-v1.0.0 -> v1.0.0 + VERSION="${GITHUB_REF_NAME#ci-base-}" fi echo "version=$VERSION" >> "$GITHUB_OUTPUT" echo "Building version: $VERSION" @@ -32,6 +32,6 @@ jobs: - name: Build and push uses: ./.forgejo/actions/build-push-image with: - context: argocd/manifests/forgejo-runner - image_name: blumeops/forgejo-runner + context: argocd/manifests/ci-base + image_name: blumeops/ci-base version: ${{ steps.version.outputs.version }} diff --git a/.forgejo/workflows/test.yaml b/.forgejo/workflows/test.yaml index 1db41ee..b293ab7 100644 --- a/.forgejo/workflows/test.yaml +++ b/.forgejo/workflows/test.yaml @@ -16,23 +16,22 @@ jobs: - name: Verify tools run: | echo "=== Node.js ===" - node --version - npm --version + node --version || echo "Node.js not available" + npm --version || echo "npm not available" echo "" echo "=== Git ===" git --version echo "" echo "=== Build tools ===" - make --version 2>&1 | head -1 || true - gcc --version 2>&1 | head -1 || true + make --version 2>&1 | head -1 || echo "make not available" + gcc --version 2>&1 | head -1 || echo "gcc not available" echo "" - echo "=== Container tools (Buildah) ===" - buildah --version - podman --version + echo "=== Container tools (Docker) ===" + docker --version || echo "Docker CLI not available" echo "" echo "=== Other tools ===" - curl --version 2>&1 | head -1 || true - jq --version + curl --version 2>&1 | head -1 || echo "curl not available" + jq --version || echo "jq not available" - name: Show repo info run: | diff --git a/ansible/roles/forgejo_runner/defaults/main.yml b/ansible/roles/forgejo_runner/defaults/main.yml index c5dc8e6..e677240 100644 --- a/ansible/roles/forgejo_runner/defaults/main.yml +++ b/ansible/roles/forgejo_runner/defaults/main.yml @@ -8,7 +8,19 @@ forgejo_runner_log_dir: /Users/erichblume/Library/Logs # Runner registration forgejo_runner_instance_url: "http://localhost:3001" forgejo_runner_name: "indri-docker-runner" -forgejo_runner_labels: "docker-builder:docker,ubuntu-latest:docker,ubuntu-22.04:docker" +# Labels format: label:docker://image +# +# Bootstrap mode (use upstream images to build our own): +# docker-builder:docker://docker:27-cli +# ubuntu-latest:docker://catthehacker/ubuntu:act-22.04 +# +# Production mode (use our own images from zot via host.docker.internal): +# docker-builder:docker://host.docker.internal:5050/blumeops/ci-base:latest +# ubuntu-latest:docker://host.docker.internal:5050/blumeops/ci-base:latest +# +# Note: Docker daemon.json must include host.docker.internal:5050 in insecure-registries +# Currently using bootstrap mode until ci-base is built +forgejo_runner_labels: "docker-builder:docker://docker:27-cli,ubuntu-latest:docker://catthehacker/ubuntu:act-22.04,ubuntu-22.04:docker://catthehacker/ubuntu:act-22.04" # Runner config forgejo_runner_capacity: 2 diff --git a/argocd/apps/forgejo-runner.yaml b/argocd/apps/forgejo-runner.yaml deleted file mode 100644 index a584d33..0000000 --- a/argocd/apps/forgejo-runner.yaml +++ /dev/null @@ -1,23 +0,0 @@ -# Forgejo Actions Runner -# Runs in k8s, polls Forgejo for workflow jobs -# -# Before syncing, create the runner token secret: -# kubectl create namespace forgejo-runner -# op inject -i argocd/manifests/forgejo-runner/secret-token.yaml.tpl | kubectl apply -f - -apiVersion: argoproj.io/v1alpha1 -kind: Application -metadata: - name: forgejo-runner - namespace: argocd -spec: - project: default - source: - repoURL: ssh://forgejo@indri.tail8d86e.ts.net:2200/eblume/blumeops.git - targetRevision: main - path: argocd/manifests/forgejo-runner - destination: - server: https://kubernetes.default.svc - namespace: forgejo-runner - syncPolicy: - syncOptions: - - CreateNamespace=true diff --git a/argocd/manifests/ci-base/Dockerfile b/argocd/manifests/ci-base/Dockerfile new file mode 100644 index 0000000..974ceb2 --- /dev/null +++ b/argocd/manifests/ci-base/Dockerfile @@ -0,0 +1,58 @@ +# CI base image for Forgejo Actions +# Used by forgejo-runner for ubuntu-latest and ubuntu-22.04 labels +FROM ubuntu:22.04 + +# Prevent interactive prompts during package installation +ENV DEBIAN_FRONTEND=noninteractive + +# Install common CI tools +RUN apt-get update && apt-get install -y --no-install-recommends \ + # Essential tools + ca-certificates \ + curl \ + wget \ + git \ + jq \ + unzip \ + zip \ + # Build tools + build-essential \ + make \ + # Python + python3 \ + python3-pip \ + python3-venv \ + # Node.js (via nodesource for LTS) + && curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \ + && apt-get install -y nodejs \ + # Docker CLI (not the daemon - we mount the socket) + && install -m 0755 -d /etc/apt/keyrings \ + && curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc \ + && chmod a+r /etc/apt/keyrings/docker.asc \ + && echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" > /etc/apt/sources.list.d/docker.list \ + && apt-get update \ + && apt-get install -y docker-ce-cli \ + # Clean up + && apt-get clean \ + && rm -rf /var/lib/apt/lists/* + +# Create a non-root user for running jobs +RUN useradd -m -s /bin/bash runner \ + && mkdir -p /home/runner/work \ + && chown -R runner:runner /home/runner + +# Set working directory +WORKDIR /home/runner/work + +# Default to runner user +USER runner + +# Verify installations +RUN echo "=== CI Base Image ===" \ + && git --version \ + && node --version \ + && npm --version \ + && python3 --version \ + && docker --version \ + && make --version | head -1 \ + && gcc --version | head -1 diff --git a/argocd/manifests/forgejo-runner/Dockerfile b/argocd/manifests/forgejo-runner/Dockerfile deleted file mode 100644 index 862f531..0000000 --- a/argocd/manifests/forgejo-runner/Dockerfile +++ /dev/null @@ -1,67 +0,0 @@ -# Build forgejo-runner from source -# Source: https://forge.tail8d86e.ts.net/eblume/forgejo-runner (mirror of code.forgejo.org/forgejo/runner) - -FROM golang:1.24-alpine AS builder - -ARG FORGEJO_RUNNER_VERSION=v3.5.1 - -RUN apk add --no-cache git make build-base - -WORKDIR /src -RUN git clone --depth 1 --branch ${FORGEJO_RUNNER_VERSION} \ - https://forge.tail8d86e.ts.net/eblume/forgejo-runner.git . - -RUN make clean && make build - -# Runtime image -FROM alpine:3.21 - -# Create runner user with proper passwd entry (required by buildah) -# Also configure subuid/subgid for rootless container builds -RUN addgroup -g 1000 runner && \ - adduser -D -u 1000 -G runner -h /data runner && \ - echo "runner:100000:65536" >> /etc/subuid && \ - echo "runner:100000:65536" >> /etc/subgid - -# Install runtime dependencies -RUN apk add --no-cache \ - # Required for actions/checkout and other Node-based actions - nodejs \ - npm \ - # Core tools - git \ - bash \ - curl \ - wget \ - jq \ - # Build essentials - make \ - gcc \ - g++ \ - musl-dev \ - # For container builds (daemonless, no Docker socket needed) - buildah \ - podman \ - fuse-overlayfs \ - ca-certificates - -# Copy runner binary from builder -COPY --from=builder /src/forgejo-runner /bin/forgejo-runner - -# Configure buildah for rootless operation -RUN mkdir -p /etc/containers && \ - printf '[storage]\ndriver = "overlay"\nrunroot = "/tmp/containers-run"\ngraphroot = "/tmp/containers-storage"\n[storage.options.overlay]\nmount_program = "/usr/bin/fuse-overlayfs"\n' \ - > /etc/containers/storage.conf - -# Configure registries (allow insecure for local registry) -RUN printf 'unqualified-search-registries = ["docker.io"]\n[[registry]]\nlocation = "registry.tail8d86e.ts.net"\ninsecure = true\n' \ - > /etc/containers/registries.conf - -# Verify tools are available -RUN node --version && npm --version && buildah --version && /bin/forgejo-runner --version - -ENV HOME=/data -WORKDIR /data -USER runner - -CMD ["/bin/forgejo-runner"] diff --git a/argocd/manifests/forgejo-runner/configmap.yaml b/argocd/manifests/forgejo-runner/configmap.yaml deleted file mode 100644 index 584efe0..0000000 --- a/argocd/manifests/forgejo-runner/configmap.yaml +++ /dev/null @@ -1,13 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: forgejo-runner-config - namespace: forgejo-runner -data: - config.yaml: | - log: - level: info - runner: - file: /data/.runner - capacity: 1 - timeout: 3h diff --git a/argocd/manifests/forgejo-runner/deployment.yaml b/argocd/manifests/forgejo-runner/deployment.yaml deleted file mode 100644 index 79c70d3..0000000 --- a/argocd/manifests/forgejo-runner/deployment.yaml +++ /dev/null @@ -1,63 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: forgejo-runner - namespace: forgejo-runner -spec: - replicas: 1 - selector: - matchLabels: - app: forgejo-runner - template: - metadata: - labels: - app: forgejo-runner - spec: - serviceAccountName: forgejo-runner - containers: - - name: runner - image: registry.tail8d86e.ts.net/blumeops/forgejo-runner:v1.0.3 - env: - # Use internal k8s service via Tailscale operator egress - - name: FORGEJO_INSTANCE_URL - value: "http://forge.tailscale.svc.cluster.local:3001" - - name: RUNNER_NAME - value: "k8s-runner-1" - - name: RUNNER_TOKEN - valueFrom: - secretKeyRef: - name: forgejo-runner-token - key: token - command: - - /bin/sh - - -c - - | - # Register runner if not already registered - if [ ! -f /data/.runner ]; then - forgejo-runner register \ - --instance "$FORGEJO_INSTANCE_URL" \ - --token "$RUNNER_TOKEN" \ - --name "$RUNNER_NAME" \ - --labels "ubuntu-latest:host,ubuntu-22.04:host" \ - --no-interactive - fi - # Start the runner daemon with config - forgejo-runner daemon --config /config/config.yaml - volumeMounts: - - name: runner-data - mountPath: /data - - name: runner-config - mountPath: /config - resources: - requests: - memory: "256Mi" - cpu: "100m" - limits: - memory: "1Gi" - cpu: "1000m" - volumes: - - name: runner-data - emptyDir: {} - - name: runner-config - configMap: - name: forgejo-runner-config diff --git a/argocd/manifests/forgejo-runner/kustomization.yaml b/argocd/manifests/forgejo-runner/kustomization.yaml deleted file mode 100644 index 332c49c..0000000 --- a/argocd/manifests/forgejo-runner/kustomization.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization -namespace: forgejo-runner -resources: - - namespace.yaml - - serviceaccount.yaml - - configmap.yaml - - deployment.yaml diff --git a/argocd/manifests/forgejo-runner/namespace.yaml b/argocd/manifests/forgejo-runner/namespace.yaml deleted file mode 100644 index 19441b1..0000000 --- a/argocd/manifests/forgejo-runner/namespace.yaml +++ /dev/null @@ -1,4 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: forgejo-runner diff --git a/argocd/manifests/forgejo-runner/secret-token.yaml.tpl b/argocd/manifests/forgejo-runner/secret-token.yaml.tpl deleted file mode 100644 index 427d8df..0000000 --- a/argocd/manifests/forgejo-runner/secret-token.yaml.tpl +++ /dev/null @@ -1,10 +0,0 @@ -# Template for op inject -# Usage: op inject -i secret-token.yaml.tpl | kubectl apply -f - -apiVersion: v1 -kind: Secret -metadata: - name: forgejo-runner-token - namespace: forgejo-runner -type: Opaque -stringData: - token: "op://blumeops/w3663ffnvkewbftncqxtcpeavy/runner_reg" diff --git a/argocd/manifests/forgejo-runner/serviceaccount.yaml b/argocd/manifests/forgejo-runner/serviceaccount.yaml deleted file mode 100644 index ef8cb25..0000000 --- a/argocd/manifests/forgejo-runner/serviceaccount.yaml +++ /dev/null @@ -1,5 +0,0 @@ -apiVersion: v1 -kind: ServiceAccount -metadata: - name: forgejo-runner - namespace: forgejo-runner -- 2.50.1 (Apple Git-155) From 35136e361eeed4d97aa316b858fd39b7693b8557 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Sat, 24 Jan 2026 10:23:37 -0800 Subject: [PATCH 20/30] Add comment to test workflow to trigger CI run Co-Authored-By: Claude Opus 4.5 --- .forgejo/workflows/test.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/.forgejo/workflows/test.yaml b/.forgejo/workflows/test.yaml index b293ab7..dfcc45e 100644 --- a/.forgejo/workflows/test.yaml +++ b/.forgejo/workflows/test.yaml @@ -1,3 +1,4 @@ +# Workflow to verify CI environment name: Test CI on: -- 2.50.1 (Apple Git-155) From 50b925791d5f971f1a1b22906716a72003805352 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Sat, 24 Jan 2026 10:28:12 -0800 Subject: [PATCH 21/30] Update test workflow comment to trigger CI Co-Authored-By: Claude Opus 4.5 --- .forgejo/workflows/test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.forgejo/workflows/test.yaml b/.forgejo/workflows/test.yaml index dfcc45e..6aa3236 100644 --- a/.forgejo/workflows/test.yaml +++ b/.forgejo/workflows/test.yaml @@ -1,4 +1,4 @@ -# Workflow to verify CI environment +# Workflow to verify CI environment and available tools name: Test CI on: -- 2.50.1 (Apple Git-155) From 15e3ec98eae585c27be9ee3fdfed1bc047204a1f Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Sat, 24 Jan 2026 10:30:28 -0800 Subject: [PATCH 22/30] Use host networking for job containers Containers need to reach localhost:3001 (Forgejo) for git operations. Co-Authored-By: Claude Opus 4.5 --- ansible/roles/forgejo_runner/defaults/main.yml | 2 +- ansible/roles/forgejo_runner/templates/config.yaml.j2 | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/ansible/roles/forgejo_runner/defaults/main.yml b/ansible/roles/forgejo_runner/defaults/main.yml index e677240..7d18548 100644 --- a/ansible/roles/forgejo_runner/defaults/main.yml +++ b/ansible/roles/forgejo_runner/defaults/main.yml @@ -27,5 +27,5 @@ forgejo_runner_capacity: 2 forgejo_runner_timeout: 3h # Docker container settings for jobs -forgejo_runner_docker_network: bridge +# Note: network is hardcoded to "host" so containers can reach localhost:3001 (Forgejo) forgejo_runner_privileged: true # Needed for container builds diff --git a/ansible/roles/forgejo_runner/templates/config.yaml.j2 b/ansible/roles/forgejo_runner/templates/config.yaml.j2 index 7de5cc0..d02e664 100644 --- a/ansible/roles/forgejo_runner/templates/config.yaml.j2 +++ b/ansible/roles/forgejo_runner/templates/config.yaml.j2 @@ -8,7 +8,8 @@ runner: timeout: {{ forgejo_runner_timeout }} container: - network: "{{ forgejo_runner_docker_network }}" + # Use host network so containers can reach localhost:3001 (Forgejo) + network: "host" privileged: {{ forgejo_runner_privileged | lower }} # Mount Docker socket so jobs can build containers valid_volumes: -- 2.50.1 (Apple Git-155) From 476b80e98583cfda2497fd9edc82b6fb24a9d400 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Sat, 24 Jan 2026 10:40:35 -0800 Subject: [PATCH 23/30] Use --add-host to map localhost to Docker host in job containers This allows containers to reach Forgejo at localhost:3001 for git operations. Co-Authored-By: Claude Opus 4.5 --- ansible/roles/forgejo_runner/templates/config.yaml.j2 | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ansible/roles/forgejo_runner/templates/config.yaml.j2 b/ansible/roles/forgejo_runner/templates/config.yaml.j2 index d02e664..c7aa4e6 100644 --- a/ansible/roles/forgejo_runner/templates/config.yaml.j2 +++ b/ansible/roles/forgejo_runner/templates/config.yaml.j2 @@ -8,9 +8,11 @@ runner: timeout: {{ forgejo_runner_timeout }} container: - # Use host network so containers can reach localhost:3001 (Forgejo) - network: "host" + network: "bridge" privileged: {{ forgejo_runner_privileged | lower }} + # Map localhost to Docker host so containers can reach Forgejo at localhost:3001 + # host-gateway is a special Docker value that resolves to the host IP + options: "--add-host localhost:host-gateway" # Mount Docker socket so jobs can build containers valid_volumes: - /var/run/docker.sock -- 2.50.1 (Apple Git-155) From 018b44186f44f03ed1dd73bd0a41e7c2b61f84c4 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Sat, 24 Jan 2026 11:03:02 -0800 Subject: [PATCH 24/30] Add tag:ci-gateway for Forgejo runner Tailscale sidecar - Add ci-gateway tag owner (admin and blumeops can assign) - Grant ci-gateway access to forge:443 for git operations - Grant ci-gateway access to registry:443 for container push/pull - Add ACL test for ci-gateway access Co-Authored-By: Claude Opus 4.5 --- pulumi/policy.hujson | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/pulumi/policy.hujson b/pulumi/policy.hujson index 7f18820..789bd96 100644 --- a/pulumi/policy.hujson +++ b/pulumi/policy.hujson @@ -74,6 +74,20 @@ "dst": ["tag:homelab"], "ip": ["tcp:3001", "tcp:2200"], }, + + // --- CI Gateway (Tailscale sidecar for Forgejo runner jobs) --- + // Can reach Forge for git clone operations + { + "src": ["tag:ci-gateway"], + "dst": ["tag:forge"], + "ip": ["tcp:443"], + }, + // Can reach registry to push/pull container images + { + "src": ["tag:ci-gateway"], + "dst": ["tag:registry"], + "ip": ["tcp:443"], + }, // Homelab can reach k8s services: PostgreSQL, CNPG metrics, Prometheus/Loki { "src": ["tag:homelab"], @@ -126,6 +140,7 @@ "tag:k8s-api": ["autogroup:admin", "tag:blumeops"], "tag:k8s-operator": ["autogroup:admin", "tag:blumeops"], "tag:k8s": ["autogroup:admin", "tag:blumeops", "tag:k8s-operator"], + "tag:ci-gateway": ["autogroup:admin", "tag:blumeops"], }, // ============== ACL Tests ============== @@ -151,5 +166,10 @@ "src": "tag:k8s", "accept": ["tag:registry:443", "tag:homelab:3001", "tag:homelab:2200"], }, + // CI gateway can reach forge and registry + { + "src": "tag:ci-gateway", + "accept": ["tag:forge:443", "tag:registry:443"], + }, ], } -- 2.50.1 (Apple Git-155) From fdf51531307a3c5b6cc6b7b09c835b7d5d733267 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Sat, 24 Jan 2026 11:28:35 -0800 Subject: [PATCH 25/30] Containerize forgejo-runner with Tailscale gateway for tailnet access Architecture: - tailscale_ci_gateway role: Runs Tailscale container on tailnet-jobs network - forgejo_runner role: Runs runner daemon in container on same network - Job containers also use tailnet-jobs network This allows the runner and jobs to reach forge.tail8d86e.ts.net via the Tailscale gateway, avoiding hairpinning issues with localhost. Changes: - Add tailscale_ci_gateway role with launchd management - Refactor forgejo_runner to use containerized daemon - Runner registers with Tailscale URL instead of localhost - Job containers run on tailnet-jobs network - Update playbook role ordering (gateway before runner) Co-Authored-By: Claude Opus 4.5 --- ansible/playbooks/indri.yml | 19 ++++++ .../roles/forgejo_runner/defaults/main.yml | 33 ++++++---- .../roles/forgejo_runner/handlers/main.yml | 4 +- ansible/roles/forgejo_runner/tasks/main.yml | 61 ++++++++----------- .../forgejo_runner/templates/config.yaml.j2 | 9 ++- .../templates/forgejo-runner.plist.j2 | 28 ++++++--- .../tailscale_ci_gateway/defaults/main.yml | 9 +++ .../tailscale_ci_gateway/handlers/main.yml | 7 +++ .../roles/tailscale_ci_gateway/tasks/main.yml | 46 ++++++++++++++ .../templates/tailscale-ci-gateway.plist.j2 | 45 ++++++++++++++ 10 files changed, 199 insertions(+), 62 deletions(-) create mode 100644 ansible/roles/tailscale_ci_gateway/defaults/main.yml create mode 100644 ansible/roles/tailscale_ci_gateway/handlers/main.yml create mode 100644 ansible/roles/tailscale_ci_gateway/tasks/main.yml create mode 100644 ansible/roles/tailscale_ci_gateway/templates/tailscale-ci-gateway.plist.j2 diff --git a/ansible/playbooks/indri.yml b/ansible/playbooks/indri.yml index b12e905..09779ec 100644 --- a/ansible/playbooks/indri.yml +++ b/ansible/playbooks/indri.yml @@ -78,6 +78,23 @@ no_log: true tags: [forgejo_runner] + # Tailscale CI gateway auth key (for job container tailnet access) + - name: Fetch tailscale ci-gateway auth key + ansible.builtin.command: + cmd: op --vault vg6xf6vvfmoh5hqjjhlhbeoaie item get w3663ffnvkewbftncqxtcpeavy --fields ci-gateway-ts-auth-key --reveal + delegate_to: localhost + register: _tailscale_ci_gateway_auth_key + changed_when: false + no_log: true + check_mode: false + tags: [tailscale_ci_gateway] + + - name: Set tailscale ci-gateway auth key fact + ansible.builtin.set_fact: + tailscale_ci_gateway_auth_key: "{{ _tailscale_ci_gateway_auth_key.stdout }}" + no_log: true + tags: [tailscale_ci_gateway] + roles: - role: alloy tags: alloy @@ -99,5 +116,7 @@ tags: plex_metrics - role: tailscale_serve tags: tailscale-serve + - role: tailscale_ci_gateway + tags: tailscale_ci_gateway - role: forgejo_runner tags: forgejo_runner diff --git a/ansible/roles/forgejo_runner/defaults/main.yml b/ansible/roles/forgejo_runner/defaults/main.yml index 7d18548..6c87755 100644 --- a/ansible/roles/forgejo_runner/defaults/main.yml +++ b/ansible/roles/forgejo_runner/defaults/main.yml @@ -1,25 +1,37 @@ --- -forgejo_runner_repo_dir: /Users/erichblume/code/3rd/forgejo-runner -forgejo_runner_binary: "{{ forgejo_runner_repo_dir }}/forgejo-runner" +# Forgejo Runner - containerized daemon on tailnet-jobs network +# +# The runner daemon runs in a Docker container with access to the tailnet +# via the tailscale-ci-gateway. This allows it to register with Forgejo +# using the Tailscale URL, so job containers can also reach Forgejo. + forgejo_runner_data_dir: /Users/erichblume/.forgejo-runner forgejo_runner_config_dir: /Users/erichblume/.config/forgejo-runner forgejo_runner_log_dir: /Users/erichblume/Library/Logs -# Runner registration -forgejo_runner_instance_url: "http://localhost:3001" +# Container settings +forgejo_runner_container_name: forgejo-runner +forgejo_runner_image: code.forgejo.org/forgejo/runner:6.2.1 +forgejo_runner_network: tailnet-jobs + +# Runner registration - use Tailscale URL since we're on tailnet-jobs network +forgejo_runner_instance_url: "https://forge.tail8d86e.ts.net" forgejo_runner_name: "indri-docker-runner" + # Labels format: label:docker://image # -# Bootstrap mode (use upstream images to build our own): +# Job containers also run on tailnet-jobs network and can reach: +# - forge.tail8d86e.ts.net for git clone +# - registry.tail8d86e.ts.net for container push/pull +# +# Bootstrap mode (use upstream images until we build ci-base): # docker-builder:docker://docker:27-cli # ubuntu-latest:docker://catthehacker/ubuntu:act-22.04 # -# Production mode (use our own images from zot via host.docker.internal): -# docker-builder:docker://host.docker.internal:5050/blumeops/ci-base:latest -# ubuntu-latest:docker://host.docker.internal:5050/blumeops/ci-base:latest +# Production mode (use our own images from zot): +# docker-builder:docker://registry.tail8d86e.ts.net/blumeops/ci-base:latest +# ubuntu-latest:docker://registry.tail8d86e.ts.net/blumeops/ci-base:latest # -# Note: Docker daemon.json must include host.docker.internal:5050 in insecure-registries -# Currently using bootstrap mode until ci-base is built forgejo_runner_labels: "docker-builder:docker://docker:27-cli,ubuntu-latest:docker://catthehacker/ubuntu:act-22.04,ubuntu-22.04:docker://catthehacker/ubuntu:act-22.04" # Runner config @@ -27,5 +39,4 @@ forgejo_runner_capacity: 2 forgejo_runner_timeout: 3h # Docker container settings for jobs -# Note: network is hardcoded to "host" so containers can reach localhost:3001 (Forgejo) forgejo_runner_privileged: true # Needed for container builds diff --git a/ansible/roles/forgejo_runner/handlers/main.yml b/ansible/roles/forgejo_runner/handlers/main.yml index a1798f4..8ace37c 100644 --- a/ansible/roles/forgejo_runner/handlers/main.yml +++ b/ansible/roles/forgejo_runner/handlers/main.yml @@ -2,6 +2,6 @@ - name: Restart forgejo-runner listen: Restart forgejo-runner ansible.builtin.shell: | - launchctl unload ~/Library/LaunchAgents/mcquack.eblume.forgejo-runner.plist 2>/dev/null || true - launchctl load ~/Library/LaunchAgents/mcquack.eblume.forgejo-runner.plist + launchctl unload ~/Library/LaunchAgents/mcquack.forgejo-runner.plist 2>/dev/null || true + launchctl load ~/Library/LaunchAgents/mcquack.forgejo-runner.plist changed_when: true diff --git a/ansible/roles/forgejo_runner/tasks/main.yml b/ansible/roles/forgejo_runner/tasks/main.yml index d7106c1..72e114b 100644 --- a/ansible/roles/forgejo_runner/tasks/main.yml +++ b/ansible/roles/forgejo_runner/tasks/main.yml @@ -1,34 +1,12 @@ --- -# Forgejo Runner on indri +# Forgejo Runner - containerized daemon on tailnet-jobs network # -# Uses Docker container mode for job isolation. -# Can build containers using Docker (via socket). +# The runner daemon runs in a Docker container with access to the tailnet +# via the tailscale-ci-gateway. Job containers also run on tailnet-jobs +# and can reach Forgejo via Tailscale. # -# ONE-TIME SETUP (before running ansible): -# -# 1. Clone forgejo-runner from forge mirror: -# ssh indri 'git clone https://forge.tail8d86e.ts.net/eblume/forgejo-runner.git ~/code/3rd/forgejo-runner' -# -# 2. Set up Go via mise: -# ssh indri 'cd ~/code/3rd/forgejo-runner && mise use go@1.24' -# -# 3. Build: -# ssh indri 'cd ~/code/3rd/forgejo-runner && mise x -- make build' -# -# 4. Run ansible to deploy config and LaunchAgent - -- name: Verify forgejo-runner binary exists - ansible.builtin.stat: - path: "{{ forgejo_runner_binary }}" - register: forgejo_runner_binary_stat - -- name: Fail if forgejo-runner binary not found - ansible.builtin.fail: - msg: | - Forgejo-runner binary not found at {{ forgejo_runner_binary }}. - Please build from source first: - ssh indri 'cd ~/code/3rd/forgejo-runner && mise x -- make build' - when: not forgejo_runner_binary_stat.stat.exists +# DEPENDENCIES: +# - tailscale_ci_gateway role must run first (creates tailnet-jobs network) - name: Ensure forgejo-runner directories exist ansible.builtin.file: @@ -46,38 +24,47 @@ mode: '0644' notify: Restart forgejo-runner +- name: Pull forgejo-runner image + ansible.builtin.command: + cmd: docker pull {{ forgejo_runner_image }} + register: forgejo_runner_pull + changed_when: "'Downloaded newer image' in forgejo_runner_pull.stdout or 'Pull complete' in forgejo_runner_pull.stdout" + - name: Check if runner is registered ansible.builtin.stat: path: "{{ forgejo_runner_data_dir }}/.runner" register: forgejo_runner_registered -- name: Register runner with Forgejo +- name: Register runner with Forgejo (via tailnet) ansible.builtin.command: cmd: > - {{ forgejo_runner_binary }} register + docker run --rm + --network {{ forgejo_runner_network }} + -v {{ forgejo_runner_data_dir }}:/data + {{ forgejo_runner_image }} + forgejo-runner register --instance "{{ forgejo_runner_instance_url }}" --token "{{ forgejo_runner_token }}" --name "{{ forgejo_runner_name }}" --labels "{{ forgejo_runner_labels }}" --no-interactive - chdir: "{{ forgejo_runner_data_dir }}" when: not forgejo_runner_registered.stat.exists changed_when: true -- name: Deploy forgejo-runner LaunchAgent plist +- name: Deploy forgejo-runner launchd plist ansible.builtin.template: src: forgejo-runner.plist.j2 - dest: ~/Library/LaunchAgents/mcquack.eblume.forgejo-runner.plist + dest: ~/Library/LaunchAgents/mcquack.forgejo-runner.plist mode: '0644' notify: Restart forgejo-runner -- name: Check if forgejo-runner LaunchAgent is loaded - ansible.builtin.command: launchctl list mcquack.eblume.forgejo-runner +- name: Check if forgejo-runner is loaded + ansible.builtin.command: launchctl list mcquack.forgejo-runner register: forgejo_runner_launchctl_check changed_when: false failed_when: false -- name: Load forgejo-runner LaunchAgent if not loaded - ansible.builtin.command: launchctl load ~/Library/LaunchAgents/mcquack.eblume.forgejo-runner.plist +- name: Load forgejo-runner if not loaded + ansible.builtin.command: launchctl load ~/Library/LaunchAgents/mcquack.forgejo-runner.plist when: forgejo_runner_launchctl_check.rc != 0 changed_when: true diff --git a/ansible/roles/forgejo_runner/templates/config.yaml.j2 b/ansible/roles/forgejo_runner/templates/config.yaml.j2 index c7aa4e6..397fbbd 100644 --- a/ansible/roles/forgejo_runner/templates/config.yaml.j2 +++ b/ansible/roles/forgejo_runner/templates/config.yaml.j2 @@ -3,16 +3,15 @@ log: level: info runner: - file: {{ forgejo_runner_data_dir }}/.runner + # Path inside the container (data dir mounted at /data) + file: /data/.runner capacity: {{ forgejo_runner_capacity }} timeout: {{ forgejo_runner_timeout }} container: - network: "bridge" + # Use tailnet-jobs network so job containers can reach Forgejo via Tailscale gateway + network: "{{ forgejo_runner_network }}" privileged: {{ forgejo_runner_privileged | lower }} - # Map localhost to Docker host so containers can reach Forgejo at localhost:3001 - # host-gateway is a special Docker value that resolves to the host IP - options: "--add-host localhost:host-gateway" # Mount Docker socket so jobs can build containers valid_volumes: - /var/run/docker.sock diff --git a/ansible/roles/forgejo_runner/templates/forgejo-runner.plist.j2 b/ansible/roles/forgejo_runner/templates/forgejo-runner.plist.j2 index 4bac25f..dc8c2ba 100644 --- a/ansible/roles/forgejo_runner/templates/forgejo-runner.plist.j2 +++ b/ansible/roles/forgejo_runner/templates/forgejo-runner.plist.j2 @@ -4,16 +4,30 @@ Label - mcquack.eblume.forgejo-runner + mcquack.forgejo-runner ProgramArguments - {{ forgejo_runner_binary }} - daemon - --config - {{ forgejo_runner_config_dir }}/config.yaml + /bin/bash + -c + /dev/null || true +docker rm {{ forgejo_runner_container_name }} 2>/dev/null || true + +# Run the forgejo-runner daemon in a container +# - On tailnet-jobs network (can reach Forgejo via Tailscale gateway) +# - Mounts docker socket to spawn job containers +# - Mounts config and data directories +exec docker run --rm \ + --name {{ forgejo_runner_container_name }} \ + --network {{ forgejo_runner_network }} \ + -v /var/run/docker.sock:/var/run/docker.sock \ + -v {{ forgejo_runner_config_dir }}/config.yaml:/config.yaml:ro \ + -v {{ forgejo_runner_data_dir }}:/data \ + {{ forgejo_runner_image }} \ + forgejo-runner daemon --config /config.yaml +]]> - WorkingDirectory - {{ forgejo_runner_data_dir }} RunAtLoad KeepAlive diff --git a/ansible/roles/tailscale_ci_gateway/defaults/main.yml b/ansible/roles/tailscale_ci_gateway/defaults/main.yml new file mode 100644 index 0000000..707df2e --- /dev/null +++ b/ansible/roles/tailscale_ci_gateway/defaults/main.yml @@ -0,0 +1,9 @@ +--- +# Tailscale CI Gateway - provides tailnet access for Forgejo runner job containers + +tailscale_ci_gateway_state_dir: /Users/erichblume/.tailscale-ci-gateway +tailscale_ci_gateway_network: tailnet-jobs +tailscale_ci_gateway_network_subnet: "172.30.0.0/24" +tailscale_ci_gateway_container_name: tailscale-ci-gateway +tailscale_ci_gateway_hostname: ci-gateway +tailscale_ci_gateway_image: tailscale/tailscale:latest diff --git a/ansible/roles/tailscale_ci_gateway/handlers/main.yml b/ansible/roles/tailscale_ci_gateway/handlers/main.yml new file mode 100644 index 0000000..e35e9b9 --- /dev/null +++ b/ansible/roles/tailscale_ci_gateway/handlers/main.yml @@ -0,0 +1,7 @@ +--- +- name: Restart tailscale-ci-gateway + listen: Restart tailscale-ci-gateway + ansible.builtin.shell: | + launchctl unload ~/Library/LaunchAgents/mcquack.tailscale-ci-gateway.plist 2>/dev/null || true + launchctl load ~/Library/LaunchAgents/mcquack.tailscale-ci-gateway.plist + changed_when: true diff --git a/ansible/roles/tailscale_ci_gateway/tasks/main.yml b/ansible/roles/tailscale_ci_gateway/tasks/main.yml new file mode 100644 index 0000000..99f1948 --- /dev/null +++ b/ansible/roles/tailscale_ci_gateway/tasks/main.yml @@ -0,0 +1,46 @@ +--- +# Tailscale CI Gateway role +# Manages a Tailscale container that provides tailnet access for CI job containers + +- name: Ensure state directory exists + ansible.builtin.file: + path: "{{ tailscale_ci_gateway_state_dir }}" + state: directory + mode: "0700" + +- name: Check if Docker network exists + ansible.builtin.command: + cmd: docker network inspect {{ tailscale_ci_gateway_network }} + register: tailscale_ci_gateway_network_check + failed_when: false + changed_when: false + +- name: Create Docker network for CI jobs + ansible.builtin.command: + cmd: >- + docker network create + --driver bridge + --subnet {{ tailscale_ci_gateway_network_subnet }} + {{ tailscale_ci_gateway_network }} + when: tailscale_ci_gateway_network_check.rc != 0 + changed_when: true + +- name: Pull Tailscale image + ansible.builtin.command: + cmd: docker pull {{ tailscale_ci_gateway_image }} + register: tailscale_ci_gateway_pull + changed_when: "'Downloaded newer image' in tailscale_ci_gateway_pull.stdout or 'Pull complete' in tailscale_ci_gateway_pull.stdout" + +- name: Deploy launchd plist for Tailscale CI gateway + ansible.builtin.template: + src: tailscale-ci-gateway.plist.j2 + dest: ~/Library/LaunchAgents/mcquack.tailscale-ci-gateway.plist + mode: "0644" + notify: Restart tailscale-ci-gateway + +- name: Ensure Tailscale CI gateway is loaded + ansible.builtin.command: + cmd: launchctl load ~/Library/LaunchAgents/mcquack.tailscale-ci-gateway.plist + register: tailscale_ci_gateway_load + failed_when: false + changed_when: tailscale_ci_gateway_load.rc == 0 diff --git a/ansible/roles/tailscale_ci_gateway/templates/tailscale-ci-gateway.plist.j2 b/ansible/roles/tailscale_ci_gateway/templates/tailscale-ci-gateway.plist.j2 new file mode 100644 index 0000000..d3fe652 --- /dev/null +++ b/ansible/roles/tailscale_ci_gateway/templates/tailscale-ci-gateway.plist.j2 @@ -0,0 +1,45 @@ + + + + + Label + mcquack.tailscale-ci-gateway + + ProgramArguments + + /bin/bash + -c + /dev/null || true +docker rm {{ tailscale_ci_gateway_container_name }} 2>/dev/null || true + +# Run the container (foreground so launchd manages lifecycle) +exec docker run --rm \ + --name {{ tailscale_ci_gateway_container_name }} \ + --hostname {{ tailscale_ci_gateway_hostname }} \ + --network {{ tailscale_ci_gateway_network }} \ + --cap-add NET_ADMIN \ + --cap-add NET_RAW \ + -v {{ tailscale_ci_gateway_state_dir }}:/var/lib/tailscale \ + -e TS_AUTHKEY="{{ tailscale_ci_gateway_auth_key }}" \ + -e TS_STATE_DIR=/var/lib/tailscale \ + -e TS_USERSPACE=false \ + -e TS_ACCEPT_DNS=true \ + {{ tailscale_ci_gateway_image }} +]]> + + + RunAtLoad + + + KeepAlive + + + StandardOutPath + {{ ansible_env.HOME }}/Library/Logs/mcquack.tailscale-ci-gateway.out.log + + StandardErrorPath + {{ ansible_env.HOME }}/Library/Logs/mcquack.tailscale-ci-gateway.err.log + + -- 2.50.1 (Apple Git-155) From 911913bb2edea1f457d7fec8c87280e43318e916 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Sat, 24 Jan 2026 11:37:04 -0800 Subject: [PATCH 26/30] Fix launchd templates to use full docker path launchd agents don't have /usr/local/bin in PATH by default. Co-Authored-By: Claude Opus 4.5 --- .../forgejo_runner/templates/forgejo-runner.plist.j2 | 10 +++++----- .../templates/tailscale-ci-gateway.plist.j2 | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/ansible/roles/forgejo_runner/templates/forgejo-runner.plist.j2 b/ansible/roles/forgejo_runner/templates/forgejo-runner.plist.j2 index dc8c2ba..adf2288 100644 --- a/ansible/roles/forgejo_runner/templates/forgejo-runner.plist.j2 +++ b/ansible/roles/forgejo_runner/templates/forgejo-runner.plist.j2 @@ -11,17 +11,17 @@ -c /dev/null || true -docker rm {{ forgejo_runner_container_name }} 2>/dev/null || true +/usr/local/bin/docker stop {{ forgejo_runner_container_name }} 2>/dev/null || true +/usr/local/bin/docker rm {{ forgejo_runner_container_name }} 2>/dev/null || true # Run the forgejo-runner daemon in a container # - On tailnet-jobs network (can reach Forgejo via Tailscale gateway) -# - Mounts docker socket to spawn job containers +# - Mounts /usr/local/bin/docker socket to spawn job containers # - Mounts config and data directories -exec docker run --rm \ +exec /usr/local/bin/docker run --rm \ --name {{ forgejo_runner_container_name }} \ --network {{ forgejo_runner_network }} \ - -v /var/run/docker.sock:/var/run/docker.sock \ + -v /var/run//usr/local/bin/docker.sock:/var/run//usr/local/bin/docker.sock \ -v {{ forgejo_runner_config_dir }}/config.yaml:/config.yaml:ro \ -v {{ forgejo_runner_data_dir }}:/data \ {{ forgejo_runner_image }} \ diff --git a/ansible/roles/tailscale_ci_gateway/templates/tailscale-ci-gateway.plist.j2 b/ansible/roles/tailscale_ci_gateway/templates/tailscale-ci-gateway.plist.j2 index d3fe652..287c120 100644 --- a/ansible/roles/tailscale_ci_gateway/templates/tailscale-ci-gateway.plist.j2 +++ b/ansible/roles/tailscale_ci_gateway/templates/tailscale-ci-gateway.plist.j2 @@ -11,11 +11,11 @@ -c /dev/null || true -docker rm {{ tailscale_ci_gateway_container_name }} 2>/dev/null || true +/usr/local/bin/docker stop {{ tailscale_ci_gateway_container_name }} 2>/dev/null || true +/usr/local/bin/docker rm {{ tailscale_ci_gateway_container_name }} 2>/dev/null || true # Run the container (foreground so launchd manages lifecycle) -exec docker run --rm \ +exec /usr/local/bin/docker run --rm \ --name {{ tailscale_ci_gateway_container_name }} \ --hostname {{ tailscale_ci_gateway_hostname }} \ --network {{ tailscale_ci_gateway_network }} \ -- 2.50.1 (Apple Git-155) From c79dc943259883b6fb802581bcd58d461f2dd0a2 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Sat, 24 Jan 2026 12:56:25 -0800 Subject: [PATCH 27/30] Fix forgejo-runner networking for tailnet access - Add --accept-routes to tailscale-ci-gateway for service routing - Run forgejo-runner as root for docker socket access - Mount actual docker socket path (not symlink) - Use gateway network namespace for tailnet connectivity - Registration uses gateway network for Forgejo access Co-Authored-By: Claude Opus 4.5 --- ansible/roles/forgejo_runner/tasks/main.yml | 2 +- .../forgejo_runner/templates/forgejo-runner.plist.j2 | 9 +++++---- .../templates/tailscale-ci-gateway.plist.j2 | 1 + 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/ansible/roles/forgejo_runner/tasks/main.yml b/ansible/roles/forgejo_runner/tasks/main.yml index 72e114b..0cce725 100644 --- a/ansible/roles/forgejo_runner/tasks/main.yml +++ b/ansible/roles/forgejo_runner/tasks/main.yml @@ -39,7 +39,7 @@ ansible.builtin.command: cmd: > docker run --rm - --network {{ forgejo_runner_network }} + --network=container:tailscale-ci-gateway -v {{ forgejo_runner_data_dir }}:/data {{ forgejo_runner_image }} forgejo-runner register diff --git a/ansible/roles/forgejo_runner/templates/forgejo-runner.plist.j2 b/ansible/roles/forgejo_runner/templates/forgejo-runner.plist.j2 index adf2288..e0d07a6 100644 --- a/ansible/roles/forgejo_runner/templates/forgejo-runner.plist.j2 +++ b/ansible/roles/forgejo_runner/templates/forgejo-runner.plist.j2 @@ -15,13 +15,14 @@ /usr/local/bin/docker rm {{ forgejo_runner_container_name }} 2>/dev/null || true # Run the forgejo-runner daemon in a container -# - On tailnet-jobs network (can reach Forgejo via Tailscale gateway) -# - Mounts /usr/local/bin/docker socket to spawn job containers +# - Uses gateway's network namespace for tailnet access (to poll Forgejo) +# - Mounts docker socket to spawn job containers # - Mounts config and data directories exec /usr/local/bin/docker run --rm \ --name {{ forgejo_runner_container_name }} \ - --network {{ forgejo_runner_network }} \ - -v /var/run//usr/local/bin/docker.sock:/var/run//usr/local/bin/docker.sock \ + --network=container:tailscale-ci-gateway \ + --user root \ + -v {{ ansible_env.HOME }}/.docker/run/docker.sock:/var/run/docker.sock \ -v {{ forgejo_runner_config_dir }}/config.yaml:/config.yaml:ro \ -v {{ forgejo_runner_data_dir }}:/data \ {{ forgejo_runner_image }} \ diff --git a/ansible/roles/tailscale_ci_gateway/templates/tailscale-ci-gateway.plist.j2 b/ansible/roles/tailscale_ci_gateway/templates/tailscale-ci-gateway.plist.j2 index 287c120..7c223eb 100644 --- a/ansible/roles/tailscale_ci_gateway/templates/tailscale-ci-gateway.plist.j2 +++ b/ansible/roles/tailscale_ci_gateway/templates/tailscale-ci-gateway.plist.j2 @@ -26,6 +26,7 @@ exec /usr/local/bin/docker run --rm \ -e TS_STATE_DIR=/var/lib/tailscale \ -e TS_USERSPACE=false \ -e TS_ACCEPT_DNS=true \ + -e TS_EXTRA_ARGS="--accept-routes" \ {{ tailscale_ci_gateway_image }} ]]> -- 2.50.1 (Apple Git-155) From cfe5c0c0dd1078e770e6cdc84d3405cc18a9bf6a Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Sat, 24 Jan 2026 13:23:39 -0800 Subject: [PATCH 28/30] Switch forgejo-runner to host execution mode Docker-based runner had networking issues reaching Forgejo from job containers. Host execution mode runs the runner daemon directly on indri, with jobs executing on the host. Actions that need Docker use host networking to access localhost:3001. - Runner binary compiled locally at ~/code/3rd/forgejo-runner - Labels use :host suffix instead of :docker://image - PATH set in launchd plist for mise-managed tools (node, etc.) - Container network set to "host" for actions needing Docker Co-Authored-By: Claude Opus 4.5 --- .../roles/forgejo_runner/defaults/main.yml | 41 +++++-------------- ansible/roles/forgejo_runner/tasks/main.yml | 25 +++-------- .../forgejo_runner/templates/config.yaml.j2 | 12 ++---- .../templates/forgejo-runner.plist.j2 | 34 ++++++--------- 4 files changed, 34 insertions(+), 78 deletions(-) diff --git a/ansible/roles/forgejo_runner/defaults/main.yml b/ansible/roles/forgejo_runner/defaults/main.yml index 6c87755..75cbd0c 100644 --- a/ansible/roles/forgejo_runner/defaults/main.yml +++ b/ansible/roles/forgejo_runner/defaults/main.yml @@ -1,42 +1,23 @@ --- -# Forgejo Runner - containerized daemon on tailnet-jobs network +# Forgejo Runner - host execution mode # -# The runner daemon runs in a Docker container with access to the tailnet -# via the tailscale-ci-gateway. This allows it to register with Forgejo -# using the Tailscale URL, so job containers can also reach Forgejo. +# The runner daemon runs directly on indri and executes jobs on the host. +# This avoids container networking complexity since it can reach Forgejo +# at localhost:3001 directly. +forgejo_runner_binary: /Users/erichblume/code/3rd/forgejo-runner/forgejo-runner forgejo_runner_data_dir: /Users/erichblume/.forgejo-runner forgejo_runner_config_dir: /Users/erichblume/.config/forgejo-runner forgejo_runner_log_dir: /Users/erichblume/Library/Logs -# Container settings -forgejo_runner_container_name: forgejo-runner -forgejo_runner_image: code.forgejo.org/forgejo/runner:6.2.1 -forgejo_runner_network: tailnet-jobs +# Runner registration - use localhost since we're running on indri +forgejo_runner_instance_url: "http://localhost:3001" +forgejo_runner_name: "indri-host-runner" -# Runner registration - use Tailscale URL since we're on tailnet-jobs network -forgejo_runner_instance_url: "https://forge.tail8d86e.ts.net" -forgejo_runner_name: "indri-docker-runner" - -# Labels format: label:docker://image -# -# Job containers also run on tailnet-jobs network and can reach: -# - forge.tail8d86e.ts.net for git clone -# - registry.tail8d86e.ts.net for container push/pull -# -# Bootstrap mode (use upstream images until we build ci-base): -# docker-builder:docker://docker:27-cli -# ubuntu-latest:docker://catthehacker/ubuntu:act-22.04 -# -# Production mode (use our own images from zot): -# docker-builder:docker://registry.tail8d86e.ts.net/blumeops/ci-base:latest -# ubuntu-latest:docker://registry.tail8d86e.ts.net/blumeops/ci-base:latest -# -forgejo_runner_labels: "docker-builder:docker://docker:27-cli,ubuntu-latest:docker://catthehacker/ubuntu:act-22.04,ubuntu-22.04:docker://catthehacker/ubuntu:act-22.04" +# Labels format for host execution: label:host +# Jobs run directly on the host, not in containers +forgejo_runner_labels: "ubuntu-latest:host,ubuntu-22.04:host" # Runner config forgejo_runner_capacity: 2 forgejo_runner_timeout: 3h - -# Docker container settings for jobs -forgejo_runner_privileged: true # Needed for container builds diff --git a/ansible/roles/forgejo_runner/tasks/main.yml b/ansible/roles/forgejo_runner/tasks/main.yml index 0cce725..0f28d88 100644 --- a/ansible/roles/forgejo_runner/tasks/main.yml +++ b/ansible/roles/forgejo_runner/tasks/main.yml @@ -1,12 +1,8 @@ --- -# Forgejo Runner - containerized daemon on tailnet-jobs network +# Forgejo Runner - host execution mode # -# The runner daemon runs in a Docker container with access to the tailnet -# via the tailscale-ci-gateway. Job containers also run on tailnet-jobs -# and can reach Forgejo via Tailscale. -# -# DEPENDENCIES: -# - tailscale_ci_gateway role must run first (creates tailnet-jobs network) +# The runner daemon runs directly on indri using a locally compiled binary. +# Jobs execute on the host, reaching Forgejo at localhost:3001. - name: Ensure forgejo-runner directories exist ansible.builtin.file: @@ -24,30 +20,21 @@ mode: '0644' notify: Restart forgejo-runner -- name: Pull forgejo-runner image - ansible.builtin.command: - cmd: docker pull {{ forgejo_runner_image }} - register: forgejo_runner_pull - changed_when: "'Downloaded newer image' in forgejo_runner_pull.stdout or 'Pull complete' in forgejo_runner_pull.stdout" - - name: Check if runner is registered ansible.builtin.stat: path: "{{ forgejo_runner_data_dir }}/.runner" register: forgejo_runner_registered -- name: Register runner with Forgejo (via tailnet) +- name: Register runner with Forgejo ansible.builtin.command: cmd: > - docker run --rm - --network=container:tailscale-ci-gateway - -v {{ forgejo_runner_data_dir }}:/data - {{ forgejo_runner_image }} - forgejo-runner register + {{ forgejo_runner_binary }} register --instance "{{ forgejo_runner_instance_url }}" --token "{{ forgejo_runner_token }}" --name "{{ forgejo_runner_name }}" --labels "{{ forgejo_runner_labels }}" --no-interactive + chdir: "{{ forgejo_runner_data_dir }}" when: not forgejo_runner_registered.stat.exists changed_when: true diff --git a/ansible/roles/forgejo_runner/templates/config.yaml.j2 b/ansible/roles/forgejo_runner/templates/config.yaml.j2 index 397fbbd..07bdb8d 100644 --- a/ansible/roles/forgejo_runner/templates/config.yaml.j2 +++ b/ansible/roles/forgejo_runner/templates/config.yaml.j2 @@ -3,15 +3,11 @@ log: level: info runner: - # Path inside the container (data dir mounted at /data) - file: /data/.runner + file: {{ forgejo_runner_data_dir }}/.runner capacity: {{ forgejo_runner_capacity }} timeout: {{ forgejo_runner_timeout }} +# Even in host execution mode, some actions run in containers. +# Use host networking so containers can access localhost services. container: - # Use tailnet-jobs network so job containers can reach Forgejo via Tailscale gateway - network: "{{ forgejo_runner_network }}" - privileged: {{ forgejo_runner_privileged | lower }} - # Mount Docker socket so jobs can build containers - valid_volumes: - - /var/run/docker.sock + network: "host" diff --git a/ansible/roles/forgejo_runner/templates/forgejo-runner.plist.j2 b/ansible/roles/forgejo_runner/templates/forgejo-runner.plist.j2 index e0d07a6..e04fa0d 100644 --- a/ansible/roles/forgejo_runner/templates/forgejo-runner.plist.j2 +++ b/ansible/roles/forgejo_runner/templates/forgejo-runner.plist.j2 @@ -7,28 +7,20 @@ mcquack.forgejo-runner ProgramArguments - /bin/bash - -c - /dev/null || true -/usr/local/bin/docker rm {{ forgejo_runner_container_name }} 2>/dev/null || true - -# Run the forgejo-runner daemon in a container -# - Uses gateway's network namespace for tailnet access (to poll Forgejo) -# - Mounts docker socket to spawn job containers -# - Mounts config and data directories -exec /usr/local/bin/docker run --rm \ - --name {{ forgejo_runner_container_name }} \ - --network=container:tailscale-ci-gateway \ - --user root \ - -v {{ ansible_env.HOME }}/.docker/run/docker.sock:/var/run/docker.sock \ - -v {{ forgejo_runner_config_dir }}/config.yaml:/config.yaml:ro \ - -v {{ forgejo_runner_data_dir }}:/data \ - {{ forgejo_runner_image }} \ - forgejo-runner daemon --config /config.yaml -]]> + {{ forgejo_runner_binary }} + daemon + --config + {{ forgejo_runner_config_dir }}/config.yaml + WorkingDirectory + {{ forgejo_runner_data_dir }} + EnvironmentVariables + + PATH + /Users/erichblume/.local/share/mise/shims:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin + HOME + /Users/erichblume + RunAtLoad KeepAlive -- 2.50.1 (Apple Git-155) From ad968eea46f25ad8e520e9bda4f3d3b44f84526d Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Sat, 24 Jan 2026 13:26:36 -0800 Subject: [PATCH 29/30] Remove tailscale_ci_gateway role and ACLs The Docker-based runner with Tailscale sidecar approach was abandoned in favor of host execution mode. Clean up the unused infrastructure: - Remove tailscale_ci_gateway role and its reference in indri.yml - Remove tag:ci-gateway ACL grants and tagOwners from pulumi policy - Plist already removed from indri Co-Authored-By: Claude Opus 4.5 --- ansible/playbooks/indri.yml | 19 -------- .../tailscale_ci_gateway/defaults/main.yml | 9 ---- .../tailscale_ci_gateway/handlers/main.yml | 7 --- .../roles/tailscale_ci_gateway/tasks/main.yml | 46 ------------------- .../templates/tailscale-ci-gateway.plist.j2 | 46 ------------------- pulumi/policy.hujson | 19 -------- 6 files changed, 146 deletions(-) delete mode 100644 ansible/roles/tailscale_ci_gateway/defaults/main.yml delete mode 100644 ansible/roles/tailscale_ci_gateway/handlers/main.yml delete mode 100644 ansible/roles/tailscale_ci_gateway/tasks/main.yml delete mode 100644 ansible/roles/tailscale_ci_gateway/templates/tailscale-ci-gateway.plist.j2 diff --git a/ansible/playbooks/indri.yml b/ansible/playbooks/indri.yml index 09779ec..b12e905 100644 --- a/ansible/playbooks/indri.yml +++ b/ansible/playbooks/indri.yml @@ -78,23 +78,6 @@ no_log: true tags: [forgejo_runner] - # Tailscale CI gateway auth key (for job container tailnet access) - - name: Fetch tailscale ci-gateway auth key - ansible.builtin.command: - cmd: op --vault vg6xf6vvfmoh5hqjjhlhbeoaie item get w3663ffnvkewbftncqxtcpeavy --fields ci-gateway-ts-auth-key --reveal - delegate_to: localhost - register: _tailscale_ci_gateway_auth_key - changed_when: false - no_log: true - check_mode: false - tags: [tailscale_ci_gateway] - - - name: Set tailscale ci-gateway auth key fact - ansible.builtin.set_fact: - tailscale_ci_gateway_auth_key: "{{ _tailscale_ci_gateway_auth_key.stdout }}" - no_log: true - tags: [tailscale_ci_gateway] - roles: - role: alloy tags: alloy @@ -116,7 +99,5 @@ tags: plex_metrics - role: tailscale_serve tags: tailscale-serve - - role: tailscale_ci_gateway - tags: tailscale_ci_gateway - role: forgejo_runner tags: forgejo_runner diff --git a/ansible/roles/tailscale_ci_gateway/defaults/main.yml b/ansible/roles/tailscale_ci_gateway/defaults/main.yml deleted file mode 100644 index 707df2e..0000000 --- a/ansible/roles/tailscale_ci_gateway/defaults/main.yml +++ /dev/null @@ -1,9 +0,0 @@ ---- -# Tailscale CI Gateway - provides tailnet access for Forgejo runner job containers - -tailscale_ci_gateway_state_dir: /Users/erichblume/.tailscale-ci-gateway -tailscale_ci_gateway_network: tailnet-jobs -tailscale_ci_gateway_network_subnet: "172.30.0.0/24" -tailscale_ci_gateway_container_name: tailscale-ci-gateway -tailscale_ci_gateway_hostname: ci-gateway -tailscale_ci_gateway_image: tailscale/tailscale:latest diff --git a/ansible/roles/tailscale_ci_gateway/handlers/main.yml b/ansible/roles/tailscale_ci_gateway/handlers/main.yml deleted file mode 100644 index e35e9b9..0000000 --- a/ansible/roles/tailscale_ci_gateway/handlers/main.yml +++ /dev/null @@ -1,7 +0,0 @@ ---- -- name: Restart tailscale-ci-gateway - listen: Restart tailscale-ci-gateway - ansible.builtin.shell: | - launchctl unload ~/Library/LaunchAgents/mcquack.tailscale-ci-gateway.plist 2>/dev/null || true - launchctl load ~/Library/LaunchAgents/mcquack.tailscale-ci-gateway.plist - changed_when: true diff --git a/ansible/roles/tailscale_ci_gateway/tasks/main.yml b/ansible/roles/tailscale_ci_gateway/tasks/main.yml deleted file mode 100644 index 99f1948..0000000 --- a/ansible/roles/tailscale_ci_gateway/tasks/main.yml +++ /dev/null @@ -1,46 +0,0 @@ ---- -# Tailscale CI Gateway role -# Manages a Tailscale container that provides tailnet access for CI job containers - -- name: Ensure state directory exists - ansible.builtin.file: - path: "{{ tailscale_ci_gateway_state_dir }}" - state: directory - mode: "0700" - -- name: Check if Docker network exists - ansible.builtin.command: - cmd: docker network inspect {{ tailscale_ci_gateway_network }} - register: tailscale_ci_gateway_network_check - failed_when: false - changed_when: false - -- name: Create Docker network for CI jobs - ansible.builtin.command: - cmd: >- - docker network create - --driver bridge - --subnet {{ tailscale_ci_gateway_network_subnet }} - {{ tailscale_ci_gateway_network }} - when: tailscale_ci_gateway_network_check.rc != 0 - changed_when: true - -- name: Pull Tailscale image - ansible.builtin.command: - cmd: docker pull {{ tailscale_ci_gateway_image }} - register: tailscale_ci_gateway_pull - changed_when: "'Downloaded newer image' in tailscale_ci_gateway_pull.stdout or 'Pull complete' in tailscale_ci_gateway_pull.stdout" - -- name: Deploy launchd plist for Tailscale CI gateway - ansible.builtin.template: - src: tailscale-ci-gateway.plist.j2 - dest: ~/Library/LaunchAgents/mcquack.tailscale-ci-gateway.plist - mode: "0644" - notify: Restart tailscale-ci-gateway - -- name: Ensure Tailscale CI gateway is loaded - ansible.builtin.command: - cmd: launchctl load ~/Library/LaunchAgents/mcquack.tailscale-ci-gateway.plist - register: tailscale_ci_gateway_load - failed_when: false - changed_when: tailscale_ci_gateway_load.rc == 0 diff --git a/ansible/roles/tailscale_ci_gateway/templates/tailscale-ci-gateway.plist.j2 b/ansible/roles/tailscale_ci_gateway/templates/tailscale-ci-gateway.plist.j2 deleted file mode 100644 index 7c223eb..0000000 --- a/ansible/roles/tailscale_ci_gateway/templates/tailscale-ci-gateway.plist.j2 +++ /dev/null @@ -1,46 +0,0 @@ - - - - - Label - mcquack.tailscale-ci-gateway - - ProgramArguments - - /bin/bash - -c - /dev/null || true -/usr/local/bin/docker rm {{ tailscale_ci_gateway_container_name }} 2>/dev/null || true - -# Run the container (foreground so launchd manages lifecycle) -exec /usr/local/bin/docker run --rm \ - --name {{ tailscale_ci_gateway_container_name }} \ - --hostname {{ tailscale_ci_gateway_hostname }} \ - --network {{ tailscale_ci_gateway_network }} \ - --cap-add NET_ADMIN \ - --cap-add NET_RAW \ - -v {{ tailscale_ci_gateway_state_dir }}:/var/lib/tailscale \ - -e TS_AUTHKEY="{{ tailscale_ci_gateway_auth_key }}" \ - -e TS_STATE_DIR=/var/lib/tailscale \ - -e TS_USERSPACE=false \ - -e TS_ACCEPT_DNS=true \ - -e TS_EXTRA_ARGS="--accept-routes" \ - {{ tailscale_ci_gateway_image }} -]]> - - - RunAtLoad - - - KeepAlive - - - StandardOutPath - {{ ansible_env.HOME }}/Library/Logs/mcquack.tailscale-ci-gateway.out.log - - StandardErrorPath - {{ ansible_env.HOME }}/Library/Logs/mcquack.tailscale-ci-gateway.err.log - - diff --git a/pulumi/policy.hujson b/pulumi/policy.hujson index 789bd96..037f085 100644 --- a/pulumi/policy.hujson +++ b/pulumi/policy.hujson @@ -75,19 +75,6 @@ "ip": ["tcp:3001", "tcp:2200"], }, - // --- CI Gateway (Tailscale sidecar for Forgejo runner jobs) --- - // Can reach Forge for git clone operations - { - "src": ["tag:ci-gateway"], - "dst": ["tag:forge"], - "ip": ["tcp:443"], - }, - // Can reach registry to push/pull container images - { - "src": ["tag:ci-gateway"], - "dst": ["tag:registry"], - "ip": ["tcp:443"], - }, // Homelab can reach k8s services: PostgreSQL, CNPG metrics, Prometheus/Loki { "src": ["tag:homelab"], @@ -140,7 +127,6 @@ "tag:k8s-api": ["autogroup:admin", "tag:blumeops"], "tag:k8s-operator": ["autogroup:admin", "tag:blumeops"], "tag:k8s": ["autogroup:admin", "tag:blumeops", "tag:k8s-operator"], - "tag:ci-gateway": ["autogroup:admin", "tag:blumeops"], }, // ============== ACL Tests ============== @@ -166,10 +152,5 @@ "src": "tag:k8s", "accept": ["tag:registry:443", "tag:homelab:3001", "tag:homelab:2200"], }, - // CI gateway can reach forge and registry - { - "src": "tag:ci-gateway", - "accept": ["tag:forge:443", "tag:registry:443"], - }, ], } -- 2.50.1 (Apple Git-155) From 34211fa8749fb8cde1c54e891697ef2abf7541b3 Mon Sep 17 00:00:00 2001 From: Erich Blume Date: Sat, 24 Jan 2026 13:29:54 -0800 Subject: [PATCH 30/30] Remove placeholder workflows and ci-base manifest Keep only test.yaml workflow for now. Container build workflows and ci-base Dockerfile will be added in a future PR. Co-Authored-By: Claude Opus 4.5 --- .forgejo/workflows/build-ci-base.yaml | 37 ----------------- .forgejo/workflows/build-devpi.yaml | 37 ----------------- argocd/manifests/ci-base/Dockerfile | 58 --------------------------- 3 files changed, 132 deletions(-) delete mode 100644 .forgejo/workflows/build-ci-base.yaml delete mode 100644 .forgejo/workflows/build-devpi.yaml delete mode 100644 argocd/manifests/ci-base/Dockerfile diff --git a/.forgejo/workflows/build-ci-base.yaml b/.forgejo/workflows/build-ci-base.yaml deleted file mode 100644 index 7a0d5f8..0000000 --- a/.forgejo/workflows/build-ci-base.yaml +++ /dev/null @@ -1,37 +0,0 @@ -name: Build CI base image - -on: - push: - tags: - - 'ci-base-v*' - workflow_dispatch: - inputs: - version: - description: 'Version (e.g. v1.0.0)' - required: true - -jobs: - build: - runs-on: docker-builder - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Extract version from tag - id: version - run: | - if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then - VERSION="${{ github.event.inputs.version }}" - else - # Extract version from tag: ci-base-v1.0.0 -> v1.0.0 - VERSION="${GITHUB_REF_NAME#ci-base-}" - fi - echo "version=$VERSION" >> "$GITHUB_OUTPUT" - echo "Building version: $VERSION" - - - name: Build and push - uses: ./.forgejo/actions/build-push-image - with: - context: argocd/manifests/ci-base - image_name: blumeops/ci-base - version: ${{ steps.version.outputs.version }} diff --git a/.forgejo/workflows/build-devpi.yaml b/.forgejo/workflows/build-devpi.yaml deleted file mode 100644 index 5329b77..0000000 --- a/.forgejo/workflows/build-devpi.yaml +++ /dev/null @@ -1,37 +0,0 @@ -name: Build devpi - -on: - push: - tags: - - 'devpi-v*' - workflow_dispatch: - inputs: - version: - description: 'Version (e.g. v1.0.0)' - required: true - -jobs: - build: - runs-on: docker-builder - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Extract version from tag - id: version - run: | - if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then - VERSION="${{ github.event.inputs.version }}" - else - # Extract version from tag: devpi-v1.0.0 -> v1.0.0 - VERSION="${GITHUB_REF_NAME#devpi-}" - fi - echo "version=$VERSION" >> "$GITHUB_OUTPUT" - echo "Building version: $VERSION" - - - name: Build and push - uses: ./.forgejo/actions/build-push-image - with: - context: argocd/manifests/devpi - image_name: blumeops/devpi - version: ${{ steps.version.outputs.version }} diff --git a/argocd/manifests/ci-base/Dockerfile b/argocd/manifests/ci-base/Dockerfile deleted file mode 100644 index 974ceb2..0000000 --- a/argocd/manifests/ci-base/Dockerfile +++ /dev/null @@ -1,58 +0,0 @@ -# CI base image for Forgejo Actions -# Used by forgejo-runner for ubuntu-latest and ubuntu-22.04 labels -FROM ubuntu:22.04 - -# Prevent interactive prompts during package installation -ENV DEBIAN_FRONTEND=noninteractive - -# Install common CI tools -RUN apt-get update && apt-get install -y --no-install-recommends \ - # Essential tools - ca-certificates \ - curl \ - wget \ - git \ - jq \ - unzip \ - zip \ - # Build tools - build-essential \ - make \ - # Python - python3 \ - python3-pip \ - python3-venv \ - # Node.js (via nodesource for LTS) - && curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \ - && apt-get install -y nodejs \ - # Docker CLI (not the daemon - we mount the socket) - && install -m 0755 -d /etc/apt/keyrings \ - && curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc \ - && chmod a+r /etc/apt/keyrings/docker.asc \ - && echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" > /etc/apt/sources.list.d/docker.list \ - && apt-get update \ - && apt-get install -y docker-ce-cli \ - # Clean up - && apt-get clean \ - && rm -rf /var/lib/apt/lists/* - -# Create a non-root user for running jobs -RUN useradd -m -s /bin/bash runner \ - && mkdir -p /home/runner/work \ - && chown -R runner:runner /home/runner - -# Set working directory -WORKDIR /home/runner/work - -# Default to runner user -USER runner - -# Verify installations -RUN echo "=== CI Base Image ===" \ - && git --version \ - && node --version \ - && npm --version \ - && python3 --version \ - && docker --version \ - && make --version | head -1 \ - && gcc --version | head -1 -- 2.50.1 (Apple Git-155)