Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(core): Refactor release script, support uagents-core release #619

Merged
merged 3 commits into from
Jan 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,12 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_TOKEN: ${{ github.token }}
run: |
pip install tomli packaging poetry
pip install tomli packaging poetry pydantic pydantic-settings
git config --global user.email "[email protected]"
git config --global user.name "CI BOT"
python3 ./scripts/do_release.py

if [[ "${{ github.event.pull_request.head.ref }}" == release/core@* ]]; then
export PACKAGE="core"
fi

python3 ./scripts/do_release.py
112 changes: 68 additions & 44 deletions python/scripts/do_release.py
Original file line number Diff line number Diff line change
@@ -1,93 +1,115 @@
"""Release automation script."""

import os
import subprocess
import sys
from pathlib import Path
from typing import Literal

import tomli
from packaging.version import Version
from pydantic_settings import BaseSettings

ROOT = Path(__file__).parent.parent


class EnvCredentials:
"""Credentials from env variables."""
class Settings(BaseSettings):
"""Settings for release script."""

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

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

# which package we are releasing
package: Literal["main", "core"] = "main"

def get_the_latest_release_version() -> Version:

def get_tag_prefix(package: str) -> str:
"""Map package to tag."""

return {"main": "v", "core": "core@"}[package]


def get_package_path(package: str) -> Path:
"""Get package path."""

return {"main": ROOT, "core": ROOT / "uagents-core"}[package]


def get_the_latest_release_version(package: str) -> 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])
tag_prefix = "refs/tags/" + get_tag_prefix(package)
tags = [i for i in tags if i.startswith(tag_prefix) and not i.endswith("^{}")]
versions = [i.replace(tag_prefix, "") for i in tags]
return Version(versions[-1]) if len(versions) > 0 else Version("0.0.0")


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


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


def make_tag(current_version: Version) -> None:
def make_tag(current_version: Version, package: str) -> None:
"""Make git tag."""
tag_prefix = get_tag_prefix(package)
tag_text = {
"main": f"Release {current_version}",
"core": f"UAgents-Core release {current_version}",
}[package]
subprocess.check_call(
f"git tag v{current_version} -m 'Release {current_version}'", shell=True
f"git tag {tag_prefix}{current_version} -m '{tag_text}'", shell=True
)


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


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


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


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

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

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",
f"poetry publish --skip-existing --username {self._settings.pypi_username} "
f"--password {self._settings.pypi_password} --verbose",
check=True,
shell=True,
cwd=str(get_package_path(self._settings.package)),
stdout=sys.stdout,
stderr=sys.stderr,
)
Expand All @@ -96,8 +118,8 @@ def upload_packages(self):

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

print("Current version:", current_version)
print("Latest release version:", latest_release_version)
Expand All @@ -109,33 +131,35 @@ def main(self):
return

# copy README.md from top level to python folder so that appears on pypi
readme = ROOT.parent / "README.md"
python_readme = ROOT / "README.md"
python_readme.write_text(readme.read_text())
if self._settings.package == "main":
readme = ROOT.parent / "README.md"
python_readme = ROOT / "README.md"
python_readme.write_text(readme.read_text())

print("\nBuilding packages")
build_packages()
build_packages(package=self._settings.package)
print("Packages built")

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

print("\nMake tag")
make_tag(current_version)
make_tag(current_version, package=self._settings.package)
print("Tag made")

print("\nPush tag")
push_tag(current_version)
tag = get_tag_prefix(self._settings.package) + str(current_version)
print(f"\nPush tag: {tag}")
push_tag(tag)
print("Tag pushed")

print("\nMake release")
make_release(current_version)
make_release(current_version, package=self._settings.package)
print("Release made.")

print("\nDONE")


if __name__ == "__main__":
creds = EnvCredentials()
ReleaseTool(creds).main()
settings = Settings() # type: ignore
ReleaseTool(settings).main()