-
Notifications
You must be signed in to change notification settings - Fork 12
201 lines (182 loc) · 7.49 KB
/
Copy pathupdate-python-lock.yml
File metadata and controls
201 lines (182 loc) · 7.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
name: Update Python Lock
on:
schedule:
# Run at 6:30 UTC every Monday, separately from Wednesday Dependabot.
- cron: "30 6 * * Mon"
workflow_dispatch:
inputs:
dry_run:
description: "Resolve updates without creating a branch or PR"
type: boolean
required: false
default: false
permissions:
contents: write
pull-requests: write
# Required by github-script for issues.addLabels/issues.createComment on PRs.
issues: write
# Only one lock refresh at a time; a scheduled and a manual run should not race.
concurrency:
group: update-python-lock
cancel-in-progress: false
jobs:
update-python-lock:
runs-on: ubuntu-latest
if: github.repository == 'streamlit/st-issues'
steps:
- name: Checkout main
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: main
fetch-depth: 0
persist-credentials: true
- name: Set up uv
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
enable-cache: true
- name: Upgrade Python lock
id: upgrade-lock
run: |
set -euo pipefail
upgrade_log="${RUNNER_TEMP}/uv-lock-upgrade.log"
# uv prints Update/Add/Remove lines on stderr; capture both streams.
uv lock --upgrade 2>&1 | tee "$upgrade_log"
{
# Use a unique delimiter so a bare "EOF" line in uv's log cannot
# close the multiline GITHUB_OUTPUT block early.
echo "upgrade_log<<EOF_UPGRADE_LOG"
# Emit Update/Add/Remove lines as markdown bullets for the PR
# body / summary; skip resolution chatter like "Resolved N packages".
while IFS= read -r line || [[ -n "$line" ]]; do
[[ -z "$line" ]] && continue
# Match Updated/Added/Removed (and Update/Add/Remove) prefixes.
[[ "$line" =~ ^(Update|Add|Remove) ]] || continue
echo "- ${line}"
done < "$upgrade_log"
echo "EOF_UPGRADE_LOG"
} >> "$GITHUB_OUTPUT"
- name: Check whether uv.lock changed
id: check-lock
run: |
if [[ -n "$(git status --porcelain -- uv.lock)" ]]; then
echo "lock_changed=true" >> "$GITHUB_OUTPUT"
else
echo "lock_changed=false" >> "$GITHUB_OUTPUT"
fi
- name: Configure Git
if: ${{ steps.check-lock.outputs.lock_changed == 'true' && inputs.dry_run != true }}
run: |
git config user.name "Streamlit Bot"
git config user.email "core+streamlitbot-github@streamlit.io"
- name: Create update branch
id: create-branch
if: ${{ steps.check-lock.outputs.lock_changed == 'true' && inputs.dry_run != true }}
run: |
branch_name="update-python-lock-$(date -u +%Y-%m-%d)-${GITHUB_RUN_ID}"
echo "branch_name=$branch_name" >> "$GITHUB_OUTPUT"
git checkout -b "$branch_name"
- name: Commit and push lock update
if: ${{ steps.check-lock.outputs.lock_changed == 'true' && inputs.dry_run != true }}
env:
BRANCH_NAME: ${{ steps.create-branch.outputs.branch_name }}
run: |
git add uv.lock
git commit -m "[chore] Update Python lock"
git push --set-upstream origin "$BRANCH_NAME"
- name: Close superseded lock-update PRs
if: ${{ steps.check-lock.outputs.lock_changed == 'true' && inputs.dry_run != true }}
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
NEW_BRANCH: ${{ steps.create-branch.outputs.branch_name }}
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
// Close any still-open lock-update PRs so only the latest one accumulates.
const repoFullName = `${context.repo.owner}/${context.repo.repo}`;
const openPrs = await github.paginate(github.rest.pulls.list, {
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
base: 'main',
per_page: 100,
});
for (const pr of openPrs) {
// Restrict to our own automation branches; a fork PR could share
// the update-python-lock- prefix but must never be closed here.
if (
pr.head.ref !== process.env.NEW_BRANCH &&
pr.head.ref.startsWith('update-python-lock-') &&
pr.head.repo?.full_name === repoFullName
) {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: pr.number,
body: 'Superseded by a newer automated Python lock update.',
});
await github.rest.pulls.update({
owner: context.repo.owner,
repo: context.repo.repo,
pull_number: pr.number,
state: 'closed',
});
}
}
- name: Create pull request
id: create-pr
if: ${{ steps.check-lock.outputs.lock_changed == 'true' && inputs.dry_run != true }}
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
BRANCH_NAME: ${{ steps.create-branch.outputs.branch_name }}
UPGRADE_LOG: ${{ steps.upgrade-lock.outputs.upgrade_log }}
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const upgradeLog = (process.env.UPGRADE_LOG || '').trim();
const packageUpdatesSection = [
'## Package updates',
'',
upgradeLog || '- No package updates reported by uv.',
].join('\n');
const created = await github.rest.pulls.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: '[chore] Update Python lock',
head: process.env.BRANCH_NAME,
base: 'main',
body: [
'Automated weekly refresh of all direct and transitive Python dependencies in `uv.lock`.',
'',
packageUpdatesSection,
].join('\n'),
draft: false,
});
core.setOutput('pr_url', created.data.html_url);
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: created.data.number,
labels: ['dependencies', 'python'],
});
- name: Write workflow summary
env:
DRY_RUN: ${{ inputs.dry_run }}
LOCK_CHANGED: ${{ steps.check-lock.outputs.lock_changed }}
PR_URL: ${{ steps.create-pr.outputs.pr_url }}
UPGRADE_LOG: ${{ steps.upgrade-lock.outputs.upgrade_log }}
run: |
{
echo "## Python lock update summary"
echo "- uv.lock changed: ${LOCK_CHANGED}"
if [[ "$DRY_RUN" == "true" ]]; then
echo "- Dry run: true (no branch or PR created)"
elif [[ "$LOCK_CHANGED" == "true" ]]; then
echo "- PR: ${PR_URL:-n/a}"
else
echo "- No compatible updates found; no PR created"
fi
echo
echo "### Package updates"
echo
echo "${UPGRADE_LOG:-- No package updates reported by uv.}"
} >> "$GITHUB_STEP_SUMMARY"