Add redbot-update command for updating Red (#6734)

This commit is contained in:
Jakub Kuczys
2026-05-14 00:14:43 +02:00
committed by GitHub
parent 899f24ceca
commit 7e2a74b276
22 changed files with 3016 additions and 76 deletions
+129
View File
@@ -0,0 +1,129 @@
import enum
from rich.text import Text
from textual.app import App, ComposeResult
from textual.binding import Binding
from textual.events import Click
from textual.widgets import Footer, Markdown, MarkdownViewer, Static
from typing_extensions import Self
from .changelog import Changelogs
# See https://github.com/Textualize/textual/discussions/6449
class MarkdownLinkTooltip(Static, inherit_css=False):
DEFAULT_CSS = """
MarkdownLinkTooltip {
layer: _tooltips;
margin: 1 0;
padding: 1 2;
background: $panel;
width: auto;
height: auto;
constrain: inside inflect;
max-width: 40;
display: none;
offset-x: -50%;
}
"""
class _MarkdownViewer(MarkdownViewer):
DEFAULT_CSS = """
_MarkdownViewer {
layers: default _tooltips;
}
"""
def compose(self) -> ComposeResult:
yield from super().compose()
yield MarkdownLinkTooltip()
def on_markdown_link_clicked(self, message: Markdown.LinkClicked) -> None:
# We don't want the default behavior of opening the browser/navigating to a file on click.
message.prevent_default()
tooltip = self.get_child_by_type(MarkdownLinkTooltip)
tooltip.display = True
# You can't cycle over the links in MarkdownViewer (see Textualize/textual#3555)
# so using mouse position is fine.
# Textualize/textual#3555: https://github.com/Textualize/textual/discussions/3555
tooltip.absolute_offset = self.app.mouse_position
# For some reason, links only render correctly when Text has a span over the whole text
# with a link but not when Text just has a style applied to it directly, i.e.:
# Text(message.href, style=f"link {message.href}")
# will not work.
tooltip.update(Text().append(message.href, style=f"link {message.href}"))
def on_click(self, message: Click) -> None:
tooltip = self.get_child_by_type(MarkdownLinkTooltip)
tooltip.display = False
class ChangelogReaderResult(enum.Enum):
QUIT = enum.auto()
CONTINUE = enum.auto()
class ChangelogReaderApp(App[ChangelogReaderResult], inherit_bindings=False):
ENABLE_COMMAND_PALETTE = False
BINDINGS = [
Binding(key="ctrl+c", action="quit", description="Exit redbot-update"),
Binding(key="q", action="continue", description="Finish reading the changelog"),
]
def __init__(self, markdown_content: str) -> None:
self.markdown_content = markdown_content
super().__init__()
@classmethod
def from_changelogs(cls, changelogs: Changelogs) -> Self:
if not changelogs:
return cls("")
parts = []
contributors = sorted(
{
contributor
for changelog in changelogs.values()
for contributor in changelog.contributors
}
)
if contributors:
contributor_thanks = (
"# Thanks to our contributors \N{HEAVY BLACK HEART}\N{VARIATION SELECTOR-16}\n"
"**The releases below were made with help from the following people:** \n"
)
contributor_thanks += ", ".join(
f"[@{contributor}](https://github.com/sponsors/{contributor})"
for contributor in contributors
)
parts.append(contributor_thanks)
parts.append("# Read before updating")
for changelog in reversed(changelogs.values()):
if changelog.read_before_updating_section:
parts.append(f"## {changelog.version}")
parts.append(changelog.read_before_updating_section)
parts.append("# User changelog")
for changelog in reversed(changelogs.values()):
if changelog.user_changelog_section:
parts.append(f"## {changelog.version}")
parts.append(changelog.user_changelog_section)
return cls("\n".join(parts))
def compose(self) -> ComposeResult:
markdown_viewer = _MarkdownViewer(
self.markdown_content, show_table_of_contents=True, open_links=False
)
markdown_viewer.code_indent_guides = False
yield markdown_viewer
yield Footer()
def action_quit(self) -> None:
self.exit(ChangelogReaderResult.QUIT)
def action_continue(self) -> None:
self.exit(ChangelogReaderResult.CONTINUE)