Skip to content

feat(ci): monthly uv lock --upgrade opens a pull request - #22

Merged
jmineau merged 1 commit into
mainfrom
feat/lock-upgrade-workflow
Oct 22, 2026
Merged

jmineau merged 1 commit into
mainfrom
feat/lock-upgrade-workflow

Conversation

@jmineau

@jmineau jmineau commented Oct 21, 2026

Copy link
Copy Markdown
Owner

Adds an Upgrade lock workflow to the template: on the 1st of each month (and by hand, from the Actions tab) it runs uv lock --upgrade and, when uv.lock changes, opens or updates one pull request. James agreed to this on his triage board on 2026-10-20 (card python-template-113-lock-forks). Dependabot's configuration is not touched.

Why. Dependabot's uv updates never move a package whose newest release needs a newer Python than the lock's oldest. SciPy 1.18 and NumPy 2.5 need Python 3.12 and the lock covers 3.11, so CI tested a SciPy that users on 3.12 and newer no longer get (jmineau/fips#113). uv lock --upgrade locks the newest release for each Python as a fork in uv.lock.

What the workflow does (template/.github/workflows/lock-upgrade.yml.jinja)

  • Runs uv lock --upgrade on main. If uv.lock is unchanged, it stops.
  • Otherwise it commits uv.lock to the branch chore/lock-upgrade (force-pushed; the branch belongs to the workflow, so last month's open pull request is replaced by this month's), opens the pull request or updates the body of the open one, and lists what uv updated in the body (for example Updated scipy v1.16.3 -> v1.17.1, v1.18.1, the fork).
  • Uses only actions/checkout, astral-sh/setup-uv and the gh CLI, like docs.yml and publish.yml; no third-party pull-request action.
  • Least privilege: contents: read for the workflow; the one job adds contents: write (push the branch), pull-requests: write and actions: write (see the next paragraph). The token is in the environment of the one step that needs it, and checkout keeps no credentials.
  • Actions are pinned by version tag, as in the other workflows: the template's zizmor policy (template/.github/zizmor.yml, unpinned-uses: ref-pin) and CONTRIBUTING.md choose tags kept current by Dependabot over commit SHAs. I did not change that.

CI on the pull request. A pull request opened with the default GITHUB_TOKEN does not start the pull_request workflows (GitHub's rule against workflows triggering workflows; workflow_dispatch is the exception). So the workflow starts Tests, Code Quality and Documentation on the branch with gh workflow run ... --ref chore/lock-upgrade right after the push; their results show on the pull request. This is the simplest thing that works with no secret: Code Quality gets a workflow_dispatch trigger for it (Tests and Documentation already had one; the deploy job of Documentation does not run on a branch). Alternatives, not taken: a personal access token or GitHub App secret in each repository (then CI starts by itself, but seven secrets to rotate), or closing and reopening the pull request by hand each month.

One-time setup per package. gh pr create with the default token needs the repository setting Settings > Actions > General > Workflow permissions > "Allow GitHub Actions to create and approve pull requests". It is off in all seven package repositories (read through the API today). The after-copy message, the README's retrofit steps and the generated CONTRIBUTING.md say so. Without it GitHub refuses to create the pull request, so that step fails after the branch is pushed (not tried). This is on the triage board for James (card python-template-22-actions-create-prs).

Dependabot. Its monthly uv PR and this one both change uv.lock; after one merges, the other is rebased (Dependabot rebases its own; this workflow rebuilds its branch each month). Dependabot waits 7 days after a release before using it. uv lock --upgrade takes the newest release at the moment it runs. The cooldown cannot be added with --exclude-newer on the command line: uv records it in uv.lock, and then uv sync --locked reports the lock as out of date (tried with uv 0.12.23). Putting exclude-newer = "7 days" under [tool.uv] would work but is a template-wide change; the choice is on the triage board (card python-template-22-lock-upgrade-cooldown). The pull request is reviewed and tested before it merges either way.

Checked (python-template just test on a compute node: both bakes, docs_notebooks and publish_to_pypi yes and no; in each, quality-check, build-docs, every pre-commit hook including zizmor on the new workflow, dist, the version checks)

  • just test passed: "All checks passed for both variants." (the zizmor hook passed in both bakes)
  • zizmor 1.30.1 (--offline) and actionlint on the baked .github: no findings.
  • Both run steps, extracted from the baked workflow and run against a local bare repository with a stub gh: a project locked in January and upgraded today (changed: branch pushed, gh pr create, three gh workflow run; unchanged: stops with no output; an open pull request: gh pr edit instead of create; uv lock failing: the step fails). The uv output and the pull request body are as described above.

Not checked. The workflow has not run on GitHub: workflow_dispatch only works for a workflow file on a repository's default branch, and the template repository has no uv.lock. The first real run happens in a package after copier update (suggest fips first, then gh workflow run lock-upgrade.yml once the setting above is on). copier update was not run on any package. I did not test that GitHub shows the dispatched runs' results on the pull request (it attaches them to the head commit); check this on the first run.

Other open python-template PRs: #20 (template/AGENTS.md.jinja) and the doctests PR (#21: pyproject.toml.jinja, AGENTS.md.jinja, CONTRIBUTING.md.jinja, README.md, justfile). I merged this branch with each in a scratch test merge: no conflicts (README.md and CONTRIBUTING.md.jinja are edited in different places by the doctests PR).

…request

Dependabot's uv updates never move a package whose newest release needs a
newer Python than the lock's oldest (SciPy 1.18 and NumPy 2.5 need 3.12, the
lock covers 3.11), so CI tested a release users on a newer Python no longer
get. `uv lock --upgrade` locks the newest release for each Python (a fork in
uv.lock).

The Upgrade lock workflow runs on the 1st of each month and by hand, pushes
chore/lock-upgrade and opens or updates one pull request when uv.lock changes.
A pull request opened with the workflow's token does not start the other
workflows, so it starts Tests, Code Quality (now also workflow_dispatch) and
Documentation on the branch. The repository setting "Allow GitHub Actions to
create and approve pull requests" is needed once; the after-copy message,
README and CONTRIBUTING say so.
@jmineau
jmineau merged commit a1b280c into main Oct 22, 2026
1 check passed
@jmineau
jmineau deleted the feat/lock-upgrade-workflow branch October 22, 2026 19:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant