Skip to content

ci: add standalone pre-commit checks and quarterly updates #1

ci: add standalone pre-commit checks and quarterly updates

ci: add standalone pre-commit checks and quarterly updates #1

# 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 <full-SHA>` 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}"