Skip to content

Commit

Permalink
Release script: milestone automation
Browse files Browse the repository at this point in the history
[noissue]
  • Loading branch information
fao89 committed Jul 3, 2020
1 parent a1d54e2 commit a2cebfd
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 7 deletions.
17 changes: 16 additions & 1 deletion .travis/publish_plugin_pypi.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,22 @@

set -euv

pip install twine
export COMMIT_MSG=$(git log --format=%B --no-merges -1)
export RELEASE=$(echo $COMMIT_MSG | grep 'Releasing' | awk '{print $2}')
export MILESTONE_URL=$(echo $COMMIT_MSG | grep 'RedmineMilestone:' | awk '{print $2}')
export REDMINE_QUERY_URL=$(echo $COMMIT_MSG | grep 'RedmineQuery:' | awk '{print $2}')

MILESTONE=$(http $MILESTONE_URL | jq -r .version.name)

echo "Releasing $RELEASE - Milestone: $MILESTONE_URL Query: $REDMINE_QUERY_URL"

if [[ "$MILESTONE" != "$RELEASE" ]]; then
echo "Milestone $MILESTONE is not equal to Release $RELEASE"
exit 1
fi

pip install python-redmine twine
python .travis/redmine.py $REDMINE_KEY $REDMINE_QUERY_URL

python setup.py sdist bdist_wheel --python-tag py3
twine check dist/* || exit 1
Expand Down
19 changes: 19 additions & 0 deletions .travis/redmine.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import sys

from redminelib import Redmine

REDMINE_KEY = sys.argv[1]
REDMINE_QUERY_URL = sys.argv[2]
CLOSED_CURRENTRELEASE = 11

redmine = Redmine(REDMINE_QUERY_URL.split("issues")[0], key=REDMINE_KEY)
query_issues = REDMINE_QUERY_URL.split("=")[-1].split(",")

for issue in query_issues:
status = redmine.issue.get(int(issue)).status.name
if status != "MODIFIED":
raise ValueError("One or more issues are not MODIFIED")

for issue in query_issues:
print(f"Closing #{issue}")
redmine.issue.update(int(issue), status_id=CLOSED_CURRENTRELEASE)
81 changes: 75 additions & 6 deletions .travis/release.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,69 @@
import argparse
import asyncio
import json
import os
import textwrap
from collections import defaultdict

import aiohttp
from git import Repo


REDMINE_QUERY_URL = "https://pulp.plan.io/issues?set_filter=1&status_id=*&issue_id="
REDMINE_URL = "https://pulp.plan.io"
REDMINE_QUERY_URL = f"{REDMINE_URL}/issues?set_filter=1&status_id=*&issue_id="


async def get_redmine_issue_data(url):
"""Get issue JSON from redmine."""
async with aiohttp.ClientSession() as session:
async with session.get(url) as response:
return await response.json()


async def validate_redmine_data(redmine_query_url, redmine_data):
"""Validate redmine milestone."""
done, _ = await asyncio.wait(redmine_data)
issues_data = [i.result() for i in done]
milestone_set = set()
project_set = set()
stats = defaultdict(list)
for issue in issues_data:
issue_id = issue["issue"]["id"]

project_name = issue["issue"]["project"]["name"]
project_set.update([project_name])
stats[f"project_{project_name.lower().replace(' ', '_')}"].append(issue_id)

if issue["issue"]["status"]["name"] != "MODIFIED":
stats["status_not_modified"].append(issue_id)

milestone = issue["issue"].get("fixed_version", {}).get("name")
if not milestone:
stats["without_milestone"].append(issue_id)
else:
milestone_id = issue["issue"].get("fixed_version", {}).get("id")
milestone_set.update([milestone])
stats[f"milestone_{milestone}"].append(issue_id)

print(f"\n\nRedmine stats: {json.dumps(stats, indent=2)}")
error_messages = []
if stats.get("status_not_modified"):
error_messages.append(f"One or more issues are not MODIFIED {stats['status_not_modified']}")
if stats.get("without_milestone"):
error_messages.append(
f"One or more issues are not associated with a milestone {stats['without_milestone']}"
)
if len(milestone_set) > 1:
error_messages.append(f"Issues with different milestones - {milestone_set}")
if len(project_set) > 1:
error_messages.append(f"Issues with different projects - {project_set}")
if error_messages:
error_messages.append(f"Verify at {redmine_query_url}")
raise RuntimeError("\n".join(error_messages))

return f"{REDMINE_URL}/versions/{milestone_id}.json"


release_path = os.path.dirname(os.path.abspath(__file__))
plugin_path = release_path
if ".travis" in release_path:
Expand All @@ -17,11 +75,18 @@
exec(version_line, version)
release_version = version["__version__"].replace(".dev", "")

to_close = []
issues_to_close = []
redmine_issues = []
for filename in os.listdir(f"{plugin_path}/CHANGES"):
if filename.split(".")[0].isdigit():
to_close.append(filename.split(".")[0])
issues = ",".join(to_close)
issue = filename.split(".")[0]
issue_url = f"{REDMINE_URL}/issues/{issue}.json"
redmine_issues.append(get_redmine_issue_data(issue_url))
issues_to_close.append(issue)

issues = ",".join(issues_to_close)
redmine_final_query = f"{REDMINE_QUERY_URL}{issues}"
milestone_url = asyncio.run(validate_redmine_data(redmine_final_query, redmine_issues))

helper = textwrap.dedent(
"""\
Expand Down Expand Up @@ -101,7 +166,11 @@
git.add(f"{plugin_path}/setup.py")
git.add(f"{plugin_path}/requirements.txt")
git.add(f"{plugin_path}/.bumpversion.cfg")
git.commit("-m", f"Releasing {release_version}\n\n[noissue]")
git.commit(
"-m",
f"Releasing {release_version}\n\nRedmineQuery: {redmine_final_query}\n"
f"RedmineMilestone: {milestone_url}\n[noissue]",
)

sha = repo.head.object.hexsha
short_sha = git.rev_parse(sha, short=7)
Expand Down Expand Up @@ -136,5 +205,5 @@
git.add(f"{plugin_path}/.bumpversion.cfg")
git.commit("-m", f"Bump to {new_dev_version}\n\n[noissue]")

print(f"\n\nRedmine query of issues to close:\n{REDMINE_QUERY_URL}{issues}")
print(f"\n\nRedmine query of issues to close:\n{redmine_final_query}")
print(f"\nRelease commit == {short_sha}")
3 changes: 3 additions & 0 deletions .travis/release_requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
aiohttp
bump2version
gitpython

0 comments on commit a2cebfd

Please sign in to comment.