Phase-6: enable Gitea Actions, in-cluster act_runner, static CI gate
STEP 1 — enable Gitea Actions: pin GITEA__actions__ENABLED=true and DEFAULT_ACTIONS_URL=github in the gitea bootstrap patch (was implicit upstream default; make it a declared property). STEP 2 — deploy + register an act_runner in a dedicated non-app namespace (gitea-actions) so the privileged DinD executor sidecar is admitted without a Kyverno policy exception (restrict-host-namespaces scopes to app namespaces only). Docker executor + DinD sidecar over a shared TLS-cert emptyDir; runner state on a Longhorn PVC (registers once). Registration token delivered as a SOPS-encrypted Secret decrypted by Flux. Runner mounts no ServiceAccount token (R-SEC-02). STEP 3 — author .gitea/workflows/gate.yml: the live enforced merge gate running the STATIC layers (spec-immutability best-effort, lint, Kyverno L0, L1 render) via `make -C ci lint l0 l1` with the pinned toolchain installed at job start. L2/L3 conformance is an if:false placeholder (needs a toolchain image + scoped cluster creds — a design decision, not wired). merge-gate.yaml reduced to workflow_dispatch (dormant full-pipeline blueprint). install-tools.sh gains check-jsonschema so the schema stage validates in CI instead of skipping. Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
1 parent
81ef78295a
commit
bc4519bc4c
7 files changed
+327
-10
No files matched your search
@@ -0,0 +1,89 @@
|
|||||||
|
# .gitea/workflows/gate.yml — the ENFORCED PR merge gate (Phase 6, WS-GITOSS).
|
||||||
|
#
|
||||||
|
# This is the LIVE gate wired to the in-cluster Gitea Actions runner (ns gitea-actions,
|
||||||
|
# Docker executor + DinD). It runs the STATIC layers of the conformance gate — the ones
|
||||||
|
# docs/14 says run "without a cluster": spec-immutability (best-effort), lint, Kyverno L0,
|
||||||
|
# and L1 offline render checks. It is the single required status check on `main`
|
||||||
|
# (see branch protection). `make -C ci lint l0 l1` here is byte-identical to a laptop run
|
||||||
|
# (R-TEST-02) — the runner just installs the pinned tools from ci/versions.env first.
|
||||||
|
#
|
||||||
|
# The L2/L3 CONFORMANCE layer (behavioural + fault-injection, needs a live cluster) is
|
||||||
|
# NOT wired yet — it is a deliberate design decision (a CI toolchain image + a scoped
|
||||||
|
# in-cluster kubeconfig/RBAC for the runner). It is present below as an `if: false`
|
||||||
|
# placeholder so the shape is visible and the wiring is a one-line flip once decided.
|
||||||
|
# The full pipeline blueprint (spec-checkout, per-layer jobs, decision job) is retained
|
||||||
|
# dormant in merge-gate.yaml.
|
||||||
|
---
|
||||||
|
name: gate
|
||||||
|
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- 'feature/**'
|
||||||
|
- 'fix/**'
|
||||||
|
- 'docs/**'
|
||||||
|
- 'chore/**'
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: gate-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# Single required status check: the static merge gate.
|
||||||
|
gate:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0 # full history so ci/touched-requirements.sh can diff vs main
|
||||||
|
|
||||||
|
- name: Install pinned CI toolchain (ci/versions.env)
|
||||||
|
run: |
|
||||||
|
./.gitea/workflows/scripts/install-tools.sh l0
|
||||||
|
./.gitea/workflows/scripts/install-tools.sh l1
|
||||||
|
|
||||||
|
# R-TEST-06: the spec is read-only to agents. The spec repo is NOT checked out into
|
||||||
|
# CI here (it is a separate repo; SPEC_REPO_SLUG is not wired into this runner), so
|
||||||
|
# the guard honestly SKIPS (exit 3) rather than faking green. A real drift/edit of a
|
||||||
|
# co-located spec would exit 1 and fail. Tolerated: exit 3 only.
|
||||||
|
- name: Spec immutability (R-TEST-06, skips if spec repo absent)
|
||||||
|
run: make -C ci spec-immutability || [ "$?" -eq 3 ]
|
||||||
|
|
||||||
|
- name: Static gate — lint + Kyverno L0 + L1 render
|
||||||
|
run: make -C ci lint l0 l1
|
||||||
|
|
||||||
|
- name: Upload gate artifacts
|
||||||
|
if: always()
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: gate-junit-coverage
|
||||||
|
path: |
|
||||||
|
results/junit/*.xml
|
||||||
|
reports/coverage/**
|
||||||
|
if-no-files-found: ignore
|
||||||
|
|
||||||
|
# ───────────────────────────────────────────────────────────────────────────
|
||||||
|
# PLACEHOLDER — NOT WIRED (STOP-and-report item, WS-GITOSS Phase 6).
|
||||||
|
# L2/L3 conformance (Chainsaw behavioural + fault-injection) needs a LIVE cluster.
|
||||||
|
# Turning this on is a design decision with two open sub-decisions:
|
||||||
|
# 1. Toolchain: build+push a pinned CI image to the Gitea container registry and
|
||||||
|
# reference it as the job image, vs. install-in-job from ci/versions.env (slower).
|
||||||
|
# 2. Cluster creds: how the runner reaches the cluster — the intended model is an
|
||||||
|
# in-cluster Chainsaw Job driven by its OWN scoped ServiceAccount (CI holds only a
|
||||||
|
# narrow trigger credential; the runner mounts NO SA token today). That SA + RBAC
|
||||||
|
# + the trigger secret do not exist yet.
|
||||||
|
# Kept `if: false` so it never runs (shows skipped, never fakes green — HARD RULE 4).
|
||||||
|
# ───────────────────────────────────────────────────────────────────────────
|
||||||
|
conformance:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [gate]
|
||||||
|
if: false
|
||||||
|
steps:
|
||||||
|
- name: Placeholder
|
||||||
|
run: |
|
||||||
|
echo "L2/L3 conformance not wired — see gate.yml header + WS-GITOSS report." >&2
|
||||||
|
exit 1
|
||||||
@@ -20,18 +20,23 @@
|
|||||||
# CI holds NO broad kubeconfig. L2/L3 run as an in-cluster Chainsaw Job whose own
|
# CI holds NO broad kubeconfig. L2/L3 run as an in-cluster Chainsaw Job whose own
|
||||||
# scoped ServiceAccount holds cluster access (docs/14, R-SEC-02); CI uses only a
|
# scoped ServiceAccount holds cluster access (docs/14, R-SEC-02); CI uses only a
|
||||||
# narrow trigger credential referenced BY SECRET NAME below.
|
# narrow trigger credential referenced BY SECRET NAME below.
|
||||||
|
#
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
# DORMANT (Phase 6, WS-GITOSS): the LIVE enforced gate is now gate.yml. This file is
|
||||||
|
# retained as the full-pipeline BLUEPRINT (spec-checkout via SPEC_REPO_SLUG + the L2/L3
|
||||||
|
# conformance job + the R-TEST-03 decision job) for when those seams are wired:
|
||||||
|
# - SPEC_REPO_SLUG must be set as a Gitea repo/org variable AND the spec repo hosted on
|
||||||
|
# this Gitea, or spec-immutability's second checkout fails the whole run.
|
||||||
|
# - vars.CLUSTER_AVAILABLE + a scoped conformance trigger credential must exist.
|
||||||
|
# Until then it must NOT auto-run (its spec checkout would fail with SPEC_REPO_SLUG unset),
|
||||||
|
# so its triggers are reduced to manual `workflow_dispatch`. Re-enable by restoring the
|
||||||
|
# pull_request/push triggers once the seams above are wired.
|
||||||
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
---
|
---
|
||||||
name: merge-gate
|
name: merge-gate
|
||||||
|
|
||||||
on:
|
on:
|
||||||
pull_request:
|
workflow_dispatch: {} # DORMANT blueprint — manual-only; gate.yml is the live gate
|
||||||
branches: [main] # PRs are the enforced merge gate into main (CONTRIBUTING.md)
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- 'feature/**' # feature-branch -> main workflow (no work on main)
|
|
||||||
- 'fix/**'
|
|
||||||
- 'docs/**'
|
|
||||||
- 'chore/**'
|
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: merge-gate-${{ github.ref }}
|
group: merge-gate-${{ github.ref }}
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ log() { printf '[install] %s\n' "$*" >&2; }
|
|||||||
|
|
||||||
install_yamllint() { have yamllint || { log "yamllint ${YAMLLINT_VERSION}"; pip install --quiet "yamllint==${YAMLLINT_VERSION}"; }; }
|
install_yamllint() { have yamllint || { log "yamllint ${YAMLLINT_VERSION}"; pip install --quiet "yamllint==${YAMLLINT_VERSION}"; }; }
|
||||||
|
|
||||||
|
install_check_jsonschema() { have check-jsonschema || { log "check-jsonschema ${CHECK_JSONSCHEMA_VERSION}"; pip install --quiet "check-jsonschema==${CHECK_JSONSCHEMA_VERSION}"; }; }
|
||||||
|
|
||||||
install_kyverno() {
|
install_kyverno() {
|
||||||
have kyverno && return 0
|
have kyverno && return 0
|
||||||
log "kyverno CLI ${KYVERNO_VERSION}"
|
log "kyverno CLI ${KYVERNO_VERSION}"
|
||||||
@@ -44,10 +46,10 @@ have() { command -v "$1" >/dev/null 2>&1; }
|
|||||||
|
|
||||||
case "$stage" in
|
case "$stage" in
|
||||||
lint) install_yamllint ;;
|
lint) install_yamllint ;;
|
||||||
l0) install_yamllint; install_kyverno ;;
|
l0) install_yamllint; install_check_jsonschema; install_kyverno ;; # schema (check-jsonschema) + Kyverno L0
|
||||||
l1) install_chainsaw ;; # L1 render via conformance runner (Chainsaw)
|
l1) install_chainsaw ;; # L1 render via conformance runner (Chainsaw)
|
||||||
conformance) install_chainsaw; install_kubectl ;; # trigger the in-cluster Job
|
conformance) install_chainsaw; install_kubectl ;; # trigger the in-cluster Job
|
||||||
all) install_yamllint; install_kyverno; install_chainsaw; install_kubectl ;;
|
all) install_yamllint; install_check_jsonschema; install_kyverno; install_chainsaw; install_kubectl ;;
|
||||||
*) echo "unknown stage: $stage" >&2; exit 2 ;;
|
*) echo "unknown stage: $stage" >&2; exit 2 ;;
|
||||||
esac
|
esac
|
||||||
log "stage '${stage}' tools ready"
|
log "stage '${stage}' tools ready"
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
# Gitea Actions runner registration token (WS-GITOSS, Phase 6). SOPS-encrypted (age).
|
||||||
|
# The in-cluster act_runner (ns gitea-actions) uses this INSTANCE-level registration
|
||||||
|
# token (from POST /api/v1/admin/actions/runners/registration-token) to register once
|
||||||
|
# against Gitea. Flux decrypts it via the sops-age key at reconcile (apps Kustomization
|
||||||
|
# decryption is wired in clusters/dev/kustomization.yaml). Rotate by resetting the token
|
||||||
|
# in Gitea (admin -> actions -> runners) and re-encrypting a fresh value here.
|
||||||
|
# Recover/inspect: infra/devcontainer/dev.sh run sops -d apps/gitea-actions-runner-secret.sops.yaml
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Secret
|
||||||
|
metadata:
|
||||||
|
name: gitea-actions-runner
|
||||||
|
namespace: gitea-actions
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: act-runner
|
||||||
|
type: Opaque
|
||||||
|
stringData:
|
||||||
|
token: ENC[AES256_GCM,data:W48mEtBqGeukX13lMhrhrer+zB21cLVp0acy3m1Xy/oo89DqO8FX/Q==,iv:wfTUU7RiFLdEc1fmEFS9DfE2SmGCFLPK17m0IKspft0=,tag:UhoXA47p7386KHcYNbPzCg==,type:str]
|
||||||
|
sops:
|
||||||
|
kms: []
|
||||||
|
gcp_kms: []
|
||||||
|
azure_kv: []
|
||||||
|
hc_vault: []
|
||||||
|
age:
|
||||||
|
- recipient: age175l8mdpdfu7hq0aml3exs3gykarwjmuvr23sq56s3wjejnyk44wsp4mfzg
|
||||||
|
enc: |
|
||||||
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBMYjhDVis5UzBvMWdjQUtk
|
||||||
|
T28wRTFZdjB6NFZOYmxQTGpOcksrb2pyK1RNCjZVdnFHU0IvOEcvcmRjZ05SQWI0
|
||||||
|
OTdEekJDYlc3cUxNU29OZU9YWlM5b00KLS0tIEtNeHlrV3NXUnhyNnJncW5vZmta
|
||||||
|
MDZLUE52QzJXbFlrNWxOcVhJbDNnY00KxCpFkbhhCuk4Fa8CC3C4+R9AcsUnaXQR
|
||||||
|
UjAfwKFoXAneXUih7TTXMbR/6+VgQBww5Bmwpn7jVLnzP0N0pIZVTA==
|
||||||
|
-----END AGE ENCRYPTED FILE-----
|
||||||
|
lastmodified: "2026-08-26T04:48:14Z"
|
||||||
|
mac: ENC[AES256_GCM,data:b99/zkL332RIT3FYBa1sR3o9+pwlmrUgKvVHawbjWYSJZ7ERUfO+LXuhKJ0HKrp6i1JfkurZlRO27iBcfrBisAXS9KLaqmsCJf4kKfFc4skANrmKCzprrXUmZexr1uiE8eDUzjNCLNxSCZfnFR6d/AkMXJqi6Hsj9/O+wH2lWsc=,iv:3DIPszSdxK81ERO3o/nTs33qQYHaAKLNcGP4qlZViFc=,tag:/B6VKkzMUwh0byNUicS5wQ==,type:str]
|
||||||
|
pgp: []
|
||||||
|
encrypted_regex: ^(data|stringData)$
|
||||||
|
version: 3.9.4
|
||||||
@@ -0,0 +1,169 @@
|
|||||||
|
---
|
||||||
|
# Gitea Actions runner (act_runner) — Phase 6 CI/CD (WS-GITOSS).
|
||||||
|
#
|
||||||
|
# WHY A DEDICATED, NON-APP NAMESPACE:
|
||||||
|
# The runner's job executor is a Docker-in-Docker (DinD) sidecar that MUST run
|
||||||
|
# `privileged: true` (dockerd needs it). The Kyverno `restrict-host-namespaces`
|
||||||
|
# policy denies privileged containers, but it (and the egress-class / auth-mode /
|
||||||
|
# default-deny-baseline policies) scope ONLY to namespaces labeled
|
||||||
|
# homelab.keithjackson.dev/app-namespace: "true". This namespace deliberately does
|
||||||
|
# NOT carry that label — it is CI plumbing, not a user-facing app — so the privileged
|
||||||
|
# DinD sidecar is admitted WITHOUT a policy exception. (Verified: a privileged Pod in
|
||||||
|
# a non-app namespace passes server-side admission; Kyverno test fixture
|
||||||
|
# `platform-workload` is likewise Excluded from restrict-host-namespaces.)
|
||||||
|
#
|
||||||
|
# EXECUTOR MODEL — Docker executor + DinD sidecar (privileged):
|
||||||
|
# act_runner runs each job step inside a container it launches via a Docker daemon.
|
||||||
|
# That daemon is the co-located `dind` sidecar (shared pod netns → tcp://localhost:2376,
|
||||||
|
# TLS via a shared emptyDir of certs). Tradeoff vs the host/self-hosted executor: DinD
|
||||||
|
# needs `privileged`, but it gives clean per-job isolation and lets the workflow pick its
|
||||||
|
# own job image (the pinned toolchain), which the host executor cannot. Privileged is
|
||||||
|
# contained to this CI-only namespace and the runner mounts NO ServiceAccount token
|
||||||
|
# (automountServiceAccountToken: false) — CI holds no cluster credentials (R-SEC-02).
|
||||||
|
apiVersion: v1
|
||||||
|
kind: Namespace
|
||||||
|
metadata:
|
||||||
|
name: gitea-actions
|
||||||
|
labels:
|
||||||
|
# NOTE: intentionally NO homelab.keithjackson.dev/app-namespace label (see header).
|
||||||
|
homelab.keithjackson.dev/ci: act-runner
|
||||||
|
---
|
||||||
|
# Default-deny ingress; broad egress (a CI runner legitimately needs the internet:
|
||||||
|
# ghcr.io / github.com / docker hub / dl.k8s.io / pypi for job images + pinned tools,
|
||||||
|
# and the Gitea ingress VIP for registration + repo clone). Nothing connects INTO the
|
||||||
|
# runner, so ingress is denied entirely.
|
||||||
|
apiVersion: networking.k8s.io/v1
|
||||||
|
kind: NetworkPolicy
|
||||||
|
metadata:
|
||||||
|
name: default-deny-ingress-allow-egress
|
||||||
|
namespace: gitea-actions
|
||||||
|
labels:
|
||||||
|
homelab.keithjackson.dev/baseline: default-deny
|
||||||
|
spec:
|
||||||
|
podSelector: {}
|
||||||
|
policyTypes:
|
||||||
|
- Ingress
|
||||||
|
- Egress
|
||||||
|
ingress: []
|
||||||
|
egress:
|
||||||
|
# DNS (cluster + split-horizon resolution of gitea.home.keithjackson.dev).
|
||||||
|
- to:
|
||||||
|
- namespaceSelector:
|
||||||
|
matchLabels:
|
||||||
|
kubernetes.io/metadata.name: kube-system
|
||||||
|
ports:
|
||||||
|
- protocol: UDP
|
||||||
|
port: 53
|
||||||
|
- protocol: TCP
|
||||||
|
port: 53
|
||||||
|
# Everything else the runner + DinD need: Gitea VIP + internet registries/tool hosts.
|
||||||
|
- {}
|
||||||
|
---
|
||||||
|
# Runner state (config.yaml + the `.runner` registration file) persists here so the
|
||||||
|
# runner registers exactly ONCE and reuses its identity across restarts (no duplicate
|
||||||
|
# offline rows in Gitea). RWO → Deployment strategy must be Recreate (fix/recreate-strategy-rwo).
|
||||||
|
apiVersion: v1
|
||||||
|
kind: PersistentVolumeClaim
|
||||||
|
metadata:
|
||||||
|
name: act-runner-data
|
||||||
|
namespace: gitea-actions
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: act-runner
|
||||||
|
spec:
|
||||||
|
accessModes:
|
||||||
|
- ReadWriteOnce
|
||||||
|
storageClassName: longhorn
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
storage: 1Gi
|
||||||
|
---
|
||||||
|
apiVersion: apps/v1
|
||||||
|
kind: Deployment
|
||||||
|
metadata:
|
||||||
|
name: act-runner
|
||||||
|
namespace: gitea-actions
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: act-runner
|
||||||
|
spec:
|
||||||
|
replicas: 1
|
||||||
|
strategy:
|
||||||
|
type: Recreate
|
||||||
|
selector:
|
||||||
|
matchLabels:
|
||||||
|
app.kubernetes.io/name: act-runner
|
||||||
|
template:
|
||||||
|
metadata:
|
||||||
|
labels:
|
||||||
|
app.kubernetes.io/name: act-runner
|
||||||
|
spec:
|
||||||
|
# CI holds no cluster credentials — do not mount a SA token into the runner or
|
||||||
|
# (via the shared netns) the job containers (R-SEC-02, docs/14).
|
||||||
|
automountServiceAccountToken: false
|
||||||
|
containers:
|
||||||
|
# ---- act_runner: registers with Gitea, dispatches jobs to the DinD daemon ----
|
||||||
|
- name: runner
|
||||||
|
image: gitea/act_runner:0.2.11
|
||||||
|
env:
|
||||||
|
- name: GITEA_INSTANCE_URL
|
||||||
|
value: https://gitea.home.keithjackson.dev
|
||||||
|
- name: GITEA_RUNNER_REGISTRATION_TOKEN
|
||||||
|
valueFrom:
|
||||||
|
secretKeyRef:
|
||||||
|
name: gitea-actions-runner
|
||||||
|
key: token
|
||||||
|
- name: GITEA_RUNNER_NAME
|
||||||
|
value: incluster-dind
|
||||||
|
# runs-on label -> job image. Both map to the catthehacker "act" image, which
|
||||||
|
# carries bash/git/curl/tar/python3+pip/node so .gitea/workflows scripts can
|
||||||
|
# install the pinned CI toolchain (ci/versions.env) and actions/checkout runs.
|
||||||
|
- name: GITEA_RUNNER_LABELS
|
||||||
|
value: "ubuntu-latest:docker://ghcr.io/catthehacker/ubuntu:act-22.04,homelab:docker://ghcr.io/catthehacker/ubuntu:act-22.04"
|
||||||
|
# Reach the DinD daemon over the shared pod network namespace, TLS-verified
|
||||||
|
# against the certs the sidecar drops into the shared /certs emptyDir.
|
||||||
|
- name: DOCKER_HOST
|
||||||
|
value: tcp://localhost:2376
|
||||||
|
- name: DOCKER_CERT_PATH
|
||||||
|
value: /certs/client
|
||||||
|
- name: DOCKER_TLS_VERIFY
|
||||||
|
value: "1"
|
||||||
|
volumeMounts:
|
||||||
|
- name: data
|
||||||
|
mountPath: /data
|
||||||
|
- name: certs
|
||||||
|
mountPath: /certs
|
||||||
|
readOnly: true
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 100m
|
||||||
|
memory: 128Mi
|
||||||
|
limits:
|
||||||
|
memory: 512Mi
|
||||||
|
# ---- Docker-in-Docker: the job executor daemon (privileged) ----
|
||||||
|
- name: dind
|
||||||
|
image: docker:27-dind
|
||||||
|
securityContext:
|
||||||
|
privileged: true
|
||||||
|
env:
|
||||||
|
# Generate + publish TLS certs into the shared emptyDir; dockerd then listens
|
||||||
|
# on tcp://0.0.0.0:2376 (TLS) which the runner reaches at localhost:2376.
|
||||||
|
- name: DOCKER_TLS_CERTDIR
|
||||||
|
value: /certs
|
||||||
|
volumeMounts:
|
||||||
|
- name: certs
|
||||||
|
mountPath: /certs
|
||||||
|
- name: dind-storage
|
||||||
|
mountPath: /var/lib/docker
|
||||||
|
resources:
|
||||||
|
requests:
|
||||||
|
cpu: 100m
|
||||||
|
memory: 256Mi
|
||||||
|
limits:
|
||||||
|
memory: 2Gi
|
||||||
|
volumes:
|
||||||
|
- name: data
|
||||||
|
persistentVolumeClaim:
|
||||||
|
claimName: act-runner-data
|
||||||
|
- name: certs
|
||||||
|
emptyDir: {}
|
||||||
|
- name: dind-storage
|
||||||
|
emptyDir: {}
|
||||||
@@ -18,6 +18,15 @@
|
|||||||
# it from a committed SOPS secret pins it across pod restarts/reschedules.
|
# it from a committed SOPS secret pins it across pod restarts/reschedules.
|
||||||
# - GITEA__server__ROOT_URL / __DOMAIN -> correct public clone/redirect URLs for the git host
|
# - GITEA__server__ROOT_URL / __DOMAIN -> correct public clone/redirect URLs for the git host
|
||||||
# that TLS-terminates at the shared Gateway (gitea.home.keithjackson.dev).
|
# that TLS-terminates at the shared Gateway (gitea.home.keithjackson.dev).
|
||||||
|
# - GITEA__actions__ENABLED=true -> turn on Gitea Actions explicitly (Phase 6 CI/CD, WS-GITOSS).
|
||||||
|
# Gitea 1.21+ defaults Actions ON, but pinning it here makes the CI merge gate a declared,
|
||||||
|
# reviewable property of the deployment rather than an implicit upstream default that a future
|
||||||
|
# version bump could flip. The in-cluster act_runner (ns gitea-actions) registers against it.
|
||||||
|
# - GITEA__actions__DEFAULT_ACTIONS_URL=github -> resolve `uses: <org>/<action>@ref` (e.g.
|
||||||
|
# actions/checkout) from github.com. The cluster HAS internet egress (LE/Cloudflare/ghcr already
|
||||||
|
# in use), so github-sourced actions work. A fully self-hosted/air-gapped value would require
|
||||||
|
# mirroring each action repo into this Gitea first; the cluster is not air-gapped, so `github`
|
||||||
|
# is the pragmatic, working choice (WS-GITOSS Phase-6 note).
|
||||||
# - A postStart lifecycle hook creates a BREAK-GLASS local admin idempotently (only if absent),
|
# - A postStart lifecycle hook creates a BREAK-GLASS local admin idempotently (only if absent),
|
||||||
# so an IdP outage never locks the operator out of recovery tools (docs/15 availability caveat).
|
# so an IdP outage never locks the operator out of recovery tools (docs/15 availability caveat).
|
||||||
# All state (repos, SQLite DB, app.ini, INTERNAL_TOKEN) lands on the /data Longhorn PVC.
|
# All state (repos, SQLite DB, app.ini, INTERNAL_TOKEN) lands on the /data Longhorn PVC.
|
||||||
@@ -43,6 +52,10 @@ spec:
|
|||||||
value: gitea.home.keithjackson.dev
|
value: gitea.home.keithjackson.dev
|
||||||
- name: GITEA__server__ROOT_URL
|
- name: GITEA__server__ROOT_URL
|
||||||
value: https://gitea.home.keithjackson.dev/
|
value: https://gitea.home.keithjackson.dev/
|
||||||
|
- name: GITEA__actions__ENABLED
|
||||||
|
value: "true"
|
||||||
|
- name: GITEA__actions__DEFAULT_ACTIONS_URL
|
||||||
|
value: github
|
||||||
- name: GITEA_ADMIN_USERNAME
|
- name: GITEA_ADMIN_USERNAME
|
||||||
valueFrom:
|
valueFrom:
|
||||||
secretKeyRef:
|
secretKeyRef:
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ resources:
|
|||||||
- pihole.yaml
|
- pihole.yaml
|
||||||
- gitea.yaml
|
- gitea.yaml
|
||||||
- gitea-bootstrap-secret.sops.yaml # SOPS: fixed SECRET_KEY + break-glass admin creds (decrypted by Flux)
|
- gitea-bootstrap-secret.sops.yaml # SOPS: fixed SECRET_KEY + break-glass admin creds (decrypted by Flux)
|
||||||
|
- gitea-actions.yaml # Phase 6 CI/CD: in-cluster act_runner (ns gitea-actions) + DinD
|
||||||
|
- gitea-actions-runner-secret.sops.yaml # SOPS: act_runner registration token (decrypted by Flux)
|
||||||
- sonarr.yaml
|
- sonarr.yaml
|
||||||
- radarr.yaml
|
- radarr.yaml
|
||||||
- plex.yaml
|
- plex.yaml
|
||||||
|
|||||||
Reference in new issue
Block a user