Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ Options:
-v, --validators <count> Number of validator nodes to generate. (default: 4)
-a, --allocations <file> Path to a genesis allocations JSON file. (default: none)
--abi-directory <path> Directory containing ABI JSON files to publish as ConfigMaps.
--subgraph-hash-file <path> Path to a file containing the subgraph IPFS hash.
-o, --outputType <type> Output target (screen, file, kubernetes). (default: "screen")
--static-node-port <number> P2P port used for static-nodes enode URIs. (default: 30303)
--static-node-discovery-port <number> Discovery port used for static-nodes enode URIs. (default: 30303)
Expand Down
3 changes: 3 additions & 0 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"@inquirer/prompts": "7.8.6",
"@kubernetes/client-node": "1.3.0",
"commander": "14.0.1",
"multiformats": "12.1.3",
"lefthook": "1.13.1",
"ox": "0.9.6",
"viem": "2.37.7",
Expand Down
64 changes: 64 additions & 0 deletions src/cli/commands/bootstrap/bootstrap.command.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const {
genesisConfigMapName: DEFAULT_GENESIS_CONFIGMAP_NAME,
staticNodesConfigMapName: DEFAULT_STATIC_NODES_CONFIGMAP_NAME,
faucetArtifactPrefix: DEFAULT_FAUCET_PREFIX,
subgraphConfigMapName: DEFAULT_SUBGRAPH_CONFIGMAP_NAME,
} = ARTIFACT_DEFAULTS;
const UNCOMPRESSED_PUBLIC_KEY_PREFIX = "04";
const UNCOMPRESSED_PUBLIC_KEY_LENGTH = 130;
Expand All @@ -35,6 +36,8 @@ const PUBLIC_KEY_REPEAT = 64;
const FIRST_VALIDATOR_INDEX = 1;
const SECOND_VALIDATOR_INDEX = 2;
const FAUCET_INDEX = VALIDATOR_RETURN + 1;
const SAMPLE_SUBGRAPH_HASH =
"bafybeigdyrztzd4gufq2bdsd6we3jh7uzulnd2ipkyli5sto6f5j6rlude";
const createFactoryStub = () => {
let counter = 0;
return {
Expand Down Expand Up @@ -204,6 +207,7 @@ describe("CLI command bootstrap", () => {
loadAbisPath = path;
return Promise.resolve([]);
},
loadSubgraphHash: () => Promise.resolve(SAMPLE_SUBGRAPH_HASH),
outputResult: async (type, payload) => {
outputInvocation = { type, payload };
await realOutputResult(type, payload);
Expand Down Expand Up @@ -242,6 +246,7 @@ describe("CLI command bootstrap", () => {
validatorPrefix: DEFAULT_POD_PREFIX,
genesisConfigMapName: DEFAULT_GENESIS_CONFIGMAP_NAME,
staticNodesConfigMapName: DEFAULT_STATIC_NODES_CONFIGMAP_NAME,
subgraphConfigMapName: DEFAULT_SUBGRAPH_CONFIGMAP_NAME,
});
});

Expand Down Expand Up @@ -278,6 +283,7 @@ describe("CLI command bootstrap", () => {
capturedAbiPath = path;
return Promise.resolve(abiArtifacts);
},
loadSubgraphHash: () => Promise.resolve(SAMPLE_SUBGRAPH_HASH),
outputResult: (_type, payload) => {
capturedPayload = payload;
return Promise.resolve();
Expand All @@ -296,6 +302,56 @@ describe("CLI command bootstrap", () => {
expect(capturedPayload?.abiArtifacts).toEqual(abiArtifacts);
});

test("runBootstrap loads subgraph hash from environment", async () => {
const factory = createFactoryStub();
let capturedPath: string | undefined;
let capturedPayload: OutputPayload | undefined;
const originalEnv = Bun.env.SUBGRAPH_HASH_FILE;

const deps: BootstrapDependencies = {
factory,
promptForCount: () => Promise.resolve(EXPECTED_DEFAULT_VALIDATOR),
promptForGenesis: async (_service, { faucetAddress }) => ({
algorithm: ALGORITHM.QBFT,
config: {
chainId: 1,
faucetWalletAddress: faucetAddress,
gasLimit: "0x1",
secondsPerBlock: 2,
},
genesis: { config: {}, extraData: "0x" } as any,
}),
promptForText: passthroughTextPrompt,
service: {} as any,
loadAllocations: () =>
Promise.resolve({} satisfies Record<string, BesuAllocAccount>),
loadAbis: () => Promise.resolve([]),
loadSubgraphHash: (path: string) => {
capturedPath = path;
return Promise.resolve(SAMPLE_SUBGRAPH_HASH);
},
outputResult: (_type, payload) => {
capturedPayload = payload;
return Promise.resolve();
},
};

Bun.env.SUBGRAPH_HASH_FILE = " /tmp/subgraph.txt ";

try {
await runBootstrap({ acceptDefaults: true }, deps);
} finally {
if (originalEnv === undefined) {
Bun.env.SUBGRAPH_HASH_FILE = undefined;
} else {
Bun.env.SUBGRAPH_HASH_FILE = originalEnv;
}
Comment on lines +344 to +348
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The finally block for restoring the SUBGRAPH_HASH_FILE environment variable can be simplified. The if/else statement is not necessary, as assigning originalEnv (which can be a string or undefined) directly to Bun.env.SUBGRAPH_HASH_FILE achieves the same result more concisely.

      Bun.env.SUBGRAPH_HASH_FILE = originalEnv;

}

expect(capturedPath).toBe("/tmp/subgraph.txt");
expect(capturedPayload?.subgraphHash).toBe(SAMPLE_SUBGRAPH_HASH);
});

test("createCliCommand wires metadata", () => {
const command = createCliCommand();
expect(command.name()).toBe("network-bootstrapper");
Expand Down Expand Up @@ -337,6 +393,7 @@ describe("CLI command bootstrap", () => {
loadAllocations: () =>
Promise.resolve({} satisfies Record<string, BesuAllocAccount>),
loadAbis: () => Promise.resolve([]),
loadSubgraphHash: () => Promise.resolve(SAMPLE_SUBGRAPH_HASH),
outputResult: async (type, payload) => {
await realOutputResult(type, payload);
},
Expand Down Expand Up @@ -405,6 +462,7 @@ describe("CLI command bootstrap", () => {
loadAllocations: () =>
Promise.resolve({} satisfies Record<string, BesuAllocAccount>),
loadAbis: () => Promise.resolve([]),
loadSubgraphHash: () => Promise.resolve(SAMPLE_SUBGRAPH_HASH),
outputResult: (_type, payload) => {
capturedPayload = payload;
return Promise.resolve();
Expand Down Expand Up @@ -457,6 +515,7 @@ describe("CLI command bootstrap", () => {
validatorPrefix: "custom-validator",
genesisConfigMapName: "custom-genesis",
staticNodesConfigMapName: "custom-static-nodes",
subgraphConfigMapName: DEFAULT_SUBGRAPH_CONFIGMAP_NAME,
});
});

Expand Down Expand Up @@ -487,6 +546,7 @@ describe("CLI command bootstrap", () => {
loadAllocations: () =>
Promise.resolve({} satisfies Record<string, BesuAllocAccount>),
loadAbis: () => Promise.resolve([]),
loadSubgraphHash: () => Promise.resolve(SAMPLE_SUBGRAPH_HASH),
outputResult: (_type, payload) => {
capturedPayload = payload;
return Promise.resolve();
Expand Down Expand Up @@ -556,6 +616,7 @@ describe("CLI command bootstrap", () => {
loadAllocations: () =>
Promise.resolve({} satisfies Record<string, BesuAllocAccount>),
loadAbis: () => Promise.resolve([]),
loadSubgraphHash: () => Promise.resolve(SAMPLE_SUBGRAPH_HASH),
outputResult: async () => {
// no-op for test
},
Expand Down Expand Up @@ -639,6 +700,7 @@ describe("CLI command bootstrap", () => {
loadAllocations: () =>
Promise.resolve({} satisfies Record<string, BesuAllocAccount>),
loadAbis: () => Promise.resolve([]),
loadSubgraphHash: () => Promise.resolve(SAMPLE_SUBGRAPH_HASH),
outputResult: (type) => {
capturedOutputType = type;
return Promise.resolve();
Expand Down Expand Up @@ -730,13 +792,15 @@ describe("CLI command bootstrap", () => {
loadAbisInvoked = true;
return Promise.resolve([]);
},
loadSubgraphHash: () => Promise.resolve(SAMPLE_SUBGRAPH_HASH),
outputResult: (_type, payload) => {
expect(payload.validators).toHaveLength(EXPECTED_DEFAULT_VALIDATOR);
expect(payload.artifactNames).toEqual({
faucetPrefix: DEFAULT_FAUCET_PREFIX,
validatorPrefix: DEFAULT_POD_PREFIX,
genesisConfigMapName: DEFAULT_GENESIS_CONFIGMAP_NAME,
staticNodesConfigMapName: DEFAULT_STATIC_NODES_CONFIGMAP_NAME,
subgraphConfigMapName: DEFAULT_SUBGRAPH_CONFIGMAP_NAME,
});
return Promise.resolve();
},
Expand Down
39 changes: 39 additions & 0 deletions src/cli/commands/bootstrap/bootstrap.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import {
promptForCount,
promptForText,
} from "./bootstrap.prompt-helpers.ts";
import { loadSubgraphHash } from "./bootstrap.subgraph.ts";

type CliOptions = {
allocations?: string;
Expand All @@ -48,6 +49,7 @@ type CliOptions = {
genesisConfigmapName?: string;
staticNodesConfigmapName?: string;
faucetArtifactPrefix?: string;
subgraphHashFile?: string;
};

type BootstrapDependencies = {
Expand All @@ -58,6 +60,7 @@ type BootstrapDependencies = {
service: BesuGenesisService;
loadAllocations: typeof loadAllocations;
loadAbis: typeof loadAbis;
loadSubgraphHash: typeof loadSubgraphHash;
outputResult: (type: OutputType, payload: OutputPayload) => Promise<void>;
};

Expand All @@ -69,6 +72,7 @@ const {
genesisConfigMapName: DEFAULT_GENESIS_CONFIGMAP_NAME,
staticNodesConfigMapName: DEFAULT_STATIC_NODES_CONFIGMAP_NAME,
faucetArtifactPrefix: DEFAULT_FAUCET_ARTIFACT_PREFIX,
subgraphConfigMapName: DEFAULT_SUBGRAPH_CONFIGMAP_NAME,
} = ARTIFACT_DEFAULTS;
const OUTPUT_CHOICES: OutputType[] = ["screen", "file", "kubernetes"];
const LEADING_DOT_REGEX = /^\./u;
Expand Down Expand Up @@ -303,6 +307,7 @@ const runBootstrap = async (
genesisConfigmapName: genesisConfigmapNameOption,
staticNodesConfigmapName: staticNodesConfigmapNameOption,
faucetArtifactPrefix: faucetArtifactPrefixOption,
subgraphHashFile: subgraphHashFileOption,
} = options;

const resolveCount = (
Expand Down Expand Up @@ -399,6 +404,22 @@ const runBootstrap = async (
? await deps.loadAbis(trimmedAbiDirectory)
: [];

const envSubgraphHashFile = Bun.env.SUBGRAPH_HASH_FILE?.trim();
const providedSubgraphHashFile =
subgraphHashFileOption === undefined
? undefined
: subgraphHashFileOption.trim();
let subgraphHashPath: string | undefined;
if (providedSubgraphHashFile && providedSubgraphHashFile.length > 0) {
subgraphHashPath = providedSubgraphHashFile;
} else if (envSubgraphHashFile && envSubgraphHashFile.length > 0) {
subgraphHashPath = envSubgraphHashFile;
}
Comment on lines +407 to +417
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The logic for determining subgraphHashPath by prioritizing the CLI option over the environment variable is more verbose than necessary. This can be simplified into a single line using the logical OR (||) operator, which improves readability and conciseness. The subgraphHashFileOption is already a trimmed string or undefined from the command's action handler, so no further processing is needed on it.

Suggested change
const envSubgraphHashFile = Bun.env.SUBGRAPH_HASH_FILE?.trim();
const providedSubgraphHashFile =
subgraphHashFileOption === undefined
? undefined
: subgraphHashFileOption.trim();
let subgraphHashPath: string | undefined;
if (providedSubgraphHashFile && providedSubgraphHashFile.length > 0) {
subgraphHashPath = providedSubgraphHashFile;
} else if (envSubgraphHashFile && envSubgraphHashFile.length > 0) {
subgraphHashPath = envSubgraphHashFile;
}
const subgraphHashPath =
subgraphHashFileOption || Bun.env.SUBGRAPH_HASH_FILE?.trim();


const subgraphHash = subgraphHashPath
? await deps.loadSubgraphHash(subgraphHashPath)
: undefined;

const { genesis } = await deps.promptForGenesis(deps.service, {
faucetAddress,
allocations: allocationOverrides,
Expand All @@ -425,8 +446,10 @@ const runBootstrap = async (
validatorPrefix: staticNodePodPrefix,
genesisConfigMapName,
staticNodesConfigMapName,
subgraphConfigMapName: DEFAULT_SUBGRAPH_CONFIGMAP_NAME,
},
abiArtifacts,
subgraphHash,
};

await deps.outputResult(outputType ?? "screen", payload);
Expand All @@ -441,6 +464,7 @@ const defaultDependencies: BootstrapDependencies = {
service: new BesuGenesisService(),
loadAllocations,
loadAbis,
loadSubgraphHash,
outputResult: defaultOutputResult,
};
/* c8 ignore end */
Expand Down Expand Up @@ -487,6 +511,11 @@ const createCliCommand = (
"Directory containing ABI JSON files to publish as ConfigMaps.",
(value: string) => stripSurroundingQuotes(value)
)
.option(
"--subgraph-hash-file <path>",
"Path to a file containing the subgraph IPFS hash.",
(value: string) => stripSurroundingQuotes(value)
)
.option(
"-o, --outputType <type>",
`Output target (${OUTPUT_CHOICES.join(", ")}).`,
Expand Down Expand Up @@ -601,6 +630,10 @@ const createCliCommand = (
normalizedOptions.abiDirectory === undefined
? undefined
: stripSurroundingQuotes(normalizedOptions.abiDirectory),
subgraphHashFile:
normalizedOptions.subgraphHashFile === undefined
? undefined
: stripSurroundingQuotes(normalizedOptions.subgraphHashFile),
};

for (const { key, sanitize } of TEXT_OPTION_DESCRIPTORS) {
Expand Down Expand Up @@ -628,6 +661,12 @@ const createCliCommand = (
trimmed.length === 0 ? undefined : trimmed;
}

if (sanitizedOptions.subgraphHashFile) {
const trimmed = sanitizedOptions.subgraphHashFile.trim();
sanitizedOptions.subgraphHashFile =
trimmed.length === 0 ? undefined : trimmed;
}

await runBootstrap(sanitizedOptions, deps);
});

Expand Down
Loading
Loading