Skip to content

Commit

Permalink
feat: Setup release process (#685)
Browse files Browse the repository at this point in the history
This should do the following:

1. Use `release-drafter` to maintain a draft of a `v###` release.
2. Build wheels with a version based on either the current state of the
file or using the version from the tag.
3. When we publish that release, it should publish to PyPi.
  • Loading branch information
bjchambers authored Aug 21, 2023
1 parent 03becc8 commit 0956cfa
Show file tree
Hide file tree
Showing 7 changed files with 210 additions and 53 deletions.
9 changes: 9 additions & 0 deletions .github/release-drafter-python-client.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
_extends: kaskada:.github/release-drafter.yml

name-template: Python $RESOLVED_VERSION
tag-template: python@v$RESOLVED_VERSION
tag-prefix: python@v

# Only include PRs with one of these labels
include-labels:
- python
10 changes: 3 additions & 7 deletions .github/release-drafter-python.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
_extends: kaskada:.github/release-drafter.yml

name-template: Python $RESOLVED_VERSION
tag-template: python@v$RESOLVED_VERSION
tag-prefix: python@v

# Only include PRs with one of these labels
include-labels:
- python
name-template: Kaskada $RESOLVED_VERSION-a.0
tag-template: v$RESOLVED_VERSION-a.0
tag-prefix: v
136 changes: 107 additions & 29 deletions .github/workflows/ci_python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ on:
merge_group:
branches:
- main
release:
types:
- published

defaults:
run:
Expand All @@ -43,6 +46,7 @@ jobs:
#
# Also, only do it on one machine.
lint:
if: github.event_name == 'pull_request' || github.event_name == 'merge_group'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
Expand Down Expand Up @@ -75,6 +79,10 @@ jobs:
poetry run pydocstyle pysrc
debug:
if: |
github.event_name == 'pull_request' ||
github.event_name == 'merge_group' ||
(github.event_name == 'push' && github.ref == 'refs/heads/main')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
Expand Down Expand Up @@ -135,11 +143,42 @@ jobs:
# Automatically uploads an artifact from the './_site' directory by default
path: ${{ github.workspace }}/python/docs/_build

version:
# Build wheels in the merge queue (to verify they build) and during release.
if: |
github.event_name == 'merge_group' ||
github.event_name == 'release'
runs-on: ubuntu-latest
outputs:
version: ${{ steps.set-version.outputs.version }}
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v4
with:
python-version: 3.11
- name: Determine version (merge)
run: |
pip install tomlkit
VERSION=$(python ../scripts/get_version.py path_to_toml_file.toml path.within.toml)
echo "VERSION=$VERSION" >> $GITHUB_ENV
- name: Determine version (release)
run: |
VERSION=${GITHUB_REF#refs/tags/v}
echo "VERSION=$VERSION" >> $GITHUB_ENV
- name: Set version
id: set-version
run: |
echo VERSION=$VERSION
echo "::set-output name=version::$VERSION"
build-wheel-macos:
# Build wheels in the merge queue. We can either duplicate this in `main`
# or retrieve the built wheels.
if: github.event_name == 'merge_group'
# Build wheels in the merge queue (to verify they build) and during release.
if: |
github.event_name == 'merge_group' ||
github.event_name == 'release'
runs-on: macos-latest
needs: [version]
strategy:
matrix:
include:
Expand All @@ -165,6 +204,14 @@ jobs:
uses: arduino/setup-protoc@v1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Set Version (For Release)
if: github.event_name == 'release'
run: |
pip install tomlkit
python ../scripts/set_versions.py ${{ needs.version.outputs.version }} \
pyproject.toml:project.version \
pyproject.toml:tool.poetry.version \
Cargo.toml:workspace.package.version
- name: Build wheel
uses: messense/maturin-action@v1
with:
Expand Down Expand Up @@ -195,9 +242,11 @@ jobs:
path: ${{ github.workspace }}/python/dist

build-wheel-windows:
# Build wheels in the merge queue. We can either duplicate this in `main`
# or retrieve the built wheels.
if: github.event_name == 'merge_group'
# Build wheels in the merge queue (to verify they build) and during release.
if: |
github.event_name == 'merge_group' ||
github.event_name == 'release'
needs: [version]
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
Expand All @@ -215,6 +264,14 @@ jobs:
uses: arduino/setup-protoc@v1
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Set Version (For Release)
if: github.event_name == 'release'
run: |
pip install tomlkit
python ../scripts/set_versions.py ${{ needs.version.outputs.version }} \
pyproject.toml:project.version \
pyproject.toml:tool.poetry.version \
Cargo.toml:workspace.package.version
- name: Build wheels
uses: messense/maturin-action@v1
with:
Expand Down Expand Up @@ -243,11 +300,11 @@ jobs:
path: ${{ github.workspace }}/python/dist

build-wheel-linux:
# Build wheels in the merge queue. We also do this on push to `main`
# because it builds the docs which we'll release.
# Build wheels in the merge queue (to verify they build) and during release.
if: |
github.event_name == 'merge_group' ||
(github.event_name == 'push' && github.ref == 'refs/heads/main')
github.event_name == 'release'
needs: [version]
runs-on: ubuntu-latest
strategy:
matrix:
Expand All @@ -268,6 +325,14 @@ jobs:
3.10
3.11
cache: poetry
- name: Set Version (For Release)
if: github.event_name == 'release'
run: |
pip install tomlkit
python ../scripts/set_versions.py ${{ needs.version.outputs.version }} \
pyproject.toml:project.version \
pyproject.toml:tool.poetry.version \
Cargo.toml:workspace.package.version
- name: Build wheels
uses: messense/maturin-action@v1
with:
Expand Down Expand Up @@ -307,12 +372,22 @@ jobs:

# Make the source distribution
sdist:
# Build wheels in the merge queue. We can either duplicate this in `main`
# or retrieve the built wheels.
if: github.event_name == 'merge_group'
# Build wheels in the merge queue (to verify they build) and during release.
if: |
github.event_name == 'merge_group' ||
github.event_name == 'release'
needs: [version]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set Version (For Release)
if: github.event_name == 'release'
run: |
pip install tomlkit
python ../scripts/set_versions.py ${{ needs.version.outputs.version }} \
pyproject.toml:project.version \
pyproject.toml:tool.poetry.version \
Cargo.toml:workspace.package.version
- name: Build sdist
uses: PyO3/maturin-action@v1
with:
Expand Down Expand Up @@ -345,20 +420,23 @@ jobs:
id: deployment
uses: actions/deploy-pages@v2

# release:
# name: Release
# runs-on: ubuntu-latest
# if: "startsWith(github.ref, 'refs/tags/')"
# needs: [linux, windows, macos, sdist]
# steps:
# - uses: actions/download-artifact@v3
# with:
# name: wheels
# - name: Publish to PyPI
# uses: PyO3/maturin-action@v1
# env:
# MATURIN_PYPI_TOKEN: ${{ secrets.PYPI_API_TOKEN }}
# with:
# command: upload
# args: --skip-existing *
# working-directory: python
release:
name: Release
runs-on: ubuntu-latest
environment: pypi
needs: [build-wheel-linux, build-wheel-windows, build-wheel-macos, sdist]
permissions:
id-token: write
if: github.event_name == 'release' && github.event.action == 'published'
steps:
- uses: actions/download-artifact@v3
with:
# unpacks default artifact into dist/
# if `name: artifact` is omitted, the action will create extra parent dir
name: artifact
path: dist

# - uses: pypa/gh-action-pypi-publish@release/v1
# To Test
#with:
# To test: repository_url: https://test.pypi.org/legacy/
14 changes: 13 additions & 1 deletion .github/workflows/release_drafter.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,24 @@ jobs:
disable-autolabeler: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
python_client_draft:
runs-on: ubuntu-latest
concurrency:
group: python-client-release
steps:
- name: Draft Python Client release
uses: release-drafter/release-drafter@v5
with:
config-name: release-drafter-python-client.yml
disable-autolabeler: true
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
python_draft:
runs-on: ubuntu-latest
concurrency:
group: python-release
steps:
- name: Draft Python release
- name: Draft Release Notes
uses: release-drafter/release-drafter@v5
with:
config-name: release-drafter-python.yml
Expand Down
18 changes: 2 additions & 16 deletions python/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ xdoctest = {extras = ["colors"], version = ">=0.15.10"}
optional = true

[tool.poetry.group.release.dependencies]
python-semantic-release = "^8.0.6"
tomlkit = "^0.12.1"

[build-system]
requires = ["maturin>=1,<2"]
Expand Down Expand Up @@ -151,18 +151,4 @@ show_error_context = true
[tool.pytest.ini_options]
testpaths = [
"pytests",
]

[tool.semantic_release]
version_toml = [
"pyproject.toml:project.version",
"pyproject.toml:tool.poetry.version",
]
tag_format = "v{version}"

[tool.semantic_release.branches.main]
match = "main"
prerelease = true
prerelease_token = "a"

[tool.semantic_release.remote]
]
28 changes: 28 additions & 0 deletions scripts/get_version.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import tomlkit
import argparse
from typing import List

def get_value_from_toml(file_path: str, toml_path: List[str]) -> str:
"""Retrieve a value from a TOML file at the given path."""
with open(file_path, 'r') as f:
data = tomlkit.parse(f.read())

temp = data
for key in toml_path:
temp = temp[key]

return str(temp) # Convert value to string in case it's a number or boolean

def main():
parser = argparse.ArgumentParser(description='Retrieve value from a TOML file.')
parser.add_argument('file', type=str, help='Path to the TOML file.')
parser.add_argument('path', type=str, help='Path within the TOML file (e.g., package.version)')

args = parser.parse_args()

toml_path = args.path.split('.')
value = get_value_from_toml(args.file, toml_path)
print(f"Value at '{args.path}' in '{args.file}': {value}")

if __name__ == "__main__":
main()
48 changes: 48 additions & 0 deletions scripts/set_versions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import tomlkit
from tomlkit import dumps
import argparse
from collections import defaultdict
from typing import Dict, List

def update_version_in_data(data: Dict, version: str, toml_paths: List[str]) -> None:
"""Update the version number in a data dictionary (parsed TOML) at multiple paths."""
for path in toml_paths:
temp = data
path = path.split('.')
for key in path[:-1]:
temp = temp[key]
temp[path[-1]] = version

def main():
parser = argparse.ArgumentParser(description='Update version in TOML files.')
parser.add_argument('version', type=str, help='The version number to set.')
parser.add_argument('entries', nargs='+', type=str,
help='TOML file and path, format: <file_path>:<toml_path> (e.g., config.toml:package.version)')

args = parser.parse_args()

# Dictionary to hold the paths for each file
file_paths_dict = defaultdict(list)

for entry in args.entries:
parts = entry.split(":")
if len(parts) != 2:
print(f"Invalid entry format: {entry}")
continue

file_path, toml_path_str = parts

file_paths_dict[file_path].append(toml_path_str)

# Update the files using the stored paths
for file_path, paths in file_paths_dict.items():
with open(file_path, 'r') as f:
data = tomlkit.parse(f.read())

update_version_in_data(data, args.version, paths)

with open(file_path, 'w') as f:
f.write(dumps(data))

if __name__ == "__main__":
main()

0 comments on commit 0956cfa

Please sign in to comment.