Feat/additional workspaces #3
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| branches: [main] | |
| pull_request: | |
| branches: [main] | |
| env: | |
| GO_VERSION: '1.24' | |
| REGISTRY: ghcr.io | |
| jobs: | |
| lint: | |
| name: Lint | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-go@v5 | |
| with: | |
| go-version: ${{ env.GO_VERSION }} | |
| - name: golangci-lint | |
| uses: golangci/golangci-lint-action@v6 | |
| with: | |
| version: v1.64.5 | |
| install-mode: goinstall | |
| args: --timeout=5m | |
| reconcile-guard: | |
| name: Reconcile Guard | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Check for unconditional Update on managed resources | |
| run: | | |
| # Detect bare r.Update/r.Create calls in the controller that bypass | |
| # controllerutil.CreateOrUpdate. Only r.Update(ctx, instance) and | |
| # r.Status().Update(ctx, instance) are allowed (CR-level updates). | |
| # See: https://github.com/openclaw-rocks/k8s-operator/issues/28 | |
| VIOLATIONS=$(grep -n 'r\.Update(ctx,' internal/controller/openclawinstance_controller.go \ | |
| | grep -v 'instance)' \ | |
| | grep -v '// reconcile-guard:allow' || true) | |
| if [ -n "$VIOLATIONS" ]; then | |
| echo "::error::Found bare r.Update() calls on managed resources. Use controllerutil.CreateOrUpdate instead." | |
| echo "Violations:" | |
| echo "$VIOLATIONS" | |
| echo "" | |
| echo "If this is intentional, add '// reconcile-guard:allow' to the line." | |
| exit 1 | |
| fi | |
| echo "No unconditional Update calls on managed resources found." | |
| helm-rbac-sync: | |
| name: Helm RBAC Sync | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Check Helm chart RBAC matches kubebuilder markers | |
| run: bash hack/check-helm-rbac-sync.sh | |
| helm-crd-sync: | |
| name: Helm CRD Sync | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Check Helm chart CRD templates match generated CRDs | |
| run: bash hack/sync-chart-crds.sh --check | |
| test: | |
| name: Test | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-go@v5 | |
| with: | |
| go-version: ${{ env.GO_VERSION }} | |
| - name: Run tests | |
| run: make test | |
| - name: Upload coverage | |
| uses: codecov/codecov-action@v4 | |
| with: | |
| files: ./cover.out | |
| fail_ci_if_error: false | |
| security-scan: | |
| name: Security Scan | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-go@v5 | |
| with: | |
| go-version: ${{ env.GO_VERSION }} | |
| - name: Run gosec | |
| uses: securego/gosec@master | |
| with: | |
| args: -exclude-generated ./... | |
| - name: Run Trivy vulnerability scanner | |
| uses: aquasecurity/trivy-action@master | |
| with: | |
| scan-type: 'fs' | |
| scan-ref: '.' | |
| severity: 'CRITICAL,HIGH' | |
| build: | |
| name: Build | |
| needs: [lint, test, security-scan] | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| packages: write | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Lowercase image name | |
| id: image | |
| run: echo "name=${GITHUB_REPOSITORY,,}" >> $GITHUB_OUTPUT | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| - name: Login to GHCR | |
| if: github.event_name != 'pull_request' | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ${{ env.REGISTRY }} | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: Extract metadata | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ${{ env.REGISTRY }}/${{ steps.image.outputs.name }} | |
| tags: | | |
| type=sha,prefix= | |
| type=ref,event=branch | |
| type=ref,event=pr | |
| type=raw,value=latest,enable={{is_default_branch}} | |
| - name: Build and push | |
| uses: docker/build-push-action@v5 | |
| with: | |
| context: . | |
| # Only build arm64 on main (where we push). PRs only need amd64 to | |
| # verify the build -- arm64 cross-compilation via QEMU adds ~15 min. | |
| platforms: ${{ github.event_name == 'pull_request' && 'linux/amd64' || 'linux/amd64,linux/arm64' }} | |
| push: ${{ github.event_name != 'pull_request' }} | |
| tags: ${{ steps.meta.outputs.tags }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| cache-from: type=gha | |
| cache-to: type=gha,mode=max | |
| e2e: | |
| name: E2E Tests | |
| needs: [lint, test, security-scan] | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: actions/setup-go@v5 | |
| with: | |
| go-version: ${{ env.GO_VERSION }} | |
| - name: Lowercase image name | |
| id: image | |
| run: echo "name=${GITHUB_REPOSITORY,,}" >> $GITHUB_OUTPUT | |
| - name: Create kind cluster | |
| uses: helm/kind-action@v1 | |
| with: | |
| cluster_name: e2e-test | |
| - name: Build operator image for kind | |
| run: | | |
| docker build -t ${{ env.REGISTRY }}/${{ steps.image.outputs.name }}:e2e . | |
| - name: Load image into kind | |
| run: | | |
| kind load docker-image ${{ env.REGISTRY }}/${{ steps.image.outputs.name }}:e2e --name e2e-test | |
| - name: Pre-pull test images | |
| run: | | |
| docker pull chromedp/headless-shell:stable | |
| kind load docker-image chromedp/headless-shell:stable --name e2e-test | |
| docker pull curlimages/curl | |
| kind load docker-image curlimages/curl --name e2e-test | |
| - name: Install CRDs | |
| run: make install | |
| - name: Deploy operator | |
| run: | | |
| make deploy IMG=${{ env.REGISTRY }}/${{ steps.image.outputs.name }}:e2e | |
| - name: Wait for operator | |
| run: | | |
| kubectl wait --for=condition=available --timeout=180s deployment/openclaw-operator-controller-manager -n openclaw-operator-system | |
| - name: Debug operator status | |
| if: failure() | |
| run: | | |
| echo "=== Deployment status ===" | |
| kubectl get deployment -n openclaw-operator-system | |
| echo "=== Pod status ===" | |
| kubectl get pods -n openclaw-operator-system | |
| echo "=== Pod logs ===" | |
| kubectl logs -n openclaw-operator-system -l control-plane=controller-manager --tail=100 || true | |
| echo "=== Pod describe ===" | |
| kubectl describe pods -n openclaw-operator-system || true | |
| - name: Run E2E tests | |
| run: make test-e2e | |
| env: | |
| OPENROUTER_API_KEY: ${{ secrets.OPENROUTER_API_KEY }} | |
| - name: Install operator-sdk | |
| run: | | |
| export ARCH=$(case $(uname -m) in x86_64) echo -n amd64 ;; aarch64) echo -n arm64 ;; *) echo -n $(uname -m) ;; esac) | |
| export OS=$(uname | awk '{print tolower($0)}') | |
| curl -sSLo /usr/local/bin/operator-sdk "https://github.com/operator-framework/operator-sdk/releases/download/v1.38.0/operator-sdk_${OS}_${ARCH}" | |
| chmod +x /usr/local/bin/operator-sdk | |
| operator-sdk version | |
| - name: Run scorecard tests | |
| run: operator-sdk scorecard bundle --wait-time 120s || true | |
| - name: Dump operator logs | |
| if: failure() | |
| run: | | |
| echo "=== Operator pod status ===" | |
| kubectl get pods -n openclaw-operator-system | |
| echo "=== OpenClawInstance resources ===" | |
| kubectl get openclawinstances -A -o wide || true | |
| echo "=== OpenClawSelfConfig resources ===" | |
| kubectl get openclawselfconfigs -A -o wide || true | |
| echo "=== Operator logs (all) ===" | |
| kubectl logs -n openclaw-operator-system -l control-plane=controller-manager --tail=-1 || true |