name: Pre-Commit Checks on: pull_request: types: [opened, synchronize, reopened] concurrency: group: pre-commit-${{ github.workflow }}-${{ github.event.pull_request.number }} cancel-in-progress: true # Top-level token defaults to read-only. A job-level block replaces this one # rather than adding to it, so each job restates every scope it needs. permissions: contents: read jobs: pre-commit: runs-on: ubuntu-22.04 env: # The pipe falls back to direct on any proxy error (hack/common-envs.mk). GOPROXY: "https://proxy.golang.org|direct" permissions: contents: read steps: - name: Checkout code uses: actions/checkout@a37ce9120846195fa4ece8f58b268e6043cb2f26 # v3 with: fetch-depth: 0 fetch-tags: true - name: Set up Python uses: actions/setup-python@7f4fc3e22c37d6ff65e88745f38bd3157c663f7c # v4 with: python-version: '3.11' - name: Install pre-commit run: pip install pre-commit - name: Set up Go uses: actions/setup-go@40f1582b2485089dde7abd97c1529aa768e1baff # v5 with: go-version-file: go.mod cache: true - name: Pre-fetch k8s.io/code-generator module # The repo-root pre-commit hook (run-make-generate-root) shells out to # hack/update-codegen.sh, which sources kube_codegen.sh from the Go # module cache. The module is not declared in go.mod, so fetch it # explicitly at the version pinned in the script - mirrors the # equivalent step in codegen-drift.yml. run: | version=$(grep -oP 'code-generator@\Kv[0-9.]+' hack/update-codegen.sh) tmpdir=$(mktemp -d) cd "$tmpdir" go mod init codegen-fetch go get "k8s.io/code-generator@${version}" - name: Install generate run: | curl -sSL https://github.com/cozystack/cozyvalues-gen/releases/download/v1.7.0/cozyvalues-gen-linux-amd64.tar.gz | tar -xzvf- -C /usr/local/bin/ cozyvalues-gen - name: Run pre-commit hooks # The complete Bats pass is a manual local hook and is therefore outside # this changed-file lint run. pull-requests.yaml's checks job runs the # full unit path for code and the Bats contracts for docs-only changes. run: | git fetch origin main || git fetch origin master base_commit=$(git rev-parse --verify origin/main || git rev-parse --verify origin/master || echo "") if [ -z "$base_commit" ]; then files=$(git ls-files '*.yaml' '*.md') else files=$(git diff --name-only "$base_commit" -- '*.yaml' '*.md') fi if [ -n "$files" ]; then echo "$files" | xargs pre-commit run --files else echo "No YAML or Markdown files to lint" fi # Commit-message lint for the attribution rules in docs/agents/contributing.md # that a reviewer would otherwise have to read `git log` to catch. It lives # here rather than in the unit-test job of pull-requests.yaml, which is gated # on the diff touching code and so skips a docs-only PR entirely; a trailer is # wrong or right regardless of which files the commit changed. commit-trailers: name: Commit trailers runs-on: ubuntu-22.04 permissions: contents: read # A backport PR is the one shape whose author cannot fix a finding: the # backport action cherry-picks merged commits verbatim, so their messages # are history, and roughly a quarter of the commits on main predate the # rule. Matched on the branch name the action generates, # backport--to-, rather than on the prefix alone; the label that # asks for a backport sits on the original PR, not on the generated one, so # a branch name is the only signal a pull_request event carries. That makes # the exemption a convention rather than something enforced: anyone can name # a branch this way. Every other PR, fork included, is checked. if: ${{ !(startsWith(github.head_ref, 'backport-') && contains(github.head_ref, '-to-')) }} steps: - name: Checkout head uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 with: ref: ${{ github.event.pull_request.head.sha }} fetch-depth: 0 persist-credentials: false - name: Check commit trailers env: BASE_REF: ${{ github.event.pull_request.base.ref }} HEAD_SHA: ${{ github.event.pull_request.head.sha }} run: | set -euo pipefail # The merge base, not the base branch tip: commits landing on the base # branch while the PR is open are not this PR's to answer for. It is # recomputed every run, so a rebased or force-pushed branch is read as # it now is rather than as some earlier walk back from HEAD. # origin/$BASE_REF is already here: fetch-depth 0 makes the checkout # fetch +refs/heads/*:refs/remotes/origin/*. base="$(git merge-base "$HEAD_SHA" "origin/$BASE_REF")" # The checker is read from the base branch, never from the checked-out # PR tree. A pull_request event takes the workflow definition from the # merge of base and head, so the moment this job exists on the base # branch it runs on every open PR — including the ones whose head # predates the script, where reading it from the head would be exit # 127 and a red check saying nothing about trailers. It also puts the # checker out of reach of the PR being measured — the job around it # is not, since the workflow definition comes from that same merge. # Before the script exists on the base branch there is nothing to read # there, so that one window runs the copy under review. checker="$RUNNER_TEMP/check-commit-trailers.sh" if git cat-file -e "origin/$BASE_REF:hack/check-commit-trailers.sh" 2>/dev/null; then git show "origin/$BASE_REF:hack/check-commit-trailers.sh" > "$checker" else echo "::notice::checker absent from $BASE_REF; running the copy from this pull request" cp hack/check-commit-trailers.sh "$checker" fi bash "$checker" "$base..$HEAD_SHA"