Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions .github/workflows/cd.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: 🚀 CD

# Publishes the org's declarative GitHub state (deploy/) as a cosign-signed OCI
# artifact consumed by the platform cluster's `github-config` tenant. There is
# NO container image here — only manifests — so it delegates to the shared
# manifests-only publish workflow in devantler-tech/reusable-workflows (the
# manifests-only sibling of publish-app.yaml).
#
# The OCI path is `github-config` (not the repo name) because `.github` is an
# invalid OCI path component (leading dot) — hence the oci-name override.
#
# Because signing runs INSIDE the reusable workflow, the cosign certificate
# identity (OIDC subject) is that workflow's path —
# https://github.com/devantler-tech/reusable-workflows/.github/workflows/publish-manifests.yaml@<ref>
# — NOT this repo's cd.yaml. The platform `github-config` OCIRepository's
# verify.matchOIDCIdentity.subject must match that (see
# k8s/providers/hetzner/github/sync.yaml in devantler-tech/platform).

on:
push:
tags:
- "v*"

permissions: {}

jobs:
publish-manifests:
name: 🗳️ Publish manifests
permissions:
contents: read # checkout
packages: write # push the OCI artifact to GHCR
id-token: write # keyless cosign signing (Fulcio + Rekor via GitHub OIDC)
# Pinned to the released commit SHA (zizmor blanket policy requires a hash;
# the SHA must be reachable from a tag, or zizmor flags it as an impostor).
# Renovate manages this pin, as it does for publish-app.yaml elsewhere.
# The platform verifier matches `@.+$`, so the ref does not affect cosign
# verification.
uses: devantler-tech/reusable-workflows/.github/workflows/publish-manifests.yaml@f974f62a8cb396130fa084d209ad1fc17e99b9fc # v5.6.1
with:
oci-name: devantler-tech/github-config
43 changes: 43 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: CI

# Validates the declarative GitHub state in deploy/ on every PR, and reports the
# `CI - Required Checks` status the org ruleset requires before merge. (cd.yaml
# only runs on v* tags, so without this no PR-time required check is produced and
# the PR stays merge-BLOCKED.)

on:
pull_request:
merge_group:

permissions: {}

jobs:
validate-manifests:
name: 🧹 Validate manifests
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: 📑 Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false

- name: 🧹 Validate deploy/ kustomize build
# kubectl (with built-in kustomize) is preinstalled on the runner. A
# successful build proves the manifests are well-formed; the Crossplane
# CRDs are applied/validated on-cluster, not here.
run: kubectl kustomize deploy/ > /dev/null

ci-required-checks:
name: CI - Required Checks
runs-on: ubuntu-latest
needs: [validate-manifests]
if: always()
permissions: {}
steps:
- name: 📊 Evaluate job results
uses: devantler-tech/actions/aggregate-job-checks@8ce19528eda1ef50f3d028b685c53aebaed6ae62 # v5.4.1
with:
job-results: >-
${{ needs.validate-manifests.result }}
22 changes: 22 additions & 0 deletions deploy/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# `deploy/` — declarative GitHub state

This directory is the source of truth for the devantler-tech org's GitHub
configuration, expressed as [Crossplane](https://crossplane.io) managed
resources (via
[provider-upjet-github](https://github.com/crossplane-contrib/provider-upjet-github)).

On every `v*` tag, [`cd.yaml`](../.github/workflows/cd.yaml) publishes this
directory as a **cosign-signed OCI artifact** to
`ghcr.io/devantler-tech/github-config/manifests`. The
[platform](https://github.com/devantler-tech/platform) cluster onboards it as
the **`github-config` tenant**: it verifies the signature, then Flux + Crossplane
reconcile the live GitHub org to match these manifests — including reverting
out-of-band changes made in the GitHub UI.

- `repositories/` — one `Repository` per managed repo.

See the platform repo's
[`docs/github-management.md`](https://github.com/devantler-tech/platform/blob/main/docs/github-management.md)
for the architecture, the GitHub App credential setup, and the **Observe-first**
adoption flow for bringing an existing repository under management without any
risk of recreating or deleting it.
10 changes: 10 additions & 0 deletions deploy/kustomization.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Root of the manifests artifact published to
# oci://ghcr.io/devantler-tech/github-config/manifests and applied by the
# platform's `github-config` tenant Kustomization (path: .). It carries the
# devantler-tech org's desired GitHub state as Crossplane managed resources.
# The target namespace (github-config) is injected by the platform-side
# Kustomization's targetNamespace, so manifests here stay namespace-agnostic.
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- repositories/
15 changes: 15 additions & 0 deletions deploy/repositories/ksail.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
apiVersion: repo.github.m.upbound.io/v1alpha1
kind: Repository
metadata:
name: ksail
annotations:
# Binds this resource to the EXISTING devantler-tech/ksail repository
# (owner comes from the ProviderConfig credentials).
crossplane.io/external-name: ksail
spec:
# Observe-only while adopting — see repositories/kustomization.yaml.
managementPolicies: ["Observe"]
forProvider: {}
providerConfigRef:
kind: ProviderConfig
name: default
12 changes: 12 additions & 0 deletions deploy/repositories/kustomization.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# One file per managed repository. Adoption is Observe-first: a repo starts with
# managementPolicies: ["Observe"] (read-only — Crossplane mirrors live state into
# status.atProvider and never writes), then forProvider is backfilled and the
# policy promoted to the full set EXCEPT Delete. Namespaced managed resources
# have no deletionPolicy; omitting Delete is what guarantees Crossplane can never
# delete a real GitHub repository. See devantler-tech/platform
# docs/github-management.md for the full adoption flow.
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- platform.yaml
- ksail.yaml
17 changes: 17 additions & 0 deletions deploy/repositories/platform.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
apiVersion: repo.github.m.upbound.io/v1alpha1
kind: Repository
metadata:
name: platform
annotations:
# Binds this resource to the EXISTING devantler-tech/platform repository
# (owner comes from the ProviderConfig credentials).
crossplane.io/external-name: platform
spec:
# Observe-only while adopting: Crossplane fills status.atProvider and never
# writes to GitHub. Backfill forProvider from status.atProvider, then promote
# to ["Observe","Create","Update","LateInitialize"] (everything except Delete).
managementPolicies: ["Observe"]
forProvider: {}
providerConfigRef:
kind: ProviderConfig
name: default