mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-08-25 00:01:55 +00:00
fix: improve windows compatibility
* 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>
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
name: Backend integration tests (Windows)
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- "ci-windows-tests"
|
||||
|
||||
env:
|
||||
CARGO_INCREMENTAL: 0
|
||||
SQLX_OFFLINE: true
|
||||
DISABLE_EMBEDDING: true
|
||||
|
||||
jobs:
|
||||
cargo_test_windows:
|
||||
runs-on: blacksmith-16vcpu-windows-2025
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Read EE repo commit hash
|
||||
shell: pwsh
|
||||
run: |
|
||||
$ee_repo_ref = Get-Content .\backend\ee-repo-ref.txt
|
||||
echo "ee_repo_ref=$ee_repo_ref" | Out-File -FilePath $env:GITHUB_ENV -Append
|
||||
|
||||
- name: Checkout windmill-ee-private repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: windmill-labs/windmill-ee-private
|
||||
path: ./windmill-ee-private
|
||||
ref: ${{ env.ee_repo_ref }}
|
||||
token: ${{ secrets.WINDMILL_EE_PRIVATE_ACCESS }}
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Substitute EE code
|
||||
shell: bash
|
||||
run: |
|
||||
./backend/substitute_ee_code.sh --copy --dir ./windmill-ee-private
|
||||
|
||||
- name: Setup PostgreSQL
|
||||
uses: ikalnytskyi/action-setup-postgres@v6
|
||||
with:
|
||||
username: postgres
|
||||
password: changeme
|
||||
database: windmill
|
||||
port: 5432
|
||||
|
||||
- uses: actions-rust-lang/setup-rust-toolchain@v1
|
||||
with:
|
||||
cache-workspaces: backend
|
||||
toolchain: 1.93.0
|
||||
|
||||
- uses: actions/setup-dotnet@v4
|
||||
with:
|
||||
dotnet-version: "9.0.x"
|
||||
|
||||
- uses: denoland/setup-deno@v2
|
||||
with:
|
||||
deno-version: v2.x
|
||||
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.21.5
|
||||
|
||||
- uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: 1.3.10
|
||||
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: "20"
|
||||
|
||||
- uses: astral-sh/setup-uv@v6.2.1
|
||||
with:
|
||||
version: "0.9.24"
|
||||
|
||||
- uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: "8.3"
|
||||
tools: composer
|
||||
|
||||
- name: Install windmill CLI
|
||||
shell: bash
|
||||
run: |
|
||||
cd cli
|
||||
bash gen_wm_client.sh
|
||||
bun install
|
||||
mkdir -p "$HOME/.local/bin"
|
||||
printf '#!/bin/sh\nexec bun run "%s/cli/src/main.ts" "$@"\n' "$GITHUB_WORKSPACE" > "$HOME/.local/bin/wmill"
|
||||
chmod +x "$HOME/.local/bin/wmill"
|
||||
echo "$HOME/.local/bin" >> $GITHUB_PATH
|
||||
|
||||
- name: Install OpenSSL via vcpkg
|
||||
run: |
|
||||
vcpkg.exe install openssl-windows:x64-windows
|
||||
vcpkg.exe install openssl:x64-windows-static
|
||||
vcpkg.exe integrate install
|
||||
|
||||
- name: Get runtime paths
|
||||
id: runtime-paths
|
||||
shell: pwsh
|
||||
run: |
|
||||
echo "DENO_PATH=$($(Get-Command deno).Source)" >> $env:GITHUB_OUTPUT
|
||||
echo "BUN_PATH=$($(Get-Command bun).Source)" >> $env:GITHUB_OUTPUT
|
||||
echo "NODE_BIN_PATH=$($(Get-Command node).Source)" >> $env:GITHUB_OUTPUT
|
||||
echo "GO_PATH=$($(Get-Command go).Source)" >> $env:GITHUB_OUTPUT
|
||||
echo "UV_PATH=$($(Get-Command uv).Source)" >> $env:GITHUB_OUTPUT
|
||||
echo "PHP_PATH=$($(Get-Command php).Source)" >> $env:GITHUB_OUTPUT
|
||||
echo "COMPOSER_PATH=$($(Get-Command composer).Source)" >> $env:GITHUB_OUTPUT
|
||||
echo "POWERSHELL_PATH=$($(Get-Command pwsh).Source)" >> $env:GITHUB_OUTPUT
|
||||
echo "DOTNET_PATH=$($(Get-Command dotnet).Source)" >> $env:GITHUB_OUTPUT
|
||||
|
||||
- name: Build DuckDB FFI module
|
||||
working-directory: backend/windmill-duckdb-ffi-internal
|
||||
timeout-minutes: 30
|
||||
run: |
|
||||
cargo build --release -p windmill_duckdb_ffi_internal
|
||||
New-Item -ItemType Directory -Path ..\target\debug -Force
|
||||
Copy-Item target\release\windmill_duckdb_ffi_internal.dll ..\target\debug\
|
||||
|
||||
- name: Print runtime versions and env
|
||||
shell: pwsh
|
||||
run: |
|
||||
deno --version
|
||||
bun -v
|
||||
node --version
|
||||
go version
|
||||
python3 --version
|
||||
php --version
|
||||
pwsh --version
|
||||
dotnet --version
|
||||
echo "TEMP=$env:TEMP"
|
||||
echo "TMP=$env:TMP"
|
||||
echo "USERPROFILE=$env:USERPROFILE"
|
||||
echo "HOME=$env:HOME"
|
||||
|
||||
- name: cargo test
|
||||
working-directory: backend
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
DATABASE_URL: postgres://postgres:changeme@localhost:5432/windmill
|
||||
RUST_LOG: "off"
|
||||
RUST_LOG_STYLE: never
|
||||
CARGO_NET_GIT_FETCH_WITH_CLI: true
|
||||
CARGO_BUILD_JOBS: 12
|
||||
VCPKGRS_DYNAMIC: 1
|
||||
OPENSSL_DIR: ${{ env.VCPKG_INSTALLATION_ROOT }}\installed\x64-windows-static
|
||||
DENO_PATH: ${{ steps.runtime-paths.outputs.DENO_PATH }}
|
||||
BUN_PATH: ${{ steps.runtime-paths.outputs.BUN_PATH }}
|
||||
NODE_BIN_PATH: ${{ steps.runtime-paths.outputs.NODE_BIN_PATH }}
|
||||
GO_PATH: ${{ steps.runtime-paths.outputs.GO_PATH }}
|
||||
UV_PATH: ${{ steps.runtime-paths.outputs.UV_PATH }}
|
||||
PHP_PATH: ${{ steps.runtime-paths.outputs.PHP_PATH }}
|
||||
COMPOSER_PATH: ${{ steps.runtime-paths.outputs.COMPOSER_PATH }}
|
||||
POWERSHELL_PATH: ${{ steps.runtime-paths.outputs.POWERSHELL_PATH }}
|
||||
DOTNET_PATH: ${{ steps.runtime-paths.outputs.DOTNET_PATH }}
|
||||
WMDEBUG_FORCE_V0_WORKSPACE_DEPENDENCIES: 1
|
||||
WMDEBUG_FORCE_RUNNABLE_SETTINGS_V0: 1
|
||||
WMDEBUG_FORCE_NO_LEGACY_DEBOUNCING_COMPAT: 1
|
||||
run: >
|
||||
cargo test
|
||||
--no-fail-fast
|
||||
--features enterprise,deno_core,duckdb,license,python,rust,scoped_cache,parquet,private,csharp,php,quickjs,mcp,run_inline
|
||||
--all
|
||||
-- --nocapture --test-threads=10
|
||||
@@ -314,14 +314,14 @@ pub async fn create_directory_async(directory_path: &str) {
|
||||
.recursive(true)
|
||||
.create(directory_path)
|
||||
.await
|
||||
.expect("could not create dir");
|
||||
.unwrap_or_else(|e| panic!("could not create dir '{}': {}", directory_path, e));
|
||||
}
|
||||
|
||||
pub fn create_directory_sync(directory_path: &str) {
|
||||
SyncDirBuilder::new()
|
||||
.recursive(true)
|
||||
.create(directory_path)
|
||||
.expect("could not create dir");
|
||||
.unwrap_or_else(|e| panic!("could not create dir '{}': {}", directory_path, e));
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
|
||||
@@ -494,7 +494,17 @@ pub async fn store_pull_query(wc: &WorkerConfig) {
|
||||
lazy_static::lazy_static! {
|
||||
pub static ref WINDMILL_DIR: String = {
|
||||
let dir = std::env::var("WINDMILL_DIR")
|
||||
.unwrap_or_else(|_| "/tmp/windmill".to_string());
|
||||
.unwrap_or_else(|_| {
|
||||
#[cfg(not(windows))]
|
||||
{ "/tmp/windmill".to_string() }
|
||||
#[cfg(windows)]
|
||||
{
|
||||
let temp = std::env::temp_dir();
|
||||
let temp_str = temp.to_string_lossy();
|
||||
let normalized = temp_str.trim_end_matches(&['/', '\\'][..]).replace('\\', "/");
|
||||
format!("{}/windmill", normalized)
|
||||
}
|
||||
});
|
||||
if dir.is_empty() {
|
||||
panic!("WINDMILL_DIR must not be empty");
|
||||
}
|
||||
|
||||
@@ -66,6 +66,8 @@ fn next_worker_name() -> String {
|
||||
.unwrap_or(s)
|
||||
})
|
||||
.unwrap_or("no thread name");
|
||||
// Replace colons because they are illegal in Windows directory names
|
||||
let thread_name = thread_name.replace(':', "_");
|
||||
format!("{id}/worker-{thread_name}")
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
// 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);
|
||||
});
|
||||
},
|
||||
};
|
||||
@@ -26,7 +26,12 @@ use windmill_queue::{
|
||||
};
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
pub static ref BIN_BASH: String = std::env::var("BASH_PATH").unwrap_or_else(|_| "/bin/bash".to_string());
|
||||
pub static ref BIN_BASH: String = std::env::var("BASH_PATH").unwrap_or_else(|_| {
|
||||
#[cfg(not(windows))]
|
||||
{ "/bin/bash".to_string() }
|
||||
#[cfg(windows)]
|
||||
{ "bash".to_string() }
|
||||
});
|
||||
}
|
||||
const NSJAIL_CONFIG_RUN_BASH_CONTENT: &str = include_str!("../nsjail/run.bash.config.proto");
|
||||
|
||||
|
||||
@@ -53,7 +53,14 @@ use windmill_object_store::attempt_fetch_bytes;
|
||||
|
||||
use windmill_parser::Typ;
|
||||
|
||||
// The Windows loader uses a virtual "windmill-url" namespace instead of writing .url
|
||||
// files to disk, which avoids Windows path issues. 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.
|
||||
#[cfg(not(windows))]
|
||||
pub const RELATIVE_BUN_LOADER: &str = include_str!("../loader.bun.js");
|
||||
#[cfg(windows)]
|
||||
pub const RELATIVE_BUN_LOADER: &str = include_str!("../loader.bun.windows.js");
|
||||
|
||||
pub const RELATIVE_BUN_BUILDER: &str = include_str!("../loader_builder.bun.js");
|
||||
|
||||
@@ -527,6 +534,8 @@ pub async fn build_loader(
|
||||
current_path: &str,
|
||||
mode: LoaderMode,
|
||||
) -> Result<()> {
|
||||
// Use forward slashes in JS strings to avoid backslash escape issues on Windows
|
||||
let job_dir_js = job_dir.replace('\\', "/");
|
||||
let loader = RELATIVE_BUN_LOADER
|
||||
.replace("W_ID", w_id)
|
||||
.replace("BASE_INTERNAL_URL", base_internal_url)
|
||||
@@ -549,13 +558,13 @@ import {{ readdir }} from "node:fs/promises";
|
||||
|
||||
let fileNames = []
|
||||
try {{
|
||||
fileNames = await readdir("{job_dir}/node_modules")
|
||||
fileNames = await readdir("{job_dir_js}/node_modules")
|
||||
}} catch (e) {{
|
||||
}}
|
||||
|
||||
try {{
|
||||
await Bun.build({{
|
||||
entrypoints: ["{job_dir}/wrapper.mjs"],
|
||||
entrypoints: ["{job_dir_js}/wrapper.mjs"],
|
||||
outdir: "./",
|
||||
target: "node",
|
||||
plugins: [p],
|
||||
@@ -597,7 +606,7 @@ plugin(p)
|
||||
|
||||
try {{
|
||||
await Bun.build({{
|
||||
entrypoints: ["{job_dir}/main.ts"],
|
||||
entrypoints: ["{job_dir_js}/main.ts"],
|
||||
outdir: "./",
|
||||
target: "{}",
|
||||
plugins: [p],
|
||||
|
||||
@@ -53,8 +53,7 @@ fn get_windows_program_files() -> String {
|
||||
|
||||
#[cfg(windows)]
|
||||
fn windows_gopath() -> String {
|
||||
let tmp_dir = get_windows_tmp_dir();
|
||||
GO_CACHE_DIR.replace("/tmp", &tmp_dir).replace("/", r"\\")
|
||||
GO_CACHE_DIR.replace('/', "\\")
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
|
||||
@@ -41,20 +41,30 @@ const NSJAIL_CONFIG_RUN_RUST_CONTENT: &str = include_str!("../nsjail/run.rust.co
|
||||
const NSJAIL_CONFIG_COMPILE_RUST_CONTENT: &str =
|
||||
include_str!("../nsjail/download.rust.config.proto");
|
||||
|
||||
#[cfg(windows)]
|
||||
const RUST_BIN_NAME: &str = "main.exe";
|
||||
#[cfg(not(windows))]
|
||||
const RUST_BIN_NAME: &str = "main";
|
||||
|
||||
fn find_cargo_path() -> String {
|
||||
if let Ok(p) = std::env::var("CARGO_PATH") {
|
||||
return p;
|
||||
}
|
||||
let from_home = format!("{}/bin/cargo", CARGO_HOME.as_str());
|
||||
if std::path::Path::new(&from_home).exists() {
|
||||
return from_home;
|
||||
}
|
||||
for p in ["/usr/local/cargo/bin/cargo", "/usr/bin/cargo"] {
|
||||
let candidates = if cfg!(windows) {
|
||||
vec![format!("{}\\bin\\cargo.exe", CARGO_HOME.as_str())]
|
||||
} else {
|
||||
vec![
|
||||
format!("{}/bin/cargo", CARGO_HOME.as_str()),
|
||||
"/usr/local/cargo/bin/cargo".to_string(),
|
||||
"/usr/bin/cargo".to_string(),
|
||||
]
|
||||
};
|
||||
for p in &candidates {
|
||||
if std::path::Path::new(p).exists() {
|
||||
return p.to_string();
|
||||
return p.clone();
|
||||
}
|
||||
}
|
||||
from_home
|
||||
candidates.into_iter().next().unwrap()
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
@@ -71,7 +81,6 @@ fn find_preinstalled_dir(env_var: &str, candidates: &[&str]) -> String {
|
||||
}
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref HOME_DIR: String = std::env::var("HOME").expect("Could not find the HOME environment variable");
|
||||
static ref CARGO_HOME: String = std::env::var("CARGO_HOME").unwrap_or_else(|_| { CARGO_HOME_DEFAULT.clone() });
|
||||
static ref RUSTUP_HOME: String = std::env::var("RUSTUP_HOME").unwrap_or_else(|_| { RUSTUP_HOME_DEFAULT.clone() });
|
||||
static ref CARGO_PATH: String = find_cargo_path();
|
||||
@@ -81,14 +90,14 @@ lazy_static::lazy_static! {
|
||||
|
||||
#[cfg(windows)]
|
||||
lazy_static::lazy_static! {
|
||||
static ref CARGO_HOME_DEFAULT: String = format!("{}\\.cargo", *HOME_DIR);
|
||||
static ref RUSTUP_HOME_DEFAULT: String = format!("{}\\.rustup", *HOME_DIR);
|
||||
static ref CARGO_HOME_DEFAULT: String = format!("{}\\.cargo", HOME_ENV.as_str());
|
||||
static ref RUSTUP_HOME_DEFAULT: String = format!("{}\\.rustup", HOME_ENV.as_str());
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
lazy_static::lazy_static! {
|
||||
static ref CARGO_HOME_DEFAULT: String = format!("{}/.cargo", *HOME_DIR);
|
||||
static ref RUSTUP_HOME_DEFAULT: String = format!("{}/.rustup", *HOME_DIR);
|
||||
static ref CARGO_HOME_DEFAULT: String = format!("{}/.cargo", HOME_ENV.as_str());
|
||||
static ref RUSTUP_HOME_DEFAULT: String = format!("{}/.rustup", HOME_ENV.as_str());
|
||||
}
|
||||
|
||||
const RUST_OBJECT_STORE_PREFIX: &str = "rustbin/";
|
||||
@@ -97,11 +106,11 @@ const RUST_OBJECT_STORE_PREFIX: &str = "rustbin/";
|
||||
lazy_static::lazy_static! {
|
||||
static ref PREINSTALLED_CARGO: String = find_preinstalled_dir(
|
||||
"CARGO_PREINSTALL_DIR",
|
||||
&["/usr/local/cargo", &format!("{}/.cargo", *HOME_DIR)],
|
||||
&["/usr/local/cargo", &format!("{}/.cargo", HOME_ENV.as_str())],
|
||||
);
|
||||
static ref PREINSTALLED_RUSTUP: String = find_preinstalled_dir(
|
||||
"RUSTUP_PREINSTALL_DIR",
|
||||
&["/usr/local/rustup", &format!("{}/.rustup", *HOME_DIR)],
|
||||
&["/usr/local/rustup", &format!("{}/.rustup", HOME_ENV.as_str())],
|
||||
);
|
||||
}
|
||||
|
||||
@@ -521,6 +530,13 @@ pub async fn build_rust_crate(
|
||||
std::env::var("TMP").unwrap_or_else(|_| "C:\\tmp".to_string()),
|
||||
);
|
||||
build_rust_cmd.env("USERPROFILE", crate::USERPROFILE_ENV.as_str());
|
||||
// MSVC linker needs LIB and INCLUDE to find kernel32.lib etc.
|
||||
if let Ok(lib) = std::env::var("LIB") {
|
||||
build_rust_cmd.env("LIB", lib);
|
||||
}
|
||||
if let Ok(include) = std::env::var("INCLUDE") {
|
||||
build_rust_cmd.env("INCLUDE", include);
|
||||
}
|
||||
}
|
||||
start_child_process(build_rust_cmd, CARGO_PATH.as_str(), false).await?
|
||||
};
|
||||
@@ -545,30 +561,29 @@ pub async fn build_rust_crate(
|
||||
|
||||
tokio::fs::copy(
|
||||
&format!(
|
||||
"{build_dir}/target/{}/main",
|
||||
"{build_dir}/target/{}/{RUST_BIN_NAME}",
|
||||
if is_preview { "debug" } else { "release" },
|
||||
),
|
||||
format! {"{job_dir}/main"},
|
||||
format!("{job_dir}/{RUST_BIN_NAME}"),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
Error::ExecutionErr(format!(
|
||||
"could not copy built binary from [...]/target/.../main to {job_dir}/main: {e:?}"
|
||||
"could not copy built binary from [...]/target/.../{RUST_BIN_NAME} to {job_dir}/{RUST_BIN_NAME}: {e:?}"
|
||||
))
|
||||
})?;
|
||||
|
||||
match save_cache(
|
||||
&bin_path,
|
||||
&format!("{RUST_OBJECT_STORE_PREFIX}{hash}"),
|
||||
&format!("{job_dir}/main"),
|
||||
&format!("{job_dir}/{RUST_BIN_NAME}"),
|
||||
false,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Err(e) => {
|
||||
let em = format!(
|
||||
"could not save {bin_path} to {} to rust cache: {e:?}",
|
||||
format!("{job_dir}/main"),
|
||||
"could not save {bin_path} to {job_dir}/{RUST_BIN_NAME} to rust cache: {e:?}",
|
||||
);
|
||||
tracing::error!(em);
|
||||
Ok(em)
|
||||
@@ -618,16 +633,16 @@ pub async fn handle_rust_job(
|
||||
let (cache, cache_logs) = crate::global_cache::load_cache(&bin_path, &remote_path, false).await;
|
||||
|
||||
let cache_logs = if cache {
|
||||
let target = format!("{job_dir}/main");
|
||||
let target = format!("{job_dir}/{RUST_BIN_NAME}");
|
||||
|
||||
#[cfg(unix)]
|
||||
let symlink = std::os::unix::fs::symlink(&bin_path, &target);
|
||||
#[cfg(windows)]
|
||||
let symlink = std::os::windows::fs::symlink_dir(&bin_path, &target);
|
||||
let symlink = std::os::windows::fs::symlink_file(&bin_path, &target);
|
||||
|
||||
symlink.map_err(|e| {
|
||||
Error::ExecutionErr(format!(
|
||||
"could not copy cached binary from {bin_path} to {job_dir}/main: {e:?}"
|
||||
"could not copy cached binary from {bin_path} to {target}: {e:?}"
|
||||
))
|
||||
})?;
|
||||
|
||||
@@ -694,7 +709,7 @@ pub async fn handle_rust_job(
|
||||
.stderr(Stdio::piped());
|
||||
start_child_process(nsjail_cmd, NSJAIL_PATH.as_str(), false).await?
|
||||
} else {
|
||||
let compiled_executable_name = "./main";
|
||||
let compiled_executable_name = &format!("{job_dir}/{RUST_BIN_NAME}");
|
||||
let mut run_rust = build_command_with_isolation(compiled_executable_name, &[]);
|
||||
run_rust
|
||||
.current_dir(job_dir)
|
||||
|
||||
@@ -563,7 +563,16 @@ lazy_static::lazy_static! {
|
||||
pub static ref DOTNET_PATH: String = std::env::var("DOTNET_PATH").unwrap_or_else(|_| DOTNET_DEFAULT_PATH.to_string());
|
||||
pub static ref NSJAIL_PATH: String = std::env::var("NSJAIL_PATH").unwrap_or_else(|_| "nsjail".to_string());
|
||||
pub static ref PATH_ENV: String = std::env::var("PATH").unwrap_or_else(|_| String::new());
|
||||
pub static ref HOME_ENV: String = std::env::var("HOME").unwrap_or_else(|_| "/tmp".to_string());
|
||||
pub static ref HOME_ENV: String = {
|
||||
#[cfg(not(windows))]
|
||||
{ std::env::var("HOME").unwrap_or_else(|_| "/tmp".to_string()) }
|
||||
#[cfg(windows)]
|
||||
{
|
||||
std::env::var("HOME")
|
||||
.or_else(|_| std::env::var("USERPROFILE"))
|
||||
.unwrap_or_else(|_| std::env::temp_dir().to_string_lossy().to_string())
|
||||
}
|
||||
};
|
||||
pub static ref GIT_PATH: String = std::env::var("GIT_PATH").unwrap_or_else(|_| "/usr/bin/git".to_string());
|
||||
|
||||
pub static ref NODE_PATH: Option<String> = std::env::var("NODE_PATH").ok();
|
||||
|
||||
Reference in New Issue
Block a user