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
20 changes: 18 additions & 2 deletions .github/workflows/release-ci-runtime.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,14 @@ on:
- l3-custom-16
- l3-custom-18
- l3-custom-20
nitro-contracts-version:
description: "Nitro contracts version baked into the snapshot"
required: true
default: "v3.2"
type: choice
options:
- v2.1
- v3.2

jobs:
publish-runtime-image:
Expand Down Expand Up @@ -59,12 +67,20 @@ jobs:
run: pnpm dev snapshot install --force --release-tag "${{ inputs.snapshot-version }}"

- name: Prepare runtime context
run: node scripts/ci/prepare-runtime-context.mjs --variant "${{ inputs.variant }}" --snapshot-id default
run: >-
node scripts/ci/prepare-runtime-context.mjs
--variant "${{ inputs.variant }}"
--snapshot-id default
--nitro-contracts-version "${{ inputs.nitro-contracts-version }}"

- name: Lowercase owner
id: lower
run: echo "owner=$(echo '${{ github.repository_owner }}' | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_OUTPUT"

- name: Contracts tag
id: nc
run: echo "tag=nc$(echo '${{ inputs.nitro-contracts-version }}' | sed 's/^v//')" >> "$GITHUB_OUTPUT"

- name: Log in to GHCR
uses: docker/login-action@v3
with:
Expand All @@ -78,4 +94,4 @@ jobs:
context: .
file: docker/ci-runtime.Dockerfile
push: true
tags: ghcr.io/${{ steps.lower.outputs.owner }}/arbitrum-testnode-ci:${{ inputs.version }}-${{ inputs.variant }}
tags: ghcr.io/${{ steps.lower.outputs.owner }}/arbitrum-testnode-ci:${{ inputs.version }}-${{ steps.nc.outputs.tag }}-${{ inputs.variant }}
3 changes: 2 additions & 1 deletion .github/workflows/test-action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,15 @@ jobs:
run: node scripts/ci/prepare-runtime-context.mjs --variant l3-eth --snapshot-id default

- name: Build local runtime image
run: docker build -f docker/ci-runtime.Dockerfile -t local/arbitrum-testnode-ci:${{ github.sha }}-l3-eth .
run: docker build -f docker/ci-runtime.Dockerfile -t local/arbitrum-testnode-ci:${{ github.sha }}-nc3.2-l3-eth .

- name: Run action
id: action
uses: ./
with:
image-repository: local/arbitrum-testnode-ci
l3-node: true
nitro-contracts-version: v3.2
version: ${{ github.sha }}

- name: Validate outputs
Expand Down
9 changes: 9 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ inputs:
required: false
default: ""
description: "Custom L3 fee-token decimals (6, 18, or 20) when l3-node=true"
nitro-contracts-version:
description: "Nitro contracts version (e.g. v2.1, v3.2)"
required: false
default: "v3.2"
output-dir:
required: false
default: ""
Expand Down Expand Up @@ -66,6 +70,9 @@ outputs:
variant:
description: "Resolved runtime variant"
value: ${{ steps.resolve.outputs.variant }}
nitro-contracts-version:
description: "Resolved nitro contracts version"
value: ${{ steps.resolve.outputs.nitro-contracts-version }}
runs:
using: composite
steps:
Expand All @@ -76,6 +83,7 @@ runs:
INPUT_FEE_TOKEN_DECIMALS: ${{ inputs.fee-token-decimals }}
INPUT_IMAGE_REPOSITORY: ${{ inputs.image-repository }}
INPUT_L3_NODE: ${{ inputs.l3-node }}
INPUT_NITRO_CONTRACTS_VERSION: ${{ inputs.nitro-contracts-version }}
INPUT_OUTPUT_DIR: ${{ inputs.output-dir }}
INPUT_VERSION: ${{ inputs.version }}
GITHUB_WORKSPACE: ${{ github.workspace }}
Expand Down Expand Up @@ -103,6 +111,7 @@ runs:
INPUT_FEE_TOKEN_DECIMALS: ${{ inputs.fee-token-decimals }}
INPUT_IMAGE_REPOSITORY: ${{ inputs.image-repository }}
INPUT_L3_NODE: ${{ inputs.l3-node }}
INPUT_NITRO_CONTRACTS_VERSION: ${{ inputs.nitro-contracts-version }}
INPUT_OUTPUT_DIR: ${{ inputs.output-dir }}
INPUT_NETWORK_CONFIG_PATH: ${{ inputs.network-config-path }}
INPUT_STARTUP_TIMEOUT_SECONDS: ${{ inputs.startup-timeout-seconds }}
Expand Down
32 changes: 29 additions & 3 deletions scripts/action/lib.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,23 @@ import { isAbsolute, join, resolve } from "node:path";

export const DEFAULT_IMAGE_REPOSITORY = "ghcr.io/offchainlabs/arbitrum-testnode-ci";

export const CONTRACTS_VERSIONS = {
"v2.1": { tagComponent: "nc2.1" },
"v3.2": { tagComponent: "nc3.2" },
};

export const DEFAULT_CONTRACTS_VERSION = "v3.2";

export function normalizeContractsVersion(value) {
const v = value || DEFAULT_CONTRACTS_VERSION;
if (!CONTRACTS_VERSIONS[v]) {
throw new Error(
`nitro-contracts-version must be one of: ${Object.keys(CONTRACTS_VERSIONS).join(", ")}`,
);
}
return v;
}

export const VARIANTS = {
l2: {
hostPorts: {
Expand Down Expand Up @@ -108,7 +125,7 @@ export function resolveVariant({ feeTokenDecimals, l3Node }) {
return `l3-custom-${decimals}`;
}

export function buildImageRef({ imageRepository, variant, version }) {
export function buildImageRef({ contractsVersion, imageRepository, variant, version }) {
if (!version) {
throw new Error("version is required");
}
Expand All @@ -117,7 +134,8 @@ export function buildImageRef({ imageRepository, variant, version }) {
if (!definition) {
throw new Error(`Unknown variant ${variant}`);
}
return `${repository}:${version}-${definition.tagSuffix}`;
const cv = normalizeContractsVersion(contractsVersion);
return `${repository}:${version}-${CONTRACTS_VERSIONS[cv].tagComponent}-${definition.tagSuffix}`;
}

export function defaultOutputDir({ runnerTemp, variant, version }) {
Expand All @@ -133,6 +151,7 @@ function sanitizeContainerName(value) {

export function buildActionState({
containerName,
contractsVersion,
feeTokenDecimals,
imageRepository,
l3Node,
Expand All @@ -146,6 +165,7 @@ export function buildActionState({
if (!definition) {
throw new Error(`Unknown variant ${variant}`);
}
const resolvedContractsVersion = normalizeContractsVersion(contractsVersion);
const resolvedOutputDir = outputDir
? isAbsolute(outputDir)
? outputDir
Expand All @@ -163,7 +183,13 @@ export function buildActionState({
return {
configDir,
containerName: resolvedContainerName,
imageRef: buildImageRef({ imageRepository, variant, version }),
contractsVersion: resolvedContractsVersion,
imageRef: buildImageRef({
contractsVersion: resolvedContractsVersion,
imageRepository,
variant,
version,
}),
outputDir: resolvedOutputDir,
paths: {
l1BridgeUiConfig: join(configDir, "l1-l2-admin", "bridgeUiConfig.json"),
Expand Down
2 changes: 2 additions & 0 deletions scripts/action/resolve.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ function writeOutput(key, value) {

const state = buildActionState({
containerName: process.env["INPUT_CONTAINER_NAME"],
contractsVersion: process.env["INPUT_NITRO_CONTRACTS_VERSION"],
feeTokenDecimals: process.env["INPUT_FEE_TOKEN_DECIMALS"],
imageRepository: process.env["INPUT_IMAGE_REPOSITORY"],
l3Node: process.env["INPUT_L3_NODE"],
Expand All @@ -33,5 +34,6 @@ writeOutput("l3-rpc-url", state.rpcUrls.l3);
writeOutput("local-network-path", state.paths.localNetwork);
writeOutput("output-dir", state.outputDir);
writeOutput("snapshot-id", state.snapshotId);
writeOutput("nitro-contracts-version", state.contractsVersion);
writeOutput("variant", state.variant);
writeOutput("l3-enabled", state.variantDefinition.l3Enabled ? "true" : "false");
3 changes: 3 additions & 0 deletions scripts/action/run.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ function waitForRpc(url, timeoutMs) {

const state = buildActionState({
containerName: process.env["INPUT_CONTAINER_NAME"],
contractsVersion: process.env["INPUT_NITRO_CONTRACTS_VERSION"],
feeTokenDecimals: process.env["INPUT_FEE_TOKEN_DECIMALS"],
imageRepository: process.env["INPUT_IMAGE_REPOSITORY"],
l3Node: process.env["INPUT_L3_NODE"],
Expand Down Expand Up @@ -136,8 +137,10 @@ writeEnv("ARBITRUM_TESTNODE_L2_BRIDGE_UI_CONFIG_PATH", state.paths.l2BridgeUiCon
writeEnv("ARBITRUM_TESTNODE_L2_RPC_URL", state.rpcUrls.l2);
writeEnv("ARBITRUM_TESTNODE_L3_RPC_URL", state.rpcUrls.l3);
writeEnv("ARBITRUM_TESTNODE_LOCAL_NETWORK_PATH", state.paths.localNetwork);
writeEnv("ARBITRUM_TESTNODE_NITRO_CONTRACTS_VERSION", state.contractsVersion);
writeEnv("ARBITRUM_TESTNODE_VARIANT", state.variant);
writeEnv("ARBITRUM_TESTNODE_CONTAINER_NAME", state.containerName);
writeEnv("INTEGRATION_TEST_NITRO_CONTRACTS_BRANCH", state.contractsVersion);

const networkConfigPaths = (process.env["INPUT_NETWORK_CONFIG_PATH"] || "")
.split(",")
Expand Down
3 changes: 2 additions & 1 deletion scripts/ci/prepare-runtime-context.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ if (!definition) {
throw new Error(`Unknown variant ${variant}`);
}

const contractsVersion = readArg("--nitro-contracts-version") || "";
const snapshotId = readArg("--snapshot-id") || definition.snapshotId;
const snapshotDir = resolve(readArg("--snapshot-dir") || join("config", "snapshots", snapshotId));
const outputDir = resolve(readArg("--output-dir") || ".ci-runtime-context");
Expand Down Expand Up @@ -106,5 +107,5 @@ rewriteTree(exportConfigDir, [

writeFileSync(
join(outputDir, "metadata.json"),
`${JSON.stringify({ l3Enabled: definition.l3Enabled, snapshotId, variant }, null, 2)}\n`,
`${JSON.stringify({ l3Enabled: definition.l3Enabled, nitroContractsVersion: contractsVersion, snapshotId, variant }, null, 2)}\n`,
);
1 change: 1 addition & 0 deletions src/snapshot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export interface SnapshotManifest {
l2: string;
l3: string;
};
nitroContractsVersion?: string;
requiredFiles: string[];
configChecksums: Record<string, string>;
volumeArchives: string[];
Expand Down
30 changes: 28 additions & 2 deletions test/ci-action.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { describe, expect, it } from "vitest";
import {
DEFAULT_CONTRACTS_VERSION,
DEFAULT_IMAGE_REPOSITORY,
buildActionState,
buildImageRef,
normalizeContractsVersion,
resolveVariant,
} from "../scripts/action/lib.mjs";

Expand Down Expand Up @@ -30,21 +32,24 @@ describe("resolveVariant", () => {

describe("buildImageRef", () => {
it("uses the default repository when none is provided", () => {
expect(buildImageRef({ variant: "l3-eth", version: "v1.2.3" })).toBe(
`${DEFAULT_IMAGE_REPOSITORY}:v1.2.3-l3-eth`,
expect(buildImageRef({ contractsVersion: "v3.2", variant: "l3-eth", version: "v1.2.3" })).toBe(
`${DEFAULT_IMAGE_REPOSITORY}:v1.2.3-nc3.2-l3-eth`,
);
});
});

describe("buildActionState", () => {
it("builds stable paths and RPC URLs for l3 variants", () => {
const state = buildActionState({
contractsVersion: "v3.2",
l3Node: "true",
runnerTemp: "/tmp/runner",
version: "v1.2.3",
});

expect(state.variant).toBe("l3-eth");
expect(state.contractsVersion).toBe("v3.2");
expect(state.imageRef).toContain("nc3.2");
expect(state.outputDir).toBe("/tmp/runner/arbitrum-testnode/v1.2.3/l3-eth");
expect(state.paths.localNetwork).toBe(
"/tmp/runner/arbitrum-testnode/v1.2.3/l3-eth/config/localNetwork.json",
Expand All @@ -56,19 +61,23 @@ describe("buildActionState", () => {

it("omits l3-specific outputs for l2", () => {
const state = buildActionState({
contractsVersion: "v3.2",
l3Node: "false",
runnerTemp: "/tmp/runner",
version: "v1.2.3",
});

expect(state.variant).toBe("l2");
expect(state.contractsVersion).toBe("v3.2");
expect(state.imageRef).toContain("nc3.2");
expect(state.paths.l2BridgeUiConfig).toBe("");
expect(state.paths.l2l3Network).toBe("");
expect(state.rpcUrls.l3).toBe("");
});

it("resolves a relative output dir against the workspace", () => {
const state = buildActionState({
contractsVersion: "v3.2",
l3Node: "true",
outputDir: "./shadow-testnode-output",
version: "v1.2.3",
Expand All @@ -78,4 +87,21 @@ describe("buildActionState", () => {
expect(state.outputDir).toBe("/workspace/sdk-shadow/shadow-testnode-output");
expect(state.configDir).toBe("/workspace/sdk-shadow/shadow-testnode-output/config");
});

it("defaults to v3.2 when contractsVersion is not provided", () => {
const state = buildActionState({
l3Node: "true",
runnerTemp: "/tmp/runner",
version: "v1.2.3",
});

expect(state.contractsVersion).toBe(DEFAULT_CONTRACTS_VERSION);
expect(state.imageRef).toContain("nc3.2");
});

it("rejects invalid contracts versions", () => {
expect(() => normalizeContractsVersion("v9.9")).toThrow(
"nitro-contracts-version must be one of: v2.1, v3.2",
);
});
});
8 changes: 8 additions & 0 deletions test/ci-runtime-context.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,5 +100,13 @@ describe("prepare-runtime-context", () => {
expect(existsSync(join(outputDir, "runtime", "sequencer", ".arbitrum", "seq.txt"))).toBe(true);
expect(existsSync(join(outputDir, "runtime", "validator", ".arbitrum", "val.txt"))).toBe(true);
expect(existsSync(join(outputDir, "runtime", "l3node", ".arbitrum", "l3.txt"))).toBe(true);

const metadata = JSON.parse(readFileSync(join(outputDir, "metadata.json"), "utf-8"));
expect(metadata).toEqual({
l3Enabled: true,
nitroContractsVersion: "",
snapshotId: "default",
variant: "l3-eth",
});
});
});
Loading