* ci: add Windows backend integration test workflow Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * ci: temporarily add push trigger for testing Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * ci: add --no-fail-fast to run all test binaries Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: Windows path handling for backend integration tests - WINDMILL_DIR: use std::env::temp_dir() on Windows instead of /tmp/windmill - HOME_ENV: fall back to USERPROFILE on Windows when HOME is not set - loader.bun.js: normalize paths to forward slashes for consistent comparison with Bun's resolver output on Windows - bun_executor.rs: convert job_dir to forward slashes in JS template strings to avoid backslash escape issues (\t -> tab, etc.) - go_executor.rs: fix windows_gopath() double backslash bug (r"\\" -> "\\") - bash_executor.rs: default to "bash" (in PATH) on Windows instead of /bin/bash Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: improve Windows diagnostics and fix onLoad handler - Include path in create_directory_async/sync panic messages - Add WINDMILL_DIR initialization debug output - Fix loader.bun.js onLoad: use properly escaped regex instead of returning undefined (Bun requires onLoad to return an object) - Add env var debug output to CI workflow Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: sanitize Windows-invalid characters in test worker names and fix cargo path - Replace :: with __ in worker names (colons illegal in Windows dir names) - Fix HOME_DIR to fall back to USERPROFILE on Windows - Add PATH fallback for cargo discovery on Windows - Add debug logging to bun loader for fetch errors Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: handle single colons in worker names, pass MSVC linker env vars, revert bun debug Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: use .exe binary name on Windows and normalize bun import URL paths Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: use absolute path for rust binary, normalize bun resolve paths Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: use .wurl extension instead of .url for bun import resolution on Windows Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: use custom namespace for bun plugin to bypass default file resolution Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: use virtual namespace for bun import resolution to avoid Windows path issues Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: handle Windows 8.3 paths and namespace-prefixed importers in bun loader Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: strip namespace prefix from args.path and handle absolute imports without leading slash in bun loader Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor: simplify bun loader and remove redundant cargo path lookups Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: use platform-specific cargo binary path with .exe on Windows Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor: replace HOME_DIR with HOME_ENV in rust_executor to remove duplication Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * refactor: keep original bun loader on linux, use virtual namespace loader only on windows Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
124 lines
4.4 KiB
JavaScript
124 lines
4.4 KiB
JavaScript
// Windows-specific bun loader that uses a virtual "windmill-url" namespace instead
|
|
// of writing .url files to disk. This avoids Windows path issues (backslashes in
|
|
// resolve(), 8.3 short filenames, drive letter prefixes). The virtual namespace
|
|
// approach is likely better on all fronts but we keep the original .url-file loader
|
|
// on Linux to avoid breaking back-compat.
|
|
const p = {
|
|
name: "windmill-relative-resolver",
|
|
async setup(build) {
|
|
const { readFileSync } = await import("fs");
|
|
const { resolve } = await import("node:path");
|
|
|
|
const base_internal_url = "BASE_INTERNAL_URL".replace(
|
|
"localhost",
|
|
"127.0.0.1"
|
|
);
|
|
|
|
const w_id = "W_ID";
|
|
const current_path = "CURRENT_PATH";
|
|
const token = "TOKEN";
|
|
|
|
const cdir = resolve("./");
|
|
const cdirNoPrivate = cdir.replace(/^\/private/, ""); // for macos
|
|
// Normalize path to forward slashes to match Bun's resolver output on Windows
|
|
const cdirFwd = cdir.replace(/\\/g, "/");
|
|
const cdirPosix = cdirFwd.replace(/^[a-zA-Z]:/, "");
|
|
const filterResolve = new RegExp(
|
|
`^(?!\\.\/main\\.ts)(?!${cdirFwd}\/main\\.ts)(?!${cdirPosix}\/main\\.ts)(?!(?:/private)?${cdirNoPrivate}\/wrapper\\.mjs).*\\.ts$`
|
|
);
|
|
|
|
let cdirNodeModules = `${cdirFwd}/node_modules/`;
|
|
|
|
const filterLoad = new RegExp(`^${cdir}\/main\\.ts$`);
|
|
const transpiler = new Bun.Transpiler({
|
|
loader: "ts",
|
|
});
|
|
|
|
function replaceRelativeImports(code) {
|
|
const imports = transpiler.scanImports(code);
|
|
for (const imp of imports) {
|
|
if (imp.kind == "import-statement") {
|
|
if (
|
|
(imp.path.startsWith(".") ||
|
|
imp.path.startsWith("/u/") ||
|
|
imp.path.startsWith("/f/")) &&
|
|
!imp.path.endsWith(".ts")
|
|
) {
|
|
code = code.replaceAll(imp.path, imp.path + ".ts");
|
|
}
|
|
}
|
|
}
|
|
return {
|
|
contents: code,
|
|
};
|
|
}
|
|
|
|
function normalizePath(rawPath) {
|
|
return rawPath.split("/").reduce((acc, seg) => {
|
|
if (seg === "..") acc.pop();
|
|
else if (seg !== "." && seg !== "") acc.push(seg);
|
|
return acc;
|
|
}, []).join("/");
|
|
}
|
|
|
|
// Resolve a windmill script import path relative to an importer path.
|
|
// Bun on Windows may prefix args with "windmill-url:" or strip leading "/".
|
|
function resolveWindmillImport(importerPath, importPath) {
|
|
const path = importPath.replace(/^windmill-url:/, "").replace(/^\//, "");
|
|
const isAbsolute = path.startsWith("f/") || path.startsWith("u/");
|
|
const endExt = path.endsWith(".ts") ? "" : ".ts";
|
|
const rawScriptPath = isAbsolute
|
|
? `${path}${endExt}`
|
|
: `${importerPath}/../${path}${endExt}`;
|
|
return { path: normalizePath(rawScriptPath), namespace: "windmill-url" };
|
|
}
|
|
|
|
build.onLoad({ filter: filterLoad }, async (args) => {
|
|
const code = readFileSync(args.path, "utf8");
|
|
return replaceRelativeImports(code);
|
|
});
|
|
|
|
// Load windmill scripts by fetching from the API
|
|
build.onLoad({ filter: /.*/, namespace: "windmill-url" }, async (args) => {
|
|
const path = args.path.replace(/^windmill-url:/, "");
|
|
const url = `${base_internal_url}/api/w/${w_id}/scripts/RAW_GET_ENDPOINT/p/${path}`;
|
|
const req = await fetch(url, {
|
|
method: "GET",
|
|
headers: {
|
|
Authorization: "Bearer " + token,
|
|
},
|
|
});
|
|
if (!req.ok) {
|
|
throw new Error(
|
|
`Failed to find relative import at ${url} (status ${req.status})`
|
|
);
|
|
}
|
|
const contents = await req.text();
|
|
return {
|
|
contents: replaceRelativeImports(contents).contents,
|
|
loader: "tsx",
|
|
};
|
|
});
|
|
|
|
// Resolve windmill script imports from the file namespace (e.g. from main.ts)
|
|
build.onResolve({ filter: filterResolve }, (args) => {
|
|
const importerFwd = args.importer?.replace(/\\/g, "/") ?? "";
|
|
if (importerFwd.startsWith(cdirNodeModules)) {
|
|
return undefined;
|
|
}
|
|
const isMainTs =
|
|
args.importer == "./main.ts" || importerFwd.endsWith("/main.ts");
|
|
const file_path = isMainTs
|
|
? current_path
|
|
: importerFwd.replace(cdirFwd + "/", "");
|
|
return resolveWindmillImport(file_path, args.path);
|
|
});
|
|
|
|
// Resolve nested imports from within windmill-url modules
|
|
build.onResolve({ filter: /\.ts$/, namespace: "windmill-url" }, (args) => {
|
|
const importer = args.importer.replace(/^windmill-url:/, "");
|
|
return resolveWindmillImport(importer, args.path);
|
|
});
|
|
},
|
|
};
|