platform-cicd installs onto any Kubernetes cluster declaratively, via ArgoCD - the
same way every other cluster-config piece gets installed: a couple of ArgoCD
Application manifests in that cluster's own gitops-cluster-<name> repo. See
gitops-cluster-dev/50-platform-cicd/ for a working reference - it wires up
tektoncd/operator (Tekton Pipelines/Triggers/Chains/Dashboard/PaC in one namespace),
glidepath-catalog, and glidepath-control-plane.
Steps for a brand-new cluster:
-
Get ArgoCD running on the target cluster (out of scope here - see that cluster's own bootstrap).
-
Generate this cluster's own values, writing straight into its
gitops-cluster-<name>repo checkout (not intoplatform-cicd- see that script's own header for why):./hack/generate-cluster-values.sh <kube-context> <cluster-name> \ ../gitops-cluster-<name>/50-platform-cicd/glidepath-control-planeThis reads the cluster's own API server root CA live and generates a fresh, independent Fulcio signing root for it - it never copies another cluster's trust material. Commit and push the resulting file.
-
Add the two Application manifests (control-plane, catalog) to that repo, each multi-source: one source is this platform's chart at
charts/glidepath-catalog/charts/glidepath-control-plane, the other adirectorysource (exclude: "*") pointed at the cluster-config repo itself,$ref'd forvalueFiles- seegitops-cluster-dev/50-platform-cicd/glidepath-control-plane/application.yamlfor the exact shape, and architecture-decisions ADR-0006 for why cluster state never lives insideplatform-cicditself. -
Push. ArgoCD takes it from there.
This keeps platform-cicd installable standalone on any cluster - it carries zero
cluster-specific secrets or identity in its own repo. For local/kind development,
hack/kind-config.yaml creates a raw, Calico-enabled cluster to point ArgoCD at -
there's no separate imperative install path beyond that; get ArgoCD running, then
follow the steps above.
Grafana serves the platform's own dashboards (rendered as ConfigMaps by the control-plane chart, not a separate apply step). The Tekton Dashboard is the complementary low-level view - installed read-only deliberately, matching this platform's PaaS/RBAC posture.
- NetworkPolicy needs a real CNI.
default-deny/allow-from-same-namespacemanifests are silent no-ops on a CNI that doesn't enforceNetworkPolicy(e.g. kindnet). TokenReview authentication on the broker - not NetworkPolicy - is the actual trust boundary (see chaining.md); NetworkPolicy is defense-in-depth on top of it. Pin a Calico/Cilium-class CNI for anything beyond a shared dev cluster. - The GitHub App needs a public endpoint. Pipelines-as-Code's webhook delivery
can't reach a local/private cluster directly - front the PaC controller with a
tunnel (
cloudflared,ngrok) for local dev, or real ingress/DNS for anything else. token-review-interceptor/glidepath-relayimages areIfNotPresent+:latest. A source change underglidepath/broker/cmd/does nothing to a running cluster until you rebuild, push, andkubectl rollout restartthe affected Deployment - there's no CI wired to a private cluster to do this automatically.- Pod Security Standards
restricted+ kaniko: validate this combination against your actual target CNI before onboarding real apps - see rootless-builds.md. - Tekton's Affinity Assistant is off (
coschedule: disabled). Set in the cluster repo's50-platform-cicd/tekton-operator/tektonconfig.yaml(2026-10-09). The default (workspaces) starts a StatefulSet and placeholder pod before every build and test run, 10-25 s each, only to keep TaskRuns that share a PVC on one node. A single-node cluster, or one whose StorageClass pins a PV to a node (local-path), gets that anyway. On a multi-node cluster with network-attachedReadWriteOncestorage usecoschedule: pipelineruns: the per-appbuild-cachePVC and the run'ssourcePVC are both mounted bybuild-sourceandunit-test, and must land together. - One Trivy server holds the vulnerability DB.
glidepath-control-planerunstrivy-serverinplatform-system(trivyServervalues; its tag must equalTRIVY_VERSIONincatalog/toolbox/Dockerfile), andimage-scanscans against it (trivyServerUrlin the catalog values). Before 2026-10-09 every scan pod downloaded the ~120 MiB DB itself. If the server does not answer/healthzor fails mid-scan, the scan runs standalone and downloads the DB as before, so an outage costs time, not a false failure.generate-sbomdoes not use it: a CycloneDX-only run has no vulnerability scanner and never needed the DB. - Ground deploys ask Argo CD to refresh.
deploy-manifestsannotates the environment's Application inargocd-appswithargocd.argoproj.io/refresh=hardright after its push, so the new release file is picked up in seconds instead of on the next 120 s (+60 s jitter) poll. The grant is per app (glidepath-apptemplates/env/argocd-refresh-rbac.yaml, get+patch on its own Ground Applications), and the control plane's ValidatingAdmissionPolicypipeline-runner-application-refresh-onlydenies any change from apipeline-runnerServiceAccount other than that annotation and thehangar.io/dora-*ones, so the grant can never change an Application's project, destination or sync operation. It needs Kubernetes 1.30+ (ValidatingAdmissionPolicy GA). Without the grant the deploy still works, at poll speed.
A separate bootstrap, covered in multi-cluster.md -
hack/bootstrap-upper-cluster.sh installs just ArgoCD on a target cluster; this
platform's own Tekton/broker never runs there.