From 043a6efbd02fd44cd20cf5d1307ea06686893068 Mon Sep 17 00:00:00 2001 From: Cameron Urban Date: Thu, 5 Nov 2026 10:29:32 -0500 Subject: [PATCH 1/6] Group Dependabot updates into one monthly PR Combine the GitHub Actions and pre-commit updates into a single multi-ecosystem group so that routine CI tooling bumps arrive as one pull request instead of two. Move the schedule from weekly to monthly to cut review overhead, since neither ecosystem affects the package's runtime dependencies and security updates are not tied to this schedule. Co-authored-by: Claude Opus 5.5 --- .github/dependabot.yml | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 41f0e18bf..051dac792 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,22 +1,20 @@ version: 2 +multi-ecosystem-groups: + ci-tooling: + schedule: + interval: monthly updates: - package-ecosystem: github-actions directory: / - schedule: - interval: weekly + patterns: + - "*" + multi-ecosystem-group: ci-tooling cooldown: default-days: 7 - groups: - actions: - patterns: - - "*" - package-ecosystem: pre-commit directory: / - schedule: - interval: weekly + patterns: + - "*" + multi-ecosystem-group: ci-tooling cooldown: default-days: 7 - groups: - pre-commit: - patterns: - - "*" From 2d2ba359000ccea3aac52945568030b700dd3aef Mon Sep 17 00:00:00 2001 From: Cameron Urban Date: Thu, 5 Nov 2026 10:38:16 -0500 Subject: [PATCH 2/6] Assign grouped Dependabot PRs to the maintainer Automate the assignment step, which has been manual on each pull request even though CODEOWNERS already requests the review. Set it in the group configuration so that every grouped update arrives assigned and shows up in the assigned pull request list. Co-authored-by: Claude Opus 5.5 --- .github/dependabot.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 051dac792..56ce48f2e 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -3,6 +3,8 @@ multi-ecosystem-groups: ci-tooling: schedule: interval: monthly + assignees: + - camUrban updates: - package-ecosystem: github-actions directory: / From 48fd06eb27c6ab3d3b66a98333923da2f6a3c384 Mon Sep 17 00:00:00 2001 From: Cameron Urban Date: Thu, 5 Nov 2026 10:56:14 -0500 Subject: [PATCH 3/6] Shorten Dependabot PR titles with a workflow Keep squash-merge commit subjects, which append the PR number to the PR title, within the 50-character limit. Rename each Dependabot pull request from a workflow, since Dependabot offers no option for a fixed title. Base the title on the changed files so that runtime dependency bumps from security updates stay distinct from CI tooling bumps in the history, and pick the singular or plural form from the dependency list in Dependabot's commit message, which survives the rename on later runs. List the new workflow in CLAUDE.md alongside the others. Co-authored-by: Claude Opus 5.5 --- .github/workflows/dependabot-title.yml | 32 ++++++++++++++++++++++++++ CLAUDE.md | 2 +- 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/dependabot-title.yml diff --git a/.github/workflows/dependabot-title.yml b/.github/workflows/dependabot-title.yml new file mode 100644 index 000000000..c84de7ff8 --- /dev/null +++ b/.github/workflows/dependabot-title.yml @@ -0,0 +1,32 @@ +name: dependabot-title + +on: + pull_request: + types: [opened, reopened, synchronize] + +permissions: {} + +jobs: + dependabot-title: + if: github.event.pull_request.user.login == 'dependabot[bot]' + runs-on: ubuntu-latest + permissions: + pull-requests: write + steps: + - name: Shorten the pull request title + env: + GH_TOKEN: ${{ github.token }} + PR_URL: ${{ github.event.pull_request.html_url }} + run: | + non_ci_files=$(gh pr view "$PR_URL" --json files --jq '[.files[].path | select(startswith(".github/") or . == ".pre-commit-config.yaml" | not)] | length') + if [ "$non_ci_files" -eq 0 ]; then + title="Bump CI tooling" + else + dependency_count=$(gh pr view "$PR_URL" --json commits --jq '[.commits[] | select(any(.authors[]; .login == "dependabot[bot]")) | .messageBody | split("\n")[] | select(startswith("- dependency-name: "))] | unique | length') + if [ "$dependency_count" -eq 1 ]; then + title="Bump runtime dependency" + else + title="Bump runtime dependencies" + fi + fi + gh pr edit "$PR_URL" --title "$title" diff --git a/CLAUDE.md b/CLAUDE.md index e093acc08..4d3bb0686 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -34,7 +34,7 @@ Requires Python 3.11, but active development is done in 3.14 ### Relevant Directories, Packages, and Files -- `.github/`: Directory with GitHub configuration files: the issue and pull request templates, the label definitions (`labels.yml`), the Dependabot, funding, and code-owner configuration, and the GitHub Actions workflows (`ascii-only.yml`, `ci.yml`, `label-sync.yml`, `pre-commit-hooks.yml`, `private-access.yml`, `publish.yml`, and `zizmor.yml`) +- `.github/`: Directory with GitHub configuration files: the issue and pull request templates, the label definitions (`labels.yml`), the Dependabot, funding, and code-owner configuration, and the GitHub Actions workflows (`ascii-only.yml`, `ci.yml`, `dependabot-title.yml`, `label-sync.yml`, `pre-commit-hooks.yml`, `private-access.yml`, `publish.yml`, and `zizmor.yml`) - `.venv/`: Directory for the Python virtual environment, configured for the host machine's OS (not included in version control) - `.venv-wsl/`: Directory for the Python virtual environment configured for a WSL OS (not included in version control, may be missing if host machine doesn't use WSL for development) - `experimental/`: Directory with experimental scripts and prototypes (not included in version control) From f6567cb3d714537b6c95d34cd54d2cfc0f9bfa71 Mon Sep 17 00:00:00 2001 From: Cameron Urban Date: Thu, 5 Nov 2026 11:10:19 -0500 Subject: [PATCH 4/6] Configure labels and assignee for pip security PRs Stop Dependabot from creating and applying its default python label on security update pull requests, since that label is not in labels.yml and has to be removed by hand each time. Assign those pull requests to the maintainer as well, matching the grouped CI tooling updates. Set the open pull request limit to zero so that the new pip entry carries these settings for security updates without also opening routine version bumps to the requirements files. Co-authored-by: Claude Opus 5.5 --- .github/dependabot.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 56ce48f2e..0dfceda42 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -20,3 +20,12 @@ updates: multi-ecosystem-group: ci-tooling cooldown: default-days: 7 + - package-ecosystem: pip + directory: / + schedule: + interval: monthly + open-pull-requests-limit: 0 + labels: + - dependencies + assignees: + - camUrban From 913850cc6ea60a2838bc7bf1110a9ea4bcdaf908 Mon Sep 17 00:00:00 2001 From: Cameron Urban Date: Thu, 5 Nov 2026 11:16:16 -0500 Subject: [PATCH 5/6] Set milestones on Dependabot PRs automatically Replace the manual step of adding each Dependabot pull request to the next release milestone. Pick the lowest open milestone titled with a semantic version rather than using Dependabot's own milestone option, which accepts only a fixed milestone number and would need editing after every release. Skip pull requests that already have a milestone so that later pushes from Dependabot never override one set by hand. Rename the workflow to reflect its broader role, and update its entry in CLAUDE.md to match. Co-authored-by: Claude Opus 5.5 --- ...ependabot-title.yml => dependabot-triage.yml} | 16 ++++++++++++++-- CLAUDE.md | 2 +- 2 files changed, 15 insertions(+), 3 deletions(-) rename .github/workflows/{dependabot-title.yml => dependabot-triage.yml} (62%) diff --git a/.github/workflows/dependabot-title.yml b/.github/workflows/dependabot-triage.yml similarity index 62% rename from .github/workflows/dependabot-title.yml rename to .github/workflows/dependabot-triage.yml index c84de7ff8..10f966b51 100644 --- a/.github/workflows/dependabot-title.yml +++ b/.github/workflows/dependabot-triage.yml @@ -1,4 +1,4 @@ -name: dependabot-title +name: dependabot-triage on: pull_request: @@ -7,10 +7,11 @@ on: permissions: {} jobs: - dependabot-title: + dependabot-triage: if: github.event.pull_request.user.login == 'dependabot[bot]' runs-on: ubuntu-latest permissions: + issues: write pull-requests: write steps: - name: Shorten the pull request title @@ -30,3 +31,14 @@ jobs: fi fi gh pr edit "$PR_URL" --title "$title" + - name: Set the milestone to the lowest open release + if: github.event.pull_request.milestone == null + env: + GH_TOKEN: ${{ github.token }} + PR_URL: ${{ github.event.pull_request.html_url }} + REPO: ${{ github.repository }} + run: | + milestone=$(gh api "repos/$REPO/milestones?state=open&per_page=100" --jq '[.[].title | select(test("^v[0-9]+\\.[0-9]+\\.[0-9]+$"))] | sort_by(ltrimstr("v") | split(".") | map(tonumber)) | first // empty') + if [ -n "$milestone" ]; then + gh pr edit "$PR_URL" --milestone "$milestone" + fi diff --git a/CLAUDE.md b/CLAUDE.md index 4d3bb0686..c364150d6 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -34,7 +34,7 @@ Requires Python 3.11, but active development is done in 3.14 ### Relevant Directories, Packages, and Files -- `.github/`: Directory with GitHub configuration files: the issue and pull request templates, the label definitions (`labels.yml`), the Dependabot, funding, and code-owner configuration, and the GitHub Actions workflows (`ascii-only.yml`, `ci.yml`, `dependabot-title.yml`, `label-sync.yml`, `pre-commit-hooks.yml`, `private-access.yml`, `publish.yml`, and `zizmor.yml`) +- `.github/`: Directory with GitHub configuration files: the issue and pull request templates, the label definitions (`labels.yml`), the Dependabot, funding, and code-owner configuration, and the GitHub Actions workflows (`ascii-only.yml`, `ci.yml`, `dependabot-triage.yml`, `label-sync.yml`, `pre-commit-hooks.yml`, `private-access.yml`, `publish.yml`, and `zizmor.yml`) - `.venv/`: Directory for the Python virtual environment, configured for the host machine's OS (not included in version control) - `.venv-wsl/`: Directory for the Python virtual environment configured for a WSL OS (not included in version control, may be missing if host machine doesn't use WSL for development) - `experimental/`: Directory with experimental scripts and prototypes (not included in version control) From 3b747390a149bc3e747635876f2ac4d226ba718a Mon Sep 17 00:00:00 2001 From: Cameron Urban Date: Thu, 5 Nov 2026 12:22:33 -0500 Subject: [PATCH 6/6] Prevent milestone overwrite if already set Add check for existing milestone before setting a new one. Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .github/workflows/dependabot-triage.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/dependabot-triage.yml b/.github/workflows/dependabot-triage.yml index 10f966b51..b69812bf4 100644 --- a/.github/workflows/dependabot-triage.yml +++ b/.github/workflows/dependabot-triage.yml @@ -32,12 +32,15 @@ jobs: fi gh pr edit "$PR_URL" --title "$title" - name: Set the milestone to the lowest open release - if: github.event.pull_request.milestone == null env: GH_TOKEN: ${{ github.token }} PR_URL: ${{ github.event.pull_request.html_url }} REPO: ${{ github.repository }} run: | + has_milestone=$(gh pr view "$PR_URL" --json milestone --jq '.milestone != null') + if [ "$has_milestone" = "true" ]; then + exit 0 + fi milestone=$(gh api "repos/$REPO/milestones?state=open&per_page=100" --jq '[.[].title | select(test("^v[0-9]+\\.[0-9]+\\.[0-9]+$"))] | sort_by(ltrimstr("v") | split(".") | map(tonumber)) | first // empty') if [ -n "$milestone" ]; then gh pr edit "$PR_URL" --milestone "$milestone"