fix(auth): offer the pre-18.9 token URL alongside the current one
Description
glab auth login tells you to generate a token at
/-/user_settings/personal_access_tokens/legacy/new. That page does not exist on older
instances, so users following the tip land on a 404 with no hint of where else to look.
This shows both URLs, leading with the current one.
This picks up #8377 (closed), which @phikai left open when !3473 (closed) was closed as stale, and implements the shape agreed there:
- @phikai: no version detection or "being smart" about which link to show — just show both, and "suggest the proper URL for users on the latest version first".
- @brendan777: lead with the newer URL, move the older one to a follow-on sentence, restore the
?scopes=api,write_repositorypre-fill, and use an indexed verb sohostnameis passed once. - @eread: "for both pre-GitLab 19 and GitLab 19.x and later, we should populate the scopes".
One correction to the agreed wording
The suggestion in !3473 (closed) said "On GitLab 18 and earlier". That would be wrong for 18.9, 18.10 and 18.11, which do have the page.
I checked the route rather than the version numbering. legacy/new is a collection route in
config/routes/user_settings.rb,
and reading that file at each tag:
| Tag | legacy/new route |
|---|---|
v18.5.0-ee |
absent |
v18.6.0-ee |
absent |
v18.7.0-ee |
absent |
v18.8.0-ee |
absent |
v18.9.0-ee |
present |
v18.10.0-ee, v18.11.0-ee, v19.0.0-ee, master |
present |
So the cutoff is 18.9, which is exactly consistent with the reporter seeing a 404 on 18.8. The tip now says "On GitLab 18.8 and earlier". Happy to change it back to the literal suggested wording if you would rather keep the version boundary coarse.
Rendered result
The minimum required scopes are 'api' and 'write_repository'.
Generate a personal access token at https://gitlab.example.com/-/user_settings/personal_access_tokens/legacy/new?scopes=api,write_repository
On GitLab 18.8 and earlier, use https://gitlab.example.com/-/user_settings/personal_access_tokens?scopes=api,write_repository instead.Both URLs pre-fill the scopes. The index URL carried ?scopes=api,write_repository until
!3303 (merged) moved the tip to legacy/new; without it, users are told which scopes they need and then
sent to a page that does not pre-select them.
Related Issues
Resolves #8377 (closed)
How has this been tested?
getAccessTokenTip had no test at all, so this adds Test_getAccessTokenTip. It is a flat set of
asserts rather than subtests: the function returns one string, so the whole contract is the scopes
sentence, both URLs, and their order.
Mutation testing
Re-run against the flattened test. The table in the first revision of this description was wrong
— it credited each mutation to a single subtest, but "drop ?scopes=" also broke the ordering
assert, because the older URL literal includes its query string. Corrected:
| Mutation | What fails |
|---|---|
| revert to the single-URL tip (the bug) | Contains(tip, older), and the ordering assert — "101" is not less than "-1" |
drop ?scopes= from the older URL |
Contains(tip, older), and the ordering assert — same -1 |
| swap the order so the older URL comes first | the ordering assert — "226" is not less than "101" |
break the indexed verb (%[1]s → %s) |
never reaches the test: go vet fails the build |
That last row is why the earlier assert.NotContains(t, tip, "%!") was not carrying weight — a
broken verb cannot reach a running test.
Jobs run locally
| Job | Command | Result |
|---|---|---|
tests:unit |
go test ./internal/commands/auth/... |
all packages ok |
lint |
golangci-lint run ./internal/commands/auth/... in golangci/golangci-lint:v2.13.1-alpine |
0 issues |
check_docs_update |
make gen-docs, then git status |
no drift; authentication.md is hand-maintained and not regenerated |
check_docs_markdown |
vale, markdownlint-cli2, lychee in the job's own image |
0 errors, 0 issues in 302 files, 0 link errors |
The full pipeline was green on the previous revision of this branch.