name: Size Report

on:
  pull_request:

permissions:
  contents: read
  pull-requests: write

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}
  cancel-in-progress: true

jobs:
  size-report:
    name: Size Report
    runs-on: ubuntu-latest

    steps:
      - name: checkout
        uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v4
        with:
          submodules: true
          fetch-depth: 0
          persist-credentials: false

      - name: setup deno
        uses: denoland/setup-deno@22d081ff2d3a40755e97629de92e3bcbfa7cf2ed # v2.0.5
        with:
          # Pinned to v2.8.3 to avoid https://github.com/denoland/deno/issues/35529
          # (v2.9.0 rejects JSR files without a manifest checksum on warm cache).
          # Restore to v2.x once v2.9.1 ships.
          deno-version: v2.8.3

      - name: setup node
        uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v4
        with:
          node-version: 24

      - name: build (head)
        run: make clean && make && deno task build:npm 0.0.0

      - name: measure sizes (head)
        run: |
          cp tasks/measure-size.ts /tmp/measure-size.ts
          deno run --allow-read /tmp/measure-size.ts > /tmp/head-sizes.json

      - name: find merge base
        id: base
        run: |
          SHA=$(git merge-base HEAD origin/${{ github.base_ref }})
          echo "sha=$SHA" >> $GITHUB_OUTPUT

      - name: checkout base commit
        run: |
          git checkout ${{ steps.base.outputs.sha }}
          git submodule update --init --recursive

      - name: build (base)
        run: make clean && make && deno task build:npm 0.0.0

      - name: measure sizes (base)
        run: deno run --allow-read /tmp/measure-size.ts > /tmp/base-sizes.json

      - name: post size report
        uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
        with:
          script: |
            const fs = require('fs');
            const head = JSON.parse(fs.readFileSync('/tmp/head-sizes.json', 'utf8'));
            const base = JSON.parse(fs.readFileSync('/tmp/base-sizes.json', 'utf8'));

            const total = (arr) => arr.reduce((acc, f) => acc + f.size, 0);
            const headTotal = total(head);
            const baseTotal = total(base);

            const delta = headTotal - baseTotal;

            const fmt = (n) => `${(n / 1024).toFixed(1)} KB`;
            const fmtDelta = (n) =>
              `${n > 0 ? '+' : ''}${(n / 1024).toFixed(1)} KB`;

            if (delta === 0) return;

            const { data: comments } = await github.rest.issues.listComments({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: context.issue.number,
            });

            const existing = comments.find(c => c.body.startsWith('<!-- size-report -->'));

            // Skip if the head sizes haven't changed since the last push
            if (existing) {
              const match = existing.body.match(/<!-- sizes:(\d+) -->/);
              if (match && Number(match[1]) === headTotal) return;
            }

            const takeaway = delta < 0 ? 'Size Reduced' : 'Size Increased';

            const body = [
              '<!-- size-report -->',
              `<!-- sizes:${headTotal} -->`,
              `**${takeaway}** — ${fmtDelta(delta)}`,
              '',
              `${fmt(headTotal)} unpacked`,
            ].join('\n');

            if (existing) {
              await github.rest.issues.updateComment({
                owner: context.repo.owner,
                repo: context.repo.repo,
                comment_id: existing.id,
                body,
              });
            } else {
              await github.rest.issues.createComment({
                owner: context.repo.owner,
                repo: context.repo.repo,
                issue_number: context.issue.number,
                body,
              });
            }
