diff --git a/CHANGES.rst b/CHANGES.rst index c1cdddd965f..ab0de5e6cfd 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,5 +1,38 @@ .. Red changelogs +Redbot 3.5.5 (2023-08-14) +========================= + +| Thanks to all these amazing people that contributed to this release: +| :ghuser:`Flame442`, :ghuser:`Jackenmen`, :ghuser:`karlsbjorn`, :ghuser:`Kreusada`, :ghuser:`ltzmax`, :ghuser:`palmtree5` + +End-user changelog +------------------ + +Changes +******* + +- **Core - Dependencies** - Red's dependencies have been bumped (:issue:`6248`) +- **Cogs - Downloader** - Cogs in the ``[p]cog list`` command are now listed alphabetically (:issue:`6214`, :issue:`6215`) + +Fixes +***** + +- **Core - Bot Commands** - Fixed handling of an edge case in the ``[p]diagnoseissues`` command that involved commands without a cog (:issue:`6237`) +- **Core - Bot Commands** - Fixed the formatting of nested result lists in the ``[p]diagnoseissues`` command (:issue:`6238`) +- **Cogs - Mod** - Fixed the formatting of the help description for the ``[p]ban``, ``[p]kick``, and ``[p]tempban`` commands (:issue:`6245`) +- |cool| **Cogs - Streams** - Updated the implementation of Twitch streams to no longer use the "Get Users Follows" endpoint that was deprecated in February 2023 (:issue:`6246`, :issue:`6247`) + +Documentation changes +--------------------- + +Changes +******* + +- Updated Python version in ``pyenv`` instructions (:issue:`6241`) + +---- + Redbot 3.5.4 (2023-08-12) ========================= diff --git a/tools/release_helper.py b/tools/release_helper.py index 42957bcfcd1..b6334823f63 100755 --- a/tools/release_helper.py +++ b/tools/release_helper.py @@ -12,9 +12,10 @@ import shlex import subprocess import time +import urllib.parse import webbrowser from collections import defaultdict -from typing import Dict, List, Optional +from typing import Dict, List, Optional, Tuple import click import requests @@ -29,7 +30,7 @@ class ReleaseType(enum.Enum): MAINTENANCE = 3 HOTFIX = 4 - def __str__(self) -> None: + def __str__(self) -> str: return f"{self.name.lower()} release" @classmethod @@ -105,9 +106,7 @@ def from_str(cls, name: str) -> Self: nodes { name target { - ... on Commit { - oid - } + commitResourcePath } } } @@ -144,6 +143,27 @@ def from_str(cls, name: str) -> Self: } } """ +GET_LAST_ISSUE_NUMBER_QUERY = """ +query getLastIssueNumber { + repository(owner: "Cog-Creators", name: "Red-DiscordBot") { + discussions(orderBy: {field: CREATED_AT, direction: DESC}, first: 1) { + nodes { + number + } + } + issues(orderBy: {field: CREATED_AT, direction: DESC}, first: 1) { + nodes { + number + } + } + pullRequests(orderBy: {field: CREATED_AT, direction: DESC}, first: 1) { + nodes { + number + } + } + } +} +""" GH_URL = "https://github.com/Cog-Creators/Red-DiscordBot" LINKIFY_ISSUE_REFS_RE = re.compile(r"#(\d+)") @@ -228,11 +248,11 @@ def get_git_config_value(key: str) -> str: return "" -def set_git_config_value(key: str, value: str) -> str: +def set_git_config_value(key: str, value: str) -> None: subprocess.check_call(("git", "config", "--local", f"red-release-helper.{key}", value)) -def wipe_git_config_values() -> str: +def wipe_git_config_values() -> None: try: subprocess.check_output( ("git", "config", "--local", "--remove-section", "red-release-helper") @@ -304,7 +324,7 @@ def set_release_stage(stage: ReleaseStage) -> None: @click.group(invoke_without_command=True) @click.option("--continue", "abort", flag_value=False, default=None) @click.option("--abort", "abort", flag_value=True, default=None) -def cli(*, abort: bool = None): +def cli(*, abort: Optional[bool] = None): """Red's release helper, guiding you through the whole process!""" stage = get_release_stage() if abort is True: @@ -516,40 +536,46 @@ def create_changelog(release_type: ReleaseType, version: str) -> None: else: changelog_branch = f"V3/changelogs/{version}" subprocess.check_call(("git", "fetch", GH_URL)) - try: - subprocess.check_call(("git", "checkout", "-b", changelog_branch, "FETCH_HEAD")) - except subprocess.CalledProcessError: - rich.print() - if click.confirm( - f"It seems that {changelog_branch} branch already exists, do you want to use it?" - ): - subprocess.check_call(("git", "checkout", changelog_branch)) - elif not click.confirm("Do you want to use a different branch?"): - raise click.ClickException("Can't continue without a changelog branch...") - elif click.confirm("Do you want to create a new branch?"): - while True: - changelog_branch = click.prompt("Input the name of the new branch") - try: - subprocess.check_call( - ("git", "checkout", "-b", changelog_branch, "FETCH_HEAD") - ) - except subprocess.CalledProcessError: - continue - else: - break - else: - while True: - changelog_branch = click.prompt("Input the name of the branch to check out") - try: - subprocess.check_call(("git", "checkout", changelog_branch)) - except subprocess.CalledProcessError: - continue - else: - break - - set_changelog_branch(changelog_branch) - set_release_stage(ReleaseStage.CHANGELOG_BRANCH_EXISTS) - + try: + subprocess.check_call(("git", "checkout", "-b", changelog_branch, "FETCH_HEAD")) + except subprocess.CalledProcessError: + rich.print() + if click.confirm( + f"It seems that {changelog_branch} branch already exists, do you want to use it?" + ): + subprocess.check_call(("git", "checkout", changelog_branch)) + elif not click.confirm("Do you want to use a different branch?"): + raise click.ClickException("Can't continue without a changelog branch...") + elif click.confirm("Do you want to create a new branch?"): + while True: + changelog_branch = click.prompt("Input the name of the new branch") + try: + subprocess.check_call( + ("git", "checkout", "-b", changelog_branch, "FETCH_HEAD") + ) + except subprocess.CalledProcessError: + continue + else: + break + else: + while True: + changelog_branch = click.prompt("Input the name of the branch to check out") + try: + subprocess.check_call(("git", "checkout", changelog_branch)) + except subprocess.CalledProcessError: + continue + else: + break + + set_changelog_branch(changelog_branch) + set_release_stage(ReleaseStage.CHANGELOG_BRANCH_EXISTS) + + title = f"Red {version} - Changelog" + commands = [ + ("git", "add", "."), + ("git", "commit", "-m", title), + ("git", "push", "-u", GH_URL, f"{changelog_branch}:{changelog_branch}"), + ] if get_release_stage() < ReleaseStage.CHANGELOG_COMMITTED: rich.print( "\n:pencil: At this point, you should have an up-to-date milestone" @@ -578,11 +604,6 @@ def create_changelog(release_type: ReleaseType, version: str) -> None: if option == "4": break - commands = [ - ("git", "add", "."), - ("git", "commit", "-m", f"Red {version} - Changelog"), - ("git", "push", "-u", GH_URL, f"{changelog_branch}:{changelog_branch}"), - ] print( "Do you want to commit everything from repo's working tree and push it?" " The following commands will run:" @@ -596,10 +617,37 @@ def create_changelog(release_type: ReleaseType, version: str) -> None: else: print("Okay, please open a changelog PR manually then.") if get_release_stage() is ReleaseStage.CHANGELOG_COMMITTED: + token = get_github_token() + resp = requests.post( + "https://api.github.com/graphql", + json={"query": GET_LAST_ISSUE_NUMBER_QUERY}, + headers={"Authorization": f"Bearer {token}"}, + ) + next_issue_number = ( + max( + next(iter(data["nodes"]), {"number": 0})["number"] + for data in resp.json()["data"]["repository"].values() + ) + + 1 + ) + docs_preview_url = ( + f"https://red-discordbot--{next_issue_number}.org.readthedocs.build" + f"/en/{next_issue_number}/changelog.html" + ) subprocess.check_call(commands[2]) + query = { + "expand": "1", + "milestone": version, + "labels": "Type: Feature,Changelog Entry: Skipped", + "title": title, + "body": ( + "### Description of the changes\n\n" + f"The PR for Red {version} changelog.\n\n" + f"Docs preview: {docs_preview_url}" + ), + } pr_url = ( - f"{GH_URL}/compare/V3/develop...{changelog_branch}" - f"?expand=1&milestone={version}&labels=Type:+Feature" + f"{GH_URL}/compare/V3/develop...{changelog_branch}?{urllib.parse.urlencode(query)}" ) print(f"Create new PR: {pr_url}") webbrowser.open_new_tab(pr_url) @@ -782,11 +830,11 @@ def create_short_lived_branch(release_type: ReleaseType, version: str) -> None: @cli.command(name="unreleased") @click.argument("version") @click.argument("base_branch") -def cli_unreleased(version: str, base_branch: str) -> int: +def cli_unreleased(version: str, base_branch: str) -> None: show_unreleased_commits(version, base_branch) -def show_unreleased_commits(version: str, base_branch: str) -> int: +def show_unreleased_commits(version: str, base_branch: str) -> None: token = get_github_token() resp = requests.post( @@ -796,7 +844,8 @@ def show_unreleased_commits(version: str, base_branch: str) -> int: ) json = resp.json() tag_commits = { - node["target"]["oid"]: node["name"] for node in json["data"]["repository"]["refs"]["nodes"] + node["target"]["commitResourcePath"].rsplit("/", 1)[-1]: node["name"] + for node in json["data"]["repository"]["refs"]["nodes"] } after = None @@ -868,7 +917,7 @@ def cli_milestone(version: str) -> None: def view_milestone_issues(version: str) -> None: - issue_views = [] + issue_views: List[str] = [] for issue_type in ("pr", "issue"): for number in subprocess.check_output( ( @@ -916,8 +965,8 @@ def get_contributors(version: str, *, show_not_merged: bool = False) -> None: def _get_contributors(version: str, *, show_not_merged: bool = False) -> List[str]: after = None has_next_page = True - authors = {} - reviewers = {} + authors: Dict[str, List[Tuple[int, str]]] = {} + reviewers: Dict[str, List[Tuple[int, str]]] = {} token = get_github_token() states = ["MERGED"] if show_not_merged: @@ -940,7 +989,6 @@ def _get_contributors(version: str, *, show_not_merged: bool = False) -> List[st milestone_data = json["data"]["repository"]["milestones"]["nodes"][0] except IndexError: raise click.ClickException("Given milestone couldn't have been found.") - milestone_title = milestone_data["title"] pull_requests = milestone_data["pullRequests"] nodes = pull_requests["nodes"] for pr_node in nodes: