From 3c64fbf405747cba7c4e6714205d06e27163e3c9 Mon Sep 17 00:00:00 2001 From: Brandon Pugsley Date: Mon, 11 May 2026 18:03:08 -0400 Subject: [PATCH] fix(v1.27 migration): qualify repo with owner so gh rename actually runs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `gh repo rename --repo` and `gh repo edit` both require the "[HOST/]OWNER/REPO" form. The v1.27.0.0 migration was passing a bare repo name (e.g. `gstack-brain-Brandon`), which `gh` rejects with: expected the "[HOST/]OWNER/REPO" format, got "gstack-brain-Brandon" Both invocations suppressed stderr with `2>/dev/null`, so the failure surfaced only as the generic "repo may not exist or permission denied" warning — misdirecting users who actually have ADMIN on the repo. The step was then `mark_done`'d, so re-running the upgrade does not retry. The local pointer at ~/.gstack-artifacts-remote.txt ends up referencing a non-existent repo until the user notices. Reproduction (before fix): $ gh repo rename gstack-artifacts-Brandon --repo gstack-brain-Brandon --yes expected the "[HOST/]OWNER/REPO" format, got "gstack-brain-Brandon" Fix: resolve the authenticated user's login via `gh api user --jq .login` and qualify both the existing-repo check (`gh repo view`) and the rename/edit calls. Capture stderr from each attempt so the warning includes the real `gh` error instead of a generic guess. The fake-gh test fixture grows a single case for `gh api user` so the existing test matrix continues to drive the rename path. Co-Authored-By: Claude Opus 4.7 --- gstack-upgrade/migrations/v1.27.0.0.sh | 41 ++++++++++++++++++++------ test/migrations-v1.27.0.0.test.ts | 11 ++++++- 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/gstack-upgrade/migrations/v1.27.0.0.sh b/gstack-upgrade/migrations/v1.27.0.0.sh index fb1ce73ce8..f187a35b2e 100755 --- a/gstack-upgrade/migrations/v1.27.0.0.sh +++ b/gstack-upgrade/migrations/v1.27.0.0.sh @@ -184,19 +184,42 @@ if ! journal_done "gh_repo_renamed"; then case "$HOST" in github) if command -v gh >/dev/null 2>&1 && gh auth status >/dev/null 2>&1; then - # Idempotent: if new name already exists, treat as success. - if gh repo view "$NEW_REPO_NAME" >/dev/null 2>&1; then - echo " repo already named $NEW_REPO_NAME on GitHub — no-op" >&2 + # `gh repo rename --repo` and `gh repo edit` both require the + # "[HOST/]OWNER/REPO" form. Passing a bare repo name (as earlier + # versions of this migration did) fails with: + # expected the "[HOST/]OWNER/REPO" format, got "" + # Resolve the authenticated user's login and qualify both names. + GH_OWNER=$(gh api user --jq .login 2>/dev/null || echo "") + if [ -z "$GH_OWNER" ]; then + echo " WARNING: could not resolve gh authenticated user — skipping rename" >&2 + echo " manual: gh repo rename $NEW_REPO_NAME --repo /$OLD_REPO_NAME --yes" >&2 mark_done "gh_repo_renamed" else - if gh repo rename "$NEW_REPO_NAME" --repo "$OLD_REPO_NAME" --yes 2>/dev/null \ - || gh repo edit "$OLD_REPO_NAME" --name "$NEW_REPO_NAME" 2>/dev/null; then - echo " renamed on GitHub" >&2 + OLD_QUALIFIED="${GH_OWNER}/${OLD_REPO_NAME}" + NEW_QUALIFIED="${GH_OWNER}/${NEW_REPO_NAME}" + # Idempotent: if new name already exists, treat as success. + if gh repo view "$NEW_QUALIFIED" >/dev/null 2>&1; then + echo " repo already named $NEW_REPO_NAME on GitHub — no-op" >&2 mark_done "gh_repo_renamed" else - echo " WARNING: gh rename failed (repo may not exist or permission denied)" >&2 - echo " skipping step 1; subsequent steps still run" >&2 - mark_done "gh_repo_renamed" + RENAME_ERR="" + RENAME_ERR2="" + RENAME_OK=0 + if RENAME_ERR=$(gh repo rename "$NEW_REPO_NAME" --repo "$OLD_QUALIFIED" --yes 2>&1); then + RENAME_OK=1 + elif RENAME_ERR2=$(gh repo edit "$OLD_QUALIFIED" --name "$NEW_REPO_NAME" 2>&1); then + RENAME_OK=1 + fi + if [ "$RENAME_OK" = "1" ]; then + echo " renamed on GitHub" >&2 + mark_done "gh_repo_renamed" + else + echo " WARNING: gh rename failed for $OLD_QUALIFIED" >&2 + [ -n "$RENAME_ERR" ] && echo " gh repo rename: $RENAME_ERR" >&2 + [ -n "$RENAME_ERR2" ] && echo " gh repo edit: $RENAME_ERR2" >&2 + echo " skipping step 1; subsequent steps still run" >&2 + mark_done "gh_repo_renamed" + fi fi fi else diff --git a/test/migrations-v1.27.0.0.test.ts b/test/migrations-v1.27.0.0.test.ts index 7a1a9908cc..1baa9097ba 100644 --- a/test/migrations-v1.27.0.0.test.ts +++ b/test/migrations-v1.27.0.0.test.ts @@ -27,11 +27,20 @@ function makeFakeGh(opts: { authStatus?: 'ok' | 'fail'; renameSucceeds?: boolean echo "gh $@" >> "${callLog}" case "$1" in auth) ${authStatus === 'ok' ? 'exit 0' : 'exit 1'} ;; + api) + # gh api user --jq .login → print authenticated login + shift + if [ "\${1:-}" = "user" ]; then + echo "testuser" + exit 0 + fi + exit 0 + ;; repo) shift case "$1" in view) - # gh repo view + # gh repo view | shift ${alreadyRenamed ? `if echo "$@" | grep -q gstack-artifacts; then exit 0; else exit 1; fi` : `exit 1`} ;;