feat: add CI output comparison between branches #82
This file contains hidden or 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: CI | |
| on: | |
| push: | |
| branches: [ main ] | |
| pull_request: | |
| jobs: | |
| build-and-test: | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: | |
| node-version: [18.x] | |
| package: | |
| - assertion-monitor | |
| - batch-poster-monitor | |
| - retryable-monitor | |
| - utils | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Use Node.js ${{ matrix.node-version }} | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ matrix.node-version }} | |
| cache: 'yarn' | |
| - name: Install dependencies | |
| run: yarn install --frozen-lockfile | |
| - name: Type check package | |
| run: | | |
| cd packages/${{ matrix.package }} | |
| if [ -f "tsconfig.json" ]; then | |
| echo "Type checking ${{ matrix.package }}..." | |
| npx tsc --noEmit | |
| else | |
| echo "No tsconfig.json found for ${{ matrix.package }}, skipping type check" | |
| fi | |
| - name: Build package | |
| run: | | |
| cd packages/${{ matrix.package }} | |
| if [ -f "package.json" ] && grep -q '"build"' package.json; then | |
| echo "Building ${{ matrix.package }}..." | |
| yarn build | |
| else | |
| echo "No build script found for ${{ matrix.package }}, skipping build step" | |
| fi | |
| - name: Run tests | |
| run: | | |
| cd packages/${{ matrix.package }} | |
| if [ -f "package.json" ] && grep -q '"test"' package.json; then | |
| echo "Testing ${{ matrix.package }}..." | |
| yarn test | |
| else | |
| echo "No test script found for ${{ matrix.package }}, skipping test step" | |
| fi | |
| # Run root level tests as well | |
| root-tests: | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: | |
| node-version: [18.x] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Use Node.js ${{ matrix.node-version }} | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ matrix.node-version }} | |
| cache: 'yarn' | |
| - name: Install dependencies | |
| run: yarn install --frozen-lockfile | |
| - name: Run all tests | |
| run: yarn test | |
| # Smoke test - verify built packages actually run | |
| smoke-test: | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: | |
| node-version: [18.x, 22.x, latest] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Use Node.js ${{ matrix.node-version }} | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ matrix.node-version }} | |
| cache: 'yarn' | |
| - name: Install dependencies | |
| run: yarn install --frozen-lockfile | |
| - name: Smoke test batch-poster-monitor dev script | |
| run: | | |
| timeout 10s yarn workspace batch-poster-monitor dev || [ $? -eq 124 ] | |
| - name: Smoke test assertion-monitor dev script | |
| run: | | |
| timeout 10s yarn workspace assertion-monitor dev || [ $? -eq 124 ] | |
| - name: Smoke test retryable-monitor dev script | |
| run: | | |
| timeout 10s yarn workspace retryable-monitor dev || [ $? -eq 124 ] | |
| # Lint check for code quality | |
| lint: | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: | |
| node-version: [18.x] | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - name: Use Node.js ${{ matrix.node-version }} | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ matrix.node-version }} | |
| cache: 'yarn' | |
| - name: Install dependencies | |
| run: yarn install --frozen-lockfile | |
| - name: Run lint | |
| run: | | |
| if [ -f "package.json" ] && grep -q '"lint"' package.json; then | |
| yarn lint | |
| else | |
| echo "No lint script found at root level" | |
| fi | |
| # Output comparison - compares monitoring output between target branch and PR branch | |
| output-comparison: | |
| runs-on: ubuntu-latest | |
| if: github.event_name == 'pull_request' | |
| steps: | |
| - name: Checkout PR branch | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ github.head_ref }} | |
| path: pr-branch | |
| - name: Checkout target branch | |
| uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ github.base_ref }} | |
| path: target-branch | |
| - name: Use Node.js 18.x | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: 18.x | |
| - name: Run monitors on target branch | |
| run: | | |
| cd target-branch | |
| yarn install --frozen-lockfile | |
| # Build all monitors | |
| yarn workspace assertion-monitor build | |
| yarn workspace batch-poster-monitor build | |
| yarn workspace retryable-monitor build | |
| # Run each monitor and extract ONLY the summary output | |
| echo "=== ASSERTION MONITOR ===" > ../target-output.txt | |
| timeout 180s yarn workspace assertion-monitor dev --configPath=../../config.ci.json 2>&1 | \ | |
| grep -E "(Assertion Monitor Alert Summary|Monitoring complete - all chains healthy|No assertion creation events found)" >> ../target-output.txt || true | |
| echo "" >> ../target-output.txt | |
| echo "=== BATCH POSTER MONITOR ===" >> ../target-output.txt | |
| timeout 180s yarn workspace batch-poster-monitor dev --configPath=../../config.ci.json 2>&1 | \ | |
| sed -n '/Batch poster monitor summary/,$p' >> ../target-output.txt || true | |
| echo "" >> ../target-output.txt | |
| echo "=== RETRYABLE MONITOR ===" >> ../target-output.txt | |
| timeout 180s yarn workspace retryable-monitor dev --configPath=../../config.ci.json 2>&1 | \ | |
| grep -E "(No retryables found|Found [0-9]+ retryable|Processing child chains)" >> ../target-output.txt || true | |
| continue-on-error: true | |
| - name: Run monitors on PR branch | |
| run: | | |
| cd pr-branch | |
| yarn install --frozen-lockfile | |
| # Build all monitors | |
| yarn workspace assertion-monitor build | |
| yarn workspace batch-poster-monitor build | |
| yarn workspace retryable-monitor build | |
| # Run each monitor and extract ONLY the summary output | |
| echo "=== ASSERTION MONITOR ===" > ../pr-output.txt | |
| timeout 180s yarn workspace assertion-monitor dev --configPath=../../config.ci.json 2>&1 | \ | |
| grep -E "(Assertion Monitor Alert Summary|Monitoring complete - all chains healthy|No assertion creation events found)" >> ../pr-output.txt || true | |
| echo "" >> ../pr-output.txt | |
| echo "=== BATCH POSTER MONITOR ===" >> ../pr-output.txt | |
| timeout 180s yarn workspace batch-poster-monitor dev --configPath=../../config.ci.json 2>&1 | \ | |
| sed -n '/Batch poster monitor summary/,$p' >> ../pr-output.txt || true | |
| echo "" >> ../pr-output.txt | |
| echo "=== RETRYABLE MONITOR ===" >> ../pr-output.txt | |
| timeout 180s yarn workspace retryable-monitor dev --configPath=../../config.ci.json 2>&1 | \ | |
| grep -E "(No retryables found|Found [0-9]+ retryable|Processing child chains)" >> ../pr-output.txt || true | |
| continue-on-error: true | |
| - name: Normalize outputs and generate diff | |
| run: | | |
| # Normalize dynamic values (block numbers, addresses) for comparison | |
| normalize() { | |
| sed -E 's/[0-9]{7,}/BLOCK_NUM/g; s/[0-9]+n\b/BIGINT/g; s/0x[a-fA-F0-9]{40}/ADDR/g' | |
| } | |
| cat target-output.txt | normalize > target-normalized.txt | |
| cat pr-output.txt | normalize > pr-normalized.txt | |
| # Generate unified diff (ignore exit code since diff returns 1 when files differ) | |
| diff -u target-normalized.txt pr-normalized.txt > output-diff.txt || true | |
| - name: Post comment | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const fs = require('fs'); | |
| const diffOutput = fs.existsSync('output-diff.txt') | |
| ? fs.readFileSync('output-diff.txt', 'utf8').trim() | |
| : ''; | |
| const hasChanges = diffOutput.length > 0; | |
| let body = ''; | |
| if (hasChanges) { | |
| body = '### ⚠️ Monitor Output Changes\n\n```diff\n' + diffOutput + '\n```'; | |
| } else { | |
| body = '### ✅ Monitor Output: No Changes'; | |
| } | |
| // Find existing comment | |
| const { data: comments } = await github.rest.issues.listComments({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| }); | |
| const botComment = comments.find(comment => | |
| comment.user.type === 'Bot' && | |
| comment.body.includes('Monitor Output') | |
| ); | |
| if (botComment) { | |
| await github.rest.issues.updateComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| comment_id: botComment.id, | |
| body: body | |
| }); | |
| } else { | |
| await github.rest.issues.createComment({ | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| issue_number: context.issue.number, | |
| body: body | |
| }); | |
| } |