Skip to content

Commit bc5421d

Browse files
authored
Merge pull request #120 from lichess-org/copilot/fix-116
add `release.py` to make releasing the package more automated
2 parents 0dbc627 + 0e2cbf4 commit bc5421d

File tree

4 files changed

+177
-7
lines changed

4 files changed

+177
-7
lines changed

Makefile

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
.PHONY: help clean clean-docs clean-test setup test format format-check docs servedocs publish
1+
.PHONY: help clean clean-docs clean-test setup test format format-check docs servedocs build publish
22
.DEFAULT_GOAL := help
33

44
define PRINT_HELP_PYSCRIPT
@@ -36,19 +36,22 @@ typecheck: ## run type checking with pyright
3636
uv run pyright berserk
3737

3838
format: ## format python files with black and docformatter
39-
uv run black berserk tests check-endpoints.py
39+
uv run black berserk tests check-endpoints.py release.py
4040
uv run docformatter --in-place --black berserk/*.py
4141

4242
format-check: ## check formatting with black (for CI)
43-
uv run black berserk tests check-endpoints.py --check
43+
uv run black berserk tests check-endpoints.py release.py --check
4444

4545
docs: ## generate Sphinx HTML documentation, including API docs
4646
uv run sphinx-build -b html docs _build -EW --keep-going
4747

4848
servedocs: docs ## compile the docs and serve them locally
4949
python3 -m http.server --directory _build --bind 127.0.0.1
5050

51-
publish: ## publish to pypi
51+
build: ## build the package
52+
uv build
53+
54+
publish: build ## publish to pypi
5255
@echo
5356
@echo "Release checklist:"
5457
@echo " - Did you update the documentation? (including adding new endpoints to the README?)"
@@ -59,5 +62,4 @@ publish: ## publish to pypi
5962
@echo
6063
@read -p "Are you sure you want to create a release? [y/N] " ans && [ $${ans:-N} = y ]
6164
sleep 5
62-
uv build
6365
uv publish

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "berserk"
3-
version = "0.13.2"
3+
version = "0.13.3.dev0"
44
description = "Python client for the lichess API"
55
authors = [
66
{name = "Lichess", email = "contact@lichess.org"}

release.py

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
#!/usr/bin/python3
2+
# Helper script to create and publish a new `berserk` release.
3+
# Based on `release.py` from `python-chess`.
4+
5+
import os
6+
import chess
7+
import sys
8+
import subprocess
9+
10+
from datetime import datetime
11+
12+
13+
def system(command):
14+
print(command)
15+
exit_code = os.system(command)
16+
if exit_code != 0:
17+
sys.exit(exit_code)
18+
19+
20+
def check_git():
21+
print("--- CHECK GIT ----------------------------------------------------")
22+
system("git diff --exit-code")
23+
system("git diff --cached --exit-code")
24+
25+
system("git fetch origin")
26+
behind = int(
27+
subprocess.check_output(["git", "rev-list", "--count", "master..origin/master"])
28+
)
29+
if behind > 0:
30+
print(f"master is {behind} commit(s) behind origin/master")
31+
sys.exit(1)
32+
33+
34+
def test():
35+
print("--- TEST ---------------------------------------------------------")
36+
system("make test")
37+
38+
39+
def update_changelog(tagname: str):
40+
print("--- UPDATING CHANGELOG ----------------------------------------------")
41+
# include today's date in format yyyy-mm-dd
42+
# to match format v0.13.2 (2023-12-04)
43+
line = f"{tagname} ({datetime.now().strftime('%Y-%m-%d')})"
44+
unreleased = "To be released\n--------------"
45+
with open("CHANGELOG.rst", "r") as changelog_file:
46+
changelog = changelog_file.read()
47+
changelog = changelog.replace(unreleased, line + "\n" + "-" * len(line))
48+
with open("CHANGELOG.rst", "w") as changelog_file:
49+
changelog_file.write(changelog)
50+
51+
52+
def check_docs():
53+
print("--- CHECK DOCS ---------------------------------------------------")
54+
system("make docs")
55+
56+
57+
def _get_current_version(must_be_dev=True) -> str:
58+
"""the dev version is always latest version + patch + dev
59+
eg: last published version 0.13.2 so dev is 0.13.3.dev0
60+
"""
61+
version = subprocess.check_output(["uv", "version"])
62+
# berserk 0.13.3.dev0
63+
assert "berserk" in version
64+
if must_be_dev:
65+
assert "dev" in version
66+
return version.split()[1].partition("dev")[0]
67+
68+
69+
def _decrement_patch(version: str) -> str:
70+
major, minor, patch = [int(x) for x in version.split(".")]
71+
assert patch > 0
72+
return f"{major}.{minor}.{patch - 1}"
73+
74+
75+
# major: Increase the major version (e.g., 1.2.3 => 2.0.0)
76+
# minor: Increase the minor version (e.g., 1.2.3 => 1.3.0)
77+
# patch: Increase the patch version (e.g., 1.2.3 => 1.2.4)
78+
# stable: Move from a pre-release to stable version (e.g., 1.2.3b4.post5.dev6 => 1.2.3)
79+
# alpha: Increase the alpha version (e.g., 1.2.3a4 => 1.2.3a5)
80+
# beta: Increase the beta version (e.g., 1.2.3b4 => 1.2.3b5)
81+
# rc: Increase the rc version (e.g., 1.2.3rc4 => 1.2.3rc5)
82+
# post: Increase the post version (e.g., 1.2.3.post5 => 1.2.3.post6)
83+
# dev: Increase the dev version (e.g., 1.2.3a4.dev6 => 1.2.3.dev7)
84+
def bump_version(bump: "major" | "minor" | "patch") -> str:
85+
last_published_version = _decrement_patch(_get_current_version())
86+
system(f"uv version {last_published_version}")
87+
system(f"uv version --bump {bump}")
88+
new_version = _get_current_version(must_be_dev=False)
89+
print(f"Bumped version: {last_published_version} -> {new_version}")
90+
tagname = f"v{new_version}"
91+
return tagname
92+
93+
94+
def tag_and_push(tagname: str):
95+
print("--- TAG AND PUSH -------------------------------------------------")
96+
release_filename = f"release-{tagname}.txt"
97+
98+
if not os.path.exists(release_filename):
99+
print(f">>> Creating {release_filename} ...")
100+
first_section = False
101+
prev_line = None
102+
with open(release_filename, "w") as release_txt, open(
103+
"CHANGELOG.rst", "r"
104+
) as changelog_file:
105+
headline = f"berserk {tagname}"
106+
release_txt.write(headline + os.linesep)
107+
108+
for line in changelog_file:
109+
if not first_section:
110+
if line.startswith("-------"):
111+
first_section = True
112+
else:
113+
if line.startswith("-------"):
114+
break
115+
else:
116+
if not prev_line.startswith("------"):
117+
release_txt.write(prev_line)
118+
119+
prev_line = line
120+
121+
with open(release_filename, "r") as release_txt:
122+
release = release_txt.read().strip() + os.linesep
123+
print(release)
124+
125+
with open(release_filename, "w") as release_txt:
126+
release_txt.write(release)
127+
128+
guessed_tagname = input(">>> Sure? Confirm tagname: ")
129+
if guessed_tagname != tagname:
130+
print(f"Actual tagname is: {tagname}")
131+
sys.exit(1)
132+
133+
system(f"git tag {tagname} -s -F {release_filename}")
134+
system(f"git push --atomic origin master {tagname}")
135+
return tagname
136+
137+
138+
def go_to_dev():
139+
print("--- GO TO DEV ----------------------------------------------------")
140+
system("uv version --bump patch")
141+
version = _get_current_version(must_be_dev=False)
142+
system(f"uv version {version}dev")
143+
system("git add -u")
144+
system('git commit -m "Bump to next development version"')
145+
system("git push origin master")
146+
147+
148+
def build():
149+
print("--- build, ---------------------------------------------------------")
150+
system("make build")
151+
152+
153+
# def github_release(tagname):
154+
# print("--- GITHUB RELEASE -----------------------------------------------")
155+
# print(f"https://github.com/niklasf/python-chess/releases/new?tag={tagname}")
156+
157+
158+
if __name__ == "__main__":
159+
check_docs()
160+
test()
161+
check_git()
162+
tagname = bump_version()
163+
update_changelog(tagname)
164+
tag_and_push(tagname)
165+
is_done = input("now release to pypi with: make publish, done when done")
166+
if is_done.lower() in ["y", "yes", "done"]:
167+
build()
168+
go_to_dev()

uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)