From 77b34153529cda1a40a52bba033466bc93363472 Mon Sep 17 00:00:00 2001 From: Ramesh Padmanabhaiah Date: Mon, 6 Jul 2026 17:50:34 -0700 Subject: [PATCH] Consume shared GitHub Bash helpers --- .github/workflows/tests.yml | 8 +- cli/bash/commands/basectl/subcommands/gh.sh | 100 +++--------------- cli/bash/commands/basectl/subcommands/repo.sh | 59 ++--------- cli/bash/commands/basectl/tests/gh.bats | 65 ++++++++++++ cli/bash/commands/basectl/tests/repo.bats | 33 ++++++ 5 files changed, 126 insertions(+), 139 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d588b5c..91ff27d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -57,7 +57,7 @@ jobs: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 with: repository: basefoundry/base-bash-libs - ref: 8bcc1d2c1104ffa6c8bb4a95b3f328811401bf27 + ref: d62e8c1fec2de9148bc832977c206b9f1827dd19 path: .dependencies/base-bash-libs - name: Install BATS @@ -91,7 +91,7 @@ jobs: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 with: repository: basefoundry/base-bash-libs - ref: 8bcc1d2c1104ffa6c8bb4a95b3f328811401bf27 + ref: d62e8c1fec2de9148bc832977c206b9f1827dd19 path: .dependencies/base-bash-libs - name: Set up Python @@ -167,7 +167,7 @@ jobs: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 with: repository: basefoundry/base-bash-libs - ref: 8bcc1d2c1104ffa6c8bb4a95b3f328811401bf27 + ref: d62e8c1fec2de9148bc832977c206b9f1827dd19 path: .dependencies/base-bash-libs - name: Set up Python @@ -210,7 +210,7 @@ jobs: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 with: repository: basefoundry/base-bash-libs - ref: 8bcc1d2c1104ffa6c8bb4a95b3f328811401bf27 + ref: d62e8c1fec2de9148bc832977c206b9f1827dd19 path: .dependencies/base-bash-libs - name: Expose reusable Bash library checkout as sibling diff --git a/cli/bash/commands/basectl/subcommands/gh.sh b/cli/bash/commands/basectl/subcommands/gh.sh index b2d6361..435276f 100644 --- a/cli/bash/commands/basectl/subcommands/gh.sh +++ b/cli/bash/commands/basectl/subcommands/gh.sh @@ -249,23 +249,11 @@ base_gh_require_git_repo() { } base_gh_default_branch() { - local default_branch + local base_default_branch repo_root - if default_branch="$(git symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null)"; then - default_branch="${default_branch#origin/}" - [[ -n "$default_branch" ]] && { - printf '%s\n' "$default_branch" - return 0 - } - fi - - if git show-ref --verify --quiet refs/heads/main; then - printf '%s\n' main - return 0 - fi - - if git show-ref --verify --quiet refs/heads/master; then - printf '%s\n' master + repo_root="$(git rev-parse --show-toplevel 2>/dev/null || printf '.')" + if gh_detect_default_branch "$repo_root" base_default_branch; then + printf '%s\n' "$base_default_branch" return 0 fi @@ -373,30 +361,12 @@ base_gh_pr_changed_paths() { } base_gh_infer_github_repo() { - local remote_url + local github_repo repo_root - remote_url="$(git remote get-url origin 2>/dev/null || true)" - [[ -n "$remote_url" ]] || return 1 - - case "$remote_url" in - git@github.com:*.git) - remote_url="${remote_url#git@github.com:}" - remote_url="${remote_url%.git}" - ;; - https://github.com/*.git) - remote_url="${remote_url#https://github.com/}" - remote_url="${remote_url%.git}" - ;; - https://github.com/*) - remote_url="${remote_url#https://github.com/}" - ;; - *) - return 1 - ;; - esac + repo_root="$(git rev-parse --show-toplevel 2>/dev/null || printf '.')" + gh_infer_repo_from_origin "$repo_root" github_repo || return 1 - [[ "$remote_url" == */* ]] || return 1 - printf '%s\n' "$remote_url" + printf '%s\n' "$github_repo" } base_gh_default_project_title() { @@ -977,37 +947,21 @@ base_gh_format_unix_date() { base_gh_worktree_path_for_branch() { local branch="$1" - local target_ref="refs/heads/$branch" - local line path="" ref - while IFS= read -r line; do - case "$line" in - "worktree "*) - path="${line#worktree }" - ;; - "branch "*) - ref="${line#branch }" - if [[ "$ref" == "$target_ref" ]]; then - printf '%s\n' "$path" - return 0 - fi - ;; - esac - done < <(git worktree list --porcelain) - - return 1 + gh_worktree_path_for_branch "$branch" } base_gh_branch_upstream() { local branch="$1" - git for-each-ref --format='%(upstream:short)' "refs/heads/$branch" + + gh_branch_upstream . "$branch" } base_gh_branch_merged_to_ref() { local branch="$1" local ref="$2" - git merge-base --is-ancestor "refs/heads/$branch" "$ref" >/dev/null 2>&1 + gh_branch_merged_to_ref . "$branch" "$ref" } base_gh_prune_github_ready() { @@ -1066,13 +1020,7 @@ base_gh_branch_delete() { } base_gh_list_remote_branches() { - local output ref - - output="$(git ls-remote --heads origin)" || return 1 - while read -r _sha ref; do - [[ "$ref" == refs/heads/* ]] || continue - printf '%s\n' "${ref#refs/heads/}" - done <<< "$output" + gh_list_remote_branches . } base_gh_branch_delete_remote() { @@ -1291,27 +1239,7 @@ base_gh_resolve_physical_path() { } base_gh_list_worktree_branches() { - local line path="" branch="" - - while IFS= read -r line; do - if [[ -z "$line" ]]; then - if [[ -n "$path" && -n "$branch" ]]; then - branch="${branch#refs/heads/}" - printf '%s\t%s\n' "$path" "$branch" - fi - path="" - branch="" - continue - fi - case "$line" in - "worktree "*) - path="${line#worktree }" - ;; - "branch "*) - branch="${line#branch }" - ;; - esac - done < <(git worktree list --porcelain; printf '\n') + gh_list_worktree_branches . } base_gh_worktree_dirty() { diff --git a/cli/bash/commands/basectl/subcommands/repo.sh b/cli/bash/commands/basectl/subcommands/repo.sh index ea93ba9..69d489e 100644 --- a/cli/bash/commands/basectl/subcommands/repo.sh +++ b/cli/bash/commands/basectl/subcommands/repo.sh @@ -1174,33 +1174,11 @@ base_repo_write_baseline() { base_repo_infer_github_repo() { local path="$1" - local remote_url + local github_repo - remote_url="$(git -C "$path" remote get-url origin 2>/dev/null || true)" - [[ -n "$remote_url" ]] || return 1 + gh_infer_repo_from_origin "$path" github_repo || return 1 - case "$remote_url" in - git@github.com:*.git) - remote_url="${remote_url#git@github.com:}" - remote_url="${remote_url%.git}" - ;; - git@github.com:*) - remote_url="${remote_url#git@github.com:}" - ;; - https://github.com/*.git) - remote_url="${remote_url#https://github.com/}" - remote_url="${remote_url%.git}" - ;; - https://github.com/*) - remote_url="${remote_url#https://github.com/}" - ;; - *) - return 1 - ;; - esac - - [[ "$remote_url" == */* ]] || return 1 - printf '%s\n' "$remote_url" + printf '%s\n' "$github_repo" } base_repo_require_gh() { @@ -1688,41 +1666,24 @@ base_repo_require_pr_worktree() { } base_repo_default_branch_for_pr() { - local default_branch + local base_remote_default_branch local repo="$1" base_repo_require_gh || return 1 - default_branch="$(gh repo view "$repo" --json defaultBranchRef --jq .defaultBranchRef.name 2>/dev/null)" || { + if ! gh_repo_default_branch "$repo" base_remote_default_branch; then log_error "Unable to determine the default branch for GitHub repository '$repo'." return 1 - } - [[ -n "$default_branch" ]] || { - log_error "GitHub repository '$repo' does not report a default branch." - return 1 - } + fi - printf '%s\n' "$default_branch" + printf '%s\n' "$base_remote_default_branch" } base_repo_detect_default_branch() { - local default_branch + local base_default_branch local root="$1" - if default_branch="$(git -C "$root" symbolic-ref --quiet --short refs/remotes/origin/HEAD 2>/dev/null)"; then - default_branch="${default_branch#origin/}" - if [[ -n "$default_branch" ]]; then - printf '%s\n' "$default_branch" - return 0 - fi - fi - - if git -C "$root" show-ref --verify --quiet refs/heads/main; then - printf '%s\n' main - return 0 - fi - - if git -C "$root" show-ref --verify --quiet refs/heads/master; then - printf '%s\n' master + if gh_detect_default_branch "$root" base_default_branch; then + printf '%s\n' "$base_default_branch" return 0 fi diff --git a/cli/bash/commands/basectl/tests/gh.bats b/cli/bash/commands/basectl/tests/gh.bats index 7d8cc91..47e00d1 100644 --- a/cli/bash/commands/basectl/tests/gh.bats +++ b/cli/bash/commands/basectl/tests/gh.bats @@ -46,11 +46,76 @@ run_gh_subcommand() { [[ "$(type -t gh_require_cli)" == "function" ]] [[ "$(type -t gh_auth_status_diagnostics)" == "function" ]] [[ "$(type -t gh_run)" == "function" ]] + [[ "$(type -t gh_detect_default_branch)" == "function" ]] + [[ "$(type -t gh_infer_repo_from_origin)" == "function" ]] + [[ "$(type -t gh_worktree_path_for_branch)" == "function" ]] + [[ "$(type -t gh_branch_merged_to_ref)" == "function" ]] ' [ "$status" -eq 0 ] } +@test "basectl gh default branch and repo inference delegate to reusable gh helpers" { + run env \ + HOME="$TEST_HOME" \ + BASE_HOME="$BASE_REPO_ROOT" \ + bash -c ' + source "$BASE_HOME/base_init.sh" + source "$BASE_HOME/cli/bash/commands/basectl/subcommands/gh.sh" + gh_detect_default_branch() { + printf -v "$2" "%s" "develop" + } + gh_infer_repo_from_origin() { + printf -v "$2" "%s" "owner/repo" + } + printf "default=%s\n" "$(base_gh_default_branch)" + printf "repo=%s\n" "$(base_gh_infer_github_repo)" + ' + + [ "$status" -eq 0 ] + [[ "$output" == *"default=develop"* ]] + [[ "$output" == *"repo=owner/repo"* ]] +} + +@test "basectl gh branch and worktree primitives delegate to reusable gh helpers" { + run env \ + HOME="$TEST_HOME" \ + BASE_HOME="$BASE_REPO_ROOT" \ + bash -c ' + source "$BASE_HOME/base_init.sh" + source "$BASE_HOME/cli/bash/commands/basectl/subcommands/gh.sh" + gh_worktree_path_for_branch() { + printf "%s\n" "/tmp/shared-worktree" + } + gh_branch_upstream() { + printf "%s\n" "origin/feature" + } + gh_branch_merged_to_ref() { + [[ "$2" == "feature" && "$3" == "main" ]] + } + gh_list_remote_branches() { + printf "%s\n" "main" "feature" + } + gh_list_worktree_branches() { + printf "%s\t%s\n" "/tmp/shared-worktree" "feature" + } + remote_branches="$(base_gh_list_remote_branches)" + printf "worktree=%s\n" "$(base_gh_worktree_path_for_branch feature)" + printf "upstream=%s\n" "$(base_gh_branch_upstream feature)" + base_gh_branch_merged_to_ref feature main + printf "merged=$?\n" + printf "remote=%s\n" "${remote_branches//$'\''\n'\''/,}" + printf "worktrees=%s\n" "$(base_gh_list_worktree_branches)" + ' + + [ "$status" -eq 0 ] + [[ "$output" == *"worktree=/tmp/shared-worktree"* ]] + [[ "$output" == *"upstream=origin/feature"* ]] + [[ "$output" == *"merged=0"* ]] + [[ "$output" == *"remote=main,feature"* ]] + [[ "$output" == *$'worktrees=/tmp/shared-worktree\tfeature'* ]] +} + @test "basectl gh joins CSV output through reusable string helper" { run env \ HOME="$TEST_HOME" \ diff --git a/cli/bash/commands/basectl/tests/repo.bats b/cli/bash/commands/basectl/tests/repo.bats index b2163ea..6433720 100644 --- a/cli/bash/commands/basectl/tests/repo.bats +++ b/cli/bash/commands/basectl/tests/repo.bats @@ -116,11 +116,44 @@ run_repo_command_with_mocks() { [[ "$(type -t gh_require_cli)" == "function" ]] [[ "$(type -t gh_auth_status_diagnostics)" == "function" ]] [[ "$(type -t gh_run)" == "function" ]] + [[ "$(type -t gh_infer_repo_from_origin)" == "function" ]] + [[ "$(type -t gh_detect_default_branch)" == "function" ]] + [[ "$(type -t gh_repo_default_branch)" == "function" ]] ' [ "$status" -eq 0 ] } +@test "basectl repo GitHub helpers delegate to reusable gh helpers" { + run env \ + HOME="$TEST_HOME" \ + BASE_HOME="$BASE_REPO_ROOT" \ + bash -c ' + source "$BASE_HOME/base_init.sh" + source "$BASE_HOME/cli/bash/commands/basectl/subcommands/repo.sh" + base_repo_require_gh() { + return 0 + } + gh_infer_repo_from_origin() { + printf -v "$2" "%s" "owner/repo" + } + gh_detect_default_branch() { + printf -v "$2" "%s" "develop" + } + gh_repo_default_branch() { + printf -v "$2" "%s" "trunk" + } + printf "repo=%s\n" "$(base_repo_infer_github_repo /tmp/repo)" + printf "detected=%s\n" "$(base_repo_detect_default_branch /tmp/repo)" + printf "remote=%s\n" "$(base_repo_default_branch_for_pr owner/repo)" + ' + + [ "$status" -eq 0 ] + [[ "$output" == *"repo=owner/repo"* ]] + [[ "$output" == *"detected=develop"* ]] + [[ "$output" == *"remote=trunk"* ]] +} + @test "basectl repo prints help" { run_basectl repo --help