From e70efb22ce659528a5725ab9943edc41e5e0dedc Mon Sep 17 00:00:00 2001 From: i759715 Date: Thu, 9 Jul 2026 15:22:42 +0300 Subject: [PATCH 1/6] Add kind-deployment validation to CI pipeline --- ci/pipelines/cf-deployment.yml | 48 ++++++++++ ci/pipelines/kind-neville.md | 42 +++++++++ ci/tasks/trigger-github-workflow/task.sh | 102 ++++++++++++++++++++++ ci/tasks/trigger-github-workflow/task.yml | 17 ++++ 4 files changed, 209 insertions(+) create mode 100644 ci/pipelines/kind-neville.md create mode 100755 ci/tasks/trigger-github-workflow/task.sh create mode 100644 ci/tasks/trigger-github-workflow/task.yml diff --git a/ci/pipelines/cf-deployment.yml b/ci/pipelines/cf-deployment.yml index 19677ec41..99eb78223 100644 --- a/ci/pipelines/cf-deployment.yml +++ b/ci/pipelines/cf-deployment.yml @@ -42,6 +42,8 @@ groups: - fips-smoke-tests - fips-cats - fips-delete-deployment + - kind-smoke-tests + - kind-cats - bless-manifest - ship-it-patch - ship-it-minor @@ -138,6 +140,10 @@ groups: - fips-cats - fips-delete-deployment - bless-manifest +- name: kind-neville + jobs: + - kind-smoke-tests + - kind-cats - name: ship-it jobs: - ship-it-patch @@ -2174,6 +2180,48 @@ jobs: - put: fips-pool params: {release: fips-pool} +- name: kind-smoke-tests + public: true + serial_groups: [ kind-workflows ] + plan: + - get: cf-deployment-develop + trigger: true + passed: + - unit-test-ops-files + - lint-cf-deployment-manifest + - unit-test-golang-tests + - unit-test-update-releases-coverage + - task: trigger-kind-smoke-workflow + file: cf-deployment-develop/ci/tasks/trigger-github-workflow/task.yml + params: + GITHUB_TOKEN: ((kind_deployment_github_token)) + GITHUB_REPO: cloudfoundry/kind-deployment + WORKFLOW_FILE: kind-smoke.yaml + WORKFLOW_REF: main + WORKFLOW_INPUTS: '{"minimal": "true", "fresh-validation": "true"}' + timeout: 3h + +- name: kind-cats + public: true + serial_groups: [ kind-workflows ] + plan: + - get: cf-deployment-develop + trigger: true + passed: + - unit-test-ops-files + - lint-cf-deployment-manifest + - unit-test-golang-tests + - unit-test-update-releases-coverage + - task: trigger-kind-cats-workflow + file: cf-deployment-develop/ci/tasks/trigger-github-workflow/task.yml + params: + GITHUB_TOKEN: ((kind_deployment_github_token)) + GITHUB_REPO: cloudfoundry/kind-deployment + WORKFLOW_FILE: kind-cats.yaml + WORKFLOW_REF: main + WORKFLOW_INPUTS: '{"fresh-validation": "true"}' + timeout: 4h + - name: bless-manifest public: true serial: true diff --git a/ci/pipelines/kind-neville.md b/ci/pipelines/kind-neville.md new file mode 100644 index 000000000..cc6e391a7 --- /dev/null +++ b/ci/pipelines/kind-neville.md @@ -0,0 +1,42 @@ +# kind-neville + +Validate cf-deployment on Cloud Foundry on Kubernetes (CF-on-kind) using the +[kind-deployment](https://github.com/cloudfoundry/kind-deployment) project. + +## Triggers + +The `kind-smoke-tests` and `kind-cats` jobs trigger automatically when a +`develop` commit passes the four unit/lint jobs (`unit-test-ops-files`, +`lint-cf-deployment-manifest`, `unit-test-golang-tests`, +`unit-test-update-releases-coverage`) — the same gate used by all BOSH +environments. They run in parallel with fresh-luna, upgrade, experimental, +windows, lite, and FIPS. + +## How it works + +Each job dispatches a GitHub Actions workflow in `cloudfoundry/kind-deployment` +via `workflow_dispatch` with `fresh-validation: true`. That flag (added in +[kind-deployment#424](https://github.com/cloudfoundry/kind-deployment/pull/424)) +causes the workflow to sync its Helm chart versions from cf-deployment's +`develop` branch before deploying, so the Kubernetes deployment is tested +against the same release versions being validated on BOSH. + +The Concourse task that handles the dispatch lives at +`ci/tasks/trigger-github-workflow/`. It dispatches the workflow, locates the +resulting run (by run ID, timestamp, and actor), and watches it to completion +so pass/fail is reflected in Concourse. + +## Credentials + +The pipeline requires a Concourse secret `kind_deployment_github_token` with +`actions: write` scope on `cloudfoundry/kind-deployment`. + +## Non-blocking + +`bless-manifest` does not depend on the kind jobs. A kind failure does not +gate releases. + +## Pipeline Management + +The kind-neville jobs live inside `ci/pipelines/cf-deployment.yml` under the +`kind-neville` group. To update the pipeline, run `ci/configure cf-deployment`. diff --git a/ci/tasks/trigger-github-workflow/task.sh b/ci/tasks/trigger-github-workflow/task.sh new file mode 100755 index 000000000..234edc98e --- /dev/null +++ b/ci/tasks/trigger-github-workflow/task.sh @@ -0,0 +1,102 @@ +#!/usr/bin/env sh +set -eu + +## +# Triggers a GitHub Actions workflow via workflow_dispatch, locates the +# resulting run, and streams its output until completion. +# +# Requires these environment variables: +# GITHUB_TOKEN – PAT or GitHub App token with Actions scope +# GITHUB_REPO – owner/repo (e.g. cloudfoundry/kind-deployment) +# WORKFLOW_FILE – workflow filename (e.g. kind-smoke.yaml) +# WORKFLOW_REF – git ref to run against (default: main) +# WORKFLOW_INPUTS – JSON object of workflow inputs (default: {}) +## + +# ── install gh CLI and jq ─────────────────────────────────────────── +apk add --no-cache --quiet \ + --repository=https://dl-cdn.alpinelinux.org/alpine/edge/community \ + github-cli jq + +export GH_TOKEN="${GITHUB_TOKEN}" + +TEAM_NAME="${BUILD_TEAM_NAME:-unknown-team}" +PIPELINE_NAME="${BUILD_PIPELINE_NAME:-unknown-pipeline}" +JOB_NAME="${BUILD_JOB_NAME:-unknown-job}" +BUILD_NAME="${BUILD_NAME:-unknown-build}" +TRIGGER_SOURCE="${TEAM_NAME}/${PIPELINE_NAME}/${JOB_NAME}/${BUILD_NAME}" +TRIGGER_ID=$(printf 'cfd-%s-%s-%s-%s-%s' \ + "${TEAM_NAME}" "${PIPELINE_NAME}" "${JOB_NAME}" "${BUILD_NAME}" "$(date -u +%s)" \ + | tr '/:@ ' '-' \ + | tr -cd '[:alnum:]._-') + +if [ -z "${TRIGGER_ID}" ]; then + TRIGGER_ID="cfd-$(date -u +%s)" +fi + +DISPATCH_TS=$(date -u +"%Y-%m-%dT%H:%M:%SZ") +ACTOR_LOGIN=$(gh api user --jq '.login') + +INPUTS_JSON=$(printf '%s' "${WORKFLOW_INPUTS}" \ + | jq -ce 'if type == "object" then . else error("WORKFLOW_INPUTS must be a JSON object") end') + +# ── snapshot the latest existing run ID so we can find our new one ── +BEFORE_ID=$(gh run list \ + --repo "${GITHUB_REPO}" \ + --workflow "${WORKFLOW_FILE}" \ + --event workflow_dispatch \ + --limit 1 \ + --json databaseId \ + --jq '.[0].databaseId // 0' 2>/dev/null || echo "0") + +# ── dispatch the workflow ─────────────────────────────────────────── +echo "Dispatching ${WORKFLOW_FILE} on ${GITHUB_REPO}@${WORKFLOW_REF}..." +echo "Trigger ID: ${TRIGGER_ID}" +echo "Trigger source: ${TRIGGER_SOURCE}" +echo "Workflow actor: ${ACTOR_LOGIN}" +echo "${INPUTS_JSON}" \ + | jq --arg ref "${WORKFLOW_REF}" '{ref: $ref, inputs: .}' \ + | gh api --method POST \ + "/repos/${GITHUB_REPO}/actions/workflows/${WORKFLOW_FILE}/dispatches" \ + --input - + +# ── locate the newly created run ──────────────────────────────────── +echo "Locating workflow run (previous latest ID: ${BEFORE_ID})..." +RUN_ID="" +attempt=0 +while [ "${attempt}" -lt 60 ]; do + RUN_ID=$(gh api \ + "/repos/${GITHUB_REPO}/actions/workflows/${WORKFLOW_FILE}/runs?event=workflow_dispatch&branch=${WORKFLOW_REF}&per_page=20" \ + 2>/dev/null \ + | jq -r \ + --argjson before_id "${BEFORE_ID}" \ + --arg dispatch_ts "${DISPATCH_TS}" \ + --arg actor_login "${ACTOR_LOGIN}" \ + '.workflow_runs + | map( + select( + (.id > $before_id) + and (.created_at >= $dispatch_ts) + and (.actor.login == $actor_login) + ) + ) + | .[0].id // empty' || true) + + if [ -n "${RUN_ID}" ]; then + break + fi + attempt=$((attempt + 1)) + sleep 10 +done + +if [ -z "${RUN_ID}" ]; then + echo "ERROR: Dispatched run not found after 10 minutes." + echo "Expected trigger_id=${TRIGGER_ID}" + echo "Expected actor=${ACTOR_LOGIN}" + echo "Workflow=${WORKFLOW_FILE} ref=${WORKFLOW_REF}" + exit 1 +fi + +# ── watch the run until completion ────────────────────────────────── +echo "Watching: https://github.com/${GITHUB_REPO}/actions/runs/${RUN_ID}" +gh run watch "${RUN_ID}" --repo "${GITHUB_REPO}" --exit-status diff --git a/ci/tasks/trigger-github-workflow/task.yml b/ci/tasks/trigger-github-workflow/task.yml new file mode 100644 index 000000000..117cb6b22 --- /dev/null +++ b/ci/tasks/trigger-github-workflow/task.yml @@ -0,0 +1,17 @@ +--- +platform: linux +image_resource: + type: docker-image + source: + repository: alpine + tag: "3.20" +inputs: +- name: cf-deployment-develop +params: + GITHUB_TOKEN: + GITHUB_REPO: + WORKFLOW_FILE: + WORKFLOW_REF: main + WORKFLOW_INPUTS: "{}" +run: + path: cf-deployment-develop/ci/tasks/trigger-github-workflow/task.sh From e4d9e60fd64e1cdf1fb68c5c5c589d97a05ebf4e Mon Sep 17 00:00:00 2001 From: i759715 Date: Thu, 9 Jul 2026 16:02:41 +0300 Subject: [PATCH 2/6] Enhance the documentation --- ci/pipelines/kind-neville.md | 364 +++++++++++++++++++++++++++++++++-- 1 file changed, 343 insertions(+), 21 deletions(-) diff --git a/ci/pipelines/kind-neville.md b/ci/pipelines/kind-neville.md index cc6e391a7..a60f08e01 100644 --- a/ci/pipelines/kind-neville.md +++ b/ci/pipelines/kind-neville.md @@ -3,38 +3,360 @@ Validate cf-deployment on Cloud Foundry on Kubernetes (CF-on-kind) using the [kind-deployment](https://github.com/cloudfoundry/kind-deployment) project. -## Triggers +## Goal -The `kind-smoke-tests` and `kind-cats` jobs trigger automatically when a -`develop` commit passes the four unit/lint jobs (`unit-test-ops-files`, -`lint-cf-deployment-manifest`, `unit-test-golang-tests`, -`unit-test-update-releases-coverage`) — the same gate used by all BOSH -environments. They run in parallel with fresh-luna, upgrade, experimental, -windows, lite, and FIPS. +Validate that Cloud Foundry release versions promoted through cf-deployment's +BOSH-based pipeline also work when deployed on Kubernetes. -## How it works +The design principle is that cf-deployment **orchestrates**, but does not +re-implement, the kind deployment logic. All Kubernetes-specific knowledge +(Helm charts, container image builds, `make up`, test suites) stays in the +kind-deployment repository. cf-deployment triggers workflows there and +consumes the result. -Each job dispatches a GitHub Actions workflow in `cloudfoundry/kind-deployment` -via `workflow_dispatch` with `fresh-validation: true`. That flag (added in -[kind-deployment#424](https://github.com/cloudfoundry/kind-deployment/pull/424)) -causes the workflow to sync its Helm chart versions from cf-deployment's -`develop` branch before deploying, so the Kubernetes deployment is tested -against the same release versions being validated on BOSH. +--- -The Concourse task that handles the dispatch lives at -`ci/tasks/trigger-github-workflow/`. It dispatches the workflow, locates the -resulting run (by run ID, timestamp, and actor), and watches it to completion -so pass/fail is reflected in Concourse. +## Background + +### How cf-deployment validates releases today (BOSH) + +The `update-releases` pipeline watches for new BOSH release versions. When a +new version appears: + +1. A lock is acquired from the pre-dev pool. +2. The release is updated in `cf-deployment.yml` (release-candidate branch). +3. A BOSH environment is stood up, CF is deployed, smoke tests run, compiled + releases are exported. +4. On success, the version is pushed to the `develop` branch. +5. The main `cf-deployment` pipeline picks up the change on `develop` and runs + full validation: fresh deploy, upgrade, CATS, smoke tests, BBR, Windows, + lite, experimental, and FIPS environments. +6. `bless-manifest` promotes `develop` to `release-candidate` once all + environments pass. + +### How kind-deployment manages versions today + +[kind-deployment](https://github.com/cloudfoundry/kind-deployment) deploys CF +on a local Kubernetes cluster (via [kind](https://kind.sigs.k8s.io/)) using +Helm charts. Key components: + +- **`docker-bake.hcl`** – defines component version variables + (`CAPI_RELEASE_VERSION`, `DIEGO_RELEASE_VERSION`, etc.). Each points to a + GitHub release tag of the corresponding BOSH release. Container images are + built from the Go source code in those release repos. +- **`helmfile.yaml.gotmpl`** + **`values.yaml.gotmpl`** – reference local + Helm charts (under `releases/`) and external OCI charts. Image tags in + `values.yaml` files within each chart carry `# sync: release=` + annotations. +- **`scripts/sync-cf-deployment-versions.py`** – a Python script that reads + `cf-deployment.yml`, extracts release versions, and updates `Chart.yaml` + (`appVersion`) and `values.yaml` (image tags) for each chart. +- **`.github/workflows/sync-cf-deployment-versions.yaml`** – runs the Python + script on a daily cron (`0 6 * * *`) or manual dispatch. It always fetches + the **latest tagged release** of cf-deployment, not `develop`. +- **Renovate** – independently watches upstream GitHub releases for each + component and creates per-component PRs. + +### The gap + +The existing daily sync in kind-deployment is **batch and delayed**: it only +picks up versions after they appear in a cf-deployment *release* (tagged), +which can be days or weeks after they land on `develop`. There is no +automated path that validates whether the versions currently being tested in +BOSH environments also work on Kubernetes. + +--- + +## Approach Considered and Rejected + +### Local kind cluster in Concourse ("dobby") + +The initial idea was to create a kind cluster directly inside a Concourse +worker, deploy CF with `make up`, and run tests — similar to how BOSH +environments work with dedicated pools and infrastructure. + +This was rejected because: + +- kind-deployment already has self-contained GitHub Actions workflows that + handle the full lifecycle (cluster creation, deployment, testing, cleanup). +- Duplicating that logic in Concourse would create a second source of truth + for the Kubernetes deployment process. +- Concourse workers are not well-suited for running Docker-in-Docker with + kind clusters. +- Maintenance burden doubles: any change in kind-deployment's setup would + need to be mirrored in cf-deployment's Concourse tasks. + +--- + +## Approach Implemented + +### Architecture + +The implementation relies on +[kind-deployment PR #424](https://github.com/cloudfoundry/kind-deployment/pull/424), +which adds a `fresh-validation` boolean input to both the `kind-smoke.yaml` +and `kind-cats.yaml` workflows. When `fresh-validation: true`, the workflow +runs `sync-cf-deployment-versions.py --ref develop` **inline**, right before +`make up` — syncing the Helm chart versions to match cf-deployment's +`develop` branch inside the workflow's own ephemeral checkout. + +This means cf-deployment does **not** need a separate sync job, a sync PR +branch, or any modification to the sync workflow. It simply dispatches the +two test workflows with `fresh-validation: true`. + +Kind validation is triggered at the **same point** as every BOSH environment +— immediately after the four unit/lint jobs pass on `develop`. This makes +kind-neville a true parallel environment, running concurrently with fresh-luna, +upgrade, experimental, windows, lite, and FIPS. + +``` +develop (trigger) + │ + ▼ + unit-test-ops-files + lint-cf-deployment-manifest (4 jobs, same gate as all BOSH environments) + unit-test-golang-tests + unit-test-update-releases-coverage + │ + ├─── fresh-acquire-pool ──► fresh-deploy ──► fresh-smoke+cats ──┐ + ├─── upgrade-acquire-pool ► upgrade-deploy ► upgrade-smoke+cats ┤ + ├─── experimental ──────────────────────────────────────────────┤ + ├─── lite ──────────────────────────────────────────────────────┤ + ├─── windows ───────────────────────────────────────────────────┤ + ├─── fips ──────────────────────────────────────────────────────┤ + │ │ + ├─── kind-smoke-tests ──► kind-smoke.yaml (fresh-validation) ───┤ + └─── kind-cats ─────────► kind-cats.yaml (fresh-validation) ───┤ + │ + bless-manifest + (waits for all above) +``` + +Inside each kind workflow, the sync happens as a self-contained step: + +``` +kind-smoke.yaml / kind-cats.yaml (dispatched with fresh-validation=true) + 1. checkout kind-deployment @ main + 2. [fresh-validation] sync-cf-deployment-versions.py --ref develop + → updates Helm chart versions from cf-deployment develop + 3. make up (deploy CF on kind with the synced versions) + 4. run smoke / CATS +``` + +The sync + deploy + test all happen within one GitHub Actions run. Nothing +is committed; the synced versions live only in the workflow's checkout for +the duration of the run. + +### Pipeline group: kind-neville + +All kind jobs are grouped under `kind-neville` in the Concourse pipeline, +following the naming convention of other environments (luna, trelawney, +hermione, etc.): + +```yaml +- name: kind-neville + jobs: + - kind-smoke-tests + - kind-cats +``` + +### Job 1: kind-smoke-tests + +**Trigger:** `cf-deployment-develop` with `passed: [unit-test-ops-files, lint-cf-deployment-manifest, unit-test-golang-tests, unit-test-update-releases-coverage]` + +Same trigger gate as every BOSH environment. Dispatches `kind-smoke.yaml` +on `main` with `{"minimal": "true", "fresh-validation": "true"}`. The +`fresh-validation` flag makes the workflow sync chart versions from +cf-deployment `develop` before deploying and running smoke tests. + +### Job 2: kind-cats + +**Trigger:** same four unit/lint jobs as above. + +Dispatches `kind-cats.yaml` on `main` with `{"fresh-validation": "true"}`. +Same self-syncing behavior, but runs the full Cloud Foundry Acceptance Tests. + +### Serial group + +Both jobs share `serial_groups: [kind-workflows]`. This serializes the +GitHub workflow dispatches so we don't run two heavy kind clusters at the +same time on shared GitHub runners. + +### Non-blocking for releases + +The `bless-manifest` job does **not** list any kind jobs in its `passed` +constraints. Kind validation is informational — a failure will not block +release promotion. This is intentional: the Kubernetes deployment path is +newer and less mature than the BOSH path, so it should not gate releases +until confidence is established. + +--- + +## Reusable Concourse Task: trigger-github-workflow + +All kind jobs use a single reusable task at +`ci/tasks/trigger-github-workflow/`: + +- **`task.yml`** – Concourse task definition using `alpine:3.20`, with + params for `GITHUB_TOKEN`, `GITHUB_REPO`, `WORKFLOW_FILE`, `WORKFLOW_REF`, + and `WORKFLOW_INPUTS`. +- **`task.sh`** – shell script that handles the full dispatch-and-watch + lifecycle. + +### Run identification mechanism + +GitHub's workflow dispatch API returns `204 No Content` with no run ID. +The script must locate the run it just created. Before dispatching, it +snapshots the highest existing run ID (`BEFORE_ID`). It also records the +dispatch timestamp and authenticated user. After dispatching, it polls the +runs API (up to 60 × 10s = 10 minutes) looking for a run matching all three +signals: + +- `id > BEFORE_ID` (new run, not pre-existing) +- `created_at >= DISPATCH_TS` (created after dispatch) +- `actor.login == ACTOR_LOGIN` (created by the expected service account) + +A `TRIGGER_ID` is still generated from Concourse build metadata +(`BUILD_TEAM_NAME`, `BUILD_PIPELINE_NAME`, `BUILD_JOB_NAME`, `BUILD_NAME`) +and logged for debugging, but it is **not** injected into the workflow inputs +(the target workflows don't declare it, and GitHub rejects unknown inputs +with a 422). The three-signal match is sufficient because +`serial_groups: [kind-workflows]` ensures only one dispatch is in flight +at a time. + +--- + +## Changes in kind-deployment (already merged) + +The kind-deployment side is provided by +[PR #424](https://github.com/cloudfoundry/kind-deployment/pull/424) +(**already merged**), which adds a `fresh-validation` boolean input to both +test workflows. + +### `.github/workflows/kind-smoke.yaml` and `kind-cats.yaml` + +| Change | Purpose | +|--------|---------| +| Add `fresh-validation` boolean input (default `false`) | Opt in to validating cf-deployment's `develop` versions | +| Add step "Use develop versions of cf-deployment" | On `workflow_dispatch` with `fresh-validation == 'true'`, run `pip3 install -r scripts/requirements.txt` then `python3 scripts/sync-cf-deployment-versions.py --ref develop` before `make up` | + +The step is gated so it only runs on manual dispatch with the flag set. The +scheduled cron runs are unaffected and keep their default behavior. + +### `scripts/sync-cf-deployment-versions.py` + +The script accepts a `--ref` argument (via argparse). When cf-deployment +dispatches with `fresh-validation: true`, the workflow calls it with +`--ref develop`, so the sync reads component versions from the tip of +`develop` instead of the latest tagged release. + +Because the sync now runs **inside** the smoke/CATS workflows, there is no +separate `sync-cf-deployment-versions.yaml` dispatch, no `auto/…` PR branch, +and no branch-push coordination. Everything happens in the workflow's own +ephemeral checkout and is discarded when the run finishes. + +--- + +## Risks and Potential Problems + +### 1. Missing container images + +When cf-deployment `develop` has a new release version, the corresponding +container image may not yet be built and pushed to `ghcr.io`. The sync +script checks for image availability and exits with an error if images are +missing. This would cause the `fresh-validation` step to fail, failing the +smoke/CATS workflow (and therefore the Concourse job). + +**Mitigation:** The sync script reports which images are missing. Container +images are typically built by kind-deployment's Renovate-triggered image +build when version bumps are detected. There may be a gap where cf-deployment +bumps a version before kind-deployment's image build runs. This is the most +likely source of transient failures. Because kind is non-blocking for +`bless-manifest`, such a failure will not hold up releases. + +### 2. Concourse-to-GitHub API reliability + +The trigger-github-workflow task depends on the GitHub API being available +for dispatching workflows and polling for run status. GitHub API outages or +rate limiting could cause false failures. + +**Mitigation:** The script uses the `gh` CLI which handles authentication +and retries. The polling loop has 60 attempts with 10-second intervals +(~10 minutes total) before giving up. Rate limiting can be mitigated with a +GitHub App token instead of a PAT. + +### 3. Serial group throughput + +Both kind jobs are in the same serial group, so smoke and CATS run +sequentially rather than concurrently. A full cycle is roughly the sum of +both durations. If the pipeline triggers frequently, kind validation may +fall behind BOSH. + +**Mitigation:** Because smoke and CATS create independent kind clusters, +they can be split into separate serial groups if throughput becomes a +problem. The serial group is a conservative default to avoid running two +heavy clusters on shared GitHub runners at once. + +### 4. Develop ref may advance between dispatch and sync + +`fresh-validation` syncs from `develop` at the moment the workflow runs. +Additional commits may land on `develop` between the Concourse trigger and +the in-workflow sync, so the test could validate a slightly newer or older +set of versions than the exact commit that triggered the job. + +**Mitigation:** Acceptable — the kind check is informational and +non-blocking. A few commits of drift do not meaningfully change what is +being validated. + +### 5. Input name coupling + +cf-deployment must pass exactly the input name kind-deployment defines +(`fresh-validation`). If kind-deployment renames the input, the Concourse +dispatch must be updated to match, otherwise the flag is silently ignored +and kind runs against `main` versions instead of `develop`. + +**Mitigation:** The name is fixed by the merged PR #424. Any future rename +in kind-deployment should be coordinated with a matching change here. + +--- ## Credentials The pipeline requires a Concourse secret `kind_deployment_github_token` with `actions: write` scope on `cloudfoundry/kind-deployment`. -## Non-blocking +--- + +## Files Changed + +### cf-deployment repository + +| File | Change | +|------|--------| +| `ci/pipelines/cf-deployment.yml` | Added `kind-smoke-tests` and `kind-cats` jobs triggered on `develop` after the four unit/lint jobs (same gate as BOSH environments), dispatching `kind-smoke.yaml`/`kind-cats.yaml` on `main` with `fresh-validation: true`. Added `kind-neville` group. `bless-manifest` does not depend on either job (non-blocking). | +| `ci/tasks/trigger-github-workflow/task.yml` | Reusable Concourse task to dispatch and watch a GitHub workflow | +| `ci/tasks/trigger-github-workflow/task.sh` | Dispatch-and-watch script with run identification via id/timestamp/actor matching | + +### kind-deployment repository (already merged — PR #424) + +| File | Change | +|------|--------| +| `.github/workflows/kind-smoke.yaml` | Added `fresh-validation` input + inline sync step (`sync-cf-deployment-versions.py --ref develop`) before `make up` | +| `.github/workflows/kind-cats.yaml` | Same `fresh-validation` input + inline sync step | + +--- + +## Operational Shape + +The intended flow on every `develop` commit that passes unit/lint: + +1. `kind-smoke-tests` dispatches `kind-smoke.yaml` with `fresh-validation: true`. +2. `kind-cats` dispatches `kind-cats.yaml` with `fresh-validation: true`. +3. Inside each workflow, versions are synced from `develop`, CF is deployed on kind, and tests run. +4. Both kind jobs report pass/fail independently in Concourse. +5. `bless-manifest` proceeds based on BOSH-based validations only; kind results are informational. -`bless-manifest` does not depend on the kind jobs. A kind failure does not -gate releases. +--- ## Pipeline Management From b9a154b431a9a0f7b4a92cd9b4936dae3841499b Mon Sep 17 00:00:00 2001 From: i759715 Date: Fri, 10 Jul 2026 10:43:57 +0300 Subject: [PATCH 3/6] Optimize with usage of other repos --- ci/pipelines/cf-deployment.yml | 4 ++-- ci/tasks/trigger-github-workflow/task.sh | 10 ++++------ ci/tasks/trigger-github-workflow/task.yml | 3 +-- 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/ci/pipelines/cf-deployment.yml b/ci/pipelines/cf-deployment.yml index 99eb78223..da39851ce 100644 --- a/ci/pipelines/cf-deployment.yml +++ b/ci/pipelines/cf-deployment.yml @@ -2194,7 +2194,7 @@ jobs: - task: trigger-kind-smoke-workflow file: cf-deployment-develop/ci/tasks/trigger-github-workflow/task.yml params: - GITHUB_TOKEN: ((kind_deployment_github_token)) + GITHUB_TOKEN: ((ard_wg_gitbot_token)) GITHUB_REPO: cloudfoundry/kind-deployment WORKFLOW_FILE: kind-smoke.yaml WORKFLOW_REF: main @@ -2215,7 +2215,7 @@ jobs: - task: trigger-kind-cats-workflow file: cf-deployment-develop/ci/tasks/trigger-github-workflow/task.yml params: - GITHUB_TOKEN: ((kind_deployment_github_token)) + GITHUB_TOKEN: ((ard_wg_gitbot_token)) GITHUB_REPO: cloudfoundry/kind-deployment WORKFLOW_FILE: kind-cats.yaml WORKFLOW_REF: main diff --git a/ci/tasks/trigger-github-workflow/task.sh b/ci/tasks/trigger-github-workflow/task.sh index 234edc98e..cf4e73e03 100755 --- a/ci/tasks/trigger-github-workflow/task.sh +++ b/ci/tasks/trigger-github-workflow/task.sh @@ -6,18 +6,16 @@ set -eu # resulting run, and streams its output until completion. # # Requires these environment variables: -# GITHUB_TOKEN – PAT or GitHub App token with Actions scope +# GITHUB_TOKEN – ard_wg_gitbot_token with Actions access on the target repo # GITHUB_REPO – owner/repo (e.g. cloudfoundry/kind-deployment) # WORKFLOW_FILE – workflow filename (e.g. kind-smoke.yaml) # WORKFLOW_REF – git ref to run against (default: main) # WORKFLOW_INPUTS – JSON object of workflow inputs (default: {}) +# +# Runs on the cloudfoundry/cf-deployment-concourse-tasks image, which +# provides the gh CLI and jq. ## -# ── install gh CLI and jq ─────────────────────────────────────────── -apk add --no-cache --quiet \ - --repository=https://dl-cdn.alpinelinux.org/alpine/edge/community \ - github-cli jq - export GH_TOKEN="${GITHUB_TOKEN}" TEAM_NAME="${BUILD_TEAM_NAME:-unknown-team}" diff --git a/ci/tasks/trigger-github-workflow/task.yml b/ci/tasks/trigger-github-workflow/task.yml index 117cb6b22..58769f0f8 100644 --- a/ci/tasks/trigger-github-workflow/task.yml +++ b/ci/tasks/trigger-github-workflow/task.yml @@ -3,8 +3,7 @@ platform: linux image_resource: type: docker-image source: - repository: alpine - tag: "3.20" + repository: cloudfoundry/cf-deployment-concourse-tasks inputs: - name: cf-deployment-develop params: From 2d6860b826a98549eaf4d05a02ca803c53cc4103 Mon Sep 17 00:00:00 2001 From: i759715 Date: Fri, 10 Jul 2026 12:30:28 +0300 Subject: [PATCH 4/6] Move the task for github workflow in the runtime-ci --- ci/pipelines/cf-deployment.yml | 36 ++++---- ci/pipelines/kind-neville.md | 30 ++++--- ci/tasks/trigger-github-workflow/task.sh | 100 ---------------------- ci/tasks/trigger-github-workflow/task.yml | 16 ---- 4 files changed, 40 insertions(+), 142 deletions(-) delete mode 100755 ci/tasks/trigger-github-workflow/task.sh delete mode 100644 ci/tasks/trigger-github-workflow/task.yml diff --git a/ci/pipelines/cf-deployment.yml b/ci/pipelines/cf-deployment.yml index da39851ce..e233e2bfa 100644 --- a/ci/pipelines/cf-deployment.yml +++ b/ci/pipelines/cf-deployment.yml @@ -2184,15 +2184,17 @@ jobs: public: true serial_groups: [ kind-workflows ] plan: - - get: cf-deployment-develop - trigger: true - passed: - - unit-test-ops-files - - lint-cf-deployment-manifest - - unit-test-golang-tests - - unit-test-update-releases-coverage + - in_parallel: + - get: cf-deployment-develop + trigger: true + passed: + - unit-test-ops-files + - lint-cf-deployment-manifest + - unit-test-golang-tests + - unit-test-update-releases-coverage + - get: runtime-ci - task: trigger-kind-smoke-workflow - file: cf-deployment-develop/ci/tasks/trigger-github-workflow/task.yml + file: runtime-ci/tasks/trigger-github-workflow/task.yml params: GITHUB_TOKEN: ((ard_wg_gitbot_token)) GITHUB_REPO: cloudfoundry/kind-deployment @@ -2205,15 +2207,17 @@ jobs: public: true serial_groups: [ kind-workflows ] plan: - - get: cf-deployment-develop - trigger: true - passed: - - unit-test-ops-files - - lint-cf-deployment-manifest - - unit-test-golang-tests - - unit-test-update-releases-coverage + - in_parallel: + - get: cf-deployment-develop + trigger: true + passed: + - unit-test-ops-files + - lint-cf-deployment-manifest + - unit-test-golang-tests + - unit-test-update-releases-coverage + - get: runtime-ci - task: trigger-kind-cats-workflow - file: cf-deployment-develop/ci/tasks/trigger-github-workflow/task.yml + file: runtime-ci/tasks/trigger-github-workflow/task.yml params: GITHUB_TOKEN: ((ard_wg_gitbot_token)) GITHUB_REPO: cloudfoundry/kind-deployment diff --git a/ci/pipelines/kind-neville.md b/ci/pipelines/kind-neville.md index a60f08e01..147dcdbfe 100644 --- a/ci/pipelines/kind-neville.md +++ b/ci/pipelines/kind-neville.md @@ -194,15 +194,20 @@ until confidence is established. ## Reusable Concourse Task: trigger-github-workflow -All kind jobs use a single reusable task at -`ci/tasks/trigger-github-workflow/`: - -- **`task.yml`** – Concourse task definition using `alpine:3.20`, with - params for `GITHUB_TOKEN`, `GITHUB_REPO`, `WORKFLOW_FILE`, `WORKFLOW_REF`, - and `WORKFLOW_INPUTS`. -- **`task.sh`** – shell script that handles the full dispatch-and-watch +All kind jobs use a single reusable task that lives in the +[runtime-ci](https://github.com/cloudfoundry/runtime-ci) repository at +`tasks/trigger-github-workflow/`: + +- **`task.yml`** – Concourse task definition using the + `cloudfoundry/cf-deployment-concourse-tasks` image (which provides `gh` + and `jq`), with params for `GITHUB_TOKEN`, `GITHUB_REPO`, `WORKFLOW_FILE`, + `WORKFLOW_REF`, and `WORKFLOW_INPUTS`. +- **`task`** – shell script that handles the full dispatch-and-watch lifecycle. +The pipeline references it via the `runtime-ci` resource +(`runtime-ci/tasks/trigger-github-workflow/task.yml`). + ### Run identification mechanism GitHub's workflow dispatch API returns `204 No Content` with no run ID. @@ -333,9 +338,14 @@ The pipeline requires a Concourse secret `kind_deployment_github_token` with | File | Change | |------|--------| -| `ci/pipelines/cf-deployment.yml` | Added `kind-smoke-tests` and `kind-cats` jobs triggered on `develop` after the four unit/lint jobs (same gate as BOSH environments), dispatching `kind-smoke.yaml`/`kind-cats.yaml` on `main` with `fresh-validation: true`. Added `kind-neville` group. `bless-manifest` does not depend on either job (non-blocking). | -| `ci/tasks/trigger-github-workflow/task.yml` | Reusable Concourse task to dispatch and watch a GitHub workflow | -| `ci/tasks/trigger-github-workflow/task.sh` | Dispatch-and-watch script with run identification via id/timestamp/actor matching | +| `ci/pipelines/cf-deployment.yml` | Added `kind-smoke-tests` and `kind-cats` jobs triggered on `develop` after the four unit/lint jobs (same gate as BOSH environments), dispatching `kind-smoke.yaml`/`kind-cats.yaml` on `main` with `fresh-validation: true`. Added `kind-neville` group. `bless-manifest` does not depend on either job (non-blocking). The jobs reference the reusable task from the `runtime-ci` resource. | + +### runtime-ci repository (PR #687) + +| File | Change | +|------|--------| +| `tasks/trigger-github-workflow/task.yml` | Reusable Concourse task to dispatch and watch a GitHub workflow | +| `tasks/trigger-github-workflow/task` | Dispatch-and-watch script with run identification via id/timestamp/actor matching | ### kind-deployment repository (already merged — PR #424) diff --git a/ci/tasks/trigger-github-workflow/task.sh b/ci/tasks/trigger-github-workflow/task.sh deleted file mode 100755 index cf4e73e03..000000000 --- a/ci/tasks/trigger-github-workflow/task.sh +++ /dev/null @@ -1,100 +0,0 @@ -#!/usr/bin/env sh -set -eu - -## -# Triggers a GitHub Actions workflow via workflow_dispatch, locates the -# resulting run, and streams its output until completion. -# -# Requires these environment variables: -# GITHUB_TOKEN – ard_wg_gitbot_token with Actions access on the target repo -# GITHUB_REPO – owner/repo (e.g. cloudfoundry/kind-deployment) -# WORKFLOW_FILE – workflow filename (e.g. kind-smoke.yaml) -# WORKFLOW_REF – git ref to run against (default: main) -# WORKFLOW_INPUTS – JSON object of workflow inputs (default: {}) -# -# Runs on the cloudfoundry/cf-deployment-concourse-tasks image, which -# provides the gh CLI and jq. -## - -export GH_TOKEN="${GITHUB_TOKEN}" - -TEAM_NAME="${BUILD_TEAM_NAME:-unknown-team}" -PIPELINE_NAME="${BUILD_PIPELINE_NAME:-unknown-pipeline}" -JOB_NAME="${BUILD_JOB_NAME:-unknown-job}" -BUILD_NAME="${BUILD_NAME:-unknown-build}" -TRIGGER_SOURCE="${TEAM_NAME}/${PIPELINE_NAME}/${JOB_NAME}/${BUILD_NAME}" -TRIGGER_ID=$(printf 'cfd-%s-%s-%s-%s-%s' \ - "${TEAM_NAME}" "${PIPELINE_NAME}" "${JOB_NAME}" "${BUILD_NAME}" "$(date -u +%s)" \ - | tr '/:@ ' '-' \ - | tr -cd '[:alnum:]._-') - -if [ -z "${TRIGGER_ID}" ]; then - TRIGGER_ID="cfd-$(date -u +%s)" -fi - -DISPATCH_TS=$(date -u +"%Y-%m-%dT%H:%M:%SZ") -ACTOR_LOGIN=$(gh api user --jq '.login') - -INPUTS_JSON=$(printf '%s' "${WORKFLOW_INPUTS}" \ - | jq -ce 'if type == "object" then . else error("WORKFLOW_INPUTS must be a JSON object") end') - -# ── snapshot the latest existing run ID so we can find our new one ── -BEFORE_ID=$(gh run list \ - --repo "${GITHUB_REPO}" \ - --workflow "${WORKFLOW_FILE}" \ - --event workflow_dispatch \ - --limit 1 \ - --json databaseId \ - --jq '.[0].databaseId // 0' 2>/dev/null || echo "0") - -# ── dispatch the workflow ─────────────────────────────────────────── -echo "Dispatching ${WORKFLOW_FILE} on ${GITHUB_REPO}@${WORKFLOW_REF}..." -echo "Trigger ID: ${TRIGGER_ID}" -echo "Trigger source: ${TRIGGER_SOURCE}" -echo "Workflow actor: ${ACTOR_LOGIN}" -echo "${INPUTS_JSON}" \ - | jq --arg ref "${WORKFLOW_REF}" '{ref: $ref, inputs: .}' \ - | gh api --method POST \ - "/repos/${GITHUB_REPO}/actions/workflows/${WORKFLOW_FILE}/dispatches" \ - --input - - -# ── locate the newly created run ──────────────────────────────────── -echo "Locating workflow run (previous latest ID: ${BEFORE_ID})..." -RUN_ID="" -attempt=0 -while [ "${attempt}" -lt 60 ]; do - RUN_ID=$(gh api \ - "/repos/${GITHUB_REPO}/actions/workflows/${WORKFLOW_FILE}/runs?event=workflow_dispatch&branch=${WORKFLOW_REF}&per_page=20" \ - 2>/dev/null \ - | jq -r \ - --argjson before_id "${BEFORE_ID}" \ - --arg dispatch_ts "${DISPATCH_TS}" \ - --arg actor_login "${ACTOR_LOGIN}" \ - '.workflow_runs - | map( - select( - (.id > $before_id) - and (.created_at >= $dispatch_ts) - and (.actor.login == $actor_login) - ) - ) - | .[0].id // empty' || true) - - if [ -n "${RUN_ID}" ]; then - break - fi - attempt=$((attempt + 1)) - sleep 10 -done - -if [ -z "${RUN_ID}" ]; then - echo "ERROR: Dispatched run not found after 10 minutes." - echo "Expected trigger_id=${TRIGGER_ID}" - echo "Expected actor=${ACTOR_LOGIN}" - echo "Workflow=${WORKFLOW_FILE} ref=${WORKFLOW_REF}" - exit 1 -fi - -# ── watch the run until completion ────────────────────────────────── -echo "Watching: https://github.com/${GITHUB_REPO}/actions/runs/${RUN_ID}" -gh run watch "${RUN_ID}" --repo "${GITHUB_REPO}" --exit-status diff --git a/ci/tasks/trigger-github-workflow/task.yml b/ci/tasks/trigger-github-workflow/task.yml deleted file mode 100644 index 58769f0f8..000000000 --- a/ci/tasks/trigger-github-workflow/task.yml +++ /dev/null @@ -1,16 +0,0 @@ ---- -platform: linux -image_resource: - type: docker-image - source: - repository: cloudfoundry/cf-deployment-concourse-tasks -inputs: -- name: cf-deployment-develop -params: - GITHUB_TOKEN: - GITHUB_REPO: - WORKFLOW_FILE: - WORKFLOW_REF: main - WORKFLOW_INPUTS: "{}" -run: - path: cf-deployment-develop/ci/tasks/trigger-github-workflow/task.sh From 49550a825dcd82f22b70f3fc0b8a2f0d4e362c8a Mon Sep 17 00:00:00 2001 From: i759715 Date: Fri, 10 Jul 2026 16:42:28 +0300 Subject: [PATCH 5/6] Adapt the docu further --- ci/pipelines/kind-neville.md | 394 ++++++++--------------------------- 1 file changed, 89 insertions(+), 305 deletions(-) diff --git a/ci/pipelines/kind-neville.md b/ci/pipelines/kind-neville.md index 147dcdbfe..bbc12e1b8 100644 --- a/ci/pipelines/kind-neville.md +++ b/ci/pipelines/kind-neville.md @@ -1,157 +1,61 @@ # kind-neville -Validate cf-deployment on Cloud Foundry on Kubernetes (CF-on-kind) using the -[kind-deployment](https://github.com/cloudfoundry/kind-deployment) project. +Validate cf-deployment on Kubernetes using [kind-deployment](https://github.com/cloudfoundry/kind-deployment). ## Goal -Validate that Cloud Foundry release versions promoted through cf-deployment's -BOSH-based pipeline also work when deployed on Kubernetes. +Validate that release versions on cf-deployment `develop` also work when +deployed on Kubernetes, in parallel with the existing BOSH-based validation. -The design principle is that cf-deployment **orchestrates**, but does not -re-implement, the kind deployment logic. All Kubernetes-specific knowledge -(Helm charts, container image builds, `make up`, test suites) stays in the -kind-deployment repository. cf-deployment triggers workflows there and -consumes the result. +cf-deployment **orchestrates** — it triggers kind-deployment's existing +GitHub Actions workflows and consumes the result. All Kubernetes logic +(Helm charts, image builds, `make up`, test suites) stays in kind-deployment. --- -## Background - -### How cf-deployment validates releases today (BOSH) - -The `update-releases` pipeline watches for new BOSH release versions. When a -new version appears: - -1. A lock is acquired from the pre-dev pool. -2. The release is updated in `cf-deployment.yml` (release-candidate branch). -3. A BOSH environment is stood up, CF is deployed, smoke tests run, compiled - releases are exported. -4. On success, the version is pushed to the `develop` branch. -5. The main `cf-deployment` pipeline picks up the change on `develop` and runs - full validation: fresh deploy, upgrade, CATS, smoke tests, BBR, Windows, - lite, experimental, and FIPS environments. -6. `bless-manifest` promotes `develop` to `release-candidate` once all - environments pass. - -### How kind-deployment manages versions today - -[kind-deployment](https://github.com/cloudfoundry/kind-deployment) deploys CF -on a local Kubernetes cluster (via [kind](https://kind.sigs.k8s.io/)) using -Helm charts. Key components: - -- **`docker-bake.hcl`** – defines component version variables - (`CAPI_RELEASE_VERSION`, `DIEGO_RELEASE_VERSION`, etc.). Each points to a - GitHub release tag of the corresponding BOSH release. Container images are - built from the Go source code in those release repos. -- **`helmfile.yaml.gotmpl`** + **`values.yaml.gotmpl`** – reference local - Helm charts (under `releases/`) and external OCI charts. Image tags in - `values.yaml` files within each chart carry `# sync: release=` - annotations. -- **`scripts/sync-cf-deployment-versions.py`** – a Python script that reads - `cf-deployment.yml`, extracts release versions, and updates `Chart.yaml` - (`appVersion`) and `values.yaml` (image tags) for each chart. -- **`.github/workflows/sync-cf-deployment-versions.yaml`** – runs the Python - script on a daily cron (`0 6 * * *`) or manual dispatch. It always fetches - the **latest tagged release** of cf-deployment, not `develop`. -- **Renovate** – independently watches upstream GitHub releases for each - component and creates per-component PRs. - -### The gap - -The existing daily sync in kind-deployment is **batch and delayed**: it only -picks up versions after they appear in a cf-deployment *release* (tagged), -which can be days or weeks after they land on `develop`. There is no -automated path that validates whether the versions currently being tested in -BOSH environments also work on Kubernetes. +## How it works ---- - -## Approach Considered and Rejected - -### Local kind cluster in Concourse ("dobby") - -The initial idea was to create a kind cluster directly inside a Concourse -worker, deploy CF with `make up`, and run tests — similar to how BOSH -environments work with dedicated pools and infrastructure. - -This was rejected because: - -- kind-deployment already has self-contained GitHub Actions workflows that - handle the full lifecycle (cluster creation, deployment, testing, cleanup). -- Duplicating that logic in Concourse would create a second source of truth - for the Kubernetes deployment process. -- Concourse workers are not well-suited for running Docker-in-Docker with - kind clusters. -- Maintenance burden doubles: any change in kind-deployment's setup would - need to be mirrored in cf-deployment's Concourse tasks. - ---- - -## Approach Implemented - -### Architecture - -The implementation relies on -[kind-deployment PR #424](https://github.com/cloudfoundry/kind-deployment/pull/424), -which adds a `fresh-validation` boolean input to both the `kind-smoke.yaml` -and `kind-cats.yaml` workflows. When `fresh-validation: true`, the workflow -runs `sync-cf-deployment-versions.py --ref develop` **inline**, right before -`make up` — syncing the Helm chart versions to match cf-deployment's -`develop` branch inside the workflow's own ephemeral checkout. - -This means cf-deployment does **not** need a separate sync job, a sync PR -branch, or any modification to the sync workflow. It simply dispatches the -two test workflows with `fresh-validation: true`. - -Kind validation is triggered at the **same point** as every BOSH environment -— immediately after the four unit/lint jobs pass on `develop`. This makes -kind-neville a true parallel environment, running concurrently with fresh-luna, -upgrade, experimental, windows, lite, and FIPS. +The implementation uses +[kind-deployment PR #424](https://github.com/cloudfoundry/kind-deployment/pull/424) +(already merged), which adds a `fresh-validation` boolean input to +`kind-smoke.yaml` and `kind-cats.yaml`. When `fresh-validation: true` is +passed via `workflow_dispatch`, the workflow runs +`sync-cf-deployment-versions.py --ref develop` inline before `make up`, +syncing Helm chart versions to cf-deployment's `develop` branch within +the workflow's ephemeral checkout. ``` develop (trigger) │ ▼ unit-test-ops-files - lint-cf-deployment-manifest (4 jobs, same gate as all BOSH environments) + lint-cf-deployment-manifest unit-test-golang-tests unit-test-update-releases-coverage │ - ├─── fresh-acquire-pool ──► fresh-deploy ──► fresh-smoke+cats ──┐ - ├─── upgrade-acquire-pool ► upgrade-deploy ► upgrade-smoke+cats ┤ - ├─── experimental ──────────────────────────────────────────────┤ - ├─── lite ──────────────────────────────────────────────────────┤ - ├─── windows ───────────────────────────────────────────────────┤ - ├─── fips ──────────────────────────────────────────────────────┤ - │ │ - ├─── kind-smoke-tests ──► kind-smoke.yaml (fresh-validation) ───┤ - └─── kind-cats ─────────► kind-cats.yaml (fresh-validation) ───┤ - │ - bless-manifest - (waits for all above) + ├─── BOSH environments (fresh, upgrade, experimental, …) ────┐ + │ │ + ├─── kind-smoke-tests ─► kind-smoke.yaml (fresh-validation) ─┤ + └─── kind-cats ────────► kind-cats.yaml (fresh-validation) ─┤ + │ + bless-manifest + (waits for BOSH only) ``` -Inside each kind workflow, the sync happens as a self-contained step: +Inside each kind workflow: -``` -kind-smoke.yaml / kind-cats.yaml (dispatched with fresh-validation=true) - 1. checkout kind-deployment @ main - 2. [fresh-validation] sync-cf-deployment-versions.py --ref develop - → updates Helm chart versions from cf-deployment develop - 3. make up (deploy CF on kind with the synced versions) - 4. run smoke / CATS -``` +1. Checkout kind-deployment @ `main` +2. `sync-cf-deployment-versions.py --ref develop` (updates chart versions) +3. `make up` (deploy CF on kind) +4. Run smoke tests / CATS -The sync + deploy + test all happen within one GitHub Actions run. Nothing -is committed; the synced versions live only in the workflow's checkout for -the duration of the run. +Nothing is committed; synced versions are discarded when the run finishes. + +--- -### Pipeline group: kind-neville +## Pipeline details -All kind jobs are grouped under `kind-neville` in the Concourse pipeline, -following the naming convention of other environments (luna, trelawney, -hermione, etc.): +### Group: `kind-neville` ```yaml - name: kind-neville @@ -160,215 +64,95 @@ hermione, etc.): - kind-cats ``` -### Job 1: kind-smoke-tests - -**Trigger:** `cf-deployment-develop` with `passed: [unit-test-ops-files, lint-cf-deployment-manifest, unit-test-golang-tests, unit-test-update-releases-coverage]` - -Same trigger gate as every BOSH environment. Dispatches `kind-smoke.yaml` -on `main` with `{"minimal": "true", "fresh-validation": "true"}`. The -`fresh-validation` flag makes the workflow sync chart versions from -cf-deployment `develop` before deploying and running smoke tests. - -### Job 2: kind-cats +### Jobs -**Trigger:** same four unit/lint jobs as above. +| Job | Dispatches | Inputs | +|-----|-----------|--------| +| `kind-smoke-tests` | `kind-smoke.yaml` on `main` | `{"minimal": "true", "fresh-validation": "true"}` | +| `kind-cats` | `kind-cats.yaml` on `main` | `{"fresh-validation": "true"}` | -Dispatches `kind-cats.yaml` on `main` with `{"fresh-validation": "true"}`. -Same self-syncing behavior, but runs the full Cloud Foundry Acceptance Tests. +Both trigger after the same four unit/lint jobs that gate all BOSH +environments and share `serial_groups: [kind-workflows]`. -### Serial group +### Non-blocking -Both jobs share `serial_groups: [kind-workflows]`. This serializes the -GitHub workflow dispatches so we don't run two heavy kind clusters at the -same time on shared GitHub runners. - -### Non-blocking for releases - -The `bless-manifest` job does **not** list any kind jobs in its `passed` -constraints. Kind validation is informational — a failure will not block -release promotion. This is intentional: the Kubernetes deployment path is -newer and less mature than the BOSH path, so it should not gate releases -until confidence is established. +`bless-manifest` does **not** depend on kind jobs. Failures are +informational and do not gate releases. --- -## Reusable Concourse Task: trigger-github-workflow - -All kind jobs use a single reusable task that lives in the -[runtime-ci](https://github.com/cloudfoundry/runtime-ci) repository at -`tasks/trigger-github-workflow/`: - -- **`task.yml`** – Concourse task definition using the - `cloudfoundry/cf-deployment-concourse-tasks` image (which provides `gh` - and `jq`), with params for `GITHUB_TOKEN`, `GITHUB_REPO`, `WORKFLOW_FILE`, - `WORKFLOW_REF`, and `WORKFLOW_INPUTS`. -- **`task`** – shell script that handles the full dispatch-and-watch - lifecycle. - -The pipeline references it via the `runtime-ci` resource -(`runtime-ci/tasks/trigger-github-workflow/task.yml`). - -### Run identification mechanism +## Reusable task: trigger-github-workflow -GitHub's workflow dispatch API returns `204 No Content` with no run ID. -The script must locate the run it just created. Before dispatching, it -snapshots the highest existing run ID (`BEFORE_ID`). It also records the -dispatch timestamp and authenticated user. After dispatching, it polls the -runs API (up to 60 × 10s = 10 minutes) looking for a run matching all three -signals: +Lives in [runtime-ci](https://github.com/cloudfoundry/runtime-ci) at +`tasks/trigger-github-workflow/`. Uses `cloudfoundry/cf-deployment-concourse-tasks` +image (provides `gh` + `jq`). -- `id > BEFORE_ID` (new run, not pre-existing) -- `created_at >= DISPATCH_TS` (created after dispatch) -- `actor.login == ACTOR_LOGIN` (created by the expected service account) - -A `TRIGGER_ID` is still generated from Concourse build metadata -(`BUILD_TEAM_NAME`, `BUILD_PIPELINE_NAME`, `BUILD_JOB_NAME`, `BUILD_NAME`) -and logged for debugging, but it is **not** injected into the workflow inputs -(the target workflows don't declare it, and GitHub rejects unknown inputs -with a 422). The three-signal match is sufficient because -`serial_groups: [kind-workflows]` ensures only one dispatch is in flight -at a time. +The dispatch API returns no run ID, so the script locates the run by +matching: `id > BEFORE_ID`, `created_at >= DISPATCH_TS`, and +`actor.login == ACTOR_LOGIN`. The serial group ensures only one dispatch +is in flight at a time. --- -## Changes in kind-deployment (already merged) - -The kind-deployment side is provided by -[PR #424](https://github.com/cloudfoundry/kind-deployment/pull/424) -(**already merged**), which adds a `fresh-validation` boolean input to both -test workflows. - -### `.github/workflows/kind-smoke.yaml` and `kind-cats.yaml` - -| Change | Purpose | -|--------|---------| -| Add `fresh-validation` boolean input (default `false`) | Opt in to validating cf-deployment's `develop` versions | -| Add step "Use develop versions of cf-deployment" | On `workflow_dispatch` with `fresh-validation == 'true'`, run `pip3 install -r scripts/requirements.txt` then `python3 scripts/sync-cf-deployment-versions.py --ref develop` before `make up` | +## Risks -The step is gated so it only runs on manual dispatch with the flag set. The -scheduled cron runs are unaffected and keep their default behavior. +### 1. Missing artifacts -### `scripts/sync-cf-deployment-versions.py` +When cf-deployment bumps a version on `develop`, the corresponding container +image or Helm chart may not yet exist. The artifact creation pipeline in +kind-deployment works as follows: -The script accepts a `--ref` argument (via argparse). When cf-deployment -dispatches with `fresh-validation: true`, the workflow calls it with -`--ref develop`, so the sync reads component versions from the tip of -`develop` instead of the latest tagged release. - -Because the sync now runs **inside** the smoke/CATS workflows, there is no -separate `sync-cf-deployment-versions.yaml` dispatch, no `auto/…` PR branch, -and no branch-push coordination. Everything happens in the workflow's own -ephemeral checkout and is discarded when the run finishes. - ---- +- A new GitHub release is created for a BOSH release repo (e.g. capi-release) +- Renovate bumps the version in `cf-k8s-releases` (runs once a day) +- The Renovate PR is verified and auto-merged +- On merge to `main`, Helm charts and Docker images are built and published -## Risks and Potential Problems +Two cases can cause a missing artifact: +- **cf-deployment is faster than Renovate** — Renovate runs daily, so there + can be up to ~24h + verification time before the artifact exists. +- **Verification failed** — the artifact could not be built automatically. -### 1. Missing container images +**Mitigation:** Kind is non-blocking, so missing artifacts won't hold up +releases. -When cf-deployment `develop` has a new release version, the corresponding -container image may not yet be built and pushed to `ghcr.io`. The sync -script checks for image availability and exits with an error if images are -missing. This would cause the `fresh-validation` step to fail, failing the -smoke/CATS workflow (and therefore the Concourse job). +### 2. GitHub API availability -**Mitigation:** The sync script reports which images are missing. Container -images are typically built by kind-deployment's Renovate-triggered image -build when version bumps are detected. There may be a gap where cf-deployment -bumps a version before kind-deployment's image build runs. This is the most -likely source of transient failures. Because kind is non-blocking for -`bless-manifest`, such a failure will not hold up releases. +The task depends on the GitHub API for dispatch and polling. -### 2. Concourse-to-GitHub API reliability - -The trigger-github-workflow task depends on the GitHub API being available -for dispatching workflows and polling for run status. GitHub API outages or -rate limiting could cause false failures. - -**Mitigation:** The script uses the `gh` CLI which handles authentication -and retries. The polling loop has 60 attempts with 10-second intervals -(~10 minutes total) before giving up. Rate limiting can be mitigated with a -GitHub App token instead of a PAT. +**Mitigation:** Uses an authenticated token (rate-limit is not a concern). +Polling retries for up to 10 minutes. ### 3. Serial group throughput -Both kind jobs are in the same serial group, so smoke and CATS run -sequentially rather than concurrently. A full cycle is roughly the sum of -both durations. If the pipeline triggers frequently, kind validation may -fall behind BOSH. - -**Mitigation:** Because smoke and CATS create independent kind clusters, -they can be split into separate serial groups if throughput becomes a -problem. The serial group is a conservative default to avoid running two -heavy clusters on shared GitHub runners at once. +Smoke and CATS run sequentially. If the pipeline triggers frequently, kind +validation may fall behind. -### 4. Develop ref may advance between dispatch and sync +**Mitigation:** Can split into separate serial groups, or drop smoke tests +entirely (CATS is a superset — it cannot miss errors that smoke tests would +catch, though smoke tests provide a faster signal). -`fresh-validation` syncs from `develop` at the moment the workflow runs. -Additional commits may land on `develop` between the Concourse trigger and -the in-workflow sync, so the test could validate a slightly newer or older -set of versions than the exact commit that triggered the job. +### 4. Develop ref drift -**Mitigation:** Acceptable — the kind check is informational and -non-blocking. A few commits of drift do not meaningfully change what is -being validated. +The workflow syncs from `develop` at execution time, which may have advanced +since the Concourse trigger. -### 5. Input name coupling - -cf-deployment must pass exactly the input name kind-deployment defines -(`fresh-validation`). If kind-deployment renames the input, the Concourse -dispatch must be updated to match, otherwise the flag is silently ignored -and kind runs against `main` versions instead of `develop`. - -**Mitigation:** The name is fixed by the merged PR #424. Any future rename -in kind-deployment should be coordinated with a matching change here. +**Mitigation:** Acceptable for an informational check. A future enhancement +could pass an exact commit SHA as a workflow input parameter. --- ## Credentials -The pipeline requires a Concourse secret `kind_deployment_github_token` with -`actions: write` scope on `cloudfoundry/kind-deployment`. - ---- - -## Files Changed - -### cf-deployment repository - -| File | Change | -|------|--------| -| `ci/pipelines/cf-deployment.yml` | Added `kind-smoke-tests` and `kind-cats` jobs triggered on `develop` after the four unit/lint jobs (same gate as BOSH environments), dispatching `kind-smoke.yaml`/`kind-cats.yaml` on `main` with `fresh-validation: true`. Added `kind-neville` group. `bless-manifest` does not depend on either job (non-blocking). The jobs reference the reusable task from the `runtime-ci` resource. | - -### runtime-ci repository (PR #687) - -| File | Change | -|------|--------| -| `tasks/trigger-github-workflow/task.yml` | Reusable Concourse task to dispatch and watch a GitHub workflow | -| `tasks/trigger-github-workflow/task` | Dispatch-and-watch script with run identification via id/timestamp/actor matching | - -### kind-deployment repository (already merged — PR #424) - -| File | Change | -|------|--------| -| `.github/workflows/kind-smoke.yaml` | Added `fresh-validation` input + inline sync step (`sync-cf-deployment-versions.py --ref develop`) before `make up` | -| `.github/workflows/kind-cats.yaml` | Same `fresh-validation` input + inline sync step | - ---- - -## Operational Shape - -The intended flow on every `develop` commit that passes unit/lint: - -1. `kind-smoke-tests` dispatches `kind-smoke.yaml` with `fresh-validation: true`. -2. `kind-cats` dispatches `kind-cats.yaml` with `fresh-validation: true`. -3. Inside each workflow, versions are synced from `develop`, CF is deployed on kind, and tests run. -4. Both kind jobs report pass/fail independently in Concourse. -5. `bless-manifest` proceeds based on BOSH-based validations only; kind results are informational. +The pipeline uses a dedicated token from the WG secret manager (stored as a +Concourse secret) with `actions: write` on `cloudfoundry/kind-deployment`. --- -## Pipeline Management +## Related PRs -The kind-neville jobs live inside `ci/pipelines/cf-deployment.yml` under the -`kind-neville` group. To update the pipeline, run `ci/configure cf-deployment`. +| Repository | PR | +|---|---| +| cf-deployment | [#1359](https://github.com/cloudfoundry/cf-deployment/pull/1359) — pipeline jobs | +| runtime-ci | [#687](https://github.com/cloudfoundry/runtime-ci/pull/687) — reusable trigger task | +| cf-deployment-concourse-tasks | [#249](https://github.com/cloudfoundry/cf-deployment-concourse-tasks/pull/249) — add `gh` CLI to image | +| kind-deployment | [#424](https://github.com/cloudfoundry/kind-deployment/pull/424) — `fresh-validation` input (merged) | From fe707b90f2eff5cb66bc8d6f753b957d6f80ec73 Mon Sep 17 00:00:00 2001 From: i759715 Date: Mon, 13 Jul 2026 14:47:03 +0300 Subject: [PATCH 6/6] Adapt the docu and the token --- ci/pipelines/cf-deployment.yml | 4 +-- ci/pipelines/kind-neville.md | 52 ++-------------------------------- 2 files changed, 5 insertions(+), 51 deletions(-) diff --git a/ci/pipelines/cf-deployment.yml b/ci/pipelines/cf-deployment.yml index e233e2bfa..582aabfb2 100644 --- a/ci/pipelines/cf-deployment.yml +++ b/ci/pipelines/cf-deployment.yml @@ -2196,7 +2196,7 @@ jobs: - task: trigger-kind-smoke-workflow file: runtime-ci/tasks/trigger-github-workflow/task.yml params: - GITHUB_TOKEN: ((ard_wg_gitbot_token)) + GITHUB_TOKEN: ((kind_deployment_github_token)) GITHUB_REPO: cloudfoundry/kind-deployment WORKFLOW_FILE: kind-smoke.yaml WORKFLOW_REF: main @@ -2219,7 +2219,7 @@ jobs: - task: trigger-kind-cats-workflow file: runtime-ci/tasks/trigger-github-workflow/task.yml params: - GITHUB_TOKEN: ((ard_wg_gitbot_token)) + GITHUB_TOKEN: ((kind_deployment_github_token)) GITHUB_REPO: cloudfoundry/kind-deployment WORKFLOW_FILE: kind-cats.yaml WORKFLOW_REF: main diff --git a/ci/pipelines/kind-neville.md b/ci/pipelines/kind-neville.md index bbc12e1b8..064a76c5b 100644 --- a/ci/pipelines/kind-neville.md +++ b/ci/pipelines/kind-neville.md @@ -94,57 +94,11 @@ is in flight at a time. --- -## Risks - -### 1. Missing artifacts - -When cf-deployment bumps a version on `develop`, the corresponding container -image or Helm chart may not yet exist. The artifact creation pipeline in -kind-deployment works as follows: - -- A new GitHub release is created for a BOSH release repo (e.g. capi-release) -- Renovate bumps the version in `cf-k8s-releases` (runs once a day) -- The Renovate PR is verified and auto-merged -- On merge to `main`, Helm charts and Docker images are built and published - -Two cases can cause a missing artifact: -- **cf-deployment is faster than Renovate** — Renovate runs daily, so there - can be up to ~24h + verification time before the artifact exists. -- **Verification failed** — the artifact could not be built automatically. - -**Mitigation:** Kind is non-blocking, so missing artifacts won't hold up -releases. - -### 2. GitHub API availability - -The task depends on the GitHub API for dispatch and polling. - -**Mitigation:** Uses an authenticated token (rate-limit is not a concern). -Polling retries for up to 10 minutes. - -### 3. Serial group throughput - -Smoke and CATS run sequentially. If the pipeline triggers frequently, kind -validation may fall behind. - -**Mitigation:** Can split into separate serial groups, or drop smoke tests -entirely (CATS is a superset — it cannot miss errors that smoke tests would -catch, though smoke tests provide a faster signal). - -### 4. Develop ref drift - -The workflow syncs from `develop` at execution time, which may have advanced -since the Concourse trigger. - -**Mitigation:** Acceptable for an informational check. A future enhancement -could pass an exact commit SHA as a workflow input parameter. - ---- - ## Credentials -The pipeline uses a dedicated token from the WG secret manager (stored as a -Concourse secret) with `actions: write` on `cloudfoundry/kind-deployment`. +The pipeline uses `((kind_deployment_github_token))` as the Concourse secret +name. The value comes from Google Secret Manager: secret `ard-wg-k8s-gitbot`, +version 2 (labelled "cf-deployment"), created by the WG for this integration. ---