@cartesi/cli 1.4.0 → 2.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/dist/baseCommand.d.ts +2 -0
  2. package/dist/baseCommand.d.ts.map +1 -1
  3. package/dist/baseCommand.js +10 -5
  4. package/dist/builder/directory.d.ts +3 -0
  5. package/dist/builder/directory.d.ts.map +1 -0
  6. package/dist/builder/directory.js +37 -0
  7. package/dist/builder/docker.d.ts +10 -0
  8. package/dist/builder/docker.d.ts.map +1 -0
  9. package/dist/builder/docker.js +103 -0
  10. package/dist/builder/empty.d.ts +3 -0
  11. package/dist/builder/empty.d.ts.map +1 -0
  12. package/dist/builder/empty.js +21 -0
  13. package/dist/builder/index.d.ts +6 -0
  14. package/dist/builder/index.d.ts.map +1 -0
  15. package/dist/builder/index.js +5 -0
  16. package/dist/builder/none.d.ts +3 -0
  17. package/dist/builder/none.d.ts.map +1 -0
  18. package/dist/builder/none.js +11 -0
  19. package/dist/builder/tar.d.ts +3 -0
  20. package/dist/builder/tar.d.ts.map +1 -0
  21. package/dist/builder/tar.js +30 -0
  22. package/dist/commands/build.d.ts +3 -15
  23. package/dist/commands/build.d.ts.map +1 -1
  24. package/dist/commands/build.js +53 -194
  25. package/dist/commands/shell.d.ts +2 -1
  26. package/dist/commands/shell.d.ts.map +1 -1
  27. package/dist/commands/shell.js +41 -41
  28. package/dist/config.d.ts +102 -0
  29. package/dist/config.d.ts.map +1 -0
  30. package/dist/config.js +378 -0
  31. package/dist/contracts.d.ts +492 -1038
  32. package/dist/contracts.d.ts.map +1 -1
  33. package/dist/contracts.js +223 -498
  34. package/dist/exec/cartesi-machine.d.ts +9 -0
  35. package/dist/exec/cartesi-machine.d.ts.map +1 -0
  36. package/dist/exec/cartesi-machine.js +20 -0
  37. package/dist/exec/crane.d.ts +15 -0
  38. package/dist/exec/crane.d.ts.map +1 -0
  39. package/dist/exec/crane.js +17 -0
  40. package/dist/exec/genext2fs.d.ts +28 -0
  41. package/dist/exec/genext2fs.d.ts.map +1 -0
  42. package/dist/exec/genext2fs.js +44 -0
  43. package/dist/exec/index.d.ts +5 -0
  44. package/dist/exec/index.d.ts.map +1 -0
  45. package/dist/exec/index.js +4 -0
  46. package/dist/exec/mksquashfs.d.ts +21 -0
  47. package/dist/exec/mksquashfs.d.ts.map +1 -0
  48. package/dist/exec/mksquashfs.js +45 -0
  49. package/dist/exec/util.d.ts +36 -0
  50. package/dist/exec/util.d.ts.map +1 -0
  51. package/dist/exec/util.js +78 -0
  52. package/dist/machine.d.ts +6 -0
  53. package/dist/machine.d.ts.map +1 -0
  54. package/dist/machine.js +80 -0
  55. package/dist/node/docker-compose-anvil.yaml +4 -3
  56. package/dist/node/docker-compose-bundler.yaml +1 -1
  57. package/dist/node/docker-compose-paymaster.yaml +1 -1
  58. package/oclif.manifest.json +32 -95
  59. package/package.json +5 -5
  60. package/dist/commands/send/dapp-address.d.ts +0 -9
  61. package/dist/commands/send/dapp-address.d.ts.map +0 -1
  62. package/dist/commands/send/dapp-address.js +0 -20
@@ -1,5 +1,6 @@
1
1
  import { Command, Interfaces } from "@oclif/core";
2
2
  import { Address, Hash } from "viem";
3
+ import { Config } from "./config.js";
3
4
  export type Flags<T extends typeof Command> = Interfaces.InferredFlags<(typeof BaseCommand)["baseFlags"] & T["flags"]>;
4
5
  export type Args<T extends typeof Command> = Interfaces.InferredArgs<T["args"]>;
5
6
  export type AddressBook = Record<string, Address>;
@@ -8,6 +9,7 @@ export declare abstract class BaseCommand<T extends typeof Command> extends Comm
8
9
  protected args: Args<T>;
9
10
  protected getServiceState(projectName: string, serviceName: string): Promise<string | undefined>;
10
11
  protected getContextPath(...paths: string[]): string;
12
+ protected getApplicationConfig(configPath: string): Config;
11
13
  protected getMachineHash(): Hash | undefined;
12
14
  protected logPrompt({ title, value }: {
13
15
  title: string;
@@ -1 +1 @@
1
- {"version":3,"file":"baseCommand.d.ts","sourceRoot":"","sources":["../src/baseCommand.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAKlD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAsB,MAAM,MAAM,CAAC;AAmBzD,MAAM,MAAM,KAAK,CAAC,CAAC,SAAS,OAAO,OAAO,IAAI,UAAU,CAAC,aAAa,CAClE,CAAC,OAAO,WAAW,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CACjD,CAAC;AACF,MAAM,MAAM,IAAI,CAAC,CAAC,SAAS,OAAO,OAAO,IAAI,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;AAChF,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAElD,8BAAsB,WAAW,CAAC,CAAC,SAAS,OAAO,OAAO,CAAE,SAAQ,OAAO;IACvE,SAAS,CAAC,KAAK,EAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAC3B,SAAS,CAAC,IAAI,EAAG,IAAI,CAAC,CAAC,CAAC,CAAC;cAET,eAAe,CAC3B,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,GACpB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAe9B,SAAS,CAAC,cAAc,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM;IAIpD,SAAS,CAAC,cAAc,IAAI,IAAI,GAAG,SAAS;IAY5C,SAAS,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE;cAItD,qBAAqB,IAAI,OAAO,CAAC,OAAO,CAAC;cAKzC,cAAc,IAAI,OAAO,CAAC,WAAW,CAAC;IAkCzC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAYrC"}
1
+ {"version":3,"file":"baseCommand.d.ts","sourceRoot":"","sources":["../src/baseCommand.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAKlD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAsB,MAAM,MAAM,CAAC;AAEzD,OAAO,EAAE,MAAM,EAAS,MAAM,aAAa,CAAC;AAiB5C,MAAM,MAAM,KAAK,CAAC,CAAC,SAAS,OAAO,OAAO,IAAI,UAAU,CAAC,aAAa,CAClE,CAAC,OAAO,WAAW,CAAC,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,CACjD,CAAC;AACF,MAAM,MAAM,IAAI,CAAC,CAAC,SAAS,OAAO,OAAO,IAAI,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;AAChF,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAElD,8BAAsB,WAAW,CAAC,CAAC,SAAS,OAAO,OAAO,CAAE,SAAQ,OAAO;IACvE,SAAS,CAAC,KAAK,EAAG,KAAK,CAAC,CAAC,CAAC,CAAC;IAC3B,SAAS,CAAC,IAAI,EAAG,IAAI,CAAC,CAAC,CAAC,CAAC;cAET,eAAe,CAC3B,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,GACpB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAe9B,SAAS,CAAC,cAAc,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM;IAIpD,SAAS,CAAC,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM;IAM1D,SAAS,CAAC,cAAc,IAAI,IAAI,GAAG,SAAS;IAY5C,SAAS,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE;cAItD,qBAAqB,IAAI,OAAO,CAAC,OAAO,CAAC;cAKzC,cAAc,IAAI,OAAO,CAAC,WAAW,CAAC;IAiCzC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAYrC"}
@@ -4,7 +4,8 @@ import { execa } from "execa";
4
4
  import fs from "fs";
5
5
  import path from "path";
6
6
  import { getAddress, isHash } from "viem";
7
- import { authorityHistoryPairFactoryAddress, cartesiDAppFactoryAddress, dAppAddressRelayAddress, erc1155BatchPortalAddress, erc1155SinglePortalAddress, erc20PortalAddress, erc721PortalAddress, etherPortalAddress, inputBoxAddress, selfHostedApplicationFactoryAddress, testMultiTokenAddress, testNftAddress, testTokenAddress, } from "./contracts.js";
7
+ import { parse } from "./config.js";
8
+ import { applicationFactoryAddress, authorityFactoryAddress, erc1155BatchPortalAddress, erc1155SinglePortalAddress, erc20PortalAddress, erc721PortalAddress, etherPortalAddress, inputBoxAddress, selfHostedApplicationFactoryAddress, testMultiTokenAddress, testNftAddress, testTokenAddress, } from "./contracts.js";
8
9
  export class BaseCommand extends Command {
9
10
  async getServiceState(projectName, serviceName) {
10
11
  // get service information
@@ -23,6 +24,11 @@ export class BaseCommand extends Command {
23
24
  getContextPath(...paths) {
24
25
  return path.join(".cartesi", ...paths);
25
26
  }
27
+ getApplicationConfig(configPath) {
28
+ return fs.existsSync(configPath)
29
+ ? parse(fs.readFileSync(configPath).toString())
30
+ : parse("");
31
+ }
26
32
  getMachineHash() {
27
33
  // read hash of the cartesi machine snapshot, if one exists
28
34
  const hashPath = this.getContextPath("image", "hash");
@@ -45,10 +51,9 @@ export class BaseCommand extends Command {
45
51
  const applicationAddress = await this.getApplicationAddress();
46
52
  // build rollups contracts address book
47
53
  const contracts = {
48
- AuthorityHistoryPairFactory: authorityHistoryPairFactoryAddress,
49
- CartesiDApp: applicationAddress,
50
- CartesiDAppFactory: cartesiDAppFactoryAddress,
51
- DAppAddressRelay: dAppAddressRelayAddress,
54
+ Application: applicationAddress,
55
+ ApplicationFactory: applicationFactoryAddress,
56
+ AuthorityFactory: authorityFactoryAddress,
52
57
  EntryPointV06: "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789",
53
58
  EntryPointV07: "0x0000000071727De22E5E9d8BAf0edAc6f37da032",
54
59
  ERC1155BatchPortal: erc1155BatchPortalAddress,
@@ -0,0 +1,3 @@
1
+ import { DirectoryDriveConfig } from "../config.js";
2
+ export declare const build: (name: string, drive: DirectoryDriveConfig, sdkImage: string, destination: string) => Promise<void>;
3
+ //# sourceMappingURL=directory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"directory.d.ts","sourceRoot":"","sources":["../../src/builder/directory.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAGpD,eAAO,MAAM,KAAK,SACR,MAAM,SACL,oBAAoB,YACjB,MAAM,eACH,MAAM,KACpB,OAAO,CAAC,IAAI,CAkCd,CAAC"}
@@ -0,0 +1,37 @@
1
+ import fs from "fs-extra";
2
+ import path from "path";
3
+ import { genext2fs, mksquashfs } from "../exec/index.js";
4
+ export const build = async (name, drive, sdkImage, destination) => {
5
+ const filename = `${name}.${drive.format}`;
6
+ // copy directory to destination
7
+ const dest = path.join(destination, name);
8
+ await fs.mkdirp(dest);
9
+ await fs.copy(drive.directory, dest);
10
+ try {
11
+ switch (drive.format) {
12
+ case "ext2": {
13
+ await genext2fs.fromDirectory({
14
+ extraSize: drive.extraSize,
15
+ input: name,
16
+ output: filename,
17
+ cwd: destination,
18
+ image: sdkImage,
19
+ });
20
+ break;
21
+ }
22
+ case "sqfs": {
23
+ await mksquashfs.fromDirectory({
24
+ input: name,
25
+ output: filename,
26
+ cwd: destination,
27
+ image: sdkImage,
28
+ });
29
+ break;
30
+ }
31
+ }
32
+ }
33
+ finally {
34
+ // delete copied
35
+ await fs.remove(dest);
36
+ }
37
+ };
@@ -0,0 +1,10 @@
1
+ import { DockerDriveConfig } from "../config.js";
2
+ type ImageInfo = {
3
+ cmd: string[];
4
+ entrypoint: string[];
5
+ env: string[];
6
+ workdir: string;
7
+ };
8
+ export declare const build: (name: string, drive: DockerDriveConfig, sdkImage: string, destination: string) => Promise<ImageInfo | undefined>;
9
+ export {};
10
+ //# sourceMappingURL=docker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docker.d.ts","sourceRoot":"","sources":["../../src/builder/docker.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAQjD,KAAK,SAAS,GAAG;IACb,GAAG,EAAE,MAAM,EAAE,CAAC;IACd,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,GAAG,EAAE,MAAM,EAAE,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACnB,CAAC;AA6DF,eAAO,MAAM,KAAK,SACR,MAAM,SACL,iBAAiB,YACd,MAAM,eACH,MAAM,KACpB,OAAO,CAAC,SAAS,GAAG,SAAS,CAsD/B,CAAC"}
@@ -0,0 +1,103 @@
1
+ import { execa } from "execa";
2
+ import fs from "fs-extra";
3
+ import path from "path";
4
+ import tmp from "tmp";
5
+ import { crane, genext2fs, mksquashfs } from "../exec/index.js";
6
+ /**
7
+ * Build Docker image (linux/riscv64). Returns image id.
8
+ */
9
+ const buildImage = async (options) => {
10
+ const { context, dockerfile, tags, target } = options;
11
+ const buildResult = tmp.tmpNameSync();
12
+ const args = [
13
+ "buildx",
14
+ "build",
15
+ "--file",
16
+ dockerfile,
17
+ "--load",
18
+ "--iidfile",
19
+ buildResult,
20
+ context,
21
+ ];
22
+ // set tags for the image built
23
+ args.push(...tags.map((tag) => ["--tag", tag]).flat());
24
+ if (target) {
25
+ args.push("--target", target);
26
+ }
27
+ await execa("docker", args, { stdio: "inherit" });
28
+ return fs.readFileSync(buildResult, "utf8");
29
+ };
30
+ /**
31
+ * Query the image using docker image inspect
32
+ * @param image image id or name
33
+ * @returns Information about the image
34
+ */
35
+ const getImageInfo = async (image) => {
36
+ const { stdout: jsonStr } = await execa("docker", [
37
+ "image",
38
+ "inspect",
39
+ image,
40
+ ]);
41
+ // parse image info from docker inspect output
42
+ const [imageInfo] = JSON.parse(jsonStr);
43
+ // validate image architecture (must be riscv64)
44
+ if (imageInfo["Architecture"] !== "riscv64") {
45
+ throw new Error(`Invalid image Architecture: ${imageInfo["Architecture"]}. Expected riscv64`);
46
+ }
47
+ const info = {
48
+ cmd: imageInfo["Config"]["Cmd"] ?? [],
49
+ entrypoint: imageInfo["Config"]["Entrypoint"] ?? [],
50
+ env: imageInfo["Config"]["Env"] || [],
51
+ workdir: imageInfo["Config"]["WorkingDir"],
52
+ };
53
+ return info;
54
+ };
55
+ export const build = async (name, drive, sdkImage, destination) => {
56
+ const { format } = drive;
57
+ const ocitar = `${name}.oci.tar`;
58
+ const tar = `${name}.tar`;
59
+ const filename = `${name}.${format}`;
60
+ // use pre-existing image or build docker image
61
+ const image = drive.image || (await buildImage(drive));
62
+ // get image info
63
+ const imageInfo = await getImageInfo(image);
64
+ try {
65
+ // create OCI Docker tarball from Docker image
66
+ await execa("docker", ["image", "save", image, "-o", ocitar], {
67
+ cwd: destination,
68
+ });
69
+ // create rootfs tar from OCI tar
70
+ await crane.exportImage({
71
+ stdin: fs.openSync(path.join(destination, ocitar), "r"),
72
+ stdout: fs.openSync(path.join(destination, tar), "w"),
73
+ image: sdkImage,
74
+ });
75
+ switch (format) {
76
+ case "ext2": {
77
+ await genext2fs.fromTar({
78
+ extraSize: drive.extraSize,
79
+ input: tar,
80
+ output: filename,
81
+ cwd: destination,
82
+ image: sdkImage,
83
+ });
84
+ break;
85
+ }
86
+ case "sqfs": {
87
+ await mksquashfs.fromTar({
88
+ input: path.join(destination, tar),
89
+ output: filename,
90
+ cwd: destination,
91
+ image: sdkImage,
92
+ });
93
+ break;
94
+ }
95
+ }
96
+ }
97
+ finally {
98
+ // delete intermediate files
99
+ await fs.remove(path.join(destination, ocitar));
100
+ await fs.remove(path.join(destination, tar));
101
+ }
102
+ return imageInfo;
103
+ };
@@ -0,0 +1,3 @@
1
+ import { EmptyDriveConfig } from "../config.js";
2
+ export declare const build: (name: string, drive: EmptyDriveConfig, sdkImage: string, destination: string) => Promise<void>;
3
+ //# sourceMappingURL=empty.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"empty.d.ts","sourceRoot":"","sources":["../../src/builder/empty.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGhD,eAAO,MAAM,KAAK,SACR,MAAM,SACL,gBAAgB,YACb,MAAM,eACH,MAAM,KACpB,OAAO,CAAC,IAAI,CAoBd,CAAC"}
@@ -0,0 +1,21 @@
1
+ import fs from "fs-extra";
2
+ import path from "path";
3
+ import { genext2fs } from "../exec/index.js";
4
+ export const build = async (name, drive, sdkImage, destination) => {
5
+ const filename = `${name}.${drive.format}`;
6
+ switch (drive.format) {
7
+ case "ext2": {
8
+ await genext2fs.empty({
9
+ output: filename,
10
+ size: drive.size,
11
+ cwd: destination,
12
+ image: sdkImage,
13
+ });
14
+ break;
15
+ }
16
+ case "raw": {
17
+ await fs.writeFile(path.join(destination, filename), Buffer.alloc(drive.size));
18
+ break;
19
+ }
20
+ }
21
+ };
@@ -0,0 +1,6 @@
1
+ export { build as buildDirectory } from "./directory.js";
2
+ export { build as buildDocker } from "./docker.js";
3
+ export { build as buildEmpty } from "./empty.js";
4
+ export { build as buildNone } from "./none.js";
5
+ export { build as buildTar } from "./tar.js";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/builder/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,IAAI,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACzD,OAAO,EAAE,KAAK,IAAI,WAAW,EAAE,MAAM,aAAa,CAAC;AACnD,OAAO,EAAE,KAAK,IAAI,UAAU,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,WAAW,CAAC;AAC/C,OAAO,EAAE,KAAK,IAAI,QAAQ,EAAE,MAAM,UAAU,CAAC"}
@@ -0,0 +1,5 @@
1
+ export { build as buildDirectory } from "./directory.js";
2
+ export { build as buildDocker } from "./docker.js";
3
+ export { build as buildEmpty } from "./empty.js";
4
+ export { build as buildNone } from "./none.js";
5
+ export { build as buildTar } from "./tar.js";
@@ -0,0 +1,3 @@
1
+ import { ExistingDriveConfig } from "../config.js";
2
+ export declare const build: (name: string, drive: ExistingDriveConfig, destination: string) => Promise<void>;
3
+ //# sourceMappingURL=none.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"none.d.ts","sourceRoot":"","sources":["../../src/builder/none.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,mBAAmB,EAAkB,MAAM,cAAc,CAAC;AAEnE,eAAO,MAAM,KAAK,SACR,MAAM,SACL,mBAAmB,eACb,MAAM,KACpB,OAAO,CAAC,IAAI,CAQd,CAAC"}
@@ -0,0 +1,11 @@
1
+ import fs from "fs-extra";
2
+ import path from "path";
3
+ import { getDriveFormat } from "../config.js";
4
+ export const build = async (name, drive, destination) => {
5
+ // no need to build, drive already exists
6
+ const src = drive.filename;
7
+ const format = getDriveFormat(src);
8
+ const filename = path.join(destination, `${name}.${format}`);
9
+ // just copy it
10
+ await fs.copyFile(src, filename);
11
+ };
@@ -0,0 +1,3 @@
1
+ import { TarDriveConfig } from "../config.js";
2
+ export declare const build: (name: string, drive: TarDriveConfig, sdkImage: string, destination: string) => Promise<void>;
3
+ //# sourceMappingURL=tar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tar.d.ts","sourceRoot":"","sources":["../../src/builder/tar.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAG9C,eAAO,MAAM,KAAK,SACR,MAAM,SACL,cAAc,YACX,MAAM,eACH,MAAM,KACpB,OAAO,CAAC,IAAI,CA4Bd,CAAC"}
@@ -0,0 +1,30 @@
1
+ import fs from "fs-extra";
2
+ import path from "path";
3
+ import { genext2fs, mksquashfs } from "../exec/index.js";
4
+ export const build = async (name, drive, sdkImage, destination) => {
5
+ const tar = `${name}.tar`;
6
+ const filename = `${name}.${drive.format}`;
7
+ // copy input tar to destination directory (with drive name)
8
+ await fs.copy(drive.filename, path.join(destination, tar));
9
+ switch (drive.format) {
10
+ case "ext2": {
11
+ await genext2fs.fromTar({
12
+ extraSize: drive.extraSize,
13
+ input: tar,
14
+ output: filename,
15
+ cwd: destination,
16
+ image: sdkImage,
17
+ });
18
+ break;
19
+ }
20
+ case "sqfs": {
21
+ await mksquashfs.fromTar({
22
+ input: path.join(destination, tar),
23
+ output: filename,
24
+ cwd: destination,
25
+ image: sdkImage,
26
+ });
27
+ break;
28
+ }
29
+ }
30
+ };
@@ -1,24 +1,12 @@
1
1
  import { BaseCommand } from "../baseCommand.js";
2
- export default class BuildApplication extends BaseCommand<typeof BuildApplication> {
2
+ export default class Build extends BaseCommand<typeof Build> {
3
3
  static summary: string;
4
4
  static description: string;
5
5
  static examples: string[];
6
- static args: {};
7
6
  static flags: {
8
- "from-image": import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
9
- target: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
7
+ config: import("@oclif/core/lib/interfaces/parser.js").OptionFlag<string, import("@oclif/core/lib/interfaces/parser.js").CustomOptions>;
8
+ "drives-only": import("@oclif/core/lib/interfaces/parser.js").BooleanFlag<boolean>;
10
9
  };
11
- /**
12
- * Build DApp image (linux/riscv64). Returns image id.
13
- * @param directory path of context containing Dockerfile
14
- */
15
- private buildImage;
16
- private getImageInfo;
17
- private createTarball;
18
- private sdkRun;
19
- private static createRootfsTarCommand;
20
- private static createExt2Command;
21
- private static createMachineSnapshotCommand;
22
10
  run(): Promise<void>;
23
11
  }
24
12
  //# sourceMappingURL=build.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/commands/build.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AA2BhD,MAAM,CAAC,OAAO,OAAO,gBAAiB,SAAQ,WAAW,CACrD,OAAO,gBAAgB,CAC1B;IACG,MAAM,CAAC,OAAO,SAAwB;IAEtC,MAAM,CAAC,WAAW,SAC8V;IAEhX,MAAM,CAAC,QAAQ,WAGb;IAEF,MAAM,CAAC,IAAI,KAAM;IAEjB,MAAM,CAAC,KAAK;;;MAWV;IAEF;;;OAGG;YACW,UAAU;YAcV,YAAY;YAsEZ,aAAa;YAUb,MAAM;IA+BpB,OAAO,CAAC,MAAM,CAAC,sBAAsB;IAoBrC,OAAO,CAAC,MAAM,CAAC,iBAAiB;IAkBhC,OAAO,CAAC,MAAM,CAAC,4BAA4B;IAwB9B,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CA0DpC"}
1
+ {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/commands/build.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAoChD,MAAM,CAAC,OAAO,OAAO,KAAM,SAAQ,WAAW,CAAC,OAAO,KAAK,CAAC;IACxD,MAAM,CAAC,OAAO,SAAwB;IAEtC,MAAM,CAAC,WAAW,SAC+E;IAEjG,MAAM,CAAC,QAAQ,WAA2C;IAE1D,MAAM,CAAC,KAAK;;;MAUV;IAEW,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;CA2CpC"}
@@ -1,214 +1,73 @@
1
1
  import { Flags } from "@oclif/core";
2
- import bytes from "bytes";
3
- import { execa } from "execa";
4
2
  import fs from "fs-extra";
5
- import semver from "semver";
3
+ import path from "path";
6
4
  import tmp from "tmp";
7
5
  import { BaseCommand } from "../baseCommand.js";
8
- import { DEFAULT_TEMPLATES_BRANCH } from "./create.js";
9
- const CARTESI_LABEL_PREFIX = "io.cartesi.rollups";
10
- const CARTESI_LABEL_RAM_SIZE = `${CARTESI_LABEL_PREFIX}.ram_size`;
11
- const CARTESI_LABEL_DATA_SIZE = `${CARTESI_LABEL_PREFIX}.data_size`;
12
- const CARTESI_DEFAULT_RAM_SIZE = "128Mi";
13
- const CARTESI_LABEL_SDK_VERSION = `${CARTESI_LABEL_PREFIX}.sdk_version`;
14
- const CARTESI_LABEL_SDK_NAME = `${CARTESI_LABEL_PREFIX}.sdk_name`;
15
- const CARTESI_DEFAULT_SDK_VERSION = "0.9.0";
16
- class BuildApplication extends BaseCommand {
17
- /**
18
- * Build DApp image (linux/riscv64). Returns image id.
19
- * @param directory path of context containing Dockerfile
20
- */
21
- async buildImage(options) {
22
- const buildResult = tmp.tmpNameSync();
23
- this.debug(`building docker image and writing result to ${buildResult}`);
24
- const args = ["buildx", "build", "--load", "--iidfile", buildResult];
25
- if (options.target) {
26
- args.push("--target", options.target);
6
+ import { buildDirectory, buildDocker, buildEmpty, buildNone, buildTar, } from "../builder/index.js";
7
+ import { bootMachine } from "../machine.js";
8
+ const buildDrive = async (name, drive, sdkImage, destination) => {
9
+ switch (drive.builder) {
10
+ case "directory": {
11
+ return buildDirectory(name, drive, sdkImage, destination);
27
12
  }
28
- await execa("docker", [...args, process.cwd()], { stdio: "inherit" });
29
- return fs.readFileSync(buildResult, "utf8");
30
- }
31
- async getImageInfo(image) {
32
- const { stdout: jsonStr } = await execa("docker", [
33
- "image",
34
- "inspect",
35
- image,
36
- ]);
37
- // parse image info from docker inspect output
38
- const [imageInfo] = JSON.parse(jsonStr);
39
- // validate image architecture (must be riscv64)
40
- if (imageInfo["Architecture"] !== "riscv64") {
41
- throw new Error(`Invalid image Architecture: ${imageInfo["Architecture"]}. Expected riscv64`);
42
- }
43
- const labels = imageInfo["Config"]["Labels"] || {};
44
- const info = {
45
- cmd: imageInfo["Config"]["Cmd"] ?? [],
46
- dataSize: labels[CARTESI_LABEL_DATA_SIZE] ?? "10Mb",
47
- entrypoint: imageInfo["Config"]["Entrypoint"] ?? [],
48
- env: imageInfo["Config"]["Env"] || [],
49
- ramSize: labels[CARTESI_LABEL_RAM_SIZE] ?? CARTESI_DEFAULT_RAM_SIZE,
50
- sdkName: labels[CARTESI_LABEL_SDK_NAME] ?? "cartesi/sdk",
51
- sdkVersion: labels[CARTESI_LABEL_SDK_VERSION] ??
52
- CARTESI_DEFAULT_SDK_VERSION,
53
- workdir: imageInfo["Config"]["WorkingDir"],
54
- };
55
- if (!info.entrypoint && !info.cmd) {
56
- throw new Error("Undefined image ENTRYPOINT or CMD");
13
+ case "docker": {
14
+ return buildDocker(name, drive, sdkImage, destination);
57
15
  }
58
- // fail if using unsupported sdk version
59
- if (!semver.valid(info.sdkVersion)) {
60
- this.warn("sdk version is not a valid semver");
16
+ case "empty": {
17
+ return buildEmpty(name, drive, sdkImage, destination);
61
18
  }
62
- else if (info.sdkName == "cartesi/sdk" &&
63
- semver.lt(info.sdkVersion, CARTESI_DEFAULT_SDK_VERSION)) {
64
- throw new Error(`Unsupported sdk version: ${info.sdkVersion} (used) < ${CARTESI_DEFAULT_SDK_VERSION} (minimum).
65
- Update your application Dockerfile using one of the templates at https://github.com/cartesi/application-templates/tree/${DEFAULT_TEMPLATES_BRANCH}
66
- `);
19
+ case "tar": {
20
+ return buildTar(name, drive, sdkImage, destination);
67
21
  }
68
- // warn for using default values
69
- info.sdkVersion ||
70
- this.warn(`Undefined ${CARTESI_LABEL_SDK_VERSION} label, defaulting to ${CARTESI_DEFAULT_SDK_VERSION}`);
71
- info.ramSize ||
72
- this.warn(`Undefined ${CARTESI_LABEL_RAM_SIZE} label, defaulting to ${CARTESI_DEFAULT_RAM_SIZE}`);
73
- // validate data size value
74
- if (bytes(info.dataSize) === null) {
75
- throw new Error(`Invalid ${CARTESI_LABEL_DATA_SIZE} value: ${info.dataSize}`);
22
+ case "none": {
23
+ return buildNone(name, drive, destination);
76
24
  }
77
- // XXX: validate other values
78
- return info;
79
- }
80
- // saves the OCI Image to a tarball
81
- async createTarball(image, outputFilePath) {
82
- // create docker tarball from app image
83
- await execa("docker", ["image", "save", image, "-o", outputFilePath]);
84
- }
85
- // this wraps the call to the sdk image with a one-shot approach
86
- // the (inputPath, outputPath) signature will mount the input as a volume and copy the output with docker cp
87
- async sdkRun(sdkImage, cmd, inputPath, outputPath) {
88
- const { stdout: cid } = await execa("docker", [
89
- "container",
90
- "create",
91
- "--volume",
92
- `./${inputPath}:/tmp/input`,
93
- sdkImage,
94
- ...cmd,
95
- ]);
96
- await execa("docker", ["container", "start", "-a", cid], {
97
- stdio: "inherit",
98
- });
99
- await execa("docker", [
100
- "container",
101
- "cp",
102
- `${cid}:/tmp/output`,
103
- outputPath,
104
- ]);
105
- await execa("docker", ["container", "stop", cid]);
106
- await execa("docker", ["container", "rm", cid]);
107
- }
108
- // returns the command to create rootfs tarball from an OCI Image tarball
109
- static createRootfsTarCommand() {
110
- const cmd = [
111
- "cat",
112
- "/tmp/input",
113
- "|",
114
- "crane",
115
- "export",
116
- "-", // OCI Image from stdin
117
- "-", // rootfs tarball to stdout
118
- "|",
119
- "bsdtar",
120
- "-cf",
121
- "/tmp/output",
122
- "--format=gnutar",
123
- "@/dev/stdin", // rootfs tarball from stdin
124
- ];
125
- return ["/usr/bin/env", "bash", "-c", cmd.join(" ")];
126
- }
127
- // returns the command to create ext2 from a rootfs
128
- static createExt2Command(extraBytes) {
129
- const blockSize = 4096;
130
- const extraBlocks = Math.ceil(extraBytes / blockSize);
131
- const extraSize = `+${extraBlocks}`;
132
- return [
133
- "xgenext2fs",
134
- "--tarball",
135
- "/tmp/input",
136
- "--block-size",
137
- blockSize.toString(),
138
- "--faketime",
139
- "-r",
140
- extraSize,
141
- "/tmp/output",
142
- ];
143
- }
144
- static createMachineSnapshotCommand(info) {
145
- const ramSize = info.ramSize;
146
- const driveLabel = "root"; // XXX: does this need to be customizable?
147
- // list of environment variables of docker image
148
- const envs = info.env.map((variable) => `--env=${variable}`);
149
- // ENTRYPOINT and CMD as a space separated string
150
- const entrypoint = [...info.entrypoint, ...info.cmd].join(" ");
151
- // command to change working directory if WORKDIR is defined
152
- const cwd = info.workdir ? `--workdir=${info.workdir}` : "";
153
- return [
154
- "create_machine_snapshot",
155
- `--ram-length=${ramSize}`,
156
- `--drive-label=${driveLabel}`,
157
- `--drive-filename=/tmp/input`,
158
- `--output=/tmp/output`,
159
- cwd,
160
- ...envs,
161
- `--entrypoint=${entrypoint}`,
162
- ];
163
25
  }
26
+ };
27
+ class Build extends BaseCommand {
164
28
  async run() {
165
- const { flags } = await this.parse(BuildApplication);
166
- const snapshotPath = this.getContextPath("image");
167
- const tarPath = this.getContextPath("image.tar");
168
- const gnuTarPath = this.getContextPath("image.gnutar");
169
- const ext2Path = this.getContextPath("image.ext2");
29
+ const { flags } = await this.parse(Build);
170
30
  // clean up temp files we create along the process
171
31
  tmp.setGracefulCleanup();
172
- // use pre-existing image or build dapp image
173
- const appImage = flags["from-image"] || (await this.buildImage(flags));
32
+ // get application configuration from 'cartesi.toml'
33
+ const config = this.getApplicationConfig(flags.config);
34
+ // destination directory for image and intermediate files
35
+ const destination = path.resolve(this.getContextPath());
174
36
  // prepare context directory
175
- await fs.emptyDir(this.getContextPath()); // XXX: make it less error prone
176
- // get and validate image info
177
- const imageInfo = await this.getImageInfo(appImage);
178
- // resolve sdk version
179
- const sdkImage = `${imageInfo.sdkName}:${imageInfo.sdkVersion}`;
180
- try {
181
- // create docker tarball for image specified
182
- await this.createTarball(appImage, tarPath);
183
- // create rootfs tar
184
- await this.sdkRun(sdkImage, BuildApplication.createRootfsTarCommand(), tarPath, gnuTarPath);
185
- // create ext2
186
- await this.sdkRun(sdkImage, BuildApplication.createExt2Command(bytes.parse(imageInfo.dataSize)), gnuTarPath, ext2Path);
187
- // create machine snapshot
188
- await this.sdkRun(sdkImage, BuildApplication.createMachineSnapshotCommand(imageInfo), ext2Path, snapshotPath);
189
- await fs.chmod(snapshotPath, 0o755);
190
- }
191
- finally {
192
- await fs.remove(gnuTarPath);
193
- await fs.remove(tarPath);
37
+ await fs.emptyDir(destination); // XXX: make it less error prone
38
+ // start build of all drives simultaneously
39
+ const results = Object.entries(config.drives).reduce((acc, [name, drive]) => {
40
+ acc[name] = buildDrive(name, drive, config.sdk, destination);
41
+ return acc;
42
+ }, {});
43
+ // await for all drives to be built
44
+ await Promise.all(Object.values(results));
45
+ if (flags["drives-only"]) {
46
+ // only build drives, so quit here
47
+ return;
194
48
  }
49
+ // get image info of root drive
50
+ const root = await results["root"];
51
+ const imageInfo = root || undefined;
52
+ // path of machine snapshot
53
+ const snapshotPath = this.getContextPath("image");
54
+ // create machine snapshot
55
+ await bootMachine(config, imageInfo, destination);
56
+ await fs.chmod(snapshotPath, 0o755);
195
57
  }
196
58
  }
197
- BuildApplication.summary = "Build application.";
198
- BuildApplication.description = "Build application starting from a Dockerfile and ending with a snapshot of the corresponding Cartesi Machine already booted and yielded for the first time. This snapshot can be used to start a Cartesi node for the application using `run`. The process can also start from a Docker image built by the developer using `docker build` using the option `--from-image`";
199
- BuildApplication.examples = [
200
- "<%= config.bin %> <%= command.id %>",
201
- "<%= config.bin %> <%= command.id %> --from-image my-app",
202
- ];
203
- BuildApplication.args = {};
204
- BuildApplication.flags = {
205
- "from-image": Flags.string({
206
- summary: "skip docker build and start from this image.",
207
- description: "if the build process of the application Dockerfile needs more control the developer can build the image using the `docker build` command, and then start the build process of the Cartesi machine starting from that image.",
59
+ Build.summary = "Build application.";
60
+ Build.description = "Build application by building Cartesi machine drives, configuring a machine and booting it";
61
+ Build.examples = ["<%= config.bin %> <%= command.id %>"];
62
+ Build.flags = {
63
+ config: Flags.file({
64
+ char: "c",
65
+ default: "cartesi.toml",
66
+ summary: "path to the configuration file",
208
67
  }),
209
- target: Flags.string({
210
- summary: "target of docker multi-stage build.",
211
- description: "if the application Dockerfile uses a multi-stage strategy, and stage of the image to be exported as a Cartesi machine is not the last stage, use this parameter to specify the target stage.",
68
+ "drives-only": Flags.boolean({
69
+ default: false,
70
+ summary: "only build drives",
212
71
  }),
213
72
  };
214
- export default BuildApplication;
73
+ export default Build;