-
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.
feat(cli): build based on configuration
- Loading branch information
Showing
41 changed files
with
1,753 additions
and
330 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@cartesi/cli": major | ||
--- | ||
|
||
build based on cartesi.toml |
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 |
---|---|---|
|
@@ -9,3 +9,4 @@ node_modules | |
oclif.manifest.json | ||
src/contracts.ts | ||
src/graphql/ | ||
test/builder/output |
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,45 @@ | ||
import fs from "fs-extra"; | ||
import path from "path"; | ||
import { DirectoryDriveConfig } from "../config.js"; | ||
import { genext2fs, mksquashfs } from "../exec/index.js"; | ||
|
||
export const build = async ( | ||
name: string, | ||
drive: DirectoryDriveConfig, | ||
sdkImage: string, | ||
destination: string, | ||
): Promise<void> => { | ||
const filename = `${name}.${drive.format}`; | ||
|
||
// copy directory to destination | ||
const dest = path.join(destination, name); | ||
await fs.mkdirp(dest); | ||
await fs.copy(drive.directory, dest); | ||
|
||
try { | ||
switch (drive.format) { | ||
case "ext2": { | ||
await genext2fs.fromDirectory({ | ||
extraSize: drive.extraSize, | ||
input: name, | ||
output: filename, | ||
cwd: destination, | ||
image: sdkImage, | ||
}); | ||
break; | ||
} | ||
case "sqfs": { | ||
await mksquashfs.fromDirectory({ | ||
input: name, | ||
output: filename, | ||
cwd: destination, | ||
image: sdkImage, | ||
}); | ||
break; | ||
} | ||
} | ||
} finally { | ||
// delete copied | ||
await fs.remove(dest); | ||
} | ||
}; |
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,138 @@ | ||
import { execa } from "execa"; | ||
import fs from "fs-extra"; | ||
import path from "path"; | ||
import tmp from "tmp"; | ||
import { DockerDriveConfig } from "../config.js"; | ||
import { crane, genext2fs, mksquashfs } from "../exec/index.js"; | ||
|
||
type ImageBuildOptions = Pick< | ||
DockerDriveConfig, | ||
"context" | "dockerfile" | "tags" | "target" | ||
>; | ||
|
||
type ImageInfo = { | ||
cmd: string[]; | ||
entrypoint: string[]; | ||
env: string[]; | ||
workdir: string; | ||
}; | ||
|
||
/** | ||
* Build Docker image (linux/riscv64). Returns image id. | ||
*/ | ||
const buildImage = async (options: ImageBuildOptions): Promise<string> => { | ||
const { context, dockerfile, tags, target } = options; | ||
const buildResult = tmp.tmpNameSync(); | ||
const args = [ | ||
"buildx", | ||
"build", | ||
"--file", | ||
dockerfile, | ||
"--load", | ||
"--iidfile", | ||
buildResult, | ||
context, | ||
]; | ||
|
||
// set tags for the image built | ||
args.push(...tags.map((tag) => ["--tag", tag]).flat()); | ||
|
||
if (target) { | ||
args.push("--target", target); | ||
} | ||
|
||
await execa("docker", args, { stdio: "inherit" }); | ||
return fs.readFileSync(buildResult, "utf8"); | ||
}; | ||
|
||
/** | ||
* Query the image using docker image inspect | ||
* @param image image id or name | ||
* @returns Information about the image | ||
*/ | ||
const getImageInfo = async (image: string): Promise<ImageInfo> => { | ||
const { stdout: jsonStr } = await execa("docker", [ | ||
"image", | ||
"inspect", | ||
image, | ||
]); | ||
// parse image info from docker inspect output | ||
const [imageInfo] = JSON.parse(jsonStr); | ||
|
||
// validate image architecture (must be riscv64) | ||
if (imageInfo["Architecture"] !== "riscv64") { | ||
throw new Error( | ||
`Invalid image Architecture: ${imageInfo["Architecture"]}. Expected riscv64`, | ||
); | ||
} | ||
|
||
const info: ImageInfo = { | ||
cmd: imageInfo["Config"]["Cmd"] ?? [], | ||
entrypoint: imageInfo["Config"]["Entrypoint"] ?? [], | ||
env: imageInfo["Config"]["Env"] || [], | ||
workdir: imageInfo["Config"]["WorkingDir"], | ||
}; | ||
|
||
return info; | ||
}; | ||
|
||
export const build = async ( | ||
name: string, | ||
drive: DockerDriveConfig, | ||
sdkImage: string, | ||
destination: string, | ||
): Promise<ImageInfo | undefined> => { | ||
const { format } = drive; | ||
|
||
const ocitar = `${name}.oci.tar`; | ||
const tar = `${name}.tar`; | ||
const filename = `${name}.${format}`; | ||
|
||
// use pre-existing image or build docker image | ||
const image = drive.image || (await buildImage(drive)); | ||
|
||
// get image info | ||
const imageInfo = await getImageInfo(image); | ||
|
||
try { | ||
// create OCI Docker tarball from Docker image | ||
await execa("docker", ["image", "save", image, "-o", ocitar], { | ||
cwd: destination, | ||
}); | ||
|
||
// create rootfs tar from OCI tar | ||
await crane.exportImage({ | ||
stdin: fs.openSync(path.join(destination, ocitar), "r"), | ||
stdout: fs.openSync(path.join(destination, tar), "w"), | ||
image: sdkImage, | ||
}); | ||
|
||
switch (format) { | ||
case "ext2": { | ||
await genext2fs.fromTar({ | ||
extraSize: drive.extraSize, | ||
input: tar, | ||
output: filename, | ||
cwd: destination, | ||
image: sdkImage, | ||
}); | ||
break; | ||
} | ||
case "sqfs": { | ||
await mksquashfs.fromTar({ | ||
input: path.join(destination, tar), | ||
output: filename, | ||
cwd: destination, | ||
image: sdkImage, | ||
}); | ||
break; | ||
} | ||
} | ||
} finally { | ||
// delete intermediate files | ||
await fs.remove(path.join(destination, ocitar)); | ||
await fs.remove(path.join(destination, tar)); | ||
} | ||
|
||
return imageInfo; | ||
}; |
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,31 @@ | ||
import fs from "fs-extra"; | ||
import path from "path"; | ||
import { EmptyDriveConfig } from "../config.js"; | ||
import { genext2fs } from "../exec/index.js"; | ||
|
||
export const build = async ( | ||
name: string, | ||
drive: EmptyDriveConfig, | ||
sdkImage: string, | ||
destination: string, | ||
): Promise<void> => { | ||
const filename = `${name}.${drive.format}`; | ||
switch (drive.format) { | ||
case "ext2": { | ||
await genext2fs.empty({ | ||
output: filename, | ||
size: drive.size, | ||
cwd: destination, | ||
image: sdkImage, | ||
}); | ||
break; | ||
} | ||
case "raw": { | ||
await fs.writeFile( | ||
path.join(destination, filename), | ||
Buffer.alloc(drive.size), | ||
); | ||
break; | ||
} | ||
} | ||
}; |
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,5 @@ | ||
export { build as buildDirectory } from "./directory.js"; | ||
export { build as buildDocker } from "./docker.js"; | ||
export { build as buildEmpty } from "./empty.js"; | ||
export { build as buildNone } from "./none.js"; | ||
export { build as buildTar } from "./tar.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,17 @@ | ||
import fs from "fs-extra"; | ||
import path from "path"; | ||
import { ExistingDriveConfig, getDriveFormat } from "../config.js"; | ||
|
||
export const build = async ( | ||
name: string, | ||
drive: ExistingDriveConfig, | ||
destination: string, | ||
): Promise<void> => { | ||
// no need to build, drive already exists | ||
const src = drive.filename; | ||
const format = getDriveFormat(src); | ||
const filename = path.join(destination, `${name}.${format}`); | ||
|
||
// just copy it | ||
await fs.copyFile(src, filename); | ||
}; |
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,39 @@ | ||
import fs from "fs-extra"; | ||
import path from "path"; | ||
import { TarDriveConfig } from "../config.js"; | ||
import { genext2fs, mksquashfs } from "../exec/index.js"; | ||
|
||
export const build = async ( | ||
name: string, | ||
drive: TarDriveConfig, | ||
sdkImage: string, | ||
destination: string, | ||
): Promise<void> => { | ||
const tar = `${name}.tar`; | ||
const filename = `${name}.${drive.format}`; | ||
|
||
// copy input tar to destination directory (with drive name) | ||
await fs.copy(drive.filename, path.join(destination, tar)); | ||
|
||
switch (drive.format) { | ||
case "ext2": { | ||
await genext2fs.fromTar({ | ||
extraSize: drive.extraSize, | ||
input: tar, | ||
output: filename, | ||
cwd: destination, | ||
image: sdkImage, | ||
}); | ||
break; | ||
} | ||
case "sqfs": { | ||
await mksquashfs.fromTar({ | ||
input: path.join(destination, tar), | ||
output: filename, | ||
cwd: destination, | ||
image: sdkImage, | ||
}); | ||
break; | ||
} | ||
} | ||
}; |
Oops, something went wrong.