Skip to content

Commit

Permalink
feat: add release automation
Browse files Browse the repository at this point in the history
  • Loading branch information
jrriehl committed Nov 24, 2023
1 parent 1840474 commit 26f2843
Show file tree
Hide file tree
Showing 3 changed files with 186 additions and 1 deletion.
48 changes: 48 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# This workflow is triggered when a PR is merged whose source branch's name starts with "release" and whose target branch is "main".
# It checks for the current package version (in pyproject.toml) and the latest tag on GitHub.
# If package version is more recent, it builds the project, uploads to PYPI, creates a Tag and makes a release on GitHub.
# Secrets PYPI_USERNAME and PYPI_PASSWORD are required!

name: Check and release
on:
pull_request:
branches: [main]
types: [closed]

jobs:
build:
defaults:
run:
shell: bash
working-directory: ./python

if: github.event.pull_request.merged == true && startsWith(github.event.pull_request.head.ref, 'release')
runs-on: ubuntu-latest
steps:

- name: Checkout Repo
uses: actions/checkout@v3

- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: '3.11'

- name: Install Poetry
run: |
export POETRY_HOME=/opt/poetry
python3 -m venv $POETRY_HOME
$POETRY_HOME/bin/pip install poetry==1.4.0
$POETRY_HOME/bin/poetry --version
- name: Run release script
env:
PYPI_USERNAME: __token__
PYPI_PASSWORD: ${{secrets.FETCHBOT_PYPI_TOKEN}}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_TOKEN: ${{ github.token }}
run: |
pip install tomli packaging poetry
git config --global user.email "ci-bot@fetch.ai"
git config --global user.name "CI BOT"
python3 ./scripts/do_release.py
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "fetchai-babble"
version = "0.3.0"
version = "0.4.0"
description = "A simple python library for interacting with the Fetch.ai messaging service (called Memorandum)"
authors = ["Fetch.AI Limited"]
license = "Apache 2.0"
Expand Down
137 changes: 137 additions & 0 deletions scripts/do_release.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
"""Release automation script."""

import os
import subprocess
import sys
from pathlib import Path

import tomli
from packaging.version import Version


ROOT = Path(__file__).parent.parent


class EnvCredentials:
"""Credentials from env variables."""

@property
def pypi_username(self) -> str:
"""Get PYPI username."""
return os.environ.get("PYPI_USERNAME") or ""

@property
def pypi_password(self) -> str:
"""Get PYPI password."""
return os.environ.get("PYPI_PASSWORD") or ""


def get_the_latest_release_version() -> Version:
"""Get release version from gihtub tags."""
text = subprocess.check_output("git ls-remote --tags origin", shell=True, text=True)
tags = [i.split("\t")[1].strip() for i in text.splitlines()]
tags = [i for i in tags if i.startswith("refs/tags/v") and not i.endswith("^{}")]
versions = [i.replace("refs/tags/v", "") for i in tags]
return Version(versions[-1])


def get_current_version() -> Version:
"""Get current code version."""
text = (ROOT / "pyproject.toml").read_text()
version = tomli.loads(text)["tool"]["poetry"]["version"]
return Version(version)


def do_we_need_to_release() -> bool:
"""Check is code version is newer than on github."""
current_version = get_current_version()
released_version = get_the_latest_release_version()
return current_version > released_version


def make_tag(current_version: Version) -> None:
"""Make git tag."""
subprocess.check_call(
f"git tag v{current_version} -m 'Release {current_version}'", shell=True
)


def push_tag(current_version) -> None:
"""Push tag to github."""
subprocess.check_call(f"git push origin v{current_version}", shell=True)


def make_release(current_version: Version) -> None:
"""Make release on Github."""
subprocess.check_call(
f"""gh release create v{current_version} --title "v{current_version}"
--generate-notes""",
shell=True,
)


def build_packages():
"""Build packages."""
subprocess.check_call("poetry build", shell=True)


class ReleaseTool:
"""Release helper tool."""

def __init__(self, credentials: EnvCredentials) -> None:
"""Init release tool instance."""
self._credentials = credentials

def upload_packages(self):
"""Upload packages to PYPI."""
result = subprocess.run(
f"poetry publish --skip-existing --username {self._credentials.pypi_username} "
f"--password {self._credentials.pypi_password} --verbose",
check=True,
shell=True,
stdout=sys.stdout,
stderr=sys.stderr,
)
if result.returncode != 0:
raise RuntimeError("Upload pacakges failed!")

def main(self):
"""Run release process."""
current_version = get_current_version()
latest_release_version = get_the_latest_release_version()

print("Current version:", current_version)
print("Latest release version:", latest_release_version)

if current_version > latest_release_version:
print("Current version is newer. Good to go.")
else:
print("Current version is not newer. Exiting.")
return

print("\nBuilding packages")
build_packages()
print("Packages built")

print("\nUpload packages")
self.upload_packages()
print("Packages uploaded")

print("\nMake tag")
make_tag(current_version)
print("Tag made")

print("\nPush tag")
push_tag(current_version)
print("Tag pushed")

print("\nMake release")
make_release(current_version)
print("Release made." "")

print("\nDONE")


if __name__ == "__main__":
creds = EnvCredentials()
ReleaseTool(creds).main()

0 comments on commit 26f2843

Please sign in to comment.