Skip to content

Commit ac2191d

Browse files
authored
Merge pull request #1561 from mvdbeek/build_and_release_script_updates
Build and release script updates
2 parents 489fa1f + b055cb9 commit ac2191d

File tree

5 files changed

+328
-54
lines changed

5 files changed

+328
-54
lines changed

Makefile

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,9 @@ test: ## run tests with the default Python (faster than tox)
7474
$(IN_VENV) pytest $(TESTS)
7575

7676
format: ## format Python code with ruff, black and isort
77-
$(IN_VENV) ruff format planemo tests
78-
$(IN_VENV) isort planemo tests
79-
$(IN_VENV) black planemo tests
77+
$(IN_VENV) ruff format planemo scripts tests
78+
$(IN_VENV) isort planemo scripts tests
79+
$(IN_VENV) black planemo scripts tests
8080

8181
quick-test: ## run quickest tests with the default Python
8282
$(IN_VENV) PLANEMO_SKIP_SLOW_TESTS=1 PLANEMO_SKIP_GALAXY_TESTS=1 pytest $(TESTS)
@@ -137,6 +137,8 @@ open-rtd: docs ## open docs on readthedocs.org
137137
open-project: ## open project on github
138138
$(OPEN_RESOURCE) $(PROJECT_URL)
139139

140+
check-dist: clean-build dist clean-build
141+
140142
dist: clean submodule ## create and check packages
141143
$(IN_VENV) python -m build
142144
$(IN_VENV) twine check dist/*
@@ -169,10 +171,10 @@ push-release: ## Push a tagged release to github
169171
git push $(UPSTREAM) master
170172
git push --tags $(UPSTREAM)
171173

172-
release: release-local push-release ## package, review, and upload a release
174+
release: release-local check-dist push-release ## package, review, and upload a release
173175

174176
add-history: ## Reformat HISTORY.rst with data from Github's API
175-
$(IN_VENV) python $(BUILD_SCRIPTS_DIR)/bootstrap_history.py $(ITEM)
177+
$(IN_VENV) python $(BUILD_SCRIPTS_DIR)/bootstrap_history.py --acknowledgements
176178

177179
update-extern: ## update external artifacts copied locally
178180
sh scripts/update_extern.sh

dev-requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,6 @@ flake8
2222
isort
2323

2424
# For release
25+
build
2526
wheel
2627
twine

docs/developing.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ Release Checklist
55
This page describes the process of releasing new versions of Planemo.
66

77
* Review ``git status`` for missing files.
8-
* Verify the latest Travis CI builds pass.
9-
* Update ``HISTORY.rst`` with the help of ``scripts/bootstrap_history.py``
10-
* ``make open-docs`` and review changelog.
8+
* Verify the latest github workflows pass.
119
* Ensure the target release is set correctly in ``planemo/__init__.py`` (
1210
``version`` will be a ``devN`` variant of target release).
11+
* Update ``HISTORY.rst`` with the help of ``make add-history`` ``scripts/bootstrap_history.py``
12+
* ``make open-docs`` and review changelog.
1313
* ``make clean && make lint && make test``
1414
* Commit outstanding changes.
1515
* Update version and history, commit, add tag, mint a new version and push

scripts/bootstrap_history.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
# Little script to make HISTORY.rst more easy to format properly, lots TODO
33
# pull message down and embed, use arg parse, handle multiple, etc...
44
import os
5+
import re
6+
import subprocess
57
import sys
68
import textwrap
79
from urllib.parse import urljoin
@@ -21,6 +23,76 @@
2123
PROJECT_API = f"https://api.github.com/repos/{PROJECT_AUTHOR}/{PROJECT_NAME}/"
2224

2325

26+
def get_last_release_tag():
27+
"""Get the last release tag based on the current version in __init__.py"""
28+
version = project.__version__
29+
# Remove .dev0 suffix if present
30+
if ".dev" in version:
31+
version = version.split(".dev")[0]
32+
33+
# Parse version components
34+
parts = version.split(".")
35+
if len(parts) >= 3:
36+
major, minor, patch = parts[:3]
37+
# Decrement patch version to get last release
38+
last_patch = max(0, int(patch) - 1)
39+
return f"{major}.{minor}.{last_patch}"
40+
return version
41+
42+
43+
def get_merge_commits_since_tag(tag):
44+
"""Get merge commits since the specified tag"""
45+
try:
46+
result = subprocess.run(
47+
["git", "log", "--merges", "--oneline", f"{tag}..HEAD"], capture_output=True, text=True, check=True
48+
)
49+
return result.stdout.strip().split("\n") if result.stdout.strip() else []
50+
except subprocess.CalledProcessError:
51+
return []
52+
53+
54+
def extract_pr_info_from_merge(merge_line):
55+
"""Extract PR number and author from merge commit message"""
56+
# Match pattern: "Merge pull request #1234 from author/branch"
57+
match = re.match(r"[a-f0-9]+\s+Merge pull request #(\d+) from ([^/]+)/", merge_line)
58+
if match:
59+
pr_number = match.group(1)
60+
author = match.group(2)
61+
return pr_number, author
62+
return None, None
63+
64+
65+
def generate_acknowledgements():
66+
"""Generate acknowledgement lines for merge commits since last release"""
67+
tag = get_last_release_tag()
68+
merge_commits = get_merge_commits_since_tag(tag)
69+
70+
acknowledgements = []
71+
for merge in merge_commits:
72+
if merge.strip():
73+
pr_number, author = extract_pr_info_from_merge(merge)
74+
if pr_number and author:
75+
try:
76+
# Get PR details from GitHub API
77+
api_url = urljoin(PROJECT_API, f"pulls/{pr_number}")
78+
req = requests.get(api_url).json()
79+
title = req.get("title", "")
80+
login = req["user"]["login"]
81+
82+
# Format acknowledgement line
83+
title_clean = title.rstrip(".")
84+
ack_line = f"* {title_clean} (thanks to `@{login}`_). `Pull Request {pr_number}`_"
85+
acknowledgements.append(ack_line)
86+
87+
# Add GitHub link
88+
github_link = f".. _Pull Request {pr_number}: {PROJECT_URL}/pull/{pr_number}"
89+
acknowledgements.append(github_link)
90+
except Exception as e:
91+
print(f"Error processing PR {pr_number}: {e}", file=sys.stderr)
92+
93+
return acknowledgements
94+
95+
2496
def main(argv):
2597
history_path = os.path.join(PROJECT_DIRECTORY, "HISTORY.rst")
2698
with open(history_path, encoding="utf-8") as fh:
@@ -30,6 +102,37 @@ def extend(from_str, line):
30102
from_str += "\n"
31103
return history.replace(from_str, from_str + line + "\n")
32104

105+
# Check if we should generate acknowledgements for merge commits
106+
if len(argv) > 1 and argv[1] == "--acknowledgements":
107+
acknowledgements = generate_acknowledgements()
108+
if acknowledgements:
109+
print("Generated acknowledgement lines:")
110+
111+
# Find the unreleased section (current dev version)
112+
current_version = project.__version__
113+
unreleased_section_marker = f"---------------------\n{current_version}\n---------------------"
114+
115+
for ack in acknowledgements:
116+
if ack.startswith("*"):
117+
print(ack)
118+
# Place acknowledgement lines in the unreleased section
119+
history = extend(unreleased_section_marker, ack)
120+
elif ack.startswith(".."):
121+
print(ack)
122+
history = extend(".. github_links", ack)
123+
124+
with open(history_path, "w", encoding="utf-8") as fh:
125+
fh.write(history)
126+
print(f"\nAcknowledgements added to {history_path}")
127+
else:
128+
print("No merge commits found since last release.")
129+
return
130+
131+
if len(argv) < 2:
132+
print("Usage: python bootstrap_history.py <identifier> [message]")
133+
print(" or: python bootstrap_history.py --acknowledgements")
134+
return
135+
33136
ident = argv[1]
34137

35138
message = ""

0 commit comments

Comments
 (0)