diff --git a/README.md b/README.md index ec5a00d..3ba4119 100644 --- a/README.md +++ b/README.md @@ -104,6 +104,21 @@ Each entry specifies a contract to validate. The `reference` field controls what } ``` +#### Removing entries + +By default, removing a contract from `upgrades.json` is a hard error — without this guard, deletions could silently bypass the upgrade-safety check. To intentionally drop entries (e.g. retiring a contract), set the top-level `"dangerous": true` flag in the same PR: + +```json +{ + "dangerous": true, + "contracts": [ + { "contract": "src/Greeter.sol:Greeter" } + ] +} +``` + +Removals will be reported as a warning instead. Reset the flag back to `false` (or drop it) in a follow-up PR. + #### 3. Use the workflow ```yaml diff --git a/scripts/upgrade-safety/validate-config.sh b/scripts/upgrade-safety/validate-config.sh index 32589ea..fa27c06 100755 --- a/scripts/upgrade-safety/validate-config.sh +++ b/scripts/upgrade-safety/validate-config.sh @@ -3,6 +3,9 @@ set -euo pipefail # Validate that no contracts were removed from the upgrades config compared to the base branch. # Compares by .contract field only. Prevents bypassing upgrade safety by removing entries. # +# Set top-level "dangerous": true in upgrades.json to opt out of the removal guard for that PR +# (removals are reported as a warning instead of an error). +# # Env inputs: # UPGRADES_CONFIG — required, path to upgrades.json # BASE_BRANCH — required, branch name to compare against @@ -34,6 +37,7 @@ BASE_CONFIG=$(git show "origin/${BASE_BRANCH}:${UPGRADES_CONFIG}") || { CURRENT_CONTRACTS=$(jq -r '[.contracts[].contract]' "$UPGRADES_CONFIG") BASE_CONTRACTS=$(echo "$BASE_CONFIG" | jq -r '[.contracts[].contract]') +DANGEROUS=$(jq -r '.dangerous == true' "$UPGRADES_CONFIG") # Find base branch contracts missing from current config REMOVED=$(jq -n --argjson base "$BASE_CONTRACTS" --argjson current "$CURRENT_CONTRACTS" \ @@ -43,16 +47,30 @@ REMOVED_COUNT=$(echo "$REMOVED" | jq 'length') if [[ "$REMOVED_COUNT" -gt 0 ]]; then REMOVED_LIST=$(echo "$REMOVED" | jq -r '.[]') - echo "::error::$REMOVED_COUNT contract(s) removed from $UPGRADES_CONFIG compared to base branch ($BASE_BRANCH):" - echo "$REMOVED_LIST" - { - echo "## Upgrade Config Validation" - echo "" - echo "> **Error:** $REMOVED_COUNT contract(s) were removed from \`$UPGRADES_CONFIG\`:" - echo "" - echo "$REMOVED_LIST" | while read -r c; do echo "- \`$c\`"; done - } >> "$GITHUB_STEP_SUMMARY" - exit 1 + if [[ "$DANGEROUS" == "true" ]]; then + echo "::warning::$REMOVED_COUNT contract(s) removed from $UPGRADES_CONFIG compared to base branch ($BASE_BRANCH) — allowed by \"dangerous\": true:" + echo "$REMOVED_LIST" + { + echo "## Upgrade Config Validation" + echo "" + echo "> **Warning:** $REMOVED_COUNT contract(s) were removed from \`$UPGRADES_CONFIG\` — allowed by \`\"dangerous\": true\`:" + echo "" + echo "$REMOVED_LIST" | while read -r c; do echo "- \`$c\`"; done + } >> "$GITHUB_STEP_SUMMARY" + else + echo "::error::$REMOVED_COUNT contract(s) removed from $UPGRADES_CONFIG compared to base branch ($BASE_BRANCH):" + echo "$REMOVED_LIST" + { + echo "## Upgrade Config Validation" + echo "" + echo "> **Error:** $REMOVED_COUNT contract(s) were removed from \`$UPGRADES_CONFIG\`:" + echo "" + echo "$REMOVED_LIST" | while read -r c; do echo "- \`$c\`"; done + echo "" + echo "> Set top-level \`\"dangerous\": true\` in \`$UPGRADES_CONFIG\` to allow this removal." + } >> "$GITHUB_STEP_SUMMARY" + exit 1 + fi fi CURRENT_COUNT=$(echo "$CURRENT_CONTRACTS" | jq 'length')