perf: use foundry official docker to run CI and speed up CI #159
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: Foundry | |
on: | |
workflow_dispatch: | |
pull_request: | |
push: | |
branches: | |
- "dev" | |
env: | |
FOUNDRY_PROFILE: ci | |
RPC_MAINNET: ${{ secrets.RPC_MAINNET }} | |
RPC_HOLESKY: ${{ secrets.RPC_HOLESKY }} | |
CHAIN_ID: ${{ secrets.CHAIN_ID }} | |
jobs: | |
# ----------------------------------------------------------------------- | |
# Forge Test | |
# ----------------------------------------------------------------------- | |
test-suite: | |
name: Test | |
runs-on: protocol-x64-16core | |
container: | |
image: ghcr.io/foundry-rs/foundry:stable | |
strategy: | |
matrix: | |
suite: [Unit, Integration, Fork] | |
steps: | |
# π§ Install Git (Needed for actions/checkout) | |
- name: Install Git | |
run: | | |
sudo apt-get update | |
sudo apt-get install -y git | |
# Check out repository with all submodules for complete codebase access. | |
- uses: actions/checkout@v4 | |
with: | |
submodules: recursive | |
# Run Forge's formatting checker to ensure consistent code style. | |
- name: "Forge Fmt" | |
run: | | |
forge fmt --check | |
id: fmt | |
# Build the project and display contract sizes. | |
- name: Forge Build | |
run: | | |
forge --version | |
forge build --sizes | |
# Run the test suite in parallel based on the matrix configuration. | |
- name: Run ${{ matrix.suite }} tests | |
run: | | |
case "${{ matrix.suite }}" in | |
Unit) forge test --no-match-contract Integration ;; | |
Integration) forge test --match-contract Integration ;; | |
Fork) forge test --match-contract Integration ;; | |
esac | |
env: | |
FOUNDRY_PROFILE: ${{ matrix.suite == 'Fork' && 'forktest' || 'ci' }} | |
# ----------------------------------------------------------------------- | |
# Forge Test (Intense) | |
# ----------------------------------------------------------------------- | |
continuous-fuzzing: | |
name: Test (Intense) | |
runs-on: protocol-x64-16core | |
container: | |
image: ghcr.io/foundry-rs/foundry:stable | |
# Only run on push events to dev branch, not on PR events | |
if: github.event_name == 'push' && github.ref == 'refs/heads/dev' | |
strategy: | |
fail-fast: true | |
steps: | |
# Check out repository with all submodules for complete codebase access. | |
- uses: actions/checkout@v4 | |
with: | |
submodules: recursive | |
# Build the project and display contract sizes. | |
- name: "Forge Build" | |
run: | | |
forge --version | |
forge build --sizes | |
id: build | |
# Run Forge Test (Intense) | |
- name: Forge Test (Intense) | |
run: | | |
echo -e "\033[1;33mWarning: This workflow may take several hours to complete.\033[0m" | |
echo -e "\033[1;33mThis intense fuzzing workflow is optional but helps catch edge cases through extended testing.\033[0m" | |
FOUNDRY_PROFILE=intense forge test -vvv | |
# ----------------------------------------------------------------------- | |
# Forge Storage Diff | |
# ----------------------------------------------------------------------- | |
storage-diff: | |
name: Test (Storage) | |
runs-on: protocol-x64-16core | |
container: | |
image: ghcr.io/foundry-rs/foundry:stable | |
steps: | |
# Check out repository with all submodules for complete codebase access. | |
- uses: actions/checkout@v4 | |
with: | |
submodules: recursive | |
# Run storage diff check to detect storage layout incompatibilities. | |
- name: "Mainnet Storage Diff" | |
run: | | |
bash bin/storage-diff.sh --rpc-url ${{ secrets.RPC_MAINNET }} --etherscan-key ${{ secrets.ETHERSCAN_API_KEY }} --input .github/configs/storage-diff.json | |
id: storage-diff | |
# ----------------------------------------------------------------------- | |
# Forge Coverage | |
# ----------------------------------------------------------------------- | |
run-coverage: | |
name: Coverage | |
runs-on: protocol-x64-16core | |
container: | |
image: ghcr.io/foundry-rs/foundry:stable | |
# Only run coverage checks on dev, testnet-holesky, and mainnet branches, or PRs targeting these branches | |
if: | | |
github.ref == 'refs/heads/dev' || | |
github.ref == 'refs/heads/testnet-holesky' || | |
github.ref == 'refs/heads/mainnet' || | |
github.base_ref == 'dev' || | |
github.base_ref == 'testnet-holesky' || | |
github.base_ref == 'mainnet' | |
strategy: | |
fail-fast: true | |
steps: | |
# Check out repository with all submodules for complete codebase access. | |
- uses: actions/checkout@v4 | |
with: | |
submodules: recursive | |
# Install LCOV for coverage report generation. | |
- name: Install LCOV | |
run: | | |
sudo apt-get install lcov | |
id: lcov | |
# Build the project and display contract sizes. | |
- name: "Forge Build" | |
run: | | |
forge --version | |
forge build --sizes | |
id: build | |
# Run Forge coverage with LCOV report format, excluding test and script files | |
- name: Forge Coverage | |
run: | | |
FOUNDRY_DENY_WARNINGS=false FOUNDRY_PROFILE=ci forge coverage --report lcov --report summary --no-match-coverage "script|test" | |
genhtml -q -o report ./lcov.info | |
# Upload coverage report as artifact before potential failure | |
- name: Upload Coverage Report | |
uses: actions/upload-artifact@v4 | |
with: | |
name: code-coverage-report | |
path: report/* | |
# Check coverage threshold after uploading report | |
- name: Check Coverage Threshold | |
run: | | |
LINES_PCT=$(lcov --summary lcov.info | grep "lines" | cut -d ':' -f 2 | cut -d '%' -f 1 | tr -d '[:space:]') | |
FUNCTIONS_PCT=$(lcov --summary lcov.info | grep "functions" | cut -d ':' -f 2 | cut -d '%' -f 1 | tr -d '[:space:]') | |
FAILED=0 | |
if (( $(echo "$LINES_PCT < 90" | bc -l) )); then | |
echo -e "\033[1;31mβ Lines coverage ($LINES_PCT%) is below minimum threshold of 90%\033[0m" | |
FAILED=1 | |
else | |
echo -e "\033[1;32mβ Lines coverage ($LINES_PCT%) meets minimum threshold of 90%\033[0m" | |
fi | |
if (( $(echo "$FUNCTIONS_PCT < 90" | bc -l) )); then | |
echo -e "\033[1;31mβ Functions coverage ($FUNCTIONS_PCT%) is below minimum threshold of 90%\033[0m" | |
FAILED=1 | |
else | |
echo -e "\033[1;32mβ Functions coverage ($FUNCTIONS_PCT%) meets minimum threshold of 90%\033[0m" | |
fi | |
if [ $FAILED -eq 1 ]; then | |
exit 1 | |
fi | |
# ----------------------------------------------------------------------- | |
# Forge Size Diff | |
# ----------------------------------------------------------------------- | |
compare-contract-sizes: | |
name: Size Diff | |
runs-on: protocol-x64-16core | |
container: | |
image: ghcr.io/foundry-rs/foundry:stable | |
steps: | |
# Check out repository with all submodules for complete codebase access. | |
- uses: actions/checkout@v4 | |
with: | |
submodules: recursive | |
- name: Build contracts on PR branch | |
run: | | |
forge build --json --sizes | jq '.' > pr_sizes.json | |
- name: Checkout target branch | |
run: | | |
git fetch origin ${{ github.base_ref }} | |
git checkout ${{ github.base_ref }} | |
- name: Build contracts on target branch | |
run: | | |
forge build --json --sizes | jq '.' > target_sizes.json | |
- name: Compare contract sizes using Bash | |
run: | | |
# Extract contract names | |
contracts=$(jq -r 'keys[]' pr_sizes.json) | |
# Track if there are any differences | |
has_differences=0 | |
echo -e "\nπ \033[1;34mContract Size Comparison Report\033[0m π\n" | |
# Iterate through contracts and compare sizes | |
for contract in $contracts; do | |
pr_runtime=$(jq -r --arg contract "$contract" '.[$contract].runtime_size // 0' pr_sizes.json) | |
pr_init=$(jq -r --arg contract "$contract" '.[$contract].init_size // 0' pr_sizes.json) | |
target_runtime=$(jq -r --arg contract "$contract" '.[$contract].runtime_size // 0' target_sizes.json) | |
target_init=$(jq -r --arg contract "$contract" '.[$contract].init_size // 0' target_sizes.json) | |
runtime_diff=$((pr_runtime - target_runtime)) | |
init_diff=$((pr_init - target_init)) | |
if [ "$runtime_diff" -ne 0 ] || [ "$init_diff" -ne 0 ]; then | |
echo -e "\033[1;36mπ $contract:\033[0m" | |
if [ "$runtime_diff" -ne 0 ]; then | |
if [ "$runtime_diff" -gt 0 ]; then | |
echo -e " Runtime: \033[1;31m+$runtime_diff bytes\033[0m π" | |
else | |
echo -e " Runtime: \033[1;32m$runtime_diff bytes\033[0m π" | |
fi | |
fi | |
if [ "$init_diff" -ne 0 ]; then | |
if [ "$init_diff" -gt 0 ]; then | |
echo -e " Init: \033[1;31m+$init_diff bytes\033[0m π" | |
else | |
echo -e " Init: \033[1;32m$init_diff bytes\033[0m π" | |
fi | |
fi | |
has_differences=1 | |
fi | |
done | |
if [ "$has_differences" -eq 0 ]; then | |
echo -e "\033[1;32mβ¨ No contract size changes detected β¨\033[0m" | |
fi |