Repository navigation
setup-project.py: the preflight's credential-helper [??] and its gh auth setup-git offer stay silent where an insteadOf rule sends github.com over ssh or a helper under another spelling of the github.com key answers, and store's files are the ones git expands ~ to, so with HOME unset it no longer names files git never reads (#1013) - #1022
Conversation
…h auth setup-git` offer stay silent where an `insteadOf` rule sends github.com over ssh or a helper under another spelling of the github.com key answers, and `store`'s files are the ones git expands `~` to, so with `HOME` unset it no longer names files git never reads (#1013) - `_github_goes_over_https` asks `git ls-remote --get-url` (local, no network) for `https://github.com/<org>/`, or `https://github.com/` without `--org`, and `_check_credential_helper` says nothing where the answer is not `https://`. A `pushInsteadOf` rule alone changes nothing. - `_credential_helpers` also reads every `credential.<url>.helper` (`git config -z --get-regexp`), counting those whose url `_names_github` reads as possibly github.com's, a superset of git's matching, pinned against `git credential fill` row by row. - `_store_files` asks git for each `~/` path (`_home_path`, `git -c <k>=~/... config --type=path`): with `HOME` unset git expands none, and the `[??]` says `store` has no file to read. - NIT-2 docstrings reworded, NIT-3 line rewrapped, NIT-4 real-git rows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Reviewer's GuideUpdates preflight credential diagnostics to reflect Git’s effective transport and credential resolution: SSH-rewritten GitHub remotes no longer trigger HTTPS warnings, URL-scoped helpers are detected across Git-compatible spellings, and Sequence diagram for GitHub credential preflight transport detectionsequenceDiagram
participant Preflight
participant Git
participant CredentialCheck
Preflight->>Git: ls-remote --get-url -- https://github.com/<org>/
Git-->>Preflight: Effective URL
alt Effective URL is not HTTPS
Preflight->>CredentialCheck: return without warning or setup-git offer
else Git fails or returns no URL
Preflight->>CredentialCheck: continue with HTTPS warning behavior
else Effective URL starts with https://
Preflight->>CredentialCheck: check credential helpers
end
Sequence diagram for Git credential helper and store-file resolutionsequenceDiagram
participant Preflight
participant Git
participant CredentialConfig
participant StoreFiles
Preflight->>Git: config --get-all credential.https://github.com.helper
Preflight->>Git: config --get-all credential.helper
Preflight->>Git: config -z --get-regexp ^credential\..*\.helper$
Git-->>CredentialConfig: Matching helper entries
CredentialConfig->>StoreFiles: _store_files(store)
StoreFiles->>Git: config --type=path --get openreposhape.storefile
Git-->>StoreFiles: Git-expanded credential path or failure
StoreFiles-->>Preflight: Files Git can read
Preflight->>Preflight: _holds_a_github_login(path)
Flow diagram for HTTPS credential warning decisionflowchart TD
A[_check_credential_helper] --> B{local_mode?}
B -->|yes| Z[Return silently]
B -->|no| C[_github_goes_over_https]
C --> D{Effective URL starts with https://?}
D -->|no| Z
D -->|yes or Git gives no answer| E[_why_no_login]
E --> F{Credential login detected?}
F -->|yes| Z
F -->|no| G[Warn and offer gh auth setup-git]
File-Level Changes
Assessment against linked issues
Possibly linked issues
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
(lane openRepoShape-3, 2026-10-20T00:33:55Z) RV1022 review of #1022 (closes #1013) at head 35da321: READY. No MUST. One SHOULD (does not block), four NITs. Measured at main e076e3e and the head, git 2.43.0. --preflight ran from a scratch folder inside env -i, with a stub gh that answers only --version and auth status. GH_TOKEN=bogus, empty GH_CONFIG_DIR, GIT_SSH_COMMAND=false, all config and HOME in scratch. Each row was also checked against git's own ls-remote --get-url and credential fill. Exit 0 at both in every row; the only output lines that differ are the credential lines.
SHOULD-1 (this PR's fix round if it gets one, else a follow-up): two new branches have no row. RV-M4, setup-project.py:1520 made "returncode == 0 and (...)", survives the -k subset, the whole file (331 passed) and the dry-run offers file (13 passed). RV-M2b, :1328 counting an empty scoped value, survives the same; on [credential "https://github.com/"] helper = alone it would silence a true no-helper [??]. Add two rows: --get-url answering (128, "") still warns, and that config alone gives NO_HELPER. Optional: a DemoOrg/ rule with --org DemoOrg stays silent (RV-M3, org lowered, survives). Tests at the head, one at a time:
Mutants: W1013's M2, M9, M14, M15 and M12 re-made, each killed by the rows the writer named. RV-M1 (no answer means ssh) killed by test_the_credential_helper_is_a_warning_not_a_refusal. RV-M2b, RV-M4 and RV-M3 survive (SHOULD-1). Merges: clean with origin/main e076e3e, clean with #1011 f5749b7, clean three-way. No other open PR touches the two files. CI tests: 2 failed, 17646 passed, 318 skipped. The 2 are the known main-red rows in test_family_add_gitlink_and_null_members.py (members: null and members: []). Land once #1026 fixes main and tests is green on a re-run. Lane: openreposhape-3 (openRepoShape-3) |
…elper =` and an org rule in capitals are pinned by rows, a `:0443` port reads as git reads it, a rule to `http://` keeps the `[??]` since git asks a helper there too, and the docstring names the local-config limit (#1013) Fix round 1 for RV1022's review of #1022 (SHOULD-1 and NIT-1 to NIT-3): - SHOULD-1: rows for `git ls-remote --get-url` failing (128), for `[credential "https://github.com/"]` holding only `helper =`, and for a `DemoOrg/` rule with `--org DemoOrg`. - NIT-1: `_names_github` takes a port of 443 with zeros before it, and not 0 (measured with `credential fill`); two `HELPER_URLS` rows. - NIT-2: `_github_goes_over_https` is `_git_asks_a_helper`, and an `http://` answer keeps the warnings; a `RULES` row. - NIT-3: its docstring names the starting folder's `.git/config` as a known limit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…les and say git sends a url over HTTP or HTTPS, and `HELPER_URLS` pins `https://github.com:000`, which git's `credential fill` does not treat as 443 (#1013) RV1022d's NIT-B: the comment over the rules lacked `DEMOORG_IN_CAPITALS_OVER_SSH` and `EVERY_ORG_OVER_HTTP`, and the one over `RULES` said git sends the urls over HTTPS where its third field is true for plain http too. NIT-C: an all-zeros port was pinned by no row, so the mutant `port.lstrip("0") in ("", "443") and port != "0"` survived the file; the new row kills it. Lane: openreposhape-3 (openRepoShape-3) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rnings-over-ssh Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
|
(lane openRepoShape-3, 2026-10-20T01:54:09Z) #1022 is pushed at
W1013, fix round 1: SHOULD-1, three rows (tests/test_setup_project_py.py):
Mutants, each against the -k "credential or rule or helper or home or store or url" subset, all killed:
Tests at f68f4b4, one at a time under the stub gh:
Diff of the fix round: setup-project.py +26/-15, tests/test_setup_project_py.py +40/-4. RV1022d, delta review: Each fix against RV1022:
Probes: --preflight at 35da321 and f68f4b4, real git, stub gh (97 except --version and auth status), GH_TOKEN=bogus, empty GH_CONFIG_DIR, GIT_SSH_COMMAND=false, all config and HOME in scratch. Full stdout+stderr compared, rc 0 at both everywhere.
New NITs (none blocks):
Mutants, each against the -k "credential or rule or helper or home or store or url" subset (100 selected):
Tests at f68f4b4, one at a time under the 97 stub gh:
Merges (git merge-tree --write-tree, exit 0, no conflicts): with origin/main e076e3e, tree d2c13b91; with #1011 f5749b7, tree 220a6241. Skipped to save usage: the other five gate files the writer ran, the three-way merge, and SonarCloud (the commit is not pushed). #1026 is still open, so land after it fixes main and tests 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 1 day and 15 hours by commenting |
…lict, so #1025 is tested against main as it stands, with #983's change to validate-family.py and #1026's fix of main's two red rows (#936) Main took twelve squashes since e076e3e: #1026, #958, #986, #1002, #1016, #1011, #1021, #1022, #973, #975, #1015 and #983. Only #983 touches validate-family.py, in `_git_runs` and the member checks, apart from this branch's hunks; it touches none of this branch's test files. Lane: openreposhape-4 (openRepoShape-4) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>



Closes #1013.
Follows #1008 (2d6f928, closed #992) and RV1008's review of it. Head
35da321e, one commit on origin/maindbf12e05. Onlysetup-project.pyandtests/test_setup_project_py.pychange; no flag, no--helpline, no code outside those two.What changed, by section
SHOULD-1, both warnings (Direction 1). New
_git_asks_a_helper(opts), which was_github_goes_over_httpsbefore fix round 1. It runsgit ls-remote --get-url -- <url>, which is local and contacts nothing._check_credential_helpernow returns before either warning when the answer starts with neitherhttps://norhttp://. Fix round 1 addedhttp://, because git asks a helper over http too.https://github.com/<org>/with--org, andhttps://github.com/without it.pushInsteadOfrule alone leaves the answerhttps://, so the warnings stay. A row pins that.https://github.com/<org>/<Project>[-spec|-code].git) starts with it, so a rule that rewrites it rewrites all three. A rule keyed below the organisation, to one repository, is not asked, and that machine still gets the[??]it gets today.NIT-1, a URL-scoped helper (Direction 2). I chose the first option: read every key and count its helpers.
_credential_helperskeeps its two--get-allreads and adds one more:git config -z --get-regexp '^credential\..*\.helper$'reads everycredential.<url>.helperkey._names_githubreads as possibly github.com's are counted with the other two keys'. The exact keycredential.https://github.com.helperis skipped there, because the first read already counts it._names_githubreads a superset of git's matching. It is pinned row by row against git's owncredential fill(HELPER_URLS, 21 urls).httpsscheme in any case, or no scheme; the hostgithub.comin any case, after%XXdecoding; an empty host;*.com; port 443.http,ssh, another host, port 8443.https://*and*.comwith no scheme, which git does not. So the cost of a miss is silence, never a false warning.credential.<url>.helper, not only the best match. Withstoreunderhttps://github.com/and a made-up helper undergithub.com,GIT_TRACEshowscredential-store getrun, and then the second helper gives the login.-zbecause a url may hold a blank..*and not.+because[credential ""]applies to every url (measured).--get-allcalls so that the recorders intests/test_setup_project_dry_run_offers.py(config --get-all) still answer them._check_credential_helper's docstring now says which spellings of the key it reads.NIT-5,
~as git expands it (Direction 3). New_home_path(path)runsgit -c openreposhape.storefile=<path> config --type=path --get openreposhape.storefile._store_filesasks it for~/.git-credentials, and for~/.config/git/credentialswhereXDG_CONFIG_HOMEis unset or empty.With
HOMEunset, git exits 128 (failed to expand user dir), and no~file is named.The case with no file at all (
HOMEunset andXDG_CONFIG_HOMEunset or empty) gets a new sentence, which a row pins byte for byte. Measured,storediesunable to set up default path; use --fileandfillexits 128:Decision: a failure with
HOMEset is not the measured case (it could be a git too old for--type)._home_pathreturns None there, andstore's files are then left alone, as a file this cannot read is.Git for Windows choosing its own
HOMEis covered by asking git, but it was not measured here: this host has no Windows.NIT-2 (Direction 4).
_why_no_loginno longer says "a file this cannot read, may answer"._holds_a_github_loginnow gives the reason, as measured: git'sstorereads a file it may not open as empty, and dies over a folder, so a push fails over either. But what such a file holds is not something this check can say, so it says nothing. The silence is kept.NIT-3 (Direction 5). The 106-character docstring line is rewrapped.
git grep "a file this cannot read, may answer" -- setup-project.pyfinds nothing (exit 1). No line fromURL_HELPER_KEYSto the end of_check_credential_helperis longer than 79 columns (count: 0).NIT-4 (Direction 6), and the test helpers.
GIT_CONFIG_SYSTEMholdscredential.helper=store,HOMEis an empty scratch folder, and the global config is scratch without the helper. It gives the[??], and silence once~/.git-credentialsholds a made-up login. RV1008's mutant R1 (--globalonly) fails it._a_real_git(...): real git through the module's ownrun, gh answered in-process and never run.GIT_CONFIG_SYSTEMandGIT_CONFIG_GLOBAL, withGIT_CONFIG_NOSYSTEM,GIT_CONFIG_COUNTandGIT_CONFIG_PARAMETERSdropped.HOMEandXDG_CONFIG_HOMEin scratch, and the cwd in a scratch folder underGIT_CEILING_DIRECTORIES.test_no_auto_maintenance.py's scan passes._a_credential_storekeeps answering the two keys itself, and runs every other git argv through_a_real_git's git, so its~is git's._SetsUpGitnow takes the recorder.Docs (Direction 7). I re-read README
:231,:406and:470and the handbook's matching lines. They still read true: an ssh machine has nothing missing and is offered nothing, and none of them quotes a printed line. SoAGENTS.md,docs/cli.md, the README and the handbook are unchanged.setup-project.pyis not a pinned copy.Fix round 1 (RV1022)
Committed locally as
f68f4b4d, on top of35da321e. It will be pushed once #1026 has fixed main.SHOULD-1, three rows.
test_a_url_git_gives_no_answer_for_keeps_the_warning:--get-urlanswering 128 still warns. It kills RV-M4.test_an_empty_helper_under_a_url_of_its_own_is_no_helper:[credential "https://github.com/"]holding onlyhelper =gives the no-helper[??]. It kills RV-M2b.RULES[this-org-in-capitals-*]: aDemoOrg/rule with--org DemoOrgstays silent. It kills RV-M3.NIT-1 (
setup-project.py:1356)._names_githubreads a port as git does: none, or 443 with zeros before it.credential fill::0443,:00443and an empty:give the login;:0,:000and:+443do not.port.lstrip("0") in ("", "443")would also count:0, so I wroteport == "" or port.lstrip("0") == "443".HELPER_URLSrows::0443(True, True) and:0(False, False).NIT-2 (
:1501,:1534). Anhttp://answer keeps both warnings, because git asks a helper over http too (measured). New rows:RULES[plain-http-*]. The docstring says the checks behind the warnings still read only github.com's https keys and logins.NIT-3 (
:1522). The docstring now names a known limit:--get-urlreads the starting folder's.git/config, and the scaffold pushes from its own repositories. So a rule kept only there silences both warnings for pushes it does not reach.NIT-B and NIT-C (RV1022d; commit
3df8c515, pushed in the mergec7adebc5of origin/main051176ce, so the "locally" and "will be pushed" above are as off68f4b4d). The rule comments intests/test_setup_project_py.pylistDEMOORG_IN_CAPITALS_OVER_SSHandEVERY_ORG_OVER_HTTPand say git sends the urls "over HTTP or HTTPS";HELPER_URLSgains("https://github.com:000", False, False)beside:0, which git'scredential filldoes not treat as 443. The mutantport.lstrip("0") in ("", "443") and port != "0"fails that row alone (1 failed, 100 passed in the-ksubset), andsetup-project.pywas restored byte for byte. At the merge:test_setup_project_py.py340 passed,test_setup_project_dry_run_offers.py13 passed,test_no_auto_maintenance.py60 passed.Tests at
f68f4b4d, one file at a time, under the same stub gh as below:test_setup_project_py.pytest_setup_project_dry_run_offers.pytest_setup_sh.pytest_readme_windows_quoting_sentence.pytest_agents_md_codes.pytest_cli_reference.pytest_update_add_codes_documented.pytest_repo_hygiene.pytest_no_auto_maintenance.pyNo
shell_quotedcall was added (count 0).Mutants, each re-made against the
-k "credential or rule or helper or home or store or url"subset. All are killed:test_a_url_git_gives_no_answer_for_keeps_the_warningtest_an_empty_helper_under_a_url_of_its_own_is_no_helperRULES[this-org-in-capitals-*]rowsHELPER_URLS[https://github.com:0443-True-True]:0counted)HELPER_URLS[https://github.com:0-False-False]http://silences)RULES[plain-http-*]rowsLeft out of scope
credential.https://github.com/otherorg.helperbeside an emptystore, with--org demoorg.credential fillexits 128, because the key's path (otherorg) is not the push's (demoorg/...): git applies a key with a path to that path alone,credential.useHttpPathor not (RV1022d's NIT-A, measured)._names_githubreads past a path. setup-project.py: the credential-helper warnings speak where an insteadOf rule sends github.com over ssh, or a URL-scoped helper answers, and the check reads ~ files git does not read #1013's Direction 2 allows it ("or stay silent where such a key exists").insteadOfrule that rewrites to anhttps://url carrying its own login (https://x-access-token:...@github.com/, a CI idiom). Git asks no helper there either, but the answer starts withhttps://, so it still warns. That would be a follow-up if wanted.--org's. Both still warn, as today (see the SHOULD-1 decision).helper =lines are still dropped rather than applied as git's list resets. That predates this PR, and it can only make the check quieter.Verify:
--preflightat a real gitRun from a folder that is not the checkout, inside
env -i. The stub gh answers only--versionandauth statusand exits 97 on everything else.GH_TOKEN=bogus, an emptyGH_CONFIG_DIR,GIT_SSH_COMMAND=false, and the system config, global config andHOMEall in scratch. Git 2.43.0.main is
setup-project.pyblobf4a71ad7, the same at 2d6f928 and at dbf12e0. head is35da321e. The exit code is 0 in every row.insteadOftogit@github.com:, no helper[??][??]storeover nothingstore[??]storeover nothing, no rulestore[??]https://github.com/demoorg/,--org demoorg[??]--org other[??][??](unchanged)--org[??][??](unchanged)pushInsteadOfalone[??][??](unchanged)store+ made-up helper undercredential.https://github.com/.helperstore[??]HOMEunset,XDG_CONFIG_HOMEunset,store[??]naming<passwd home>/.git-credentialsand<passwd home>/.config/git/credentialsHOMEunset,XDG_CONFIG_HOMEset,store[??]naming<passwd home>/.git-credentialsand the XDG file[??]naming the XDG file aloneTests
One file at a time. Each run has the stub gh first on PATH (exit 97),
GH_TOKEN=bogus, an emptyGH_CONFIG_DIRandEMAIL=tests@openreposhape.invalid, and runs/usr/bin/python3 -m pytest -q -p no:cacheprovider --basetemp=<scratch>/bt-<name> -o tmp_path_retention_policy=failed.tests/test_setup_project_py.py(at head)tests/test_setup_project_py.py -k "credential or rule or helper or home or store"tests/test_setup_project_dry_run_offers.py(at head)tests/test_setup_sh.pytests/test_doctor_machine_row.pytests/test_readme_windows_quoting_sentence.pytests/test_agents_md_codes.pytests/test_update_add_codes_documented.pytests/test_repo_hygiene.pytests/test_cli_reference.pytests/test_no_auto_maintenance.pytests/test_setup_project_name_edges.py(drives the preflight in a subprocess; not in the brief's list)tests/test_github_name_limits.py(the same)tests/test_setup_project_py.pyon the three-way tree (main + #1011 + head)The rows marked "at head",
test_setup_project_name_edges.py,test_github_name_limits.pyand the three-way row ran at the pushed head. The-krow ran after the rewrap below and before the rebase. The other eight ran before two later steps: a rewrap of theNO_HELPERliteral in the test file, and the rebase onto dbf12e0, which touched neither file. None of those eight imports a helper this PR changed. 45 rows are new: 14RULESrows, 5BESIDE_STORE, 21HELPER_URLS, 2 system-scope, 2HOME-unset and 1_home_path-failure.Mutants
Each was applied alone to
setup-project.pyand run against the-ksubset above. All are killed._github_goes_over_httpscheckRULESrows (every-org-*,this-org-*)--orgRULES[this-org-none],[this-org-an-empty-store]credential.<url>.helperkeys readBESIDE_STORE,HELPER_URLS).+for.*inURL_HELPER_KEYSHELPER_URLS[-True-True]([credential ""])HELPER_URLS[https://github.com:8443-False-False]BESIDE_STORE[http://github.com/],HELPER_URLS[http://...],[ssh://...]BESIDE_STORE[https://GitHub.com],HELPER_URLS[https://GitHub.com/...]%XXdecodingHELPER_URLS[https://github%2Ecom-True-True]HELPER_URLS[https://github.com-True-True](the helper is counted once)~again (HOMEorexpanduser)test_with_home_unset_...rows andtest_a_home_git_will_not_expand_..._home_pathfailure means "no file"test_a_home_git_will_not_expand_with_home_set_is_left_alone_home_pathfailure means "cannot read"test_with_home_unset_...rowstest_with_home_unset_...[unset]--globalon both--get-alltest_a_store_in_the_system_config_...rowsNone in readcheck removedtest_a_home_git_will_not_expand_with_home_set_is_left_aloneXDG_CONFIG_HOMEignoredtest_a_credential_store_with_no_github_login_is_warned_about[no-file]Merges
git merge-tree --write-tree, all clean:dbf12e05: treedee27f30, the head's own tree.SHAPE_PIN_SOURCE_<LINK>the run guard passes over is named once by alink-source-collidesnote,--link-remedy=openXdox=a/openYdox=bgivesopenXdoxits words, and a row runs setup step (4) from another folder (#990, #991, part of #999) #1011l3-990-991-pinned-copiesf5749b7b: treeeabe258b.SHAPE_PIN_SOURCE_<LINK>the run guard passes over is named once by alink-source-collidesnote,--link-remedy=openXdox=a/openYdox=bgivesopenXdoxits words, and a row runs setup step (4) from another folder (#990, #991, part of #999) #1011) then head: treeeabe258b, made with two local scratch commits that were never pushed.test_setup_project_py.pypasses on it: 331 passed.family.py bumpnames--family-root, so each runs as printed (#927) #10023df537a4and with shape-doctor.py and shape_advisory.py: a path in the root this user cannot reach is named on every Python, never read as absent or stood in for by the template, a root it cannot search is refused first, and a row that could not ask is never DRIFTED #9753fd80cbe, which touch the README and the handbook: clean (d379d2ab,78afd489).setup-project.pyortests/test_setup_project_py.py(13 open PRs read).Lane: openreposhape-3 (openRepoShape-3)
🤖 Generated with Claude Code
Summary by Sourcery
Make credential preflight warnings reflect Git's actual transport, helper matching, and credential-store path behavior.
Bug Fixes:
gh auth setup-gitoffers when Git rewrites GitHub remotes to SSH or when matching URL-scoped credential helpers already provide authentication.HOMEis unset, including a dedicated warning when no store file is available.Enhancements:
Tests: