fix(3597): make markdown-fence regex CRLF-tolerant in 3 windows-failing tests
bug-2801, bug-3072, bug-3321 each read .md workflow files from disk and
parse fenced code blocks with /\`\`\`bash\n(...)\`\`\`/ — on a Windows
checkout with autocrlf=true the file content is CRLF, the regex never
matches, code blocks come back empty, and the assertion fails with
"expected bash code blocks in workflow" (and similar).
Fix: accept optional \r before each literal \n in the fence delimiter
and split lines on /\r?\n/. Same pattern bug-2839 had with its local
parseFrontmatter copy (already migrated to the CRLF-aware shared helper
in 1be0e4e2). bug-2995 + tests/security-scan.test.cjs also match this
grep but use \n in literal-string fixtures (not file-content regexes);
not affected.
Validated: holodeck (ubuntu docker) 11224/0 pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -105,7 +105,7 @@ describe('bug-2801: ingest-docs.md workflow calls gsd-tools not gsd-sdk', () =>
|
||||
const content = fs.readFileSync(WORKFLOW_FILE, 'utf-8');
|
||||
// Extract bash fenced code blocks structurally.
|
||||
const bashBlocks = [];
|
||||
const codeBlockRe = /```bash\n([\s\S]*?)```/g;
|
||||
const codeBlockRe = /```bash\r?\n([\s\S]*?)```/g;
|
||||
let m;
|
||||
while ((m = codeBlockRe.exec(content)) !== null) {
|
||||
bashBlocks.push(m[1]);
|
||||
@@ -129,7 +129,7 @@ describe('bug-2801: ingest-docs.md workflow calls gsd-tools not gsd-sdk', () =>
|
||||
test('ingest-docs.md init step uses canonical node-path gsd-tools.cjs invocation', () => {
|
||||
const content = fs.readFileSync(WORKFLOW_FILE, 'utf-8');
|
||||
// Parse fenced bash blocks structurally — do not match raw markdown text.
|
||||
const codeBlockRe = /```bash\n([\s\S]*?)```/g;
|
||||
const codeBlockRe = /```bash\r?\n([\s\S]*?)```/g;
|
||||
const bashLines = [...content.matchAll(codeBlockRe)]
|
||||
.flatMap((m) => m[1].split('\n'))
|
||||
.filter((l) => !/^\s*#/.test(l));
|
||||
|
||||
@@ -13,13 +13,13 @@ function read(rel) {
|
||||
|
||||
function extractFindingsProbesFromBashBlocks(markdown) {
|
||||
const probes = [];
|
||||
const fenceRe = /```bash\n([\s\S]*?)```/g;
|
||||
const fenceRe = /```bash\r?\n([\s\S]*?)```/g;
|
||||
let fenceMatch;
|
||||
|
||||
while ((fenceMatch = fenceRe.exec(markdown)) !== null) {
|
||||
const block = fenceMatch[1];
|
||||
const baseLine = markdown.slice(0, fenceMatch.index).split('\n').length;
|
||||
const lines = block.split('\n');
|
||||
const baseLine = markdown.slice(0, fenceMatch.index).split(/\r?\n/).length;
|
||||
const lines = block.split(/\r?\n/);
|
||||
|
||||
lines.forEach((line, idx) => {
|
||||
if (!line.includes('.claude/skills/')) return;
|
||||
|
||||
@@ -15,7 +15,7 @@ function verifierProbeContract(content) {
|
||||
assert.notEqual(sectionEnd, -1, 'verifier must close Step 7c before Step 8');
|
||||
|
||||
const section = content.slice(sectionStart, sectionEnd);
|
||||
const codeBlocks = [...section.matchAll(/```bash\n([\s\S]*?)\n```/g)].map((match) => match[1]);
|
||||
const codeBlocks = [...section.matchAll(/```bash\r?\n([\s\S]*?)\r?\n```/g)].map((match) => match[1].split(/\r?\n/).join('\n'));
|
||||
const executionSteps = [...section.matchAll(/^\d+\.\s+(.+)$/gm)].map((match) => match[1]);
|
||||
return {
|
||||
title: 'Step 7c: Probe Execution',
|
||||
|
||||
Reference in New Issue
Block a user