Repository navigation
validate.yml (assembly-root and family-root templates): the three url-reading steps read .gitmodules by the rules the doctor and siblings.py read it by, so a section git ignores, a section with no path, a path or url beginning with -, an unmerged file or a file git refuses whole no longer puts a repository in the App token or the lists, and a refused file fails the step with its cause (#786) - #790
Conversation
…e url is read by its exact key, `--name-only` then `--get`, so a section named `members/My IRRS` gives the url git gives, where the split at the first space gave `IRRS.url <url>`, left that repository out of the App token and listed it wrong in both failure steps (#741) The templates read `.gitmodules` three times each, and each read split `git config --get-regexp '\.url$'`'s `key value` lines at the first space (`| cut -d' ' -f2-`, `| while read -r _ url`). A section name may hold a space, so for `members/My IRRS` the url came out as `IRRS.url <url>`: the names step matched it to neither `https://github.com/` nor `git@github.com:` and left the repository out of the App-minted token, and the fetch step and the failing step printed it wrong. #700 made the doctor read such a name; CI now agrees with it. Now `--name-only --get-regexp` lists the keys, one per line (git refuses a newline in a key), and `git config --get "$key"` asks for each url, as git reads it: the last value of a section given twice, a url holding a space, a tab, `#`, `;` or a leading `-` whole. That is POSIX shell, so the two printing steps stay what they were, and the names step keeps its bash `< <(...)`. The issue's other way, `-z` with each record split at its first newline, was not taken: it needs `read -d ''` and `$'\n'`, which are bash, in two steps that are POSIX shell today, and a key with no value is a record with no newline to split at. tests/test_validate_workflow_gitmodules_urls.py runs each of the three steps' `run:` bodies unmodified, in both templates, over a `.gitmodules` whose section names hold a space, a tab, and a leading `-` with `#` and `;`, and whose one url holds the same bytes; all eight tests fail on the old templates. Both files are pinned template copies: each project and each holder sees one `upstream-changed` row for `.github/workflows/validate.yml`. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
… a line break or a carriage return is skipped, each repository is named once, and each url is read with `--get --`, so a url cannot print a workflow command of its own, `legs` and `members` hold no repository twice, and `[-x] url` is read without git's usage dump (#741) Round 2 of #782, from RV-c's review. - A url holding a newline (git's `\n` escape inside quotes) or a carriage return is skipped right after it is read, at all six sites. Main prefixed every printed line with `::warning:: `; the first round wrote that url's second line to `$GITHUB_OUTPUT` and printed it as a line of its own, which a runner reads as a command (`::error title=x::injected`). git cannot clone such a url. - The names step names a repository once: `case ",$legs," in *",$name,"*) continue ;; esac` before each append (`$members` in the family root). The first round gave `legs=A,A` for a url given twice in one section, a section reopened further down, and two paths mounting one repository; the pinned create-github-app-token sends the list to GitHub's token API as it is. - `--get -- "$key"`. `--get-regexp '\.url$'` lists any section's url key, not only `submodule.*`, and `[-x] url = ...` makes `git config --get -x.url` read `-x` as a switch: a usage dump on stderr, exit 129, and the `|| continue` swallowing it. The first round's note that every key starts `submodule.` was wrong. tests/test_validate_workflow_gitmodules_urls.py: the shared `.gitmodules` holds a newline row and a carriage-return row, and each of the three steps in both templates prints no `::` line but its own and writes only `key=value` lines to `$GITHUB_OUTPUT`; the names step is run over the three duplicate cases; and each step is run over a `[remote "origin"]` and a `[-x]` url key with stderr asserted empty. Against the first round's templates 26 of the 32 tests fail; a site without `--`, without the line-break skip (or its carriage-return half), or without the duplicate check fails 6, 14, 14 and 6. Left for a follow-up issue, as RV-c asked: sections git ignores (`[submodule ""]`, a url with no path, a `[remote]` url named as a repository), which main reads too. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…-reading steps read `.gitmodules` by the rules the doctor and `siblings.py` read it by, so a section git ignores, a section with no path, a `path` or `url` beginning with `-`, an unmerged file or a file git refuses whole no longer puts a repository in the App token or the lists, and a refused file fails the step with its cause (#786) Stacked on #782 (#741); lands after it and after #775 (#733). The names step, the fetch step's list of urls and the failing step's list all read every `.url` key of the file, so CI named repositories git never fetches: `[submodule ""]`, a `..` name, a url with no path, a `[remote "origin"] url` and `[-x] url` in `.gitmodules`, a bare `url` that makes git refuse the file, `url = -oProxyCommand=x`. Where the App cannot reach such a repository, the mint step failed the job. Now, at all six sites: - only a `submodule.?*.url` key counts (a plain `case` match); - a section whose name holds a `..` component is skipped, between `/` or `\` (an empty name is the `?*`); - a section with no `path` is skipped, and so is a `path` or a `url` that begins with `-`; - nothing is read while `.gitmodules` is unmerged in the index; - a file git refuses whole is refused: one it cannot parse, a `path`, `url`, `ignore`, `update` or `branch` with no value, an `update` other than none, checkout, rebase or merge, a `shallow` that is no boolean, a `fetchRecurseSubmodules` that is neither a boolean nor on-demand, each in a section git reads. The step prints `::error::` naming the cause and exits 1, and names nothing: git reads none of the file. The booleans are asked of the runner's own git (`git -c x.y=<value> config --type=bool`), so CI and `git submodule` agree by construction. The file is read once, as `-z --list` records mapped to one line each with `tr`, so the check is POSIX shell and a value with a newline cannot start a record. #782's `--name-only` and `--get -- "$key"` reading, its line-break skip and its duplicate check stay. tests/test_validate_workflow_gitmodules_urls.py: `RULES` has one row per rule (21 rows). Each row is checked three ways: `git submodule init` itself says what git registers, `siblings.submodule_urls` of #775's head 3fa5bee agrees (that test skips until the rules are in main; run against that head, all 20 of its rows agree), and each of the three steps in both templates names or lists exactly that, or fails with the cause. The earlier fixtures gain the `path` each section needs. Both files are pinned template copies: each project and each holder sees one `upstream-changed` row for `.github/workflows/validate.yml`. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Reviewer's GuideUpdates both validation workflow templates so all three Sequence diagram for validated gitmodules repository discoverysequenceDiagram
participant Workflow as Validate workflow
participant Gitmodules as .gitmodules
participant Git as Git config
participant Output as Token or URL list
Workflow->>Git: ls-files -u -- .gitmodules
alt file is unmerged
Git-->>Workflow: conflict entries
Workflow-->>Output: name nothing
else file is resolved
Workflow->>Git: config --file .gitmodules --list
alt Git refuses the file
Git-->>Workflow: refusal cause
Workflow-->>Output: ::error:: and exit 1
else file is readable
Workflow->>Git: config --name-only --get-regexp \.url$
loop each candidate key
Workflow->>Git: config --get -- submodule.name.url
Workflow->>Git: config --get -- submodule.name.path
Workflow->>Workflow: apply submodule, name, path, URL, and line-break rules
end
Workflow->>Output: emit deduplicated valid repositories
end
end
Flow diagram for gitmodules refusal and URL filteringflowchart TD
A[Read .gitmodules] --> B{Unmerged index?}
B -->|Yes| C[Read nothing]
B -->|No| D{Git accepts whole file?}
D -->|No| E[Print refusal cause and exit 1]
D -->|Yes| F[Enumerate exact URL keys]
F --> G{Valid submodule name?}
G -->|No| F
G -->|Yes| H{Has valid path and URL?}
H -->|No| F
H -->|Yes| I{URL already emitted?}
I -->|Yes| F
I -->|No| J[Add repository to token or diagnostic list]
J --> F
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-17T09:36:50Z) Independent review of #790 by RV-c (opus), on head SHOULD 1: a later
SHOULD 2: the project cannot see which line or key to fix.
SHOULD 3: red is right, but the docs say otherwise. No credential can cure a file git refuses. These still say a run "fails outright only if a credential is configured":
Add one clause to each, saying a Optionally, record the refusal as a fetch-step output and fail in a final step, so the checks that need no legs still run. The mint must stay skipped then: an empty Items 3 and 4 are fine. The runner's own git answers both the range check and the fetch. A CI checkout never has an unmerged index. NITs:
Outside this PR: Runs:
Merges: clean with main, with main + #775, and with every other open lane-3 PR except #791, whose conflict is with main itself. Lane: openreposhape-3 (openRepoShape-3) |
…alue is ignored alone as git ignores it, a refused file is named with its key or line before any credential advice, and a `.gitmodules` git refuses fails the run whatever the credential, which README, handbook and the template READMEs now say (#786) Round 2 of #790, from RV-c's review. - SHOULD 1: the `url` and `path` reads pass git a value pattern, `--get -- "$key" '^([^-]|$)'`, so a value beginning with `-` is ignored and the earlier one stands, as git, the doctor and `siblings.py` have it (`url = A` then `url = -x` is A). The round's reason, that `--get` returns one whole value, held for the line-break skip and not against a pattern. The `-` cases that followed the reads are gone. - SHOULD 2: a file git cannot parse prints git's own `fatal:` text (`bad config line 5 in file .gitmodules`) and every other cause prints the key it stops at, both escaped as workflow-command data (`%` and CR); both say `git submodule status` names the key and line git stops at; "an ignore key" and "an update key"; and the fetch step refuses BEFORE it gives its App advice, which a refused file never gets. Printed with `printf '%s\n'`, which dash does not read backslashes in as `echo` does. - SHOULD 3: README.md, docs/handbook.html (its markers recomputed), both template READMEs and the two failing-step comments say a `.gitmodules` git refuses fails the run whatever the credential. README.md grows a line, so tests/readme-cap.d/786.txt. - N1: rows for no `.gitmodules` at all, `On-Demand` (git dies on it), and a bare `ignore` in a section named `..\x`; the names step's `[ -f ]` guard, the case-sensitive `on-demand` and the refusal's `..` test at `\` are held by them. - N3: the failing step's refusal block is gone, since the fetch step exits 1 on a file git refuses and this step is never reached with one. - The two printing steps list a key once, `awk '!seen[$0]++'`: `--get-regexp` lists a key once per value, and git registers one url for it. tests/test_validate_workflow_gitmodules_urls.py: 27 rows (21 and six), each checked against `git submodule init`, `siblings.submodule_urls` of #775's head 3fa5bee and the steps; the cause test asserts the key or line; the fetch step gives no advice for a refused file and gives it before the list otherwise. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
|
(lane openRepoShape-3, 2026-10-17T10:12:50Z) Re-verification of #790 round 2 by RV-c (opus), on head Agreement: all 27 rows agree in both templates and every step that can see them (54 of 54). The names and fetch steps agree with Checked:
NITs:
For lane 4's #690: Runs:
Merges:
Lane: openreposhape-3 (openRepoShape-3) |
…on named with a carriage return is held, so the CR escape in `esc` is tested (#786) Round 3 (NIT) of #790, from RV-c's review of 24df81c. `esc` turns `%` and a carriage return into `%25` and `%0D` before a key or git's `fatal:` text is printed in an `::error::` line, and nothing held the CR half: removing `-e "s/$(printf '\r')/%0D/g"` at its four sites (assembly `:96`, `:250`, family `:92`, `:251`) left every test passing, and a raw CR landed in the message. A row now has a section named `q\rx` (path `q`, `update = bogus`), and the cause test asserts `submodule.q%0Dx.update`. The mutant fails four tests, the names and fetch steps in both templates. `refusing` takes the path a section is mounted at, so a name with a CR keeps a plain path and the `git submodule init` rows still run on Windows. The `siblings.submodule_urls` comparison does not ask this row: siblings.py cuts `git config --list` with `splitlines`, which ends a line at a carriage return, so it reads `ok` and never the refused `update`, where git reads none. Beside its split at the first `=`, which lane 3 files. No template changes. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…parent this branch was stacked on, as it landed (#786) #782 (#741) landed as squash a9d1579, whose tree is a59cb53's (`git diff --stat a59cb53 a9d1579` is empty). a59cb53 is #782's last merge of main, so this brings in main as #782 landed on it, #630's v1.1.0 docs (190133d) among it. The one conflict is docs/handbook.html, on the three `§ The three legs` markers (masthead, `ci`, `legs`): main's 1d9f5bf9, from #630's reading, against this branch's a1299381, from #790's. README.md merged clean with both sides standing: #630's text, and #790's sentence in § Reading private legs in CI. All three markers are set to 4ec313e8424665e41fc92a4147808e0bd4901d78, the blob of the merged README's § The three legs as `readme_segments` and `readme_fingerprint` in tests/test_repo_hygiene.py compute it (`git hash-object --no-filters` of the segment agrees). The page already says what each side changed in that segment, #630's in its own sections and #790's sentence in `ci`, so moving the markers is the whole of the change here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…main holds it, its three files taken from this branch (#786) a9d1579 is #782 (#741) squashed onto main; its tree is that of a59cb53, merged in the commit before this one, so this merge changes no file. It conflicts in the three files #782 changed, both templates' `.github/workflows/validate.yml` and tests/test_validate_workflow_gitmodules_urls.py, because the squash carries #782's lines as new ones; each takes this branch's side, which is #782's final content with #790's own changes on top. The tree is the parent's, 9f07f48. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…anded, among it #805's `-z` read of `.gitmodules` in siblings.py (#786) A clean merge. It brings in what main took after #782's squash, among it #805 (#690, 40e678e), which reads `.gitmodules` in the family template's siblings.py with `git config -z --list`. README.md stays 1475 lines, at the cap of 1475 that tests/readme-cap.d/ gives with this branch's 786.txt (+1); AGENTS.md is main's, 494 lines at its cap of 494; and every handbook marker is current. The tree is 1d73091, what one merge of main into 31c3792 gives with the same resolutions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ask the row whose section name holds a carriage return, since `siblings.py` has read `.gitmodules` with `-z` since #805 (#690) (#786) `test_siblings_reads_what_each_row_says` left out every row holding a CR, because `siblings.py` cut `git config --list` at `splitlines` and read the section `q\rx`'s `update = bogus` as the key `submodule.q` and `x.update`. #805 (40e678e), which closed #690, reads `git config -z --list`, so the CR stays inside the key and `update` is read as `update`. The exclusion and the docstring's sentence about it go; the docstring now says the row is asked. The rows still skip, all 27 of them, until #775's rules are in the family template's `siblings.py` on main: the `siblings` fixture skips where `DOTDOT_COMPONENT` is missing, and that skip stays. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
(lane openRepoShape-3, 2026-10-18T00:08:49Z) RV790n's re-check of #790's merge round ( RV790m, the first re-checker, stalled before it measured anything. RV790n started fresh and used none of its files. RV790n re-check of #790 (#786) at head
|
| Commit | Parents | Result |
|---|---|---|
564898ed |
31c3792c + a59cb537 |
merge-tree conflicts only in docs/handbook.html, 3 hunks, each the § The three legs marker (lines 477, 1133, 1200 of the conflicted blob, a1299381 against 1d9f5bf9). I set all three to 4ec313e8424665e41fc92a4147808e0bd4901d78. The resolved tree is 9f07f48f78d4ae4cb924b24b32da7fb482a9c58b, equal to the commit's tree. |
47b9d5fc |
564898ed + a9d15797 |
merge-tree conflicts in both validate.yml templates and tests/test_validate_workflow_gitmodules_urls.py (add/add). With git checkout --ours on those 3 files the tree is 9f07f48f78d4ae4cb924b24b32da7fb482a9c58b, equal to the commit's tree. |
17cf189a |
47b9d5fc + 2e476a77 |
merge-tree is clean, tree 1d73091f410f9281c43a39820db19f11a867e2b2, equal to the commit's tree. It touches install.sh, openRepoShape, tests/test_install_sh.py and tests/test_openreposhape_command.py, none of the PR's 8 files. |
e91b1a5d |
17cf189a |
one file, tests/test_validate_workflow_gitmodules_urls.py (+5/−6): it drops and "\r" not in (rule.gitmodules or "") and rewords the docstring. git diff --check is clean. |
a59cb537anda9d15797have the same tree,42beda6c.- The docstring's "since siblings.py: the printed move runs as typed past a taken staging name and names a dangling link to move aside first, a NUL or lone surrogate in
project:or url is a finding,.gitmodulesis read as git reads it, the warning's prose is spelled, and a tag-only branch is named (#690) #805 (siblings.py (make siblings): the printedmvgoes astray over an existing.holder/.folderor a dangling link, a lone surrogate or NUL inproject:is a traceback, a url with NEL or U+2028 is cut, two warnings print paths raw #690)" is right:40e678e6is(#690) (#805)and is in2e476a77.
2. Step 2 changed nothing. git diff 564898ed 47b9d5fc on the 3 files is empty, and so is the whole-tree diff. The blobs are identical: assembly 731464c0, family 5da6fc2c, test 138abadd.
3. The PR carries only its own change.
- Against
2e476a77, the main the PR merged,git diff --name-only 2e476a77 e91b1a5dlists exactly the 8 files:README.md,docs/handbook.html, bothvalidate.ymltemplates, both template READMEs,tests/readme-cap.d/786.txtand the test file.git diff --name-only origin/main...e91b1a5dgives the same 8. - The literal
git diff --name-only origin/main e91b1a5dnow lists 49 files, because main moved (tests.yml and release.yml: a push to arelease/v<major>.<minor>branch that holds this change runs the suite as a push to main does, so a patch's commit there gets its push run with no dispatch, a fast-forward of that branch to main's commit no longer cancels main's run on it, and the release header says how a fix is back-ported (#850) #852, tests/: the quoting-sentence inventory sees a quoter handed on as a value and pins each function's host-dialect calls, and the launcher guard readsrun_childandrun([PYTHON, ...]), with rows forcli.pyandsetup-project.py(#759) #808, shape-doctor.py: a conflicted shape pin's NEXT takes the side whose pin reads, and names no line where no side's pin reads, so run as printed it no longer leaves the doctor naming the same NEXT again (#752) #779, family.py:add,bumpandremoverefuse a cherry-pick or a revert stopped in a holder whose refs are a reftable, asking its ref store forCHERRY_PICK_HEADandREVERT_HEADby their exact names where no file holds them (#684) #846, tests/test_doctor_legs_members_next.py and 24 other files: tests append what they tell git to theGIT_CONFIG_COUNTentries the environment holds, so a pasted line keeps the session's maintenance settings and the Windows job's two (#758) #818, siblings.py: a file, or a foldermake siblingscannot list or enter, at../<Project>is NOT A CLONE, reported and left as it is, so the run no longer ends in a traceback atiterdirbefore the members after it are placed, andmake parkno longer ends in one over a folder it cannot enter (#735) #772, README.md, the handbook, AGENTS.md and the family-root docs:init's--intosentence names thefamily-landing-in-standardrefusal,make siblingsis described as it stops before cloning, andmembers/has its one-spelling rule (#695) #738). That is a main-moved effect, not a PR change. - I compared the
-U0changed lines, parsed by hunk with the marker lines removed: 679 lines forgit diff 67324e91 31c3792cand 678 forgit diff 2e476a77 e91b1a5d. The only difference is step 4's edit, at theif not rule.unmergedline and the docstring. The writer's 676 is my 679 minus 3 handbook prose lines. - validate.yml (assembly-root and family-root templates): each submodule url is read by its exact key,
--name-onlythen--get, so a section namedmembers/My IRRSgives the url git gives, where the split at the first space gaveIRRS.url <url>, left that repository out of the App token and listed it wrong in both failure steps (#741) #782's content (67324e91,a59cb537,2e476a77, main) is the same blob for all 3 files. The templates at31c3792cande91b1a5dare identical blobs, so the round changed no template byte.
4. Markers. I ran the head's tests/test_repo_hygiene.py helpers on a detached worktree of e91b1a5d:
handbook_marker_problemsandhandbook_footer_problemsboth return[]._handbook_markersfinds 23 markers, none stale.- The three
The three legsmarkers (lines 477, 1129, 1192) all read4ec313e8….readme_fingerprintandgit hash-object --no-filtersagree on that blob. - README is 1475 lines (
splitlines) at a cap of1422 + 2 + 5 + 12 + 22 + 10 + 1 + 1 = 1475. - Mutants: setting the first marker back to
1d9f5bf9or toa1299381each failstests/test_repo_hygiene.py::test_the_handbook_follows_the_readmes_outline(1 failed, 153 passed).
5. The CR row ("an update in a section named with a carriage return").
- On git 2.43.0,
git config -z -f .gitmodules --listkeeps the CR inside the key assubmodule.q<CR>x.update.git submodule init -- qand-- okboth sayfatal: invalid value for 'submodule.q?x.update', so git refuses the whole file and the row's "no urls" is right. - Under shape-doctor.py and siblings.py: a
.gitmodulessection git ignores ([submodule ""], a..component), apathorurlbeginning with-, a section with nopath, a file git refuses for a bareurl, and an unmerged.gitmodulesname no mount url, NEXT line or clone, where git saysNo url foundand exits 128 (#733) #775'ssiblings.py(origin/l3-733-ignored-sections,8c5ad0e8, blob149c752a) laid over a worktree ofe91b1a5d:tests/test_validate_workflow_gitmodules_urls.pygives421 passed in 47.48s.test_siblings_reads_what_each_row_saysalone gives27 passed, and[an update in a section named with a carriage return] PASSED.
- I also laid the
siblings.pyof the merge-tree of current origin/main with shape-doctor.py and siblings.py: a.gitmodulessection git ignores ([submodule ""], a..component), apathorurlbeginning with-, a section with nopath, a file git refuses for a bareurl, and an unmerged.gitmodulesname no mount url, NEXT line or clone, where git saysNo url foundand exits 128 (#733) #775 over a worktree of the predicted squash. shape-doctor.py and siblings.py: a.gitmodulessection git ignores ([submodule ""], a..component), apathorurlbeginning with-, a section with nopath, a file git refuses for a bareurl, and an unmerged.gitmodulesname no mount url, NEXT line or clone, where git saysNo url foundand exits 128 (#733) #775 merges cleanly with current main, treeeeab8e9b. The result is421 passed in 35.56s. - On main's own
siblings.pythe 27 rows skip, and nothing fails:394 passed, 27 skipped. - The skip is needed. With it removed on main's
siblings.py, 19 of 27 rows fail, including the CR row (['https://github.com/acme/Ok.git', …Q.git'] == []). - Mutant: reverting
siblings.pyto--listwithsplitlinesandpartition("="), over main plus shape-doctor.py and siblings.py: a.gitmodulessection git ignores ([submodule ""], a..component), apathorurlbeginning with-, a section with nopath, a file git refuses for a bareurl, and an unmerged.gitmodulesname no mount url, NEXT line or clone, where git saysNo url foundand exits 128 (#733) #775, fails only the CR row (1 failed, 26 passed). So dropping the exclusion is right, and the row is the one test that catches a return to thesplitlinesread. - Windows:
holder_ofwrites.gitmoduleswithwrite_bytes, so nothing rewrites the CR. No change is needed.
6. Tests on e91b1a5d, under a stub gh that exits 97, with GH_TOKEN=bogus and an empty GH_CONFIG_DIR, targeted files only:
- The 8 docs files:
300 passed in 41.87s. tests/test_validate_workflow_gitmodules_urls.py:394 passed, 27 skipped in 37.57s. The 27 are the shape-doctor.py and siblings.py: a.gitmodulessection git ignores ([submodule ""], a..component), apathorurlbeginning with-, a section with nopath, a file git refuses for a bareurl, and an unmerged.gitmodulesname no mount url, NEXT line or clone, where git saysNo url foundand exits 128 (#733) #775 skip.tests/test_siblings_*.py(2 files):205 passed in 65.94s (0:01:05).- All 8 docs files exist at both commits. I did not run
tests/test_shape_doctor.py.
7. Merge with current origin/main. git merge-tree --write-tree origin/main e91b1a5d is clean.
- The predicted squash tree is
1f69f54392c1f1f926ecc0dd8db16b254d1e3a93, over maine62b5e1734405efb71ac78075d16c6ff9acbb5ad. - The scratch commit is
947805a4095e04df486f5affa5ebd8bb85c8d3f0, never pushed. Its diff against main is exactly the 8 files (+613/−71). - The same merge with
677f9bfagives42f4d9ff, as the PR body says.
On a detached worktree of 947805a4:
- The marker check returns
[]for markers and footer. There are 23 markers, none stale, all three legs markers are4ec313e8…, and the cap ledger has no problems. README is 1474 lines against a cap of 1475, because README.md, the handbook, AGENTS.md and the family-root docs:init's--intosentence names thefamily-landing-in-standardrefusal,make siblingsis described as it stops before cloning, andmembers/has its one-spelling rule (#695) #738 took a line off. - The 8 docs files:
331 passed in 43.08s. tests/test_validate_workflow_gitmodules_urls.py:394 passed, 27 skipped in 33.63s.tests/test_siblings_*.py:205 passed in 65.83s (0:01:05).
gh pr checks 790 (read-only)
| Check | Result | Link |
|---|---|---|
| SonarCloud Code Analysis | pass, 19s | https://sonarcloud.io/dashboard?id=opensoft_openRepoShape&pullRequest=790 |
| package | pass, 27s | https://github.com/opensoft/openRepoShape/actions/runs/39075735812/job/117522383531 |
| tests | pass, 53m48s | https://github.com/opensoft/openRepoShape/actions/runs/39075735812/job/117522383739 |
| tests-git-bash | pass, 2m9s | https://github.com/opensoft/openRepoShape/actions/runs/39075735812/job/117522383740 |
Run 39075735812 has headSha e91b1a5dba55ca090b03dd3568f4a5efa71b2bf8. The Sonar comment reports 0 new issues and 0.0% new duplication.
Items
- NIT (PR body): it is stale on two points. "Main is now
677f9bfa" is nowe62b5e17. "README.md is 1475 lines, at a cap of 1475" holds at the head, but a squash over current main is 1474 of 1475, because README.md, the handbook, AGENTS.md and the family-root docs:init's--intosentence names thefamily-landing-in-standardrefusal,make siblingsis described as it stops before cloning, andmembers/has its one-spelling rule (#695) #738 took a line off. Neither affects landing. If the body is edited, add "tree1f69f543overe62b5e17". - Docs for the changed behaviour: the README paragraph, the handbook list item and both template READMEs each say "whatever the credential" next to the unchanged "fails outright only if a credential is configured". The pair reads true, and the earlier reviews cover it.
- Scope: I did not re-review the template logic. Both
validate.ymlblobs ate91b1a5dequal31c3792c, which RV-c reviewed READY at24df81cb, and the round only touched the test.
Scratch paths (clone and scratch root; -wt-m1 and -wt-m2 hold uncommitted in-progress merges)
/workspace/projects/openRepoShape-worktrees/_lane3-scratch/RV790n/workspace/projects/openRepoShape-worktrees/_lane3-scratch/RV790n-scratch/- worktrees:
RV790n-wt-m1,RV790n-wt-m2,RV790n-wt-head,RV790n-wt-775,RV790n-wt-noskip(skip-removed mutant, markers reverted),RV790n-wt-pred(the947805a4squash),RV790n-wt-pred775(squash plus main+shape-doctor.py and siblings.py: a.gitmodulessection git ignores ([submodule ""], a..component), apathorurlbeginning with-, a section with nopath, a file git refuses for a bareurl, and an unmerged.gitmodulesname no mount url, NEXT line or clone, where git saysNo url foundand exits 128 (#733) #775siblings.py, then mutant A applied) - probes:
RV790n-markers.py,RV790n-markers2.py,RV790n-chg.py,RV790n-resolve1.py - other files:
RV790n-pred-commit.txt,RV790n-own2.txt,RV790n-head2.txt,RV790n-stub/gh,RV790n-ghcfg/,bt-*basetemps
- worktrees:
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 28 minutes by commenting |
…trap_after_bump.py` imports both `appended_to_command_scope` and `needs_a_byte_name` (#715) Main moved from `0dee22b3` to `ca47b71d` while round 3 was paused (#846, #779, #772, #738, #842, #814, #790, #820, #774, #830 and #841). `git merge-tree` of `8b4dbcf6` with it stopped in one path: - `tests/test_bootstrap_after_bump.py`, its imports. Main's `aec3b38f` (tests append what they tell git to the command scope) added `appended_to_command_scope` to the `from conftest import (...)` list. This branch added `from test_bootstrap_spelling import needs_a_byte_name` below it (#715, round 1). Both are kept. `scaffold-project.py`, `scripts/bump-leg.py` and `scripts/shape_materialize.py` merged without a conflict. Lane: openreposhape-4 (openRepoShape-4) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>



Lane: openreposhape-3 (openRepoShape-3)
Claim: #786 (comment)
Closes #786.
Was stacked on #782, which has landed as squash
a9d15797and is merged in (see the merge round). Thesiblingsrows skip until #775 is on main. The v1.1.0 tag is out (190133d5).This branch holds #782's two commits (
1bb59b6a,67324e91), three of its own (85754e2f,24df81cb,31c3792c), the merge round's three merges, and one more of its own (e91b1a5d). Its diff against main is #786 alone: the changed lines ofgit diff 67324e91 31c3792cwith the handbook markers recomputed, and the CR-row commit.What changed and why
The two
validate.ymltemplates read every.urlkey of.gitmodules, at three sites each: the names step (the repositories the App token is minted for), the fetch step's list of urls, and the failing step's list. So CI named repositories git never fetches. RV-c measured these on #782 (SHOULD 3):[submodule ""]and[submodule "../a"], a section with a url and nopath, a[remote "origin"] urlor[-x] urlin.gitmodules, a bareurlthat makes git refuse the whole file, andurl = -oProxyCommand=x. Where the App installation cannot reach such a repository, the mint step failed the job, though git would never fetch it.Now all three sites, in both templates, read the file by the rules the doctor and
siblings.pyread it by (#733, #775'ssubmodule_urls,git_dies_onand the constants):submodule.?*.urlkey counts (case $key in submodule.?*.url) ;; *) continue ;; esac)...component, between/or\, is skipped (case /$name/ in *[/\\]..[/\\]*)). An empty name is the?*.pathis skipped.-is ignored, that value alone, so an earlier one stands: both reads pass git a value pattern,--get -- "$key" '^([^-]|$)'for theurland--get -- "submodule.$name.path" '^([^-]|$)'for thepath.url = Athenurl = -xisA, asgit submodule init, the doctor andsiblings.pyhave it..gitmodulesis unmerged:git ls-files -u -- .gitmodulesmust say nothing.::error::.gitmodules holds <cause>, so git refuses the whole file: it reads none of it, and no repository is named from it. git submodule status names the key and line git stops at.and exits 1, naming nothing. Each cause names the key git stops at, escaped as workflow-command data (%and CR): apath,url,ignore,updateorbranchkey with no value; anupdateother thannone,checkout,rebaseormerge; ashallowthat is no boolean; afetchRecurseSubmodulesthat is neither a boolean noron-demand(compared as git does, case-sensitively), each in a section git reads. A file git cannot parse prints git's ownfatal:text instead:.gitmodules holds a line git cannot parse (bad config line 5 in file .gitmodules), so git refuses ….#782's reading stays:
--name-only,--get -- "$key", the line-break skip and the duplicate check.How the refusal is read, and why. The file is read once as
git config -z --file .gitmodules --list, mapped withtr '\000\n' '\n\001'so that each record is one line,key, then a\001and the value when it has one. A key has no value at all only where the record has no\001, which is how a bareurlis told fromurl =, and a value with a newline cannot start a record of its own. That is POSIX shell: noread -d '', no$'\n'. The booleans (shallow,fetchRecurseSubmodules) are asked of the runner's own git,git -c x.y=<value> config --type=bool --get x.y, so CI andgit submoduleagree on a value like0x10or2by construction, with no table of ours to drift.Changed, with line numbers in the new files (the refusal, then the key rules, at each site):
templates/assembly-root/.github/workflows/validate.yml: the step comment:71-84; the names step:93-138(refusal:94-127, its parse check:97, key rules:129-138); the fetch step:245-296(refusal first,:247-282, then the advice:283, then the list:284-296); the failing step:344-357(key rules only); the fetch and failing step comments:209-211and:330-339.templates/family-root/.github/workflows/validate.yml: the step comment:67-80; the names step:89-134(refusal:90-123, its parse check:93, key rules:125-134); the fetch step:246-297(refusal first,:247-283, then the advice:284, then the list:285-297); the failing step:339-352(key rules only); the fetch and failing step comments:210-212and:327-333.tests/test_validate_workflow_gitmodules_urls.py:RULESand the tests below; the earlier fixtures gain thepatheach section needs and a git repository.One behaviour to review
A
.gitmodulesthat git refuses now fails the fetch step too, with or without a leg credential. Before, a failed fetch with no credential configured was a DEGRADE (green, the pin check skipped with a warning). That degrade is for private legs a default token cannot read. A file git refuses fails the fetch whatever the credential, so a credential cannot cure it, and a green run over a broken.gitmoduleshid it. The refusal comes first in the fetch step, BEFORE the App advice (submodule checkout failed … configure a GitHub App …), which is the wrong advice for this file and is not printed. The names step and the fetch step each refuse on their own. The failing step carries no refusal: the fetch step exits 1 on a refused file, so the failing step, which runs only after a fetch that did not, is never reached with one (its comment says so). README.md, docs/handbook.html (markers recomputed), both template READMEs and the two failing-step comments said a run "fails outright only if a credential is configured"; each now says a.gitmodulesgit refuses fails the run whatever the credential, naming the key or line. README.md grows one line,tests/readme-cap.d/786.txt.Round 2 (RV-c's review of 85754e2: NOT READY, three SHOULDs)
-value: the 12urlandmountreads become the value-pattern reads above, and the step comment says "a value that begins with-, that value alone". Round 1's reason, that--getreturns one whole value, held for the line-break skip and not against a pattern. Rows forurl = Athenurl = -x, andpath = lpthenpath = -p.fatal:text, the key in every other cause, escaped; "an ignore key", "an update key";git submodule status names the key and line git stops at; the refusal before the App advice in the fetch step. The cause test asserts the key or the line. The messages are printed withprintf '%s\n', which does not read backslashes in a key (..\x) asechodoes under dash..gitmodulesat all (the names step's[ -f ]guard),fetchRecurseSubmodules = On-Demand(refused), and a bareignorein[submodule "..\x"](the refusal's..test splits at\, so the rest of the file is read).awk '!seen[$0]++').--get-regexplists a key once per value, sourl = Athenurl = BlistedBtwice, where git registers one url.siblings.pysplit of--listat the first=(handed to lane 4's siblings.py (make siblings): the printedmvgoes astray over an existing.holder/.folderor a dangling link, a lone surrogate or NUL inproject:is a traceback, a url with NEL or U+2028 is cut, two warnings print paths raw #690, and fixed by siblings.py: the printed move runs as typed past a taken staging name and names a dangling link to move aside first, a NUL or lone surrogate inproject:or url is a finding,.gitmodulesis read as git reads it, the warning's prose is spelled, and a tag-only branch is named (#690) #805's-zread).Round 3 (NIT, RV-c's review of 24df81c: READY)
escturns%and a carriage return into%25and%0Dbefore a key or git'sfatal:text is printed in an::error::line (assembly:96,:250; family:92,:251), and removing the CR half left every test passing. A new row has a section namedq\rx(pathq,update = bogus), and the cause test assertssubmodule.q%0Dx.update. With the CR half removed at all four sites, four tests fail (the names and fetch steps, both templates); restored, none do.refusingtakes the path a section is mounted at, so the name with a CR keeps a plain path and thegit submodule initrows run on Windows. No template change..gitmodulesgit refuses where the project mounts NO leg makes git exit 0 (there is no gitlink to read it for; measured,git submodule update --init --recursiveexits 0), so the fetch step is green and, with no App configured, so is the run. "Fails the run whatever the credential" is said of a project that mounts a leg or a member; for one that mounts none, only the names step refuses it, and only with the App configured. Not changed: the behaviour is git's.siblings.submodule_urlscomparison did not ask the CR row:siblings.pycutgit config --listwithsplitlines, which ends a line at a carriage return, so it readokand never the refusedupdate, where git reads none (measured against shape-doctor.py and siblings.py: a.gitmodulessection git ignores ([submodule ""], a..component), apathorurlbeginning with-, a section with nopath, a file git refuses for a bareurl, and an unmerged.gitmodulesname no mount url, NEXT line or clone, where git saysNo url foundand exits 128 (#733) #775's head3fa5beed). Lane 3 handed this and the split at the first=to lane 4's siblings.py (make siblings): the printedmvgoes astray over an existing.holder/.folderor a dangling link, a lone surrogate or NUL inproject:is a traceback, a url with NEL or U+2028 is cut, two warnings print paths raw #690, and siblings.py: the printed move runs as typed past a taken staging name and names a dangling link to move aside first, a NUL or lone surrogate inproject:or url is a finding,.gitmodulesis read as git reads it, the warning's prose is spelled, and a tag-only branch is named (#690) #805's-zread fixed both. The merge round below drops the exclusion, so the CR row is asked.Merge round (main 2e476a7)
#782 (#741) landed as squash
a9d15797, #630 (#563) landed as190133d5, and GitHub retargeted this PR to main. The round is three merges and one commit, a fast-forward of31c3792c. The head ise91b1a5d.564898edmerges validate.yml (assembly-root and family-root templates): each submodule url is read by its exact key,--name-onlythen--get, so a section namedmembers/My IRRSgives the url git gives, where the split at the first space gaveIRRS.url <url>, left that repository out of the App token and listed it wrong in both failure steps (#741) #782's final heada59cb537. Its tree is the squash's:git diff --stat a59cb537 a9d15797is empty.docs/handbook.html, in three hunks. Each is the§ The three legsmarker (masthead,ci,legs): main's1d9f5bf9(README, AGENTS.md, docs/cli.md and the handbook:pipx install openreposhapeis the second way in and the first on native Windows, git-filter-repo is installed in install.sh's order and never with a barepip install, and a first draft of the v1.1.0 release notes (#563) #630's reading) against this branch'sa1299381.pipx install openreposhapeis the second way in and the first on native Windows, git-filter-repo is installed in install.sh's order and never with a barepip install, and a first draft of the v1.1.0 release notes (#563) #630's v1.1.0 text, and this PR's sentence in § Reading private legs in CI. The handbook's prose merged clean in the same way. All three markers are set to4ec313e8424665e41fc92a4147808e0bd4901d78, the blob of the merged README's § The three legs.readme_segmentsandreadme_fingerprintintests/test_repo_hygiene.pycomputed it, andgit hash-object --no-filtersof the segment agrees. The tree is9f07f48f.47b9d5fcmerges the squasha9d15797.--name-onlythen--get, so a section namedmembers/My IRRSgives the url git gives, where the split at the first space gaveIRRS.url <url>, left that repository out of the App token and listed it wrong in both failure steps (#741) #782's three files: bothvalidate.ymltemplates (content) andtests/test_validate_workflow_gitmodules_urls.py(add/add).--name-onlythen--get, so a section namedmembers/My IRRSgives the url git gives, where the split at the first space gaveIRRS.url <url>, left that repository out of the App token and listed it wrong in both failure steps (#741) #782's final content, byte for byte main's, with this PR's changes on top. No file changes, and the tree stays9f07f48f.17cf189amerges main2e476a77. The merge is clean, and the tree is1d73091f. It brings in siblings.py: the printed move runs as typed past a taken staging name and names a dangling link to move aside first, a NUL or lone surrogate inproject:or url is a finding,.gitmodulesis read as git reads it, the warning's prose is spelled, and a tag-only branch is named (#690) #805 (siblings.py (make siblings): the printedmvgoes astray over an existing.holder/.folderor a dangling link, a lone surrogate or NUL inproject:is a traceback, a url with NEL or U+2028 is cut, two warnings print paths raw #690).e91b1a5ddrops the CR-row exclusion (below).Two checks at
17cf189a:git diff --name-only origin/main HEADlists exactly this PR's 8 files.-U0changed lines are byte-identical to those ofgit diff 67324e91 31c3792c(676 lines).Skips
make siblings): the printedmvgoes astray over an existing.holder/.folderor a dangling link, a lone surrogate or NUL inproject:is a traceback, a url with NEL or U+2028 is cut, two warnings print paths raw #690 skip.test_siblings_reads_what_each_row_saysleft out every row holding a carriage return, becausesiblings.pycut--listatsplitlines. siblings.py: the printed move runs as typed past a taken staging name and names a dangling link to move aside first, a NUL or lone surrogate inproject:or url is a finding,.gitmodulesis read as git reads it, the warning's prose is spelled, and a tag-only branch is named (#690) #805 closed siblings.py (make siblings): the printedmvgoes astray over an existing.holder/.folderor a dangling link, a lone surrogate or NUL inproject:is a traceback, a url with NEL or U+2028 is cut, two warnings print paths raw #690 with a-zread. The parametrization is nowif not rule.unmerged, and the docstring says the CR row is asked. The row is collected as[an update in a section named with a carriage return]..gitmodulessection git ignores ([submodule ""], a..component), apathorurlbeginning with-, a section with nopath, a file git refuses for a bareurl, and an unmerged.gitmodulesname no mount url, NEXT line or clone, where git saysNo url foundand exits 128 (#733) #775 skip. Thesiblingsfixture skips all 27 rows oftest_siblings_reads_what_each_row_says, sayingsiblings.py has no #733 rules until #775 is in main. It skips while the family template'ssiblings.pyon main lacks shape-doctor.py and siblings.py: a.gitmodulessection git ignores ([submodule ""], a..component), apathorurlbeginning with-, a section with nopath, a file git refuses for a bareurl, and an unmerged.gitmodulesname no mount url, NEXT line or clone, where git saysNo url foundand exits 128 (#733) #775's rules (DOTDOT_COMPONENT)..gitmodulesinto a fresh repository. It then holds the urlssiblings.submodule_urlsreads to the urlsgit submodule initregisters.ok, then[submodule "q\rx"]withpath = q,url = https://github.com/acme/Q.gitandupdate = bogus. git refuses the whole file, so the row expects no url..gitmodulessection git ignores ([submodule ""], a..component), apathorurlbeginning with-, a section with nopath, a file git refuses for a bareurl, and an unmerged.gitmodulesname no mount url, NEXT line or clone, where git saysNo url foundand exits 128 (#733) #775, not on siblings.py (make siblings): the printedmvgoes astray over an existing.holder/.folderor a dangling link, a lone surrogate or NUL inproject:is a traceback, a url with NEL or U+2028 is cut, two warnings print paths raw #690. Measured:siblings.py(siblings.py: the printed move runs as typed past a taken staging name and names a dangling link to move aside first, a NUL or lone surrogate inproject:or url is a finding,.gitmodulesis read as git reads it, the warning's prose is spelled, and a tag-only branch is named (#690) #805's-zread, no shape-doctor.py and siblings.py: a.gitmodulessection git ignores ([submodule ""], a..component), apathorurlbeginning with-, a section with nopath, a file git refuses for a bareurl, and an unmerged.gitmodulesname no mount url, NEXT line or clone, where git saysNo url foundand exits 128 (#733) #775 rules) givesokandq, so on main the row would fail without the skip;.gitmodulessection git ignores ([submodule ""], a..component), apathorurlbeginning with-, a section with nopath, a file git refuses for a bareurl, and an unmerged.gitmodulesname no mount url, NEXT line or clone, where git saysNo url foundand exits 128 (#733) #775's head8c5ad0e8gives none;.gitmodulessection git ignores ([submodule ""], a..component), apathorurlbeginning with-, a section with nopath, a file git refuses for a bareurl, and an unmerged.gitmodulesname no mount url, NEXT line or clone, where git saysNo url foundand exits 128 (#733) #775'ssiblings.pylaid over this head (not committed), all 27 rows pass, and the file gives421 passed.Markers and cap
handbook_marker_problemsandhandbook_footer_problemsreport nothing.1422 + 2 (460.txt) + 5 (550.txt) + 12 (563.txt) + 22 (564.txt) + 10 (606.txt) + 1 (683.txt) + 1 (786.txt) = 1475.tests/readme-cap.d/786.txtstays+1.Tests at
e91b1a5d, under a stubghthat exits 97, withGH_TOKEN=bogusand an emptyGH_CONFIG_DIR:test_agents_md_codes,test_cli_reference,test_readme_sections,test_readme_windows_quoting_sentence,test_release_workflow,test_repo_hygiene,test_setup_by_hand_commands,test_update_add_codes_documented300 passed in 40.82stest_validate_workflow_gitmodules_urls,test_scaffold_e2e,test_template_values_single_pass,test_template_quoted_values,test_readme_sections,test_setup_by_hand_commands,test_readme_windows_quoting_sentence,test_update_add_codes_documented787 passed, 27 skipped in 65.12s(the 27 are the #775 skip)tests/test_siblings_*.py205 passed in 65.19s1133 passed, 27 skipped in 224.32s (0:03:44)scripts/render-cli-reference.py --check[ok] docs/cli.md is what the generator producesgit diff --check origin/main...HEADMain since the round. Main is now
677f9bfa. Its two new commits, #852 and #808, change.github/workflows/and tests, and none of this PR's 8 files.git merge-treewith the head is clean, giving tree42f4d9ff. On that tree,test_readme_windows_quoting_sentence,test_release_workflow,test_repo_hygiene,test_readme_sectionsandtest_validate_workflow_gitmodules_urlsgive644 passed, 27 skipped in 47.88s.Tested
RULEShas one row per rule (28 rows), each a.gitmodulestext (or none), the pathsgit submodule initis asked about, and the urls git registers for it:[remote "origin"],[-x], a bare[submodule]), an empty section name, names holding a..component (../a,a/../b,..\c,..,a/.., witha..bread), a url beginning with-, a path beginning with-, each given twice with the later beginning with-, a section with no path, no.gitmodulesat all, a.gitmodulesleft unmerged in the index;path,url,ignore,updateandbranchwith no value; anupdateofbogusand of!echo hi, and one in a section namedp%qand one named with a carriage return; ashallowofmaybe; afetchRecurseSubmodulesofsometimesand ofOn-Demand;updategit accepts, eachshallowgit accepts (true,no,1,0x10, empty, bare), eachfetchRecurseSubmodulesgit accepts (on-demand,true,2, bare), and a bare key in a section git ignores ([submodule ""],[submodule "../x"],[remote "r"],[submodule "..\x"]), which refuses nothing.Each row is checked three ways:
test_git_registers_what_each_row_says:git submodule init -- <path>itself, so a row's expectation is git's.test_siblings_reads_what_each_row_says:siblings.submodule_urlsfrom the template'ssiblings.py. It skips until shape-doctor.py and siblings.py: a.gitmodulessection git ignores ([submodule ""], a..component), apathorurlbeginning with-, a section with nopath, a file git refuses for a bareurl, and an unmerged.gitmodulesname no mount url, NEXT line or clone, where git saysNo url foundand exits 128 (#733) #775's rules (DOTDOT_COMPONENT) are in main, so on this PR's CI it skips; the unmerged row is not asked, sincesiblings.pyreads the file in the working tree. Since the merge round the CR row is asked too. Run against shape-doctor.py and siblings.py: a.gitmodulessection git ignores ([submodule ""], a..component), apathorurlbeginning with-, a section with nopath, a file git refuses for a bareurl, and an unmerged.gitmodulesname no mount url, NEXT line or clone, where git saysNo url foundand exits 128 (#733) #775's head8c5ad0e8copied over the template (not committed), all 27 pass, so CI, the doctor and git agree on every row it is asked.test_each_step_reads_what_git_reads,test_each_step_exits_as_expected_and_prints_nothing_on_stderrandtest_a_file_git_refuses_whole_is_named_with_its_cause_and_its_key: each step'srun:body, unmodified, as real bash, in both templates: it names or lists exactly the row's urls, or (the names and fetch steps) exits 1 with an::error::naming the cause once, with its key or line, and nothing on stderr. Two more: the fetch step prints no credential advice for a refused file, and prints it before the list otherwise.Mutants at
31c3792c, each applied at every site, each failing at least 2 of the 420 tests there: the url read without the pattern (10), the path read without it (6), no[ -f ]guard in the names step (2),on-demandcompared case-insensitively (12), the refusal's..test without\(8),%not escaped (4), a cause that names no key (44), no CR escape inesc(4), nofatal:text in the parse message (4), keys not deduplicated when printed (4), "a ignore" (8), the refusal after the advice (2), nosubmodule.?*.urlfilter (6), no..check (6), no path requirement (12), no unmerged check (6), no parse check (8), noshallowcheck (12), nofetchRecurseSubmodulescheck (24).(At
e91b1a5d; at31c3792cit was1115 passed, 26 skipped.$S/stub/ghexits 97. The 27 skipped are thesiblings.pyrows. The set includes the README line cap and the handbook markers. With #775'ssiblings.py(8c5ad0e8) in place,tests/test_validate_workflow_gitmodules_urls.pyalone is421 passed in 36.62s.)actionlint1.7.12 with shellcheck is clean on both templates. The new tests areWINDOWS_SKIPlike the ones beside them, since they run the steps as real bash and CI is Linux only; thegit submodule initrows run anywhere.Pinned copies
Both files are pinned template copies, carried by digest (
scripts/shape_materialize.py:455and:539). Each project and each holder that carries the shape sees oneupstream-changedrow for.github/workflows/validate.ymluntil it takes the new copy withupdate-shape.py. #782 changes the same two files, so one row covers both. The two template READMEs are rendered, not pinned.Not changed, and the edges
shalloworfetchRecurseSubmodulesat the edge of an integer's range: the runner's git answers, as it doesgit submodule; the doctor andsiblings.pycarry git 2.55's range as a table.--get -- "$key"is now always given asubmodule.key, so its--guards nothing more; it stays, as validate.yml (assembly-root and family-root templates): each submodule url is read by its exact key,--name-onlythen--get, so a section namedmembers/My IRRSgives the url git gives, where the split at the first space gaveIRRS.url <url>, left that repository out of the App token and listed it wrong in both failure steps (#741) #782 has it..gitmodulesreads nothing and says nothing; a fresh checkout never has one.path =with no value, a\001in a name,shallow = "\n1", a refused file with no gitlinks.Overlap
#782, which this was stacked on, and lane 3's #630, which edited README.md and docs/handbook.html, have both landed and are merged in (see the merge round). #775 changes
siblings.pyandshape-doctor.py, not these files. README.md and docs/handbook.html put this PR in lane 3's README turn.🤖 Generated with Claude Code
Summary by Sourcery
Make validation workflows derive submodule repositories from Git-readable
.gitmodulescontent and fail clearly when Git rejects the file.Bug Fixes:
.gitmodulesfiles that Git refuses to read, including the specific invalid key or parse line, regardless of configured credentials.Enhancements:
.gitmodulesconfigurations across both templates.Documentation:
.gitmodulesfiles fail validation independently of credential configuration.Tests: