diff --git a/bb b/bb index 8f0cdbd..04e6d08 100755 --- a/bb +++ b/bb @@ -233,6 +233,38 @@ resolve_workspace() { fi } +# Resolve positional args for PR commands of shape `bb [repo] [extras...]`. +# Sets caller-scope: +# repo -- via resolve_repo (caller must `local repo`) +# pr_id -- the PR id (caller must `local pr_id`) +# pr_args_consumed -- 1 or 2 (caller `shift $pr_args_consumed` to reach extras) +# +# Heuristic: if $1 is purely digits, treat it as the id and auto-detect the +# repo from git. Otherwise $1 is the [repo] slot and $2 is the id (the +# existing positional form, unchanged). This makes `bb pr 42` Just Work +# from inside a checkout instead of treating 42 as a repo slug. Mirrors +# the state-recognition heuristic in cmd_pr_list (bb prs MERGED). +# +# Tradeoff: a repo literally named with pure digits (e.g. slug "42") would +# be shadowed by the heuristic. Acceptable: Bitbucket slugs by convention +# are lowercase-hyphenated, pure-digit slugs are vanishingly rare, and the +# explicit "workspace/42" form remains as a clean escape hatch (the +# heuristic only triggers on the bare-digits form). +_resolve_pr_args() { + case "${1:-}" in + ''|*[!0-9]*) + resolve_repo "${1:-}" + pr_id="${2:-}" + pr_args_consumed=2 + ;; + *) + resolve_repo "" + pr_id="$1" + pr_args_consumed=1 + ;; + esac +} + repo_path() { local repo="$1" # Validate inputs at the boundary so a malformed slug doesn't @@ -935,12 +967,11 @@ cmd_pr_list() { } cmd_pr_view() { - local repo - resolve_repo "${1:-}" - local pr_id="${2:-}" + local repo pr_id pr_args_consumed + _resolve_pr_args "$@" if [[ -z "$pr_id" ]]; then - echo "Usage: bb pr-view [repo] " >&2 + echo "Usage: bb pr-view [repo] (or: bb pr from inside a checkout)" >&2 exit 1 fi @@ -1077,9 +1108,8 @@ cmd_pr_create() { } cmd_pr_approve() { - local repo - resolve_repo "${1:-}" - local pr_id="${2:-}" + local repo pr_id pr_args_consumed + _resolve_pr_args "$@" if [[ -z "$pr_id" ]]; then echo "Usage: bb pr-approve [repo] " >&2 @@ -1099,9 +1129,8 @@ cmd_pr_approve() { } cmd_pr_unapprove() { - local repo - resolve_repo "${1:-}" - local pr_id="${2:-}" + local repo pr_id pr_args_consumed + _resolve_pr_args "$@" if [[ -z "$pr_id" ]]; then echo "Usage: bb pr-unapprove [repo] " >&2 @@ -1120,16 +1149,20 @@ cmd_pr_unapprove() { } cmd_pr_merge() { - local repo - resolve_repo "${1:-}" - local pr_id="${2:-}" - local strategy="${3:-merge_commit}" - + local repo pr_id pr_args_consumed + _resolve_pr_args "$@" + # Check usage BEFORE `shift $pr_args_consumed`. If the user passed a + # single non-numeric arg (e.g. `bb pr-merge myrepo` with no id), + # pr_args_consumed=2 but only 1 positional exists, so shift 2 returns + # non-zero, which under `set -euo pipefail` aborts the script before + # the friendly Usage message can print. if [[ -z "$pr_id" ]]; then - echo "Usage: bb pr-merge [repo] [strategy]" >&2 + echo "Usage: bb pr-merge [repo] [strategy] (or: bb pr-merge [strategy] from inside a checkout)" >&2 echo " Strategies: merge_commit (default), squash, fast_forward" >&2 exit 1 fi + shift $pr_args_consumed + local strategy="${1:-merge_commit}" # Validate strategy against Bitbucket's accepted set so a typo # (e.g. "squash_commit") fails locally with a clear message @@ -1168,9 +1201,8 @@ cmd_pr_merge() { } cmd_pr_decline() { - local repo - resolve_repo "${1:-}" - local pr_id="${2:-}" + local repo pr_id pr_args_consumed + _resolve_pr_args "$@" if [[ -z "$pr_id" ]]; then echo "Usage: bb pr-decline [repo] " >&2 @@ -1188,9 +1220,8 @@ cmd_pr_decline() { } cmd_pr_diff() { - local repo - resolve_repo "${1:-}" - local pr_id="${2:-}" + local repo pr_id pr_args_consumed + _resolve_pr_args "$@" if [[ -z "$pr_id" ]]; then echo "Usage: bb pr-diff [repo] " >&2 @@ -1207,9 +1238,8 @@ cmd_pr_diff() { } cmd_pr_comments() { - local repo - resolve_repo "${1:-}" - local pr_id="${2:-}" + local repo pr_id pr_args_consumed + _resolve_pr_args "$@" if [[ -z "$pr_id" ]]; then echo "Usage: bb pr-comments [repo] " >&2 @@ -1231,17 +1261,25 @@ cmd_pr_comments() { } cmd_pr_comment_add() { - local repo - resolve_repo "${1:-}" - local pr_id="${2:-}" - local body="${3:-}" - - if [[ -z "$pr_id" || -z "$body" ]]; then - echo "Usage: bb pr-comment [repo] " >&2 + local repo pr_id pr_args_consumed + _resolve_pr_args "$@" + # Same ordering rule as cmd_pr_merge: check pr_id BEFORE shift to + # avoid `shift 2` aborting under `set -euo pipefail` when the user + # passed only one (non-numeric) arg. + _usage_pr_comment() { + echo "Usage: bb pr-comment [repo] (or: bb pr-comment from inside a checkout)" >&2 echo "" >&2 echo " Add a top-level comment to PR #." >&2 echo " Use single quotes around if it contains spaces or shell metacharacters." >&2 exit 1 + } + if [[ -z "$pr_id" ]]; then + _usage_pr_comment + fi + shift $pr_args_consumed + local body="${1:-}" + if [[ -z "$body" ]]; then + _usage_pr_comment fi # Bitbucket's contract: POST {"content": {"raw": ""}}. @@ -2745,6 +2783,10 @@ GLOBAL FLAGS NOTES [repo] is auto-detected from the current git remote if omitted. + For PR-id commands (pr / pr-merge / pr-diff / pr-comments / pr-approve / + pr-unapprove / pr-decline / pr-comment), a bare numeric first arg is + treated as the PR id and the repo is auto-detected (e.g. `bb pr 42` from + inside the checkout). Pass `workspace/slug ` for an explicit repo. Config: ~/.config/bb/config or env vars BB_USER, BB_TOKEN, BB_WORKSPACE. Auth uses Atlassian API tokens with HTTP Basic auth.