Skip to content

Commit 2f9d1ac

Browse files
committed
Add create-empty-github-release entrypoint; fix latest-release flag
The new create-empty-github-release entrypoint lets a release workflow create the GitHub release at the version tag early, so release artefacts can be attached to it as they are built across per-platform jobs. The release is created with an empty body and is explicitly not marked as the latest release; a later publish-release-notes-to-github call fills in the body. If a release for the tag already exists, the entrypoint is a no-op so that restart paths do not clobber partial progress. publish-release-notes-to-github now sets make_latest=true when creating or updating the release. Without this, GitHub's "releases/latest" URL did not correspond to the most recent release for repos using this action — an oversight that becomes load-bearing as consumers (starting with ponyup) begin pulling binaries from GitHub Releases. The script also gains GithubException handling around update_release and create_git_release so API failures exit with a clear message instead of a traceback. PyGithub is bumped to 2.9.0 because make_latest was added in 2.1.0. The bump also deprecates the positional Github(token) constructor, so both existing PyGithub-using scripts are updated to the Auth.Token form to avoid DeprecationWarning noise in release logs. Because PyGithub's create_git_release defaults make_latest to "true" (not NotSet), the new entrypoint must pass make_latest="false" explicitly to keep the empty release from being marked latest. Design: ponylang/ponyup#406
1 parent 8a845c1 commit 2f9d1ac

5 files changed

Lines changed: 121 additions & 10 deletions

File tree

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ RUN apt-get update \
1111

1212
RUN pip3 install --break-system-packages\
1313
gitpython \
14-
pygithub==1.55 \
14+
pygithub==2.9.0 \
1515
pylint \
1616
pyyaml \
1717
zulip

README.md

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,12 +152,16 @@ jobs:
152152
153153
### Release
154154
155-
The meat of the release process. This is the workflow that builds our actual release artefacts, updates documentation and whatever else. Release-bot only provides two commands to be used in this workflow.
155+
The meat of the release process. This is the workflow that builds our actual release artefacts, updates documentation and whatever else. Release-bot provides three commands to be used in this workflow.
156156
157157
- pre-artefact-changelog-check
158158
159159
Can be used to verify that the release workflow wasn't "accidentally" triggered.
160160
161+
- create-empty-github-release
162+
163+
Creates an empty GitHub release for the version tag so that release artefacts can be attached to it as they are built. Not required unless you need a GitHub release for artefact attachment.
164+
161165
- trigger-release-announcement
162166
163167
Is used to start the `announce-a-release` workflow and is meant to be run after all artefact building has completed.
@@ -356,6 +360,30 @@ An example step config:
356360
GIT_USER_EMAIL: "ponylang.main@gmail.com"
357361
```
358362

363+
### create-empty-github-release
364+
365+
Creates an empty GitHub release for the tag being released. The release is created with an empty body, not marked as a draft, and not marked as the latest release. The release body and the "latest" flag can be set later by `publish-release-notes-to-github`.
366+
367+
This command exists so that release artefacts can be attached to the GitHub release as they are built during the `release` workflow. It should be run as part of the `pre-artefact-creation` job, after `pre-artefact-changelog-check`.
368+
369+
If a release for the tag already exists, `create-empty-github-release` does nothing. This means the command is safe to re-run if the `release` workflow has to be restarted.
370+
371+
- **Must** be triggered by an `X.Y.Z` tag push.
372+
- **Should** be run after `pre-artefact-changelog-check` and before any artefact building steps.
373+
374+
`create-empty-github-release` requires a GitHub personal access token with `public_repo` access. The personal access token needs to be passed in the environment variable `RELEASE_TOKEN`.
375+
376+
An example step config:
377+
378+
```yml
379+
- name: Create empty GitHub release
380+
uses: docker://ghcr.io/ponylang/release-bot-action:0.6.5
381+
with:
382+
entrypoint: create-empty-github-release
383+
env:
384+
RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }}
385+
```
386+
359387
### delete-announcement-tag
360388

361389
Deletes the `announce-X.Y.Z` tag that is used to kick off various release announcement commands.

scripts/add-announcement-to-last-week-in-pony

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import os
66
import re
77
import sys
8+
from github import Auth
89
from github import Github
910

1011
ENDC = '\033[0m'
@@ -25,14 +26,14 @@ if 'GITHUB_REPOSITORY' not in os.environ:
2526
print(ERROR + "GITHUB_REPOSITORY needs to be set in env. Exiting." + ENDC)
2627
sys.exit(1)
2728

28-
# version is in the form of "refs/tags/release-1.0.0" where the version is 1.0.0
29+
# version is in the form of "refs/tags/announce-1.0.0" where the version is 1.0.0
2930
version = re.sub('refs/tags/announce-', '', os.environ['GITHUB_REF'])
3031

3132
release_repo = os.environ['GITHUB_REPOSITORY']
3233

3334
print(INFO + "Adding release to Last Week in Pony..." + ENDC)
3435

35-
g = Github(os.environ['RELEASE_TOKEN'])
36+
g = Github(auth=Auth.Token(os.environ['RELEASE_TOKEN']))
3637
repo = g.get_repo('ponylang/ponylang-website')
3738
results = repo.get_issues(labels=['last-week-in-pony'])
3839
if results.totalCount == 0:
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#!/usr/bin/python3
2+
# pylint: disable=C0103
3+
# pylint: disable=C0114
4+
5+
import os
6+
import re
7+
import sys
8+
from github import Auth
9+
from github import Github
10+
from github import GithubException
11+
from github import UnknownObjectException
12+
13+
ENDC = '\033[0m'
14+
ERROR = '\033[31m'
15+
INFO = '\033[34m'
16+
NOTICE = '\033[33m'
17+
18+
# validate env
19+
if 'RELEASE_TOKEN' not in os.environ:
20+
print(ERROR + "RELEASE_TOKEN needs to be set in env. Exiting." + ENDC)
21+
sys.exit(1)
22+
23+
if 'GITHUB_REF' not in os.environ:
24+
print(ERROR + "GITHUB_REF needs to be set in env. Exiting." + ENDC)
25+
sys.exit(1)
26+
27+
if 'GITHUB_REPOSITORY' not in os.environ:
28+
print(ERROR + "GITHUB_REPOSITORY needs to be set in env. Exiting." + ENDC)
29+
sys.exit(1)
30+
31+
# version is in the form of "refs/tags/1.0.0" where the version is 1.0.0
32+
version = re.sub('refs/tags/', '', os.environ['GITHUB_REF'])
33+
34+
g = Github(auth=Auth.Token(os.environ['RELEASE_TOKEN']))
35+
repo = g.get_repo(os.environ['GITHUB_REPOSITORY'])
36+
37+
# If the release already exists, leave it alone. A prior run may have
38+
# uploaded assets, populated the body, or set the "latest" flag; modifying
39+
# any of that here would clobber recovery paths. Only create the release
40+
# when it's missing.
41+
print(INFO + "Checking for existing GitHub release for " + version + "..."
42+
+ ENDC)
43+
try:
44+
repo.get_release(version)
45+
print(INFO + "Release already exists. Not modifying." + ENDC)
46+
sys.exit(0)
47+
except UnknownObjectException:
48+
print(INFO + "Release does not exist. Creating empty release." + ENDC)
49+
except GithubException as e:
50+
print(ERROR + f"GitHub API error: {e.status} {e.data}" + ENDC,
51+
file=sys.stderr)
52+
sys.exit(1)
53+
54+
# Explicitly set make_latest="false" so the empty-body release is not
55+
# marked as the "latest" release. The "latest" flag is deferred until
56+
# publish-release-notes-to-github runs. PyGithub's create_git_release
57+
# defaults make_latest to "true", so the argument must be supplied.
58+
try:
59+
repo.create_git_release(tag=version, name=version, message="",
60+
draft=False, prerelease=False,
61+
make_latest="false")
62+
print(INFO + "Empty release created." + ENDC)
63+
except GithubException as e:
64+
print(ERROR + f"GitHub API error: {e.status} {e.data}" + ENDC,
65+
file=sys.stderr)
66+
sys.exit(1)

scripts/publish-release-notes-to-github

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ from pathlib import Path
77
import re
88
import subprocess
99
import sys
10+
from github import Auth
1011
from github import Github
12+
from github import GithubException
1113
from github import UnknownObjectException
1214

1315
ENDC = '\033[0m'
@@ -28,7 +30,7 @@ if 'GITHUB_REPOSITORY' not in os.environ:
2830
print(ERROR + "GITHUB_REPOSITORY needs to be set in env. Exiting." + ENDC)
2931
sys.exit(1)
3032

31-
# version is in the form of "refs/tags/release-1.0.0" where the version is 1.0.0
33+
# version is in the form of "refs/tags/announce-1.0.0" where the version is 1.0.0
3234
version = re.sub('refs/tags/announce-', '', os.environ['GITHUB_REF'])
3335

3436
release_repo = os.environ['GITHUB_REPOSITORY']
@@ -62,16 +64,30 @@ else:
6264
print(INFO + "No CHANGELOG.md found." + ENDC)
6365

6466

65-
g = Github(os.environ['RELEASE_TOKEN'])
67+
g = Github(auth=Auth.Token(os.environ['RELEASE_TOKEN']))
6668
repo = g.get_repo(os.environ['GITHUB_REPOSITORY'])
6769

6870
print(INFO + "Uploading release notes..." + ENDC)
6971
# check to see if the release already exists
72+
ghrelease = None
7073
try:
7174
ghrelease = repo.get_release(version)
72-
print(INFO + "Release already exists. Updating release notes." + ENDC)
73-
ghrelease.update_release(name=version, message=release_notes)
7475
except UnknownObjectException:
75-
print(INFO + "Release does not exist. Creating release." + ENDC)
76-
repo.create_git_release(version, version, release_notes)
76+
pass
77+
except GithubException as e:
78+
print(ERROR + f"GitHub API error: {e.status} {e.data}" + ENDC)
79+
sys.exit(1)
80+
81+
try:
82+
if ghrelease is not None:
83+
print(INFO + "Release already exists. Updating release notes." + ENDC)
84+
ghrelease.update_release(name=version, message=release_notes,
85+
make_latest="true")
86+
else:
87+
print(INFO + "Release does not exist. Creating release." + ENDC)
88+
repo.create_git_release(version, version, release_notes,
89+
make_latest="true")
90+
except GithubException as e:
91+
print(ERROR + f"GitHub API error: {e.status} {e.data}" + ENDC)
92+
sys.exit(1)
7793
print(INFO + "Release notes uploaded." + ENDC)

0 commit comments

Comments
 (0)