Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add CI workflows to streamline release process
- Loading branch information
1 parent
cf17939
commit 1f780b4
Showing
6 changed files
with
273 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
jobs: | ||
prepare_release: | ||
name: Prepare Release v${{ github.event.inputs.version }} | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v2 | ||
with: | ||
ssh-key: ${{ secrets.SSH_DEPLOY_KEY }} | ||
- uses: actions/setup-python@v2 | ||
with: | ||
python-version: 3.x | ||
- name: Install dependencies | ||
run: pip install packaging | ||
- name: Prepare Git Variables | ||
run: | | ||
git config --global author.email ${{ github.actor }}@users.noreply.github.com | ||
git config --global author.name ${{ github.actor }} | ||
git config --global committer.email noreply@github.com | ||
git config --global committer.name GitHub | ||
- name: Set desired version | ||
run: | | ||
tools/set_version.py ${{ github.event.inputs.version }} > tmp_version | ||
echo "version=$(cat tmp_version)" >> $GITHUB_ENV | ||
- name: Commit desired version | ||
run: git commit -am "Bump to v${{ env.version }}" | ||
- name: Set development version | ||
run: | | ||
tools/set_version.py Unreleased > tmp_version | ||
echo "dev_version=$(cat tmp_version)" >> $GITHUB_ENV | ||
rm tmp_version | ||
- name: Commit development version | ||
run: git commit -am "Set development version v${{ env.dev_version }}" | ||
- name: Create Pull Request | ||
uses: peter-evans/create-pull-request@v3 | ||
with: | ||
body: | ||
branch: prepare_release_v${{ env.version }} | ||
draft: false | ||
title: Release v${{ env.version }} | ||
|
||
name: Prepare Release | ||
on: | ||
workflow_dispatch: | ||
inputs: | ||
version: | ||
description: 'The version to prepare for release' | ||
required: true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,28 +1,24 @@ | ||
name: Upload Python Package | ||
|
||
on: | ||
release: | ||
types: [created] | ||
|
||
jobs: | ||
deploy: | ||
|
||
release: | ||
name: Build and release | ||
runs-on: ubuntu-latest | ||
|
||
steps: | ||
- uses: actions/checkout@v2 | ||
- name: Set up Python | ||
uses: actions/setup-python@v2 | ||
- uses: actions/setup-python@v2 | ||
with: | ||
python-version: '3.x' | ||
- name: Install dependencies | ||
run: | | ||
python -m pip install --upgrade pip | ||
pip install setuptools wheel twine | ||
- name: Build and publish | ||
env: | ||
- env: | ||
TWINE_USERNAME: __token__ | ||
TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }} | ||
TWINE_PASSWORD: ${{ secrets.PYPI_TOKEN }} | ||
name: Build and release | ||
run: | | ||
python setup.py sdist bdist_wheel | ||
twine upload dist/* | ||
name: Upload Python Package | ||
on: | ||
release: | ||
types: [published] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
jobs: | ||
release_tag: | ||
if: "startsWith(github.event.head_commit.message, 'Merge pull request #') && contains(github.event.head_commit.message, ' from praw-dev/prepare_release_v')" | ||
name: Tag Release | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v2 | ||
with: | ||
fetch-depth: 3 | ||
ssh-key: ${{ secrets.SSH_DEPLOY_KEY }} | ||
- uses: actions/setup-python@v2 | ||
with: | ||
python-version: 3.x | ||
- name: Install dependencies | ||
run: pip install packaging docutils | ||
- name: Extract Version | ||
run: | | ||
git checkout HEAD^2^ | ||
echo "commit=$(git rev-parse HEAD)" >> $GITHUB_ENV | ||
git log --format=%B -n 1 | ./tools/bump_version.py > tmp_version | ||
echo "version=$(cat tmp_version)" >> $GITHUB_ENV | ||
cat tmp_version | python -c 'import sys; from packaging import version; print(int(version.Version(sys.stdin.readline()).is_prerelease))' > tmp_is_prerelease | ||
echo "is_prerelease=$(cat tmp_is_prerelease)" >> $GITHUB_ENV | ||
- name: Extract Change Log | ||
run: | | ||
echo ${{ env.version }} | ./tools/extract_log_entry.py > version_changelog | ||
- env: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
name: Create GitHub Release | ||
uses: actions/create-release@v1 | ||
with: | ||
body_path: version_changelog | ||
commitish: ${{ env.commit }} | ||
draft: true | ||
prerelease: ${{ env.is_prerelease == '1' }} | ||
release_name: v${{ env.version }} | ||
tag_name: v${{ env.version }} | ||
name: Tag Release | ||
on: | ||
push: | ||
branches: | ||
- master | ||
- release_test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
#!/usr/bin/env python3 | ||
import sys | ||
|
||
COMMIT_PREFIX = "Bump to v" | ||
|
||
|
||
def main(): | ||
line = sys.stdin.readline() | ||
if not line.startswith(COMMIT_PREFIX): | ||
sys.stderr.write( | ||
f"Commit message does not begin with `{COMMIT_PREFIX}`.\nMessage:\n\n{line}" | ||
) | ||
return 1 | ||
print(line[len(COMMIT_PREFIX) : -1]) | ||
|
||
|
||
if __name__ == "__main__": | ||
sys.exit(main()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
#!/usr/bin/env python3 | ||
import sys | ||
|
||
import docutils.nodes | ||
import docutils.parsers.rst | ||
import docutils.utils | ||
|
||
|
||
def get_entry_slice(doc): | ||
current_version = sys.stdin.readline().strip() | ||
start_line = None | ||
end_line = None | ||
for section in doc.children[0].children: | ||
if start_line: | ||
end_line = section.children[0].line - 2 | ||
break | ||
header = section.children[0] | ||
if current_version in header.rawsource: | ||
start_line = header.line - 2 | ||
return slice(start_line, end_line) | ||
|
||
|
||
def parse_rst(text: str) -> docutils.nodes.document: | ||
parser = docutils.parsers.rst.Parser() | ||
components = (docutils.parsers.rst.Parser,) | ||
settings = docutils.frontend.OptionParser( | ||
components=components | ||
).get_default_values() | ||
settings.report_level = 4 | ||
document = docutils.utils.new_document("<rst-doc>", settings=settings) | ||
parser.parse(text, document) | ||
return document | ||
|
||
|
||
with open("CHANGES.rst") as f: | ||
source = f.read() | ||
document = parse_rst(source) | ||
|
||
sys.stdout.write("\n".join(source.splitlines()[get_entry_slice(document)])) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
#!/usr/bin/env python3 | ||
import re | ||
import sys | ||
from datetime import date | ||
|
||
import packaging.version | ||
|
||
CHANGELOG_HEADER = "Change Log\n==========\n\n" | ||
UNRELEASED_HEADER = "Unreleased\n----------\n\n" | ||
|
||
|
||
def add_unreleased_to_changelog(): | ||
with open("CHANGES.rst") as fp: | ||
content = fp.read() | ||
|
||
if not content.startswith(CHANGELOG_HEADER): | ||
sys.stderr.write("Unexpected CHANGES.rst header\n") | ||
return False | ||
new_header = f"{CHANGELOG_HEADER}{UNRELEASED_HEADER}" | ||
if content.startswith(new_header): | ||
sys.stderr.write("CHANGES.rst already contains Unreleased header\n") | ||
return False | ||
|
||
with open("CHANGES.rst", "w") as fp: | ||
fp.write(f"{new_header}{content[len(CHANGELOG_HEADER):]}") | ||
return True | ||
|
||
|
||
def handle_unreleased(): | ||
return add_unreleased_to_changelog() and increment_development_version() | ||
|
||
|
||
def handle_version(version): | ||
version = valid_version(version) | ||
if not version: | ||
return False | ||
return update_changelog(version) and update_package(version) | ||
|
||
|
||
def increment_development_version(): | ||
with open("asyncpraw/const.py") as fp: | ||
version = re.search('__version__ = "([^"]+)"', fp.read()).group(1) | ||
|
||
parsed_version = valid_version(version) | ||
if not parsed_version: | ||
return False | ||
|
||
if parsed_version.is_devrelease: | ||
pre = "".join(str(x) for x in parsed_version.pre) if parsed_version.pre else "" | ||
new_version = f"{parsed_version.base_version}{pre}.dev{parsed_version.dev + 1}" | ||
elif parsed_version.is_prerelease: | ||
new_version = f"{parsed_version}.dev0" | ||
else: | ||
assert parsed_version.base_version == version | ||
new_version = f"{parsed_version.major}.{parsed_version.minor}.{parsed_version.micro + 1}.dev0" | ||
|
||
assert valid_version(new_version) | ||
return update_package(new_version) | ||
|
||
|
||
def main(): | ||
if len(sys.argv) != 2: | ||
sys.stderr.write(f"Usage: {sys.argv[0]} VERSION\n") | ||
return 1 | ||
if sys.argv[1] == "Unreleased": | ||
return not handle_unreleased() | ||
return not handle_version(sys.argv[1]) | ||
|
||
|
||
def update_changelog(version): | ||
with open("CHANGES.rst") as fp: | ||
content = fp.read() | ||
|
||
expected_header = f"{CHANGELOG_HEADER}{UNRELEASED_HEADER}" | ||
if not content.startswith(expected_header): | ||
sys.stderr.write("CHANGES.rst does not contain Unreleased header.\n") | ||
return False | ||
|
||
date_string = date.today().strftime("%Y/%m/%d") | ||
version_line = f"{version} ({date_string})\n" | ||
version_header = f"{version_line}{'-' * len(version_line[:-1])}\n\n" | ||
|
||
with open("CHANGES.rst", "w") as fp: | ||
fp.write(f"{CHANGELOG_HEADER}{version_header}{content[len(expected_header):]}") | ||
return True | ||
|
||
|
||
def update_package(version): | ||
with open("asyncpraw/const.py") as fp: | ||
content = fp.read() | ||
|
||
updated = re.sub('__version__ = "([^"]+)"', f'__version__ = "{version}"', content) | ||
if content == updated: | ||
sys.stderr.write("Package version string not changed\n") | ||
return False | ||
|
||
with open("asyncpraw/const.py", "w") as fp: | ||
fp.write(updated) | ||
|
||
print(version) | ||
return True | ||
|
||
|
||
def valid_version(version): | ||
parsed_version = packaging.version.parse(version) | ||
if isinstance(parsed_version, packaging.version.LegacyVersion): | ||
sys.stderr.write(f"Invalid PEP 440 version: {version}\n") | ||
return False | ||
if parsed_version.local or parsed_version.is_postrelease or parsed_version.epoch: | ||
sys.stderr.write("epoch, local postrelease version parts are not supported") | ||
return False | ||
return parsed_version | ||
|
||
|
||
if __name__ == "__main__": | ||
sys.exit(main()) |