-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
15 changed files
with
403 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import { parse, Range, SemVer } from "semver"; | ||
import { | ||
DockerFallbackOptions, | ||
execaDockerFallback, | ||
ExecaOptionsDockerFallback, | ||
} from "./util.js"; | ||
|
||
export const requiredVersion = new Range("^0.18.1"); | ||
|
||
export const boot = async ( | ||
args: readonly string[], | ||
options: ExecaOptionsDockerFallback, | ||
) => { | ||
return execaDockerFallback("cartesi-machine", args, options); | ||
}; | ||
|
||
export const version = async ( | ||
options?: DockerFallbackOptions, | ||
): Promise<SemVer | null> => { | ||
const { image } = options || {}; | ||
try { | ||
const { stdout } = await execaDockerFallback( | ||
"cartesi-machine", | ||
["--version-json"], | ||
{ image }, | ||
); | ||
if (typeof stdout === "string") { | ||
const output = JSON.parse(stdout); | ||
return parse(output.version); | ||
} | ||
return null; | ||
} catch (e: unknown) { | ||
return null; | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { parse, Range, SemVer } from "semver"; | ||
import { Stream } from "stream"; | ||
import { DockerFallbackOptions, spawnSyncDockerFallback } from "./util.js"; | ||
|
||
export const requiredVersion = new Range("^0.19.1"); | ||
|
||
export const exportImage = async ( | ||
options: { | ||
stdin: Stream; | ||
stdout: Stream; | ||
} & DockerFallbackOptions, | ||
) => { | ||
const { image, stdin, stdout } = options; | ||
return spawnSyncDockerFallback("crane", ["export", "-", "-"], { | ||
image, | ||
stdio: [stdin, stdout, "inherit"], | ||
}); | ||
}; | ||
|
||
export const version = async ( | ||
options?: DockerFallbackOptions, | ||
): Promise<SemVer | null> => { | ||
const result = spawnSyncDockerFallback("crane", ["version"], options || {}); | ||
if (result.error) { | ||
return null; | ||
} | ||
return parse(result.stdout.toString()); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import { parse, Range, SemVer } from "semver"; | ||
import { DockerFallbackOptions, execaDockerFallback } from "./util.js"; | ||
|
||
const BLOCK_SIZE = 4096; // fixed at 4k | ||
|
||
export const requiredVersion: Range = new Range("^1.5.6"); | ||
|
||
const baseArgs = (options: { extraBlocks: number }) => [ | ||
"--block-size", | ||
BLOCK_SIZE.toString(), | ||
"--faketime", | ||
"--readjustment", | ||
`+${options.extraBlocks}`, | ||
]; | ||
|
||
export const fromDirectory = async ( | ||
options: { | ||
cwd?: string; | ||
extraSize: number; | ||
input: string; | ||
output: string; | ||
} & DockerFallbackOptions, | ||
) => { | ||
const { cwd, extraSize, image, input, output } = options; | ||
const extraBlocks = Math.ceil(extraSize / BLOCK_SIZE); | ||
return execaDockerFallback( | ||
"xgenext2fs", | ||
[...baseArgs({ extraBlocks }), "--root", input, output], | ||
{ cwd, image }, | ||
); | ||
}; | ||
|
||
export const fromTar = async ( | ||
options: { | ||
cwd?: string; | ||
extraSize: number; | ||
input: string; | ||
output: string; | ||
} & DockerFallbackOptions, | ||
) => { | ||
const { cwd, extraSize, image, input, output } = options; | ||
const extraBlocks = Math.ceil(extraSize / BLOCK_SIZE); | ||
return execaDockerFallback( | ||
"xgenext2fs", | ||
[...baseArgs({ extraBlocks }), "--tarball", input, output], | ||
{ cwd, image }, | ||
); | ||
}; | ||
|
||
export const version = async ( | ||
options?: DockerFallbackOptions, | ||
): Promise<SemVer | null> => { | ||
const { stdout } = await execaDockerFallback( | ||
"xgenext2fs", | ||
["--version"], | ||
options || {}, | ||
); | ||
if (typeof stdout === "string") { | ||
const regex = | ||
/(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?/; | ||
const m = stdout.match(regex); | ||
if (m && m[0]) { | ||
return parse(m[0]); | ||
} | ||
} | ||
return null; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export * as cartesiMachine from "./cartesi-machine.js"; | ||
export * as crane from "./crane.js"; | ||
export * as genext2fs from "./genext2fs.js"; | ||
export * as mksquashfs from "./mksquashfs.js"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
import { parse, Range, SemVer } from "semver"; | ||
import { DockerFallbackOptions, execaDockerFallback } from "./util.js"; | ||
|
||
const COMPRESSION = "lzo"; // make customizable? default is gzip | ||
|
||
export const requiredVersion: Range = new Range("^4.5.1"); | ||
|
||
const baseArgs = () => [ | ||
"--all-time", | ||
"0", | ||
"-all-root", // XXX: should we use this? | ||
"-noappend", | ||
"-comp", | ||
COMPRESSION, | ||
"-no-progress", | ||
]; | ||
|
||
export const fromDirectory = ( | ||
options: { | ||
cwd?: string; | ||
input: string; | ||
output: string; | ||
} & DockerFallbackOptions, | ||
) => { | ||
const { cwd, image, input, output } = options; | ||
return execaDockerFallback("mksquashfs", [input, output, ...baseArgs()], { | ||
cwd, | ||
image, | ||
}); | ||
}; | ||
|
||
export const fromTar = ( | ||
options: { | ||
cwd?: string; | ||
input: string; | ||
output: string; | ||
} & DockerFallbackOptions, | ||
) => { | ||
const { cwd, image, input, output } = options; | ||
return execaDockerFallback( | ||
"mksquashfs", | ||
["-", output, "-tar", ...baseArgs()], | ||
{ | ||
cwd, | ||
image, | ||
inputFile: input, // use stdin in case of tar file | ||
}, | ||
); | ||
}; | ||
|
||
export const version = async ( | ||
options?: DockerFallbackOptions, | ||
): Promise<SemVer | null> => { | ||
try { | ||
const { stdout } = await execaDockerFallback( | ||
"mksquashfs", | ||
["-version"], | ||
options || {}, | ||
); | ||
if (typeof stdout === "string") { | ||
const regex = | ||
/(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?/gm; | ||
const m = stdout.match(regex); | ||
if (m && m[0]) { | ||
return parse(m[0]); | ||
} | ||
} | ||
} catch (e: unknown) { | ||
console.error(e); | ||
return null; | ||
} | ||
return null; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
import { spawnSync, SpawnSyncOptions } from "child_process"; | ||
import { execa, ExecaError, Options } from "execa"; | ||
import os from "os"; | ||
|
||
export type DockerFallbackOptions = | ||
| { image: string; forceDocker: true } | ||
| { image?: string; forceDocker?: false }; | ||
|
||
/** | ||
* Calls execa and falls back to docker run if command (on the host) fails | ||
* @param command command to be executed | ||
* @param args arguments to be passed to the command | ||
* @param options execution options | ||
* @returns return of execa | ||
*/ | ||
export type ExecaOptionsDockerFallback = Options & DockerFallbackOptions; | ||
export const execaDockerFallback = async ( | ||
command: string, | ||
args: readonly string[], | ||
options: ExecaOptionsDockerFallback, | ||
) => { | ||
try { | ||
if (options.forceDocker) { | ||
const error = new ExecaError(); | ||
error.code = "ENOENT"; | ||
throw error; | ||
} | ||
return await execa(command, args, options); | ||
} catch (error) { | ||
if (error instanceof ExecaError) { | ||
if (error.code === "ENOENT" && options.image) { | ||
if (!options.forceDocker) { | ||
console.warn( | ||
`error executing '${command}', falling back to docker execution using image '${options.image}'`, | ||
); | ||
} | ||
const userInfo = os.userInfo(); | ||
const dockerOpts = [ | ||
"--volume", | ||
`${options.cwd}:/work`, | ||
"--workdir", | ||
"/work", | ||
"--interactive", | ||
"--user", | ||
`${userInfo.uid}:${userInfo.gid}`, | ||
]; | ||
return await execa( | ||
"docker", | ||
["run", ...dockerOpts, options.image, command, ...args], | ||
options, | ||
); | ||
} else { | ||
console.error(`error executing '${command}'`, error); | ||
} | ||
} | ||
throw error; | ||
} | ||
}; | ||
|
||
/** | ||
* Calls spawnSync and falls back to docker run if command (on the host) fails | ||
* @param command command to be executed | ||
* @param args arguments to be passed to the command | ||
* @param options execution options | ||
* @returns return of execa | ||
*/ | ||
export type SpawnOptionsDockerFallback = SpawnSyncOptions & | ||
DockerFallbackOptions; | ||
export const spawnSyncDockerFallback = ( | ||
command: string, | ||
args: readonly string[], | ||
options: SpawnOptionsDockerFallback, | ||
) => { | ||
const result = options.forceDocker | ||
? { error: { code: "ENOENT" }, stdout: "" } | ||
: spawnSync(command, args, options); | ||
if (result.error) { | ||
const code = (result.error as any).code; | ||
if (code === "ENOENT" && options.image) { | ||
if (!options.forceDocker) { | ||
console.warn( | ||
`error executing '${command}', falling back to docker execution using image '${options.image}'`, | ||
); | ||
} | ||
const userInfo = os.userInfo(); | ||
const dockerOpts = [ | ||
"--volume", | ||
`${options.cwd}:/work`, | ||
"--workdir", | ||
"/work", | ||
"--interactive", | ||
"--user", | ||
`${userInfo.uid}:${userInfo.gid}`, | ||
]; | ||
const dockerArgs = [ | ||
"run", | ||
...dockerOpts, | ||
options.image, | ||
command, | ||
...args, | ||
]; | ||
const dockerResult = spawnSync("docker", dockerArgs, options); | ||
if (dockerResult.error) { | ||
console.error( | ||
`error executing '${command}'`, | ||
dockerResult.error, | ||
); | ||
throw dockerResult.error; | ||
} | ||
return dockerResult; | ||
} else { | ||
console.error(`error executing '${command}'`, result.error); | ||
throw result.error; | ||
} | ||
} | ||
return result; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { satisfies } from "semver"; | ||
import { describe, expect, it } from "vitest"; | ||
import { cartesiMachine } from "../../src/exec/index.js"; | ||
|
||
describe("cartesi-machine", () => { | ||
it("should report version", async () => { | ||
const version = await cartesiMachine.version({ | ||
forceDocker: true, | ||
image: "cartesi/sdk:0.11.0", | ||
}); | ||
expect(version).toBeDefined(); | ||
expect( | ||
satisfies(version!.format(), cartesiMachine.requiredVersion), | ||
).toBeTruthy(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { satisfies } from "semver"; | ||
import { describe, expect, it } from "vitest"; | ||
import { crane } from "../../src/exec/index.js"; | ||
|
||
describe("crane", () => { | ||
it("should report version", async () => { | ||
const version = await crane.version({ | ||
forceDocker: true, | ||
image: "cartesi/sdk:0.11.0", | ||
}); | ||
expect(version).toBeDefined(); | ||
expect( | ||
satisfies(version!.format(), crane.requiredVersion), | ||
).toBeTruthy(); | ||
}); | ||
}); |
Oops, something went wrong.