From f91afd7837b8c79ff5a9e41290a92971458a8978 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 12 Jan 2023 15:00:52 +0100 Subject: [PATCH] Release: support generating LTS release notes from Gitea Now a single script to generate both links and release notes. It also includes the issue ID for the LTS releases, so only the release version needs to be specified. --- release/lts/README.md | 47 ++--- release/lts/create_release_notes.py | 195 ++++-------------- ...reate_download_urls.py => lts_download.py} | 20 +- release/lts/lts_issue.py | 168 +++++++++++++++ release/lts/requirements.txt | 2 +- 5 files changed, 219 insertions(+), 213 deletions(-) rename release/lts/{create_download_urls.py => lts_download.py} (69%) mode change 100755 => 100644 create mode 100644 release/lts/lts_issue.py diff --git a/release/lts/README.md b/release/lts/README.md index 8f7b6f0b87a..c95aecabe18 100644 --- a/release/lts/README.md +++ b/release/lts/README.md @@ -1,41 +1,18 @@ -This folder contains several scripts to smoothen the Blender LTS releases. +This folder contains a script to generate release notes and download URLs +for Blender LTS releases. -create_download_urls.py -======================= +Ensure required Python modules are installed before running: -This python script is used to generate the download urls which we can -copy-paste directly into the CMS of www.blender.org. + pip3 install -r ./requirements.txt -Usage: create_download_urls.py --version 2.83.7 +Then run for example: -Arguments: - --version VERSION Version string in the form of {major}.{minor}.{build} - (eg 2.83.7) + ./create_release_notes.py --version 3.3.2 --format=html -The resulting html will be printed to the console. +Available arguments: -create_release_notes.py -======================= - -This python script is used to generate the release notes which we can -copy-paste directly into the CMS of www.blender.org and stores. - -Usage: ./create_release_notes.py --task=T77348 --version=2.83.7 - -Arguments: - --version VERSION Version string in the form of {major}.{minor}.{build} - (e.g. 2.83.7) - --task TASK Phabricator ticket that is contains the release notes - information (e.g. T77348) - --format FORMAT Format the result in `text`, `steam`, `wiki` or `html` - -Requirements -============ - -* Python 3.8 or later -* Python phabricator client version 0.7.0 - https://pypi.org/project/phabricator/ - -For convenience the python modules can be installed using pip - - pip3 install -r ./requirements.txt \ No newline at end of file + --version VERSION Version string in the form of {major}.{minor}.{build} + (e.g. 3.3.2) + --issue ISSUE Gitea issue that is contains the release notes + information (e.g. #77348) + --format FORMAT Format the result in `text`, `steam`, `wiki` or `html` diff --git a/release/lts/create_release_notes.py b/release/lts/create_release_notes.py index a51721057bb..cfab24c94ae 100755 --- a/release/lts/create_release_notes.py +++ b/release/lts/create_release_notes.py @@ -1,169 +1,46 @@ +#!/usr/bin/env python3 # SPDX-License-Identifier: GPL-2.0-or-later -#!/usr/bin/env python3 - import argparse -import phabricator +import lts_issue +import lts_download -DESCRIPTION = ("This python script is used to generate the release notes " - "which we can copy-paste directly into the CMS of " +DESCRIPTION = ("This python script is used to generate the release notes and " + "download URLs which we can copy-paste directly into the CMS of " "www.blender.org and stores.") -USAGE = "./create_release_notes.py --task=T77348 --version=2.83.7" +# Parse arguments +parser = argparse.ArgumentParser(description=DESCRIPTION) +parser.add_argument( + "--version", + required=True, + help="Version string in the form of {major}.{minor}.{patch} (e.g. 3.3.2)") +parser.add_argument( + "--issue", + help="Task that is contains the release notes information (e.g. #77348)") +parser.add_argument( + "--format", + help="Format the result in `text`, `steam`, `wiki` or `html`", + default="text") +args = parser.parse_args() -class ReleaseLogLine: - """ - Class containing the information of a single line of the release log +# Determine issue number +version = args.version +issue = args.issue +if not issue: + if version.startswith("2.83."): + issue = "#77348" + elif version.startswith("2.93."): + issue = "#88449" + elif version.startswith("3.3."): + issue = "#100749" + else: + raise ValueError("Specify --issue or update script to include issue number for this version") - Instance attributes: +# Print +if args.format == "html": + lts_download.print_urls(version=version) + print("") - * line: (str) the original line used to create this log line - * task_id: (int or None) the extracted task id associated with this log - line. Can be None if the log line isn't associated with a task. - * commit_id: (str or None) the extracted commit id associated with this log - line. Only filled when no `task_id` could be found. - * ref: (str) `task_id` or `commit_id` of this line, including `T` for tasks - or `D` for diffs. - * title: (str) title of this log line. When constructed this attribute is - an empty string. The called needs to retrieve the title from the - backend. - * url: (str) url of the ticket task or commit. - """ - - def __init__(self, line: str): - self.line = line - items = line.split("|") - self.task_id = None - self.commit_id = None - try: - task_id = int(items[1].strip()[1:]) - self.task_id = task_id - self.ref = f"T{self.task_id}" - except ValueError: - # no task - commit_string = items[3].strip() - commits = commit_string.split(",") - commit_id = commits[0] - commit_id = commit_id.replace("{", "").replace("}", "") - if not commit_id.startswith("rB"): - commit_id = f"rB{commit_id}" - self.commit_id = commit_id - - self.ref = f"{self.commit_id}" - - self.title = "" - self.url = f"https://developer.blender.org/{self.ref}" - - def __format_as_html(self) -> str: - return f"
  • {self.title} [{self.ref}]
  • " - - def __format_as_text(self) -> str: - return f"* {self.title} [{self.ref}]" - - def __format_as_steam(self) -> str: - return f"* {self.title} ([url={self.url}]{self.ref}[/url])" - - def __format_as_wiki(self) -> str: - if self.task_id: - return f"* {self.title} [{{{{BugReport|{self.task_id}}}}}]" - else: - return f"* {self.title} [{{{{GitCommit|{self.commit_id[2:]}}}}}]" - - def format(self, format: str) -> str: - """ - Format this line - - :attr format: the desired format. Possible values are 'text', 'steam' or 'html' - :type string: - """ - if format == 'html': - return self.__format_as_html() - elif format == 'steam': - return self.__format_as_steam() - elif format == 'wiki': - return self.__format_as_wiki() - else: - return self.__format_as_text() - - -def format_title(title: str) -> str: - title = title.strip() - if not title.endswith("."): - title = title + "." - return title - - -def extract_release_notes(version: str, task_id: int): - """ - Extract all release notes logs - - # Process - - 1. Retrieval of description of the given `task_id`. - 2. Find rows for the given `version` and convert to `ReleaseLogLine`. - 3. based on the associated task or commit retrieves the title of the log - line. - """ - phab = phabricator.Phabricator() - phab.update_interfaces() - task = phab.maniphest.info(task_id=task_id) - description = task["description"] - lines = description.split("\n") - start_index = lines.index(f"## Blender {version} ##") - lines = lines[start_index + 1:] - for line in lines: - if not line.strip(): - continue - if line.startswith("| **Report**"): - continue - if line.startswith("## Blender"): - break - - log_line = ReleaseLogLine(line) - if log_line.task_id: - issue_task = phab.maniphest.info(task_id=log_line.task_id) - log_line.title = format_title(issue_task.title) - yield log_line - elif log_line.commit_id: - commits = phab.diffusion.commit.search(constraints={"identifiers": [log_line.commit_id]}) - commit = commits.data[0] - commit_message = commit['fields']['message'] - commit_title = commit_message.split("\n")[0] - log_line.title = format_title(commit_title) - yield log_line - - -def print_release_notes(version: str, format: str, task_id: int): - """ - Generate and print the release notes to the console. - """ - if format == 'html': - print("") - if format == 'steam': - print("[/ul]") - - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description=DESCRIPTION, usage=USAGE) - parser.add_argument( - "--version", - required=True, - help="Version string in the form of {major}.{minor}.{build} (e.g. 2.83.7)") - parser.add_argument( - "--task", - required=True, - help="Phabricator ticket that is contains the release notes information (e.g. T77348)") - parser.add_argument( - "--format", - help="Format the result in `text`, `steam`, `wiki` or `html`", - default="text") - args = parser.parse_args() - - print_release_notes(version=args.version, format=args.format, task_id=int(args.task[1:])) +lts_issue.print_notes(version=version, format=args.format, issue=issue) diff --git a/release/lts/create_download_urls.py b/release/lts/lts_download.py old mode 100755 new mode 100644 similarity index 69% rename from release/lts/create_download_urls.py rename to release/lts/lts_download.py index c6b01acdf60..6d0dd81397b --- a/release/lts/create_download_urls.py +++ b/release/lts/lts_download.py @@ -1,14 +1,9 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: GPL-2.0-or-later -import argparse import datetime -DESCRIPTION = ("This python script is used to generate the download urls " - "which we can copy-paste directly into the CMS of " - "www.blender.org") -USAGE = "create_download_urls --version=2.83.7" # Used date format: "September 30, 2020" DATE_FORMAT = "%B %d, %Y" @@ -62,19 +57,8 @@ def generate_html(version: Version) -> str: return "\n".join(lines) -def print_download_urls(version: Version): +def print_urls(version: str): """ Generate the download urls and print them to the console. """ - print(generate_html(version)) - - -if __name__ == "__main__": - parser = argparse.ArgumentParser(description=DESCRIPTION, usage=USAGE) - parser.add_argument("--version", - required=True, - help=("Version string in the form of {major}.{minor}." - "{build} (eg 2.83.7)")) - args = parser.parse_args() - - print_download_urls(version=Version(args.version)) + print(generate_html(Version(version))) diff --git a/release/lts/lts_issue.py b/release/lts/lts_issue.py new file mode 100644 index 00000000000..f84cfb29b15 --- /dev/null +++ b/release/lts/lts_issue.py @@ -0,0 +1,168 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-or-later + +import requests + +class ReleaseLogLine: + """ + Class containing the information of a single line of the release log + + Instance attributes: + + * line: (str) the original line used to create this log line + * issue_id: (int or None) the extracted issue id associated with this log + line. Can be None if the log line isn't associated with a issue. + * commit_id: (str or None) the extracted commit id associated with this log + line. Only filled when no `issue_id` could be found. + * ref: (str) `issue_id` or `commit_id` of this line, including `T` for issues + or `D` for diffs. + * title: (str) title of this log line. When constructed this attribute is + an empty string. The called needs to retrieve the title from the + backend. + * url: (str) url of the ticket issue or commit. + """ + + def __init__(self, line: str): + self.line = line + items = line.split("|") + self.issue_id = None + self.issue_repo = None + self.commit_id = None + self.commit_repo = None + base_url = "https://projects.blender.org" + try: + issue_tokens = items[1].strip().split("#") + if len(issue_tokens[0]) > 0: + self.issue_repo = issue_tokens[0] + self.issue_id = issue_tokens[1] + else: + self.issue_repo = "blender/blender" + self.issue_id = issue_tokens[1] + + self.ref = f"#{self.issue_id}" + self.url = f"{base_url}/{self.issue_repo}/issues/{self.issue_id}" + except IndexError: + # no issue + commit_string = items[3].strip() + commit_string = commit_string.split(",")[0] + commit_string = commit_string.split("]")[0] + commit_string = commit_string.replace("[", "") + + commit_tokens = commit_string.split("@") + if len(commit_tokens) > 1: + self.commit_repo = commit_tokens[0] + self.commit_id = commit_tokens[1] + else: + self.commit_repo = "blender/blender" + self.commit_id = commit_tokens[0] + + self.ref = f"{self.commit_id}" + self.url = f"{base_url}/{self.commit_repo}/commit/{self.commit_id}" + + self.title = "" + + def __format_as_html(self) -> str: + return f"
  • {self.title} [{self.ref}]
  • " + + def __format_as_text(self) -> str: + return f"* {self.title} [{self.ref}]" + + def __format_as_steam(self) -> str: + return f"* {self.title} ([url={self.url}]{self.ref}[/url])" + + def __format_as_wiki(self) -> str: + if self.issue_id: + return f"* {self.title} [{{{{BugReport|{self.issue_id}}}}}]" + else: + return f"* {self.title} [{{{{GitCommit|{self.commit_id[2:]}}}}}]" + + def format(self, format: str) -> str: + """ + Format this line + + :attr format: the desired format. Possible values are 'text', 'steam' or 'html' + :type string: + """ + if format == 'html': + return self.__format_as_html() + elif format == 'steam': + return self.__format_as_steam() + elif format == 'wiki': + return self.__format_as_wiki() + else: + return self.__format_as_text() + + +def format_title(title: str) -> str: + title = title.strip() + if not title.endswith("."): + title = title + "." + return title + + +def extract_release_notes(version: str, issue: str): + """ + Extract all release notes logs + + # Process + + 1. Retrieval of description of the given `issue_id`. + 2. Find rows for the given `version` and convert to `ReleaseLogLine`. + 3. based on the associated issue or commit retrieves the title of the log + line. + """ + base_url = "https://projects.blender.org/api/v1/repos" + issues_url = base_url + "/blender/blender/issues/" + headers = {'accept': 'application/json'} + + response = requests.get(issues_url + issue[1:], headers=headers) + description = response.json()["body"] + + lines = description.split("\n") + start_index = lines.index(f"## Blender {version}") + lines = lines[start_index + 1:] + for line in lines: + if not line.strip(): + continue + if line.startswith("| **Report**"): + continue + if line.startswith("## Blender"): + break + if line.find("| -- |") != -1: + continue + + log_line = ReleaseLogLine(line) + if log_line.issue_id: + issue_url = f"{base_url}/{log_line.issue_repo}/issues/{log_line.issue_id}" + response = requests.get(issue_url, headers=headers) + if response.status_code != 200: + raise ValueError("Issue not found: " + str(log_line.issue_id)) + + log_line.title = format_title(response.json()["title"]) + yield log_line + elif log_line.commit_id: + commit_url = f"{base_url}/{log_line.commit_repo}/git/commits/{log_line.commit_id}" + response = requests.get(commit_url, headers=headers) + if response.status_code != 200: + raise ValueError("Commit not found: " + log_line.commit_id) + + commit_message = response.json()['commit']['message'] + commit_title = commit_message.split("\n")[0] + log_line.title = format_title(commit_title) + yield log_line + + +def print_notes(version: str, format: str, issue: str): + """ + Generate and print the release notes to the console. + """ + if format == 'html': + print("") + if format == 'steam': + print("[/ul]") diff --git a/release/lts/requirements.txt b/release/lts/requirements.txt index dc91ba85b01..f2293605cf1 100644 --- a/release/lts/requirements.txt +++ b/release/lts/requirements.txt @@ -1 +1 @@ -phabricator==0.7.0 \ No newline at end of file +requests -- 2.30.2