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

Refactor view #14

Draft
wants to merge 23 commits into
base: master
Choose a base branch
from
Draft
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
22 changes: 22 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<!-- Detailed PR description for reviewers goes here... -->


<!-- Official release description goes below RELEASES-->
RELEASES


<!-- Add a gif to your PR 🥳 (optional) -->
![](gif_link)


<!-- ~~EXAMPLE~~
Detailed descrIption goes here

RELEASES
Change default avatar image #public Closes #1425

- Newline after RELEASES (need this for proper formatting in #releases).
- Add a helpful description!
- Try to make it human readable.
- Keep it a single line.
-->
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ test: ## run tests quickly with the default Python
pytest

coverage: ## check code coverage quickly with the default Python
coverage run -m pytest
coverage run --source rocket_releaser -m pytest
coverage report -m
coverage html
$(BROWSER) htmlcov/index.html
Expand Down
22 changes: 19 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,13 @@ Change default avatar image #public Closes [ENG-1234]
- Add the ticket number in square brackets.
- Indicate whether the release will close or fix a ticket with Closes or Fixes before the ticket number.
-->

QA
default avatar image should be a penguin [ENG-1234]
```

## Configuration
To configure the format of the release notes output you can create a `rocket_releaser_format.json` file in the root of your repo.

You can use the default configuration as a reference: [link](rocket_releaser/defaultFormat.json)

## Using Ansible?:

You can run Rocket Releaser in ansible like so:
Expand Down Expand Up @@ -76,3 +78,17 @@ Q: Why use this over [semantic-release](https://github.com/semantic-release/sema

A: Semantic-release's [slack plugin](https://github.com/juliuscc/semantic-release-slack-bot) as of 05/21 does not generate an extended changelog. Semantic Release does not have a plugin for tagging github PR's or tickets either, as far as I am aware.

## Contributing to this repo:
We welcome contributions!

* To setup the repo run `python -m pip install -r dev-requirements.pip`.
* To run the script run `python -m rocket_releaser` from the root of the repo with your desired arguments. Trying to run the release notes script from the command line directly will result in a ImportError!
* To run tests run `pytest` from the root of the repo


### Generating Code Coverage
Follow the instructions in https://coverage.readthedocs.io/en/coverage-5.5/
Make note of the following:
* To prevent site packages from being shown in coverage, run `coverage run --source rocket_releaser -m pytest`
* To open a html file in WSL, run explorer.exe index.html in the htmlcov directory.
* There is a handy makefile coverage command to use if you wish
147 changes: 58 additions & 89 deletions rocket_releaser/changelog.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from collections import defaultdict
import re
import logging
from typing import List
from typing import DefaultDict, List

logger = logging.getLogger(__name__)

Expand All @@ -12,17 +13,14 @@ def __init__(
self.pull_request_dicts = pull_request_dicts

self.features = []
self.fixes = []
self.noteworthy = []
self.qa_notes = []
self.org_name = org_name
self.repo_name = repo_name
self.jira_url = jira_url

@property
def release_bodies(self):
return [
(pr.get("number"), pr.get("body"))
(pr.get("number"), pr.get("body"), pr.get("labels"))
for pr in self.pull_request_dicts
if "release" in pr.get("body").lower()
]
Expand All @@ -38,7 +36,7 @@ def is_fix(line: str):

@staticmethod
def add_github_link(org_name: str, repo_name: str, line, pr_number):
return f"{line} PR: <https://github.com/{org_name}/{repo_name}/pull/{pr_number}|{pr_number}>"
return f"{line} <https://github.com/{org_name}/{repo_name}/pull/{pr_number}|PR-{pr_number}>"

@staticmethod
def add_jira_link(line: str, jira_url: str):
Expand All @@ -49,7 +47,9 @@ def add_jira_link(line: str, jira_url: str):
)

@staticmethod
def linkify(org_name: str, repo_name: str, line: str, pr_number, jira_url=""):
def linkify(
org_name: str, repo_name: str, line: str, pr_number, jira_url: str = ""
):
"""add jira & github links"""

if jira_url:
Expand All @@ -58,87 +58,56 @@ def linkify(org_name: str, repo_name: str, line: str, pr_number, jira_url=""):
return line

@staticmethod
def make_jira_id_bold(line: str):
return line.replace("[", "*[").replace("]", "]*")
def extract_notes_from_pr(pr_body: str, header: str):
"""
Extracts the block of text under a specified header
"""
# Remove HTML comments. Will not work in all cases.
lines = re.sub("<!--.+?>", "", pr_body, flags=re.DOTALL)
lines = lines.strip().split("\n")
# Clean lines up
lines = list(map(lambda line_: line_.strip().lstrip("-").strip(), lines))

features = ""
collecting_releases = False
for line in lines:
if collecting_releases and not line:
# No more lines in RELEASES block
break

if collecting_releases:
features = features + " " + line

if not collecting_releases and line.lower().startswith(header):
collecting_releases = True

return features

@staticmethod
def extract_category_from_pr(labels: List[str], category_indicator: str):
for label in labels:
if label.startswith(category_indicator):
return label.replace(category_indicator, "")
return "Uncategorized"
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Totally forgot about https://github.com/15five/dev-discussions/issues/117#issuecomment-839225813 -_-

I'll need to add a new thing to extra category from the Jira ticket.


def parse_bodies(self):
for pr_number, body in self.release_bodies:
# Remove HTML comments. Will not work in all cases.
lines = re.sub("<!--.+?>", "", body, flags=re.DOTALL)
lines = lines.strip().split("\n")
# Clean lines up
lines = list(map(lambda line_: line_.strip().lstrip("-").strip(), lines))

fixes = ""
noteworthy = ""
features = ""
collecting_releases = False
for line in lines:
if collecting_releases and not line:
# No more lines in RELEASES block
break

if collecting_releases:
if self.is_fix(line):
fixes = fixes + " " + line
elif self.is_noteworthy(line):
line = self.make_jira_id_bold(line)
noteworthy = noteworthy + " " + line
else:
features = features + " " + line

if not collecting_releases and line.lower().startswith("release"):
collecting_releases = True

if fixes:
self.fixes.append(
self.linkify(
self.org_name, self.repo_name, fixes, pr_number, self.jira_url
)
)
if noteworthy:
self.noteworthy.append(
self.linkify(
self.org_name,
self.repo_name,
noteworthy,
pr_number,
self.jira_url,
)
)
if features:
self.features.append(
self.linkify(
self.org_name,
self.repo_name,
features,
pr_number,
self.jira_url,
)
)

qa_notes = ""
collecting_qa_notes = False
for line in lines:
if collecting_qa_notes and not line:
# No more lines in QA block
break

if collecting_qa_notes:
qa_notes = qa_notes + " " + line

if not collecting_qa_notes and line.lower().startswith("qa"):
collecting_qa_notes = True

if qa_notes:
self.qa_notes.append(
self.linkify(
self.org_name,
self.repo_name,
qa_notes,
pr_number,
self.jira_url,
)
)

return self
notes_by_category: DefaultDict[str, List[str]] = defaultdict(list)
for pr_number, body, labels in self.release_bodies:
features = ChangeLog.extract_notes_from_pr(body, "release")
linky_features = self.linkify(
self.org_name,
self.repo_name,
features,
pr_number,
self.jira_url,
)
category = ChangeLog.extract_category_from_pr(labels, "feat-")
notes_by_category[category].append("•" + linky_features)

release_notes = ""
for category in notes_by_category:
notes = "\n".join(notes_by_category[category])
notes_section = f"*{category}*\n{notes}\n\n"
release_notes += notes_section

return release_notes
35 changes: 35 additions & 0 deletions rocket_releaser/defaultFormat.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"hotfix_alert_format": ":fire: HOTFIX :fire: ",
"note_format": "~~currently not used~~• $DESCRIPTION <$TICKET_LINK|$TICKET_ID> <$PR_LINK|PR-$PR_ID>",
"plaintext_format": "${HOTFIX_ALERT}${ENV} Release\n$RELEASE_NOTES\n*Changeset*: <$CHANGESET_LINK|$CHANGESET>\n*Stats*: $NUM_TICKETS tickets | $NUM_PRS PR's",
"slack_format": {
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "${HOTFIX_ALERT}${ENV} Release"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "$RELEASE_NOTES"
}
},
{
"type": "divider"
},
{
"type": "context",
"elements": [
{
"type": "mrkdwn",
"text": "*Changeset*: <$CHANGESET_LINK|$CHANGESET>\n*Stats*: $NUM_TICKETS tickets | $NUM_PRS PR's"
}
]
}
]
}
}
9 changes: 8 additions & 1 deletion rocket_releaser/prs.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ def pull_request_dicts(self, deploy_shas: List[str] = []) -> List[dict]:

# number of associatedPullRequests to pull is entirely arbitrary
# 99% of cases it should only be 1 anyways
query = """
# Example query variables:
# {"sha": "fa6e8664600bcf1ff6c2968c74e7555eb1370aaa", "repo": "rocket_releaser", "owner": "15five"}
query: str = """
query associatedPRs($sha: String, $repo: String!, $owner: String!){
repository(name: $repo, owner: $owner) {
commit: object(expression: $sha) {
Expand All @@ -39,6 +41,11 @@ def pull_request_dicts(self, deploy_shas: List[str] = []) -> List[dict]:
number
body
merged
labels(first:100) {
nodes{
name
}
}
}
}
}
Expand Down
Loading