From 030a2812c1ae955585b71478f52acd2c617c9c6d Mon Sep 17 00:00:00 2001 From: aboudjem Date: Wed, 29 May 2024 15:45:46 +0400 Subject: [PATCH] refactor: Update coverage script to use --ir-minimum flag --- .github/workflows/ci.yml | 38 +++- .github/workflows/generate-gas-report.yml | 41 ++++ scripts/foundry/generateGasReport.js | 210 ++++++++++---------- scripts/foundry/generate_coverage_report.sh | 2 +- 4 files changed, 186 insertions(+), 105 deletions(-) create mode 100644 .github/workflows/generate-gas-report.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 85eb26e1..c63103fe 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -171,4 +171,40 @@ jobs: console.log("Slither report is empty. No comment will be posted."); return; } - await script({ github, context, header, body }) \ No newline at end of file + await script({ github, context, header, body }) + + gas_report: + needs: setup + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4.1.6 + + - name: Cache node modules + uses: actions/cache@v4 + with: + path: | + **/node_modules + key: ${{ needs.setup.outputs.cache-key }} + + - name: Generate gas report + run: yarn run gas-report + + - name: Commit and push gas report + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + git config --global user.name "github-actions[bot]" + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git add gas_report.md + git commit -m "Update gas report" || echo "No changes to gas report" + git push origin HEAD:${{ github.event.pull_request.head.ref }} + + - name: Post gas report comment + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gas_report=$(cat gas_report.md) + curl -s -H "Authorization: token $GITHUB_TOKEN" -X POST \ + -d "{\"body\":\"## Gas Report\n\n${gas_report}\"}" \ + "https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments" diff --git a/.github/workflows/generate-gas-report.yml b/.github/workflows/generate-gas-report.yml new file mode 100644 index 00000000..89cc1165 --- /dev/null +++ b/.github/workflows/generate-gas-report.yml @@ -0,0 +1,41 @@ +name: Generate Gas Report + +on: + pull_request: + types: [opened, synchronize] + +jobs: + generate-gas-report: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + + - name: Set up Node.js + uses: actions/setup-node@v3 + with: + node-version: "16" + + - name: Install dependencies + run: yarn install + + - name: Generate gas report + run: yarn run gas-report + + - name: Commit and push gas report + run: | + git config --global user.name "github-actions[bot]" + git config --global user.email "github-actions[bot]@users.noreply.github.com" + git add gas_report.md + git commit -m "Update gas report" || echo "No changes to gas report" + git push origin HEAD:${{ github.event.pull_request.head.ref }} + + - name: Post gas report comment + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gas_report=$(cat gas_report.md) + curl -s -H "Authorization: token $GITHUB_TOKEN" -X POST \ + -d "{\"body\":\"## Gas Report\n\n${gas_report}\"}" \ + "https://api.github.com/repos/${{ github.repository }}/issues/${{ github.event.pull_request.number }}/comments" diff --git a/scripts/foundry/generateGasReport.js b/scripts/foundry/generateGasReport.js index 8f6297d7..01bb4a96 100644 --- a/scripts/foundry/generateGasReport.js +++ b/scripts/foundry/generateGasReport.js @@ -1,121 +1,125 @@ -const fs = require('fs'); -const readline = require('readline'); -const { exec } = require('child_process'); +const fs = require("fs"); +const readline = require("readline"); +const { exec } = require("child_process"); // Define the log file and the output markdown file -const LOG_FILE = 'gas.log'; -const OUTPUT_FILE = 'gas_report.md'; +const LOG_FILE = "gas.log"; +const OUTPUT_FILE = "gas_report.md"; // Function to execute the `forge test` command function runForgeTest() { - return new Promise((resolve, reject) => { - console.log('🚀 Running forge tests, this may take a few minutes...'); - exec('forge test -vv --mt test_Gas > gas.log', (error, stdout, stderr) => { - if (error) { - console.error(`❌ Exec error: ${error}`); - reject(`exec error: ${error}`); - } - console.log('✅ Forge tests completed.'); - resolve(stdout ? stdout : stderr); - }); + return new Promise((resolve, reject) => { + console.log("🚀 Running forge tests, this may take a few minutes..."); + exec("forge test -vv --mt test_Gas > gas.log", (error, stdout, stderr) => { + if (error) { + console.error(`❌ Exec error: ${error}`); + reject(`exec error: ${error}`); + } + console.log("✅ Forge tests completed."); + resolve(stdout ? stdout : stderr); }); + }); } // Function to parse the log file and generate the report async function generateReport() { - await runForgeTest(); - - const fileStream = fs.createReadStream(LOG_FILE); - const rl = readline.createInterface({ - input: fileStream, - crlfDelay: Infinity - }); - - const results = []; - - console.log('📄 Parsing log file, please wait...'); - for await (const line of rl) { - console.log(line); - if (line.includes('::')) { - const parts = line.split('::'); - const PROTOCOL = parts[0]; - const ACTION_FUNCTION = parts[1]; - let ACCOUNT_TYPE; - let IS_DEPLOYED; - if (line.includes('EOA')) { - ACCOUNT_TYPE = 'EOA'; - IS_DEPLOYED = 'False'; - } else if (line.includes('Nexus')) { - ACCOUNT_TYPE = 'Smart Account'; - IS_DEPLOYED = 'True'; - } else { - ACCOUNT_TYPE = 'Smart Account'; - IS_DEPLOYED = 'False'; - } - - const WITH_PAYMASTER = line.includes('WithPaymaster') ? 'True' : 'False'; - - const GAS_INFO = parts[4]; - const ACCESS_TYPE = GAS_INFO.split(': ')[0]; - const GAS_USED = GAS_INFO.split(': ')[1]; - - let RECEIVER_ACCESS; - if (ACCESS_TYPE === 'ColdAccess') { - RECEIVER_ACCESS = '🧊 ColdAccess'; - } else if (ACCESS_TYPE === 'WarmAccess') { - RECEIVER_ACCESS = '🔥 WarmAccess'; - } else { - RECEIVER_ACCESS = 'N/A'; - } - - results.push({ - PROTOCOL, - ACTION_FUNCTION, - ACCOUNT_TYPE, - IS_DEPLOYED, - WITH_PAYMASTER, - RECEIVER_ACCESS, - GAS_USED, - FULL_LOG: line.trim() - }); - } + await runForgeTest(); + + const fileStream = fs.createReadStream(LOG_FILE); + const rl = readline.createInterface({ + input: fileStream, + crlfDelay: Infinity, + }); + + const results = []; + + console.log("📄 Parsing log file, please wait..."); + for await (const line of rl) { + console.log(line); + if (line.includes("::")) { + const parts = line.split("::"); + const PROTOCOL = parts[0]; + const ACTION_FUNCTION = parts[1]; + let ACCOUNT_TYPE; + let IS_DEPLOYED; + if (line.includes("EOA")) { + ACCOUNT_TYPE = "EOA"; + IS_DEPLOYED = "False"; + } else if (line.includes("Nexus")) { + ACCOUNT_TYPE = "Smart Account"; + IS_DEPLOYED = "True"; + } else { + ACCOUNT_TYPE = "Smart Account"; + IS_DEPLOYED = "False"; + } + + const WITH_PAYMASTER = line.includes("WithPaymaster") ? "True" : "False"; + + const GAS_INFO = parts[4]; + const ACCESS_TYPE = GAS_INFO.split(": ")[0]; + const GAS_USED = GAS_INFO.split(": ")[1]; + + let RECEIVER_ACCESS; + if (ACCESS_TYPE === "ColdAccess") { + RECEIVER_ACCESS = "🧊 ColdAccess"; + } else if (ACCESS_TYPE === "WarmAccess") { + RECEIVER_ACCESS = "🔥 WarmAccess"; + } else { + RECEIVER_ACCESS = "N/A"; + } + + results.push({ + PROTOCOL, + ACTION_FUNCTION, + ACCOUNT_TYPE, + IS_DEPLOYED, + WITH_PAYMASTER, + RECEIVER_ACCESS, + GAS_USED, + FULL_LOG: line.trim(), + }); } - - console.log('🔄 Sorting results...'); - // Custom sort: Group by protocol alphabetically, then by EOA first, Smart Account with Is Deployed=True next, then the rest - results.sort((a, b) => { - if (a.PROTOCOL < b.PROTOCOL) return -1; - if (a.PROTOCOL > b.PROTOCOL) return 1; - if (a.ACCOUNT_TYPE === 'EOA' && b.ACCOUNT_TYPE !== 'EOA') return -1; - if (a.ACCOUNT_TYPE !== 'EOA' && b.ACCOUNT_TYPE === 'EOA') return 1; - if (a.IS_DEPLOYED === 'True' && b.IS_DEPLOYED !== 'True') return -1; - if (a.IS_DEPLOYED !== 'True' && b.IS_DEPLOYED === 'True') return 1; - return 0; - }); - - console.log('🖋️ Writing report...'); - // Write the report - const outputStream = fs.createWriteStream(OUTPUT_FILE); - outputStream.write("# Gas Report\n"); - outputStream.write("| **Protocol** | **Actions / Function** | **Account Type** | **Is Deployed** | **With Paymaster?** | **Receiver Access** | **Gas Used** | **Full Log** |\n"); - outputStream.write("|:------------:|:---------------------:|:----------------:|:--------------:|:-------------------:|:-------------------:|:------------:|:-------------:|\n"); - - results.forEach(result => { - outputStream.write(`| ${result.PROTOCOL} | ${result.ACTION_FUNCTION} | ${result.ACCOUNT_TYPE} | ${result.IS_DEPLOYED} | ${result.WITH_PAYMASTER} | ${result.RECEIVER_ACCESS} | ${result.GAS_USED} | ${result.FULL_LOG} |\n`); - }); - - console.log(`📊 Gas report generated and saved to ${OUTPUT_FILE}`); + } + + console.log("🔄 Sorting results..."); + // Custom sort: Group by protocol alphabetically, then by EOA first, Smart Account with Is Deployed=True next, then the rest + results.sort((a, b) => { + if (a.PROTOCOL < b.PROTOCOL) return -1; + if (a.PROTOCOL > b.PROTOCOL) return 1; + if (a.ACCOUNT_TYPE === "EOA" && b.ACCOUNT_TYPE !== "EOA") return -1; + if (a.ACCOUNT_TYPE !== "EOA" && b.ACCOUNT_TYPE === "EOA") return 1; + if (a.IS_DEPLOYED === "True" && b.IS_DEPLOYED !== "True") return -1; + if (a.IS_DEPLOYED !== "True" && b.IS_DEPLOYED === "True") return 1; + return 0; + }); + + console.log("🖋️ Writing report..."); + // Write the report + const outputStream = fs.createWriteStream(OUTPUT_FILE); + outputStream.write("# Gas Report\n"); + outputStream.write( + "| **Protocol** | **Actions / Function** | **Account Type** | **Is Deployed** | **With Paymaster?** | **Receiver Access** | **Gas Used** | **Full Log** |\n", + ); + outputStream.write( + "|:------------:|:---------------------:|:----------------:|:--------------:|:-------------------:|:-------------------:|:------------:|:-------------:|\n", + ); + + results.forEach((result) => { + outputStream.write( + `| ${result.PROTOCOL} | ${result.ACTION_FUNCTION} | ${result.ACCOUNT_TYPE} | ${result.IS_DEPLOYED} | ${result.WITH_PAYMASTER} | ${result.RECEIVER_ACCESS} | ${result.GAS_USED} | ${result.FULL_LOG} |\n`, + ); + }); + + console.log(`📊 Gas report generated and saved to ${OUTPUT_FILE}`); } // Function to clean up temporary files function cleanUp() { - fs.unlink(LOG_FILE, (err) => { - if (err) console.error(`❌ Error deleting ${LOG_FILE}: ${err}`); - else console.log(`🗑️ ${LOG_FILE} deleted successfully.`); - }); + fs.unlink(LOG_FILE, (err) => { + if (err) console.error(`❌ Error deleting ${LOG_FILE}: ${err}`); + else console.log(`🗑️ ${LOG_FILE} deleted successfully.`); + }); } // Run the function to generate the report and then clean up -generateReport() - .then(cleanUp) - .catch(console.error); +generateReport().then(cleanUp).catch(console.error); diff --git a/scripts/foundry/generate_coverage_report.sh b/scripts/foundry/generate_coverage_report.sh index ba32662d..426d2c7c 100755 --- a/scripts/foundry/generate_coverage_report.sh +++ b/scripts/foundry/generate_coverage_report.sh @@ -1,7 +1,7 @@ #!/bin/bash # Generate lcov.info -forge coverage --report lcov +forge coverage --ir-minimum --report lcov # Install lcov if not installed if ! command -v lcov &>/dev/null; then