Skip to content

[ci] fix #690: bound the target-map brace scan and self-test the parser - #769

Open
ritikpal1122 wants to merge 3 commits into
Travsr-com:masterfrom
ritikpal1122:fix/690-target-map-brace-scan
Open

[ci] fix #690: bound the target-map brace scan and self-test the parser#769
ritikpal1122 wants to merge 3 commits into
Travsr-com:masterfrom
ritikpal1122:fix/690-target-map-brace-scan

Conversation

@ritikpal1122

Copy link
Copy Markdown
Collaborator

Closes #690.

The bug

extractObjectValues() found the end of an object literal by scanning for the first line trimming to exactly };. With that line deleted it did not fail: it latched onto an unrelated later }; and pulled everything between into the literal. The guard still failed closed, but the message named the wrong problem, sending the reader to fix a target map that was fine.

Brace depth alone does not fix it

Worth flagging, because it contradicts the fix suggested in the issue. With pure depth counting the scan still does not stop: the rest of installer.ts re-balances and absorbs the missing brace, so it latches onto the last } in the file and extracts 10 phantom triples instead of 7. That is worse than the original bug.

So the scan combines quote-aware depth counting with an indentation bound: a map's entries are always indented under its declaration, so a non-blank line back at the declaration's own indentation means the literal should already have closed. Lines opening with {/} are exempt so the literal's own }; is not mistaken for the bound.

Three distinct reasons now exist: unclosed, unopened, malformed.

Before / after

BEFORE  ERROR: ... TARGET_MAP claims 'travsr', but release.yml build matrix
        ships no such artifact (guaranteed 404).       <- wrong problem

AFTER   ERROR: the closing brace of TARGET_MAP in packages/travsr-vscode/src/installer.ts
        (declared on line 25) is missing: the object literal is still open at line 30,
        which reads `export function resolveTargetTriple(`, which is back at the
        declaration's own indentation and so cannot be part of the literal.
        Restore the "};" that closes TARGET_MAP.

Test harness

The script had none, which is why this survived. No JS test convention exists for these scripts, so I followed the precedent of .github/scripts/build-id.sh --self-test: node .github/scripts/check-target-maps.mjs --self-test, stdlib only, no deps, exits before reading any repo file. 12 cases.

The fixture keeps the three properties that made #690 bite: a }; line for a shape scan to latch onto, quoted strings the value regex scrapes, and a brace count returning to zero for a depth-only scan to latch onto. One test asserts those fixture properties directly, so the regression cases cannot silently stop testing anything.

Verified load-bearing by patching the parser back to (a) the old line-shape scan and (b) a depth-only scan: both fail the mandated case and 4 others.

Notes

  • Wired as a step in the existing target-maps job. Job name: release/installer target maps is unchanged, so release.yml's preflight required-check matching is unaffected.
  • A map entry commented out at column 0 would now trip the indentation bound. Documented on the function.
  • The issue text is slightly off on the latch site: the real one is line 103 ( }; closing an arrow function), not the }); at 102.
  • Second commit fixes a message that rendered unbalanced, since atText is a source line carrying its own (. Three self-test regexes pinned the old rendering and the suite caught all three.

…t the parser

extractObjectValues() ended the object literal at the first downstream line
trimming to "};". With TARGET_MAP's own closing brace deleted, that latched
onto the "};" closing an arrow-function const 74 lines later, scraped
'travsr' and 'travsr-vscode-installer' out of the code between as if they
were target triples, and blamed the release matrix for shipping no such
artifact. The guard still failed closed, but it pointed at the wrong file.

Replaced with quote-aware brace-depth counting plus an indentation bound.
Depth counting alone is not enough: the rest of installer.ts re-balances, so
the missing brace is absorbed and the scan latches onto the last "}" in the
file instead (10 phantom triples rather than 7). The bound is what makes the
failure detectable, since a map's entries are always indented under its
declaration. Missing, unopened and malformed closers now each get their own
message naming the object, the file and the line where the scan stopped.

The script had no test harness, which is why this survived: on a clean tree
a broken scan and a correct one print the same thing. Added --self-test,
following .github/scripts/build-id.sh, and wired it into the existing
target-maps CI job (job name unchanged, release.yml matches it by name).
Its fixture is checked to defeat both a shape scan and a depth-only scan, so
the regression cases cannot silently stop testing anything.
`atText` is a raw source line, so it routinely carries its own "(": the very
case this check explains renders as

  still open at line 30 (export function resolveTargetTriple(), which is ...

which is visibly unbalanced. A guard whose whole purpose is a message the
reader can act on should not ship a message that reads as broken. Quoted now.

Three self-test regexes pinned the old rendering. The suite caught all three,
one per run, which is the harness doing its job on its first real edit.
@ritikpal1122
ritikpal1122 requested a review from raj-rkv as a code owner August 23, 2026 09:32
raj-rkv
raj-rkv previously approved these changes Aug 23, 2026

@raj-rkv raj-rkv left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed against a worktree of origin/master, diffed at the merge base ef3eac1. I reproduced #690 on the real installer.ts rather than trusting the before/after in the description, and independently sabotage-checked the new suite.

The bug reproduces exactly as described. Deleting line 29 (the }; closing TARGET_MAP) from the real file and running master's parser:

OK: packages/travsr-vscode/src/installer.ts TARGET_MAP claims 7 target(s)
ERROR: ... TARGET_MAP claims 'travsr', but release.yml build matrix ships no such artifact
ERROR: ... TARGET_MAP claims 'travsr-vscode-installer', but ... ships no such artifact

Seven, with travsr and travsr-vscode-installer scraped out of code below the map, and the blame pointed at release.yml, which was fine. This branch on the same broken file:

ERROR: the closing brace of TARGET_MAP in packages/travsr-vscode/src/installer.ts
(declared on line 25) is missing: the object literal is still open at line 30,
which reads `export function resolveTargetTriple(`, ...

The claim that depth counting alone is worse is true, and the number is right. I removed the indentation bound and the terminator check to get a pure depth scan, against the same broken file:

OK: packages/travsr-vscode/src/installer.ts TARGET_MAP claims 10 target(s)
5 x "no such artifact"

Exactly the 10 the description names, up from 7. With the terminator check left in it instead runs to line 299, the last } in the file, and reports malformed. Either way the bound is what makes the failure locatable, so the reasoning for going past the fix suggested in the issue holds.

The suite is load-bearing. Patching the parser back to each rejected design:

shape scan  -> 5 FAIL: the mandated case, its message assertion, single-line map,
                       non-object declaration, malformed closer
depth only  -> 3 FAIL: the mandated case, its message assertion, non-object declaration

(the description says "the mandated case and 4 others" for both; that holds for the shape scan and is 2 others for depth-only, which does not change the conclusion.)

Also checked, since these are the things that would bite elsewhere:

  • --self-test really does exit before touching any repo file, and it is cwd-independent: it passes run from /tmp against an absolute script path, so the CI step cannot depend on the working directory.
  • The job name: release/installer target maps is byte-identical and still on line 393, so #766's preflight required-check list is unaffected. Worth confirming given the two PRs are open together and both touch ci.yml; they auto-merge with no conflict.
  • The fixture-property test is the right instinct. It means the #690 case cannot quietly stop testing anything if that regex line in installer.ts ever changes shape.

Local: 12/12 self-test cases pass, the real check passes on a clean tree, ci.yml parses, auto-merges onto current master, no em-dashes added. 24 CI checks green.

One small finding in the inline comment. It does not block.

Comment thread .github/scripts/check-target-maps.mjs
The unclosed message appended "which is back at the declaration's own
indentation and so cannot be part of the literal" to every shape, but `where`
has two, and the end of a file has no indentation. A map at the end of a file,
or a truncated one, rendered a reason that cannot describe where it stopped.

Same class as the parenthesis fix in this branch: both are the failure message
this PR exists to make accurate. And the reason it survived is the reason Travsr-com#690
survived at all, one level up: all twelve cases end inside a file, so every one
exercised the atLine branch and none could see the other.

The justification now travels with the shape rather than being appended to it.
A thirteenth case pins the end-of-file wording and asserts the indentation
clause is absent, so the branch the suite could not see is the one it now
watches.

The unopened arm needed no change: "does not open an object literal before the
end of the file" never explained the stopping point.
@ritikpal1122

Copy link
Copy Markdown
Collaborator Author

Fixed in f1219e4. You are right, and the reason it survived is the same shape one level up: all twelve cases end inside a file, so every one exercised the atLine branch and none could see the other. That is #690's own failure mode reappearing in the fix for #690.

The justification now travels with the shape instead of being appended to every one:

const whyStopped = atLine
  ? `${where}, which is back at the declaration's own indentation and so cannot be part of the literal`
  : `${where}, with no closing brace anywhere below the declaration`;

Thirteenth case added, and it asserts both directions: that the end-of-file wording appears, and that back at the declaration's own indentation is absent. Mutation-checked by restoring the unconditional clause, which reproduces your exact output:

FAIL: a map running off the end of the file does not claim an indentation
  message "... still open at the end of the file, which is back at the declaration's
  own indentation and so cannot be part of the literal. ..."
  does not match /still open at the end of the file, with no closing brace anywhere below the declaration/

Agreed the unopened arm needed nothing: "does not open an object literal before the end of the file" never explained the stopping point, so it reads correctly either way.

13/13 self-test cases pass, the real check is still green on a clean tree, and this merges onto current master.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

check-target-maps.mjs: brace-scan latches onto an unrelated later "};" when TARGET_MAP's real closing brace is missing

2 participants