Compare commits

...
Author SHA1 Message Date
Tabish Bidiwale 31b267ae2d fix(artifact-graph): normalize paths for cross-platform glob compatibility
Add FileSystemUtils.toPosixPath() utility and consolidate scattered
path normalization patterns. This fixes Windows test failures where
fast-glob couldn't match paths containing backslashes.

Root cause: path.join() uses backslashes on Windows, but fast-glob
requires forward slashes for glob patterns on all platforms.

Changes:
- Add toPosixPath() to FileSystemUtils for cross-platform path handling
- Update artifact-graph/state.ts to normalize glob patterns
- Consolidate path normalization in update.ts, validator.ts, and
  json-converter.ts to use the new utility
2025-12-25 22:26:35 +11:00
5 changed files with 19 additions and 6 deletions
+4 -1
View File
@@ -3,6 +3,7 @@ import * as path from 'node:path';
import fg from 'fast-glob';
import type { CompletedSet } from './types.js';
import type { ArtifactGraph } from './graph.js';
import { FileSystemUtils } from '../../utils/file-system.js';
/**
* Detects which artifacts are completed by checking file existence in the change directory.
@@ -54,8 +55,10 @@ function isGlobPattern(pattern: string): boolean {
/**
* Checks if a glob pattern has any matches.
* Normalizes Windows backslashes to forward slashes for cross-platform glob compatibility.
*/
function hasGlobMatches(pattern: string): boolean {
const matches = fg.sync(pattern, { onlyFiles: true });
const normalizedPattern = FileSystemUtils.toPosixPath(pattern);
const matches = fg.sync(normalizedPattern, { onlyFiles: true });
return matches.length > 0;
}
+2 -1
View File
@@ -3,6 +3,7 @@ import path from 'path';
import { MarkdownParser } from '../parsers/markdown-parser.js';
import { ChangeParser } from '../parsers/change-parser.js';
import { Spec, Change } from '../schemas/index.js';
import { FileSystemUtils } from '../../utils/file-system.js';
export class JsonConverter {
convertSpecToJson(filePath: string): string {
@@ -43,7 +44,7 @@ export class JsonConverter {
}
private extractNameFromPath(filePath: string): string {
const normalizedPath = filePath.replaceAll('\\', '/');
const normalizedPath = FileSystemUtils.toPosixPath(filePath);
const parts = normalizedPath.split('/');
for (let i = parts.length - 1; i >= 0; i--) {
+1 -1
View File
@@ -107,7 +107,7 @@ export class UpdateCommand {
if (updatedSlashFiles.length > 0) {
// Normalize to forward slashes for cross-platform log consistency
const normalized = updatedSlashFiles.map((p) => p.replace(/\\/g, '/'));
const normalized = updatedSlashFiles.map((p) => FileSystemUtils.toPosixPath(p));
summaryParts.push(`Updated slash commands: ${normalized.join(', ')}`);
}
+4 -3
View File
@@ -5,12 +5,13 @@ import { SpecSchema, ChangeSchema, Spec, Change } from '../schemas/index.js';
import { MarkdownParser } from '../parsers/markdown-parser.js';
import { ChangeParser } from '../parsers/change-parser.js';
import { ValidationReport, ValidationIssue, ValidationLevel } from './types.js';
import {
import {
MIN_PURPOSE_LENGTH,
MAX_REQUIREMENT_TEXT_LENGTH,
VALIDATION_MESSAGES
VALIDATION_MESSAGES
} from './constants.js';
import { parseDeltaSpec, normalizeRequirementName } from '../parsers/requirement-blocks.js';
import { FileSystemUtils } from '../../utils/file-system.js';
export class Validator {
private strictMode: boolean;
@@ -359,7 +360,7 @@ export class Validator {
}
private extractNameFromPath(filePath: string): string {
const normalizedPath = filePath.replaceAll('\\', '/');
const normalizedPath = FileSystemUtils.toPosixPath(filePath);
const parts = normalizedPath.split('/');
// Look for the directory name after 'specs' or 'changes'
+8
View File
@@ -42,6 +42,14 @@ function findMarkerIndex(
}
export class FileSystemUtils {
/**
* Converts a path to use forward slashes (POSIX style).
* Essential for cross-platform compatibility with glob libraries like fast-glob.
*/
static toPosixPath(p: string): string {
return p.replace(/\\/g, '/');
}
private static isWindowsBasePath(basePath: string): boolean {
return /^[A-Za-z]:[\\/]/.test(basePath) || basePath.startsWith('\\');
}