Skip to content

Commit 6753263

Browse files
committed
Remove the personal access token (PAT) limitation
The new Stacks REST API is public, so any user authenticated with the GitHub CLI (including via a PAT with repo scope) can perform stack operations once the feature is enabled for their repository. Remove the PAT detection and the private-preview gating: - Delete Config.WarnIfPAT / IsPersonalAccessToken and the TokenForHostFn test hook (internal/config/auth.go is no longer needed). - Drop the submit pre-flight that aborted on a PAT. - Rename warnStacksUnavailableOrPAT to warnStacksUnavailable and simplify it to the "stacked PRs not enabled" message. Copilot-Session: 03673c26-a245-42da-93ed-dfcebc92a740
1 parent cbab0a1 commit 6753263

11 files changed

Lines changed: 16 additions & 271 deletions

File tree

cmd/checkout.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ func checkoutRemoteStack(cfg *config.Config, sf *stack.StackFile, gitDir string,
194194
if err != nil {
195195
var httpErr *api.HTTPError
196196
if errors.As(err, &httpErr) && httpErr.StatusCode == 404 {
197-
warnStacksUnavailableOrPAT(cfg)
197+
warnStacksUnavailable(cfg)
198198
return nil, "", ErrAPIFailure
199199
}
200200
cfg.Errorf("failed to list stacks: %v", err)

cmd/checkout_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ func TestCheckout_NumericTarget_StacksNotAvailable(t *testing.T) {
158158
require.NoError(t, stack.Save(gitDir, &stack.StackFile{SchemaVersion: 1, Stacks: []stack.Stack{}}))
159159

160160
cfg, outR, errR := config.NewTestConfig()
161-
setTestTokenForHost(cfg, "gho_test_oauth_token")
161+
setTestRepo(cfg)
162162
cfg.GitHubClientOverride = &github.MockClient{
163163
FindStackForPRFn: func(int) (*github.RemoteStack, error) {
164164
return nil, &api.HTTPError{StatusCode: 404, Message: "Not Found"}

cmd/link.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,7 @@ func listStacksSafe(cfg *config.Config, client github.ClientOps) ([]github.Remot
366366
if err != nil {
367367
var httpErr *api.HTTPError
368368
if errors.As(err, &httpErr) && httpErr.StatusCode == 404 {
369-
warnStacksUnavailableOrPAT(cfg)
369+
warnStacksUnavailable(cfg)
370370
return nil, ErrStacksUnavailable
371371
}
372372
cfg.Errorf("failed to list stacks: %v", err)
@@ -559,7 +559,7 @@ func createLink(cfg *config.Config, client github.ClientOps, prNumbers []int) er
559559
cfg.Errorf("Cannot create stack: %s", httpErr.Message)
560560
return ErrAPIFailure
561561
case 404:
562-
warnStacksUnavailableOrPAT(cfg)
562+
warnStacksUnavailable(cfg)
563563
return ErrStacksUnavailable
564564
default:
565565
cfg.Errorf("Failed to create stack (HTTP %d): %s", httpErr.StatusCode, httpErr.Message)

cmd/link_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ func TestLink_DuplicateArgs(t *testing.T) {
244244

245245
func TestLink_StacksUnavailable(t *testing.T) {
246246
cfg, _, errR := config.NewTestConfig()
247-
setTestTokenForHost(cfg, "gho_test_oauth_token")
247+
setTestRepo(cfg)
248248
cfg.GitHubClientOverride = &github.MockClient{
249249
FindPRByNumberFn: func(n int) (*github.PullRequest, error) {
250250
return &github.PullRequest{Number: n, HeadRefName: "b", BaseRefName: "main"}, nil

cmd/submit.go

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -113,16 +113,11 @@ func runSubmit(cfg *config.Config, opts *submitOptions) error {
113113
return ErrAPIFailure
114114
}
115115

116-
// Pre-flight: abort early if the user is authenticating with a PAT.
117-
if cfg.WarnIfPAT() {
118-
return ErrStacksUnavailable
119-
}
120-
121116
// Verify that the repository has stacked PRs enabled.
122117
stacksAvailable := s.ID != ""
123118
if !stacksAvailable {
124119
if _, err := client.ListStacks(); err != nil {
125-
warnStacksUnavailableOrPAT(cfg)
120+
warnStacksUnavailable(cfg)
126121
if cfg.IsInteractive() {
127122
p := prompter.New(cfg.In, cfg.Out, cfg.Err)
128123
proceed, promptErr := p.Confirm("Would you still like to create regular PRs?", false)
@@ -917,7 +912,7 @@ func createNewStack(cfg *config.Config, client github.ClientOps, s *stack.Stack,
917912
case 422:
918913
return handleCreate422(cfg, httpErr, prNumbers)
919914
case 404:
920-
warnStacksUnavailableOrPAT(cfg)
915+
warnStacksUnavailable(cfg)
921916
return false
922917
default:
923918
cfg.Warningf("Failed to create stack on GitHub: %s", httpErr.Message)

cmd/submit_test.go

Lines changed: 2 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1590,9 +1590,7 @@ func TestSubmit_PreflightCheck_404_BailsOut(t *testing.T) {
15901590
},
15911591
}
15921592

1593-
// Use an OAuth token so the PAT pre-flight check passes and we
1594-
// exercise the ListStacks 404 path.
1595-
setTestTokenForHost(cfg, "gho_test_oauth_token")
1593+
setTestRepo(cfg)
15961594

15971595
cmd := SubmitCmd(cfg)
15981596
cmd.SetArgs([]string{"--auto"})
@@ -1645,9 +1643,7 @@ func TestSubmit_PreflightCheck_404_Interactive_UserDeclinesAborts(t *testing.T)
16451643
},
16461644
}
16471645

1648-
// Use an OAuth token so the PAT pre-flight check passes and we
1649-
// exercise the ListStacks 404 path.
1650-
setTestTokenForHost(cfg, "gho_test_oauth_token")
1646+
setTestRepo(cfg)
16511647

16521648
cmd := SubmitCmd(cfg)
16531649
cmd.SetArgs([]string{"--auto"})
@@ -2345,91 +2341,6 @@ func TestSubmit_NoTemplate_UsesFooter(t *testing.T) {
23452341
assert.Contains(t, capturedBody, feedbackURL)
23462342
}
23472343

2348-
func TestSubmit_PreflightCheck_PAT_BailsOut(t *testing.T) {
2349-
s := stack.Stack{
2350-
Trunk: stack.BranchRef{Branch: "main"},
2351-
Branches: []stack.BranchRef{
2352-
{Branch: "b1"},
2353-
{Branch: "b2"},
2354-
},
2355-
}
2356-
2357-
tmpDir := t.TempDir()
2358-
writeStackFile(t, tmpDir, s)
2359-
2360-
pushed := false
2361-
mock := newSubmitMock(tmpDir, "b1")
2362-
mock.PushFn = func(string, []string, bool, bool) error {
2363-
pushed = true
2364-
return nil
2365-
}
2366-
restore := git.SetOps(mock)
2367-
defer restore()
2368-
2369-
listStacksCalled := false
2370-
cfg, _, errR := config.NewTestConfig()
2371-
cfg.GitHubClientOverride = &github.MockClient{
2372-
ListStacksFn: func() ([]github.RemoteStack, error) {
2373-
listStacksCalled = true
2374-
return nil, nil
2375-
},
2376-
}
2377-
2378-
// Simulate a classic PAT — the pre-flight check should abort.
2379-
setTestTokenForHost(cfg, "ghp_classic_pat_token")
2380-
2381-
cmd := SubmitCmd(cfg)
2382-
cmd.SetArgs([]string{"--auto"})
2383-
cmd.SetOut(io.Discard)
2384-
cmd.SetErr(io.Discard)
2385-
err := cmd.Execute()
2386-
2387-
cfg.Err.Close()
2388-
errOut, _ := io.ReadAll(errR)
2389-
output := string(errOut)
2390-
2391-
assert.ErrorIs(t, err, ErrStacksUnavailable)
2392-
assert.Contains(t, output, "Personal access tokens are not supported by gh stack")
2393-
assert.Contains(t, output, "gh auth login")
2394-
assert.False(t, pushed, "should not push when using a PAT")
2395-
assert.False(t, listStacksCalled, "should not call ListStacks when PAT detected")
2396-
}
2397-
2398-
func TestSubmit_PreflightCheck_FinegrainedPAT_BailsOut(t *testing.T) {
2399-
s := stack.Stack{
2400-
Trunk: stack.BranchRef{Branch: "main"},
2401-
Branches: []stack.BranchRef{
2402-
{Branch: "b1"},
2403-
{Branch: "b2"},
2404-
},
2405-
}
2406-
2407-
tmpDir := t.TempDir()
2408-
writeStackFile(t, tmpDir, s)
2409-
2410-
mock := newSubmitMock(tmpDir, "b1")
2411-
restore := git.SetOps(mock)
2412-
defer restore()
2413-
2414-
cfg, _, errR := config.NewTestConfig()
2415-
cfg.GitHubClientOverride = &github.MockClient{}
2416-
2417-
setTestTokenForHost(cfg, "github_pat_11AABBCC_xxxx")
2418-
2419-
cmd := SubmitCmd(cfg)
2420-
cmd.SetArgs([]string{"--auto"})
2421-
cmd.SetOut(io.Discard)
2422-
cmd.SetErr(io.Discard)
2423-
err := cmd.Execute()
2424-
2425-
cfg.Err.Close()
2426-
errOut, _ := io.ReadAll(errR)
2427-
output := string(errOut)
2428-
2429-
assert.ErrorIs(t, err, ErrStacksUnavailable)
2430-
assert.Contains(t, output, "Personal access tokens are not supported by gh stack")
2431-
}
2432-
24332344
func TestSubmit_DisablesAutoMergeOnExistingPR(t *testing.T) {
24342345
s := stack.Stack{
24352346
Trunk: stack.BranchRef{Branch: "main"},

cmd/utils.go

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,9 @@ func printInterrupt(cfg *config.Config) {
7272
cfg.Infof("Received interrupt, aborting operation")
7373
}
7474

75-
// warnStacksUnavailableOrPAT prints an appropriate warning when a stacks API
76-
// call returns 404. If the token is a PAT the message focuses on the auth
77-
// issue; otherwise it falls back to the generic "not enabled" message.
78-
func warnStacksUnavailableOrPAT(cfg *config.Config) {
79-
if cfg.WarnIfPAT() {
80-
return
81-
}
75+
// warnStacksUnavailable prints a warning when a stacks API call returns 404,
76+
// indicating stacked PRs are not enabled for the repository.
77+
func warnStacksUnavailable(cfg *config.Config) {
8278
cfg.Warningf("Stacked PRs are not enabled for this repository")
8379
}
8480

cmd/utils_test.go

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -691,39 +691,21 @@ func TestStackNeedsRebase_SkipsMergedBranches(t *testing.T) {
691691
assert.False(t, stackNeedsRebase(s), "should skip merged branches and find stack up to date")
692692
}
693693

694-
// setTestTokenForHost sets cfg.TokenForHostFn to return the given token for
695-
// any host. Also sets RepoOverride so tests don't depend on real git context.
696-
func setTestTokenForHost(cfg *config.Config, token string) {
697-
cfg.TokenForHostFn = func(string) (string, string) { return token, "test" }
694+
// setTestRepo sets RepoOverride so tests don't depend on real git context.
695+
func setTestRepo(cfg *config.Config) {
698696
cfg.RepoOverride = &repository.Repository{Host: "github.com", Owner: "o", Name: "r"}
699697
}
700698

701-
func TestWarnStacksUnavailableOrPAT_ShowsPATMessage(t *testing.T) {
699+
func TestWarnStacksUnavailable_ShowsNotEnabled(t *testing.T) {
702700
cfg, _, errR := config.NewTestConfig()
703-
setTestTokenForHost(cfg, "github_pat_fine_grained")
704701

705-
warnStacksUnavailableOrPAT(cfg)
706-
707-
cfg.Err.Close()
708-
errOut, _ := io.ReadAll(errR)
709-
output := string(errOut)
710-
711-
assert.Contains(t, output, "Personal access tokens are not supported by gh stack")
712-
assert.NotContains(t, output, "Stacked PRs are not enabled")
713-
}
714-
715-
func TestWarnStacksUnavailableOrPAT_ShowsNotEnabledForOAuth(t *testing.T) {
716-
cfg, _, errR := config.NewTestConfig()
717-
setTestTokenForHost(cfg, "gho_oauth_token")
718-
719-
warnStacksUnavailableOrPAT(cfg)
702+
warnStacksUnavailable(cfg)
720703

721704
cfg.Err.Close()
722705
errOut, _ := io.ReadAll(errR)
723706
output := string(errOut)
724707

725708
assert.Contains(t, output, "Stacked PRs are not enabled for this repository")
726-
assert.NotContains(t, output, "Personal access tokens")
727709
}
728710

729711
func TestEnsureLocalTrunk_AlreadyExists(t *testing.T) {

internal/config/auth.go

Lines changed: 0 additions & 65 deletions
This file was deleted.

internal/config/auth_test.go

Lines changed: 0 additions & 69 deletions
This file was deleted.

0 commit comments

Comments
 (0)