From 603aaaabffc5722f4e65edadc1cf70fc803c8a51 Mon Sep 17 00:00:00 2001 From: Jeroen Grusewski Date: Tue, 21 Jul 2026 10:14:48 +0000 Subject: [PATCH] ci(fxhnt): single-trigger sensor + in-workflow git-diff mode decision --- infra/argo/fxhnt-build-sensor.yaml | 57 ++++++++++++++++++ .../argo/fxhnt-cockpit-workflowtemplate.yaml | 59 ++++++++++++++++++- 2 files changed, 113 insertions(+), 3 deletions(-) create mode 100644 infra/argo/fxhnt-build-sensor.yaml diff --git a/infra/argo/fxhnt-build-sensor.yaml b/infra/argo/fxhnt-build-sensor.yaml new file mode 100644 index 0000000..570e069 --- /dev/null +++ b/infra/argo/fxhnt-build-sensor.yaml @@ -0,0 +1,57 @@ +# Argo Events Sensor — hears the shared Gitea webhook for pushes to gitadmin/fxhnt master, and submits the +# fxhnt-cockpit WorkflowTemplate (ns foxhunt). Structurally identical to build-msp-portal (single dependency, +# single trigger) — the build-vs-deploy decision is made INSIDE the workflow via `git diff` (decide-mode step in +# fxhnt-cockpit-workflowtemplate.yaml), not via a sensor-side path filter. See +# docs/superpowers/plans/2026-07-21-fxhnt-cicd-pipeline.md Task 3 design note: the Sensor CRD here has an open +# schema, so trigger-level path-filter negation can't be reliably validated — keeping the sensor simple is the +# robust choice. +apiVersion: argoproj.io/v1alpha1 +kind: Sensor +metadata: + name: build-fxhnt + namespace: argo-events + labels: { app.kubernetes.io/instance: build-pipeline } +spec: + dependencies: + - name: push-to-master + eventSourceName: gitea-webhook + eventName: push + filters: + dataLogicalOperator: and + data: + - path: body.ref + type: string + comparator: "=" + value: ["refs/heads/master"] + - path: body.pusher.username + type: string + comparator: "!=" + value: ["argocd-image-updater"] + template: + serviceAccountName: argo-events-sa + triggers: + - template: + name: submit-build + argoWorkflow: + operation: submit + source: + resource: + apiVersion: argoproj.io/v1alpha1 + kind: Workflow + metadata: + generateName: build-fxhnt- + namespace: foxhunt + spec: + workflowTemplateRef: + name: fxhnt-cockpit + arguments: + parameters: + - name: commit-sha + value: "" + - name: mode + value: auto + parameters: + - src: + dependencyName: push-to-master + dataKey: body.after + dest: spec.arguments.parameters.0.value diff --git a/infra/argo/fxhnt-cockpit-workflowtemplate.yaml b/infra/argo/fxhnt-cockpit-workflowtemplate.yaml index 6455242..675d000 100644 --- a/infra/argo/fxhnt-cockpit-workflowtemplate.yaml +++ b/infra/argo/fxhnt-cockpit-workflowtemplate.yaml @@ -24,15 +24,68 @@ spec: arguments: parameters: - { name: commit-sha, value: HEAD } - - { name: mode, value: build } # "build" = kaniko rebuild + deploy; "deploy" = skip build, just apply + rollout restart (git-sync re-pulls code). The sensor routes src-only pushes to "deploy". + - { name: mode, value: auto } # "auto" = decide-mode git-diffs commit-sha~1..commit-sha to pick build/deploy; "build" = force kaniko rebuild + deploy; "deploy" = force skip build, just apply + rollout restart (git-sync re-pulls code). A human can still force build/deploy via `argo submit -p mode=...`. templates: - name: pipeline dag: tasks: + # decide-mode: when mode=auto, git-diffs the pushed commit against its parent to pick build vs deploy + # (dep/Dockerfile changes -> build; anything else, or a missing parent, -> build/deploy per script logic + # below). When mode is forced (build|deploy), decide-mode just echoes it back unchanged. + - { name: decide-mode, template: decide-mode } # Skip the ~5min kaniko build for code-only pushes (mode=deploy) — git-sync re-pulls src/ on the # rollout restart, so only dep/Dockerfile changes need a new image (mode=build). - - { name: build, template: kaniko-build, when: "{{workflow.parameters.mode}} == build" } - - { name: deploy, template: kubectl-deploy, depends: "build.Succeeded || build.Skipped" } + - { name: build, template: kaniko-build, depends: decide-mode, when: "{{tasks.decide-mode.outputs.parameters.mode}} == build" } + - { name: deploy, template: kubectl-deploy, depends: "decide-mode && (build.Succeeded || build.Skipped)" } + + # --- decide-mode: resolve "auto" to build|deploy via git diff of the pushed commit vs its parent --- + - name: decide-mode + metadata: + labels: { app.kubernetes.io/part-of: argo-workflows } + nodeSelector: { k8s.scaleway.com/pool-name: large, topology.kubernetes.io/zone: fr-par-2 } + tolerations: [{ effect: NoSchedule, key: node.cilium.io/agent-not-ready, operator: Exists }] + outputs: + parameters: + - name: mode + valueFrom: { path: /tmp/mode } + container: + image: alpine/git:latest + command: [/bin/sh, -c] + args: + - | + set -e + mkdir -p /root/.ssh + cp /etc/git-ssh/ssh-privatekey /root/.ssh/id_ed25519 + chmod 600 /root/.ssh/id_ed25519 + printf 'Host *\n StrictHostKeyChecking no\n UserKnownHostsFile /dev/null\n' > /root/.ssh/config + git clone --filter=blob:none \ + "ssh://git@gitea-sshd.foxhunt.svc.cluster.local:22/gitadmin/fxhnt.git" /workspace/src + cd /workspace/src && git checkout "{{workflow.parameters.commit-sha}}" + + if [ "{{workflow.parameters.mode}}" != "auto" ]; then + echo -n "{{workflow.parameters.mode}}" > /tmp/mode + else + SHA="{{workflow.parameters.commit-sha}}" + RESOLVED="$(git rev-parse "$SHA")" + if CHANGED=$(git diff --name-only "${RESOLVED}~1" "${RESOLVED}" 2>/dev/null); then + # diff succeeded (parent exists): build only if a dep/Dockerfile file changed, else deploy. + if echo "$CHANGED" | grep -qE '^pyproject\.toml$|^infra/docker/fxhnt\.Dockerfile$'; then + echo -n build > /tmp/mode + else + echo -n deploy > /tmp/mode + fi + else + # diff failed (no parent -- first commit / shallow history): default to build (safe, never wrong). + echo -n build > /tmp/mode + fi + fi + cat /tmp/mode + volumeMounts: + - { name: git-ssh-key, mountPath: /etc/git-ssh, readOnly: true } + - { name: workspace, mountPath: /workspace } + volumes: + - { name: workspace, emptyDir: {} } + - { name: git-ssh-key, secret: { secretName: argo-git-ssh-key, defaultMode: 256 } } # --- build: Kaniko builds infra/docker/fxhnt.Dockerfile -> internal registry --- - name: kaniko-build