Skip to content

Commit 290465b

Browse files
authored
Merge pull request #77 from Lellansin/fix/windows-prettier-crlf
fix: resolve Windows CI failures (CRLF, MCP spawn, cross-platform test runner)
2 parents 64bf782 + d5da01e commit 290465b

11 files changed

Lines changed: 303 additions & 142 deletions

.gitattributes

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Normalize line endings to LF across all platforms
2+
* text=auto eol=lf
3+
4+
# Binary files should not be normalized
5+
*.png binary
6+
*.jpg binary
7+
*.gif binary
8+
*.ico binary
9+
*.woff binary
10+
*.woff2 binary
11+
*.eot binary
12+
*.ttf binary

.github/workflows/ci.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ jobs:
1818
- windows-latest
1919
- macos-latest
2020
node-version:
21+
- "20"
2122
- "22"
2223
- "24"
2324

package-lock.json

Lines changed: 95 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@
3232
"format:check": "prettier --check 'src/**/*.{ts,tsx}'",
3333
"check": "npm run typecheck && npm run lint && npm run format:check",
3434
"build": "npm run check && npm run bundle && node -e \"require('fs').chmodSync('dist/cli.js', 0o755)\"",
35-
"test": "tsx --test src/tests/*.test.ts",
35+
"test": "node src/tests/run-tests.mjs",
3636
"test:single": "tsx --test",
3737
"prepack": "npm run build",
3838
"prepare": "husky"
@@ -58,6 +58,7 @@
5858
"eslint": "^9.39.4",
5959
"eslint-config-prettier": "^10.1.8",
6060
"eslint-plugin-react-hooks": "^7.1.1",
61+
"glob": "^13.0.6",
6162
"husky": "^9.1.7",
6263
"lint-staged": "^17.0.4",
6364
"prettier": "^3.8.3",

src/mcp/mcp-client.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -128,10 +128,10 @@ export class McpClient {
128128
const isWindows = os.platform() === "win32";
129129

130130
if (isWindows) {
131-
// On Windows, .cmd files require shell: true to be spawned.
132-
// Build a single command string so cmd.exe handles quoting correctly.
133-
const cmd = [this.command + ".cmd", ...args].join(" ");
134-
this.process = spawn(cmd, [], {
131+
// On Windows, shell: true lets cmd.exe resolve the command via
132+
// PATHEXT (npx → npx.cmd, etc.) without blindly appending .cmd,
133+
// which would break absolute paths like process.execPath.
134+
this.process = spawn(this.command, args, {
135135
stdio: ["pipe", "pipe", "pipe"],
136136
env: childEnv,
137137
shell: true,

src/tests/clipboard.test.ts

Lines changed: 38 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -36,40 +36,44 @@ test("readClipboardImage returns null when no clipboard helpers are installed",
3636
assert.equal(result, null);
3737
});
3838

39-
test("readClipboardImage uses osascript fallback on macOS when pngpaste is missing", async () => {
40-
const binDir = fs.mkdtempSync(path.join(os.tmpdir(), "deepcode-clipboard-test-bin-"));
41-
try {
42-
fs.writeFileSync(path.join(binDir, "pngpaste"), "#!/bin/sh\nexit 1\n", { mode: 0o755 });
43-
fs.writeFileSync(
44-
path.join(binDir, "osascript"),
45-
[
46-
"#!/bin/sh",
47-
'for arg in "$@"; do',
48-
' case "$arg" in',
49-
" *'open for access POSIX file " + '"' + "'*)",
50-
' path_part=${arg#*POSIX file \\"}',
51-
' out_path=${path_part%%\\"*}',
52-
' printf fakepng > "$out_path"',
53-
" exit 0",
54-
" ;;",
55-
" esac",
56-
"done",
57-
"exit 1",
58-
"",
59-
].join("\n"),
60-
{ mode: 0o755 }
61-
);
39+
test(
40+
"readClipboardImage uses osascript fallback on macOS when pngpaste is missing",
41+
{ skip: process.platform === "win32" },
42+
async () => {
43+
const binDir = fs.mkdtempSync(path.join(os.tmpdir(), "deepcode-clipboard-test-bin-"));
44+
try {
45+
fs.writeFileSync(path.join(binDir, "pngpaste"), "#!/bin/sh\nexit 1\n", { mode: 0o755 });
46+
fs.writeFileSync(
47+
path.join(binDir, "osascript"),
48+
[
49+
"#!/bin/sh",
50+
'for arg in "$@"; do',
51+
' case "$arg" in',
52+
" *'open for access POSIX file " + '"' + "'*)",
53+
' path_part=${arg#*POSIX file \\"}',
54+
' out_path=${path_part%%\\"*}',
55+
' printf fakepng > "$out_path"',
56+
" exit 0",
57+
" ;;",
58+
" esac",
59+
"done",
60+
"exit 1",
61+
"",
62+
].join("\n"),
63+
{ mode: 0o755 }
64+
);
6265

63-
const moduleUrl = new URL(`../ui/clipboard.ts?t=${Date.now()}`, import.meta.url).href;
64-
const { readClipboardImage } = (await import(moduleUrl)) as ClipboardModule;
66+
const moduleUrl = new URL(`../ui/clipboard.ts?t=${Date.now()}`, import.meta.url).href;
67+
const { readClipboardImage } = (await import(moduleUrl)) as ClipboardModule;
6568

66-
process.env.PATH = binDir;
67-
const result = withPlatform("darwin", () => readClipboardImage());
68-
assert.equal(result?.mimeType, "image/png");
69-
assert.equal(result?.dataUrl, `data:image/png;base64,${Buffer.from("fakepng").toString("base64")}`);
70-
} finally {
71-
process.env.PATH = ORIGINAL_PATH;
72-
Object.defineProperty(process, "platform", { value: ORIGINAL_PLATFORM });
73-
fs.rmSync(binDir, { recursive: true, force: true });
69+
process.env.PATH = binDir;
70+
const result = withPlatform("darwin", () => readClipboardImage());
71+
assert.equal(result?.mimeType, "image/png");
72+
assert.equal(result?.dataUrl, `data:image/png;base64,${Buffer.from("fakepng").toString("base64")}`);
73+
} finally {
74+
process.env.PATH = ORIGINAL_PATH;
75+
Object.defineProperty(process, "platform", { value: ORIGINAL_PLATFORM });
76+
fs.rmSync(binDir, { recursive: true, force: true });
77+
}
7478
}
75-
});
79+
);

src/tests/run-tests.mjs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Cross-platform test runner: finds all *.test.ts files and runs them via tsx.
2+
// Uses the glob package for reliable cross-platform pattern expansion (Node 20+).
3+
/* eslint-disable */
4+
5+
import { globSync } from "glob";
6+
import { spawnSync } from "child_process";
7+
8+
const cwd = new URL("../..", import.meta.url);
9+
const testFiles = globSync("src/tests/*.test.ts", { cwd });
10+
11+
const result = spawnSync(process.execPath, ["--import", "tsx", "--test", ...testFiles], { stdio: "inherit", cwd });
12+
13+
process.exit(result.status ?? 1);

0 commit comments

Comments
 (0)