diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5d2ed5c2..79b261be 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,12 +3,34 @@ name: Build # Validates GoReleaser can build on all branches (fork-friendly) on: pull_request: + paths: + - "**.go" + - "go.mod" + - "go.sum" + - ".goreleaser.yaml" + - "scripts/windows/**" + - ".github/workflows/build.yml" + - ".github/workflows/release.yml" + - ".github/workflows/release-fork.yml" push: branches: - '**' # Run on all branches + paths: + - "**.go" + - "go.mod" + - "go.sum" + - ".goreleaser.yaml" + - "scripts/windows/**" + - ".github/workflows/build.yml" + - ".github/workflows/release.yml" + - ".github/workflows/release-fork.yml" permissions: {} +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + jobs: goreleaser-build: name: Build opkssh with GoReleaser @@ -23,7 +45,6 @@ jobs: uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 with: go-version-file: 'go.mod' - cache: false - name: Run GoReleaser uses: goreleaser/goreleaser-action@ec59f474b9834571250b370d4735c50f8e2d1e29 # v7.0.0 with: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dfd53610..e574b474 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,23 +4,39 @@ name: CI # This allows fork developers to get CI feedback on their branches on: pull_request: + paths: + - "**.go" + - "go.mod" + - "go.sum" + - "policy/**" + - "scripts/**" + - "test/**" + - ".github/workflows/**" push: branches: - '**' # Run on all branches - # schedule: - # - cron: 0 14 * * MON-FRI # Every weekday at 14:00 UTC + paths: + - "**.go" + - "go.mod" + - "go.sum" + - "policy/**" + - "scripts/**" + - "test/**" + - ".github/workflows/**" + workflow_dispatch: permissions: {} +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + jobs: - # Check that binary can be built + # Check that binary can be built. build: name: Build runs-on: ubuntu-24.04 timeout-minutes: 5 - strategy: - matrix: - go-version: [1.24.x] steps: - name: Checkout uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 @@ -29,13 +45,11 @@ jobs: - name: Install Go uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 with: - go-version: ${{ matrix.go-version }} - - name: Install dependencies - run: go mod download + go-version-file: 'go.mod' - name: Build run: go build -v -o /dev/null - - # Check that binary can be built on Windows + + # Check that binary can be built on Windows. build-windows: name: Build Windows runs-on: windows-latest @@ -46,11 +60,9 @@ jobs: with: persist-credentials: false - name: Install Go - uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0 + uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 with: go-version-file: 'go.mod' - - name: Install dependencies - run: go mod download - name: Build Windows AMD64 shell: pwsh run: | @@ -67,8 +79,8 @@ jobs: shell: pwsh run: | .\opkssh-amd64.exe --version - - # Run Windows unit tests + + # Run Windows unit tests. test-windows: name: 'Windows Tests' runs-on: windows-latest @@ -79,20 +91,19 @@ jobs: with: persist-credentials: false - name: Install Go - uses: actions/setup-go@7a3fe6cf4cb3a834922a1244abfce67bcef6a0c5 # v6.2.0 + uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 with: go-version-file: 'go.mod' - - name: Install dependencies - run: go mod download - name: Run unit tests shell: pwsh run: go test ./... - name: Run Pester tests shell: pwsh run: Invoke-Pester -Path scripts/windows/test -Output Detailed - - # Check that binary can be built natively on Windows ARM64 + + # Keep expensive Windows ARM64 coverage on the upstream repository. build-windows-arm64: + if: github.repository == 'openpubkey/opkssh' && (github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch') name: Build Windows ARM64 runs-on: windows-11-arm timeout-minutes: 5 @@ -105,8 +116,6 @@ jobs: uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 with: go-version-file: 'go.mod' - - name: Install dependencies - run: go mod download - name: Build Windows ARM64 shell: pwsh run: go build -v -o opkssh-arm64.exe @@ -114,9 +123,10 @@ jobs: shell: pwsh run: | .\opkssh-arm64.exe --version - - # Run Windows ARM64 unit tests + + # Keep expensive Windows ARM64 coverage on the upstream repository. test-windows-arm64: + if: github.repository == 'openpubkey/opkssh' && (github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch') name: 'Windows ARM64 Tests' runs-on: windows-11-arm timeout-minutes: 8 @@ -129,25 +139,19 @@ jobs: uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 with: go-version-file: 'go.mod' - - name: Install dependencies - run: go mod download - name: Run unit tests shell: pwsh run: go test ./... - - # Run integration tests + + # Run integration tests on x64 for every relevant change. test: - needs: build name: 'Integration Tests' - runs-on: ${{ matrix.runs_on }} + runs-on: ubuntu-24.04 timeout-minutes: 8 strategy: + fail-fast: false matrix: - runs_on: [ubuntu-24.04, ubuntu-24.04-arm] os: [ubuntu, centos, arch, opensuse] - exclude: - - runs_on: ubuntu-24.04-arm - os: arch env: OS_TYPE: ${{ matrix.os }} steps: @@ -160,11 +164,36 @@ jobs: with: go-version-file: 'go.mod' - name: Install Docker - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0 - - name: Install dependencies - run: go mod download + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 - name: Run integration tests run: go test -tags=integration ./test/integration -timeout=15m -count=1 -parallel=2 -v + + # Keep ARM integration coverage on the upstream repository. + test-arm: + if: github.repository == 'openpubkey/opkssh' && (github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch') + name: 'Integration Tests ARM64' + runs-on: ubuntu-24.04-arm + timeout-minutes: 8 + strategy: + fail-fast: false + matrix: + os: [ubuntu, centos, opensuse] + env: + OS_TYPE: ${{ matrix.os }} + steps: + - name: Checkout + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + persist-credentials: false + - name: Install Go + uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 + with: + go-version-file: 'go.mod' + - name: Install Docker + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 + - name: Run integration tests + run: go test -tags=integration ./test/integration -timeout=15m -count=1 -parallel=2 -v + lint-scripts: name: Shell Scripts Lint & Test runs-on: ubuntu-24.04 diff --git a/.github/workflows/cli-docs.yml b/.github/workflows/cli-docs.yml index 0dbdb227..e7d3f307 100644 --- a/.github/workflows/cli-docs.yml +++ b/.github/workflows/cli-docs.yml @@ -1,15 +1,21 @@ name: CLI Docs -# Runs CI when code merges to the CLI on main +# Runs CI when code merges to the CLI on main. on: push: branches: - main paths: - "main.go" + - "commands/**" + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: check-updates: + if: github.repository == 'openpubkey/opkssh' name: Check for updates to the CLI docs runs-on: ubuntu-latest permissions: diff --git a/.github/workflows/gha-windows.yml b/.github/workflows/gha-windows.yml index 76a33cb3..c3af6599 100644 --- a/.github/workflows/gha-windows.yml +++ b/.github/workflows/gha-windows.yml @@ -2,16 +2,28 @@ name: Test GitHub Provider Windows on: push: + branches: + - '**' + paths: + - "**.go" + - "go.mod" + - "go.sum" + - "scripts/windows/**" + - ".github/workflows/gha-windows.yml" + workflow_dispatch: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true jobs: - build: + test: name: Test on ${{ matrix.name }} runs-on: ${{ matrix.runner }} permissions: id-token: write contents: read timeout-minutes: ${{ matrix.timeout }} - strategy: fail-fast: false matrix: @@ -19,67 +31,170 @@ jobs: - name: Windows Server 2022 runner: windows-2022 timeout: 10 - cache: true - name: Windows Server 2025 runner: windows-2025 timeout: 10 - cache: true - - name: Windows 11 ARM64 - runner: windows-11-arm - timeout: 15 - cache: false steps: - name: Install OpenSSH Server run: | Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0 - + - name: Create default OpenSSH config files run: | Start-Service sshd Start-Sleep -Seconds 2 Stop-Service sshd - + - name: Enable OpenSSH Server logs run: | $sshdConfig = "$env:ProgramData\ssh\sshd_config" (Get-Content $sshdConfig -Raw).Replace('#SyslogFacility AUTH', 'SyslogFacility LOCAL0').Replace('#LogLevel INFO', 'LogLevel Debug3') | Set-Content $sshdConfig - + - name: Checkout uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: persist-credentials: false - - - name: Cache Go modules - if: matrix.cache - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + + - name: Install Go + uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 + with: + go-version-file: 'go.mod' + + - name: Build opkssh + run: go build -v -o opkssh.exe + + - name: Install opkssh with local binary + run: | + powershell -ExecutionPolicy Bypass -File "scripts/windows/Install-OpksshServer.ps1" ` + -InstallFrom "$PWD\opkssh.exe" ` + -NoSshdRestart ` + -Verbose + + - name: Verify system PATH after install + run: | + $installDir = 'C:\Program Files\opkssh' + $regPath = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment' + $systemPath = (Get-ItemProperty -Path $regPath -Name Path).Path + $folders = $systemPath -split [IO.Path]::PathSeparator + $found = $folders | Where-Object { $_.TrimEnd([IO.Path]::DirectorySeparatorChar) -eq $installDir } + if (-not $found) { + throw "FAIL: '$installDir' was NOT found in the system PATH after install" + } + Write-Host "PASS: '$installDir' is in the system PATH after install" + + - name: Add GitHub provider to opkssh configuration + run: | + $providersPath = 'C:\ProgramData\opk\providers' + if ((Get-Content -Path $providersPath -Raw) -notmatch "`r?`n$") { + Add-Content -Path $providersPath -Value '' + } + Add-Content -Path $providersPath -Value 'https://token.actions.githubusercontent.com github oidc' + + - name: Add current repository to policy + run: | + & 'C:\Program Files\opkssh\opkssh.exe' add runneradmin "repo:${env:GITHUB_REPOSITORY}:ref:${env:GITHUB_REF}" https://token.actions.githubusercontent.com + + - name: Start SSH service + run: Start-Service sshd + + - name: Test SSH connection without opkssh (should fail) + run: | + ssh -o StrictHostKeyChecking=no -o PasswordAuthentication=no runneradmin@localhost dir 2>&1 + if ($LASTEXITCODE -eq 0) { throw "SSH should have failed but succeeded" } + Write-Host "SSH correctly rejected without opkssh (exit code: $LASTEXITCODE)" + exit 0 + + - name: Login with GitHub OIDC + run: | + & 'C:\Program Files\opkssh\opkssh.exe' login github --print-id-token + + - name: Test SSH connection with opkssh (should pass) + run: | + ssh -o StrictHostKeyChecking=no -o PasswordAuthentication=no runneradmin@localhost dir + + - name: Debug - Dump opkssh config + run: | + Get-ChildItem 'C:\ProgramData\opk' -Recurse -ErrorAction Continue + Get-Content 'C:\ProgramData\opk\providers' -ErrorAction Continue + Get-Content 'C:\ProgramData\opk\auth_id' -ErrorAction Continue + if: always() + + - name: Debug - Dump opkssh logs + run: | + Get-Content 'C:\ProgramData\opk\logs\opkssh.log' -ErrorAction Continue + if: always() + + - name: Debug - Dump sshd logs + run: | + Get-Content 'C:\ProgramData\ssh\logs\sshd.log' -ErrorAction Continue + if: always() + + - name: Uninstall opkssh + run: | + powershell -ExecutionPolicy Bypass -File "scripts/windows/Uninstall-OpksshServer.ps1" ` + -Force ` + -NoSshdRestart ` + -Verbose + + - name: Verify system PATH after uninstall + run: | + $installDir = 'C:\Program Files\opkssh' + $regPath = 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment' + $systemPath = (Get-ItemProperty -Path $regPath -Name Path).Path + $folders = $systemPath -split [IO.Path]::PathSeparator + $found = $folders | Where-Object { $_.TrimEnd([IO.Path]::DirectorySeparatorChar) -eq $installDir } + if ($found) { + throw "FAIL: '$installDir' is still in the system PATH after uninstall" + } + Write-Host "PASS: '$installDir' was correctly removed from the system PATH after uninstall" + + test-arm64: + if: github.repository == 'openpubkey/opkssh' && (github.ref == 'refs/heads/main' || github.event_name == 'workflow_dispatch') + name: Test on Windows 11 ARM64 + runs-on: windows-11-arm + permissions: + id-token: write + contents: read + timeout-minutes: 15 + steps: + - name: Install OpenSSH Server + run: | + Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0 + + - name: Create default OpenSSH config files + run: | + Start-Service sshd + Start-Sleep -Seconds 2 + Stop-Service sshd + + - name: Enable OpenSSH Server logs + run: | + $sshdConfig = "$env:ProgramData\ssh\sshd_config" + (Get-Content $sshdConfig -Raw).Replace('#SyslogFacility AUTH', 'SyslogFacility LOCAL0').Replace('#LogLevel INFO', 'LogLevel Debug3') | + Set-Content $sshdConfig + + - name: Checkout + uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: - path: | - ~/go/pkg/mod - ~/.cache/go-build - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }} - restore-keys: | - ${{ runner.os }}-go- + persist-credentials: false - name: Install Go uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 with: go-version-file: 'go.mod' - - name: Install dependencies - run: go mod download - - name: Build opkssh run: go build -v -o opkssh.exe - + - name: Install opkssh with local binary run: | powershell -ExecutionPolicy Bypass -File "scripts/windows/Install-OpksshServer.ps1" ` -InstallFrom "$PWD\opkssh.exe" ` -NoSshdRestart ` -Verbose - + - name: Verify system PATH after install run: | $installDir = 'C:\Program Files\opkssh' @@ -91,7 +206,7 @@ jobs: throw "FAIL: '$installDir' was NOT found in the system PATH after install" } Write-Host "PASS: '$installDir' is in the system PATH after install" - + - name: Add GitHub provider to opkssh configuration run: | $providersPath = 'C:\ProgramData\opk\providers' @@ -99,36 +214,36 @@ jobs: Add-Content -Path $providersPath -Value '' } Add-Content -Path $providersPath -Value 'https://token.actions.githubusercontent.com github oidc' - + - name: Add current repository to policy run: | & 'C:\Program Files\opkssh\opkssh.exe' add runneradmin "repo:${env:GITHUB_REPOSITORY}:ref:${env:GITHUB_REF}" https://token.actions.githubusercontent.com - + - name: Start SSH service run: Start-Service sshd - + - name: Test SSH connection without opkssh (should fail) run: | ssh -o StrictHostKeyChecking=no -o PasswordAuthentication=no runneradmin@localhost dir 2>&1 if ($LASTEXITCODE -eq 0) { throw "SSH should have failed but succeeded" } Write-Host "SSH correctly rejected without opkssh (exit code: $LASTEXITCODE)" exit 0 - + - name: Login with GitHub OIDC run: | & 'C:\Program Files\opkssh\opkssh.exe' login github --print-id-token - + - name: Test SSH connection with opkssh (should pass) run: | ssh -o StrictHostKeyChecking=no -o PasswordAuthentication=no runneradmin@localhost dir - + - name: Debug - Dump opkssh config run: | Get-ChildItem 'C:\ProgramData\opk' -Recurse -ErrorAction Continue Get-Content 'C:\ProgramData\opk\providers' -ErrorAction Continue Get-Content 'C:\ProgramData\opk\auth_id' -ErrorAction Continue if: always() - + - name: Debug - Dump opkssh logs run: | Get-Content 'C:\ProgramData\opk\logs\opkssh.log' -ErrorAction Continue @@ -138,14 +253,14 @@ jobs: run: | Get-Content 'C:\ProgramData\ssh\logs\sshd.log' -ErrorAction Continue if: always() - + - name: Uninstall opkssh run: | powershell -ExecutionPolicy Bypass -File "scripts/windows/Uninstall-OpksshServer.ps1" ` -Force ` -NoSshdRestart ` -Verbose - + - name: Verify system PATH after uninstall run: | $installDir = 'C:\Program Files\opkssh' diff --git a/.github/workflows/gha.yml b/.github/workflows/gha.yml index 0d145087..9063e574 100644 --- a/.github/workflows/gha.yml +++ b/.github/workflows/gha.yml @@ -4,9 +4,21 @@ on: push: branches: - main + paths: + - "**.go" + - "go.mod" + - "go.sum" + - "scripts/install-linux.sh" + - ".github/workflows/gha.yml" + - ".github/workflows/gha_opkssh.Dockerfile" + workflow_dispatch: permissions: {} +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: build: name: Build @@ -24,14 +36,10 @@ jobs: uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 with: go-version-file: 'go.mod' - - name: Install dependencies - run: go mod download - - name: Build - run: go build -v -o /dev/null - name: Set up Docker Buildx - uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3.12.0 + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 - name: Build and export to Docker - uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 # zizmor: ignore[cache-poisoning] + uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7.0.0 # zizmor: ignore[cache-poisoning] with: build-args: | AUTHORIZED_REPOSITORY=${{ github.repository }} diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index d734b047..068eb036 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -7,6 +7,7 @@ on: - "**.go" - "go.mod" - "go.sum" + - ".github/workflows/go.yml" push: branches: - '**' # Run on all branches @@ -14,9 +15,14 @@ on: - "**.go" - "go.mod" - "go.sum" + - ".github/workflows/go.yml" permissions: {} +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + jobs: golangci-linter: name: Run golangci linter @@ -29,7 +35,7 @@ jobs: with: go-version-file: 'go.mod' - name: golangci-lint - uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v8.0.0 + uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.2.0 with: version: v2.11.2 @@ -47,8 +53,5 @@ jobs: with: go-version-file: 'go.mod' - - name: Download dependencies - run: go mod download - - name: Test run: go test ./... diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 007bdbbf..41e1c835 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -6,18 +6,24 @@ on: - main pull_request: types: [opened, reopened, synchronize] + permissions: contents: read +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + jobs: update_release_draft: + if: github.repository == 'openpubkey/opkssh' name: Update Release Draft permissions: contents: write pull-requests: write runs-on: ubuntu-24.04 steps: - - uses: release-drafter/release-drafter@b1476f6e6eb133afa41ed8589daba6dc69b4d3f5 # v6.1.0 + - uses: release-drafter/release-drafter@139054aeaa9adc52ab36ddf67437541f039b88e2 # v7.1.1 with: config-name: release-drafter-config.yml publish: false diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ffb5e319..f9d4993d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,6 +8,7 @@ permissions: {} jobs: goreleaser-release: + if: github.repository == 'openpubkey/opkssh' name: Build and release opkssh with GoReleaser runs-on: ubuntu-24.04 permissions: @@ -23,7 +24,6 @@ jobs: uses: actions/setup-go@4b73464bb391d4059bd26b0524d20df3927bd417 # v6.3.0 with: go-version-file: 'go.mod' - cache: false - name: Run GoReleaser uses: goreleaser/goreleaser-action@ec59f474b9834571250b370d4735c50f8e2d1e29 # v7.0.0 with: diff --git a/.github/workflows/staging.yml b/.github/workflows/staging.yml index ef50b394..e3a1470b 100644 --- a/.github/workflows/staging.yml +++ b/.github/workflows/staging.yml @@ -1,15 +1,24 @@ -name: Go Checks +name: Coverage Report on: push: branches: [ "main" ] + paths: + - "**.go" + - "go.mod" + - "go.sum" + - ".github/workflows/staging.yml" permissions: {} -jobs: +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true +jobs: codecov: - name: Push to main test + if: github.repository == 'openpubkey/opkssh' + name: Push to main coverage runs-on: ubuntu-24.04 permissions: contents: write diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index a14f3d03..6543bed7 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -1,16 +1,26 @@ name: GitHub Actions Security Analysis with zizmor 🌈 -# Run security scanning on all branches (fork-friendly) +# Run security scanning only when workflow automation changes. on: push: branches: - '**' # Run on all branches + paths: + - '.github/workflows/**' + - '.github/actions/**' pull_request: branches: - '**' + paths: + - '.github/workflows/**' + - '.github/actions/**' permissions: {} +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + jobs: zizmor: name: Run zizmor 🌈