From 0d80967211ba36a9c1e7100312ca0e9e3fd5bd3f Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Thu, 1 Oct 2026 16:27:26 -0700 Subject: [PATCH 1/5] ci: add standalone pre-commit checks and quarterly updates --- .github/workflows/pre-commit-autoupdate.yml | 155 ++++++++++++++++++++ .github/workflows/pre-commit.yml | 73 +++++++++ 2 files changed, 228 insertions(+) create mode 100644 .github/workflows/pre-commit-autoupdate.yml create mode 100644 .github/workflows/pre-commit.yml diff --git a/.github/workflows/pre-commit-autoupdate.yml b/.github/workflows/pre-commit-autoupdate.yml new file mode 100644 index 00000000000..1ed6decc06b --- /dev/null +++ b/.github/workflows/pre-commit-autoupdate.yml @@ -0,0 +1,155 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# SPDX-License-Identifier: Apache-2.0 + +name: "CI: pre-commit autoupdate" + +on: + schedule: + # Quarterly, with a non-zero minute to avoid the busiest scheduling window. + - cron: "23 8 1 1,4,7,10 *" + push: + branches: + # Temporary: bootstrap a dry run before this reaches the default branch. + - "rwgk/maint/pre-commit-actions" + workflow_dispatch: + inputs: + dry-run: + description: "Show hook updates without pushing a branch or opening a PR" + type: boolean + default: true + +permissions: {} + +defaults: + run: + shell: bash --noprofile --norc -xeuo pipefail {0} + +jobs: + autoupdate: + name: Update pre-commit hooks + if: ${{ github.repository_owner == 'nvidia' }} + runs-on: ubuntu-latest + timeout-minutes: 30 + permissions: + contents: write + pull-requests: write + concurrency: + group: pre-commit-autoupdate + cancel-in-progress: false + steps: + - name: Checkout repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 1 + persist-credentials: false + # Dry runs can test the selected workflow ref. Updates always start + # from the default branch, regardless of the dispatch ref. + ref: ${{ (github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && inputs.dry-run)) && github.ref || github.event.repository.default_branch }} + + - name: Set up Python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: '3.13' + + - name: Install pre-commit + run: python -m pip install --upgrade pip pre-commit + + - name: Update hook revisions + run: | + pre-commit validate-config + # Lychee's hook revision, LYCHEE_VERSION argument, and CI binary pin + # must be upgraded together. Leave it for a coordinated manual update. + repositories="$(python - <<'PY' + from pre_commit.clientlib import load_config + + excluded = {'local', 'meta', 'https://github.com/lycheeverse/lychee'} + repositories = [ + repository['repo'] + for repository in load_config('.pre-commit-config.yaml')['repos'] + if repository['repo'] not in excluded + ] + if not repositories: + raise SystemExit('No hook repositories selected for autoupdate') + print('\n'.join(repositories)) + PY + )" + arguments=() + while IFS= read -r repository; do + arguments+=(--repo "${repository}") + done <<<"${repositories}" + pre-commit autoupdate --freeze "${arguments[@]}" + git diff -- .pre-commit-config.yaml + + - name: Compose PR body + id: pr-body + if: ${{ github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && !inputs.dry-run) }} + run: | + body_path="${RUNNER_TEMP}/pre-commit-autoupdate-body.md" + cat > "${body_path}" <<'EOF' + Quarterly pre-commit hook refresh using `pre-commit autoupdate --freeze`. + Only `.pre-commit-config.yaml` is included. See #2652. + + Lychee is deliberately excluded: its hook revision, `LYCHEE_VERSION` + argument, and CI binary pin require a coordinated manual update. + + > [!IMPORTANT] + > This draft PR was opened with `GITHUB_TOKEN`. A maintainer with write access must: + > + > 1. Select **Approve workflows to run** in the merge box. + > 2. Assign or reassign themselves to this PR. The human-generated `assigned` + > event creates the required **PR has assignee, labels, and milestone** check. + > 3. Review the diff and comment `/ok to test ` using the PR's + > current full head SHA when ready to trigger the heavyweight CI. + + Keep the PR in draft mode until its updates and workflow results are reviewed. + EOF + echo "path=${body_path}" >> "${GITHUB_OUTPUT}" + + - name: Open or update a hook-only draft PR + id: cpr + if: ${{ github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && !inputs.dry-run) }} + uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1 + with: + token: ${{ github.token }} + add-paths: .pre-commit-config.yaml + base: ${{ github.event.repository.default_branch }} + branch: rwgk/maint/pre-commit-autoupdate + draft: true + commit-message: "chore: refresh pre-commit hooks" + title: "chore: refresh pre-commit hooks" + signoff: true + sign-commits: true + labels: | + CI/CD + dependencies + assignees: rwgk + milestone: 54 + body-path: ${{ steps.pr-body.outputs.path }} + author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> + committer: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> + + - name: Warn about required maintainer action + if: ${{ steps.cpr.outputs.pull-request-operation == 'created' || steps.cpr.outputs.pull-request-operation == 'updated' }} + run: echo "::warning::The draft refresh PR requires maintainer workflow approval, assignment, and an explicit /ok to test with its full head SHA." + + - name: Summarize + env: + DRY_RUN: ${{ github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && inputs.dry-run) }} + PR_OPERATION: ${{ steps.cpr.outputs.pull-request-operation }} + PR_URL: ${{ steps.cpr.outputs.pull-request-url }} + run: | + { + echo "### Pre-commit autoupdate" + echo + if [[ "${DRY_RUN}" == "true" ]]; then + echo "- Dry run: no branch was pushed and no PR was opened." + echo "- Updated hook revisions are shown in the job log." + elif [[ -n "${PR_URL}" ]]; then + echo "- Operation: \`${PR_OPERATION}\`" + echo "- Draft PR: ${PR_URL}" + else + echo "- No hook revision diff; PR not opened." + fi + echo "- Lychee upgrades remain coordinated manual updates." + } >> "${GITHUB_STEP_SUMMARY}" diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml new file mode 100644 index 00000000000..3989ec5993f --- /dev/null +++ b/.github/workflows/pre-commit.yml @@ -0,0 +1,73 @@ +# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +# +# SPDX-License-Identifier: Apache-2.0 + +name: "CI: pre-commit" + +on: + pull_request: + types: [opened, reopened, synchronize] + push: + # PR events cover draft and fork PRs without also checking copy-pr-bot's + # pull-request/N mirrors. Only main needs a push check. + branches: + - "main" + # Temporary: bootstrap this workflow before it is on the default branch. + - "rwgk/maint/pre-commit-actions" + workflow_dispatch: {} + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }} + cancel-in-progress: true + +permissions: + contents: read + +defaults: + run: + shell: bash --noprofile --norc -xeuo pipefail {0} + +jobs: + pre-commit: + name: Pre-commit (${{ matrix.name }}) + runs-on: ${{ matrix.os }} + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + include: + - name: Linux + os: ubuntu-latest + - name: Windows + os: windows-latest + env: + # Link checks have their own Linux workflow. The installation reminder + # is for developer machines; CI does not install a Git commit hook. + SKIP: lychee,check-precommit-installed + steps: + - name: Checkout repository + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + fetch-depth: 1 + persist-credentials: false + + - name: Set up Python + id: python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: '3.13' + + - name: Set pre-commit cache location + run: echo "PRE_COMMIT_HOME=${RUNNER_TEMP}/pre-commit" >> "${GITHUB_ENV}" + + - name: Cache pre-commit environments + uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 + with: + path: ${{ env.PRE_COMMIT_HOME }} + key: pre-commit-${{ runner.os }}-${{ steps.python.outputs.python-version }}-${{ hashFiles('.pre-commit-config.yaml') }} + + - name: Install pre-commit + run: python -m pip install --upgrade pip pre-commit + + - name: Run pre-commit + run: pre-commit run --all-files --show-diff-on-failure From 85d5fd155f4ff59721988dc7991a4b78899da12a Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Thu, 1 Oct 2026 16:33:39 -0700 Subject: [PATCH 2/5] ci: retain manual testing after workflow bootstrap --- .github/workflows/pre-commit-autoupdate.yml | 8 ++------ .github/workflows/pre-commit.yml | 2 -- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/.github/workflows/pre-commit-autoupdate.yml b/.github/workflows/pre-commit-autoupdate.yml index 1ed6decc06b..b6173d98e99 100644 --- a/.github/workflows/pre-commit-autoupdate.yml +++ b/.github/workflows/pre-commit-autoupdate.yml @@ -8,10 +8,6 @@ on: schedule: # Quarterly, with a non-zero minute to avoid the busiest scheduling window. - cron: "23 8 1 1,4,7,10 *" - push: - branches: - # Temporary: bootstrap a dry run before this reaches the default branch. - - "rwgk/maint/pre-commit-actions" workflow_dispatch: inputs: dry-run: @@ -45,7 +41,7 @@ jobs: persist-credentials: false # Dry runs can test the selected workflow ref. Updates always start # from the default branch, regardless of the dispatch ref. - ref: ${{ (github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && inputs.dry-run)) && github.ref || github.event.repository.default_branch }} + ref: ${{ github.event_name == 'workflow_dispatch' && inputs.dry-run && github.ref || github.event.repository.default_branch }} - name: Set up Python uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 @@ -135,7 +131,7 @@ jobs: - name: Summarize env: - DRY_RUN: ${{ github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && inputs.dry-run) }} + DRY_RUN: ${{ github.event_name == 'workflow_dispatch' && inputs.dry-run }} PR_OPERATION: ${{ steps.cpr.outputs.pull-request-operation }} PR_URL: ${{ steps.cpr.outputs.pull-request-url }} run: | diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index 3989ec5993f..cf02f6d5191 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -12,8 +12,6 @@ on: # pull-request/N mirrors. Only main needs a push check. branches: - "main" - # Temporary: bootstrap this workflow before it is on the default branch. - - "rwgk/maint/pre-commit-actions" workflow_dispatch: {} concurrency: From 14c955c11e948361f0597fa854ac42572f6905f0 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Fri, 2 Oct 2026 08:31:46 -0700 Subject: [PATCH 3/5] ci: use Dependabot for quarterly pre-commit hook updates --- .github/dependabot.yml | 27 ++++ .github/workflows/pre-commit-autoupdate.yml | 151 -------------------- 2 files changed, 27 insertions(+), 151 deletions(-) delete mode 100644 .github/workflows/pre-commit-autoupdate.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 8c8e4274c41..9e39c373900 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -21,6 +21,33 @@ updates: patterns: ["*"] update-types: ["major", "minor", "patch"] + # Group quarterly hook revision updates, preserving SHA pins and frozen comments. + - package-ecosystem: pre-commit + directory: / + schedule: + interval: "quarterly" + time: "09:00" + timezone: "America/Los_Angeles" + + open-pull-requests-limit: 1 + + # Match hook repositories, leaving additional_dependencies pins unchanged. + allow: + - dependency-name: "https://github.com/*" + # Lychee's hook revision, LYCHEE_VERSION argument, and CI binary pin must + # be upgraded together in a coordinated manual update. + ignore: + - dependency-name: "https://github.com/lycheeverse/lychee" + + groups: + pre-commit-quarterly: + applies-to: version-updates + patterns: ["*"] + + assignees: [rwgk] + labels: [CI/CD, dependencies] + milestone: 54 + # GitHub Actions updates targeting the 12.9.x branch - package-ecosystem: github-actions directory: / diff --git a/.github/workflows/pre-commit-autoupdate.yml b/.github/workflows/pre-commit-autoupdate.yml deleted file mode 100644 index b6173d98e99..00000000000 --- a/.github/workflows/pre-commit-autoupdate.yml +++ /dev/null @@ -1,151 +0,0 @@ -# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. -# -# SPDX-License-Identifier: Apache-2.0 - -name: "CI: pre-commit autoupdate" - -on: - schedule: - # Quarterly, with a non-zero minute to avoid the busiest scheduling window. - - cron: "23 8 1 1,4,7,10 *" - workflow_dispatch: - inputs: - dry-run: - description: "Show hook updates without pushing a branch or opening a PR" - type: boolean - default: true - -permissions: {} - -defaults: - run: - shell: bash --noprofile --norc -xeuo pipefail {0} - -jobs: - autoupdate: - name: Update pre-commit hooks - if: ${{ github.repository_owner == 'nvidia' }} - runs-on: ubuntu-latest - timeout-minutes: 30 - permissions: - contents: write - pull-requests: write - concurrency: - group: pre-commit-autoupdate - cancel-in-progress: false - steps: - - name: Checkout repository - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - with: - fetch-depth: 1 - persist-credentials: false - # Dry runs can test the selected workflow ref. Updates always start - # from the default branch, regardless of the dispatch ref. - ref: ${{ github.event_name == 'workflow_dispatch' && inputs.dry-run && github.ref || github.event.repository.default_branch }} - - - name: Set up Python - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 - with: - python-version: '3.13' - - - name: Install pre-commit - run: python -m pip install --upgrade pip pre-commit - - - name: Update hook revisions - run: | - pre-commit validate-config - # Lychee's hook revision, LYCHEE_VERSION argument, and CI binary pin - # must be upgraded together. Leave it for a coordinated manual update. - repositories="$(python - <<'PY' - from pre_commit.clientlib import load_config - - excluded = {'local', 'meta', 'https://github.com/lycheeverse/lychee'} - repositories = [ - repository['repo'] - for repository in load_config('.pre-commit-config.yaml')['repos'] - if repository['repo'] not in excluded - ] - if not repositories: - raise SystemExit('No hook repositories selected for autoupdate') - print('\n'.join(repositories)) - PY - )" - arguments=() - while IFS= read -r repository; do - arguments+=(--repo "${repository}") - done <<<"${repositories}" - pre-commit autoupdate --freeze "${arguments[@]}" - git diff -- .pre-commit-config.yaml - - - name: Compose PR body - id: pr-body - if: ${{ github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && !inputs.dry-run) }} - run: | - body_path="${RUNNER_TEMP}/pre-commit-autoupdate-body.md" - cat > "${body_path}" <<'EOF' - Quarterly pre-commit hook refresh using `pre-commit autoupdate --freeze`. - Only `.pre-commit-config.yaml` is included. See #2652. - - Lychee is deliberately excluded: its hook revision, `LYCHEE_VERSION` - argument, and CI binary pin require a coordinated manual update. - - > [!IMPORTANT] - > This draft PR was opened with `GITHUB_TOKEN`. A maintainer with write access must: - > - > 1. Select **Approve workflows to run** in the merge box. - > 2. Assign or reassign themselves to this PR. The human-generated `assigned` - > event creates the required **PR has assignee, labels, and milestone** check. - > 3. Review the diff and comment `/ok to test ` using the PR's - > current full head SHA when ready to trigger the heavyweight CI. - - Keep the PR in draft mode until its updates and workflow results are reviewed. - EOF - echo "path=${body_path}" >> "${GITHUB_OUTPUT}" - - - name: Open or update a hook-only draft PR - id: cpr - if: ${{ github.event_name == 'schedule' || (github.event_name == 'workflow_dispatch' && !inputs.dry-run) }} - uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1 - with: - token: ${{ github.token }} - add-paths: .pre-commit-config.yaml - base: ${{ github.event.repository.default_branch }} - branch: rwgk/maint/pre-commit-autoupdate - draft: true - commit-message: "chore: refresh pre-commit hooks" - title: "chore: refresh pre-commit hooks" - signoff: true - sign-commits: true - labels: | - CI/CD - dependencies - assignees: rwgk - milestone: 54 - body-path: ${{ steps.pr-body.outputs.path }} - author: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> - committer: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> - - - name: Warn about required maintainer action - if: ${{ steps.cpr.outputs.pull-request-operation == 'created' || steps.cpr.outputs.pull-request-operation == 'updated' }} - run: echo "::warning::The draft refresh PR requires maintainer workflow approval, assignment, and an explicit /ok to test with its full head SHA." - - - name: Summarize - env: - DRY_RUN: ${{ github.event_name == 'workflow_dispatch' && inputs.dry-run }} - PR_OPERATION: ${{ steps.cpr.outputs.pull-request-operation }} - PR_URL: ${{ steps.cpr.outputs.pull-request-url }} - run: | - { - echo "### Pre-commit autoupdate" - echo - if [[ "${DRY_RUN}" == "true" ]]; then - echo "- Dry run: no branch was pushed and no PR was opened." - echo "- Updated hook revisions are shown in the job log." - elif [[ -n "${PR_URL}" ]]; then - echo "- Operation: \`${PR_OPERATION}\`" - echo "- Draft PR: ${PR_URL}" - else - echo "- No hook revision diff; PR not opened." - fi - echo "- Lychee upgrades remain coordinated manual updates." - } >> "${GITHUB_STEP_SUMMARY}" From 5a545e0a03e9f48ebee3a2ed4685648acfcf6e29 Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Fri, 2 Oct 2026 08:35:04 -0700 Subject: [PATCH 4/5] ci: run pre-commit with Python 3.14 --- .github/workflows/pre-commit.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml index cf02f6d5191..b22bb5673b1 100644 --- a/.github/workflows/pre-commit.yml +++ b/.github/workflows/pre-commit.yml @@ -53,7 +53,7 @@ jobs: id: python uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: - python-version: '3.13' + python-version: '3.14' - name: Set pre-commit cache location run: echo "PRE_COMMIT_HOME=${RUNNER_TEMP}/pre-commit" >> "${GITHUB_ENV}" From 7464d45ecc0bdb86d7facd89fe6ee3c8a1a23aad Mon Sep 17 00:00:00 2001 From: "Ralf W. Grosse-Kunstleve" Date: Fri, 2 Oct 2026 09:31:18 -0700 Subject: [PATCH 5/5] ci: align Dependabot schedules and bot PR metadata --- .github/dependabot.yml | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 9e39c373900..2502835c370 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -21,11 +21,13 @@ updates: patterns: ["*"] update-types: ["major", "minor", "patch"] - # Group quarterly hook revision updates, preserving SHA pins and frozen comments. + labels: [CI/CD, dependencies] + + # Group monthly hook revision updates, preserving SHA pins and frozen comments. - package-ecosystem: pre-commit directory: / schedule: - interval: "quarterly" + interval: "monthly" time: "09:00" timezone: "America/Los_Angeles" @@ -40,13 +42,11 @@ updates: - dependency-name: "https://github.com/lycheeverse/lychee" groups: - pre-commit-quarterly: + pre-commit-monthly: applies-to: version-updates patterns: ["*"] - assignees: [rwgk] labels: [CI/CD, dependencies] - milestone: 54 # GitHub Actions updates targeting the 12.9.x branch - package-ecosystem: github-actions @@ -65,3 +65,5 @@ updates: applies-to: version-updates patterns: ["*"] update-types: ["major", "minor", "patch"] + + labels: [CI/CD, dependencies]