name: PR Check on: [ pull_request, merge_group ] # When a new revision is pushed to a PR, cancel all in-progress CI runs for that # PR. See https://docs.github.com/en/actions/using-jobs/using-concurrency concurrency: group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} cancel-in-progress: true permissions: contents: read security-events: write jobs: # Check what types of changes this PR contains check-changes: name: Check what files changed runs-on: ubuntu-24.04 outputs: go: ${{ steps.changes.outputs.go }} wasm: ${{ steps.changes.outputs.wasm }} docs: ${{ steps.changes.outputs.docs }} rego: ${{ steps.changes.outputs.rego }} yaml: ${{ steps.changes.outputs.yaml }} proto: ${{ steps.changes.outputs.proto }} steps: - name: Check out repository code # Shallow checkout for a faster clone; merge_group fetches the base below. uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Download OPA uses: open-policy-agent/setup-opa@b2b258e089860efaadaaf71bf6e3aecb4a3eeff1 # v2.4.0 with: version: latest - name: Check for file changes id: changes run: | # zizmor: ignore[template-injection] These SHAs are generated by GitHub, not user-controlled. set -e # Default to running all checks echo "go=true" >> $GITHUB_OUTPUT echo "wasm=true" >> $GITHUB_OUTPUT echo "docs=true" >> $GITHUB_OUTPUT echo "rego=true" >> $GITHUB_OUTPUT echo "yaml=true" >> $GITHUB_OUTPUT echo "proto=true" >> $GITHUB_OUTPUT # Get changed files: use git diff for merge_group, PR API for pull_request if [ -n "${{ github.event.merge_group.base_sha }}" ]; then # Fetch just the base commit; shallow checkout above omits it. git fetch --no-tags --depth=1 origin "${{ github.event.merge_group.base_sha }}" git diff --name-only "${{ github.event.merge_group.base_sha }}" "${{ github.event.merge_group.head_sha }}" \ | jq -R '{filename: .}' | jq -s '.' > changed_files.json elif ! curl -s -o changed_files.json -H "Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}" \ "https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/files"; then echo "Error: Failed to fetch changed files from GitHub API" echo "Defaulting to running all checks (go=true, wasm=true, docs=true, rego=true, yaml=true, proto=true)" exit 0 fi if [ ! -s changed_files.json ]; then echo "Warning: No changed files found" echo "Defaulting to running all checks (go=true, wasm=true, docs=true, rego=true, yaml=true, proto=true)" exit 0 fi echo "Changed files:" jq -r '.[].filename' changed_files.json opa eval \ --data build/policy/pr-check/pr_check.rego \ --input changed_files.json \ --format pretty \ 'data.policy["pr-check"].changes' > changes.json echo "Final outputs:" jq -r ' ["go","wasm","docs","rego","yaml","proto"][] as $k | "\($k)=\(.[$k] // false)" ' changes.json | tee -a "$GITHUB_OUTPUT" | sed 's/^/ /' # All jobs essentially re-create the `ci-release-test` make target, but are split # up for parallel runners for faster PR feedback and a nicer UX. generate: name: Generate Code runs-on: ubuntu-24.04 needs: check-changes if: ${{ needs.check-changes.outputs.go == 'true' }} steps: - name: Check out code uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Generate run: make clean generate - name: Upload generated artifacts uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: generated path: | internal/compiler/wasm/opa capabilities.json go-build: name: Go Build (${{ matrix.os }}${{ matrix.arch && format(' {0}', matrix.arch) || '' }}${{ matrix.go_tags }}) runs-on: ${{ matrix.run }} needs: [generate, check-changes] if: ${{ needs.check-changes.outputs.go == 'true' }} strategy: fail-fast: false matrix: include: - os: linux run: ubuntu-24.04 targets: ci-go-ci-build-linux ci-go-ci-build-linux-static arch: amd64 - os: linux run: ubuntu-24.04 targets: ci-go-ci-build-linux ci-go-ci-build-linux-static arch: arm64 - os: windows run: ubuntu-24.04 targets: ci-build-windows arch: amd64 - os: darwin run: macos-15-intel targets: ci-build-darwin arch: amd64 - os: darwin run: macos-15 targets: ci-build-darwin ci-build-darwin-arm64-static arch: arm64 - # NB(sr): We're only building this to see that it still builds. # The resulting binary is not used in any other way. os: linux run: ubuntu-24.04 targets: ci-build-linux-static go_tags: GO_TAGS="-tags=opa_no_oci" arch: arm64 steps: - name: Check out code uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Install Go uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version-file: .go-version if: matrix.os != 'linux' - uses: jdx/mise-action@9e7f7633ff6f6d6048a9418a68d48f288f50eb14 # v4.2.3 with: install: true cache: true mise_toml: | [tools] zig = "0.16.0" if: matrix.os == 'windows' - name: Download generated artifacts uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: generated - name: Build run: make ${{ matrix.go_tags }} ${{ matrix.targets }} env: GOARCH: ${{ matrix.arch }} timeout-minutes: 30 - name: Upload binaries - No Go tags uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 if: ${{ matrix.go_tags == '' }} with: name: binaries-${{ matrix.os }}-${{ matrix.arch }} path: _release go-test: name: Go Test (${{ matrix.os }}) runs-on: ${{ matrix.run }} needs: [generate, check-changes] if: ${{ needs.check-changes.outputs.go == 'true' }} strategy: fail-fast: false matrix: include: - os: linux run: ubuntu-24.04 - os: darwin run: macos-15 steps: - name: Check out code uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Install Go uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version-file: .go-version - name: Install Node # docs/.nvmrc is the source of truth for the Node version across the repo. uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version-file: docs/.nvmrc - name: Download generated artifacts uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: generated - name: Unit Test Golang run: make test-coverage timeout-minutes: 30 - name: E2E Test Golang run: make e2e go-lint: name: Go Lint runs-on: ubuntu-24.04 needs: check-changes if: ${{ needs.check-changes.outputs.go == 'true' }} steps: - name: Check out code uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Golang Style and Lint Check run: make check timeout-minutes: 30 yaml-lint: name: YAML Lint runs-on: ubuntu-24.04 needs: check-changes if: ${{ needs.check-changes.outputs.yaml == 'true' }} steps: - name: Check out code uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: YAML Style and Lint Check run: make check-yaml-tests timeout-minutes: 30 env: YAML_LINT_FORMAT: github proto-check: name: Proto Lint and Breaking runs-on: ubuntu-24.04 needs: check-changes if: ${{ needs.check-changes.outputs.proto == 'true' }} steps: - name: Check out code uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Install buf uses: bufbuild/buf-setup-action@a47c93e0b1648d5651a065437926377d060baa99 # v1.50.0 with: github_token: ${{ secrets.GITHUB_TOKEN }} - name: buf lint run: buf lint - name: buf breaking against base # Bootstrap PR has no buf.yaml on the comparison ref — skip then. env: BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.merge_group.base_sha }} run: | set -euo pipefail if [ -n "${BASE_SHA:-}" ]; then git fetch --no-tags --depth=1 origin "$BASE_SHA" ref="$BASE_SHA" else git fetch --no-tags --depth=1 origin main ref="$(git rev-parse FETCH_HEAD)" fi if git show "${ref}:buf.yaml" >/dev/null 2>&1; then buf breaking --against ".git#ref=${ref}" else echo "Comparison ref ${ref} has no buf.yaml — bootstrap PR, skipping breaking-change check." fi gh-actions-lint: name: Github Actions Lint runs-on: ubuntu-24.04 needs: check-changes if: ${{ needs.check-changes.outputs.yaml == 'true' }} steps: - name: Checkout repository uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Run zizmor uses: zizmorcore/zizmor-action@6fc4b006235f201fdab3722e17240ab420d580e5 # v0.6.1 wasm: name: WASM runs-on: ubuntu-24.04 needs: [generate, check-changes] if: ${{ needs.check-changes.outputs.wasm == 'true' }} steps: - name: Check out code uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Download generated artifacts uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: generated - name: Build and Test Wasm run: make ci-wasm timeout-minutes: 15 - name: Build and Test Wasm SDK run: make ci-go-wasm-sdk-e2e-test timeout-minutes: 30 env: DOCKER_RUNNING: 0 check-generated: name: Check Generated runs-on: ubuntu-24.04 needs: [generate, check-changes] if: ${{ needs.check-changes.outputs.go == 'true' }} steps: - name: Check out code uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Download generated artifacts uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: generated - name: Check Working Copy run: make ci-check-working-copy timeout-minutes: 15 env: DOCKER_RUNNING: 0 race-detector: name: Go Race Detector runs-on: ubuntu-24.04 needs: [generate, check-changes] if: ${{ needs.check-changes.outputs.go == 'true' }} steps: - name: Check out code uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Download generated artifacts uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: generated - name: Test with Race Detector run: make ci-go-race-detector env: DOCKER_RUNNING: 0 smoke-test-docker-images: name: docker image smoke test runs-on: ubuntu-24.04 needs: [go-build, check-changes] if: ${{ needs.check-changes.outputs.go == 'true' }} steps: - name: Check out code uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Set up QEMU uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0 with: platforms: arm64 - name: Download release binaries uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: pattern: binaries-* merge-multiple: true path: _release - name: Test amd64 images run: make ci-image-smoke-test - name: Test arm64 images run: make ci-image-smoke-test env: GOARCH: arm64 # Note(philipc): We only run the amd64 targets for windows/linux smoke-test-binaries: runs-on: ${{ matrix.run }} needs: [go-build, check-changes] if: ${{ needs.check-changes.outputs.go == 'true' }} strategy: matrix: include: - os: linux run: ubuntu-24.04 exec: opa_linux_amd64 arch: amd64 - os: linux run: ubuntu-24.04 exec: opa_linux_amd64_static arch: amd64 wasm: disabled - os: darwin run: macos-15-intel exec: opa_darwin_amd64 arch: amd64 - os: darwin run: macos-15 exec: opa_darwin_arm64_static arch: arm64 wasm: disabled - os: windows run: windows-latest exec: opa_windows_amd64.exe arch: amd64 steps: - name: Check out code uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Install Go uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: stable - name: Download release binaries uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: binaries-${{ matrix.os }}-${{ matrix.arch }} path: _release - name: Prep tests run: cd build/tools && go install tool - name: CLI E2E tests run: | matches=($BINARY_PATH_GLOB) # expand glob export OPA="$(pwd)/${matches[0]}" chmod +x "$OPA" find . -type f -name '*.txtar' -path '*/script/*' -print0 \ | xargs -0 -I{} testscript -e OPA {} shell: bash env: BINARY_PATH_GLOB: _release/*/${{ matrix.exec }} - name: wasm smoke test run: _release/*/${{ matrix.exec }} eval --target wasm 'time.now_ns()' shell: bash if: matrix.wasm != 'disabled' go-version-build: name: Go compat build/test needs: [generate, check-changes] if: ${{ needs.check-changes.outputs.go == 'true' }} runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-24.04, macos-15] version: ["1.25.7"] steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Download generated artifacts uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: generated - uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0 with: go-version: ${{ matrix.version }} - run: make build env: DOCKER_RUNNING: 0 - run: make go-test env: DOCKER_RUNNING: 0 # TEMPORARY JOB - safe to delete once Go 1.27 is released and OPA is updated to it go-1-27-compat: name: Go 1.27 compat build/test (${{ matrix.version }}) needs: [generate, check-changes] if: ${{ needs.check-changes.outputs.go == 'true' }} runs-on: ubuntu-24.04 strategy: fail-fast: false matrix: include: - version: "1.26.5" sha256: 5c2c3b16caefa1d968a94c1daca04a7ca301a496d9b086e17ad77bb81393f053 - version: "1.27rc2" sha256: e2dfdfc2b2d4092bf23d5ffb0a11221c2f3eed2d8acfc51344066b9c83a368db steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Download generated artifacts uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: generated - name: Install Go ${{ matrix.version }} env: VERSION: ${{ matrix.version }} SHA256: ${{ matrix.sha256 }} run: | set -euo pipefail tarball="go${VERSION}.linux-amd64.tar.gz" curl -fsSL --retry 3 -o "${RUNNER_TEMP}/${tarball}" "https://go.dev/dl/${tarball}" echo "${SHA256} ${RUNNER_TEMP}/${tarball}" | sha256sum --check --strict - mkdir -p "${RUNNER_TEMP}/toolchain" tar -C "${RUNNER_TEMP}/toolchain" -xzf "${RUNNER_TEMP}/${tarball}" echo "${RUNNER_TEMP}/toolchain/go/bin" >> "${GITHUB_PATH}" - name: Report Go version run: go version - run: make go-test env: DOCKER_RUNNING: 0 GOTOOLCHAIN: local # Run PR metadata against Rego policies rego-check-pr: name: Rego PR checks runs-on: ubuntu-24.04 needs: check-changes if: ${{ needs.check-changes.outputs.rego == 'true' }} steps: - name: Checkout code # Shallow checkout for a faster clone; merge_group fetches the base below. uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Download OPA uses: open-policy-agent/setup-opa@b2b258e089860efaadaaf71bf6e3aecb4a3eeff1 # v2.4.0 with: version: edge - name: Test policies run: opa test --schema build/policy/schema --bundle build/policy - name: Test embedded Rego policies run: ./build/run-rego-tests.sh - name: Run file policy checks on changed files run: | if [ -n "${{ github.event.merge_group.base_sha }}" ]; then # Fetch just the base commit; shallow checkout above omits it. git fetch --no-tags --depth=1 origin "${{ github.event.merge_group.base_sha }}" git diff --name-only "${{ github.event.merge_group.base_sha }}" "${{ github.event.merge_group.head_sha }}" \ | jq -R '{filename: .}' | jq -s '.' > files.json else curl --silent --fail --header 'Authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' -o files.json \ https://api.github.com/repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/files fi opa eval --bundle build/policy --format values --input files.json --fail-defined 'data.files.deny[message]' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Download Regal uses: open-policy-agent/setup-regal@761188c3b435761fa254beca508a44875619648f # v2.0.0 with: version: latest - name: Run Regal lint # Current configuration ensures anything but build/policy is ignored. While this could point Regal only at that # directory, this will serve as a reminder when more Rego policies are added, as they should be linted by default. run: regal lint --format github . docs-build: name: Build Docs runs-on: ubuntu-24.04 needs: check-changes if: ${{ needs.check-changes.outputs.docs == 'true' }} steps: - name: Check out code uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Install Node uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version-file: docs/.nvmrc cache: npm cache-dependency-path: docs/package-lock.json - name: Build docs run: make docs-ci docs-build docs-fmt-check: name: Docs Format Check runs-on: ubuntu-24.04 needs: check-changes if: ${{ needs.check-changes.outputs.docs == 'true' }} steps: - name: Check out code uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Install Node uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version-file: docs/.nvmrc cache: npm cache-dependency-path: docs/package-lock.json - name: Check docs formatting run: make docs-ci docs-fmt-check docs-lint-check: name: Docs Lint Check runs-on: ubuntu-24.04 needs: check-changes if: ${{ needs.check-changes.outputs.docs == 'true' }} steps: - name: Check out code uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Install Node uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version-file: docs/.nvmrc cache: npm cache-dependency-path: docs/package-lock.json - name: Lint docs run: make docs-ci docs-lint-check docs-markdownlint-check: name: Docs Markdownlint Check runs-on: ubuntu-24.04 needs: check-changes if: ${{ needs.check-changes.outputs.docs == 'true' }} steps: - name: Check out code uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Install Node uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0 with: node-version-file: docs/.nvmrc cache: npm cache-dependency-path: docs/package-lock.json - name: Check markdown linting run: make docs-ci docs-markdownlint-check docs-gen-check: name: Docs Gen Check runs-on: ubuntu-24.04 needs: check-changes if: ${{ needs.check-changes.outputs.docs == 'true' }} steps: - name: Check out code uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Build OPA uses: ./.github/actions/build-opa - name: Check generated docs are up to date run: make docs-gen-check docs-spell-check: name: Docs Spell Check runs-on: ubuntu-24.04 needs: check-changes if: ${{ needs.check-changes.outputs.docs == 'true' }} steps: - name: Check out code uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Install Vale run: | wget -O vale.tar.gz https://github.com/errata-ai/vale/releases/download/v3.13.0/vale_3.13.0_Linux_64-bit.tar.gz sudo tar xzf vale.tar.gz -C /usr/local/bin vale rm vale.tar.gz vale --version - name: Run Vale spell check run: make docs-spell-check benchmarks: name: Benchmarks needs: check-changes if: github.event_name == 'merge_group' && needs.check-changes.outputs.go == 'true' uses: ./.github/workflows/run-benchmarks.yaml with: publish: false test-ocp-with-opa: needs: check-changes if: github.event_name == 'merge_group' && needs.check-changes.outputs.go == 'true' uses: ./.github/workflows/test-ocp-with-opa.yaml test-regal-with-opa: needs: check-changes if: github.event_name == 'merge_group' && needs.check-changes.outputs.go == 'true' uses: ./.github/workflows/test-regal-with-opa.yaml # This job is required to complete before merging, and is set as a branch # protection rule: # https://github.com/open-policy-agent/opa/settings/branch_protection_rules pr-check-summary: name: PR Check Summary runs-on: ubuntu-24.04 needs: [ check-changes, generate, go-build, go-test, go-lint, yaml-lint, gh-actions-lint, proto-check, wasm, check-generated, race-detector, smoke-test-docker-images, smoke-test-binaries, go-version-build, go-1-27-compat, rego-check-pr, docs-build, docs-fmt-check, docs-lint-check, docs-markdownlint-check, docs-gen-check, docs-spell-check, benchmarks, test-ocp-with-opa, test-regal-with-opa, ] if: always() steps: - name: Check out code uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Download OPA uses: open-policy-agent/setup-opa@b2b258e089860efaadaaf71bf6e3aecb4a3eeff1 # v2.4.0 with: version: edge - name: Check job results run: | # zizmor: ignore[template-injection] The 'needs' var is controlled by us. # Create the input file with all job results echo '${{ toJSON(needs) }}' > input.json # Find failed or cancelled jobs using OPA opa eval -d .github/workflows/pull-request.yaml \ --input=input.json \ '{job|some _, job in data.jobs["pr-check-summary"].needs} & {job | input[job].result in {"failure", "cancelled"}}' \ --format=raw > failed_jobs.json # Check for failures and display a nice message if [ "$(cat failed_jobs.json)" != "[]" ]; then echo "The following required jobs did not complete successfully:" jq -r '.[]' failed_jobs.json | sed 's/^/- /' exit 1 fi echo "All jobs completed successfully or were skipped"