forked from BeanieODM/beanie
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
GH Action: Set new version and publish on push (BeanieODM#961)
* Implemented * remove extra newline * remove testing part from gh action script
- Loading branch information
1 parent
ff709bf
commit 6c940bd
Showing
6 changed files
with
227 additions
and
5 deletions.
There are no files selected for viewing
Empty file.
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,91 @@ | ||
import subprocess | ||
from dataclasses import dataclass | ||
from datetime import datetime | ||
from typing import List | ||
|
||
import requests # type: ignore | ||
|
||
|
||
@dataclass | ||
class PullRequest: | ||
number: int | ||
title: str | ||
user: str | ||
user_url: str | ||
url: str | ||
|
||
|
||
class GitHubHandler: | ||
def __init__( | ||
self, | ||
username: str, | ||
repository: str, | ||
current_version: str, | ||
new_version: str, | ||
): | ||
self.username = username | ||
self.repository = repository | ||
self.base_url = f"https://api.github.com/repos/{username}/{repository}" | ||
self.current_version = current_version | ||
self.new_version = new_version | ||
self.commits = self.get_commits_after_tag(current_version) | ||
self.prs = [self.get_pr_for_commit(commit) for commit in self.commits] | ||
|
||
def get_commits_after_tag(self, tag: str) -> List[str]: | ||
result = subprocess.run( | ||
["git", "log", f"{tag}..HEAD", "--pretty=format:%H"], | ||
stdout=subprocess.PIPE, | ||
text=True, | ||
) | ||
return result.stdout.split() | ||
|
||
def get_pr_for_commit(self, commit_sha: str) -> PullRequest: | ||
url = f"{self.base_url}/commits/{commit_sha}/pulls" | ||
response = requests.get(url) | ||
response.raise_for_status() | ||
pr_data = response.json()[0] | ||
return PullRequest( | ||
number=pr_data["number"], | ||
title=pr_data["title"], | ||
user=pr_data["user"]["login"], | ||
user_url=pr_data["user"]["html_url"], | ||
url=pr_data["html_url"], | ||
) | ||
|
||
def build_markdown_for_many_prs(self) -> str: | ||
markdown = f"\n## [{self.new_version}] - {datetime.now().strftime('%Y-%m-%d')}\n" | ||
for pr in self.prs: | ||
markdown += ( | ||
f"### {pr.title.capitalize()}\n" | ||
f"- Author - [{pr.user}]({pr.user_url})\n" | ||
f"- PR <{pr.url}>\n" | ||
) | ||
markdown += f"\n[{self.new_version}]: https://pypi.org/project/{self.repository}/{self.new_version}\n" | ||
return markdown | ||
|
||
def commit_changes(self): | ||
self.run_git_command( | ||
["git", "config", "--global", "user.name", "github-actions[bot]"] | ||
) | ||
self.run_git_command( | ||
[ | ||
"git", | ||
"config", | ||
"--global", | ||
"user.email", | ||
"github-actions[bot]@users.noreply.github.com", | ||
] | ||
) | ||
self.run_git_command(["git", "add", "."]) | ||
self.run_git_command( | ||
["git", "commit", "-m", f"Bump version to {self.new_version}"] | ||
) | ||
self.run_git_command(["git", "tag", self.new_version]) | ||
self.git_push() | ||
|
||
def git_push(self): | ||
self.run_git_command(["git", "push", "origin", "main", "--tags"]) | ||
|
||
@staticmethod | ||
def run_git_command(command: List[str]): | ||
subprocess.run(command, check=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 |
---|---|---|
@@ -0,0 +1,116 @@ | ||
import subprocess | ||
from pathlib import Path | ||
|
||
import requests # type: ignore | ||
import toml | ||
from gh import GitHubHandler | ||
|
||
|
||
class SemVer: | ||
def __init__(self, version: str): | ||
self.version = version | ||
self.major, self.minor, self.patch = map(int, self.version.split(".")) | ||
|
||
def increment_minor(self): | ||
return SemVer(f"{self.major}.{self.minor + 1}.0") | ||
|
||
def __str__(self): | ||
return self.version | ||
|
||
def __eq__(self, other): | ||
return self.version == other.version | ||
|
||
def __gt__(self, other): | ||
return ( | ||
(self.major > other.major) | ||
or (self.major == other.major and self.minor > other.minor) | ||
or ( | ||
self.major == other.major | ||
and self.minor == other.minor | ||
and self.patch > other.patch | ||
) | ||
) | ||
|
||
|
||
class VersionHandler: | ||
PACKAGE_NAME = "beanie" | ||
ROOT_PATH = Path(__file__).parent.parent.parent.parent | ||
|
||
def __init__(self): | ||
self.pyproject = self.ROOT_PATH / "pyproject.toml" | ||
self.init_py = self.ROOT_PATH / "beanie" / "__init__.py" | ||
self.changelog = self.ROOT_PATH / "docs" / "changelog.md" | ||
|
||
self.current_version = self.parse_version_from_pyproject( | ||
self.pyproject | ||
) | ||
self.pypi_version = self.get_version_from_pypi() | ||
|
||
if self.current_version < self.pypi_version: | ||
raise ValueError("Current version is less than pypi version") | ||
|
||
if self.current_version == self.pypi_version: | ||
self.current_version = self.current_version.increment_minor() | ||
self.update_files() | ||
else: | ||
self.flit_publish() | ||
|
||
@staticmethod | ||
def parse_version_from_pyproject(pyproject: Path) -> SemVer: | ||
toml_data = toml.loads(pyproject.read_text()) | ||
return SemVer(toml_data["project"]["version"]) | ||
|
||
def get_version_from_pypi(self) -> SemVer: | ||
response = requests.get( | ||
f"https://pypi.org/pypi/{self.PACKAGE_NAME}/json" | ||
) | ||
if response.status_code == 200: | ||
return SemVer(response.json()["info"]["version"]) | ||
raise ValueError("Can't get version from pypi") | ||
|
||
def update_files(self): | ||
self.update_pyproject_version() | ||
self.update_file_versions([self.init_py]) | ||
self.update_changelog() | ||
|
||
def update_pyproject_version(self): | ||
pyproject = toml.loads(self.pyproject.read_text()) | ||
pyproject["project"]["version"] = str(self.current_version) | ||
self.pyproject.write_text(toml.dumps(pyproject)) | ||
|
||
def update_file_versions(self, files_to_update): | ||
for file_path in files_to_update: | ||
content = file_path.read_text() | ||
content = content.replace( | ||
str(self.pypi_version), str(self.current_version) | ||
) | ||
file_path.write_text(content) | ||
|
||
def update_changelog(self): | ||
handler = GitHubHandler( | ||
"BeanieODM", | ||
"beanie", | ||
str(self.pypi_version), | ||
str(self.current_version), | ||
) | ||
changelog_content = handler.build_markdown_for_many_prs() | ||
|
||
changelog_lines = self.changelog.read_text().splitlines() | ||
new_changelog_lines = [] | ||
inserted = False | ||
|
||
for line in changelog_lines: | ||
new_changelog_lines.append(line) | ||
if line.strip() == "# Changelog" and not inserted: | ||
new_changelog_lines.append(changelog_content) | ||
inserted = True | ||
|
||
self.changelog.write_text("\n".join(new_changelog_lines)) | ||
handler.commit_changes() | ||
|
||
def flit_publish(self): | ||
subprocess.run(["flit", "publish"], check=True) | ||
|
||
|
||
if __name__ == "__main__": | ||
VersionHandler() |
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
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
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,5 +1,15 @@ | ||
from pathlib import Path | ||
|
||
import toml | ||
|
||
from beanie import __version__ | ||
|
||
|
||
def parse_version_from_pyproject(): | ||
pyproject = Path(__file__).parent.parent / "pyproject.toml" | ||
toml_data = toml.loads(pyproject.read_text()) | ||
return toml_data["project"]["version"] | ||
|
||
|
||
def test_version(): | ||
assert __version__ == "1.26.0" | ||
assert __version__ == parse_version_from_pyproject() |