diff --git a/.github/packages/npm-package/ephemeralValidator.ts b/.github/packages/npm-package/ephemeralValidator.ts index 887cb707d..ade502502 100755 --- a/.github/packages/npm-package/ephemeralValidator.ts +++ b/.github/packages/npm-package/ephemeralValidator.ts @@ -3,9 +3,9 @@ import fs from "fs"; import { spawn, spawnSync } from "child_process"; import path from "path"; import { arch, platform } from "os"; -import { version } from "./package.json"; +import { VERSIONS } from "./getVersions"; -const PACKAGE_VERSION = `ephemeral-validator ${version}`; +const PACKAGE_VERSION = `ephemeral-validator ${VERSIONS.EPHEMERAL_VALIDATOR}`; function getBinaryVersion(location: string): [string | null, string | null] { const result = spawnSync(location, ["--version"]); diff --git a/.github/packages/npm-package/getVersions.ts b/.github/packages/npm-package/getVersions.ts new file mode 100644 index 000000000..6eb47103d --- /dev/null +++ b/.github/packages/npm-package/getVersions.ts @@ -0,0 +1,26 @@ +import fs from "fs"; +import path from "path"; + +interface PackageJson { + version: string; + optionalDependencies: Record; +} + +function readPackageJson(): PackageJson { + // When compiled to lib/, we need to go up one directory to find package.json + const packageJsonPath = path.join(__dirname, "../package.json"); + const packageJsonContent = fs.readFileSync(packageJsonPath, "utf8"); + return JSON.parse(packageJsonContent); +} + +export function getVersions() { + const pkg = readPackageJson(); + + return { + EPHEMERAL_VALIDATOR: pkg.version, + VRF_ORACLE: pkg.optionalDependencies["@magicblock-labs/vrf-oracle-linux-x64"], + RPC_ROUTER: pkg.optionalDependencies["@magicblock-labs/rpc-router-linux-x64"], + } as const; +} + +export const VERSIONS = getVersions(); \ No newline at end of file diff --git a/.github/packages/npm-package/package.json b/.github/packages/npm-package/package.json index 9ecbbedae..94affcafe 100644 --- a/.github/packages/npm-package/package.json +++ b/.github/packages/npm-package/package.json @@ -1,6 +1,6 @@ { "name": "@magicblock-labs/ephemeral-validator", - "version": "0.3.1", + "version": "0.5.1", "description": "MagicBlock Ephemeral Validator", "homepage": "https://github.com/magicblock-labs/magicblock-validator#readme", "bugs": { @@ -13,7 +13,9 @@ "license": "Business Source License 1.1", "bin": { "ephemeral-validator": "ephemeralValidator.js", - "mb-test-validator": "mbTestValidator.js" + "mb-test-validator": "mbTestValidator.js", + "rpc-router": "rpcRouter.js", + "vrf-oracle": "vrfOracle.js" }, "scripts": { "typecheck": "tsc --noEmit", @@ -28,11 +30,19 @@ "typescript": "^4.9.4" }, "optionalDependencies": { - "@magicblock-labs/ephemeral-validator-darwin-arm64": "0.3.1", - "@magicblock-labs/ephemeral-validator-darwin-x64": "0.3.1", - "@magicblock-labs/ephemeral-validator-linux-arm64": "0.3.1", - "@magicblock-labs/ephemeral-validator-linux-x64": "0.3.1", - "@magicblock-labs/ephemeral-validator-windows-x64": "0.3.1" + "@magicblock-labs/ephemeral-validator-darwin-arm64": "0.5.1", + "@magicblock-labs/ephemeral-validator-darwin-x64": "0.5.1", + "@magicblock-labs/ephemeral-validator-linux-arm64": "0.5.1", + "@magicblock-labs/ephemeral-validator-linux-x64": "0.5.1", + "@magicblock-labs/ephemeral-validator-windows-x64": "0.5.1", + "@magicblock-labs/vrf-oracle-linux-x64": "0.2.1", + "@magicblock-labs/vrf-oracle-linux-arm64": "0.2.1", + "@magicblock-labs/vrf-oracle-darwin-x64": "0.2.1", + "@magicblock-labs/vrf-oracle-darwin-arm64": "0.2.1", + "@magicblock-labs/rpc-router-linux-x64": "0.0.1", + "@magicblock-labs/rpc-router-linux-arm64": "0.0.1", + "@magicblock-labs/rpc-router-darwin-x64": "0.0.1", + "@magicblock-labs/rpc-router-darwin-arm64": "0.0.1" }, "publishConfig": { "access": "public" diff --git a/.github/packages/npm-package/package.json.tmpl b/.github/packages/npm-package/package.json.tmpl index 96e7615a7..400372b09 100644 --- a/.github/packages/npm-package/package.json.tmpl +++ b/.github/packages/npm-package/package.json.tmpl @@ -1,7 +1,7 @@ { "name": "@magicblock-labs/${node_pkg}", "description": "Ephemeral Validator (${node_pkg})", - "version": "0.3.1", + "version": "0.5.1", "repository": { "type": "git", "url": "git+https://github.com/magicblock-labs/magicblock-validator.git" diff --git a/.github/packages/npm-package/rpcRouter.ts b/.github/packages/npm-package/rpcRouter.ts new file mode 100755 index 000000000..d69c861eb --- /dev/null +++ b/.github/packages/npm-package/rpcRouter.ts @@ -0,0 +1,113 @@ +#!/usr/bin/env node +import fs from "fs"; +import { spawn, spawnSync } from "child_process"; +import path from "path"; +import { arch, platform } from "os"; +import { VERSIONS } from "./getVersions"; + +const PACKAGE_VERSION = `rpc-router ${VERSIONS.RPC_ROUTER}`; + +function getBinaryVersion(location: string): [string | null, string | null] { + const result = spawnSync(location, ["--version"]); + const error: string | null = + (result.error && result.error.toString()) || + (result.stderr.length > 0 && result.stderr.toString().trim()) || + null; + return [error, result.stdout && result.stdout.toString().trim()]; +} + +function getExePath(): string { + let os: string = platform(); + let extension = ""; + if (["win32", "cygwin"].includes(os)) { + os = "windows"; + extension = ".exe"; + } + const binaryName = `@magicblock-labs/rpc-router-${os}-${arch()}/bin/rpc-router${extension}`; + try { + return require.resolve(binaryName); + } catch (e) { + throw new Error( + `Couldn't find application binary inside node_modules for ${os}-${arch()}, expected location: ${binaryName}`, + ); + } +} + +function runWithForwardedExit(child: ReturnType): void { + child.on("exit", (code: number | null, signal: NodeJS.Signals | null) => { + process.on("exit", () => { + if (signal) { + process.kill(process.pid, signal); + } else if (code !== null) { + process.exit(code); + } + }); + }); + + process.on("SIGINT", () => { + child.kill("SIGINT"); + child.kill("SIGTERM"); + }); +} + +function runRpcRouter(location: string): void { + const args = process.argv.slice(2); + const env = { + ...process.env, + }; + const rpcRouter = spawn(location, args, { stdio: "inherit", env }); + runWithForwardedExit(rpcRouter); +} + +function tryPackageRpcRouter(): boolean { + try { + const path = getExePath(); + runRpcRouter(path); + return true; + } catch (e) { + console.error( + "Failed to run rpc-router from package:", + e instanceof Error ? e.message : e, + ); + return false; + } +} + +function trySystemRpcRouter(): void { + const absolutePath = process.env.PATH?.split(path.delimiter) + .filter((dir) => dir !== path.dirname(process.argv[1])) + .find((dir) => { + try { + fs.accessSync(`${dir}/rpc-router`, fs.constants.X_OK); + return true; + } catch { + return false; + } + }); + + if (!absolutePath) { + console.error( + `Could not find globally installed rpc-router, please install with cargo.`, + ); + process.exit(1); + } + + const absoluteBinaryPath = `${absolutePath}/rpc-router`; + const [error, binaryVersion] = getBinaryVersion(absoluteBinaryPath); + + if (error !== null) { + console.error(`Failed to get version of global binary: ${error}`); + return; + } + if (binaryVersion !== PACKAGE_VERSION) { + console.error( + `Globally installed rpc-router version is not correct. Expected "${PACKAGE_VERSION}", found "${binaryVersion}".`, + ); + return; + } + + runRpcRouter(absoluteBinaryPath); +} + +// Try to run rpc-router from package first, then fall back to system installation. +tryPackageRpcRouter() || trySystemRpcRouter(); diff --git a/.github/packages/npm-package/vrfOracle.ts b/.github/packages/npm-package/vrfOracle.ts new file mode 100755 index 000000000..9eb542244 --- /dev/null +++ b/.github/packages/npm-package/vrfOracle.ts @@ -0,0 +1,114 @@ +#!/usr/bin/env node +import fs from "fs"; +import { spawn, spawnSync } from "child_process"; +import path from "path"; +import { arch, platform } from "os"; +import { VERSIONS } from "./getVersions"; + +const PACKAGE_VERSION = `vrf-oracle ${VERSIONS.VRF_ORACLE}`; + +function getBinaryVersion(location: string): [string | null, string | null] { + const result = spawnSync(location, ["--version"]); + const error: string | null = + (result.error && result.error.toString()) || + (result.stderr.length > 0 && result.stderr.toString().trim()) || + null; + return [error, result.stdout && result.stdout.toString().trim()]; +} + +function getExePath(): string { + let os: string = platform(); + let extension = ""; + if (["win32", "cygwin"].includes(os)) { + os = "windows"; + extension = ".exe"; + } + const binaryName = `@magicblock-labs/vrf-oracle-${os}-${arch()}/bin/vrf-oracle${extension}`; + try { + return require.resolve(binaryName); + } catch (e) { + throw new Error( + `Couldn't find application binary inside node_modules for ${os}-${arch()}, expected location: ${binaryName}`, + ); + } +} + +function runWithForwardedExit(child: ReturnType): void { + child.on("exit", (code: number | null, signal: NodeJS.Signals | null) => { + process.on("exit", () => { + if (signal) { + process.kill(process.pid, signal); + } else if (code !== null) { + process.exit(code); + } + }); + }); + + process.on("SIGINT", () => { + child.kill("SIGINT"); + child.kill("SIGTERM"); + }); +} + +function runVrfOracle(location: string): void { + const args = process.argv.slice(2); + const env = { + ...process.env, + RUST_LOG: "quiet", + }; + const vrfOracle = spawn(location, args, { stdio: "inherit", env}); + runWithForwardedExit(vrfOracle); +} + +function tryPackageVrfOracle(): boolean { + try { + const path = getExePath(); + runVrfOracle(path); + return true; + } catch (e) { + console.error( + "Failed to run vrf-oracle from package:", + e instanceof Error ? e.message : e, + ); + return false; + } +} + +function trySystemVrfOracle(): void { + const absolutePath = process.env.PATH?.split(path.delimiter) + .filter((dir) => dir !== path.dirname(process.argv[1])) + .find((dir) => { + try { + fs.accessSync(`${dir}/vrf-oracle`, fs.constants.X_OK); + return true; + } catch { + return false; + } + }); + + if (!absolutePath) { + console.error( + `Could not find globally installed vrf-oracle, please install with cargo.`, + ); + process.exit(1); + } + + const absoluteBinaryPath = `${absolutePath}/vrf-oracle`; + const [error, binaryVersion] = getBinaryVersion(absoluteBinaryPath); + + if (error !== null) { + console.error(`Failed to get version of global binary: ${error}`); + return; + } + if (binaryVersion !== PACKAGE_VERSION) { + console.error( + `Globally installed vrf-oracle version is not correct. Expected "${PACKAGE_VERSION}", found "${binaryVersion}".`, + ); + return; + } + + runVrfOracle(absoluteBinaryPath); +} + +// Try to run vrf-oracle from package first, then fall back to system installation. +tryPackageVrfOracle() || trySystemVrfOracle(); diff --git a/.github/version-align.sh b/.github/version-align.sh index fa3ca8e75..30ebaa83c 100755 --- a/.github/version-align.sh +++ b/.github/version-align.sh @@ -19,18 +19,38 @@ case "$(uname)" in Darwin*) sedi=(-i '') esac -# Update the version in crates/bolt-cli/npm-package/package.json.tmpl +# Update the main package version in packages/npm-package/package.json.tmpl jq --arg version "$version" '.version = $version' packages/npm-package/package.json.tmpl > temp.json && mv temp.json packages/npm-package/package.json.tmpl -# Update the main package version and all optionalDependencies versions in crates/bolt-cli/npm-package/package.json -jq --arg version "$version" '(.version = $version) | (.optionalDependencies[] = $version)' packages/npm-package/package.json > temp.json && mv temp.json packages/npm-package/package.json +# Update the main package version and only ephemeral-validator optionalDependencies versions in packages/npm-package/package.json +jq --arg version "$version" '(.version = $version) | (.optionalDependencies |= with_entries(if (.key | contains("ephemeral-validator")) then .value = $version else . end))' packages/npm-package/package.json > temp.json && mv temp.json packages/npm-package/package.json + +# Check and update vrf-oracle and rpc-router to latest NPM versions +echo "Checking latest NPM versions for vrf-oracle and rpc-router..." + +# Get latest versions from NPM +vrf_latest=$(npm view @magicblock-labs/vrf-oracle-linux-x64 version 2>/dev/null || echo "") +router_latest=$(npm view @magicblock-labs/rpc-router-linux-x64 version 2>/dev/null || echo "") + +if [ -n "$vrf_latest" ]; then + echo "Updating vrf-oracle dependencies to version: $vrf_latest" + jq --arg version "$vrf_latest" '.optionalDependencies |= with_entries(if (.key | contains("vrf-oracle")) then .value = $version else . end)' packages/npm-package/package.json > temp.json && mv temp.json packages/npm-package/package.json +else + echo "Warning: Could not fetch latest vrf-oracle version from NPM" +fi + +if [ -n "$router_latest" ]; then + echo "Updating rpc-router dependencies to version: $router_latest" + jq --arg version "$router_latest" '.optionalDependencies |= with_entries(if (.key | contains("rpc-router")) then .value = $version else . end)' packages/npm-package/package.json > temp.json && mv temp.json packages/npm-package/package.json +else + echo "Warning: Could not fetch latest rpc-router version from NPM" +fi # Check if the any changes have been made to the specified files, if running with --check if [[ "$1" == "--check" ]]; then files_to_check=( - "clients/typescript/package.json" "packages/npm-package/package.json.tmpl" - "packages/package.json" + "packages/npm-package/package.json" ) for file in "${files_to_check[@]}"; do