Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
12 changes: 3 additions & 9 deletions .github/workflows/_foundry-cicd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -409,20 +409,14 @@ jobs:
path: .etherform
sparse-checkout: scripts

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}

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

- name: Setup Node.js
if: inputs.package-manager != 'none'
Comment thread
RonTuretzky marked this conversation as resolved.
uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
cache: ${{ inputs.package-manager }}
cache: ${{ inputs.package-manager != 'none' && inputs.package-manager || '' }}
Comment thread
RonTuretzky marked this conversation as resolved.
Outdated

- name: Install dependencies
if: inputs.package-manager != 'none'
Expand Down Expand Up @@ -451,7 +445,7 @@ jobs:
always() &&
needs.detect-changes.outputs.should-run == 'true' &&
needs.ci.result == 'success' &&
(needs.upgrade-safety.result == 'success' || needs.upgrade-safety.result == 'skipped') &&
needs.upgrade-safety.result == 'success' &&
Comment thread
RonTuretzky marked this conversation as resolved.
inputs.deploy-on-pr &&
github.event_name == 'pull_request'
steps:
Expand Down Expand Up @@ -494,7 +488,7 @@ jobs:
DEPLOY_ENV_VARS: ${{ secrets.DEPLOY_ENV_VARS }}
run: |
source .etherform/scripts/deploy/prepare-env.sh
forge script ${{ inputs.deploy-script }} \
forge script "${{ inputs.deploy-script }}" \
--rpc-url "$RPC_URL" \
--private-key "$PRIVATE_KEY" \
--broadcast \
Expand Down
8 changes: 1 addition & 7 deletions .github/workflows/_upgrade-safety.yml
Comment thread
RonTuretzky marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -52,20 +52,14 @@ jobs:
path: .etherform
sparse-checkout: scripts

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}

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

- name: Setup Node.js
if: inputs.package-manager != 'none'
uses: actions/setup-node@v4
with:
node-version: ${{ inputs.node-version }}
cache: ${{ inputs.package-manager }}
cache: ${{ inputs.package-manager != 'none' && inputs.package-manager || '' }}

- name: Install dependencies
if: inputs.package-manager != 'none'
Expand Down
16 changes: 15 additions & 1 deletion scripts/deploy/prepare-env.sh
Comment thread
RonTuretzky marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,20 @@ fi
if [[ -n "${DEPLOY_ENV_VARS:-}" ]]; then
while IFS= read -r line; do
[[ -z "$line" || "$line" == \#* ]] && continue
export "${line?}"

# Expect KEY=VALUE; skip malformed lines
if [[ "$line" != *=* ]]; then
continue
fi

key=${line%%=*}
value=${line#*=}

# Validate KEY against a safe variable-name pattern
if [[ ! "$key" =~ ^[A-Za-z_][A-Za-z0-9_]*$ ]]; then
continue
fi
Comment thread
RonTuretzky marked this conversation as resolved.
Outdated

export "$key=$value"
done <<< "$DEPLOY_ENV_VARS"
fi
6 changes: 6 additions & 0 deletions scripts/deploy/resolve-network.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ if [[ -z "$BLOCKSCOUT_URL" || "$BLOCKSCOUT_URL" == "null" ]]; then
BLOCKSCOUT_URL=$(jq -r '.testnets[0].blockscout_url' "$NETWORK_CONFIG")
fi

# Post-fallback validation: ensure we have a non-empty, HTTP(S) Blockscout URL
if [[ -z "$BLOCKSCOUT_URL" || "$BLOCKSCOUT_URL" == "null" || ! "$BLOCKSCOUT_URL" =~ ^https?:// ]]; then
echo "::error::No valid blockscout_url found in $NETWORK_CONFIG"
exit 1
fi

NETWORK_NAME=$(jq -r --argjson cid "$CHAIN_ID" '
(.testnets // [])
| map(select(.chain_id == $cid))
Expand Down
2 changes: 1 addition & 1 deletion scripts/deploy/verify-blockscout.sh
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ for entry in "${CONTRACTS[@]}"; do
VERIFIED=0
for check in $(seq 1 "$MAX_CHECKS"); do
# Query Blockscout API for verification status
API_RESULT=$(curl -sf "${BLOCKSCOUT_URL}/api?module=contract&action=getabi&address=${CONTRACT_ADDR}" 2>/dev/null) || true
API_RESULT=$(curl -sf --connect-timeout 10 --max-time 30 "${BLOCKSCOUT_URL%/}/api?module=contract&action=getabi&address=${CONTRACT_ADDR}" 2>/dev/null) || true
API_STATUS=$(echo "$API_RESULT" | jq -r '.status // "0"' 2>/dev/null) || API_STATUS="0"
Comment thread
RonTuretzky marked this conversation as resolved.

if [[ "$API_STATUS" == "1" ]]; then
Expand Down
40 changes: 34 additions & 6 deletions scripts/upgrade-safety/validate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ set -euo pipefail
: "${BASE_BRANCH:?BASE_BRANCH is required}"
: "${PACKAGE_MANAGER:?PACKAGE_MANAGER is required}"
CURRENT_BUILD="${CURRENT_BUILD:-out/build-info}"
ALLOW_FALLBACK="${ALLOW_FALLBACK:-false}"
OZ_UPGRADES_CORE_VERSION="${OZ_UPGRADES_CORE_VERSION:-1.44.2}"

# Check if config exists
if [[ ! -f "$UPGRADES_CONFIG" ]]; then
Expand All @@ -37,9 +39,25 @@ if ! jq -e 'has("contracts")' "$UPGRADES_CONFIG" > /dev/null 2>&1; then
exit 1
fi

# Check for empty contracts array
# Check for empty contracts array — fail if base branch has entries (prevents bypass via emptying the config)
Comment thread
RonTuretzky marked this conversation as resolved.
Outdated
CONTRACT_COUNT=$(jq '.contracts | length' "$UPGRADES_CONFIG")
if [[ "$CONTRACT_COUNT" -eq 0 ]]; then
git fetch origin "$BASE_BRANCH" 2>/dev/null || true
BASE_CONTRACT_COUNT=0
if git show "origin/${BASE_BRANCH}:${UPGRADES_CONFIG}" > /dev/null 2>&1; then
BASE_CONTRACT_COUNT=$(git show "origin/${BASE_BRANCH}:${UPGRADES_CONFIG}" | jq '.contracts | length' 2>/dev/null) || BASE_CONTRACT_COUNT=0
Comment thread
RonTuretzky marked this conversation as resolved.
Outdated
fi

if [[ "$BASE_CONTRACT_COUNT" -gt 0 ]]; then
echo "::error::$UPGRADES_CONFIG has 0 contracts but base branch ($BASE_BRANCH) has $BASE_CONTRACT_COUNT — refusing to skip validation"
{
echo "## Upgrade Safety Validation"
echo ""
echo "> **Error:** \`$UPGRADES_CONFIG\` has no contracts, but the base branch has $BASE_CONTRACT_COUNT. This looks like an attempt to bypass validation."
} >> "$GITHUB_STEP_SUMMARY"
exit 1
fi

echo "::warning::No contracts defined in $UPGRADES_CONFIG — skipping upgrade safety validation"
{
echo "## Upgrade Safety Validation"
Expand Down Expand Up @@ -84,10 +102,20 @@ if [[ "$NEEDS_BASE" == "true" ]]; then
BASE_BUILD="${BASE_DIR}/out/base-build-info"
echo "Base branch built successfully"
else
echo "::warning::Failed to build base branch — contracts without explicit reference will be validated for upgradeability only"
if [[ "$ALLOW_FALLBACK" == "true" ]]; then
echo "::warning::Failed to build base branch — contracts without explicit reference will be validated for upgradeability only (ALLOW_FALLBACK=true)"
else
echo "::error::Failed to build base branch. Set ALLOW_FALLBACK=true to downgrade to upgradeability-only validation."
exit 1
fi
fi
else
echo "::warning::Could not checkout base branch '$BASE_BRANCH' — contracts without explicit reference will be validated for upgradeability only"
if [[ "$ALLOW_FALLBACK" == "true" ]]; then
echo "::warning::Could not checkout base branch '$BASE_BRANCH' — contracts without explicit reference will be validated for upgradeability only (ALLOW_FALLBACK=true)"
else
echo "::error::Could not checkout base branch '$BASE_BRANCH'. Set ALLOW_FALLBACK=true to downgrade to upgradeability-only validation."
exit 1
fi
fi
fi

Expand All @@ -107,7 +135,7 @@ while IFS= read -r entry; do
# === Base branch comparison (default) ===
if [[ -n "$BASE_BUILD" ]] && git show "origin/${BASE_BRANCH}:${CONTRACT_PATH}" > /dev/null 2>&1; then
# Contract exists on base branch — compare storage layouts
if OUTPUT=$(npx @openzeppelin/upgrades-core validate "$CURRENT_BUILD" \
if OUTPUT=$(npx @openzeppelin/upgrades-core@"$OZ_UPGRADES_CORE_VERSION" validate "$CURRENT_BUILD" \
--contract "$CONTRACT" \
--reference "base-build-info:${CONTRACT}" \
--referenceBuildInfoDirs "$BASE_BUILD" 2>&1); then
Expand All @@ -122,7 +150,7 @@ while IFS= read -r entry; do
else
# Contract doesn't exist on base branch or base build failed — validate upgradeability only
echo "Contract not found on $BASE_BRANCH or base build unavailable, validating upgradeability only..."
if OUTPUT=$(npx @openzeppelin/upgrades-core validate "$CURRENT_BUILD" \
if OUTPUT=$(npx @openzeppelin/upgrades-core@"$OZ_UPGRADES_CORE_VERSION" validate "$CURRENT_BUILD" \
--contract "$CONTRACT" 2>&1); then
echo "$OUTPUT"
SUMMARY="${SUMMARY}| \`${CONTRACT}\` | (new contract) | Pass |"$'\n'
Expand All @@ -137,7 +165,7 @@ while IFS= read -r entry; do
elif echo "$REF_VALUE" | jq -e 'type == "string"' > /dev/null 2>&1; then
# === Contract qualifier reference ===
QUALIFIER=$(echo "$entry" | jq -r '.reference')
if OUTPUT=$(npx @openzeppelin/upgrades-core validate "$CURRENT_BUILD" \
if OUTPUT=$(npx @openzeppelin/upgrades-core@"$OZ_UPGRADES_CORE_VERSION" validate "$CURRENT_BUILD" \
--contract "$CONTRACT" \
--reference "$QUALIFIER" 2>&1); then
echo "$OUTPUT"
Expand Down
Loading