diff --git a/.github/workflows/integration-reusable.yml b/.github/workflows/integration-reusable.yml new file mode 100644 index 0000000..88c8b9d --- /dev/null +++ b/.github/workflows/integration-reusable.yml @@ -0,0 +1,74 @@ +name: Integration (reusable) + +on: + workflow_call: + inputs: + imageTag: + description: "Image tag to test. Empty string defaults to chart appVersion." + type: string + default: "" + pullPolicy: + description: "Image pull policy (IfNotPresent for pinned, Always for rolling tags)." + type: string + default: "IfNotPresent" + +permissions: + contents: read + +jobs: + integration: + runs-on: ubuntu-24.04 + timeout-minutes: 20 + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Install kind + run: | + curl -sLo /tmp/kind-linux-amd64 \ + https://kind.sigs.k8s.io/dl/v0.23.0/kind-linux-amd64 + curl -sLo /tmp/kind.sha256 \ + https://kind.sigs.k8s.io/dl/v0.23.0/kind-linux-amd64.sha256sum + (cd /tmp && sha256sum -c kind.sha256) + install -m 0755 /tmp/kind-linux-amd64 /usr/local/bin/kind + + - name: Install kubectl + run: | + curl -sLo /tmp/kubectl \ + "https://dl.k8s.io/release/v1.30.0/bin/linux/amd64/kubectl" + curl -sLo /tmp/kubectl.sha256 \ + "https://dl.k8s.io/release/v1.30.0/bin/linux/amd64/kubectl.sha256" + echo "$(cat /tmp/kubectl.sha256) /tmp/kubectl" | sha256sum -c - + install -m 0755 /tmp/kubectl /usr/local/bin/kubectl + + - name: Set up Helm + uses: azure/setup-helm@dda3372f752e03dde6b3237bc9431cdc2f7a02a2 # v5.0.0 + with: + version: v4.1.4 + + - name: Log image digest under test + if: inputs.imageTag != '' + run: | + echo "Testing image arcadedata/arcadedb:${{ inputs.imageTag }}" + docker buildx imagetools inspect "arcadedata/arcadedb:${{ inputs.imageTag }}" || true + + - name: Create kind cluster + run: kind create cluster --wait 60s + + - name: Install chart + run: | + helm install test-arcadedb charts/arcadedb/ \ + --set replicaCount=3 \ + --set persistence.enabled=false \ + --set arcadedb.defaultDatabases="" \ + --set image.tag="${{ inputs.imageTag }}" \ + --set image.pullPolicy="${{ inputs.pullPolicy }}" \ + --timeout 5m \ + --wait + + - name: Run integration tests + run: make test-integration + + - name: Delete kind cluster + if: always() + run: kind delete cluster diff --git a/.github/workflows/latest-image.yml b/.github/workflows/latest-image.yml new file mode 100644 index 0000000..1afe95e --- /dev/null +++ b/.github/workflows/latest-image.yml @@ -0,0 +1,18 @@ +name: CI - Latest Image Guard + +# Pre-release guard: exercises the chart against the rolling `latest` ArcadeDB +# image so regressions in the upcoming release surface before it ships. +on: + schedule: + - cron: "0 6 * * 1" # weekly, Monday 06:00 UTC (matches Dependabot cadence) + workflow_dispatch: + +permissions: + contents: read + +jobs: + integration-latest: + uses: ./.github/workflows/integration-reusable.yml + with: + imageTag: latest + pullPolicy: Always diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index cb344ec..f570d1c 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -39,50 +39,4 @@ jobs: run: make test-unit integration: - runs-on: ubuntu-24.04 - timeout-minutes: 20 - steps: - - name: Checkout - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - - name: Install kind - run: | - curl -sLo /tmp/kind-linux-amd64 \ - https://kind.sigs.k8s.io/dl/v0.23.0/kind-linux-amd64 - curl -sLo /tmp/kind.sha256 \ - https://kind.sigs.k8s.io/dl/v0.23.0/kind-linux-amd64.sha256sum - (cd /tmp && sha256sum -c kind.sha256) - install -m 0755 /tmp/kind-linux-amd64 /usr/local/bin/kind - - - name: Install kubectl - run: | - curl -sLo /tmp/kubectl \ - "https://dl.k8s.io/release/v1.30.0/bin/linux/amd64/kubectl" - curl -sLo /tmp/kubectl.sha256 \ - "https://dl.k8s.io/release/v1.30.0/bin/linux/amd64/kubectl.sha256" - echo "$(cat /tmp/kubectl.sha256) /tmp/kubectl" | sha256sum -c - - install -m 0755 /tmp/kubectl /usr/local/bin/kubectl - - - name: Set up Helm - uses: azure/setup-helm@dda3372f752e03dde6b3237bc9431cdc2f7a02a2 # v5.0.0 - with: - version: v4.1.4 - - - name: Create kind cluster - run: kind create cluster --wait 60s - - - name: Install chart - run: | - helm install test-arcadedb charts/arcadedb/ \ - --set replicaCount=3 \ - --set persistence.enabled=false \ - --set arcadedb.defaultDatabases="" \ - --timeout 5m \ - --wait - - - name: Run integration tests - run: make test-integration - - - name: Delete kind cluster - if: always() - run: kind delete cluster + uses: ./.github/workflows/integration-reusable.yml diff --git a/.gitignore b/.gitignore index 92e1784..72abcff 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ .cr-release-packages/ .helm/ +.claude/settings.local.json +.debug/ +.idea/ diff --git a/README.md b/README.md index dc3d181..b07890b 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,26 @@ make test # run all of the above Unit-test suites live in `charts/arcadedb/tests/` and use [helm-unittest](https://github.com/helm-unittest/helm-unittest). The plugin version is pinned in the `Makefile`. +### Latest-image guard + +`.github/workflows/latest-image.yml` runs the full kind HA integration suite +against the rolling `arcadedata/arcadedb:latest` image every Monday (and on +manual `workflow_dispatch`). It is a **blocking** pre-release guard: a red run +means the upcoming ArcadeDB release breaks the chart. It shares its steps with +the PR integration job via `.github/workflows/integration-reusable.yml`, so both +exercise an identical suite differing only by image tag. + +### Release-bump checklist + +When a new ArcadeDB version is released: + +1. Bump `version` and `appVersion` in `charts/arcadedb/Chart.yaml`. +2. Update the pinned image literal in `charts/arcadedb/tests/statefulset_test.yaml` + to the new version, or `helm-unittest` will fail (it cannot reference + `Chart.AppVersion` in an assertion). +3. The latest-image guard needs no change — it keeps watching the next cycle's + rolling image. + ## Release New chart versions are published via the GitHub Actions Release workflow: diff --git a/docs/superpowers/plans/2026-06-16-latest-image-ci-guard.md b/docs/superpowers/plans/2026-06-16-latest-image-ci-guard.md new file mode 100644 index 0000000..4a26fb4 --- /dev/null +++ b/docs/superpowers/plans/2026-06-16-latest-image-ci-guard.md @@ -0,0 +1,332 @@ +# Latest-Image CI Guard Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Add a blocking, scheduled CI guard that runs the existing kind HA integration suite against the rolling `latest` ArcadeDB image, so regressions in the upcoming release surface before it ships. + +**Architecture:** Extract the current `integration` job from `lint.yml` into a `workflow_call` reusable workflow parameterized by `imageTag` + `pullPolicy`. The existing CI calls it with defaults (released tag); a new scheduled workflow calls it with `latest`/`Always`. This keeps both runs byte-identical except the image tag. + +**Tech Stack:** GitHub Actions (reusable workflows), Helm 4, kind, the repo's `make test-integration` (`ci/integration-test.sh`). + +**Spec:** `docs/superpowers/specs/2026-06-16-latest-image-ci-guard-design.md` + +**Verification tooling (already installed locally):** `actionlint` for workflow syntax/semantics, `helm` for render checks. + +--- + +### Task 1: Reusable integration workflow + +Lift the existing `integration` job into a reusable workflow, threading `imageTag` +and `pullPolicy` into the `helm install`. With default inputs it must behave +exactly as the current job (`arcadedata/arcadedb:26.6.1`, `IfNotPresent`). + +**Files:** +- Create: `.github/workflows/integration-reusable.yml` + +- [ ] **Step 1: Write the reusable workflow file** + +Create `.github/workflows/integration-reusable.yml` with this exact content: + +```yaml +name: Integration (reusable) + +on: + workflow_call: + inputs: + imageTag: + description: "Image tag to test. Empty string defaults to chart appVersion." + type: string + default: "" + pullPolicy: + description: "Image pull policy (IfNotPresent for pinned, Always for rolling tags)." + type: string + default: "IfNotPresent" + +permissions: + contents: read + +jobs: + integration: + runs-on: ubuntu-24.04 + timeout-minutes: 20 + steps: + - name: Checkout + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Install kind + run: | + curl -sLo /tmp/kind-linux-amd64 \ + https://kind.sigs.k8s.io/dl/v0.23.0/kind-linux-amd64 + curl -sLo /tmp/kind.sha256 \ + https://kind.sigs.k8s.io/dl/v0.23.0/kind-linux-amd64.sha256sum + (cd /tmp && sha256sum -c kind.sha256) + install -m 0755 /tmp/kind-linux-amd64 /usr/local/bin/kind + + - name: Install kubectl + run: | + curl -sLo /tmp/kubectl \ + "https://dl.k8s.io/release/v1.30.0/bin/linux/amd64/kubectl" + curl -sLo /tmp/kubectl.sha256 \ + "https://dl.k8s.io/release/v1.30.0/bin/linux/amd64/kubectl.sha256" + echo "$(cat /tmp/kubectl.sha256) /tmp/kubectl" | sha256sum -c - + install -m 0755 /tmp/kubectl /usr/local/bin/kubectl + + - name: Set up Helm + uses: azure/setup-helm@dda3372f752e03dde6b3237bc9431cdc2f7a02a2 # v5.0.0 + with: + version: v4.1.4 + + - name: Log image digest under test + if: inputs.imageTag != '' + run: | + echo "Testing image arcadedata/arcadedb:${{ inputs.imageTag }}" + docker buildx imagetools inspect "arcadedata/arcadedb:${{ inputs.imageTag }}" || true + + - name: Create kind cluster + run: kind create cluster --wait 60s + + - name: Install chart + run: | + helm install test-arcadedb charts/arcadedb/ \ + --set replicaCount=3 \ + --set persistence.enabled=false \ + --set arcadedb.defaultDatabases="" \ + --set image.tag="${{ inputs.imageTag }}" \ + --set image.pullPolicy="${{ inputs.pullPolicy }}" \ + --timeout 5m \ + --wait + + - name: Run integration tests + run: make test-integration + + - name: Delete kind cluster + if: always() + run: kind delete cluster +``` + +- [ ] **Step 2: Validate workflow syntax** + +Run: `actionlint .github/workflows/integration-reusable.yml` +Expected: no output (exit 0). + +- [ ] **Step 3: Verify the embedded install command threads values correctly** + +This proves the `--set` flags produce the right image with default inputs. Run: + +```bash +helm template t charts/arcadedb \ + --set persistence.enabled=false \ + --set image.tag="" \ + --set image.pullPolicy=IfNotPresent \ + | grep -E 'image:|imagePullPolicy:' | sort -u +``` + +Expected output: +``` + image: "arcadedata/arcadedb:26.6.1" + imagePullPolicy: IfNotPresent +``` + +- [ ] **Step 4: Commit** + +```bash +git add .github/workflows/integration-reusable.yml +git commit -m "ci: add reusable integration workflow parameterized by image tag" +``` + +--- + +### Task 2: Refactor `lint.yml` integration job to call the reusable workflow + +Replace the inline `integration` job (steps-based) with a single `uses:` call so +the existing CI runs through the reusable workflow with defaults. `lint` and +`unittest` jobs are untouched. + +**Files:** +- Modify: `.github/workflows/lint.yml:41-88` (the entire `integration:` job) + +- [ ] **Step 1: Replace the integration job** + +In `.github/workflows/lint.yml`, replace the whole `integration:` job (currently +lines 41-88, starting at ` integration:` and ending at the final +` run: kind delete cluster`) with exactly: + +```yaml + integration: + uses: ./.github/workflows/integration-reusable.yml +``` + +Leave everything above it (`lint` and `unittest` jobs, `on:`, `permissions:`) +unchanged. + +- [ ] **Step 2: Validate workflow syntax** + +Run: `actionlint .github/workflows/lint.yml` +Expected: no output (exit 0). + +- [ ] **Step 3: Confirm the default render is unchanged** + +Run: +```bash +helm template t charts/arcadedb --set persistence.enabled=false \ + | grep -E 'image:|imagePullPolicy:' | sort -u +``` +Expected output: +``` + image: "arcadedata/arcadedb:26.6.1" + imagePullPolicy: IfNotPresent +``` + +- [ ] **Step 4: Commit** + +```bash +git add .github/workflows/lint.yml +git commit -m "ci: run integration job via reusable workflow" +``` + +--- + +### Task 3: Scheduled latest-image guard workflow + +A new workflow that runs weekly (and on manual dispatch), calling the reusable +workflow against the rolling `latest` image with `Always` pull policy. Blocking by +default — a failure marks the run red. + +**Files:** +- Create: `.github/workflows/latest-image.yml` + +- [ ] **Step 1: Write the guard workflow file** + +Create `.github/workflows/latest-image.yml` with this exact content: + +```yaml +name: CI - Latest Image Guard + +# Pre-release guard: exercises the chart against the rolling `latest` ArcadeDB +# image so regressions in the upcoming release surface before it ships. +on: + schedule: + - cron: "0 6 * * 1" # weekly, Monday 06:00 UTC (matches Dependabot cadence) + workflow_dispatch: + +permissions: + contents: read + +jobs: + integration-latest: + uses: ./.github/workflows/integration-reusable.yml + with: + imageTag: latest + pullPolicy: Always +``` + +- [ ] **Step 2: Validate workflow syntax** + +Run: `actionlint .github/workflows/latest-image.yml` +Expected: no output (exit 0). + +- [ ] **Step 3: Verify the latest-tag render is correct** + +Confirms the values the guard passes produce the expected image. Run: +```bash +helm template t charts/arcadedb --set persistence.enabled=false \ + --set image.tag=latest --set image.pullPolicy=Always \ + | grep -E 'image:|imagePullPolicy:' | sort -u +``` +Expected output: +``` + image: "arcadedata/arcadedb:latest" + imagePullPolicy: Always +``` + +- [ ] **Step 4: Commit** + +```bash +git add .github/workflows/latest-image.yml +git commit -m "ci: add weekly latest-image regression guard" +``` + +--- + +### Task 4: Document the guard and the release-bump coupling + +Surface the guard in the README's CI/Development section and record the +release-bump checklist (bumping appVersion requires bumping the pinned literal in +`statefulset_test.yaml`). + +**Files:** +- Modify: `README.md` (Development / CI section) + +- [ ] **Step 1: Locate the CI/Development section** + +Run: `grep -n "Development\|lint\|test-integration\|CI" README.md` +Expected: shows the line numbers of the Development section that documents the +`make` targets and CI jobs. + +- [ ] **Step 2: Add the guard + checklist documentation** + +Under the Development/CI section (after the existing description of the lint / +unittest / integration jobs), add this Markdown: + +```markdown +### Latest-image guard + +`.github/workflows/latest-image.yml` runs the full kind HA integration suite +against the rolling `arcadedata/arcadedb:latest` image every Monday (and on +manual `workflow_dispatch`). It is a **blocking** pre-release guard: a red run +means the upcoming ArcadeDB release breaks the chart. It shares its steps with +the PR integration job via `.github/workflows/integration-reusable.yml`, so both +exercise an identical suite differing only by image tag. + +### Release-bump checklist + +When a new ArcadeDB version is released: + +1. Bump `version` and `appVersion` in `charts/arcadedb/Chart.yaml`. +2. Update the pinned image literal in `charts/arcadedb/tests/statefulset_test.yaml` + to the new version, or `helm-unittest` will fail (it cannot reference + `Chart.AppVersion` in an assertion). +3. The latest-image guard needs no change — it keeps watching the next cycle's + rolling image. +``` + +- [ ] **Step 3: Verify the README still renders as valid Markdown** + +Run: `python3 -c "import pathlib; print(len(pathlib.Path('README.md').read_text()))"` +Expected: prints a number greater than before (no exception = file readable). + +- [ ] **Step 4: Commit** + +```bash +git add README.md +git commit -m "docs: document latest-image guard and release-bump checklist" +``` + +--- + +### Task 5: Post-merge live verification (manual) + +The reusable-workflow `uses:` reference resolves against the default branch, so +the guard can only be triggered after these changes land on `main`. This task is a +manual checklist, not an automated test. + +- [ ] **Step 1: After merge, trigger the guard manually** + +Run: `gh workflow run "CI - Latest Image Guard"` +Expected: command succeeds (queues a run). + +- [ ] **Step 2: Watch the run** + +Run: `gh run watch $(gh run list --workflow="CI - Latest Image Guard" --limit 1 --json databaseId --jq '.[0].databaseId')` +Expected: the run completes. Green = chart works against the upcoming image. +Red = first real breakage; inspect the "Log image digest under test" step output +to identify which `latest` build broke, then open a follow-up to fix the chart. + +- [ ] **Step 3: Confirm normal PR CI is unaffected** + +Verify the most recent `CI - Lint, Unit Tests, Integration Tests` run on the merge +commit is green, confirming the reusable extraction did not regress the +default-tag integration path. + +Run: `gh run list --workflow="CI - Lint, Unit Tests, Integration Tests" --limit 1` +Expected: latest run shows `completed success`. diff --git a/docs/superpowers/specs/2026-06-16-latest-image-ci-guard-design.md b/docs/superpowers/specs/2026-06-16-latest-image-ci-guard-design.md new file mode 100644 index 0000000..6f5b39b --- /dev/null +++ b/docs/superpowers/specs/2026-06-16-latest-image-ci-guard-design.md @@ -0,0 +1,124 @@ +# Latest-Image CI Guard — Design + +**Date:** 2026-06-16 +**Status:** Approved (design) + +## Problem + +The chart is pinned to a released ArcadeDB version (currently appVersion `26.6.1`, +image tag defaults to appVersion). All CI — `helm lint`, `helm-unittest`, and the +kind-based 6-phase HA integration suite — runs against that released image only. + +When ArcadeDB ships a new release, the chart has had no signal about whether the +upcoming image still works with it. We want regressions in the *upcoming* +ArcadeDB build to surface **before** that version is officially released, while it +is only available as the rolling `latest` / `*-SNAPSHOT` image on Docker Hub. + +## Goal + +Add an ongoing CI job that continuously exercises the chart against the rolling +`latest` ArcadeDB image, using the existing full kind HA integration suite. The +job is **blocking** (a failure marks the workflow red) and runs on a schedule plus +manual dispatch. This is a permanent pre-release guard, not a one-off — after each +ArcadeDB release the chart bumps to the released tag and the guard keeps watching +the next cycle's `latest`. + +Any breakage the guard surfaces is fixed as a follow-up chart change. That is the +"improve" half of the work, driven by whatever the job reveals. + +## Non-Goals + +- Changing the default image tag of the chart (stays pinned to appVersion). +- Blocking PRs on `latest` stability (the guard is its own scheduled workflow). +- Notifications/issue automation beyond GitHub's native red-workflow signal. +- Running `helm-unittest` against `latest`. The guard is integration-only by + design (see below). + +## Relationship to the version-pinned unit tests + +`charts/arcadedb/tests/statefulset_test.yaml` pins the chart's `AppVersion` in an +`equal` assertion (e.g. `value: arcadedata/arcadedb:26.6.1`), because +`helm-unittest` cannot reference `Chart.AppVersion` inside an assertion value. + +This pinning does **not** affect the guard: +- The guard runs the integration suite only — it never executes `helm-unittest`. +- The guard's `helm install` passes `--set image.tag=latest`, overriding the + AppVersion default, so the pinned literal never collides with `latest`. + +The pinning is instead a **release-bump** coupling, unrelated to this guard: it +must be updated in lockstep with `AppVersion`. See the checklist below. + +### Release-bump checklist (when a new ArcadeDB version ships) + +1. Bump `version` and `appVersion` in `charts/arcadedb/Chart.yaml`. +2. Update the pinned image literal(s) in + `charts/arcadedb/tests/statefulset_test.yaml` to the new version, or + `helm-unittest` will go red. +3. The `latest` guard keeps watching the next cycle's rolling image — no change + needed. + +## Approach + +Chosen: **reusable workflow**. Extract the integration steps into a +`workflow_call` reusable workflow parameterized by image tag and pull policy. Both +the existing PR/push CI and the new scheduled guard call it, so the two runs +exercise byte-identical steps differing only by the image tag. This structurally +prevents the default-tag and latest-tag suites from diverging — the key risk for a +regression guard. + +Rejected alternatives: +- *Standalone scheduled workflow that copies the integration job* — duplicated YAML + drifts over time. +- *Matrix leg on the existing integration job* — conditional matrix gating couples + the scheduled latest run to the PR workflow and is fiddly to reason about. + +## Components + +### 1. `.github/workflows/integration-reusable.yml` +- Trigger: `on: workflow_call`. +- Inputs: + - `imageTag` (string, default `""`) — threaded to `--set image.tag`. + - `pullPolicy` (string, default `IfNotPresent`) — threaded to `--set image.pullPolicy`. +- Body: the current `integration` job steps, lifted verbatim — install kind, + install kubectl, set up Helm, create kind cluster, `helm install` (now with the + two values threaded through), `make test-integration`, delete cluster + (`if: always()`). +- Before the test run, log the resolved image digest of the tag under test so a red + run identifies exactly which build broke. + +### 2. `.github/workflows/lint.yml` +- `lint` and `unittest` jobs unchanged. +- `integration` job replaced by a single call: + `uses: ./.github/workflows/integration-reusable.yml` with default inputs + (released tag, `IfNotPresent`). Behavior is identical to today. + +### 3. `.github/workflows/latest-image.yml` +- Triggers: `on: schedule` (weekly cron, matching the existing weekly Dependabot + cadence) and `workflow_dispatch`. +- Single job that calls the reusable workflow with `imageTag: latest`, + `pullPolicy: Always`. +- Blocking: a failure marks the workflow run red. + +## Behavior Details + +- `pullPolicy: Always` is required for `latest` so kind does not serve a stale + cached layer. +- The digest-logging step makes red runs diagnosable against a moving tag. +- Weekly cron is the chosen default (changeable); manual `workflow_dispatch` allows + on-demand runs. + +## Verification + +After wiring it up, trigger one manual `workflow_dispatch` run against `latest` +(currently the 26.7.1-SNAPSHOT build) to confirm the guard goes green, or to +capture the first real breakage. + +Also confirm the refactored `integration` job in `lint.yml` still runs green on a +normal PR — the reusable extraction must not change existing behavior. + +## Risks + +- A red guard caused by upstream snapshot instability (not a chart problem) is + expected and acceptable; it is the signal we want. Triage on failure. +- The reusable-workflow refactor touches passing CI; the verification step above + guards against regressions in the default-tag path.