diff --git a/.cargo/config.toml b/.cargo/config.toml index 38c284958a..59a9cb990d 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -18,5 +18,11 @@ rustflags = [ "-C", "link-arg=dynamic_lookup", ] +[target.'cfg(all(windows, target_env = "msvc"))'] +rustflags = [ + "--cfg", "tokio_unstable", + "-C", "link-arg=/STACK:8388608", +] + [env] LIBSQLITE3_FLAGS = "SQLITE_ENABLE_BATCH_ATOMIC_WRITE" diff --git a/Cargo.lock b/Cargo.lock index 7d4e0fd88b..a763c0e2c8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5904,6 +5904,7 @@ dependencies = [ "rivet-config", "rivet-env", "rivet-metrics", + "rivet-util", "tokio", "tracing", ] @@ -6202,6 +6203,7 @@ dependencies = [ "rivet-util-id", "rivet-util-serde", "serde", + "socket2 0.6.0", "tokio", "tracing", "url", diff --git a/Cargo.toml b/Cargo.toml index 2ced15da30..049718fc46 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -162,6 +162,7 @@ members = [ slog = "2.7" slog-async = "2.8" slog-term = "2.9" + socket2 = "0.6" scopeguard = "1.2" statrs = "0.18" subtle = "2" diff --git a/docs-internal/WINDOWS_DEVELOPMENT.md b/docs-internal/WINDOWS_DEVELOPMENT.md new file mode 100644 index 0000000000..8376d513f9 --- /dev/null +++ b/docs-internal/WINDOWS_DEVELOPMENT.md @@ -0,0 +1,48 @@ +# Windows Development + +Build and run Rivet natively on Windows, without WSL. Run all commands in PowerShell from the repository root. + +## Prerequisites + +```powershell +winget install Microsoft.VisualStudio.2022.BuildTools --override "--quiet --wait --norestart --add Microsoft.VisualStudio.Workload.VCTools --includeRecommended" +winget install Rustlang.Rustup +winget install LLVM.LLVM +winget install Kitware.CMake +winget install NASM.NASM +winget install OpenJS.NodeJS.LTS +``` + +NASM does not add itself to `PATH`, and `bindgen` needs to find `libclang`: + +```powershell +[Environment]::SetEnvironmentVariable("Path", [Environment]::GetEnvironmentVariable("Path", "User") + ";$env:LOCALAPPDATA\bin\NASM", "User") +[Environment]::SetEnvironmentVariable("LIBCLANG_PATH", "$env:ProgramFiles\LLVM\bin", "User") +``` + +Open a new terminal so the changes apply. + +## Build + +```powershell +npm install --global pnpm +pnpm install +pnpm -F @rivetkit/engine-frontend build:engine +cargo build -p rivet-engine +$env:SKIP_WASM_BUILD = "1" +pnpm build -F rivetkit -F @rivetkit/react +``` + +`build:engine` builds the dashboard that the engine embeds, so build it before the engine. `SKIP_WASM_BUILD` skips the wasm runtime, which this guide does not cover. + +## Run + +The published Windows engine package does not run yet, so point RivetKit at the locally built engine: + +```powershell +$env:RIVET_ENGINE_BINARY_PATH = "$PWD\target\debug\rivet-engine.exe" +cd examples\hello-world +pnpm dev +``` + +Open http://localhost:5173 for the example and http://127.0.0.1:6420/ui/ for the dashboard. diff --git a/engine/packages/api-peer/src/lib.rs b/engine/packages/api-peer/src/lib.rs index 9fc1427869..e8ab0cfee2 100644 --- a/engine/packages/api-peer/src/lib.rs +++ b/engine/packages/api-peer/src/lib.rs @@ -21,7 +21,7 @@ pub async fn start(config: rivet_config::Config, pools: rivet_pools::Pools) -> R let router = router::router(config, pools).await?; - let listener = tokio::net::TcpListener::bind(addr).await?; + let listener = rivet_util::net::bind_tcp_listener(addr)?; tracing::info!(?host, ?port, "api-peer server listening"); axum::serve(listener, router).await?; diff --git a/engine/packages/guard-core/src/server.rs b/engine/packages/guard-core/src/server.rs index da43c7f05c..65477ac8ab 100644 --- a/engine/packages/guard-core/src/server.rs +++ b/engine/packages/guard-core/src/server.rs @@ -37,7 +37,7 @@ pub async fn run_server( routing_fn.clone(), cache_key_fn.clone(), )); - let http_listener = tokio::net::TcpListener::bind(http_addr).await?; + let http_listener = rivet_util::net::bind_tcp_listener(http_addr)?; // Set up HTTPS server (if configured) let (https_addr, https_factory, https_listener, https_acceptor) = if let Some(https) = @@ -49,7 +49,7 @@ pub async fn run_server( routing_fn.clone(), cache_key_fn.clone(), )); - let listener = tokio::net::TcpListener::bind(https_addr).await?; + let listener = rivet_util::net::bind_tcp_listener(https_addr)?; // Configure TLS if resolver function is provided let acceptor = if let Some(resolver_fn) = cert_resolver_fn { diff --git a/engine/packages/metrics-server/Cargo.toml b/engine/packages/metrics-server/Cargo.toml index 43316560c5..36abf8347c 100644 --- a/engine/packages/metrics-server/Cargo.toml +++ b/engine/packages/metrics-server/Cargo.toml @@ -17,5 +17,6 @@ opentelemetry_sdk.workspace = true rivet-config.workspace = true rivet-env.workspace = true rivet-metrics.workspace = true +rivet-util.workspace = true tokio.workspace = true tracing.workspace = true diff --git a/engine/packages/metrics-server/src/server.rs b/engine/packages/metrics-server/src/server.rs index b2512b3e31..6a20148a03 100644 --- a/engine/packages/metrics-server/src/server.rs +++ b/engine/packages/metrics-server/src/server.rs @@ -4,6 +4,7 @@ use anyhow::*; use hyper::{ Body, Request, Response, Server, header::CONTENT_TYPE, + server::{Builder, conn::AddrIncoming}, service::{make_service_fn, service_fn}, }; use rivet_metrics::prometheus::{Encoder, TextEncoder}; @@ -14,7 +15,7 @@ pub async fn run_standalone(config: rivet_config::Config) -> Result<()> { let port = config.metrics.port(); let addr = SocketAddr::from((host, port)); - let server = match Server::try_bind(&addr) { + let server = match bind_server(addr) { Ok(x) => x, Err(err) => { tracing::error!(?host, ?port, ?err, "failed to bind metrics server"); @@ -34,6 +35,11 @@ pub async fn run_standalone(config: rivet_config::Config) -> Result<()> { Ok(()) } +fn bind_server(addr: SocketAddr) -> Result> { + let listener = rivet_util::net::bind_std_tcp_listener(addr)?; + Ok(Server::from_tcp(listener)?) +} + #[tracing::instrument(level = "debug", skip_all)] async fn serve_req(_req: Request) -> Result, hyper::Error> { let encoder = TextEncoder::new(); diff --git a/engine/packages/runner-protocol/build.rs b/engine/packages/runner-protocol/build.rs index 4a5fb4577c..9803cb1291 100644 --- a/engine/packages/runner-protocol/build.rs +++ b/engine/packages/runner-protocol/build.rs @@ -62,7 +62,8 @@ mod typescript { let output_path = src_dir.join("index.ts"); - let output = Command::new(repo_root.join("node_modules/@bare-ts/tools/dist/bin/cli.js")) + let output = Command::new("node") + .arg(repo_root.join("node_modules/@bare-ts/tools/dist/bin/cli.js")) .arg("compile") .arg("--generator") .arg("ts") diff --git a/engine/packages/util/Cargo.toml b/engine/packages/util/Cargo.toml index 918616ba1f..9efa032869 100644 --- a/engine/packages/util/Cargo.toml +++ b/engine/packages/util/Cargo.toml @@ -28,6 +28,7 @@ rivet-tracing-utils.workspace = true rivet-util-serde.workspace = true rivet-util-id = { workspace = true, features = ["universaldb"] } serde.workspace = true +socket2.workspace = true tokio.workspace = true tracing.workspace = true url.workspace = true diff --git a/engine/packages/util/src/lib.rs b/engine/packages/util/src/lib.rs index 2bb9709f00..026b05cb2b 100644 --- a/engine/packages/util/src/lib.rs +++ b/engine/packages/util/src/lib.rs @@ -13,6 +13,7 @@ pub mod geo; pub mod math; pub mod metric; pub mod metrics; +pub mod net; pub mod req; pub mod serde; pub mod size; diff --git a/engine/packages/util/src/net.rs b/engine/packages/util/src/net.rs new file mode 100644 index 0000000000..0004cefa9a --- /dev/null +++ b/engine/packages/util/src/net.rs @@ -0,0 +1,34 @@ +use std::{io, net::SocketAddr}; + +use socket2::{Domain, Protocol, Socket, Type}; + +const LISTEN_BACKLOG: i32 = 1024; + +pub fn bind_tcp_listener(addr: SocketAddr) -> io::Result { + tokio::net::TcpListener::from_std(bind_std_tcp_listener(addr)?) +} + +pub fn bind_std_tcp_listener(addr: SocketAddr) -> io::Result { + let socket = Socket::new(Domain::for_address(addr), Type::STREAM, Some(Protocol::TCP))?; + + if addr.is_ipv6() { + socket.set_only_v6(false)?; + } + + allow_fast_rebind(&socket)?; + socket.bind(&addr.into())?; + socket.listen(LISTEN_BACKLOG)?; + socket.set_nonblocking(true)?; + + Ok(socket.into()) +} + +#[cfg(unix)] +fn allow_fast_rebind(socket: &Socket) -> io::Result<()> { + socket.set_reuse_address(true) +} + +#[cfg(not(unix))] +fn allow_fast_rebind(_socket: &Socket) -> io::Result<()> { + Ok(()) +} diff --git a/engine/packages/util/tests/net.rs b/engine/packages/util/tests/net.rs new file mode 100644 index 0000000000..5180d88d6e --- /dev/null +++ b/engine/packages/util/tests/net.rs @@ -0,0 +1,24 @@ +use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; + +use rivet_util::net::bind_tcp_listener; +use tokio::net::TcpStream; + +#[tokio::test] +async fn ipv6_unspecified_listener_accepts_ipv4_loopback_connections() { + assert_accepts_ipv4_loopback_connection(Ipv6Addr::UNSPECIFIED.into()).await; +} + +#[tokio::test] +async fn ipv4_listener_accepts_ipv4_loopback_connections() { + assert_accepts_ipv4_loopback_connection(Ipv4Addr::LOCALHOST.into()).await; +} + +async fn assert_accepts_ipv4_loopback_connection(listen_ip: IpAddr) { + let listener = bind_tcp_listener(SocketAddr::new(listen_ip, 0)).expect("bind listener"); + let port = listener.local_addr().expect("listener address").port(); + + TcpStream::connect((Ipv4Addr::LOCALHOST, port)) + .await + .expect("connect over ipv4 loopback"); + listener.accept().await.expect("accept ipv4 connection"); +} diff --git a/engine/sdks/rust/actor-runtime-socket-protocol/build.rs b/engine/sdks/rust/actor-runtime-socket-protocol/build.rs index b99b603e66..58c02b6d31 100644 --- a/engine/sdks/rust/actor-runtime-socket-protocol/build.rs +++ b/engine/sdks/rust/actor-runtime-socket-protocol/build.rs @@ -46,7 +46,8 @@ mod typescript { .and_then(|stem| stem.to_str()) .expect("schema has valid UTF-8 file stem"); let output_path = output_dir.join(format!("{version}.ts")); - let output = Command::new(&cli_js_path) + let output = Command::new("node") + .arg(&cli_js_path) .arg("compile") .arg("--generator") .arg("ts") diff --git a/engine/sdks/rust/envoy-protocol/build.rs b/engine/sdks/rust/envoy-protocol/build.rs index 9e1210c825..f81ef9702f 100644 --- a/engine/sdks/rust/envoy-protocol/build.rs +++ b/engine/sdks/rust/envoy-protocol/build.rs @@ -72,20 +72,21 @@ mod typescript { let output_path = src_dir.join("index.ts"); - let output = Command::new( - workspace_root - .parent() - .unwrap() - .join("node_modules/@bare-ts/tools/dist/bin/cli.js"), - ) - .arg("compile") - .arg("--generator") - .arg("ts") - .arg(highest_version_path) - .arg("-o") - .arg(&output_path) - .output() - .expect("Failed to execute bare compiler for TypeScript"); + let output = Command::new("node") + .arg( + workspace_root + .parent() + .unwrap() + .join("node_modules/@bare-ts/tools/dist/bin/cli.js"), + ) + .arg("compile") + .arg("--generator") + .arg("ts") + .arg(highest_version_path) + .arg("-o") + .arg(&output_path) + .output() + .expect("Failed to execute bare compiler for TypeScript"); if !output.status.success() { panic!( diff --git a/frontend/package.json b/frontend/package.json index d00b9efe46..bbf279b758 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -7,14 +7,14 @@ "dev": "vite", "dev:inspector": "vite build --config vite.inspector-ui.config.ts --watch", "check-types": "tsc --noEmit", - "build": "NODE_OPTIONS='--max-old-space-size=8192' vite build --mode=production", - "build:engine": "BASE_URL=/ui/ NODE_OPTIONS='--max-old-space-size=8192' vite build --mode=production", + "build": "node scripts/vite-build.mjs", + "build:engine": "node scripts/vite-build.mjs --engine", "preview": "vite preview", "test": "vitest run", "test:watch": "vitest", "dev:ladle": "ladle dev", - "build:inspector": "NODE_OPTIONS='--max-old-space-size=8192' vite build --config vite.inspector.config.ts --mode=production", - "build:inspector-ui": "NODE_OPTIONS='--max-old-space-size=8192' vite build --config vite.inspector-ui.config.ts --mode=production && node scripts/generate-inspector-tab-css.mjs", + "build:inspector": "node scripts/vite-build.mjs --config vite.inspector.config.ts", + "build:inspector-ui": "node scripts/vite-build.mjs --config vite.inspector-ui.config.ts && node scripts/generate-inspector-tab-css.mjs", "build:inspector-tab-css": "node scripts/generate-inspector-tab-css.mjs", "build:ladle": "ladle build" }, diff --git a/frontend/scripts/vite-build.mjs b/frontend/scripts/vite-build.mjs new file mode 100644 index 0000000000..1764b7e39c --- /dev/null +++ b/frontend/scripts/vite-build.mjs @@ -0,0 +1,49 @@ +#!/usr/bin/env node +import { execFileSync } from "node:child_process"; +import { createRequire } from "node:module"; +import { dirname, join } from "node:path"; + +const require = createRequire(import.meta.url); + +const BUILD_HEAP_LIMIT_MB = 8192; +const ENGINE_BUILD_DEFAULTS = { + BASE_URL: "/ui/", + VITE_APP_API_URL: "__SAME__", + VITE_FEATURE_FLAGS: "", +}; + +const args = process.argv.slice(2); +const isEngineBuild = args.includes("--engine"); +const viteArgs = args.filter((arg) => arg !== "--engine"); + +function resolveViteBin() { + const packageJsonPath = require.resolve("vite/package.json"); + const { bin } = require(packageJsonPath); + return join(dirname(packageJsonPath), bin.vite); +} + +function engineBuildEnv() { + return { + ...process.env, + BASE_URL: ENGINE_BUILD_DEFAULTS.BASE_URL, + VITE_APP_API_URL: + process.env.VITE_APP_API_URL ?? ENGINE_BUILD_DEFAULTS.VITE_APP_API_URL, + VITE_FEATURE_FLAGS: + process.env.VITE_FEATURE_FLAGS ?? ENGINE_BUILD_DEFAULTS.VITE_FEATURE_FLAGS, + }; +} + +execFileSync( + process.execPath, + [ + `--max-old-space-size=${BUILD_HEAP_LIMIT_MB}`, + resolveViteBin(), + "build", + "--mode=production", + ...viteArgs, + ], + { + stdio: "inherit", + env: isEngineBuild ? engineBuildEnv() : process.env, + }, +); diff --git a/rivetkit-rust/engine/artifacts/errors/actor_runtime_socket.unsupported.json b/rivetkit-rust/engine/artifacts/errors/actor_runtime_socket.unsupported.json index e159d5dd29..cfd2b6f717 100644 --- a/rivetkit-rust/engine/artifacts/errors/actor_runtime_socket.unsupported.json +++ b/rivetkit-rust/engine/artifacts/errors/actor_runtime_socket.unsupported.json @@ -2,4 +2,4 @@ "code": "unsupported", "group": "actor_runtime_socket", "message": "Actor Runtime Socket is only available on Unix native runtimes." -} +} \ No newline at end of file diff --git a/rivetkit-rust/packages/client-protocol/build.rs b/rivetkit-rust/packages/client-protocol/build.rs index 636c6f1a87..f199a2a4d1 100644 --- a/rivetkit-rust/packages/client-protocol/build.rs +++ b/rivetkit-rust/packages/client-protocol/build.rs @@ -55,7 +55,8 @@ mod typescript { .expect("schema has valid UTF-8 file stem"); let output_path = output_dir.join(format!("{version}.ts")); - let output = Command::new(&cli_js_path) + let output = Command::new("node") + .arg(&cli_js_path) .arg("compile") .arg("--generator") .arg("ts") diff --git a/rivetkit-rust/packages/engine-process/src/lib.rs b/rivetkit-rust/packages/engine-process/src/lib.rs index 51c7f39b52..27256854bb 100644 --- a/rivetkit-rust/packages/engine-process/src/lib.rs +++ b/rivetkit-rust/packages/engine-process/src/lib.rs @@ -20,6 +20,8 @@ const ENGINE_VERSION_ENV: &str = "RIVETKIT_ENGINE_VERSION"; const RELEASES_ENDPOINT_ENV: &str = "RIVETKIT_ENGINE_RELEASES_ENDPOINT"; const RELEASES_ENDPOINT: &str = "https://releases.rivet.dev"; const DOWNLOAD_TIMEOUT: Duration = Duration::from_secs(60); +#[cfg(windows)] +const CREATE_NO_WINDOW: u32 = 0x0800_0000; #[derive(Debug, Deserialize)] struct EngineHealthResponse { @@ -116,6 +118,8 @@ pub enum EngineStartup { /// `Child` does not send SIGKILL on drop. /// - Stdout and stderr are routed to log files at spawn time so the engine's /// write fds remain valid after the host's pipes close. +/// - The engine runs in its own process group on Unix and with its own hidden +/// console on Windows, so terminal signals aimed at the host skip it. /// - On startup we probe the configured endpoint and reuse a healthy engine /// instead of spawning a duplicate. /// @@ -226,6 +230,13 @@ impl EngineProcessManager { #[cfg(unix)] command.process_group(0); + // Windows gives a console program started by a windowed host, such as + // Electron, a visible console window of its own. A hidden console avoids + // that window, and like the Unix process group above it keeps Ctrl+C in + // the host's terminal from reaching the engine. + #[cfg(windows)] + command.creation_flags(CREATE_NO_WINDOW); + let mut child = command .spawn() .with_context(|| format!("spawn engine binary `{}`", binary_path.display()))?; diff --git a/rivetkit-rust/packages/inspector-protocol/build.rs b/rivetkit-rust/packages/inspector-protocol/build.rs index 1cfa3e53a7..4f70d01197 100644 --- a/rivetkit-rust/packages/inspector-protocol/build.rs +++ b/rivetkit-rust/packages/inspector-protocol/build.rs @@ -55,7 +55,8 @@ mod typescript { .expect("schema has valid UTF-8 file stem"); let output_path = output_dir.join(format!("{version}.ts")); - let output = Command::new(&cli_js_path) + let output = Command::new("node") + .arg(&cli_js_path) .arg("compile") .arg("--generator") .arg("ts") diff --git a/rivetkit-typescript/packages/rivetkit-napi/scripts/build.mjs b/rivetkit-typescript/packages/rivetkit-napi/scripts/build.mjs index dfa8539c3a..dd6889e601 100644 --- a/rivetkit-typescript/packages/rivetkit-napi/scripts/build.mjs +++ b/rivetkit-typescript/packages/rivetkit-napi/scripts/build.mjs @@ -3,6 +3,27 @@ * Build wrapper for rivetkit-napi. */ import { execFileSync } from "node:child_process"; +import { createRequire } from "node:module"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; + +const packageDir = dirname(dirname(fileURLToPath(import.meta.url))); +const require = createRequire(import.meta.url); + +function resolveNapiCliBin() { + try { + const packageJsonPath = require.resolve("@napi-rs/cli/package.json", { + paths: [packageDir], + }); + const { bin } = require(packageJsonPath); + return join(dirname(packageJsonPath), bin.napi); + } catch (err) { + throw new Error( + "Missing @napi-rs/cli dependency. Run pnpm install before building @rivetkit/rivetkit-napi.", + { cause: err }, + ); + } +} const args = process.argv.slice(2); const extraFlags = args.includes("--release") ? ["--release"] : []; @@ -25,5 +46,7 @@ if (process.env.SKIP_NAPI_BUILD === "1") { // for local builds run `pnpm -F @rivetkit/engine-frontend build:inspector-ui` // (or `turbo build:inspector-ui`) first. const cmd = ["build", "--platform", ...extraFlags]; +const napiCliBin = resolveNapiCliBin(); + console.log(`[rivetkit-napi/build] running: napi ${cmd.join(" ")}`); -execFileSync("napi", cmd, { stdio: "inherit" }); +execFileSync(process.execPath, [napiCliBin, ...cmd], { stdio: "inherit" }); diff --git a/rivetkit-typescript/packages/rivetkit/tests/driver/engine-restart-serverless.ts b/rivetkit-typescript/packages/rivetkit/tests/driver/engine-restart-serverless.ts index 6c54b13165..fe066a1c73 100644 --- a/rivetkit-typescript/packages/rivetkit/tests/driver/engine-restart-serverless.ts +++ b/rivetkit-typescript/packages/rivetkit/tests/driver/engine-restart-serverless.ts @@ -7,12 +7,11 @@ import { fileURLToPath } from "node:url"; import { getEnginePath } from "@rivetkit/engine-cli"; import getPort from "get-port"; import { createClient } from "../../src/client/mod"; +import { REPO_ENGINE_BINARY } from "../repo-engine-binary"; const TEST_DIR = join(dirname(fileURLToPath(import.meta.url)), ".."); const PACKAGE_DIR = dirname(TEST_DIR); -const REPO_ENGINE_BINARY = - process.env.ENGINE_BINARY ?? - join(PACKAGE_DIR, "../../../target/debug/rivet-engine"); +const ENGINE_BINARY = process.env.ENGINE_BINARY ?? REPO_ENGINE_BINARY; const SERVERLESS_RUNTIME_PATH = join( TEST_DIR, "fixtures/engine-restart-serverless-runtime.ts", @@ -491,8 +490,8 @@ async function main() { } function resolveEngineBinaryPath(): string { - if (existsSync(REPO_ENGINE_BINARY)) { - return REPO_ENGINE_BINARY; + if (existsSync(ENGINE_BINARY)) { + return ENGINE_BINARY; } return getEnginePath(); diff --git a/rivetkit-typescript/packages/rivetkit/tests/fixtures/napi-runtime-server.ts b/rivetkit-typescript/packages/rivetkit/tests/fixtures/napi-runtime-server.ts index 15157b33ae..5baf9e30f2 100644 --- a/rivetkit-typescript/packages/rivetkit/tests/fixtures/napi-runtime-server.ts +++ b/rivetkit-typescript/packages/rivetkit/tests/fixtures/napi-runtime-server.ts @@ -1,17 +1,10 @@ import { existsSync } from "node:fs"; -import { dirname, resolve } from "node:path"; -import { fileURLToPath } from "node:url"; import { getEnginePath } from "@rivetkit/engine-cli"; import { z } from "zod/v4"; import { db } from "../../src/db/mod"; import { actor, event, queue, setup, UserError } from "../../src/mod"; import { buildNativeRegistry } from "../../src/registry/native"; - -const fixtureDir = dirname(fileURLToPath(import.meta.url)); -const repoEngineBinary = resolve( - fixtureDir, - "../../../../../target/debug/rivet-engine", -); +import { REPO_ENGINE_BINARY } from "../repo-engine-binary"; const endpoint = process.env.RIVETKIT_TEST_ENDPOINT ?? "http://127.0.0.1:6642"; const connParamsSchema = z.object({ @@ -30,8 +23,8 @@ const jobSchema = z.object({ }); function resolveEngineBinaryPath(): string { - if (existsSync(repoEngineBinary)) { - return repoEngineBinary; + if (existsSync(REPO_ENGINE_BINARY)) { + return REPO_ENGINE_BINARY; } return getEnginePath(); diff --git a/rivetkit-typescript/packages/rivetkit/tests/platforms/supabase-functions.test.ts b/rivetkit-typescript/packages/rivetkit/tests/platforms/supabase-functions.test.ts index 320705856c..ea2169381a 100644 --- a/rivetkit-typescript/packages/rivetkit/tests/platforms/supabase-functions.test.ts +++ b/rivetkit-typescript/packages/rivetkit/tests/platforms/supabase-functions.test.ts @@ -15,6 +15,7 @@ import { fileURLToPath } from "node:url"; import { getEnginePath } from "@rivetkit/engine-cli"; import getPort from "get-port"; import { describe, expect, test } from "vitest"; +import { REPO_ENGINE_BINARY } from "../repo-engine-binary"; import { buildPlatformSqliteCounterActorSource, createPlatformServerlessRunner, @@ -32,7 +33,6 @@ import { const SUPABASE_VERSION = "2.95.4"; const TEST_DIR = dirname(fileURLToPath(import.meta.url)); const REPO_ROOT = resolve(TEST_DIR, "../../../../.."); -const REPO_ENGINE_BINARY = resolve(REPO_ROOT, "target/debug/rivet-engine"); const RIVETKIT_PACKAGE_DIR = resolve( REPO_ROOT, "rivetkit-typescript/packages/rivetkit", diff --git a/rivetkit-typescript/packages/rivetkit/tests/repo-engine-binary.ts b/rivetkit-typescript/packages/rivetkit/tests/repo-engine-binary.ts new file mode 100644 index 0000000000..0ee7bab7ba --- /dev/null +++ b/rivetkit-typescript/packages/rivetkit/tests/repo-engine-binary.ts @@ -0,0 +1,13 @@ +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; + +const REPO_ROOT = join(dirname(fileURLToPath(import.meta.url)), "../../../.."); +const ENGINE_BINARY_NAME = + process.platform === "win32" ? "rivet-engine.exe" : "rivet-engine"; + +export const REPO_ENGINE_BINARY = join( + REPO_ROOT, + "target", + "debug", + ENGINE_BINARY_NAME, +); diff --git a/rivetkit-typescript/packages/rivetkit/tests/shared-engine.ts b/rivetkit-typescript/packages/rivetkit/tests/shared-engine.ts index b817c8abf5..3b8b3a584a 100644 --- a/rivetkit-typescript/packages/rivetkit/tests/shared-engine.ts +++ b/rivetkit-typescript/packages/rivetkit/tests/shared-engine.ts @@ -15,12 +15,9 @@ import { dirname, join } from "node:path"; import { fileURLToPath } from "node:url"; import { getEnginePath } from "@rivetkit/engine-cli"; import getPort from "get-port"; +import { REPO_ENGINE_BINARY } from "./repo-engine-binary"; const TEST_DIR = dirname(fileURLToPath(import.meta.url)); -const REPO_ENGINE_BINARY = join( - TEST_DIR, - "../../../../target/debug/rivet-engine", -); const TIMING_ENABLED = process.env.RIVETKIT_DRIVER_TEST_TIMING === "1"; const ENGINE_START_LOCK_STALE_MS = 120_000; diff --git a/scripts/format/agent-format.mjs b/scripts/format/agent-format.mjs index 5218beee61..5ca84a1bf6 100755 --- a/scripts/format/agent-format.mjs +++ b/scripts/format/agent-format.mjs @@ -1,8 +1,10 @@ #!/usr/bin/env node import { existsSync, readFileSync, statSync } from "node:fs"; -import { isAbsolute, relative, resolve } from "node:path"; +import { createRequire } from "node:module"; +import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path"; import { spawnSync } from "node:child_process"; +const require = createRequire(import.meta.url); const root = git(["rev-parse", "--show-toplevel"]) || process.cwd(); process.chdir(root); @@ -19,8 +21,8 @@ const biomeFiles = existingFiles.filter(isBiomeFile); const rustFiles = existingFiles.filter((path) => path.endsWith(".rs")); if (biomeFiles.length > 0) { - run("pnpm", [ - "biome", + run(process.execPath, [ + resolvePackageBin("@biomejs/biome", "biome"), "format", "--write", "--files-ignore-unknown=true", @@ -86,7 +88,15 @@ function normalizeRepoPath(path) { const absolute = isAbsolute(path) ? path : resolve(root, path); const relativePath = relative(root, absolute); if (!relativePath || relativePath.startsWith("..")) return null; - return relativePath; + return relativePath.split(sep).join("/"); +} + +function resolvePackageBin(packageName, binName) { + const packageJsonPath = require.resolve(`${packageName}/package.json`, { + paths: [root], + }); + const { bin } = require(packageJsonPath); + return join(dirname(packageJsonPath), bin[binName]); } function isGeneratedOutput(path) {