Promote php-homepage 1.0.0-1-g7d1db6f

This commit is contained in:
2026-10-18 17:38:03 +02:00
parent 4e763a3c5b
commit 8e53abf9db
2 changed files with 78 additions and 124 deletions
+71 -123
View File
@@ -1,141 +1,95 @@
# Homelab GitOps
Argo CD is the only component that deploys the resources in this repository. Application CI publishes immutable
container images and then commits the selected image tag here; it never receives Kubernetes credentials.
Argo CD is the only component that deploys resources from this repository. Application repositories publish
immutable images and/or Helm charts, then commit the exact chart version selected by the corresponding Argo
`Application`. CI never receives Kubernetes credentials.
## Repository layout
## Ownership boundary
- `apps/immich-sync/` is the desired Kubernetes state for Immich Share Sync.
- `apps/immichframe/` wraps the reusable ImmichFrame chart with this cluster's
hostname, album selection, resources, and encrypted API credential.
- `apps/metallb/` pins the upstream native MetalLB installation and owns this
cluster's L2 address pool.
- `apps/argocd/` wraps the pinned upstream Argo CD chart with this cluster's
ingress, Authentik OIDC, RBAC, and secret-reference configuration.
- `apps/brgkepler-com/`, `apps/gschissenes-equipment/`,
`apps/immich-crack/`, `apps/pfostendynamics-com/`, and
`apps/watschn-studio/` pin the shared PHP homepage chart and reference their
existing CephFS content claims.
- `apps/brunner-ninja/`, `apps/flaresolverr/`, `apps/ombi/`,
`apps/tautulli/`, `apps/uptime-kuma/`, and `apps/wiki-js/` pin their existing
upstream chart releases and cluster values.
- `apps/mosquitto/` and `apps/visual-studio-code/` preserve their small local
manifest contracts; `apps/artcraft/` and `apps/coturn/` use local adapter
charts for their repeated or multi-resource third-party integrations.
- `bootstrap/` contains Argo CD projects and Applications, including the
upstream Sealed Secrets controller and the five PHP sites above.
- `scripts/update-immich-sync-image.sh` is the single image-promotion interface used by CI.
- `scripts/update-immichframe-chart.sh` is the chart-promotion interface used
by ImmichFrame chart CI.
- An application repository owns its image, reusable Kubernetes behavior, probes, services, ingress capability,
persistence capability, monitoring capability, tests, and Helm chart.
- This repository owns only Argo Applications, exact chart selections, cluster values such as domains, addresses,
storage classes and sizes, existing PVC names, resource sizing, and sealed credentials.
- A maintained chart records the upstream or custom application release in `appVersion`.
- One independently releasable workload has one chart and one Argo Application. The 13 Artcraft applications are
separate charts and Applications but all target the shared `artcraft` namespace.
- Upstream charts are referenced directly. GitOps-local dependency wrappers are forbidden.
- Secrets are either references to existing Secrets or `SealedSecret` ciphertext. Plaintext never belongs here.
The only canonical Immich Share Sync image version is `images[0].newTag` in
`apps/immich-sync/kustomization.yaml`. The resource manifest deliberately omits a tag, and Kustomize supplies the
immutable full Git SHA while rendering.
See [docs/chart-ownership.md](docs/chart-ownership.md) for the complete chart versioning and promotion contract.
The only canonical ImmichFrame chart version is the `immichframe` dependency
in `apps/immichframe/Chart.yaml`. Chart CI publishes a unique immutable version
and commits that dependency update here. The chart owns the reusable workload
shape; `apps/immichframe/values.yaml` owns only this cluster's choices.
## Layout
## First-time setup
- `bootstrap/` contains AppProjects and Applications.
- `apps/<name>/values.yaml` contains only this cluster's values for that chart.
- `apps/<name>/resources/` is allowed only for genuinely cluster-owned resources, such as a MetalLB address pool or
an ImmichFrame `SealedSecret`.
- `scripts/update-chart-version.sh` is the common CI promotion interface.
The cluster already runs Argo CD in the `argocd` namespace and exposes its UI at <https://cd.brunner.ninja>.
## Version and promotion flow
Argo CD manages its own chart after bootstrap. If the controller does not
exist, recover it first from the same canonical chart dependency and values,
then recreate the AppProjects and Applications:
Maintained chart repositories tag releases with `vMAJOR.MINOR.PATCH` (or `<chart>-vMAJOR.MINOR.PATCH` in the shared
chart repository). `git describe --tags --long` is canonical:
- tag `v1.0.0` packages chart `1.0.0`;
- the next commit packages `1.0.0-1-g41627ab`;
- the Git hash is retained to prevent two histories from producing the same immutable package version;
- dirty trees are rejected.
After publishing succeeds, CI checks out this repository with `GITOPS_TOKEN`, runs:
```sh
./scripts/update-chart-version.sh APPLICATION CHART_VERSION
```
and commits `bootstrap/APPLICATION.yaml`. Argo CD observes that Git commit, renders the published chart with the
cluster values, and reconciles Kubernetes.
Upstream chart upgrades have no repository under our control, so their exact `targetRevision` is updated here
manually or by a dependency updater. They do not need a fake promotion repository.
## Gitea credentials
Prefer owner- or organization-level Actions secrets so every maintained chart repository receives the same narrowly
scoped credentials:
- `PACKAGE_USERNAME` and `PACKAGE_TOKEN`: upload-only credentials for the `feedc0de` Helm package repository;
- `GITOPS_TOKEN`: a token with `write:repository` access to only `feedc0de/homelab-gitops`;
- `QUAY_USERNAME` and `QUAY_TOKEN`: required only by repositories that publish container images.
The workflow commits as the normal `feedc0de` Git identity. The token determines authorization; the commit name is
not a separate Gitea account. A dedicated promoter account is still preferable if a one-repository permission scope
is desired.
## Bootstrap and recovery
The expected kubectl context is `kubernetes-admin@kubernetes`.
```sh
./bootstrap/install-argocd.sh
./bootstrap/install.sh
```
The recovery installer preserves `argocd-secret`, `argocd-oidc`,
`argocd-notifications-secret`, and `argocd-redis`. Back up or provision those
Secrets separately; none of their values belongs in this public repository.
Argo CD manages its own upstream chart after recovery. The repo-server is given cluster-local host aliases for
`code.brunner.ninja` and `brunner.ninja`, routed through the Traefik ClusterIP, because this cluster cannot hairpin to
the public WAN address. The canonical alias configuration is in `apps/argocd/values.yaml`.
1. Push this repository so Argo CD can read its public HTTPS URL.
2. Configure the `immich-sync` repository's cross-repository token as described below.
3. Review the rendered workload:
Before adopting a stateful workload, record its PVC UID and volume, render and dry-run the new chart, then verify the
same values after synchronization:
```sh
kubectl kustomize apps/immich-sync
```
```sh
kubectl get pvc -A -o custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,UID:.metadata.uid,VOLUME:.spec.volumeName'
kubectl --namespace argocd get applications \
-o custom-columns='NAME:.metadata.name,SYNC:.status.sync.status,HEALTH:.status.health.status'
```
4. Record the existing persistent volume before the first synchronization:
```sh
kubectl --namespace default get pvc immich-sync-stats \
-o jsonpath='{.metadata.uid}{"|"}{.spec.volumeName}{"\n"}'
```
5. Bootstrap the Argo CD resources:
```sh
./bootstrap/install.sh
```
6. Inspect the result in the UI or CLI:
```sh
argocd app get immich-sync
argocd app diff immich-sync
kubectl --namespace default rollout status deployment/immich-sync
kubectl --namespace default get deployment immich-sync \
-o jsonpath='{.spec.template.spec.containers[0].image}{"\n"}'
```
7. Confirm that the PVC UID and volume name are unchanged. Only after Argo CD reports the application `Synced` and
`Healthy`, remove the obsolete CI deployer and delete the old `KUBE_CONFIG_BASE64` secret from Gitea:
```sh
kubectl --namespace default delete rolebinding/immich-sync-deployer \
role/immich-sync-deployer serviceaccount/immich-sync-deployer \
secret/immich-sync-deployer-token
```
The bootstrap Application enables automatic synchronization, pruning, and self-healing. Consequently, direct edits
to its managed Kubernetes resources are reverted to Git. Change Git when experimenting, observe `OutOfSync`, and then
watch Argo CD return the application to `Synced` and `Healthy`.
## Give Immich Share Sync CI permission to promote images
The built-in Gitea Actions job token cannot write to another repository. Create a dedicated bot account, for example
`gitops-promoter`, add it to `feedc0de/homelab-gitops` with repository **Write** permission, and create a personal
access token for that account with only the `write:repository` scope.
In `feedc0de/immich-sync`, open **Settings → Actions → Secrets** and create `GITOPS_TOKEN` containing that token. The
workflow passes it directly to `actions/checkout` while checking out this repository; the checkout action configures
authenticated HTTPS for the final `git push` without placing the token in a committed URL.
The main-branch workflow now performs this sequence:
1. Build and push `registry.brunner.ninja/feedc0de/immich-sync:<source SHA>`.
2. Verify that the completed build still belongs to the current `main` commit.
3. Update `apps/immich-sync/kustomization.yaml` with that SHA.
4. Commit and push the promotion to this repository.
5. Let Argo CD detect and synchronize the Git commit.
For a personal token owned by `feedc0de`, `write:repository` also applies to other repositories that account can
write. A dedicated bot restricted to this one repository therefore gives the token a meaningfully smaller blast
radius.
## Manual recovery path
Argo CD is the normal deployment mechanism. If it is unavailable, `apps/immich-sync/install.sh` renders, performs a
server-side dry-run, applies the same Kustomize source, and waits for the Deployment rollout. A later Argo CD refresh
will see the same desired resources.
Automatic sync, pruning, and self-healing mean direct live edits are temporary. Change Git and observe Argo return
the Application to `Synced` and `Healthy`.
## Sealed Secrets
Argo CD installs the official Sealed Secrets chart pinned in
`bootstrap/sealed-secrets.yaml`. Public Git stores only `SealedSecret`
ciphertext. The controller decrypts that ciphertext into namespace-scoped
Kubernetes Secrets; the private sealing key never belongs in this repository.
Install the matching `kubeseal` client, then seal a Secret for its final name
and namespace. The default strict scope prevents ciphertext from being moved to
another name or namespace:
Argo CD installs the official Sealed Secrets chart. Public Git stores only `SealedSecret` ciphertext; the controller's
private sealing key must be backed up to encrypted offline storage and never committed.
```sh
kubectl --namespace default create secret generic example \
@@ -146,9 +100,3 @@ kubectl --namespace default create secret generic example \
--controller-namespace sealed-secrets \
--format yaml > example-sealed-secret.yaml
```
The controller's generated private key is the disaster-recovery key for every
sealed value. Back up the Secrets labelled
`sealedsecrets.bitnami.com/sealed-secrets-key=active` from namespace
`sealed-secrets` to an encrypted offline store. Do not commit that backup. A
cluster rebuilt without one of those keys cannot decrypt existing ciphertext.
+7 -1
View File
@@ -9,7 +9,10 @@ Each independently versioned application has one chart and one Argo CD
Application. This includes each Artcraft application: WordCraft, GridCraft,
DeckCraft, PhotoCraft, LightCraft, VectorCraft, FilmCraft, EffectCraft,
DesignCraft, FigCraft, PdfCraft, SoundCraft, and CADCraft are separate releases,
not one suite chart.
not one suite chart. They share the `artcraft` namespace but do not share
Kubernetes resource ownership. Their static upstream release bundles and nginx
configuration are baked into distinct immutable images; pods never download
application code at startup.
Maintained chart repositories set `Chart.yaml.appVersion` to the tested
application/image version. CI derives the chart package version from Git:
@@ -18,6 +21,9 @@ application/image version. CI derives the chart package version from Git:
- the next commit produces a unique version such as
`1.0.0-1-g41627abcdef`.
The shared chart repository uses per-chart tags such as
`wordcraft-v1.0.0`; a single-chart repository uses `v1.0.0`.
The hash is retained to prevent package-version collisions. CI publishes to
`https://brunner.ninja/charts`, then uses `GITOPS_TOKEN` to update only the
matching Argo Application's exact Helm `targetRevision`. Argo CD performs the