diff --git a/.github/workflows/_deploy-testnet.yml b/.github/workflows/_deploy-testnet.yml index 4299ec6..dc82296 100644 --- a/.github/workflows/_deploy-testnet.yml +++ b/.github/workflows/_deploy-testnet.yml @@ -50,6 +50,9 @@ jobs: deploy-testnet: name: Deploy to Testnet runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write outputs: network_name: ${{ steps.network.outputs.network_name }} blockscout_url: ${{ steps.network.outputs.blockscout_url }} @@ -140,6 +143,20 @@ jobs: SUMMARY_TITLE: Testnet Deployment Summary run: bash .etherform/scripts/deploy/deployment-summary.sh + - name: Write deployment comment + if: github.event_name == 'pull_request' + env: + BLOCKSCOUT_URL: ${{ steps.network.outputs.blockscout_url }} + NETWORK_NAME: ${{ steps.network.outputs.network_name }} + run: bash .etherform/scripts/deploy/deployment-comment.sh + + - name: Post deployment comment + if: github.event_name == 'pull_request' + uses: marocchino/sticky-pull-request-comment@v2 + with: + header: testnet-deployment-${{ steps.network.outputs.network_name }} + path: deployment-comment.md + verify-contracts: name: Verify Contracts runs-on: ubuntu-latest diff --git a/.github/workflows/_foundry-cicd.yml b/.github/workflows/_foundry-cicd.yml index e88d73c..b367cb8 100644 --- a/.github/workflows/_foundry-cicd.yml +++ b/.github/workflows/_foundry-cicd.yml @@ -441,6 +441,9 @@ jobs: deploy-testnet: name: Deploy Testnet runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: write needs: [detect-changes, ci, upgrade-safety] if: | always() && @@ -525,6 +528,20 @@ jobs: SUMMARY_TITLE: Testnet Deployment Summary run: bash .etherform/scripts/deploy/deployment-summary.sh + - name: Write deployment comment + if: github.event_name == 'pull_request' + env: + BLOCKSCOUT_URL: ${{ steps.network.outputs.blockscout_url }} + NETWORK_NAME: ${{ steps.network.outputs.network_name }} + run: bash .etherform/scripts/deploy/deployment-comment.sh + + - name: Post deployment comment + if: github.event_name == 'pull_request' + uses: marocchino/sticky-pull-request-comment@v2 + with: + header: testnet-deployment-${{ steps.network.outputs.network_name }} + path: deployment-comment.md + verify-contracts: name: Verify Contracts runs-on: ubuntu-latest diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a22c56e..75da3db 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -35,6 +35,9 @@ jobs: - name: Test deployment-summary run: bash tests/test-deployment-summary.sh + - name: Test deployment-comment + run: bash tests/test-deployment-comment.sh + - name: Test extract-summary run: bash tests/test-extract-summary.sh diff --git a/scripts/deploy/deployment-comment.sh b/scripts/deploy/deployment-comment.sh new file mode 100755 index 0000000..d04e703 --- /dev/null +++ b/scripts/deploy/deployment-comment.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +set -euo pipefail +# Render the deployment address table as a sticky-PR-comment markdown file. +# +# Env inputs: +# BLOCKSCOUT_URL — required +# NETWORK_NAME — optional, included in the title when set +# OUTPUT_FILE — optional, defaults to deployment-comment.md +# +# File inputs: +# deployment-summary.txt must exist in the working directory + +: "${BLOCKSCOUT_URL:?BLOCKSCOUT_URL is required}" +NETWORK_NAME="${NETWORK_NAME:-}" +OUTPUT_FILE="${OUTPUT_FILE:-deployment-comment.md}" + +if [[ -n "$NETWORK_NAME" ]]; then + TITLE="Testnet Deployment — $NETWORK_NAME" +else + TITLE="Testnet Deployment" +fi + +{ + echo "## $TITLE" + echo "" + echo "| Contract | Address | Explorer |" + echo "|----------|---------|----------|" + + while read -r line; do + CONTRACT=$(echo "$line" | cut -d: -f1) + ADDRESS=$(echo "$line" | cut -d: -f2 | tr -d ' ') + echo "| $CONTRACT | \`$ADDRESS\` | [View](${BLOCKSCOUT_URL}/address/$ADDRESS) |" + done < deployment-summary.txt +} > "$OUTPUT_FILE" diff --git a/tests/test-deployment-comment.sh b/tests/test-deployment-comment.sh new file mode 100644 index 0000000..8d3dc08 --- /dev/null +++ b/tests/test-deployment-comment.sh @@ -0,0 +1,118 @@ +#!/usr/bin/env bash +set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)" +FAILURES=0 + +echo "=== Testing scripts/deploy/deployment-comment.sh ===" + +# Test 1: Renders markdown table from deployment-summary.txt +( + TMPDIR=$(mktemp -d) + trap 'rm -rf "$TMPDIR"' EXIT + cd "$TMPDIR" + + cat > deployment-summary.txt <<'EOF' +MyToken: 0x1234567890abcdef1234567890abcdef12345678 +MyProxy: 0xabcdefabcdefabcdefabcdefabcdefabcdefabcd +EOF + + export BLOCKSCOUT_URL="https://eth-sepolia.blockscout.com" + + bash "$SCRIPT_DIR/scripts/deploy/deployment-comment.sh" + + [[ -f deployment-comment.md ]] || { echo "FAIL: default output file not created"; exit 1; } + grep -q "## Testnet Deployment" deployment-comment.md || { echo "FAIL: missing title"; exit 1; } + grep -q "MyToken" deployment-comment.md || { echo "FAIL: MyToken row missing"; exit 1; } + grep -q "MyProxy" deployment-comment.md || { echo "FAIL: MyProxy row missing"; exit 1; } + grep -q "https://eth-sepolia.blockscout.com/address/0x1234567890abcdef1234567890abcdef12345678" deployment-comment.md \ + || { echo "FAIL: explorer link missing or wrong"; exit 1; } + + echo "PASS: renders markdown table" +) || { FAILURES=$((FAILURES + 1)); } + +# Test 2: Includes network name in title when set +( + TMPDIR=$(mktemp -d) + trap 'rm -rf "$TMPDIR"' EXIT + cd "$TMPDIR" + + cat > deployment-summary.txt <<'EOF' +MyToken: 0x1234567890abcdef1234567890abcdef12345678 +EOF + + export BLOCKSCOUT_URL="https://eth-sepolia.blockscout.com" + export NETWORK_NAME="sepolia" + + bash "$SCRIPT_DIR/scripts/deploy/deployment-comment.sh" + + grep -q "## Testnet Deployment — sepolia" deployment-comment.md \ + || { echo "FAIL: network name not in title"; exit 1; } + + echo "PASS: network name appears in title" +) || { FAILURES=$((FAILURES + 1)); } + +# Test 3: Omits network name from title when unset/empty +( + TMPDIR=$(mktemp -d) + trap 'rm -rf "$TMPDIR"' EXIT + cd "$TMPDIR" + + cat > deployment-summary.txt <<'EOF' +MyToken: 0x1234567890abcdef1234567890abcdef12345678 +EOF + + export BLOCKSCOUT_URL="https://eth-sepolia.blockscout.com" + export NETWORK_NAME="" + + bash "$SCRIPT_DIR/scripts/deploy/deployment-comment.sh" + + FIRST_LINE=$(head -1 deployment-comment.md) + [[ "$FIRST_LINE" == "## Testnet Deployment" ]] \ + || { echo "FAIL: expected plain title, got: $FIRST_LINE"; exit 1; } + + echo "PASS: omits network name when unset" +) || { FAILURES=$((FAILURES + 1)); } + +# Test 4: Honors OUTPUT_FILE +( + TMPDIR=$(mktemp -d) + trap 'rm -rf "$TMPDIR"' EXIT + cd "$TMPDIR" + + cat > deployment-summary.txt <<'EOF' +MyToken: 0x1234567890abcdef1234567890abcdef12345678 +EOF + + export BLOCKSCOUT_URL="https://eth-sepolia.blockscout.com" + export OUTPUT_FILE="custom-name.md" + + bash "$SCRIPT_DIR/scripts/deploy/deployment-comment.sh" + + [[ -f custom-name.md ]] || { echo "FAIL: custom OUTPUT_FILE not honored"; exit 1; } + [[ ! -f deployment-comment.md ]] || { echo "FAIL: default file written despite OUTPUT_FILE"; exit 1; } + + echo "PASS: writes to custom OUTPUT_FILE" +) || { FAILURES=$((FAILURES + 1)); } + +# Test 5: Fails when BLOCKSCOUT_URL is missing +( + TMPDIR=$(mktemp -d) + trap 'rm -rf "$TMPDIR"' EXIT + cd "$TMPDIR" + + cat > deployment-summary.txt <<'EOF' +MyToken: 0x1234567890abcdef1234567890abcdef12345678 +EOF + + unset BLOCKSCOUT_URL || true + + if bash "$SCRIPT_DIR/scripts/deploy/deployment-comment.sh" 2>/dev/null; then + echo "FAIL: expected non-zero exit when BLOCKSCOUT_URL missing" + exit 1 + fi + + echo "PASS: fails fast when BLOCKSCOUT_URL missing" +) || { FAILURES=$((FAILURES + 1)); } + +echo "--- $((5 - FAILURES))/5 tests passed ---" +exit $FAILURES