Repository navigation
setup-project.py: a store credential helper holding no login for github.com gets the preflight's [??] and its gh auth setup-git offer, as a machine with no helper does, and the check no longer says every configured helper answers git's question for github.com (#992) - #1008
Conversation
…thub.com gets the preflight's `[??]` and its `gh auth setup-git` offer, as a machine with no helper does, and the check no longer says every configured helper answers git's question for github.com (#992) A configured helper is not a stored login. The check counted any helper as an answer, and its docstring said `osxkeychain`, Windows' `manager`, `store` and a devcontainer's own "all answer git's question for github.com". `store` answers only with a login stored in it: over an empty `~/.git-credentials` it answers nothing, and with its prompt off git could not read a username (#992's probe). Every push the scaffold makes runs with that prompt off since #971 (#915), so that machine's first push fails after the three repositories exist (RV971 measured it). Where every helper the two keys hold is `store`, the check now reads the files git's `store` reads (`~/.git-credentials`, then `$XDG_CONFIG_HOME/git/credentials` or `~/.config/git/credentials`, or a `--file` that is an absolute path `sh` reads as itself) for a login git would give https://github.com, read as git reads it and measured against git's own `store`. None there, and it says so with the same `[??]` and offer. Any other helper, a mix, a path it cannot read as git would, or a file it cannot read is left alone. No helper is ever asked: `git credential fill` runs every helper configured. The warning stays out of the `checked` list, so `--preflight` exits 0 either way, and a dry run still warns and offers nothing (#857). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reviewer's GuideThe PR narrows credential preflight from “any helper is configured” to detecting whether an exclusively Sequence diagram for empty store credential preflightsequenceDiagram
participant Preflight
participant GitConfig
participant StoreFiles
participant User
participant gh
Preflight->>GitConfig: _credential_helpers()
GitConfig-->>Preflight: store helper configuration
Preflight->>StoreFiles: _store_files(helper)
StoreFiles-->>Preflight: credential file paths
Preflight->>StoreFiles: _holds_a_github_login(path)
StoreFiles-->>Preflight: no readable GitHub login
Preflight->>User: warn(...)
Preflight->>User: offer(gh auth setup-git)
User-->>Preflight: accepts offer
Preflight->>gh: gh auth setup-git
gh-->>Preflight: success
Preflight->>GitConfig: _why_no_login()
GitConfig-->>Preflight: configured gh helper now answers
Flow diagram for credential preflight login detectionflowchart TD
A[Preflight checks credential helpers] --> B["_why_no_login()"]
B --> C{Credential helpers configured?}
C -->|No| D[Warn: no helper; offer gh auth setup-git]
C -->|Yes| E{Every helper is readable store configuration?}
E -->|No| F[Leave configuration alone]
E -->|Yes| G[Read store credential files]
G --> H{Readable GitHub HTTPS login found?}
H -->|Yes| F
H -->|No| I[Warn: no stored GitHub login; offer gh auth setup-git]
D --> J[Run gh auth setup-git]
I --> J
J --> K{"_why_no_login() after setup"}
K -->|No warning| L[Report git will ask gh for credentials]
K -->|Still warning| M[Leave warning state]
File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
|
RV1008 review of #1008 (closes #992) at head b2dbc8b: READY. No MUST. Measured at main 3864b18 and the head, with --preflight run from a scratch folder that is not the checkout, inside env -i. The stub gh answers only --version and auth status and exits 97 on anything else. GH_TOKEN=bogus, empty GH_CONFIG_DIR, GIT_SSH_COMMAND=false, system and global config and HOME in scratch, no remote. The exit code was 0 at both in every case.
The four decisions are sound. Notes below on 1, 2 and 4. SHOULD-1 (follow-up issue, does not block): setup-project.py:1380-1406. With url.git@github.com:.insteadOf https://github.com/ and store holding no github login, the head prints a false [??] and an offer where main was silent: git ls-remote --get-url https://github.com/demoorg/x.git gives git@github.com:demoorg/x.git, so no helper is asked. The no-helper warning on main has the same blindness. Fix both in a follow-up: ask git ls-remote --get-url (no network) and skip both warnings when the answer is not https://. Tests at the head, one at a time:
Mutants: the writer's M1, M13 and M16 remade and killed. Mine: a remedy naming gh auth login, killed by 9 rows; the re-check asking only whether a helper is configured, killed by ..._asked_about_again_after_setup_git[False]; helpers read with --global only, which survives (NIT-4). Merges: clean with origin/main 3864b18, clean with #1011 f090a3c, and clean three-way (main with #1011, then the head). test_setup_project_py.py on the three-way tree: 286 passed. No other open PR touches setup-project.py or tests/test_setup_project_py.py. SonarCloud: 0 open issues. CI tests was still pending at 2026-10-19T21:47Z: land only once it is green. Lane: openreposhape-3 (openRepoShape-3) |
|
Sorry @brettheap, you've used your own review budget of 250,000 diff characters for the last 7 days. You can request another review in 20 hours and 27 minutes by commenting |



Closes #992.
What changed
setup-project.pysaid thatstoreand every other configured helper "answer git's question for github.com", and it counted any configured helper as a login. A helper that holds nothing answers nothing:storeover an empty~/.git-credentialsanswers nothing, and with git's prompt off (every scaffold push runs that way since #971, #915) git cannot read a username, so the first push fails after the three repositories exist (RV971's NIT-3 on #971).The words first.
_credential_helper_configured) and the docstring line "they all answer git's question" are gone. A grep of both files for either phrase finds nothing.store, the one kind measured, answers only with a login stored in it. They say nothing of what other helpers answer.tests/test_setup_project_py.py,test_a_credential_helper_that_is_not_ghs_is_left_alonechanged only in its docstring; its name and body are as they were.Then the check says it too, in the narrow form the issue offered.
credential.https://githubproxy.fjygbaifeng.eu.org.helper, thencredential.helper) is exactlystore, the check reads the files git'sstorereads:storealone:~/.git-credentials, then$XDG_CONFIG_HOME/git/credentials(or~/.config/git/credentialswhere that is unset or empty);store --file=<path>orstore --file <path>: that one file, where the path is absolute andshreads it as itself (SHELL_BARE_POSIX).[??] git's credential helper for github.com is `store`, and no login for github.com is stored in <files>, so the first push over HTTPS will fail. Fix it once: gh auth setup-gitand makes the same offer as for no helper. Aftergh auth setup-gitit asks again.credential_from_url): scheme exactlyhttps, a user and a password before the first@, the hostgithubproxy.fjygbaifeng.eu.orgafter%XXdecoding, no port, any path. A probe of git 2.43's owngit credential-store getover 22 made-up lines agreed with that reading, and a test row asks git's owncredential-storefor 16 lines.storebeside one (including gh's helper overstore);cache; a--filepath the shell would change, a relative one, or another spelling such as--fil=; a file that exists but cannot be read.git credential fillis never run. It runs every configured helper, and a helper may open a window or ask at a keychain. A file's lines are read for the one question and never printed.checkedlist, so--preflightexits 0 either way (a warning that refuses is a refusal); a dry run still warns and offers nothing (setup-project.py: a passed-through-- --dry-runstill offersgh auth setup-git, which writes the person's global git config on a typedyes, andchecked_refpassesa.lock/b, which git refuses #857); the no-helper sentence is byte-identical.--helpline changed, soAGENTS.md,docs/cli.mdand the handbook are untouched.setup-project.pyis not a pinned copy, so no project re-pins.The four decisions
HOMEunset: the check falls back toos.path.expanduser. Git for Windows may useHOMEDRIVE/HOMEPATHinstead; that is not measured.storedies on a folder there (measured, rc=128). The check can say nothing about a file it cannot read.Out of scope
fatal: Authentication failed): scaffold-project.py: a push whose stored login GitHub rejects (fatal: Authentication failed) is refused with only the ruleset hint, where a push git has no login for is told how to give git one #993, the scaffold's.LinkSources.__init__is over SonarCloud's 15, and--link-remedy=openXdox=a/openYdox=bgivesopenYdoxthe wordsb#991's SHOULD-3 row intests/test_setup_project_py.py(main:3281-3297, now:3535-3551): untouched.storeand every other helper "answer git's question for github.com" and counts any helper as configured, though one holding nothing answers nothing, so setup is silent on a push with no login #992 has no "Needs a ruling" or "Documented limit" section.Tests
Every run was under a stub
ghthat exits 97 first on PATH,GH_TOKEN=bogus, an emptyGH_CONFIG_DIR,HOMEandXDG_CONFIG_HOMEin scratch,/usr/bin/python3 -m pytest -q -p no:cacheprovider --basetemp=<scratch>/bt-<name> -o tmp_path_retention_policy=failed, one process at a time, at headb2dbc8b2(the 14 files from the F992 run also undertimeout 900,GIT_SSH_COMMAND=false,EMAILset). No file hung and none is environment-blocked.New rows, all in
tests/test_setup_project_py.py:test_a_key_holding_only_an_empty_line_is_no_credential_helper;test_a_credential_store_with_no_github_login_is_warned_about(6 cases);test_a_dry_run_warns_of_an_empty_credential_store_and_offers_nothing;test_a_credential_store_holding_a_github_login_is_left_alone(6);test_a_credential_helper_whose_logins_this_cannot_read_is_left_alone(6);test_a_credential_store_file_is_read_as_git_reads_it(16 lines, asks git's owncredential-store);test_an_empty_credential_store_is_asked_about_again_after_setup_git(2).Main moved after the base (to
04923f10d), so I also built the merge of this head with it as a local, unpushed commit and re-ran the setup-facing files on that tree: tests/test_setup_project_py.py 286 passed in 107.82s; tests/test_setup_project_dry_run_offers.py 13 passed in 1.70s; tests/test_github_limits_init_check_org.py 94 passed in 27.81s; tests/test_github_name_limits.py 64 passed in 22.77s; tests/test_agents_md_codes.py 47 passed in 1.98s; tests/test_cli_reference.py 23 passed in 30.32s; tests/test_repo_hygiene.py 100 passed in 2.57s.tests/test_shape_doctor.pywas not run:shape-doctor.pyimports nothing fromsetup-project.py, and this change touches neither.Mutants: 23 written, 23 killed
M1 any helper counts as an answer; M2 the XDG file not read; M3 an empty
XDG_CONFIG_HOMEcounts as set; M4 no~/.configfallback; M5--file=not read; M6--file <path>not read; M7 a path the shell would change is taken; M8 a relative path is taken; M9 any scheme; M10 no password needed; M11 host not decoded; M12 only/ends the host; M13 last@instead of first; M14 blanks at the ends kept; M15 an absent file counts as unreadable; M16 an unreadable file counts as empty; M17 a file named twice; M18 a helper besidestoreignored; M19 the re-check inverted; M20 no re-check; M21 a dry run offers; M22 empty helper lines counted; M23 the scoped key not read.Each is killed by one or more of the rows above (for example M1 by
..._with_no_github_login_is_warned_about, the dry-run row and the..._asked_about_again_after_setup_gitrow; M9, M10 and M11 to M14 by..._read_as_git_reads_it; M7, M8 and M16 by..._whose_logins_this_cannot_read_is_left_alone; M22 by..._holding_only_an_empty_line_is_no_credential_helper).Merges
git merge-tree --write-treeofb2dbc8b2withorigin/main04923f10d: clean (treef03b66eb225db7a39b3845090683f4c5044e34bd). Also clean with each of the 13 open PRs: #1006, #1002, #988, #986, #983, #976, #975, #974, #973, #970, #958, #956, #954. None touchessetup-project.pyortests/test_setup_project_py.py; #975 and #970 touchdocs/cli.md, which this branch does not change.Known NIT
One docstring line in
_check_credential_helper(setup-project.pyline 1416) is 106 characters, a rewrap miss. The repository sets no line-length rule and no test failed. It is left as it is so the gated head stays as it is.Lane: openreposhape-3 (openRepoShape-3)
🤖 Generated with Claude Code
Summary by Sourcery
Warn about empty GitHub
storecredentials and offergh auth setup-gitbefore scaffold HTTPS pushes fail.New Features:
storecredential files for GitHub and warn users before HTTPS pushes can fail.gh auth setup-gitfor machines whose configured helpers are exclusively empty or GitHub-login-freestorehelpers, including a recheck after setup.Bug Fixes:
storecredential helper cannot provide credentials.Enhancements:
storecredential locations and GitHub credential URL formats without invoking configured helpers or exposing stored credentials.storehelpers, mixed helpers, unreadable or unsupported credential files, dry runs, and preflight exit status.Tests: