feat(claw4k8s): multi-namespace watcher + Slack/Discord notifications #156
Workflow file for this run
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] | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.ref }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| jobs: | |
| changes: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| go: ${{ steps.filter.outputs.go }} | |
| docs: ${{ steps.filter.outputs.docs }} | |
| hermesrs: ${{ steps.filter.outputs.hermesrs }} | |
| steps: | |
| - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| - uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 | |
| id: filter | |
| with: | |
| filters: | | |
| go: | |
| - '**/*.go' | |
| - 'go.mod' | |
| - 'go.sum' | |
| - 'Makefile' | |
| - 'Dockerfile*' | |
| - 'runtimes/**/Dockerfile*' | |
| docs: | |
| - '**/*.md' | |
| - 'docs/**' | |
| hermesrs: | |
| - 'runtimes/hermesrs/**' | |
| - 'internal/runtime/hermesrs.go' | |
| lint: | |
| needs: changes | |
| if: needs.changes.outputs.go == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v8.0.0 | |
| with: | |
| version: latest | |
| test: | |
| needs: changes | |
| if: needs.changes.outputs.go == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - name: Install setup-envtest | |
| run: go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest | |
| - name: Run tests | |
| run: | | |
| export KUBEBUILDER_ASSETS="$(setup-envtest use 1.32.x -p path)" | |
| go test -race -coverprofile=coverage.out -covermode=atomic ./... | |
| - name: Upload coverage | |
| if: always() | |
| uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 | |
| with: | |
| name: coverage | |
| path: coverage.out | |
| build: | |
| needs: changes | |
| if: needs.changes.outputs.go == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - name: Build | |
| run: go build ./... | |
| - name: Vet | |
| run: go vet ./... | |
| verify-codegen: | |
| needs: changes | |
| if: needs.changes.outputs.go == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - name: Install controller-gen | |
| run: go install sigs.k8s.io/controller-tools/cmd/controller-gen@v0.20.1 | |
| - name: Verify generated files are up to date | |
| run: | | |
| make generate | |
| make manifests | |
| if [ -n "$(git status --porcelain)" ]; then | |
| echo "::error::Generated files are out of date. Run 'make generate && make manifests' and commit the changes." | |
| git diff | |
| exit 1 | |
| fi | |
| docker-build: | |
| needs: changes | |
| if: needs.changes.outputs.go == 'true' | |
| runs-on: ubuntu-latest | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| target: | |
| - { name: operator, file: Dockerfile, context: . } | |
| - { name: claw4k8s, file: Dockerfile.claw4k8s, context: . } | |
| - { name: init, file: Dockerfile.init, context: . } | |
| - { name: ipcbus, file: Dockerfile.ipcbus, context: . } | |
| - { name: channel-slack, file: Dockerfile.channel-slack, context: . } | |
| - { name: channel-webhook, file: Dockerfile.channel-webhook, context: . } | |
| - { name: openclaw, file: runtimes/openclaw/Dockerfile, context: runtimes/openclaw } | |
| steps: | |
| - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| - name: Build ${{ matrix.target.name }} | |
| run: docker build -f ${{ matrix.target.file }} ${{ matrix.target.context }} | |
| # Separate slow build for hermes-agent-rs (Rust full compile ~10 min). | |
| # Only runs when the Dockerfile or adapter changes — keeps PR feedback fast. | |
| docker-build-hermesrs: | |
| needs: changes | |
| if: needs.changes.outputs.hermesrs == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| - name: Build hermes-agent-rs | |
| run: | | |
| docker build -f runtimes/hermesrs/Dockerfile \ | |
| --build-arg HERMES_REPO=https://github.com/willamhou/hermes-agent-rs.git \ | |
| --build-arg HERMES_REF=main \ | |
| -t hermesrs:ci-smoke \ | |
| runtimes/hermesrs/ | |
| govulncheck: | |
| needs: changes | |
| if: needs.changes.outputs.go == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - name: Install govulncheck | |
| run: go install golang.org/x/vuln/cmd/govulncheck@latest | |
| - name: Run govulncheck | |
| run: govulncheck ./... | |
| coverage-gate: | |
| needs: [changes, test] | |
| if: needs.changes.outputs.go == 'true' && needs.test.result == 'success' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| - uses: actions/setup-go@d35c59abb061a4a6fb18e82ac0862c26744d6ab5 # v5.5.0 | |
| with: | |
| go-version-file: go.mod | |
| cache: true | |
| - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 | |
| with: | |
| name: coverage | |
| - name: Check coverage threshold | |
| run: | | |
| THRESHOLD=60 | |
| TOTAL=$(go tool cover -func=coverage.out | grep '^total:' | awk '{print $NF}' | tr -d '%') | |
| echo "Total coverage: ${TOTAL}%" | |
| if awk "BEGIN {exit !(${TOTAL} < ${THRESHOLD})}"; then | |
| echo "::error::Coverage ${TOTAL}% is below threshold ${THRESHOLD}%" | |
| exit 1 | |
| fi | |
| dco: | |
| if: github.event_name == 'pull_request' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 | |
| with: | |
| fetch-depth: 0 | |
| - name: Check DCO sign-off | |
| run: | | |
| commits=$(git log --no-merges --format='%H %s' origin/${{ github.base_ref }}..HEAD) | |
| failed=0 | |
| while IFS= read -r line; do | |
| [ -z "$line" ] && continue | |
| sha=$(echo "$line" | cut -d' ' -f1) | |
| body=$(git log -1 --format='%B' "$sha") | |
| if ! echo "$body" | grep -q "Signed-off-by:"; then | |
| echo "::error::Commit $sha is missing Signed-off-by line. Use 'git commit -s'." | |
| failed=1 | |
| fi | |
| done <<< "$commits" | |
| if [ "$failed" -eq 1 ]; then | |
| exit 1 | |
| fi | |
| # Aggregate gate for branch protection. | |
| # | |
| # Set this single job as the sole required status check on main. | |
| # It passes when all applicable jobs either succeeded or were correctly | |
| # skipped (e.g., docs-only PRs skip Go jobs). It fails if any required | |
| # job failed, was cancelled, or errored. | |
| # | |
| # This lets docs-only PRs merge without admin override while still | |
| # failing fast on any real regression. | |
| ci-gate: | |
| needs: [changes, lint, test, build, verify-codegen, docker-build, docker-build-hermesrs, govulncheck, coverage-gate, dco] | |
| if: ${{ always() }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check all required jobs passed or were skipped | |
| run: | | |
| # Any failure or cancellation in a dependency fails the gate. | |
| # 'skipped' is acceptable (path-filtered jobs on docs-only PRs). | |
| results='${{ toJSON(needs) }}' | |
| echo "needs results: $results" | |
| failed=$(echo "$results" | jq -r 'to_entries[] | select(.value.result == "failure" or .value.result == "cancelled") | .key') | |
| if [ -n "$failed" ]; then | |
| echo "::error::One or more required jobs failed or were cancelled:" | |
| echo "$failed" | |
| exit 1 | |
| fi | |
| echo "All required jobs passed or were correctly skipped." |