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 398ff36..1787719 100644 --- a/.github/workflows/test-kind.yml +++ b/.github/workflows/test-kind.yml @@ -36,14 +36,23 @@ 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 + 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 @@ -135,6 +144,126 @@ 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\":\"${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\":\"${VERSION_DOWNGRADE_LOW}\"}}" + + 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" = "$VERSION_DOWNGRADE_HIGH" ]; then + echo "PASS: lastDeployedVersion preserved as high-water mark (${LAST})" + else + echo "FAIL: lastDeployedVersion was overwritten (got: '${LAST}', expected: '${VERSION_DOWNGRADE_HIGH}')" + 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. 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 - </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 to min version — condition should clear on next reconcile + kubectl patch claw instance-compat -n $NAMESPACE \ + --type=merge -p "{\"spec\":{\"version\":\"${PLUGIN_MIN_VERSION}\"}}" + + 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')" + kubectl get claw instance-compat -n $NAMESPACE -o yaml + 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: | @@ -193,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