From d6a09809c52770f430a7745dc421d33cc8c86fe1 Mon Sep 17 00:00:00 2001 From: Ryan Cook Date: Thu, 18 Jun 2026 15:04:27 -0400 Subject: [PATCH 1/4] ci: add VersionDowngrade and PluginCompatibility kind test steps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds two new validation steps to the kind CI test: VersionDowngrade — simulates a previously-deployed version by writing lastDeployedVersion directly to the status subresource (bypasses the ready guard so no real image tag is required), then patches spec.version down to "2026.6.5" and asserts VersionDowngrade=True appears and that lastDeployedVersion is preserved as a high-water mark at "2026.6.8". PluginCompatibility — creates a separate Claw (instance-compat) with a GCP-type Anthropic credential and spec.version "2026.6.1", which is below the PluginMinVersion threshold of "2026.6.8". The operator sets PluginCompatibility=False at reconcile time without needing the pod to start. Upgrading to "2026.6.8" verifies the condition clears. InitContainerFailure is covered by unit tests; it requires actual plugin download timing and is too fragile for a kind smoke test. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/test-kind.yml | 119 ++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) diff --git a/.github/workflows/test-kind.yml b/.github/workflows/test-kind.yml index 398ff36..5224785 100644 --- a/.github/workflows/test-kind.yml +++ b/.github/workflows/test-kind.yml @@ -135,6 +135,125 @@ jobs: echo "$CONTAINERS" | grep -q "gateway" || { echo "FAIL: gateway container not found"; exit 1; } echo "PASS: gateway container verified" + - name: Validate VersionDowngrade detection + run: | + # Simulate a previously-deployed version by writing directly to the status + # subresource (bypasses the ready guard so we don't need a real image tag). + kubectl patch claw $INSTANCE -n $NAMESPACE --subresource=status \ + --type=merge -p '{"status":{"lastDeployedVersion":"2026.6.8"}}' + + # Patch spec to a lower version — operator reconciles and should set condition. + kubectl patch claw $INSTANCE -n $NAMESPACE \ + --type=merge -p '{"spec":{"version":"2026.6.5"}}' + + echo "Waiting for VersionDowngrade condition..." + for i in $(seq 1 30); do + COND=$(kubectl get claw $INSTANCE -n $NAMESPACE \ + -o 'jsonpath={.status.conditions[?(@.type=="VersionDowngrade")].status}' 2>/dev/null || true) + [ "$COND" = "True" ] && break + sleep 2 + done + + COND=$(kubectl get claw $INSTANCE -n $NAMESPACE \ + -o 'jsonpath={.status.conditions[?(@.type=="VersionDowngrade")].status}' 2>/dev/null || true) + if [ "$COND" = "True" ]; then + echo "PASS: VersionDowngrade condition detected" + else + echo "FAIL: VersionDowngrade condition not set (got: '$COND')" + exit 1 + fi + + LAST=$(kubectl get claw $INSTANCE -n $NAMESPACE \ + -o 'jsonpath={.status.lastDeployedVersion}' 2>/dev/null || true) + if [ "$LAST" = "2026.6.8" ]; then + echo "PASS: lastDeployedVersion preserved as high-water mark (${LAST})" + else + echo "FAIL: lastDeployedVersion was overwritten (got: '${LAST}', expected: '2026.6.8')" + exit 1 + fi + + # Restore: clear version so the pod stays on the default image + kubectl patch claw $INSTANCE -n $NAMESPACE \ + --type=json -p '[{"op":"remove","path":"/spec/version"}]' 2>/dev/null || \ + kubectl patch claw $INSTANCE -n $NAMESPACE \ + --type=merge -p '{"spec":{"version":null}}' + + - name: Validate PluginCompatibility detection + run: | + # A minimal fake service-account JSON — the operator mounts it as a + # volume; the content is only consumed by the proxy at runtime. + kubectl create secret generic fake-gcp-key \ + --from-literal=sa-key.json='{"type":"service_account","project_id":"test"}' \ + -n $NAMESPACE + + # Apply a separate Claw with a GCP-type Anthropic credential and a version + # below the PluginMinVersion threshold (2026.6.8). + # The operator sets PluginCompatibility=False at reconcile time without + # needing the pod to start. + kubectl apply -n $NAMESPACE -f - <<'EOF' + apiVersion: claw.sandbox.redhat.com/v1alpha1 + kind: Claw + metadata: + name: instance-compat + spec: + version: "2026.6.1" + credentials: + - name: anthropic-vertex + type: gcp + provider: anthropic + secretRef: + - name: fake-gcp-key + key: sa-key.json + gcp: + project: test-project + location: us-central1 + EOF + + echo "Waiting for PluginCompatibility=False condition..." + for i in $(seq 1 30); do + COND=$(kubectl get claw instance-compat -n $NAMESPACE \ + -o 'jsonpath={.status.conditions[?(@.type=="PluginCompatibility")].status}' 2>/dev/null || true) + [ "$COND" = "False" ] && break + sleep 2 + done + + COND=$(kubectl get claw instance-compat -n $NAMESPACE \ + -o 'jsonpath={.status.conditions[?(@.type=="PluginCompatibility")].status}' 2>/dev/null || true) + MSG=$(kubectl get claw instance-compat -n $NAMESPACE \ + -o 'jsonpath={.status.conditions[?(@.type=="PluginCompatibility")].message}' 2>/dev/null || true) + if [ "$COND" = "False" ]; then + echo "PASS: PluginCompatibility=False detected: $MSG" + else + echo "FAIL: PluginCompatibility condition not set (got: '$COND')" + kubectl get claw instance-compat -n $NAMESPACE -o yaml + exit 1 + fi + + # Upgrade version — condition should clear on next reconcile + kubectl patch claw instance-compat -n $NAMESPACE \ + --type=merge -p '{"spec":{"version":"2026.6.8"}}' + + echo "Waiting for PluginCompatibility condition to clear..." + for i in $(seq 1 30); do + COND=$(kubectl get claw instance-compat -n $NAMESPACE \ + -o 'jsonpath={.status.conditions[?(@.type=="PluginCompatibility")].status}' 2>/dev/null || true) + [ -z "$COND" ] && break + sleep 2 + done + + COND=$(kubectl get claw instance-compat -n $NAMESPACE \ + -o 'jsonpath={.status.conditions[?(@.type=="PluginCompatibility")].status}' 2>/dev/null || true) + if [ -z "$COND" ]; then + echo "PASS: PluginCompatibility condition cleared after version upgrade" + else + echo "FAIL: PluginCompatibility condition still present after upgrade (got: '$COND')" + exit 1 + fi + + # Cleanup + kubectl delete claw instance-compat -n $NAMESPACE --ignore-not-found + kubectl delete secret fake-gcp-key -n $NAMESPACE --ignore-not-found + - name: Collect debug info on failure if: failure() run: | From c6ada54ec15817f235f928eb501e4c701aab339b Mon Sep 17 00:00:00 2001 From: Ryan Cook Date: Thu, 18 Jun 2026 15:05:57 -0400 Subject: [PATCH 2/4] ci: extract test version strings into job-level env vars MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Centralizes the four version constants used by the VersionDowngrade and PluginCompatibility kind test steps so they can be bumped in one place: PLUGIN_MIN_VERSION — must match PluginMinVersion in knownProviders PLUGIN_COMPAT_LOW_VERSION — below threshold to trigger PluginCompatibility=False VERSION_DOWNGRADE_HIGH — recorded as lastDeployedVersion baseline VERSION_DOWNGRADE_LOW — below HIGH to trigger VersionDowngrade=True Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/test-kind.yml | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/.github/workflows/test-kind.yml b/.github/workflows/test-kind.yml index 5224785..8856c54 100644 --- a/.github/workflows/test-kind.yml +++ b/.github/workflows/test-kind.yml @@ -36,6 +36,15 @@ jobs: CONTAINER_TOOL: docker INSTANCE: instance NAMESPACE: default + # Plugin compatibility test versions. + # PLUGIN_MIN_VERSION must match the PluginMinVersion for the Anthropic Vertex + # plugin in knownProviders (claw_providers.go). + PLUGIN_MIN_VERSION: "2026.6.8" + PLUGIN_COMPAT_LOW_VERSION: "2026.6.1" + # Version downgrade test: HIGH is recorded as lastDeployedVersion, + # LOW triggers the VersionDowngrade condition. + VERSION_DOWNGRADE_HIGH: "2026.6.8" + VERSION_DOWNGRADE_LOW: "2026.6.5" steps: - name: Clone the code uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 @@ -140,11 +149,11 @@ jobs: # Simulate a previously-deployed version by writing directly to the status # subresource (bypasses the ready guard so we don't need a real image tag). kubectl patch claw $INSTANCE -n $NAMESPACE --subresource=status \ - --type=merge -p '{"status":{"lastDeployedVersion":"2026.6.8"}}' + --type=merge -p "{\"status\":{\"lastDeployedVersion\":\"${VERSION_DOWNGRADE_HIGH}\"}}" # Patch spec to a lower version — operator reconciles and should set condition. kubectl patch claw $INSTANCE -n $NAMESPACE \ - --type=merge -p '{"spec":{"version":"2026.6.5"}}' + --type=merge -p "{\"spec\":{\"version\":\"${VERSION_DOWNGRADE_LOW}\"}}" echo "Waiting for VersionDowngrade condition..." for i in $(seq 1 30); do @@ -165,10 +174,10 @@ jobs: LAST=$(kubectl get claw $INSTANCE -n $NAMESPACE \ -o 'jsonpath={.status.lastDeployedVersion}' 2>/dev/null || true) - if [ "$LAST" = "2026.6.8" ]; then + if [ "$LAST" = "$VERSION_DOWNGRADE_HIGH" ]; then echo "PASS: lastDeployedVersion preserved as high-water mark (${LAST})" else - echo "FAIL: lastDeployedVersion was overwritten (got: '${LAST}', expected: '2026.6.8')" + echo "FAIL: lastDeployedVersion was overwritten (got: '${LAST}', expected: '${VERSION_DOWNGRADE_HIGH}')" exit 1 fi @@ -187,16 +196,16 @@ jobs: -n $NAMESPACE # Apply a separate Claw with a GCP-type Anthropic credential and a version - # below the PluginMinVersion threshold (2026.6.8). - # The operator sets PluginCompatibility=False at reconcile time without - # needing the pod to start. - kubectl apply -n $NAMESPACE -f - <<'EOF' + # below the PluginMinVersion threshold. The operator sets + # PluginCompatibility=False at reconcile time without needing the pod to start. + # Note: heredoc uses unquoted EOF so env vars expand inside it. + kubectl apply -n $NAMESPACE -f - < Date: Thu, 18 Jun 2026 15:10:57 -0400 Subject: [PATCH 3/4] ci: dump instance-compat CR state on PluginCompatibility assertion failure Addresses CodeRabbit feedback: show the full CR yaml when the PluginCompatibility-cleared assertion fails so CI logs contain the actual condition state for debugging. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/test-kind.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test-kind.yml b/.github/workflows/test-kind.yml index 8856c54..2fef2a1 100644 --- a/.github/workflows/test-kind.yml +++ b/.github/workflows/test-kind.yml @@ -256,6 +256,7 @@ jobs: echo "PASS: PluginCompatibility condition cleared after version upgrade" else echo "FAIL: PluginCompatibility condition still present after upgrade (got: '$COND')" + kubectl get claw instance-compat -n $NAMESPACE -o yaml exit 1 fi From fe37a55264a101791b9f8ffe562a165a8524cae0 Mon Sep 17 00:00:00 2001 From: Ryan Cook Date: Thu, 18 Jun 2026 16:05:29 -0400 Subject: [PATCH 4/4] ci: upgrade GitHub Actions to Node 24 compatible versions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Node 20 is deprecated on GitHub Actions runners. Update all actions to versions that use the node24 runtime: actions/checkout: v4 (node20) → v7 (node24) actions/setup-go: v5 (node20) → v6 (node24) golangci/golangci-lint-action: v8 (node20) → v9 (node24) Pinned SHAs updated accordingly. codecov/codecov-action uses a composite runtime and is unaffected. Co-Authored-By: Claude Sonnet 4.6 --- .github/workflows/lint-bundle.yml | 4 ++-- .github/workflows/lint.yml | 6 +++--- .github/workflows/test-e2e.yml | 4 ++-- .github/workflows/test-kind.yml | 8 ++++---- .github/workflows/test.yml | 4 ++-- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/.github/workflows/lint-bundle.yml b/.github/workflows/lint-bundle.yml index 2fb5d13..842e0d5 100644 --- a/.github/workflows/lint-bundle.yml +++ b/.github/workflows/lint-bundle.yml @@ -14,10 +14,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Clone the code - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Setup Go - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: go-version-file: go.mod diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 474231d..379ab52 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -14,14 +14,14 @@ jobs: runs-on: ubuntu-latest steps: - name: Clone the code - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Setup Go - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: go-version-file: go.mod - name: Run linter - uses: golangci/golangci-lint-action@v8 + uses: golangci/golangci-lint-action@v9 with: version: v2.11.4 diff --git a/.github/workflows/test-e2e.yml b/.github/workflows/test-e2e.yml index 04fca45..dc8bab6 100644 --- a/.github/workflows/test-e2e.yml +++ b/.github/workflows/test-e2e.yml @@ -14,10 +14,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Clone the code - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Setup Go - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: go-version-file: go.mod diff --git a/.github/workflows/test-kind.yml b/.github/workflows/test-kind.yml index 2fef2a1..1787719 100644 --- a/.github/workflows/test-kind.yml +++ b/.github/workflows/test-kind.yml @@ -47,12 +47,12 @@ jobs: VERSION_DOWNGRADE_LOW: "2026.6.5" steps: - name: Clone the code - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 with: persist-credentials: false - name: Setup Go - uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c with: go-version-file: go.mod @@ -322,13 +322,13 @@ jobs: if: github.event_name == 'workflow_dispatch' || (github.event_name == 'push' && vars.QUAY_PUBLISH_ENABLED == 'true') steps: - name: Clone the code - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 with: fetch-depth: 0 persist-credentials: false - name: Setup Go - uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff + uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c with: go-version-file: go.mod diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2e1c880..234c5c4 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,10 +14,10 @@ jobs: runs-on: ubuntu-latest steps: - name: Clone the code - uses: actions/checkout@v4 + uses: actions/checkout@v7 - name: Setup Go - uses: actions/setup-go@v5 + uses: actions/setup-go@v6 with: go-version-file: go.mod