-
Notifications
You must be signed in to change notification settings - Fork 120
257 lines (226 loc) 路 9.46 KB
/
Copy pathrelease.yml
File metadata and controls
257 lines (226 loc) 路 9.46 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
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
name: Publish release
# Run manually from the Actions tab: Publish release -> Run workflow -> version (e.g. 3.8.1).
# It does what the release used to take by hand:
# 1. sets lithops/version.py to the version and renames the development heading of
# CHANGELOG.md to it, commits "Update version to X" and tags it X
# 2. builds the sdist and wheel and publishes them (trusted publishing, no token)
# 3. creates the GitHub release "Lithops-X" with the changelog section as notes
# 4. bumps the branch to the next development version (X.Y.Z+1.dev0)
# 5. at the end, publishes the docs to the GitHub Pages repository as "Update docs to X"
# (the Publish docs workflow, publish-docs.yml, which can also be run by hand to republish them)
# Commits go to the branch the workflow runs on, authored by whoever runs it.
# With "dry run" checked it only does the edits and the package build, and pushes or
# publishes nothing.
#
# In lithops-cloud/lithops it releases from master to PyPI. In a fork it publishes to
# TestPyPI instead, so the whole release can be tried out from a throwaway branch.
on:
workflow_dispatch:
inputs:
version:
description: "Version to release, e.g. 3.8.1"
required: true
dry_run:
description: "Dry run: build and check only, do not push, publish or release"
type: boolean
default: false
concurrency:
group: release
cancel-in-progress: false
jobs:
prepare:
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: write
outputs:
author_name: ${{ steps.author.outputs.name }}
author_email: ${{ steps.author.outputs.email }}
steps:
- name: Check the branch
if: ${{ github.repository == 'lithops-cloud/lithops' && github.ref_name != 'master' }}
run: |
echo "Releases of lithops-cloud/lithops are made from master, not ${{ github.ref_name }}"
exit 1
- name: Clone Lithops repository
uses: actions/checkout@v5
with:
fetch-depth: 0
- name: Install Python
uses: actions/setup-python@v6
with:
python-version: '3.12'
- name: Get the git identity of who runs the workflow
id: author
env:
GH_TOKEN: ${{ github.token }}
LOGIN: ${{ github.actor }}
LOGIN_ID: ${{ github.actor_id }}
run: |
# Their GitHub username, and the email of their latest commit in this repository,
# which is the one their own commits use. Someone who never committed here gets
# their GitHub noreply address
# (gh prints the error body on stdout when the call fails, so drop it then)
email=$(gh api "repos/$GITHUB_REPOSITORY/commits?author=$LOGIN&per_page=1" \
--jq '.[0].commit.author.email // empty' 2>/dev/null) || email=""
[ -n "$email" ] || email="$LOGIN_ID+$LOGIN@users.noreply.github.com"
echo "Committing as $LOGIN <$email>"
echo "name=$LOGIN" >> "$GITHUB_OUTPUT"
echo "email=$email" >> "$GITHUB_OUTPUT"
- name: Set the release version
env:
VERSION: ${{ inputs.version }}
run: |
python3 - <<'EOF'
import os, re, subprocess, sys
version = os.environ['VERSION'].strip()
if not re.fullmatch(r'\d+\.\d+\.\d+', version):
sys.exit(f'Version must look like 3.8.1, got {version!r}')
if subprocess.run(['git', 'rev-parse', '-q', '--verify', f'refs/tags/{version}'],
capture_output=True).returncode == 0:
sys.exit(f'Tag {version} already exists')
with open('lithops/version.py') as f:
source = f.read()
source, n = re.subn(r'__version__ = "[^"]+"', f'__version__ = "{version}"', source)
if n != 1:
sys.exit('Could not find __version__ in lithops/version.py')
with open('lithops/version.py', 'w') as f:
f.write(source)
with open('CHANGELOG.md') as f:
changelog = f.read()
heading = re.search(r'^## \[v?(\d+\.\d+\.\d+)\.dev\d*\][ \t]*$', changelog, re.M)
if not heading:
sys.exit('The first CHANGELOG.md section must be the development version, e.g. ## [v3.8.1.dev0]')
changelog = changelog[:heading.start()] + f'## [v{version}]' + changelog[heading.end():]
with open('CHANGELOG.md', 'w') as f:
f.write(changelog)
# the section of this release becomes the notes of the GitHub release
body = changelog[heading.start():].split('\n', 1)[1]
notes = re.split(r'^## \[', body, maxsplit=1, flags=re.M)[0].strip()
if not notes:
sys.exit('The CHANGELOG.md section of this release is empty')
with open('release-notes.md', 'w') as f:
f.write(notes + '\n')
EOF
git diff
- name: Build the sdist and wheel
run: |
pip3 install -U build twine
python3 -m build
twine check dist/*
ls -l dist
- name: Commit and tag the release
env:
VERSION: ${{ inputs.version }}
AUTHOR_NAME: ${{ steps.author.outputs.name }}
AUTHOR_EMAIL: ${{ steps.author.outputs.email }}
run: |
git config user.name "$AUTHOR_NAME"
git config user.email "$AUTHOR_EMAIL"
git commit -am "Update version to $VERSION"
git tag "$VERSION"
- name: Push the release commit and tag
if: ${{ !inputs.dry_run }}
env:
VERSION: ${{ inputs.version }}
run: git push --atomic origin "HEAD:$GITHUB_REF_NAME" "refs/tags/$VERSION"
- name: Keep the build for the next jobs
uses: actions/upload-artifact@v7
with:
name: release
path: |
dist/
release-notes.md
if-no-files-found: error
publish:
needs: prepare
if: ${{ !inputs.dry_run }}
runs-on: ubuntu-latest
timeout-minutes: 10
# Create this environment in the repository settings (optionally with required
# reviewers, to approve every upload) and register it as a trusted publisher:
# "pypi" on pypi.org for lithops-cloud/lithops, "testpypi" on test.pypi.org for a fork
environment:
name: ${{ github.repository == 'lithops-cloud/lithops' && 'pypi' || 'testpypi' }}
url: ${{ github.repository == 'lithops-cloud/lithops' && 'https://pypi.org' || 'https://test.pypi.org' }}/project/lithops/${{ inputs.version }}/
permissions:
id-token: write
steps:
- name: Get the build
uses: actions/download-artifact@v8
with:
name: release
- name: Publish to ${{ github.repository == 'lithops-cloud/lithops' && 'PyPI' || 'TestPyPI' }}
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository-url: ${{ github.repository == 'lithops-cloud/lithops' && 'https://upload.pypi.org/legacy/' || 'https://test.pypi.org/legacy/' }}
github-release:
needs: publish
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: write
steps:
- name: Get the build
uses: actions/download-artifact@v8
with:
name: release
- name: Create the GitHub release
env:
GH_TOKEN: ${{ github.token }}
VERSION: ${{ inputs.version }}
run: |
gh release create "$VERSION" dist/* \
--repo "$GITHUB_REPOSITORY" \
--title "Lithops-$VERSION" \
--notes-file release-notes.md
bump-dev:
needs: [prepare, publish]
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: write
steps:
- name: Clone Lithops repository
uses: actions/checkout@v5
- name: Set the next development version
env:
VERSION: ${{ inputs.version }}
run: |
# the checkout is the branch as it was when the workflow started; bring in the
# release commit pushed by the prepare job
git pull --ff-only origin "$GITHUB_REF_NAME"
python3 - <<'EOF'
import os, re
major, minor, patch = os.environ['VERSION'].strip().split('.')
dev = f'{major}.{minor}.{int(patch) + 1}.dev0'
with open('lithops/version.py') as f:
source = f.read()
with open('lithops/version.py', 'w') as f:
f.write(re.sub(r'__version__ = "[^"]+"', f'__version__ = "{dev}"', source))
with open('CHANGELOG.md') as f:
changelog = f.read()
first = re.search(r'^## \[', changelog, re.M)
changelog = changelog[:first.start()] + f'## [v{dev}]\n\n' + changelog[first.start():]
with open('CHANGELOG.md', 'w') as f:
f.write(changelog)
with open(os.environ['GITHUB_ENV'], 'a') as f:
f.write(f'DEV_VERSION={dev}\n')
EOF
- name: Commit and push
env:
AUTHOR_NAME: ${{ needs.prepare.outputs.author_name }}
AUTHOR_EMAIL: ${{ needs.prepare.outputs.author_email }}
run: |
git config user.name "$AUTHOR_NAME"
git config user.email "$AUTHOR_EMAIL"
git commit -am "Bump version to $DEV_VERSION"
git push origin "HEAD:$GITHUB_REF_NAME"
docs:
# last, once the release is out and master bumped (so a dry run, which skips those,
# does not publish the docs either)
needs: [github-release, bump-dev]
permissions:
contents: read
uses: ./.github/workflows/publish-docs.yml
secrets: inherit