Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
c750bda
feat: add Docker support for running upgrade scripts
yahgwai Feb 5, 2026
1f0f4f3
chore: add .DS_Store to gitignore
yahgwai Feb 5, 2026
eea7623
fix: use Yarn Classic (v1) in Dockerfile for lockfile compatibility
yahgwai Feb 5, 2026
c5948fa
fix: add submodule checkout for Docker build
yahgwai Feb 5, 2026
4c8edb8
fix: use forge install instead of git submodules for Docker build prereq
yahgwai Feb 5, 2026
9cb07c5
Update README.md
yahgwai Feb 5, 2026
c445d7d
feat: add browsable CLI for upgrade scripts
yahgwai Feb 6, 2026
15b87cf
feat: add Verify scripts for contract upgrades
yahgwai Feb 9, 2026
a8adfeb
ci: add Docker Hub publishing workflow
yahgwai Feb 9, 2026
4d391f9
refactor: rewrite CLI in TypeScript
yahgwai Feb 9, 2026
c5520f7
refactor: remove obvious comments and add named constants
yahgwai Feb 9, 2026
c93b86f
fix: update docker tests and eslint config for TS CLI
yahgwai Feb 9, 2026
92fbc31
test: add local smoke tests for CLI
yahgwai Feb 9, 2026
e3addac
docs: reorganize README with CLI and Docker sections
yahgwai Feb 9, 2026
596182b
refactor: simplify env loading to current directory only
yahgwai Feb 10, 2026
9dc0108
refactor: extract duplicate execute/verify logic in arbos-upgrade
yahgwai Feb 10, 2026
6dd231f
refactor: remove unused Commander subcommands
yahgwai Feb 10, 2026
aaa6eea
build: pin Foundry to specific nightly version
yahgwai Feb 10, 2026
ead0814
chore: remove obvious Dockerfile comments
yahgwai Feb 10, 2026
7977435
fix: correct Foundry version pin and lint issues
yahgwai Feb 10, 2026
2f30dde
revert: remove Foundry version pin that fails in Docker
yahgwai Feb 10, 2026
05c1282
Merge remote-tracking branch 'origin/main' into feat/380-dockerize
yahgwai Feb 10, 2026
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
30 changes: 30 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Dependencies (will be installed fresh in container)
node_modules/

# Build artifacts (will be built fresh in container)
out/
cache_forge/
cache/
artifacts/
typechain-types/

# Environment files (contain secrets)
.env
.env.*
!.env.example

# Git
.git/
.gitignore

# IDE
.vscode/
.idea/

# Test artifacts
broadcast/
coverage/

# Docker
Dockerfile
.dockerignore
1 change: 1 addition & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
lib/
dist/
8 changes: 1 addition & 7 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ module.exports = {
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
],
plugins: ['@typescript-eslint', 'prettier', '@typescript-eslint/tslint'],
plugins: ['@typescript-eslint', 'prettier'],
rules: {
'no-empty-pattern': 'warn',
'prettier/prettier': ['error', { singleQuote: true }],
Expand All @@ -77,12 +77,6 @@ module.exports = {
caughtErrorsIgnorePattern: '^_',
},
],
'@typescript-eslint/tslint/config': [
'error',
{
rules: { 'strict-comparisons': true },
},
],
'no-implicit-coercion': 'error',
'@typescript-eslint/no-shadow': ['error'],
},
Expand Down
53 changes: 53 additions & 0 deletions .github/workflows/publish-docker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
name: Publish Docker

on:
push:
branches: [main]
tags: ['v*']
workflow_dispatch:

jobs:
publish:
name: Build and Push Docker Image
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: stable

- name: Install forge dependencies
run: forge install

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Extract metadata for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: offchainlabs/chain-actions
tags: |
type=raw,value=latest,enable={{is_default_branch}}
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=ref,event=branch
Comment on lines +37 to +43
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

The publish workflow targets offchainlabs/chain-actions, but the PR description/README/Docker usage references offchainlabs/orbit-actions. If orbit-actions is the intended image name, update images: accordingly to avoid publishing to the wrong repository.

Copilot uses AI. Check for mistakes.

- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
38 changes: 38 additions & 0 deletions .github/workflows/test-docker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
name: Test Docker

on:
pull_request:
workflow_dispatch:

jobs:
test-docker:
name: Test Docker Image
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: stable

- name: Install forge dependencies
run: forge install

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Build Docker image
uses: docker/build-push-action@v5
with:
context: .
load: true
tags: orbit-actions:test
cache-from: type=gha
cache-to: type=gha,mode=max

- name: Run Docker smoke tests
run: ./test/docker/test-docker.bash
env:
DOCKER_IMAGE: orbit-actions:test
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
node_modules
.env
.DS_Store

# TypeScript build output
/dist

# Hardhat files
/cache
Expand Down
29 changes: 29 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
FROM node:18-slim

# Install dependencies for Foundry, git, and jq (for JSON parsing in upgrade scripts)
RUN apt-get update && apt-get install -y \
curl \
git \
jq \
&& rm -rf /var/lib/apt/lists/*

# Install Foundry
ENV PATH="/root/.foundry/bin:${PATH}"
RUN curl -L https://foundry.paradigm.xyz | bash && foundryup

# Install Yarn Classic (v1) - matches the repo's yarn.lock format
RUN npm install -g --force yarn@1.22.22

WORKDIR /app

# Copy package files first for better layer caching
COPY package.json yarn.lock ./

# --ignore-scripts: forge install runs separately after full copy
RUN yarn install --frozen-lockfile --ignore-scripts

COPY . .
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

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

The Docker build relies on Forge dependencies being present, but the Dockerfile never runs forge install (and yarn install --ignore-scripts skips the repo's prepare hook that would normally do it). This makes docker build fail unless the build context already contains lib/ from a prior host-side forge install (as your CI currently does). Add an explicit RUN forge install inside the Dockerfile (after COPY . . and before forge build) so the image builds reliably from a clean checkout.

Suggested change
COPY . .
COPY . .
RUN forge install

Copilot uses AI. Check for mistakes.
RUN forge build
RUN yarn build:cli

ENTRYPOINT ["node", "/app/dist/cli/index.js"]
48 changes: 47 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,50 @@ See [setCacheManager](scripts/foundry/stylus/setCacheManager).

Currently limited to L2s; L3 support is expected in a future update.

See [Nitro contracts 3.1.0 upgrade](https://github.com/OffchainLabs/orbit-actions/tree/main/scripts/foundry/contract-upgrades/3.1.0).
See [Nitro contracts 3.1.0 upgrade](https://github.com/OffchainLabs/orbit-actions/tree/main/scripts/foundry/contract-upgrades/3.1.0).

# CLI

The `orbit-actions` CLI provides a guided interface for running upgrade scripts. It wraps Foundry commands and handles the deploy/execute/verify workflow.

```bash
# Browse available scripts
yarn orbit-actions # List top-level directories
yarn orbit-actions contract-upgrades # List versions
yarn orbit-actions contract-upgrades/1.2.1 # List contents + commands

# View files
yarn orbit-actions contract-upgrades/1.2.1/README.md

# Run contract upgrades
yarn orbit-actions contract-upgrades/1.2.1/deploy-execute-verify --dry-run
yarn orbit-actions contract-upgrades/1.2.1/deploy --private-key $KEY

# Run ArbOS upgrades
yarn orbit-actions arbos-upgrades/at-timestamp/deploy-execute-verify 32 --dry-run
```

Run `yarn orbit-actions help` for full usage details. The CLI reads configuration from a `.env` file in the working directory.

## Docker

The CLI is available as a Docker image at `offchainlabs/orbit-actions`:

```bash
# Check contract versions
docker run --rm \
-e INBOX_ADDRESS=0xaE21fDA3de92dE2FDAF606233b2863782Ba046F9 \
-e INFURA_KEY=$INFURA_KEY \
offchainlabs/orbit-actions:versioner \
--network arb1

# Browse upgrade scripts
docker run --rm offchainlabs/orbit-actions contract-upgrades

# Run upgrade with env file and capture broadcast output
docker run --rm \
-v $(pwd)/.env:/app/.env \
-v $(pwd)/broadcast:/app/broadcast \
offchainlabs/orbit-actions \
contract-upgrades/1.2.1/deploy-execute-verify --dry-run
```
10 changes: 10 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@
"version": "1.0.0",
"repository": "https://github.com/OffchainLabs/blockchain-eng-template.git",
"license": "Apache 2.0",
"bin": {
"orbit-actions": "./dist/cli/index.js"
},
"scripts": {
"build:cli": "tsc -p tsconfig.cli.json",
"cli": "ts-node src/cli/index.ts",
"prepare": "forge install && cd lib/arbitrum-sdk && yarn",
"minimal-publish": "./scripts/publish.bash",
"minimal-install": "yarn --ignore-scripts && forge install",
Expand All @@ -20,6 +25,7 @@
"test:gas-check": "forge snapshot --check --tolerance 1 --match-path \"test/unit/**/*.t.sol\"",
"test:sigs": "./test/signatures/test-sigs.bash",
"test:storage": "./test/storage/test-storage.bash",
"test:docker": "./test/docker/test-docker.bash",
"orbit:contracts:version": "hardhat run scripts/orbit-versioner/orbitVersioner.ts",
"gas-snapshot": "forge snapshot --match-path \"test/unit/**/*.t.sol\"",
"fix": "yarn format; yarn test:sigs; yarn test:storage; yarn gas-snapshot"
Expand Down Expand Up @@ -61,5 +67,9 @@
"ts-node": ">=8.0.0",
"typechain": "^8.3.0",
"typescript": ">=4.5.0"
},
"dependencies": {
"commander": "^12.0.0",
"execa": "^5.1.1"
}
}
4 changes: 2 additions & 2 deletions scripts/foundry/contract-upgrades/1.2.1/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ forge script --sender $EXECUTOR --rpc-url $PARENT_CHAIN_RPC --broadcast ./Execut
```
If you have a multisig as executor, you can still run the above command without broadcasting to get the payload for the multisig transaction.

4. That's it, upgrade has been performed. You can make sure it has successfully executed by checking wasm module root:
4. That's it, upgrade has been performed. You can verify by running:
```bash
cast call --rpc-url $PARENT_CHAIN_RPC $ROLLUP "wasmModuleRoot()"
forge script --rpc-url $PARENT_CHAIN_RPC VerifyNitroContracts1Point2Point1Upgrade -vvv
```
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.16;

import "forge-std/Script.sol";

interface IRollupCore {
function wasmModuleRoot() external view returns (bytes32);
}

/**
* @title VerifyNitroContracts1Point2Point1Upgrade
* @notice Verifies the upgrade to Nitro Contracts 1.2.1 by checking the wasmModuleRoot
*/
contract VerifyNitroContracts1Point2Point1Upgrade is Script {
function run() public view {
address rollup = vm.envAddress("ROLLUP");
bytes32 wasmRoot = IRollupCore(rollup).wasmModuleRoot();
console.log("wasmModuleRoot:");
console.logBytes32(wasmRoot);
}
}
4 changes: 2 additions & 2 deletions scripts/foundry/contract-upgrades/2.1.0/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,10 @@ forge script --sender $EXECUTOR --rpc-url $PARENT_CHAIN_RPC --broadcast ExecuteN

If you have a multisig as executor, you can still run the above command without broadcasting to get the payload for the multisig transaction.

4. That's it, upgrade has been performed. You can make sure it has successfully executed by checking wasm module root:
4. That's it, upgrade has been performed. You can verify by running:

```bash
cast call --rpc-url $PARENT_CHAIN_RPC $ROLLUP "wasmModuleRoot()"
forge script --rpc-url $PARENT_CHAIN_RPC VerifyNitroContracts2Point1Point0Upgrade -vvv
```

## FAQ
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.16;

import "forge-std/Script.sol";

interface IRollupCore {
function wasmModuleRoot() external view returns (bytes32);
}

/**
* @title VerifyNitroContracts2Point1Point0Upgrade
* @notice Verifies the upgrade to Nitro Contracts 2.1.0 by checking the wasmModuleRoot
*/
contract VerifyNitroContracts2Point1Point0Upgrade is Script {
function run() public view {
address rollup = vm.envAddress("ROLLUP");
bytes32 wasmRoot = IRollupCore(rollup).wasmModuleRoot();
console.log("wasmModuleRoot:");
console.logBytes32(wasmRoot);
}
}
5 changes: 2 additions & 3 deletions scripts/foundry/contract-upgrades/2.1.2/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,11 +73,10 @@ forge script --sender $EXECUTOR --rpc-url $PARENT_CHAIN_RPC --broadcast ExecuteN

If you have a multisig as executor, you can still run the above command without broadcasting to get the payload for the multisig transaction.

4. That's it, upgrade has been performed. You can make sure it has successfully executed by checking the native token decimals.
4. That's it, upgrade has been performed. You can verify by running:

```bash
# should return 18
cast call --rpc-url $PARENT_CHAIN_RPC $BRIDGE "nativeTokenDecimals()(uint8)"
forge script --rpc-url $PARENT_CHAIN_RPC VerifyNitroContracts2Point1Point2Upgrade -vvv
```

## FAQ
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity 0.8.16;

import "forge-std/Script.sol";

interface IInbox {
function bridge() external view returns (address);
}

interface IBridge {
function nativeTokenDecimals() external view returns (uint8);
}

/**
* @title VerifyNitroContracts2Point1Point2Upgrade
* @notice Verifies the upgrade to Nitro Contracts 2.1.2 by checking nativeTokenDecimals
*/
contract VerifyNitroContracts2Point1Point2Upgrade is Script {
function run() public view {
address inbox = vm.envAddress("INBOX_ADDRESS");
address bridge = IInbox(inbox).bridge();
uint8 decimals = IBridge(bridge).nativeTokenDecimals();
console.log("nativeTokenDecimals:", decimals);
}
}
5 changes: 2 additions & 3 deletions scripts/foundry/contract-upgrades/2.1.3/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,10 @@ forge script --sender $EXECUTOR --rpc-url $PARENT_CHAIN_RPC --broadcast ExecuteN

If you have a multisig as executor, you can still run the above command without broadcasting to get the payload for the multisig transaction.

4. That's it, upgrade has been performed. You can make sure it has successfully executed by checking the native token decimals.
4. That's it, upgrade has been performed. You can verify by running:

```bash
# should return 18
cast call --rpc-url $PARENT_CHAIN_RPC $BRIDGE "nativeTokenDecimals()(uint8)"
forge script --rpc-url $PARENT_CHAIN_RPC VerifyNitroContracts2Point1Point3Upgrade -vvv
```

## FAQ
Expand Down
Loading