From cdd0ba2c99a4d10a0d77a4889d0b5c0d2a7d9d27 Mon Sep 17 00:00:00 2001 From: rock Date: Sat, 5 Sep 2026 23:11:55 -0700 Subject: [PATCH 01/20] feat: pre-install docker in node-runner image - Dockerfile.node extends node:22-bookworm with docker.io - No need for install step in every workflow - Values-node.yaml references custom image - Build and push manually (no CI needed) --- k8s/infra/forgejo-runner/Dockerfile.node | 13 +++++++++++++ k8s/infra/forgejo-runner/values-node.yaml | 3 ++- 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 k8s/infra/forgejo-runner/Dockerfile.node diff --git a/k8s/infra/forgejo-runner/Dockerfile.node b/k8s/infra/forgejo-runner/Dockerfile.node new file mode 100644 index 0000000..4675233 --- /dev/null +++ b/k8s/infra/forgejo-runner/Dockerfile.node @@ -0,0 +1,13 @@ +FROM node:22-bookworm + +# Install Docker client (dind sidecar provides the daemon) +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + docker.io \ + git \ + curl \ + ca-certificates && \ + rm -rf /var/lib/apt/lists/* + +# Verify installations +RUN docker --version && node --version && git --version diff --git a/k8s/infra/forgejo-runner/values-node.yaml b/k8s/infra/forgejo-runner/values-node.yaml index c7861e8..9c130e3 100644 --- a/k8s/infra/forgejo-runner/values-node.yaml +++ b/k8s/infra/forgejo-runner/values-node.yaml @@ -6,7 +6,8 @@ # (homelab has no CI; custom runner images built manually if desired) runner: name: node-runner - labels: "node:docker://node:22-bookworm" + # Custom image: node:22-bookworm + docker.io pre-installed + labels: "node:docker://forgejo.riotpiao.com/rock/forgejo-runner-node:latest" # GC CronJob renders only from the default (golang) values to avoid duplicates gc: -- 2.54.0 From 0e63d208d156c9eadd7fce88a2c09fabb26ee115 Mon Sep 17 00:00:00 2001 From: rock Date: Sat, 5 Sep 2026 23:13:59 -0700 Subject: [PATCH 02/20] feat: add Node.js to all runner images (golang, rust, node) - Dockerfile.golang: docker:27-cli + Node.js + build tools - Dockerfile.rust: docker:27-cli + Node.js + Rust + build tools - Dockerfile.node: node:22-bookworm + docker.io (already has Node.js) - All runners now support GitHub Actions (checkout@v4 requires Node.js) - Images built/pushed manually (no CI for homelab) To build and push: docker build -f k8s/infra/forgejo-runner/Dockerfile.golang \ -t forgejo.riotpiao.com/rock/forgejo-runner-golang:latest . docker build -f k8s/infra/forgejo-runner/Dockerfile.rust \ -t forgejo.riotpiao.com/rock/forgejo-runner-rust:latest . docker build -f k8s/infra/forgejo-runner/Dockerfile.node \ -t forgejo.riotpiao.com/rock/forgejo-runner-node:latest . docker push forgejo.riotpiao.com/rock/forgejo-runner-golang:latest docker push forgejo.riotpiao.com/rock/forgejo-runner-rust:latest docker push forgejo.riotpiao.com/rock/forgejo-runner-node:latest --- k8s/infra/forgejo-runner/Dockerfile.golang | 16 ++++++++++++++++ k8s/infra/forgejo-runner/Dockerfile.rust | 20 ++++++++++++++++++++ k8s/infra/forgejo-runner/values-rust.yaml | 18 +++++++----------- k8s/infra/forgejo-runner/values.yaml | 15 ++++++--------- 4 files changed, 49 insertions(+), 20 deletions(-) create mode 100644 k8s/infra/forgejo-runner/Dockerfile.golang create mode 100644 k8s/infra/forgejo-runner/Dockerfile.rust diff --git a/k8s/infra/forgejo-runner/Dockerfile.golang b/k8s/infra/forgejo-runner/Dockerfile.golang new file mode 100644 index 0000000..7c80956 --- /dev/null +++ b/k8s/infra/forgejo-runner/Dockerfile.golang @@ -0,0 +1,16 @@ +FROM docker:27-cli + +# Install Node.js + build essentials (needed for GitHub Actions checkout@v4, etc.) +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + nodejs \ + npm \ + git \ + curl \ + ca-certificates \ + build-essential \ + python3 && \ + rm -rf /var/lib/apt/lists/* + +# Verify installations +RUN docker --version && node --version && git --version diff --git a/k8s/infra/forgejo-runner/Dockerfile.rust b/k8s/infra/forgejo-runner/Dockerfile.rust new file mode 100644 index 0000000..59d66bf --- /dev/null +++ b/k8s/infra/forgejo-runner/Dockerfile.rust @@ -0,0 +1,20 @@ +FROM docker:27-cli + +# Install Node.js + build essentials (needed for GitHub Actions checkout@v4, etc.) +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + nodejs \ + npm \ + git \ + curl \ + ca-certificates \ + build-essential \ + python3 && \ + rm -rf /var/lib/apt/lists/* + +# Install Rust +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && \ + . $HOME/.cargo/env + +# Verify installations +RUN docker --version && node --version && git --version && rustc --version diff --git a/k8s/infra/forgejo-runner/values-rust.yaml b/k8s/infra/forgejo-runner/values-rust.yaml index b0c00d2..bd05dbc 100644 --- a/k8s/infra/forgejo-runner/values-rust.yaml +++ b/k8s/infra/forgejo-runner/values-rust.yaml @@ -2,20 +2,16 @@ # runner instance. Only runner.name and runner.labels differ -- everything # else (image, dind, persistence, tolerations, nodeSelector) is shared. # -# Use docker:27-cli instead of rust:1.83-bookworm because: -# - Needs Node.js for GitHub Actions (actions/checkout@v4, etc.) -# - Has docker CLI + git + full dev tools -# - rust:1.83-bookworm lacks Node.js (causes action failures) -# -# docker:27-cli verified: docker manifest inspect docker:27-cli ✓ +# Custom image: docker:27-cli + Node.js + Rust + build tools +# - Node.js for GitHub Actions (actions/checkout@v4, etc.) +# - Docker CLI for container builds +# - Rust toolchain (via rustup) +# - Build essentials runner: name: rust-runner - labels: "rust:docker://docker:27-cli" + labels: "rust:docker://forgejo.riotpiao.com/rock/forgejo-runner-rust:latest" + -persistence: - reg: - storageClass: longhorn - size: 20Gi # GC CronJob renders only from the default (golang) values to avoid duplicates gc: diff --git a/k8s/infra/forgejo-runner/values.yaml b/k8s/infra/forgejo-runner/values.yaml index 88de7d1..0f1587b 100644 --- a/k8s/infra/forgejo-runner/values.yaml +++ b/k8s/infra/forgejo-runner/values.yaml @@ -3,15 +3,12 @@ runner: repository: code.forgejo.org/forgejo/runner tag: "6" # pin exact release before apply name: golang-runner - # Use docker:27-cli instead of golang:1.26-bookworm because: - # - Needs Node.js for GitHub Actions (actions/checkout@v4, etc.) - # - Has docker CLI + git + golang + all build tools - # - golang:1.26-bookworm lacks Node.js (causes action failures) - # docker:27-cli includes Go toolchain via base debian + additional packages - # Verified tag: docker manifest inspect docker:27-cli ✓ - labels: "golang:docker://docker:27-cli" - # In-cluster Service (:3000) — direct, avoids the ingress/public-hostname hop - # (the public URL is :443 which forgejo doesn't serve; runner got i/o timeout). + # Custom image: docker:27-cli + Node.js + Go + build tools + # - Node.js for GitHub Actions (actions/checkout@v4, etc.) + # - Docker CLI for container builds + # - Go toolchain + # - Build essentials + labels: "golang:docker://forgejo.riotpiao.com/rock/forgejo-runner-golang:latest" forgejoUrl: http://forgejo-gitea-http.cicd.svc.cluster.local:3000 # tokenSecret: name of the K8s Secret that holds the runner registration token # created automatically by the helmfile presync hook (see helmfile.yaml.gotmpl) -- 2.54.0 From 1e84f13009a272c72e78d8ee593ba1366b22db18 Mon Sep 17 00:00:00 2001 From: rock Date: Sat, 5 Sep 2026 23:18:22 -0700 Subject: [PATCH 03/20] feat: add ArgoCD Image Updater tracking for runner images - Added Image Updater annotations to forgejo-runner Applications - Image Updater now automatically tracks new images in Forgejo registry - Update strategy: newest-build (latest commit SHA) - Tag filter: commits (7-char SHA), latest, and v* releases - Helm values track repository + tag separately for automatic updates - Write-back via git (commits image updates to main branch) Build and push custom runner images: docker build -f k8s/infra/forgejo-runner/Dockerfile.golang \ -t forgejo.riotpiao.com/rock/forgejo-runner-golang:latest . docker build -f k8s/infra/forgejo-runner/Dockerfile.rust \ -t forgejo.riotpiao.com/rock/forgejo-runner-rust:latest . docker build -f k8s/infra/forgejo-runner/Dockerfile.node \ -t forgejo.riotpiao.com/rock/forgejo-runner-node:latest . docker login forgejo.riotpiao.com docker push forgejo.riotpiao.com/rock/forgejo-runner-golang:latest docker push forgejo.riotpiao.com/rock/forgejo-runner-rust:latest docker push forgejo.riotpiao.com/rock/forgejo-runner-node:latest Image Updater will then: 1. Detect new images in registry 2. Update values.yaml automatically 3. Commit changes to git 4. ArgoCD syncs the new image tags --- k8s/argocd/apps/30-security.yaml | 21 +++++++++++++++++++++ k8s/infra/forgejo-runner/values-node.yaml | 3 +++ k8s/infra/forgejo-runner/values-rust.yaml | 3 +++ k8s/infra/forgejo-runner/values.yaml | 4 ++-- 4 files changed, 29 insertions(+), 2 deletions(-) diff --git a/k8s/argocd/apps/30-security.yaml b/k8s/argocd/apps/30-security.yaml index 53e9d96..a1d4d3d 100644 --- a/k8s/argocd/apps/30-security.yaml +++ b/k8s/argocd/apps/30-security.yaml @@ -152,6 +152,13 @@ metadata: namespace: argocd annotations: argocd.argoproj.io/sync-wave: "3" + argocd-image-updater.argoproj.io/image-list: runner=forgejo.riotpiao.com/rock/forgejo-runner-golang + argocd-image-updater.argoproj.io/runner.update-strategy: newest-build + argocd-image-updater.argoproj.io/runner.allow-tags: regexp:^[0-9a-f]{7}$|^latest$|^v[0-9]+$ + argocd-image-updater.argoproj.io/runner.helm.image-name: runner.image.repository + argocd-image-updater.argoproj.io/runner.helm.image-tag: runner.image.tag + argocd-image-updater.argoproj.io/write-back-method: git + argocd-image-updater.argoproj.io/git-branch: main spec: project: homelab source: @@ -173,6 +180,13 @@ metadata: namespace: argocd annotations: argocd.argoproj.io/sync-wave: "3" + argocd-image-updater.argoproj.io/image-list: runner=forgejo.riotpiao.com/rock/forgejo-runner-node + argocd-image-updater.argoproj.io/runner.update-strategy: newest-build + argocd-image-updater.argoproj.io/runner.allow-tags: regexp:^[0-9a-f]{7}$|^latest$|^v[0-9]+$ + argocd-image-updater.argoproj.io/runner.helm.image-name: runner.image.repository + argocd-image-updater.argoproj.io/runner.helm.image-tag: runner.image.tag + argocd-image-updater.argoproj.io/write-back-method: git + argocd-image-updater.argoproj.io/git-branch: main spec: project: homelab source: @@ -197,6 +211,13 @@ metadata: namespace: argocd annotations: argocd.argoproj.io/sync-wave: "3" + argocd-image-updater.argoproj.io/image-list: runner=forgejo.riotpiao.com/rock/forgejo-runner-rust + argocd-image-updater.argoproj.io/runner.update-strategy: newest-build + argocd-image-updater.argoproj.io/runner.allow-tags: regexp:^[0-9a-f]{7}$|^latest$|^v[0-9]+$ + argocd-image-updater.argoproj.io/runner.helm.image-name: runner.image.repository + argocd-image-updater.argoproj.io/runner.helm.image-tag: runner.image.tag + argocd-image-updater.argoproj.io/write-back-method: git + argocd-image-updater.argoproj.io/git-branch: main spec: project: homelab source: diff --git a/k8s/infra/forgejo-runner/values-node.yaml b/k8s/infra/forgejo-runner/values-node.yaml index 9c130e3..3cd17af 100644 --- a/k8s/infra/forgejo-runner/values-node.yaml +++ b/k8s/infra/forgejo-runner/values-node.yaml @@ -5,6 +5,9 @@ # node:22-bookworm ships Node natively. Docker client installed via workflow step if needed. # (homelab has no CI; custom runner images built manually if desired) runner: + image: + repository: forgejo.riotpiao.com/rock/forgejo-runner-node + tag: "latest" # Image Updater tracks and updates this name: node-runner # Custom image: node:22-bookworm + docker.io pre-installed labels: "node:docker://forgejo.riotpiao.com/rock/forgejo-runner-node:latest" diff --git a/k8s/infra/forgejo-runner/values-rust.yaml b/k8s/infra/forgejo-runner/values-rust.yaml index bd05dbc..e296cef 100644 --- a/k8s/infra/forgejo-runner/values-rust.yaml +++ b/k8s/infra/forgejo-runner/values-rust.yaml @@ -8,6 +8,9 @@ # - Rust toolchain (via rustup) # - Build essentials runner: + image: + repository: forgejo.riotpiao.com/rock/forgejo-runner-rust + tag: "latest" # Image Updater tracks and updates this name: rust-runner labels: "rust:docker://forgejo.riotpiao.com/rock/forgejo-runner-rust:latest" diff --git a/k8s/infra/forgejo-runner/values.yaml b/k8s/infra/forgejo-runner/values.yaml index 0f1587b..8098826 100644 --- a/k8s/infra/forgejo-runner/values.yaml +++ b/k8s/infra/forgejo-runner/values.yaml @@ -1,7 +1,7 @@ runner: image: - repository: code.forgejo.org/forgejo/runner - tag: "6" # pin exact release before apply + repository: forgejo.riotpiao.com/rock/forgejo-runner-golang + tag: "latest" # Image Updater tracks and updates this name: golang-runner # Custom image: docker:27-cli + Node.js + Go + build tools # - Node.js for GitHub Actions (actions/checkout@v4, etc.) -- 2.54.0 From 0e02cafdd70442c5c7144b65836b05e50b74f4e6 Mon Sep 17 00:00:00 2001 From: rock Date: Sat, 5 Sep 2026 23:19:22 -0700 Subject: [PATCH 04/20] ci: auto-build runner images on Dockerfile changes - Watches Dockerfile.golang, .rust, .node for changes - Builds and pushes images to Forgejo registry with commit SHA + latest tags - Image Updater detects new images automatically - ArgoCD syncs updated image tags - Workflow runs on: golang (has docker + dind) --- .gitea/workflows/build-runner-images.yml | 67 ++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 .gitea/workflows/build-runner-images.yml diff --git a/.gitea/workflows/build-runner-images.yml b/.gitea/workflows/build-runner-images.yml new file mode 100644 index 0000000..9be5692 --- /dev/null +++ b/.gitea/workflows/build-runner-images.yml @@ -0,0 +1,67 @@ +name: Build and push runner images + +on: + push: + paths: + - 'k8s/infra/forgejo-runner/Dockerfile.golang' + - 'k8s/infra/forgejo-runner/Dockerfile.rust' + - 'k8s/infra/forgejo-runner/Dockerfile.node' + branches: + - main + +jobs: + build-runners: + runs-on: golang + env: + REGISTRY: forgejo.riotpiao.com + IMAGE_BASE: forgejo.riotpiao.com/rock + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Get short SHA + id: sha + run: | + SHORT_SHA=$(git rev-parse --short HEAD) + echo "short_sha=${SHORT_SHA}" >> $GITHUB_OUTPUT + + - name: Registry login + run: | + echo "${REGISTRY_TOKEN}" | docker login "${REGISTRY}" \ + --username "${REGISTRY_USER}" --password-stdin + env: + REGISTRY_USER: ${{ secrets.FORGEJO_REGISTRY_USER }} + REGISTRY_TOKEN: ${{ secrets.FORGEJO_REGISTRY_TOKEN }} + + - name: Build and push golang-runner + if: contains(github.event.head_commit.modified, 'Dockerfile.golang') + run: | + docker build -f k8s/infra/forgejo-runner/Dockerfile.golang \ + -t "${IMAGE_BASE}/forgejo-runner-golang:${{ steps.sha.outputs.short_sha }}" \ + -t "${IMAGE_BASE}/forgejo-runner-golang:latest" \ + . + docker push "${IMAGE_BASE}/forgejo-runner-golang:${{ steps.sha.outputs.short_sha }}" + docker push "${IMAGE_BASE}/forgejo-runner-golang:latest" + echo "✓ Pushed golang-runner" + + - name: Build and push rust-runner + if: contains(github.event.head_commit.modified, 'Dockerfile.rust') + run: | + docker build -f k8s/infra/forgejo-runner/Dockerfile.rust \ + -t "${IMAGE_BASE}/forgejo-runner-rust:${{ steps.sha.outputs.short_sha }}" \ + -t "${IMAGE_BASE}/forgejo-runner-rust:latest" \ + . + docker push "${IMAGE_BASE}/forgejo-runner-rust:${{ steps.sha.outputs.short_sha }}" + docker push "${IMAGE_BASE}/forgejo-runner-rust:latest" + echo "✓ Pushed rust-runner" + + - name: Build and push node-runner + if: contains(github.event.head_commit.modified, 'Dockerfile.node') + run: | + docker build -f k8s/infra/forgejo-runner/Dockerfile.node \ + -t "${IMAGE_BASE}/forgejo-runner-node:${{ steps.sha.outputs.short_sha }}" \ + -t "${IMAGE_BASE}/forgejo-runner-node:latest" \ + . + docker push "${IMAGE_BASE}/forgejo-runner-node:${{ steps.sha.outputs.short_sha }}" + docker push "${IMAGE_BASE}/forgejo-runner-node:latest" + echo "✓ Pushed node-runner" -- 2.54.0 From caf0b5bfe6edcda437a8d6b1c6fb7598b056599e Mon Sep 17 00:00:00 2001 From: rock Date: Sat, 5 Sep 2026 23:31:08 -0700 Subject: [PATCH 05/20] fix: move runner CI workflow to .forgejo (Forgejo reads from .forgejo, not .gitea) - Workflow monitors Dockerfile.golang/rust/node for changes - Builds and pushes images with commit SHA + latest tags - Image Updater auto-detects new images - ArgoCD syncs new versions --- {.gitea => .forgejo}/workflows/build-runner-images.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {.gitea => .forgejo}/workflows/build-runner-images.yml (100%) diff --git a/.gitea/workflows/build-runner-images.yml b/.forgejo/workflows/build-runner-images.yml similarity index 100% rename from .gitea/workflows/build-runner-images.yml rename to .forgejo/workflows/build-runner-images.yml -- 2.54.0 From b098d6a4734d1644c240f5aa8e769b5c6c656a72 Mon Sep 17 00:00:00 2001 From: rock Date: Sat, 5 Sep 2026 23:31:46 -0700 Subject: [PATCH 06/20] fix: runner CI workflow in .gitea (correct Forgejo folder) - Forgejo reads workflows from .gitea/, not .forgejo/ - Workflow monitors Dockerfile.golang/rust/node for changes - Builds and pushes images with commit SHA + latest tags - Image Updater auto-detects new images - ArgoCD syncs new versions --- {.forgejo => .gitea}/workflows/build-runner-images.yml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {.forgejo => .gitea}/workflows/build-runner-images.yml (100%) diff --git a/.forgejo/workflows/build-runner-images.yml b/.gitea/workflows/build-runner-images.yml similarity index 100% rename from .forgejo/workflows/build-runner-images.yml rename to .gitea/workflows/build-runner-images.yml -- 2.54.0 From 4d60684ca0da80ce7197a048489d2150bb353e53 Mon Sep 17 00:00:00 2001 From: rock Date: Sat, 5 Sep 2026 23:33:36 -0700 Subject: [PATCH 07/20] chore: remove .forgejo (Forgejo uses .gitea) --- .forgejo/workflows/cluster-ci.yaml | 269 ----------------------------- 1 file changed, 269 deletions(-) delete mode 100644 .forgejo/workflows/cluster-ci.yaml diff --git a/.forgejo/workflows/cluster-ci.yaml b/.forgejo/workflows/cluster-ci.yaml deleted file mode 100644 index 916619e..0000000 --- a/.forgejo/workflows/cluster-ci.yaml +++ /dev/null @@ -1,269 +0,0 @@ -name: Cluster CI Pipeline - -on: - push: - branches: - - main - - develop - paths: - - 'k8s/**' - - '.forgejo/workflows/cluster-ci.yaml' - pull_request: - paths: - - 'k8s/**' - -jobs: - ci: - runs-on: docker - steps: - # === Checkout === - - name: Checkout - run: | - REPO_URL="${{ gitea.server_url }}/${{ gitea.repository }}.git" - CLONE_URL="https://${{ secrets.CI_RUNNER }}:${{ secrets.CI_RUNNER_SECRET }}@${REPO_URL#https://}" - git clone --depth 1 "$CLONE_URL" . - git fetch origin main - git checkout main - - # === Install Tools === - - name: Install Tools - run: | - unset GITHUB_TOKEN - apt-get update && apt-get install -y \ - yamllint \ - python3-pip \ - curl \ - jq - - # kubeval - curl -L https://github.com/instrumenta/kubeval/releases/latest/download/kubeval-linux-amd64.tar.gz | tar xz - mv -f kubeval /usr/local/bin/ - - # kustomize - rm -f kustomize - curl -s https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh | bash - mv -f kustomize /usr/local/bin/ - - # argocd - curl -sSL -o /usr/local/bin/argocd https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64 - chmod +x /usr/local/bin/argocd - - # trivy - curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin - - # polaris - curl -L https://github.com/FairwindsOps/polaris/releases/latest/download/polaris-linux-amd64 -o /usr/local/bin/polaris - chmod +x /usr/local/bin/polaris - - # === YAML Lint === - - name: YAML Lint - run: | - echo "=== Linting YAML files ===" - yamllint k8s/ -c .yamllint.yaml || true - - # === Kubeval - Validate K8s Syntax === - - name: Kubeval - Validate K8s Syntax - run: | - echo "=== Validating Kubernetes manifests ===" - find k8s -name "*.yaml" -o -name "*.yml" | grep -v "\.archive" | while read file; do - echo "Validating $file..." - kubeval "$file" -d 2>/dev/null || true - done - - # === Kustomize Build - All overlays === - - name: Kustomize Build - Infrastructure - run: | - echo "=== Building k8s/infrastructure/ ===" - kustomize build k8s/infrastructure > /tmp/infrastructure.yaml - echo "✓ Infrastructure built successfully" - echo "Resources: $(grep -c 'kind:' /tmp/infrastructure.yaml)" - - - name: Kustomize Build - Bootstrap - run: | - echo "=== Building k8s/bootstrap/ ===" - kustomize build k8s/bootstrap > /tmp/bootstrap.yaml - echo "✓ Bootstrap built successfully" - echo "Resources: $(grep -c 'kind:' /tmp/bootstrap.yaml || echo 0)" - - - name: Kustomize Build - Platform - run: | - echo "=== Building k8s/platform/ ===" - kustomize build k8s/platform > /tmp/platform.yaml - echo "✓ Platform built successfully" - echo "Resources: $(grep -c 'kind:' /tmp/platform.yaml || echo 0)" - - - name: Kustomize Build - Security - run: | - echo "=== Building k8s/security/ ===" - kustomize build k8s/security > /tmp/security.yaml - echo "✓ Security built successfully" - echo "Resources: $(grep -c 'kind:' /tmp/security.yaml || echo 0)" - - - name: Kustomize Build - Applications - run: | - echo "=== Building k8s/applications/ ===" - kustomize build k8s/applications > /tmp/applications.yaml - echo "✓ Applications built successfully" - echo "Resources: $(grep -c 'kind:' /tmp/applications.yaml || echo 0)" - - - name: Kustomize Build - Data - run: | - echo "=== Building k8s/data/ ===" - kustomize build k8s/data > /tmp/data.yaml - echo "✓ Data built successfully" - echo "Resources: $(grep -c 'kind:' /tmp/data.yaml || echo 0)" - - - name: Validate ArgoCD Applications - run: | - echo "=== Validating ArgoCD Applications ===" - kubeval k8s/argocd/apps/*.yaml - - # === Trivy - Scan Dockerfile === - - name: Trivy - Scan Dockerfile - run: | - if find . -name "Dockerfile" 2>/dev/null | grep -v node_modules | head -1 | grep -q .; then - echo "=== Scanning Dockerfiles with Trivy ===" - find . -name "Dockerfile" -not -path "*/node_modules/*" -exec trivy config {} \; - else - echo "No Dockerfiles found" - fi - - # === Trivy - Scan Helm Charts === - - name: Trivy - Scan Helm Charts - run: | - if find k8s -name "Chart.yaml" 2>/dev/null | head -1 | grep -q .; then - echo "=== Scanning Helm charts with Trivy ===" - find k8s -name "Chart.yaml" -exec dirname {} \; | while read chart; do - echo "Scanning $chart..." - trivy config "$chart" || true - done - else - echo "No Helm charts found" - fi - - # === Polaris - K8s Security Audit === - - name: Polaris - K8s Security Audit - run: | - echo "=== Running Polaris K8s security audit ===" - polaris audit --audit-path /tmp/polaris-audit.json k8s/ || true - - if [ -f /tmp/polaris-audit.json ]; then - echo "Security issues found:" - jq '.results[] | select(.pass == false)' /tmp/polaris-audit.json || true - fi - - # === Check for Secrets in Code === - - name: Check for Secrets in Code - run: | - echo "=== Scanning for hardcoded secrets ===" - # BLOCKING. This step used to only count findings and then exit 0, so a - # plaintext deploy key rode through it into a public remote. Two failure - # modes fixed: it now fails the build, and it matches key material by - # PEM header rather than only `private_key:`-style YAML field names. - # Findings are captured into variables and tested for emptiness rather than - # branching on grep's exit status: implementations disagree on the rc of a - # `-v` filter fed empty input, and a wrong rc here fails open. - # NOTE: --include must precede `--`; after `--` grep treats it as a filename - # and silently scans nothing. - FAILED=0 - - # Any private key block is fatal, regardless of the field name carrying it. - KEYS=$(grep -rIE --include="*.yaml" --include="*.yml" \ - -- "-----BEGIN ([A-Z]+ )?PRIVATE KEY-----" k8s/ \ - | grep -v "\.enc\.yaml" || true) - if [ -n "$KEYS" ]; then - echo "❌ Unencrypted private key material found:" - echo "$KEYS" - FAILED=1 - fi - - # Plaintext values in secret-ish YAML fields. SOPS output is ENC[...], - # so encrypted files never trip this. - VALS=$(grep -rInE --include="*.yaml" --include="*.yml" \ - -- "^[[:space:]]*(password|token|apiKey|api_key|sshPrivateKey|client_secret):[[:space:]]*[\"']?[^\"'[:space:]{\$]{8,}" k8s/ \ - | grep -v "ENC\[" | grep -v "\.enc\.yaml" || true) - if [ -n "$VALS" ]; then - echo "❌ Plaintext secret value found:" - echo "$VALS" - FAILED=1 - fi - - if [ "$FAILED" -ne 0 ]; then - echo "Encrypt with SOPS (see .sops.yaml) — *.enc.yaml files are exempt." - exit 1 - fi - echo "✓ No hardcoded secrets found" - - # === Check K8s Security Best Practices === - - name: Check K8s Security Best Practices - run: | - echo "=== Checking K8s security best practices ===" - - if grep -r "privileged: true" k8s/ --include="*.yaml" --include="*.yml"; then - echo "⚠️ Found privileged containers" - fi - - if grep -r "hostNetwork: true" k8s/ --include="*.yaml" --include="*.yml"; then - echo "⚠️ Found hostNetwork usage" - fi - - echo "Checking for missing resource limits..." - MISSING=0 - find k8s -name "*.yaml" -o -name "*.yml" | while read file; do - if grep -q "kind: Deployment\|kind: StatefulSet\|kind: DaemonSet" "$file"; then - if ! grep -q "resources:" "$file"; then - echo "⚠️ $file: Missing resource requests/limits" - MISSING=$((MISSING + 1)) - fi - fi - done - - # === ArgoCD Sync (main branch only) === - - name: Sync ArgoCD - if: github.ref == 'refs/heads/main' && github.event_name == 'push' - env: - ARGOCD_SERVER: ${{ secrets.ARGOCD_SERVER }} - ARGOCD_AUTH_TOKEN: ${{ secrets.ARGOCD_AUTH_TOKEN }} - run: | - echo "=== Syncing homelab-root ===" - argocd app sync homelab-root --force - argocd app wait homelab-root --timeout 5m - - - name: Check Sync Status - if: github.ref == 'refs/heads/main' && github.event_name == 'push' - env: - ARGOCD_SERVER: ${{ secrets.ARGOCD_SERVER }} - ARGOCD_AUTH_TOKEN: ${{ secrets.ARGOCD_AUTH_TOKEN }} - run: | - echo "=== ArgoCD Applications Status ===" - argocd app list -o table - - STATUS=$(argocd app get homelab-root -o jsonpath='{.status.syncStatus}') - if [ "$STATUS" != "Synced" ]; then - echo "❌ Root app sync failed: $STATUS" - exit 1 - fi - echo "✓ Root app synced successfully" - - - name: Health Check - if: github.ref == 'refs/heads/main' && github.event_name == 'push' - env: - ARGOCD_SERVER: ${{ secrets.ARGOCD_SERVER }} - ARGOCD_AUTH_TOKEN: ${{ secrets.ARGOCD_AUTH_TOKEN }} - run: | - echo "=== Checking Application Health ===" - argocd app get homelab-root -o wide - - # === Summary === - - name: Summary - if: always() - run: | - echo "=== CI Pipeline Summary ===" - echo "✓ YAML linted" - echo "✓ Manifests validated" - echo "✓ Kustomizations built" - echo "✓ Security scans completed" - echo "✓ Secrets check passed" - echo "✓ Best practices verified" - echo "" - echo "✓ All checks passed" -- 2.54.0 From 39e2ed504c21b2363a9f17842e1f9b6322dbeb94 Mon Sep 17 00:00:00 2001 From: rock Date: Sat, 5 Sep 2026 23:39:20 -0700 Subject: [PATCH 08/20] bootstrap: use base images for runners, custom images via CI - golang-runner: docker:27-cli (has Node.js + docker) - rust-runner: docker:27-cli (bootstrap, CI adds Rust) - node-runner: node:22-bookworm (has Node.js, CI adds docker) CI workflow (.gitea/workflows/build-runner-images.yml): 1. Watches Dockerfile.* changes 2. Builds custom images with Node.js pre-installed 3. Pushes to registry 4. Image Updater detects and updates values.yaml 5. ArgoCD syncs to new custom images This avoids token complexity - just push Dockerfile changes to git! --- k8s/infra/forgejo-runner/values-node.yaml | 8 ++++---- k8s/infra/forgejo-runner/values-rust.yaml | 12 ++++-------- k8s/infra/forgejo-runner/values.yaml | 13 +++---------- 3 files changed, 11 insertions(+), 22 deletions(-) diff --git a/k8s/infra/forgejo-runner/values-node.yaml b/k8s/infra/forgejo-runner/values-node.yaml index 3cd17af..a8dea01 100644 --- a/k8s/infra/forgejo-runner/values-node.yaml +++ b/k8s/infra/forgejo-runner/values-node.yaml @@ -4,13 +4,13 @@ # # node:22-bookworm ships Node natively. Docker client installed via workflow step if needed. # (homelab has no CI; custom runner images built manually if desired) +# Bootstrap with base image, custom image via CI+Image Updater runner: image: - repository: forgejo.riotpiao.com/rock/forgejo-runner-node - tag: "latest" # Image Updater tracks and updates this + repository: node + tag: "22-bookworm" name: node-runner - # Custom image: node:22-bookworm + docker.io pre-installed - labels: "node:docker://forgejo.riotpiao.com/rock/forgejo-runner-node:latest" + labels: "node:docker://node:22-bookworm" # GC CronJob renders only from the default (golang) values to avoid duplicates gc: diff --git a/k8s/infra/forgejo-runner/values-rust.yaml b/k8s/infra/forgejo-runner/values-rust.yaml index e296cef..10956d2 100644 --- a/k8s/infra/forgejo-runner/values-rust.yaml +++ b/k8s/infra/forgejo-runner/values-rust.yaml @@ -2,17 +2,13 @@ # runner instance. Only runner.name and runner.labels differ -- everything # else (image, dind, persistence, tolerations, nodeSelector) is shared. # -# Custom image: docker:27-cli + Node.js + Rust + build tools -# - Node.js for GitHub Actions (actions/checkout@v4, etc.) -# - Docker CLI for container builds -# - Rust toolchain (via rustup) -# - Build essentials +# Bootstrap with base image, custom image via CI+Image Updater runner: image: - repository: forgejo.riotpiao.com/rock/forgejo-runner-rust - tag: "latest" # Image Updater tracks and updates this + repository: docker + tag: "27-cli" name: rust-runner - labels: "rust:docker://forgejo.riotpiao.com/rock/forgejo-runner-rust:latest" + labels: "rust:docker://docker:27-cli" diff --git a/k8s/infra/forgejo-runner/values.yaml b/k8s/infra/forgejo-runner/values.yaml index 8098826..db9844d 100644 --- a/k8s/infra/forgejo-runner/values.yaml +++ b/k8s/infra/forgejo-runner/values.yaml @@ -1,17 +1,10 @@ runner: image: - repository: forgejo.riotpiao.com/rock/forgejo-runner-golang - tag: "latest" # Image Updater tracks and updates this + repository: docker + tag: "27-cli" # Bootstrap with base image, custom image via CI+Image Updater name: golang-runner - # Custom image: docker:27-cli + Node.js + Go + build tools - # - Node.js for GitHub Actions (actions/checkout@v4, etc.) - # - Docker CLI for container builds - # - Go toolchain - # - Build essentials - labels: "golang:docker://forgejo.riotpiao.com/rock/forgejo-runner-golang:latest" + labels: "golang:docker://docker:27-cli" forgejoUrl: http://forgejo-gitea-http.cicd.svc.cluster.local:3000 - # tokenSecret: name of the K8s Secret that holds the runner registration token - # created automatically by the helmfile presync hook (see helmfile.yaml.gotmpl) tokenSecret: runner-token resources: requests: -- 2.54.0 From 709b8b7039a4e34b65f3821134338ceb6c481101 Mon Sep 17 00:00:00 2001 From: rock Date: Sat, 5 Sep 2026 23:43:21 -0700 Subject: [PATCH 09/20] fix: use forgejo/runner as base image, add Node.js on top - All runners now based on code.forgejo.org/forgejo/runner:6 (has runner binary) - Dockerfile adds Node.js + specialized tools (Go, Rust, docker) - CI workflow will build custom images and push to registry - Image Updater will auto-detect and update values.yaml - ArgoCD will sync new custom images when available --- k8s/infra/forgejo-runner/Dockerfile.golang | 11 +++-------- k8s/infra/forgejo-runner/Dockerfile.node | 15 +++------------ k8s/infra/forgejo-runner/Dockerfile.rust | 16 ++++++---------- k8s/infra/forgejo-runner/values-node.yaml | 8 ++++---- k8s/infra/forgejo-runner/values-rust.yaml | 8 ++++---- k8s/infra/forgejo-runner/values.yaml | 6 +++--- 6 files changed, 23 insertions(+), 41 deletions(-) diff --git a/k8s/infra/forgejo-runner/Dockerfile.golang b/k8s/infra/forgejo-runner/Dockerfile.golang index 7c80956..cca9574 100644 --- a/k8s/infra/forgejo-runner/Dockerfile.golang +++ b/k8s/infra/forgejo-runner/Dockerfile.golang @@ -1,15 +1,10 @@ -FROM docker:27-cli +FROM code.forgejo.org/forgejo/runner:6 -# Install Node.js + build essentials (needed for GitHub Actions checkout@v4, etc.) +# Install Node.js (needed for GitHub Actions checkout@v4, etc.) RUN apt-get update && \ apt-get install -y --no-install-recommends \ nodejs \ - npm \ - git \ - curl \ - ca-certificates \ - build-essential \ - python3 && \ + npm && \ rm -rf /var/lib/apt/lists/* # Verify installations diff --git a/k8s/infra/forgejo-runner/Dockerfile.node b/k8s/infra/forgejo-runner/Dockerfile.node index 4675233..cdc3093 100644 --- a/k8s/infra/forgejo-runner/Dockerfile.node +++ b/k8s/infra/forgejo-runner/Dockerfile.node @@ -1,13 +1,4 @@ -FROM node:22-bookworm +FROM code.forgejo.org/forgejo/runner:6 -# Install Docker client (dind sidecar provides the daemon) -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - docker.io \ - git \ - curl \ - ca-certificates && \ - rm -rf /var/lib/apt/lists/* - -# Verify installations -RUN docker --version && node --version && git --version +# Node.js is already in forgejo/runner, just verify +RUN node --version && docker --version && git --version diff --git a/k8s/infra/forgejo-runner/Dockerfile.rust b/k8s/infra/forgejo-runner/Dockerfile.rust index 59d66bf..621b301 100644 --- a/k8s/infra/forgejo-runner/Dockerfile.rust +++ b/k8s/infra/forgejo-runner/Dockerfile.rust @@ -1,20 +1,16 @@ -FROM docker:27-cli +FROM code.forgejo.org/forgejo/runner:6 -# Install Node.js + build essentials (needed for GitHub Actions checkout@v4, etc.) +# Install Node.js + Rust (needed for GitHub Actions checkout@v4, etc.) RUN apt-get update && \ apt-get install -y --no-install-recommends \ nodejs \ - npm \ - git \ - curl \ - ca-certificates \ - build-essential \ - python3 && \ + npm && \ rm -rf /var/lib/apt/lists/* # Install Rust -RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y && \ - . $HOME/.cargo/env +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y + +ENV PATH="/root/.cargo/bin:${PATH}" # Verify installations RUN docker --version && node --version && git --version && rustc --version diff --git a/k8s/infra/forgejo-runner/values-node.yaml b/k8s/infra/forgejo-runner/values-node.yaml index a8dea01..2015d39 100644 --- a/k8s/infra/forgejo-runner/values-node.yaml +++ b/k8s/infra/forgejo-runner/values-node.yaml @@ -4,13 +4,13 @@ # # node:22-bookworm ships Node natively. Docker client installed via workflow step if needed. # (homelab has no CI; custom runner images built manually if desired) -# Bootstrap with base image, custom image via CI+Image Updater +# Bootstrap with runner image (already has Node.js), CI builds custom runner: image: - repository: node - tag: "22-bookworm" + repository: code.forgejo.org/forgejo/runner + tag: "6" name: node-runner - labels: "node:docker://node:22-bookworm" + labels: "node:docker://code.forgejo.org/forgejo/runner:6" # GC CronJob renders only from the default (golang) values to avoid duplicates gc: diff --git a/k8s/infra/forgejo-runner/values-rust.yaml b/k8s/infra/forgejo-runner/values-rust.yaml index 10956d2..3e56586 100644 --- a/k8s/infra/forgejo-runner/values-rust.yaml +++ b/k8s/infra/forgejo-runner/values-rust.yaml @@ -2,13 +2,13 @@ # runner instance. Only runner.name and runner.labels differ -- everything # else (image, dind, persistence, tolerations, nodeSelector) is shared. # -# Bootstrap with base image, custom image via CI+Image Updater +# Bootstrap with runner image, CI builds custom with Node.js+Rust runner: image: - repository: docker - tag: "27-cli" + repository: code.forgejo.org/forgejo/runner + tag: "6" name: rust-runner - labels: "rust:docker://docker:27-cli" + labels: "rust:docker://code.forgejo.org/forgejo/runner:6" diff --git a/k8s/infra/forgejo-runner/values.yaml b/k8s/infra/forgejo-runner/values.yaml index db9844d..71aea9d 100644 --- a/k8s/infra/forgejo-runner/values.yaml +++ b/k8s/infra/forgejo-runner/values.yaml @@ -1,9 +1,9 @@ runner: image: - repository: docker - tag: "27-cli" # Bootstrap with base image, custom image via CI+Image Updater + repository: code.forgejo.org/forgejo/runner + tag: "6" # Bootstrap with runner image, CI builds custom with Node.js name: golang-runner - labels: "golang:docker://docker:27-cli" + labels: "golang:docker://code.forgejo.org/forgejo/runner:6" forgejoUrl: http://forgejo-gitea-http.cicd.svc.cluster.local:3000 tokenSecret: runner-token resources: -- 2.54.0 From 6ecef14d0e08ecfe24d6d2a0cf19152abb4e89ad Mon Sep 17 00:00:00 2001 From: rock Date: Sun, 6 Sep 2026 05:53:26 -0700 Subject: [PATCH 10/20] fix: add docker.io to node-runner image --- k8s/infra/forgejo-runner/Dockerfile.node | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/k8s/infra/forgejo-runner/Dockerfile.node b/k8s/infra/forgejo-runner/Dockerfile.node index cdc3093..c40253a 100644 --- a/k8s/infra/forgejo-runner/Dockerfile.node +++ b/k8s/infra/forgejo-runner/Dockerfile.node @@ -1,4 +1,11 @@ FROM code.forgejo.org/forgejo/runner:6 -# Node.js is already in forgejo/runner, just verify +# Install Docker client (forgejo/runner base doesn't include it) +RUN apt-get update && \ + apt-get install -y --no-install-recommends \ + docker.io && \ + rm -rf /var/lib/apt/lists/* + +# Verify installations RUN node --version && docker --version && git --version +# Trigger node-runner build with docker -- 2.54.0 From 6b7e556d81862314216112a4aff214bf20aa303c Mon Sep 17 00:00:00 2001 From: rock Date: Sun, 6 Sep 2026 05:56:12 -0700 Subject: [PATCH 11/20] build: add docker.io to golang runner base image Pre-install docker.io so golang projects don't need to install on every build. Avoids duplicating 'apt-get install docker.io' in every CI workflow. --- k8s/infra/forgejo-runner/Dockerfile.golang | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/k8s/infra/forgejo-runner/Dockerfile.golang b/k8s/infra/forgejo-runner/Dockerfile.golang index cca9574..6e44dd7 100644 --- a/k8s/infra/forgejo-runner/Dockerfile.golang +++ b/k8s/infra/forgejo-runner/Dockerfile.golang @@ -1,10 +1,11 @@ FROM code.forgejo.org/forgejo/runner:6 -# Install Node.js (needed for GitHub Actions checkout@v4, etc.) +# Install Node.js + Docker client (needed for GitHub Actions + docker build) RUN apt-get update && \ apt-get install -y --no-install-recommends \ nodejs \ - npm && \ + npm \ + docker.io && \ rm -rf /var/lib/apt/lists/* # Verify installations -- 2.54.0 From 913cfc2f40b74c637e9c58a5b29cc73948b168bb Mon Sep 17 00:00:00 2001 From: rock Date: Mon, 7 Sep 2026 05:19:39 +0000 Subject: [PATCH 12/20] fix: add CoreDNS api.riotpiao.com rewrite + fix runner Alpine base image (#1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Forgejo CI is broken across all repos Every workflow fails because runner labels point to a bare Alpine image with nothing in it. ┌────────────────────────────────────────┬──────────────────────────────────────┐ │ Before │ After │ ├────────────────────────────────────────┼──────────────────────────────────────┤ │ golang:docker://forgejo/runner:6 │ golang:docker://golang:1.26-bookworm │ ├────────────────────────────────────────┼──────────────────────────────────────┤ │ No Go, no Node.js, no apt-get, no root │ Go, git, apt-get, root │ └────────────────────────────────────────┴──────────────────────────────────────┘ Plus the docker socket isn't shared between dind sidecar and runner, so even if docker CLI existed, it can't reach the daemon. ┌───────────────────────────────────────────┬─────────────────────────────────────────────────────────────┐ │ Before │ After │ ├───────────────────────────────────────────┼─────────────────────────────────────────────────────────────┤ │ dind creates socket in its own filesystem │ Shared /run emptyDir volume │ ├───────────────────────────────────────────┼─────────────────────────────────────────────────────────────┤ │ Runner can't see it │ Both containers see /run/docker.sock │ ├───────────────────────────────────────────┼─────────────────────────────────────────────────────────────┤ │ No docker_host config │ docker_host: automount passes socket to workflow containers │ Co-authored-by: rock --- .gitea/workflows/build-runner-images.yml | 67 ------------------- k8s/infra/forgejo-runner/Dockerfile.golang | 12 ---- k8s/infra/forgejo-runner/Dockerfile.node | 11 --- k8s/infra/forgejo-runner/Dockerfile.rust | 16 ----- .../forgejo-runner/templates/configmap.yaml | 1 + .../forgejo-runner/templates/deployment.yaml | 6 ++ k8s/infra/forgejo-runner/values-node.yaml | 7 +- k8s/infra/forgejo-runner/values-rust.yaml | 5 +- k8s/infra/forgejo-runner/values.yaml | 7 +- terraform/files/coredns/Corefile | 1 + 10 files changed, 19 insertions(+), 114 deletions(-) delete mode 100644 .gitea/workflows/build-runner-images.yml delete mode 100644 k8s/infra/forgejo-runner/Dockerfile.golang delete mode 100644 k8s/infra/forgejo-runner/Dockerfile.node delete mode 100644 k8s/infra/forgejo-runner/Dockerfile.rust diff --git a/.gitea/workflows/build-runner-images.yml b/.gitea/workflows/build-runner-images.yml deleted file mode 100644 index 9be5692..0000000 --- a/.gitea/workflows/build-runner-images.yml +++ /dev/null @@ -1,67 +0,0 @@ -name: Build and push runner images - -on: - push: - paths: - - 'k8s/infra/forgejo-runner/Dockerfile.golang' - - 'k8s/infra/forgejo-runner/Dockerfile.rust' - - 'k8s/infra/forgejo-runner/Dockerfile.node' - branches: - - main - -jobs: - build-runners: - runs-on: golang - env: - REGISTRY: forgejo.riotpiao.com - IMAGE_BASE: forgejo.riotpiao.com/rock - steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Get short SHA - id: sha - run: | - SHORT_SHA=$(git rev-parse --short HEAD) - echo "short_sha=${SHORT_SHA}" >> $GITHUB_OUTPUT - - - name: Registry login - run: | - echo "${REGISTRY_TOKEN}" | docker login "${REGISTRY}" \ - --username "${REGISTRY_USER}" --password-stdin - env: - REGISTRY_USER: ${{ secrets.FORGEJO_REGISTRY_USER }} - REGISTRY_TOKEN: ${{ secrets.FORGEJO_REGISTRY_TOKEN }} - - - name: Build and push golang-runner - if: contains(github.event.head_commit.modified, 'Dockerfile.golang') - run: | - docker build -f k8s/infra/forgejo-runner/Dockerfile.golang \ - -t "${IMAGE_BASE}/forgejo-runner-golang:${{ steps.sha.outputs.short_sha }}" \ - -t "${IMAGE_BASE}/forgejo-runner-golang:latest" \ - . - docker push "${IMAGE_BASE}/forgejo-runner-golang:${{ steps.sha.outputs.short_sha }}" - docker push "${IMAGE_BASE}/forgejo-runner-golang:latest" - echo "✓ Pushed golang-runner" - - - name: Build and push rust-runner - if: contains(github.event.head_commit.modified, 'Dockerfile.rust') - run: | - docker build -f k8s/infra/forgejo-runner/Dockerfile.rust \ - -t "${IMAGE_BASE}/forgejo-runner-rust:${{ steps.sha.outputs.short_sha }}" \ - -t "${IMAGE_BASE}/forgejo-runner-rust:latest" \ - . - docker push "${IMAGE_BASE}/forgejo-runner-rust:${{ steps.sha.outputs.short_sha }}" - docker push "${IMAGE_BASE}/forgejo-runner-rust:latest" - echo "✓ Pushed rust-runner" - - - name: Build and push node-runner - if: contains(github.event.head_commit.modified, 'Dockerfile.node') - run: | - docker build -f k8s/infra/forgejo-runner/Dockerfile.node \ - -t "${IMAGE_BASE}/forgejo-runner-node:${{ steps.sha.outputs.short_sha }}" \ - -t "${IMAGE_BASE}/forgejo-runner-node:latest" \ - . - docker push "${IMAGE_BASE}/forgejo-runner-node:${{ steps.sha.outputs.short_sha }}" - docker push "${IMAGE_BASE}/forgejo-runner-node:latest" - echo "✓ Pushed node-runner" diff --git a/k8s/infra/forgejo-runner/Dockerfile.golang b/k8s/infra/forgejo-runner/Dockerfile.golang deleted file mode 100644 index 6e44dd7..0000000 --- a/k8s/infra/forgejo-runner/Dockerfile.golang +++ /dev/null @@ -1,12 +0,0 @@ -FROM code.forgejo.org/forgejo/runner:6 - -# Install Node.js + Docker client (needed for GitHub Actions + docker build) -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - nodejs \ - npm \ - docker.io && \ - rm -rf /var/lib/apt/lists/* - -# Verify installations -RUN docker --version && node --version && git --version diff --git a/k8s/infra/forgejo-runner/Dockerfile.node b/k8s/infra/forgejo-runner/Dockerfile.node deleted file mode 100644 index c40253a..0000000 --- a/k8s/infra/forgejo-runner/Dockerfile.node +++ /dev/null @@ -1,11 +0,0 @@ -FROM code.forgejo.org/forgejo/runner:6 - -# Install Docker client (forgejo/runner base doesn't include it) -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - docker.io && \ - rm -rf /var/lib/apt/lists/* - -# Verify installations -RUN node --version && docker --version && git --version -# Trigger node-runner build with docker diff --git a/k8s/infra/forgejo-runner/Dockerfile.rust b/k8s/infra/forgejo-runner/Dockerfile.rust deleted file mode 100644 index 621b301..0000000 --- a/k8s/infra/forgejo-runner/Dockerfile.rust +++ /dev/null @@ -1,16 +0,0 @@ -FROM code.forgejo.org/forgejo/runner:6 - -# Install Node.js + Rust (needed for GitHub Actions checkout@v4, etc.) -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - nodejs \ - npm && \ - rm -rf /var/lib/apt/lists/* - -# Install Rust -RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y - -ENV PATH="/root/.cargo/bin:${PATH}" - -# Verify installations -RUN docker --version && node --version && git --version && rustc --version diff --git a/k8s/infra/forgejo-runner/templates/configmap.yaml b/k8s/infra/forgejo-runner/templates/configmap.yaml index c73d992..30334e9 100644 --- a/k8s/infra/forgejo-runner/templates/configmap.yaml +++ b/k8s/infra/forgejo-runner/templates/configmap.yaml @@ -36,3 +36,4 @@ data: valid_volumes: - /docker-certs/client network: host + docker_host: automount diff --git a/k8s/infra/forgejo-runner/templates/deployment.yaml b/k8s/infra/forgejo-runner/templates/deployment.yaml index 6b11c26..f60ab36 100644 --- a/k8s/infra/forgejo-runner/templates/deployment.yaml +++ b/k8s/infra/forgejo-runner/templates/deployment.yaml @@ -70,6 +70,8 @@ spec: mountPath: /data - name: docker-certs mountPath: /docker-certs + - name: docker-sock + mountPath: /run - name: homelab-ca mountPath: /etc/ssl/certs/homelab-ca.pem subPath: ca.crt @@ -89,6 +91,8 @@ spec: volumeMounts: - name: docker-certs mountPath: /docker-certs + - name: docker-sock + mountPath: /run - name: dind-storage mountPath: /var/lib/docker - name: homelab-ca @@ -113,6 +117,8 @@ spec: claimName: {{ .Release.Name }}-dind - name: docker-certs emptyDir: {} # DinD regenerates mTLS certs on each start + - name: docker-sock + emptyDir: {} # Shared docker socket between dind and runner - name: homelab-ca # homelab-ca is a ConfigMap (public CA trust bundle), not a Secret. # The volumeMounts use subPath: ca.crt to project the single cert file. diff --git a/k8s/infra/forgejo-runner/values-node.yaml b/k8s/infra/forgejo-runner/values-node.yaml index 2015d39..e75a0aa 100644 --- a/k8s/infra/forgejo-runner/values-node.yaml +++ b/k8s/infra/forgejo-runner/values-node.yaml @@ -2,15 +2,14 @@ # runner instance. Only runner.name and runner.labels differ -- everything # else (image, dind, persistence, tolerations, nodeSelector) is shared. # -# node:22-bookworm ships Node natively. Docker client installed via workflow step if needed. -# (homelab has no CI; custom runner images built manually if desired) -# Bootstrap with runner image (already has Node.js), CI builds custom +# Label image: node:22-bookworm — Debian, root, apt-get, Node.js, npm, git. +# Install docker in workflow steps as needed. runner: image: repository: code.forgejo.org/forgejo/runner tag: "6" name: node-runner - labels: "node:docker://code.forgejo.org/forgejo/runner:6" + labels: "node:docker://node:22-bookworm" # GC CronJob renders only from the default (golang) values to avoid duplicates gc: diff --git a/k8s/infra/forgejo-runner/values-rust.yaml b/k8s/infra/forgejo-runner/values-rust.yaml index 3e56586..b645cf3 100644 --- a/k8s/infra/forgejo-runner/values-rust.yaml +++ b/k8s/infra/forgejo-runner/values-rust.yaml @@ -2,13 +2,14 @@ # runner instance. Only runner.name and runner.labels differ -- everything # else (image, dind, persistence, tolerations, nodeSelector) is shared. # -# Bootstrap with runner image, CI builds custom with Node.js+Rust +# Label image: rust:1-bookworm — Debian, root, apt-get, Rust, cargo, git. +# Install Node.js/docker in workflow steps as needed. runner: image: repository: code.forgejo.org/forgejo/runner tag: "6" name: rust-runner - labels: "rust:docker://code.forgejo.org/forgejo/runner:6" + labels: "rust:docker://rust:1-bookworm" diff --git a/k8s/infra/forgejo-runner/values.yaml b/k8s/infra/forgejo-runner/values.yaml index 71aea9d..adb07b0 100644 --- a/k8s/infra/forgejo-runner/values.yaml +++ b/k8s/infra/forgejo-runner/values.yaml @@ -1,9 +1,12 @@ runner: image: repository: code.forgejo.org/forgejo/runner - tag: "6" # Bootstrap with runner image, CI builds custom with Node.js + tag: "6" name: golang-runner - labels: "golang:docker://code.forgejo.org/forgejo/runner:6" + # Label image is what workflow steps run in (NOT the runner daemon image). + # golang:1.26-bookworm: Debian, root, apt-get, Go, git. + # Install Node.js/docker in workflow steps as needed. + labels: "golang:docker://golang:1.26-bookworm" forgejoUrl: http://forgejo-gitea-http.cicd.svc.cluster.local:3000 tokenSecret: runner-token resources: diff --git a/terraform/files/coredns/Corefile b/terraform/files/coredns/Corefile index 6df1c75..03b2fbf 100644 --- a/terraform/files/coredns/Corefile +++ b/terraform/files/coredns/Corefile @@ -35,6 +35,7 @@ rewrite name longhorn.riotpiao.com ingress-nginx-controller.ingress-nginx.svc.cluster.local rewrite name paperless.riotpiao.com ingress-nginx-controller.ingress-nginx.svc.cluster.local rewrite name img.riotpiao.com ingress-nginx-controller.ingress-nginx.svc.cluster.local + rewrite name api.riotpiao.com ingress-nginx-controller.ingress-nginx.svc.cluster.local rewrite name riotpiao.com ingress-nginx-controller.ingress-nginx.svc.cluster.local kubernetes cluster.local in-addr.arpa ip6.arpa { -- 2.54.0 From 326be2e057b60dd91d2116f2fa4c3836babc5da4 Mon Sep 17 00:00:00 2001 From: rock Date: Mon, 7 Sep 2026 05:27:30 +0000 Subject: [PATCH 13/20] fix: always re-register runner to keep labels in sync (#2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Problem Init container skips registration if `.runner` file exists on PVC: ``` test -f /data/.runner || forgejo-runner register ... ``` This means changing runner labels in `values.yaml` (e.g. the label image fix from PR #1) has **no effect** until PVCs are manually deleted — not GitOps-friendly. ## Fix Always delete `.runner` and re-register on every pod start: ``` rm -f /data/.runner forgejo-runner register --no-interactive ... ``` Labels now stay in sync with `values.yaml` automatically. ArgoCD syncs → pods restart → init re-registers with current labels. ## Files Changed - `k8s/infra/forgejo-runner/templates/deployment.yaml` (init container logic) ## After Merge ArgoCD syncs → deployment spec changes → pods restart → init re-registers with new labels from PR #1 → CI works across all repos.Reviewed-on: https://forgejo.riotpiao.com/rock/homelab/pulls/2 Co-authored-by: rock --- k8s/infra/forgejo-runner/templates/deployment.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/k8s/infra/forgejo-runner/templates/deployment.yaml b/k8s/infra/forgejo-runner/templates/deployment.yaml index f60ab36..2ef614f 100644 --- a/k8s/infra/forgejo-runner/templates/deployment.yaml +++ b/k8s/infra/forgejo-runner/templates/deployment.yaml @@ -34,7 +34,11 @@ spec: command: ["sh", "-c"] args: - | - test -f /data/.runner || forgejo-runner register --no-interactive \ + # Always re-register to keep labels in sync with values.yaml. + # Without this, changing a runner label requires manually deleting + # the PVC or .runner file — not GitOps-friendly. + rm -f /data/.runner + forgejo-runner register --no-interactive \ --instance {{ .Values.runner.forgejoUrl }} \ --token $(RUNNER_TOKEN) \ --name {{ .Values.runner.name }} \ -- 2.54.0 From f5217632925655aa7ed06313c7353d297716a112 Mon Sep 17 00:00:00 2001 From: rock Date: Sun, 6 Sep 2026 23:19:27 -0700 Subject: [PATCH 14/20] docs: add unified CI workflow pattern to shareable template --- CLAUDE.example.md | 92 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/CLAUDE.example.md b/CLAUDE.example.md index 4870b5e..b50dcf6 100644 --- a/CLAUDE.example.md +++ b/CLAUDE.example.md @@ -208,3 +208,95 @@ versions without warning in your own values file. Grouping by layer (rather than by day or by "misc fixes") makes it much easier to `git log --oneline -- ` your way back to *why* a given piece of config looks the way it does, months later. + +## Unified Forgejo CI Workflow Pattern (Enforced 2026-09-07+) + +All repositories MUST follow this exact structure. No variations. + +```yaml +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +env: + REGISTRY: + IMAGE: // + +jobs: + test: + name: Test + runs-on: [golang|node|rust] + steps: + - name: Install Node.js for actions runtime + run: apt-get update && apt-get install -y nodejs + + - name: Checkout code + uses: actions/checkout@v4 + + # Language-specific tests here (no docker, no registry) + # - name: Run tests + # run: npm test -- --run || true + + build-push: + name: Build & Push Image + needs: test + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + runs-on: [golang|node|rust] + steps: + - name: Install Node.js and Docker + run: | + apt-get update + apt-get install -y nodejs docker.io + + - name: Checkout code + uses: actions/checkout@v4 + + - name: Get short SHA + id: sha + run: | + SHORT_SHA=$(git rev-parse --short HEAD) + echo "short_sha=${SHORT_SHA}" >> $GITHUB_OUTPUT + + - name: Registry login + run: | + echo "${REGISTRY_TOKEN}" | docker login "${REGISTRY}" \ + --username "${REGISTRY_USER}" --password-stdin + env: + REGISTRY_USER: ${{ secrets.FORGEJO_REGISTRY_USER }} + REGISTRY_TOKEN: ${{ secrets.FORGEJO_REGISTRY_TOKEN }} + + - name: Build Docker image + run: | + docker build --no-cache \ + -t "${IMAGE}:${{ steps.sha.outputs.short_sha }}" \ + -t "${IMAGE}:latest" \ + . + + - name: Push Docker image + run: | + docker push "${IMAGE}:${{ steps.sha.outputs.short_sha }}" + docker push "${IMAGE}:latest" + + - name: Prune unused images + run: docker image prune -a --force 2>&1 | tail -3 || true +``` + +### Anti-Patterns (DO NOT USE) + +- ❌ `container: image: golang:1.26` overrides — breaks docker socket sharing +- ❌ Conditional `if:` on individual steps — use separate jobs instead +- ❌ Installing docker.io in test job — only needed in build-push +- ❌ Monolithic job doing test + build + push — hard to debug +- ❌ Using `{{ github.sha }}` for image tag — use short commit SHA for readability + +### How It Works + +1. **PR to feature branch** → test job runs, build-push skipped, nothing pushed +2. **Push to main** → test runs, build-push runs after test passes, image pushed +3. Docker socket shared between dind sidecar and runner via emptyDir mount at `/run` +4. `docker_host: automount` in runner config injects socket into workflow containers +5. Secrets (FORGEJO_REGISTRY_USER, TOKEN) set in Forgejo repo settings, NOT in git -- 2.54.0 From 32ef4eae0ed72c9efd0485babda4c31c895c125b Mon Sep 17 00:00:00 2001 From: rock Date: Mon, 7 Sep 2026 09:08:10 -0700 Subject: [PATCH 15/20] ci: add custom runner images with Node.js pre-installed --- .gitea/workflows/build-runner-images.yml | 82 +++++++++++++++++++ .../forgejo-runner-images/Dockerfile.golang | 12 +++ .../forgejo-runner-images/Dockerfile.node | 11 +++ .../forgejo-runner-images/Dockerfile.rust | 12 +++ k8s/infra/forgejo-runner/values-node.yaml | 2 +- k8s/infra/forgejo-runner/values-rust.yaml | 2 +- k8s/infra/forgejo-runner/values.yaml | 2 +- 7 files changed, 120 insertions(+), 3 deletions(-) create mode 100644 .gitea/workflows/build-runner-images.yml create mode 100644 k8s/infra/forgejo-runner-images/Dockerfile.golang create mode 100644 k8s/infra/forgejo-runner-images/Dockerfile.node create mode 100644 k8s/infra/forgejo-runner-images/Dockerfile.rust diff --git a/.gitea/workflows/build-runner-images.yml b/.gitea/workflows/build-runner-images.yml new file mode 100644 index 0000000..26c9b66 --- /dev/null +++ b/.gitea/workflows/build-runner-images.yml @@ -0,0 +1,82 @@ +name: Build Runner Images + +on: + push: + branches: [main] + paths: + - k8s/infra/forgejo-runner-images/Dockerfile.* + - .gitea/workflows/build-runner-images.yml + pull_request: + branches: [main] + paths: + - k8s/infra/forgejo-runner-images/Dockerfile.* + +env: + REGISTRY: forgejo.riotpiao.com + +jobs: + build: + name: Build and Push Runner Images + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + runs-on: golang + steps: + - name: Install Node.js and Docker + run: | + apt-get update + apt-get install -y nodejs docker.io + + - name: Checkout code + uses: actions/checkout@v4 + + - name: Get short SHA + id: sha + run: | + SHORT_SHA=$(git rev-parse --short HEAD) + echo "short_sha=${SHORT_SHA}" >> $GITHUB_OUTPUT + + - name: Registry login + run: | + echo "${REGISTRY_TOKEN}" | docker login "${REGISTRY}" \ + --username "${REGISTRY_USER}" --password-stdin + env: + REGISTRY_USER: ${{ secrets.FORGEJO_REGISTRY_USER }} + REGISTRY_TOKEN: ${{ secrets.FORGEJO_REGISTRY_TOKEN }} + + - name: Build and push golang image + run: | + IMAGE="${REGISTRY}/rock/forgejo-runner-golang:${{ steps.sha.outputs.short_sha }}" + docker build --no-cache \ + -f k8s/infra/forgejo-runner-images/Dockerfile.golang \ + -t "${IMAGE}" \ + -t "${REGISTRY}/rock/forgejo-runner-golang:latest" \ + . + docker push "${IMAGE}" + docker push "${REGISTRY}/rock/forgejo-runner-golang:latest" + echo "✓ Pushed golang: ${IMAGE}" + + - name: Build and push node image + run: | + IMAGE="${REGISTRY}/rock/forgejo-runner-node:${{ steps.sha.outputs.short_sha }}" + docker build --no-cache \ + -f k8s/infra/forgejo-runner-images/Dockerfile.node \ + -t "${IMAGE}" \ + -t "${REGISTRY}/rock/forgejo-runner-node:latest" \ + . + docker push "${IMAGE}" + docker push "${REGISTRY}/rock/forgejo-runner-node:latest" + echo "✓ Pushed node: ${IMAGE}" + + - name: Build and push rust image + run: | + IMAGE="${REGISTRY}/rock/forgejo-runner-rust:${{ steps.sha.outputs.short_sha }}" + docker build --no-cache \ + -f k8s/infra/forgejo-runner-images/Dockerfile.rust \ + -t "${IMAGE}" \ + -t "${REGISTRY}/rock/forgejo-runner-rust:latest" \ + . + docker push "${IMAGE}" + docker push "${REGISTRY}/rock/forgejo-runner-rust:latest" + echo "✓ Pushed rust: ${IMAGE}" + + - name: Prune unused images + run: docker image prune -a --force 2>&1 | tail -3 || true diff --git a/k8s/infra/forgejo-runner-images/Dockerfile.golang b/k8s/infra/forgejo-runner-images/Dockerfile.golang new file mode 100644 index 0000000..44a8908 --- /dev/null +++ b/k8s/infra/forgejo-runner-images/Dockerfile.golang @@ -0,0 +1,12 @@ +FROM golang:1.26-bookworm + +# Add Node.js precheck script +RUN apt-get update && apt-get install -y --no-install-recommends \ + nodejs npm \ + && rm -rf /var/lib/apt/lists/* + +# Verify installations +RUN node --version && npm --version && go version + +LABEL maintainer="homelab" \ + description="Golang 1.26 + Node.js pre-installed with precheck" diff --git a/k8s/infra/forgejo-runner-images/Dockerfile.node b/k8s/infra/forgejo-runner-images/Dockerfile.node new file mode 100644 index 0000000..025b90a --- /dev/null +++ b/k8s/infra/forgejo-runner-images/Dockerfile.node @@ -0,0 +1,11 @@ +FROM node:22-bookworm + +# Node.js already installed, ensure npm is up to date +RUN apt-get update && apt-get upgrade -y \ + && rm -rf /var/lib/apt/lists/* + +# Verify installations +RUN node --version && npm --version + +LABEL maintainer="homelab" \ + description="Node.js 22 with latest npm" diff --git a/k8s/infra/forgejo-runner-images/Dockerfile.rust b/k8s/infra/forgejo-runner-images/Dockerfile.rust new file mode 100644 index 0000000..a9ba6ea --- /dev/null +++ b/k8s/infra/forgejo-runner-images/Dockerfile.rust @@ -0,0 +1,12 @@ +FROM rust:1-bookworm + +# Add Node.js +RUN apt-get update && apt-get install -y --no-install-recommends \ + nodejs npm \ + && rm -rf /var/lib/apt/lists/* + +# Verify installations +RUN rustc --version && cargo --version && node --version && npm --version + +LABEL maintainer="homelab" \ + description="Rust 1 + Node.js pre-installed" diff --git a/k8s/infra/forgejo-runner/values-node.yaml b/k8s/infra/forgejo-runner/values-node.yaml index e75a0aa..947099b 100644 --- a/k8s/infra/forgejo-runner/values-node.yaml +++ b/k8s/infra/forgejo-runner/values-node.yaml @@ -9,7 +9,7 @@ runner: repository: code.forgejo.org/forgejo/runner tag: "6" name: node-runner - labels: "node:docker://node:22-bookworm" + labels: "node:docker://forgejo.riotpiao.com/rock/forgejo-runner-node:latest" # GC CronJob renders only from the default (golang) values to avoid duplicates gc: diff --git a/k8s/infra/forgejo-runner/values-rust.yaml b/k8s/infra/forgejo-runner/values-rust.yaml index b645cf3..903e066 100644 --- a/k8s/infra/forgejo-runner/values-rust.yaml +++ b/k8s/infra/forgejo-runner/values-rust.yaml @@ -9,7 +9,7 @@ runner: repository: code.forgejo.org/forgejo/runner tag: "6" name: rust-runner - labels: "rust:docker://rust:1-bookworm" + labels: "rust:docker://forgejo.riotpiao.com/rock/forgejo-runner-rust:latest" diff --git a/k8s/infra/forgejo-runner/values.yaml b/k8s/infra/forgejo-runner/values.yaml index adb07b0..683d88c 100644 --- a/k8s/infra/forgejo-runner/values.yaml +++ b/k8s/infra/forgejo-runner/values.yaml @@ -6,7 +6,7 @@ runner: # Label image is what workflow steps run in (NOT the runner daemon image). # golang:1.26-bookworm: Debian, root, apt-get, Go, git. # Install Node.js/docker in workflow steps as needed. - labels: "golang:docker://golang:1.26-bookworm" + labels: "golang:docker://forgejo.riotpiao.com/rock/forgejo-runner-golang:latest" forgejoUrl: http://forgejo-gitea-http.cicd.svc.cluster.local:3000 tokenSecret: runner-token resources: -- 2.54.0 From f253731cb96487540059028bbce1375999c0a30d Mon Sep 17 00:00:00 2001 From: rock Date: Mon, 7 Sep 2026 09:09:59 -0700 Subject: [PATCH 16/20] ci: add docker.io to custom runner images --- k8s/infra/forgejo-runner-images/Dockerfile.golang | 8 ++++---- k8s/infra/forgejo-runner-images/Dockerfile.node | 9 +++++---- k8s/infra/forgejo-runner-images/Dockerfile.rust | 8 ++++---- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/k8s/infra/forgejo-runner-images/Dockerfile.golang b/k8s/infra/forgejo-runner-images/Dockerfile.golang index 44a8908..93d7f34 100644 --- a/k8s/infra/forgejo-runner-images/Dockerfile.golang +++ b/k8s/infra/forgejo-runner-images/Dockerfile.golang @@ -1,12 +1,12 @@ FROM golang:1.26-bookworm -# Add Node.js precheck script +# Install Node.js, npm, and Docker CLI RUN apt-get update && apt-get install -y --no-install-recommends \ - nodejs npm \ + nodejs npm docker.io \ && rm -rf /var/lib/apt/lists/* # Verify installations -RUN node --version && npm --version && go version +RUN go version && node --version && npm --version && docker --version LABEL maintainer="homelab" \ - description="Golang 1.26 + Node.js pre-installed with precheck" + description="Golang 1.26 + Node.js + Docker CLI pre-installed" diff --git a/k8s/infra/forgejo-runner-images/Dockerfile.node b/k8s/infra/forgejo-runner-images/Dockerfile.node index 025b90a..6bbc58b 100644 --- a/k8s/infra/forgejo-runner-images/Dockerfile.node +++ b/k8s/infra/forgejo-runner-images/Dockerfile.node @@ -1,11 +1,12 @@ FROM node:22-bookworm -# Node.js already installed, ensure npm is up to date -RUN apt-get update && apt-get upgrade -y \ +# Node.js already installed, add Docker CLI +RUN apt-get update && apt-get install -y --no-install-recommends \ + docker.io \ && rm -rf /var/lib/apt/lists/* # Verify installations -RUN node --version && npm --version +RUN node --version && npm --version && docker --version LABEL maintainer="homelab" \ - description="Node.js 22 with latest npm" + description="Node.js 22 + Docker CLI pre-installed" diff --git a/k8s/infra/forgejo-runner-images/Dockerfile.rust b/k8s/infra/forgejo-runner-images/Dockerfile.rust index a9ba6ea..bece352 100644 --- a/k8s/infra/forgejo-runner-images/Dockerfile.rust +++ b/k8s/infra/forgejo-runner-images/Dockerfile.rust @@ -1,12 +1,12 @@ FROM rust:1-bookworm -# Add Node.js +# Add Node.js and Docker CLI RUN apt-get update && apt-get install -y --no-install-recommends \ - nodejs npm \ + nodejs npm docker.io \ && rm -rf /var/lib/apt/lists/* # Verify installations -RUN rustc --version && cargo --version && node --version && npm --version +RUN rustc --version && cargo --version && node --version && npm --version && docker --version LABEL maintainer="homelab" \ - description="Rust 1 + Node.js pre-installed" + description="Rust 1 + Node.js + Docker CLI pre-installed" -- 2.54.0 From 8f197fcc23a43f090631f639431e8406f047de55 Mon Sep 17 00:00:00 2001 From: rock Date: Mon, 7 Sep 2026 09:11:45 -0700 Subject: [PATCH 17/20] ci: update runner daemon images to custom runner images (enables Image Updater tracking) --- k8s/infra/forgejo-runner/values-node.yaml | 4 ++-- k8s/infra/forgejo-runner/values-rust.yaml | 4 ++-- k8s/infra/forgejo-runner/values.yaml | 10 +++++----- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/k8s/infra/forgejo-runner/values-node.yaml b/k8s/infra/forgejo-runner/values-node.yaml index 947099b..1d30cfd 100644 --- a/k8s/infra/forgejo-runner/values-node.yaml +++ b/k8s/infra/forgejo-runner/values-node.yaml @@ -6,8 +6,8 @@ # Install docker in workflow steps as needed. runner: image: - repository: code.forgejo.org/forgejo/runner - tag: "6" + repository: forgejo.riotpiao.com/rock/forgejo-runner-node + tag: "latest" name: node-runner labels: "node:docker://forgejo.riotpiao.com/rock/forgejo-runner-node:latest" diff --git a/k8s/infra/forgejo-runner/values-rust.yaml b/k8s/infra/forgejo-runner/values-rust.yaml index 903e066..62175ff 100644 --- a/k8s/infra/forgejo-runner/values-rust.yaml +++ b/k8s/infra/forgejo-runner/values-rust.yaml @@ -6,8 +6,8 @@ # Install Node.js/docker in workflow steps as needed. runner: image: - repository: code.forgejo.org/forgejo/runner - tag: "6" + repository: forgejo.riotpiao.com/rock/forgejo-runner-rust + tag: "latest" name: rust-runner labels: "rust:docker://forgejo.riotpiao.com/rock/forgejo-runner-rust:latest" diff --git a/k8s/infra/forgejo-runner/values.yaml b/k8s/infra/forgejo-runner/values.yaml index 683d88c..f4e7067 100644 --- a/k8s/infra/forgejo-runner/values.yaml +++ b/k8s/infra/forgejo-runner/values.yaml @@ -1,11 +1,11 @@ runner: image: - repository: code.forgejo.org/forgejo/runner - tag: "6" + repository: forgejo.riotpiao.com/rock/forgejo-runner-golang + tag: "latest" name: golang-runner - # Label image is what workflow steps run in (NOT the runner daemon image). - # golang:1.26-bookworm: Debian, root, apt-get, Go, git. - # Install Node.js/docker in workflow steps as needed. + # Label image is what workflow steps run in. + # Uses custom image with Node.js and Docker pre-installed. + # Image Updater will track and update the :latest tag automatically. labels: "golang:docker://forgejo.riotpiao.com/rock/forgejo-runner-golang:latest" forgejoUrl: http://forgejo-gitea-http.cicd.svc.cluster.local:3000 tokenSecret: runner-token -- 2.54.0 From fcf60cc54628b308968439d62ab2c7a51367948f Mon Sep 17 00:00:00 2001 From: rock Date: Mon, 7 Sep 2026 09:15:44 -0700 Subject: [PATCH 18/20] ci: improve runner image build workflow - always run + manual trigger + weekly schedule --- .gitea/workflows/build-runner-images.yml | 75 ++++++++++++++---------- 1 file changed, 43 insertions(+), 32 deletions(-) diff --git a/.gitea/workflows/build-runner-images.yml b/.gitea/workflows/build-runner-images.yml index 26c9b66..1573a72 100644 --- a/.gitea/workflows/build-runner-images.yml +++ b/.gitea/workflows/build-runner-images.yml @@ -1,23 +1,18 @@ -name: Build Runner Images +name: Build & Push Runner Images on: push: branches: [main] - paths: - - k8s/infra/forgejo-runner-images/Dockerfile.* - - .gitea/workflows/build-runner-images.yml - pull_request: - branches: [main] - paths: - - k8s/infra/forgejo-runner-images/Dockerfile.* + workflow_dispatch: # Manual trigger + schedule: + - cron: '0 2 * * 0' # Weekly on Sunday 2 AM UTC (ensure fresh images) env: REGISTRY: forgejo.riotpiao.com jobs: - build: - name: Build and Push Runner Images - if: github.event_name == 'push' && github.ref == 'refs/heads/main' + build-runner-images: + name: Build & Push All Runner Images runs-on: golang steps: - name: Install Node.js and Docker @@ -42,41 +37,57 @@ jobs: REGISTRY_USER: ${{ secrets.FORGEJO_REGISTRY_USER }} REGISTRY_TOKEN: ${{ secrets.FORGEJO_REGISTRY_TOKEN }} - - name: Build and push golang image + - name: Build and push Golang runner image run: | - IMAGE="${REGISTRY}/rock/forgejo-runner-golang:${{ steps.sha.outputs.short_sha }}" + IMAGE="${REGISTRY}/rock/forgejo-runner-golang" + echo "Building: ${IMAGE}:${{ steps.sha.outputs.short_sha }}" + docker build --no-cache \ -f k8s/infra/forgejo-runner-images/Dockerfile.golang \ - -t "${IMAGE}" \ - -t "${REGISTRY}/rock/forgejo-runner-golang:latest" \ + -t "${IMAGE}:${{ steps.sha.outputs.short_sha }}" \ + -t "${IMAGE}:latest" \ . - docker push "${IMAGE}" - docker push "${REGISTRY}/rock/forgejo-runner-golang:latest" - echo "✓ Pushed golang: ${IMAGE}" + + docker push "${IMAGE}:${{ steps.sha.outputs.short_sha }}" + docker push "${IMAGE}:latest" + echo "✓ Pushed golang runner" - - name: Build and push node image + - name: Build and push Node runner image run: | - IMAGE="${REGISTRY}/rock/forgejo-runner-node:${{ steps.sha.outputs.short_sha }}" + IMAGE="${REGISTRY}/rock/forgejo-runner-node" + echo "Building: ${IMAGE}:${{ steps.sha.outputs.short_sha }}" + docker build --no-cache \ -f k8s/infra/forgejo-runner-images/Dockerfile.node \ - -t "${IMAGE}" \ - -t "${REGISTRY}/rock/forgejo-runner-node:latest" \ + -t "${IMAGE}:${{ steps.sha.outputs.short_sha }}" \ + -t "${IMAGE}:latest" \ . - docker push "${IMAGE}" - docker push "${REGISTRY}/rock/forgejo-runner-node:latest" - echo "✓ Pushed node: ${IMAGE}" + + docker push "${IMAGE}:${{ steps.sha.outputs.short_sha }}" + docker push "${IMAGE}:latest" + echo "✓ Pushed node runner" - - name: Build and push rust image + - name: Build and push Rust runner image run: | - IMAGE="${REGISTRY}/rock/forgejo-runner-rust:${{ steps.sha.outputs.short_sha }}" + IMAGE="${REGISTRY}/rock/forgejo-runner-rust" + echo "Building: ${IMAGE}:${{ steps.sha.outputs.short_sha }}" + docker build --no-cache \ -f k8s/infra/forgejo-runner-images/Dockerfile.rust \ - -t "${IMAGE}" \ - -t "${REGISTRY}/rock/forgejo-runner-rust:latest" \ + -t "${IMAGE}:${{ steps.sha.outputs.short_sha }}" \ + -t "${IMAGE}:latest" \ . - docker push "${IMAGE}" - docker push "${REGISTRY}/rock/forgejo-runner-rust:latest" - echo "✓ Pushed rust: ${IMAGE}" + + docker push "${IMAGE}:${{ steps.sha.outputs.short_sha }}" + docker push "${IMAGE}:latest" + echo "✓ Pushed rust runner" - name: Prune unused images run: docker image prune -a --force 2>&1 | tail -3 || true + + - name: Summary + run: | + echo "=== Runner Images Built and Pushed ===" + echo "✓ forgejo.riotpiao.com/rock/forgejo-runner-golang:latest" + echo "✓ forgejo.riotpiao.com/rock/forgejo-runner-node:latest" + echo "✓ forgejo.riotpiao.com/rock/forgejo-runner-rust:latest" -- 2.54.0 From 038cf40fe5d9f4f0e3f926c7a501c7782c5aa305 Mon Sep 17 00:00:00 2001 From: rock Date: Mon, 7 Sep 2026 09:17:04 -0700 Subject: [PATCH 19/20] ci: temporarily revert runners to public images to bootstrap custom image build --- k8s/infra/forgejo-runner/values-node.yaml | 6 +++--- k8s/infra/forgejo-runner/values-rust.yaml | 6 +++--- k8s/infra/forgejo-runner/values.yaml | 12 ++++++------ 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/k8s/infra/forgejo-runner/values-node.yaml b/k8s/infra/forgejo-runner/values-node.yaml index 1d30cfd..e75a0aa 100644 --- a/k8s/infra/forgejo-runner/values-node.yaml +++ b/k8s/infra/forgejo-runner/values-node.yaml @@ -6,10 +6,10 @@ # Install docker in workflow steps as needed. runner: image: - repository: forgejo.riotpiao.com/rock/forgejo-runner-node - tag: "latest" + repository: code.forgejo.org/forgejo/runner + tag: "6" name: node-runner - labels: "node:docker://forgejo.riotpiao.com/rock/forgejo-runner-node:latest" + labels: "node:docker://node:22-bookworm" # GC CronJob renders only from the default (golang) values to avoid duplicates gc: diff --git a/k8s/infra/forgejo-runner/values-rust.yaml b/k8s/infra/forgejo-runner/values-rust.yaml index 62175ff..b645cf3 100644 --- a/k8s/infra/forgejo-runner/values-rust.yaml +++ b/k8s/infra/forgejo-runner/values-rust.yaml @@ -6,10 +6,10 @@ # Install Node.js/docker in workflow steps as needed. runner: image: - repository: forgejo.riotpiao.com/rock/forgejo-runner-rust - tag: "latest" + repository: code.forgejo.org/forgejo/runner + tag: "6" name: rust-runner - labels: "rust:docker://forgejo.riotpiao.com/rock/forgejo-runner-rust:latest" + labels: "rust:docker://rust:1-bookworm" diff --git a/k8s/infra/forgejo-runner/values.yaml b/k8s/infra/forgejo-runner/values.yaml index f4e7067..c8e7052 100644 --- a/k8s/infra/forgejo-runner/values.yaml +++ b/k8s/infra/forgejo-runner/values.yaml @@ -1,12 +1,12 @@ runner: image: - repository: forgejo.riotpiao.com/rock/forgejo-runner-golang - tag: "latest" + repository: code.forgejo.org/forgejo/runner + tag: "6" name: golang-runner - # Label image is what workflow steps run in. - # Uses custom image with Node.js and Docker pre-installed. - # Image Updater will track and update the :latest tag automatically. - labels: "golang:docker://forgejo.riotpiao.com/rock/forgejo-runner-golang:latest" + # Label image is what workflow steps run in (NOT the runner daemon image). + # golang:1.26-bookworm: Debian, root, apt-get, Go, git. + # TODO: Switch to custom image once build-runner-images.yml pushes images + labels: "golang:docker://golang:1.26-bookworm" forgejoUrl: http://forgejo-gitea-http.cicd.svc.cluster.local:3000 tokenSecret: runner-token resources: -- 2.54.0 From cfacbce19416b01baef055132db07f61efa5c552 Mon Sep 17 00:00:00 2001 From: rock Date: Mon, 7 Sep 2026 13:01:22 -0700 Subject: [PATCH 20/20] ci: remove custom runner image approach (use public Docker Hub images instead) Custom runner images were never needed. Forgejo runners pull label images (golang:1.26-bookworm, node:22-bookworm, rust:1-bookworm) from Docker Hub at job time. Node.js/docker are installed in workflow steps. Removes dead-end Dockerfiles and build workflow that couldn't push through Cloudflare's 100MB payload limit. --- .gitea/workflows/build-runner-images.yml | 93 ------------------- .../forgejo-runner-images/Dockerfile.golang | 12 --- .../forgejo-runner-images/Dockerfile.node | 12 --- .../forgejo-runner-images/Dockerfile.rust | 12 --- 4 files changed, 129 deletions(-) delete mode 100644 .gitea/workflows/build-runner-images.yml delete mode 100644 k8s/infra/forgejo-runner-images/Dockerfile.golang delete mode 100644 k8s/infra/forgejo-runner-images/Dockerfile.node delete mode 100644 k8s/infra/forgejo-runner-images/Dockerfile.rust diff --git a/.gitea/workflows/build-runner-images.yml b/.gitea/workflows/build-runner-images.yml deleted file mode 100644 index 1573a72..0000000 --- a/.gitea/workflows/build-runner-images.yml +++ /dev/null @@ -1,93 +0,0 @@ -name: Build & Push Runner Images - -on: - push: - branches: [main] - workflow_dispatch: # Manual trigger - schedule: - - cron: '0 2 * * 0' # Weekly on Sunday 2 AM UTC (ensure fresh images) - -env: - REGISTRY: forgejo.riotpiao.com - -jobs: - build-runner-images: - name: Build & Push All Runner Images - runs-on: golang - steps: - - name: Install Node.js and Docker - run: | - apt-get update - apt-get install -y nodejs docker.io - - - name: Checkout code - uses: actions/checkout@v4 - - - name: Get short SHA - id: sha - run: | - SHORT_SHA=$(git rev-parse --short HEAD) - echo "short_sha=${SHORT_SHA}" >> $GITHUB_OUTPUT - - - name: Registry login - run: | - echo "${REGISTRY_TOKEN}" | docker login "${REGISTRY}" \ - --username "${REGISTRY_USER}" --password-stdin - env: - REGISTRY_USER: ${{ secrets.FORGEJO_REGISTRY_USER }} - REGISTRY_TOKEN: ${{ secrets.FORGEJO_REGISTRY_TOKEN }} - - - name: Build and push Golang runner image - run: | - IMAGE="${REGISTRY}/rock/forgejo-runner-golang" - echo "Building: ${IMAGE}:${{ steps.sha.outputs.short_sha }}" - - docker build --no-cache \ - -f k8s/infra/forgejo-runner-images/Dockerfile.golang \ - -t "${IMAGE}:${{ steps.sha.outputs.short_sha }}" \ - -t "${IMAGE}:latest" \ - . - - docker push "${IMAGE}:${{ steps.sha.outputs.short_sha }}" - docker push "${IMAGE}:latest" - echo "✓ Pushed golang runner" - - - name: Build and push Node runner image - run: | - IMAGE="${REGISTRY}/rock/forgejo-runner-node" - echo "Building: ${IMAGE}:${{ steps.sha.outputs.short_sha }}" - - docker build --no-cache \ - -f k8s/infra/forgejo-runner-images/Dockerfile.node \ - -t "${IMAGE}:${{ steps.sha.outputs.short_sha }}" \ - -t "${IMAGE}:latest" \ - . - - docker push "${IMAGE}:${{ steps.sha.outputs.short_sha }}" - docker push "${IMAGE}:latest" - echo "✓ Pushed node runner" - - - name: Build and push Rust runner image - run: | - IMAGE="${REGISTRY}/rock/forgejo-runner-rust" - echo "Building: ${IMAGE}:${{ steps.sha.outputs.short_sha }}" - - docker build --no-cache \ - -f k8s/infra/forgejo-runner-images/Dockerfile.rust \ - -t "${IMAGE}:${{ steps.sha.outputs.short_sha }}" \ - -t "${IMAGE}:latest" \ - . - - docker push "${IMAGE}:${{ steps.sha.outputs.short_sha }}" - docker push "${IMAGE}:latest" - echo "✓ Pushed rust runner" - - - name: Prune unused images - run: docker image prune -a --force 2>&1 | tail -3 || true - - - name: Summary - run: | - echo "=== Runner Images Built and Pushed ===" - echo "✓ forgejo.riotpiao.com/rock/forgejo-runner-golang:latest" - echo "✓ forgejo.riotpiao.com/rock/forgejo-runner-node:latest" - echo "✓ forgejo.riotpiao.com/rock/forgejo-runner-rust:latest" diff --git a/k8s/infra/forgejo-runner-images/Dockerfile.golang b/k8s/infra/forgejo-runner-images/Dockerfile.golang deleted file mode 100644 index 93d7f34..0000000 --- a/k8s/infra/forgejo-runner-images/Dockerfile.golang +++ /dev/null @@ -1,12 +0,0 @@ -FROM golang:1.26-bookworm - -# Install Node.js, npm, and Docker CLI -RUN apt-get update && apt-get install -y --no-install-recommends \ - nodejs npm docker.io \ - && rm -rf /var/lib/apt/lists/* - -# Verify installations -RUN go version && node --version && npm --version && docker --version - -LABEL maintainer="homelab" \ - description="Golang 1.26 + Node.js + Docker CLI pre-installed" diff --git a/k8s/infra/forgejo-runner-images/Dockerfile.node b/k8s/infra/forgejo-runner-images/Dockerfile.node deleted file mode 100644 index 6bbc58b..0000000 --- a/k8s/infra/forgejo-runner-images/Dockerfile.node +++ /dev/null @@ -1,12 +0,0 @@ -FROM node:22-bookworm - -# Node.js already installed, add Docker CLI -RUN apt-get update && apt-get install -y --no-install-recommends \ - docker.io \ - && rm -rf /var/lib/apt/lists/* - -# Verify installations -RUN node --version && npm --version && docker --version - -LABEL maintainer="homelab" \ - description="Node.js 22 + Docker CLI pre-installed" diff --git a/k8s/infra/forgejo-runner-images/Dockerfile.rust b/k8s/infra/forgejo-runner-images/Dockerfile.rust deleted file mode 100644 index bece352..0000000 --- a/k8s/infra/forgejo-runner-images/Dockerfile.rust +++ /dev/null @@ -1,12 +0,0 @@ -FROM rust:1-bookworm - -# Add Node.js and Docker CLI -RUN apt-get update && apt-get install -y --no-install-recommends \ - nodejs npm docker.io \ - && rm -rf /var/lib/apt/lists/* - -# Verify installations -RUN rustc --version && cargo --version && node --version && npm --version && docker --version - -LABEL maintainer="homelab" \ - description="Rust 1 + Node.js + Docker CLI pre-installed" -- 2.54.0