PYTHON-5980 Add uv lock update action - #113
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a new composite GitHub Action to automate uv.lock upgrades in consumer repositories by maintaining a single “lock update” pull request that is created once and refreshed on subsequent runs, with documentation and CI coverage for the new logic.
Changes:
- Introduces
python/uv-lock-updatecomposite action plus supporting shell/Python scripts to upgradeuv.lockand create/update a PR viagh. - Adds bash-based tests for the lock diffing and PR create/update decision logic, and runs them in CI.
- Documents usage and operational behavior in
README.md.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| README.md | Documents the new python/uv-lock-update action and how to call it. |
| python/uv-lock-update/action.yml | Defines inputs/steps for generating an app token, upgrading uv.lock, and creating/updating the PR. |
| python/uv-lock-update/update_lock.sh | Generates a change summary, commits uv.lock, force-pushes the bot branch, and dispatches PR creation/update. |
| python/uv-lock-update/decide_pr_action.sh | Uses gh to decide whether to create a PR or edit the existing open PR for the branch (with dry-run support). |
| python/uv-lock-update/diff_lock.py | Diffs two uv.lock files and prints a markdown summary of package version changes. |
| python/uv-lock-update/test_diff_lock.sh | Tests the lock diffing behavior across additions/removals/forks/unchanged packages. |
| python/uv-lock-update/test_decide_pr_action.sh | Tests PR create vs update decisions using a fake gh shim and logged calls. |
| .github/workflows/ci.yml | Adds a test-python job to run the new bash tests in CI. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Adds a python/uv-lock-update composite action that runs uv lock --upgrade in a consuming repository and maintains a single open pull request with the result. The caller checks out the repository and supplies uv. A GitHub App token is required outside dry runs, so the pull request the action opens triggers CI. Later runs refresh that pull request rather than opening another. Also adds a test-python job to ci.yml, the first automated test coverage for the actions outside code-scanning-export.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 8 out of 8 changed files in this pull request and generated no new comments.
Suppressed comments (3)
python/uv-lock-update/update_lock.sh:39
- The push embeds
GH_TOKENdirectly in the remote URL. Ifgit pushfails, the full URL can be echoed in error output, and the token may be exposed (even if usually masked). Prefer authenticating via an HTTP header so the token is not part of the URL.
# Authenticate the push inline rather than with `git remote set-url`, so the
# token is never written to the workspace .git/config.
if [ "$DRY_RUN" != "true" ]; then
git push --force "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git" "$BRANCH"
fi
python/uv-lock-update/action.yml:68
GH_TOKENis set via${{ steps.app-token.outputs.token || github.token }}. Given the prior step explicitly rejects usinggithub.tokenfor non-dry runs, it’s safer (and clearer) to only usegithub.tokenwheninputs.dry_run == 'true'and otherwise require the app token output, so a future edit can’t accidentally fall back togithub.tokenfor live runs.
GH_TOKEN: ${{ steps.app-token.outputs.token || github.token }}
README.md:509
- This action shells out to
gh pr ...(seepython/uv-lock-update/decide_pr_action.sh), so it also requires the GitHub CLI (gh) to be available onPATH. The README section currently only mentionsuv.
The caller checks out the repository and puts `uv` on `PATH`. The cooldown on new
releases comes from `exclude-newer` in the consuming repo's `pyproject.toml`, not
from this action.
|
Both token findings addressed in 49e2f16. Token in the push URL. Credentials now come from a helper that reads Implicit |
There was a problem hiding this comment.
LGTM with optional nits!
- In
decide_pr_action.sh:14we could add--ownerto avoid the extremely unlikely scenario that someone else's PR is found before ours. - We could add a
working_directoryinput and putworking-directory: ${{ inputs.working_directory }}on each run step in action.yml for clarity - Replace
git diff --quietwithif cmp -s "$OLD_LOCK" uv.lock; then…because otherwise we have different baselines for "did anything change"- action.yml:60 copies uv.lock to $OLD_LOCK before running the upgrade — that's the working-tree content at action start.
- update_lock.sh:9 runs git diff --quiet uv.lock — that compares working tree against the index/HEAD.
|
I addressed the first and last, I think we can defer working-directory for if/when we need monorepo support. |
Adds a
python/uv-lock-updatecomposite action that refreshes a consuming repo'suv.lockand keeps a single open pull request with the result.For repos that commit
uv.lockand want routine dependency upgrades handled on a schedule rather than as individual Dependabot pull requests. The first consumer will be mongodb/mongo-python-driver#2977 (PYTHON-5980).The caller checks out the repository and supplies
uv. A GitHub App token is required outside dry runs. Later runs refresh the open pull request instead of opening another.Also adds a
test-pythonjob toci.yml. Usage is documented in the README.Validation
Include 23 unit assertions that run in CI.
Exercised end to end tests against my fork of mongo-python-driver, with a real GitHub App and an out-of-date lock file:
uv.lock. CI ran on it on the PR, as expected.