refactor: change brand to imua #505
Workflow file for this run
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
name: Forge CI | |
on: | |
merge_group: | |
pull_request: | |
push: | |
branches: | |
- main | |
- release/** | |
tags: | |
- "*" | |
concurrency: | |
# cancel the other workflows on the same PR | |
# we do not do the same for any of the `workflow_run` triggered CI despite the possibility because | |
# (1) it is complicated to set up, since the PR number is not available directly in that context, and | |
# (2) cancelling this workflow will prevent triggering of any related `workflow_run` CIs, and | |
# (3) in the edge case wherein the `workflow_run` CI is already in progress, its results will be over- | |
# written by the results of the next `workflow_run` CI due to timing, so it is not a big deal. it | |
# is not guaranteed, however, but the risk is acceptable given the status checks are required on | |
# a commit hash basis. in the worst case, we will see a comment referring to the old commit hash | |
# via `status-comment.yml` or similar. | |
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref_name || github.sha }} | |
cancel-in-progress: true | |
jobs: | |
setup: | |
# A full job can be used as a reusable workflow but not a step. | |
uses: ./.github/workflows/reusable-foundry-setup.yml | |
with: | |
# The below line does not accept environment variables, | |
# so it becomes the single source of truth for the version. | |
foundry-version: nightly | |
build: | |
# Caching is slow; takes about 3 minutes. | |
timeout-minutes: 15 | |
runs-on: ubuntu-latest | |
needs: setup | |
outputs: | |
# The cache-key only contains the version name. It is only used so that the name does not | |
# need to be repeated everywhere; instead setting the `foundry-version` above suffices. | |
cache-key: ${{ needs.setup.outputs.cache-key }} | |
# Github's cache actions are a bit weird to deal with. It wouldn't let me restore the | |
# binaries to /usr/bin, so I restored them to the original location and added it to PATH. | |
# This output will let us carry it to other jobs. | |
installation-dir: ${{ needs.setup.outputs.installation-dir }} | |
steps: | |
- name: Restore cached Foundry toolchain | |
uses: actions/cache/restore@v3 | |
with: | |
path: ${{ needs.setup.outputs.installation-dir }} | |
key: ${{ needs.setup.outputs.cache-key }} | |
- name: Add Foundry to PATH | |
run: echo "${{ needs.setup.outputs.installation-dir }}" >> "$GITHUB_PATH" | |
- name: Checkout repository | |
uses: actions/checkout@v4 | |
with: | |
submodules: recursive | |
- name: Build | |
run: forge build | |
- name: Cache `forge build` results | |
uses: actions/cache/save@v3 | |
with: | |
path: | | |
./lib | |
./out | |
./cache | |
./broadcast | |
key: build-${{ github.event.pull_request.head.sha || github.event.after || github.sha }} | |
test: | |
# Takes less than 30s | |
timeout-minutes: 5 | |
runs-on: ubuntu-latest | |
needs: build | |
steps: | |
- name: Restore cached Foundry toolchain | |
uses: actions/cache/restore@v3 | |
with: | |
path: ${{ needs.build.outputs.installation-dir }} | |
key: ${{ needs.build.outputs.cache-key }} | |
- name: Add Foundry to PATH | |
run: echo "${{ needs.build.outputs.installation-dir }}" >> "$GITHUB_PATH" | |
- name: Checkout repository | |
uses: actions/checkout@v4 | |
- name: Restore `forge build` results | |
uses: actions/cache/restore@v3 | |
with: | |
path: | | |
./lib | |
./out | |
./cache | |
./broadcast | |
key: build-${{ github.event.pull_request.head.sha || github.event.after || github.sha }} | |
- name: Clear out the `etherscan` section in `foundry.toml` for missing env vars | |
run: sed -i '/\[etherscan\]/,/^\[/ s/^/#/' foundry.toml | |
- name: Run tests | |
env: | |
FOUNDRY_PROFILE: test | |
run: forge test | |
- name: Set test snapshot as summary | |
env: | |
FOUNDRY_PROFILE: test | |
NO_COLOR: 1 | |
run: forge snapshot >> "$GITHUB_STEP_SUMMARY" | |
format: | |
# Takes less than 30s | |
timeout-minutes: 5 | |
runs-on: ubuntu-latest | |
needs: build | |
steps: | |
- name: Restore cached Foundry toolchain | |
uses: actions/cache/restore@v3 | |
with: | |
path: ${{ needs.build.outputs.installation-dir }} | |
key: ${{ needs.build.outputs.cache-key }} | |
- name: Add Foundry to PATH | |
run: echo "${{ needs.build.outputs.installation-dir }}" >> "$GITHUB_PATH" | |
- name: Checkout repository | |
uses: actions/checkout@v4 | |
- name: Restore `forge build` results | |
uses: actions/cache/restore@v3 | |
with: | |
path: | | |
./lib | |
./out | |
./cache | |
./broadcast | |
key: build-${{ github.event.pull_request.head.sha || github.event.after || github.sha }} | |
- name: Check formatting | |
run: forge fmt --check | |
check-contract-deployments: | |
# Takes less than 60s | |
timeout-minutes: 10 | |
runs-on: ubuntu-latest | |
needs: build | |
steps: | |
- name: Restore cached Foundry toolchain | |
uses: actions/cache/restore@v3 | |
with: | |
path: ${{ needs.build.outputs.installation-dir }} | |
key: ${{ needs.build.outputs.cache-key }} | |
- name: Add Foundry to PATH | |
run: echo "${{ needs.build.outputs.installation-dir }}" >> "$GITHUB_PATH" | |
- name: Checkout repository | |
uses: actions/checkout@v4 | |
- name: Validate deployedContracts.json and prepare artifact | |
run: | | |
data=$(cat script/deployments/deployedContracts.json) | |
bootstrap=$(echo "$data" | jq -r '.clientChain.bootstrapLogic // empty') | |
clientGateway=$(echo "$data" | jq -r '.clientChain.clientGatewayLogic // empty') | |
vault=$(echo "$data" | jq -r '.clientChain.vaultImplementation // empty') | |
rewardVault=$(echo "$data" | jq -r '.clientChain.rewardVaultImplementation // empty') | |
capsule=$(echo "$data" | jq -r '.clientChain.capsuleImplementation // empty') | |
validate_address() { | |
local address=$1 | |
if [ -z "$address" ]; then | |
echo "Validation failed: Address is empty" | |
exit 1 | |
fi | |
if [ "$(cast 2a $address)" != "$address" ]; then | |
echo "Validation failed: $address is not a valid Ethereum checksum address" | |
exit 1 | |
fi | |
} | |
# Check each address | |
echo "Validating bootstrap address..." | |
validate_address "$bootstrap" | |
echo "Validating clientGateway address..." | |
validate_address "$clientGateway" | |
echo "Validating vault address..." | |
validate_address "$vault" | |
echo "Validating rewardVault address..." | |
validate_address "$rewardVault" | |
echo "Validating capsule address..." | |
validate_address "$capsule" | |
# Prepare JSON for artifact. Instead of using the file within the repo, | |
# we create an artifact from the PR because the `compare-layouts` workflow | |
# runs on the base branch and doesn't have access to the PR's files. | |
# Given the presence of the artifact, technically, the above validation | |
# could be offloaded to the `compare-layouts` workflow, but this is a | |
# basic short circuit to avoid running the `compare-layouts` workflow | |
# if the PR has invalid addresses. Other validations to include, if | |
# possible, would be the verification of the contract on Etherscan; | |
# however, we cannot do that in the PR context without leaking the | |
# Etherscan API key. | |
# Note that | |
# (0) we use the logic addresses to match the comparison code: eg. Bootstrap is | |
# compared against BootstrapLogic instead of Bootstrap because Bootstrap is | |
# a proxy contract that points to the logic contract. | |
# (1) keys of the input json dict below are sourced from `deployedContracts.json` | |
# (2) keys of the output json dict below are case sensitive and must match the | |
# keys `x.deployed.json` defined in `compareLayouts.js` exactly. | |
jq -n \ | |
--arg bootstrap "$bootstrapLogic" \ | |
--arg clientGateway "$clientGatewayLogic" \ | |
--arg vault "$vaultImplementation" \ | |
--arg rewardVault "$rewardVaultImplementation" \ | |
--arg capsule "$capsuleImplementation" \ | |
'{ | |
Bootstrap: $bootstrap, | |
ClientChainGateway: $clientGateway, | |
Vault: $vault, | |
RewardVault: $rewardVault, | |
ImuaCapsule: $capsule | |
}' > validatedContracts.json | |
echo "Validation passed: All fields are non-empty and valid Ethereum checksum addresses" | |
- name: Upload validated contracts artifact | |
uses: actions/upload-artifact@v4 | |
with: | |
name: validated-contracts-${{ github.event.pull_request.head.sha || github.event.after || github.sha }} | |
path: validatedContracts.json | |
extract-storage-layout: | |
# Takes less than 30 seconds per matrix member | |
timeout-minutes: 5 | |
runs-on: ubuntu-latest | |
needs: build | |
outputs: | |
storage-layout-file: ${{ steps.generate-storage-layout.outputs.output-file }} | |
artifact-name: ${{ steps.generate-storage-layout.outputs.artifact-name }} | |
strategy: | |
matrix: | |
include: | |
- contract: ImuachainGateway | |
base: true | |
- contract: Bootstrap | |
base: false | |
- contract: ClientChainGateway | |
base: false | |
- contract: RewardVault | |
base: false | |
- contract: Vault | |
base: false | |
- contract: ImuachainGateway | |
base: false | |
- contract: ImuaCapsule | |
base: false | |
steps: | |
- name: Restore cached Foundry toolchain | |
uses: actions/cache/restore@v3 | |
with: | |
path: ${{ needs.build.outputs.installation-dir }} | |
key: ${{ needs.build.outputs.cache-key }} | |
- name: Add Foundry to PATH | |
run: echo "${{ needs.build.outputs.installation-dir }}" >> "$GITHUB_PATH" | |
- name: Checkout repository | |
uses: actions/checkout@v4 | |
if: ${{ !matrix.base }} | |
- name: Checkout base branch of repository | |
uses: actions/checkout@v4 | |
with: | |
ref: ${{ github.event.pull_request.base.ref || github.event.before }} | |
submodules: recursive | |
if: ${{ matrix.base }} | |
- name: Restore `forge build` results | |
uses: actions/cache/restore@v3 | |
# The restoration can only happen for the non-base case. For the base case, | |
# the context is different and anything created within the PR context will | |
# not be available to the cache restore action. | |
if: ${{ !matrix.base }} | |
with: | |
path: | | |
./lib | |
./out | |
./cache | |
./broadcast | |
key: build-${{ github.event.pull_request.head.sha || github.event.after || github.sha }} | |
- name: Generate storage layout file | |
id: generate-storage-layout | |
run: | | |
set -e | |
artifact_name="compiled-layout-${{ matrix.contract }}" | |
if [ "${{ matrix.base }}" = "true" ]; then | |
output_file="${{ matrix.contract }}.base.json" | |
artifact_name="${artifact_name}-base" | |
else | |
output_file="${{ matrix.contract }}.compiled.json" | |
artifact_name="${artifact_name}-proposed" | |
fi | |
artifact_name="${artifact_name}-${{ github.event.pull_request.head.sha || github.event.after || github.sha }}" | |
forge inspect --json src/core/${{ matrix.contract }}.sol:${{ matrix.contract }} storage-layout > $output_file | |
echo "output-file=$output_file" >> "$GITHUB_OUTPUT" | |
echo "artifact-name=$artifact_name" >> "$GITHUB_OUTPUT" | |
- name: Upload storage layout file as an artifact | |
uses: actions/upload-artifact@v4 | |
with: | |
path: ${{ steps.generate-storage-layout.outputs.output-file }} | |
name: ${{ steps.generate-storage-layout.outputs.artifact-name }} | |
combine-storage-layouts: | |
# Takes less than 10 seconds | |
timeout-minutes: 5 | |
runs-on: ubuntu-latest | |
needs: | |
- extract-storage-layout | |
steps: | |
- name: Download artifacts | |
uses: actions/download-artifact@v4 | |
# No name means all artifacts created by this workflow are downloaded | |
# within their respective subfolders (paths) inside the provided path (`combined`). | |
with: | |
path: combined | |
- name: Zip up the compiled layouts | |
run: find combined -type f -name "*.json" ! -name "validatedContracts.json" -exec zip -j compiled-layouts.zip {} + | |
- name: Upload the compiled layouts file as an artifact | |
uses: actions/upload-artifact@v4 | |
with: | |
path: compiled-layouts.zip | |
name: compiled-layouts-${{ github.event.pull_request.head.sha || github.event.after || github.sha }} |