Skip to content
Open
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Top of tree

* Print a summary of the stack's PR URLs at the end of `submit` and `view`. (#50)

# Version 0.1.3

* Fix a bug with replacing $USERNAME in the branch name. (#44)
Expand Down
17 changes: 17 additions & 0 deletions src/stack_pr/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -563,6 +563,19 @@ def print_stack(st: list[StackEntry], *, links: bool, level: int = 1) -> None:
log(" * " + e.pprint(links=links), level=level)


def print_pr_summary(st: list[StackEntry], *, level: int = 1) -> None:
# Print a plain, copy-pasteable list of the stack's PR URLs. Entries without a
# PR are skipped, and nothing is printed at all if the stack has no PRs (e.g.
# a 'view' on a stack that hasn't been exported yet).
if not any(e.has_pr() for e in st):
return
log(b("PRs:"), level=level)
for e in reversed(st):
if not e.has_pr():
continue
log(" * " + e.pr, level=level)


def draft_bitmask_type(value: str) -> list[bool]:
# Validate that only 0s and 1s are present
if value and not set(value).issubset({"0", "1"}):
Expand Down Expand Up @@ -1157,6 +1170,9 @@ def command_submit(

delete_local_branches(st, verbose=args.verbose)
print_tips_after_export(st, args)
# Printed unconditionally (not gated behind --show-tips): the PR URLs are core
# output, not a usage hint.
print_pr_summary(st)
log(h(blue("SUCCESS!")), level=1)


Expand Down Expand Up @@ -1484,6 +1500,7 @@ def command_view(args: CommonArgs) -> None:
set_base_branches(st, target=args.target)
print_stack(st, links=args.hyperlinks)
print_tips_after_view(st, args)
print_pr_summary(st)
log(h(blue("SUCCESS!")))


Expand Down
42 changes: 42 additions & 0 deletions tests/test_misc.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

import sys
import tempfile
from pathlib import Path
Expand All @@ -7,11 +9,14 @@
import pytest

from stack_pr.cli import (
CommitHeader,
StackEntry,
generate_available_branch_name,
generate_branch_name,
get_branch_id,
get_gh_username,
get_taken_branch_ids,
print_pr_summary,
)
from stack_pr.git import git_config, is_rebase_in_progress

Expand Down Expand Up @@ -123,3 +128,40 @@ def test_is_rebase_in_progress() -> None:
# Test with None repo_dir (current directory)
# This should not raise an error even if .git doesn't exist in cwd
assert not is_rebase_in_progress(None)


def _entry(pr: str | None) -> StackEntry:
# print_pr_summary only reads .has_pr()/.pr, so an empty commit header is fine.
return StackEntry(CommitHeader(""), _pr=pr)


def test_print_pr_summary_lists_prs_top_of_stack_first(
capsys: pytest.CaptureFixture[str],
) -> None:
st = [
_entry("https://github.com/o/r/pull/1"),
_entry("https://github.com/o/r/pull/2"),
]
print_pr_summary(st)
out = capsys.readouterr().out
assert "https://github.com/o/r/pull/1" in out
assert "https://github.com/o/r/pull/2" in out
# Ordering matches print_stack (top of the stack, i.e. the last entry, first).
assert out.index("pull/2") < out.index("pull/1")


def test_print_pr_summary_skips_entries_without_a_pr(
capsys: pytest.CaptureFixture[str],
) -> None:
st = [_entry("https://github.com/o/r/pull/1"), _entry(None)]
print_pr_summary(st)
out = capsys.readouterr().out
assert "https://github.com/o/r/pull/1" in out
assert out.count("http") == 1


def test_print_pr_summary_prints_nothing_when_no_prs(
capsys: pytest.CaptureFixture[str],
) -> None:
print_pr_summary([_entry(None), _entry(None)])
assert capsys.readouterr().out == ""