Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 104 additions & 0 deletions .github/workflows/update-uv-lock.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
name: Refresh uv.lock

# Dependabot's uv ecosystem only updates dependencies declared in
# pyproject.toml; a transitive dependency that exists solely in uv.lock is
# never bumped, so its advisories pile up as alerts with no pull request
# attached (dependabot/dependabot-core#16449). This job re-resolves the
# lockfile weekly to pick those up. Direct dependencies are all `==` pinned,
# so `uv lock --upgrade` can only move transitive versions -- Dependabot
# still owns every direct bump.

on:
schedule:
- cron: '0 0 * * 1' # Runs at 00:00 UTC every Monday
workflow_dispatch:

permissions:
contents: write
pull-requests: write

env:
PYTHON_VERSION: "3.12"

jobs:
refresh-uv-lock:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v7
with:
token: ${{ secrets.BOT_PAT }}

- name: Set up Python ${{ env.PYTHON_VERSION }}
uses: actions/setup-python@v7
with:
python-version: ${{ env.PYTHON_VERSION }}

- name: Install uv
run: |
python -m pip install --upgrade pip
python -m pip install uv

- name: Refresh uv.lock
id: refresh
run: |
# uv reports each version change as "Updated <pkg> vA -> vB" on
# stderr. Capture it so the pull request can say what moved rather
# than only that the lockfile changed.
UV_OUTPUT="$(mktemp)"
trap 'rm -f "$UV_OUTPUT"' EXIT

# Redirect rather than pipe to tee: a pipeline would mask a
# non-zero exit from uv itself.
uv lock --upgrade >"$UV_OUTPUT" 2>&1 || {
status=$?
cat "$UV_OUTPUT"
echo "::error::uv lock --upgrade failed"
exit "$status"
}
cat "$UV_OUTPUT"

if git diff --quiet -- uv.lock; then
echo "changes_detected=false" >> "$GITHUB_OUTPUT"
exit 0
fi
echo "changes_detected=true" >> "$GITHUB_OUTPUT"

# Assign before writing: `grep | sed` exits 0 even when grep
# matches nothing, so a `||` fallback on the pipeline never fires.
CHANGES="$(grep -E '^(Updated|Added|Removed) ' "$UV_OUTPUT" | sed 's/^/- /')"
if [[ -z "$CHANGES" ]]; then
CHANGES="- (uv reported no per-package changes)"
fi

{
echo "summary<<UV_LOCK_DIFF_EOF"
echo "$CHANGES"
echo "UV_LOCK_DIFF_EOF"
} >> "$GITHUB_OUTPUT"

- name: Create PR
if: steps.refresh.outputs.changes_detected == 'true'
uses: peter-evans/create-pull-request@v8
with:
token: ${{ secrets.BOT_PAT }}
commit-message: "Automated: Refresh uv.lock"
title: "Automated: Refresh uv.lock"
body: |
Re-resolve uv.lock to pick up transitive dependency updates.

${{ steps.refresh.outputs.summary }}

Direct dependencies are `==` pinned in pyproject.toml, so only
transitive versions can move here; Dependabot still opens the
pull requests for direct bumps. This job exists because
Dependabot cannot update a transitive dependency that appears
only in uv.lock (dependabot/dependabot-core#16449).

This PR was automatically generated by a GitHub Action.
push-to-fork: dknowles-bot/pytboss
base: main
branch: uv-lock-${{ github.run_number }}
delete-branch: true
labels: dependencies
4 changes: 4 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,8 @@ test = [
dev = [
{include-group = "test"},
"pre-commit==4.6.2",
# Transitive dependency of pre-commit, pinned here only so Dependabot
# tracks it as a direct dependency: it cannot bump transitive packages
# that exist only in uv.lock. See dependabot/dependabot-core#16449.
"virtualenv==21.14.5",
]
16 changes: 9 additions & 7 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading