TBD-A69. PR #2005 fixed build-organization-controller.yaml only. The other six controller workflows (application, blueprint, continuum, environment, sandbox, useraccess) had the same gaps that caused the #1997 18h deploy gap: - application-controller: missing pkg/** in path filter (auto-bump already present from earlier work). - blueprint, continuum, environment, useraccess: missing BOTH pkg/** path filter AND auto-bump pipeline (permissions promotion + values.yaml bump + commit/push + blueprint-release dispatch). - sandbox: already complete (pkg/** + auto-bump to platform/sandbox chart) — left untouched. Each updated workflow inherits the canonical shape from build-organization-controller.yaml (PR #2005): 1. `core/controllers/pkg/**` added to BOTH push.paths and pull_request.paths. Without this, a fix that only touches the shared HTTP-client tree (gitea/keycloak/kc-mappers) silently fails to rebuild the controller image. 2. `permissions.contents: write` + `actions: write` so the build job can push the values.yaml bump and dispatch the downstream chart re-publish. 3. An awk-scoped `Bump controllers.<who>.image.tag in values.yaml` step that updates ONLY the targeted controller's tag (verified locally — sibling tags remain untouched). 4. A commit/push step that bumps products/catalyst/chart/values.yaml (or products/continuum/chart/values.yaml for continuum, which has its own chart). 5. A `gh workflow run blueprint-release.yaml` dispatch so the bot-pushed commit fires the downstream chart re-publish (GitHub Actions silently filters bot pushes from path-trigger workflows otherwise). Adds two new files to lock the shape in: - `scripts/check-controller-workflow-uniformity.sh` — a CI regression test that grep-asserts every controller workflow has the canonical pkg/** filter + auto-bump pipeline. Fails loudly if any new controller workflow ships without the canonical shape, or if an existing one regresses. - `.github/workflows/check-controller-workflow-uniformity.yaml` — push-on-touch + pull_request-on-touch event-driven wrapper that runs the script. Mirrors the shape of check-vendor-coupling.yaml. Verified locally: - YAML syntax valid for all 7 controller workflows + the new check workflow. - Regression script passes on all 7 controller workflows. - Simulated awk bumps against products/catalyst/chart/values.yaml and products/continuum/chart/values.yaml — each script bumps ONLY the targeted controller's tag, sibling tags untouched. No chart bumps. No Go/chart changes. CI-workflow-only. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
213 lines
9.0 KiB
YAML
213 lines
9.0 KiB
YAML
name: Build environment-controller
|
|
|
|
# environment-controller — slice C2 of EPIC-0 #1095. Watches
|
|
# Environment.catalyst.openova.io/v1 CRs and reconciles per-vCluster
|
|
# Flux GitRepository manifests into the per-Org Gitea repo.
|
|
#
|
|
# Per docs/INVIOLABLE-PRINCIPLES.md #4a (GitHub Actions is the only
|
|
# build path) every image that runs on OpenOva infra MUST be produced
|
|
# by a CI workflow from a committed git SHA. Mirrors the existing
|
|
# build-cert-manager-dynadot-webhook.yaml shape — same auth flow,
|
|
# same cosign keyless signing, same SBOM attestation.
|
|
|
|
on:
|
|
push:
|
|
paths:
|
|
- 'core/controllers/environment/**'
|
|
- 'core/controllers/internal/**'
|
|
# core/controllers/pkg/** is the shared HTTP-client tree (gitea,
|
|
# keycloak, kc-mappers, …) consumed by every Group C controller's
|
|
# Containerfile via `COPY core/controllers/pkg`. Without this path
|
|
# entry a change to the shared pkg/ tree rebuilds the image only
|
|
# if the same PR also happens to touch files under environment/ —
|
|
# which silently held the t38 #1997 gitea-405 fix in main for
|
|
# ~12h. Uniform pattern across every build-*-controller.yaml
|
|
# (TBD-A69 #2006).
|
|
- 'core/controllers/pkg/**'
|
|
- 'core/controllers/go.mod'
|
|
- 'core/controllers/go.sum'
|
|
- '.github/workflows/build-environment-controller.yaml'
|
|
branches: [main]
|
|
pull_request:
|
|
paths:
|
|
- 'core/controllers/environment/**'
|
|
- 'core/controllers/internal/**'
|
|
- 'core/controllers/pkg/**'
|
|
- 'core/controllers/go.mod'
|
|
- 'core/controllers/go.sum'
|
|
- '.github/workflows/build-environment-controller.yaml'
|
|
workflow_dispatch:
|
|
|
|
env:
|
|
REGISTRY: ghcr.io
|
|
IMAGE: ghcr.io/openova-io/openova/environment-controller
|
|
|
|
jobs:
|
|
build:
|
|
runs-on: ubuntu-latest
|
|
permissions:
|
|
# contents: write — the deploy step below pushes a values.yaml SHA
|
|
# bump back to main so the bp-catalyst-platform chart picks up the
|
|
# newly-built image without an operator manually editing the file
|
|
# (per `feedback_no_mvp_no_workarounds.md` rule 1: target-state,
|
|
# never "manual follow-up bump"). Pre-#2006 this workflow shipped
|
|
# without auto-bump — same deploy-gap class as #1997.
|
|
contents: write
|
|
packages: write
|
|
# id-token write is required by cosign keyless signing (Sigstore).
|
|
id-token: write
|
|
# actions: write — required for `gh workflow run` to dispatch the
|
|
# downstream blueprint-release chart re-publish workflow.
|
|
actions: write
|
|
outputs:
|
|
sha_short: ${{ steps.vars.outputs.sha_short }}
|
|
digest: ${{ steps.build.outputs.digest }}
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Set short SHA
|
|
id: vars
|
|
run: echo "sha_short=$(echo $GITHUB_SHA | head -c 7)" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Set up Go
|
|
uses: actions/setup-go@v5
|
|
with:
|
|
go-version: '1.23'
|
|
cache-dependency-path: |
|
|
core/controllers/go.sum
|
|
|
|
- name: go vet
|
|
working-directory: core/controllers
|
|
# Slice CC1 (#1095) consolidated the 5 Group C controllers into
|
|
# a single shared go.mod. Vet scoped to this controller's tree
|
|
# plus the shared internal/ helpers it depends on.
|
|
run: go vet ./environment/... ./internal/...
|
|
|
|
- name: Run unit tests
|
|
working-directory: core/controllers
|
|
run: go test -count=1 -race ./environment/... ./internal/...
|
|
|
|
# On pull_request runs we stop here — image push requires
|
|
# `packages: write` which only main-branch authors hold.
|
|
- name: Login to GHCR
|
|
if: github.event_name != 'pull_request'
|
|
uses: docker/login-action@v3
|
|
with:
|
|
registry: ${{ env.REGISTRY }}
|
|
username: ${{ github.actor }}
|
|
password: ${{ secrets.GITHUB_TOKEN }}
|
|
|
|
- name: Set up Docker Buildx
|
|
if: github.event_name != 'pull_request'
|
|
uses: docker/setup-buildx-action@v3
|
|
|
|
- name: Build and push image
|
|
id: build
|
|
if: github.event_name != 'pull_request'
|
|
uses: docker/build-push-action@v6
|
|
with:
|
|
# Build context is the repository root so the Containerfile's
|
|
# COPY paths can reach core/controllers/environment/.
|
|
context: .
|
|
file: core/controllers/environment/Containerfile
|
|
push: true
|
|
tags: |
|
|
${{ env.IMAGE }}:${{ steps.vars.outputs.sha_short }}
|
|
${{ env.IMAGE }}:latest
|
|
labels: |
|
|
org.opencontainers.image.source=https://github.com/openova-io/openova
|
|
org.opencontainers.image.revision=${{ github.sha }}
|
|
org.opencontainers.image.title=environment-controller
|
|
org.opencontainers.image.description=Reconciles Environment.catalyst.openova.io/v1 → Gitea + Flux GitRepository (slice C2 of EPIC-0 #1095)
|
|
# provenance=false: containerd 1.7.x on k3s mis-resolves the
|
|
# provenance attestation manifest. SBOM attestation handled by
|
|
# the cosign attest step below.
|
|
provenance: false
|
|
sbom: false
|
|
|
|
- name: Install cosign
|
|
if: github.event_name != 'pull_request'
|
|
uses: sigstore/cosign-installer@v3
|
|
|
|
- name: Sign image with cosign (keyless)
|
|
if: github.event_name != 'pull_request'
|
|
env:
|
|
DIGEST: ${{ steps.build.outputs.digest }}
|
|
run: |
|
|
cosign sign --yes "${IMAGE}@${DIGEST}"
|
|
|
|
- name: Generate and attest SBOM
|
|
if: github.event_name != 'pull_request'
|
|
env:
|
|
DIGEST: ${{ steps.build.outputs.digest }}
|
|
run: |
|
|
cosign attest --yes \
|
|
--predicate <(echo '{"sbom":"in-toto-spdx attached at build time"}') \
|
|
--type spdx \
|
|
"${IMAGE}@${DIGEST}"
|
|
|
|
# Auto-bump the chart values.yaml tag so the next Sovereign chart
|
|
# rollout picks up this image without a manual edit. Per
|
|
# `feedback_no_mvp_no_workarounds.md` rule 1 (target-state, no
|
|
# operator-action gates) and `feedback_inviolable_principles.md`
|
|
# (event-driven, never cron). Mirrors the pattern in
|
|
# build-application-controller.yaml + build-organization-controller.yaml.
|
|
# Added as part of TBD-A69 (#2006) — pre-#2006 this workflow shipped
|
|
# without auto-bump, so the same deploy-gap class as #1997 was live
|
|
# for every environment-controller code fix.
|
|
- name: Bump controllers.environment.image.tag in values.yaml
|
|
if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main'
|
|
env:
|
|
SHA_SHORT: ${{ steps.vars.outputs.sha_short }}
|
|
run: |
|
|
VALUES="products/catalyst/chart/values.yaml"
|
|
# awk: find ` environment:` under `controllers:`, then update
|
|
# the next `tag: "..."` line. Stops at the next top-level key
|
|
# so we don't accidentally bump a sibling controller's tag.
|
|
awk -v sha="${SHA_SHORT}" '
|
|
/^controllers:/ { in_ctrls=1 }
|
|
in_ctrls && /^ environment:/ { print; in_env=1; next }
|
|
in_ctrls && /^ [a-z]/ && !/^ environment:/ { in_env=0 }
|
|
in_env && /^ tag:/ { sub(/"[^"]*"/, "\"" sha "\""); in_env=0 }
|
|
{ print }
|
|
' "${VALUES}" > "${VALUES}.tmp" && mv "${VALUES}.tmp" "${VALUES}"
|
|
echo "values.yaml after bump:"
|
|
grep -A4 "^ environment:" "${VALUES}" | head -10
|
|
|
|
- name: Commit and push values.yaml bump
|
|
id: deploy_commit
|
|
if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main'
|
|
env:
|
|
SHA_SHORT: ${{ steps.vars.outputs.sha_short }}
|
|
run: |
|
|
git config user.name "github-actions[bot]"
|
|
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
if git diff --quiet products/catalyst/chart/values.yaml; then
|
|
echo "no values.yaml change — already pinned to ${SHA_SHORT}"
|
|
echo "pushed=false" >> "$GITHUB_OUTPUT"
|
|
exit 0
|
|
fi
|
|
git add products/catalyst/chart/values.yaml
|
|
git commit -m "deploy: bump environment-controller image to ${SHA_SHORT}"
|
|
# Pull-rebase to avoid races with parallel build commits.
|
|
git pull --rebase --autostash origin main || true
|
|
git push origin HEAD:main
|
|
echo "pushed=true" >> "$GITHUB_OUTPUT"
|
|
|
|
# GitHub Actions does NOT trigger workflows from bot pushes by
|
|
# default (anti-recursion safeguard). Without this dispatch the
|
|
# rebuilt image is NEVER baked into a new chart version, so
|
|
# Sovereigns keep installing the previous chart with the previous
|
|
# image tag (`feedback_no_mvp_no_workarounds.md` rule 1 violation).
|
|
- name: Dispatch blueprint-release for chart re-publish
|
|
if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main' && steps.deploy_commit.outputs.pushed == 'true'
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
run: |
|
|
gh workflow run blueprint-release.yaml \
|
|
--repo "${GITHUB_REPOSITORY}" \
|
|
--ref main \
|
|
-f blueprint=catalyst \
|
|
-f tree=products
|