Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CI production build size summary #401

Merged
merged 18 commits into from
Dec 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 58 additions & 0 deletions .github/workflows/pr-manipulation.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: PR Comment Generation

on:
workflow_run:
workflows: ["Build and Test"]
types:
- completed

jobs:
comment_on_pr:
runs-on: ubuntu-latest
if: >
github.event.workflow_run.event == 'pull_request' &&
github.event.workflow_run.conclusion == 'success'
steps:
- name: 'Download artifact'
uses: actions/[email protected]
with:
script: |
const fs = require('fs');
const artifacts = await github.actions.listWorkflowRunArtifacts({
...context.repo,
run_id: ${{github.event.workflow_run.id }},
});
const matchArtifact = artifacts.data.artifacts.filter((artifact) => {
return artifact.name == "pr"
})[0];
const download = await github.actions.downloadArtifact({
...context.repo,
artifact_id: matchArtifact.id,
archive_format: 'zip',
});

fs.writeFileSync('${{github.workspace}}/pr.zip', Buffer.from(download.data));
- run: unzip pr.zip

- name: 'Comment on PR'
uses: actions/github-script@v3
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const fs = require('fs');
const issueNumber = Number(fs.readFileSync('./NR'));
const summaryContent = fs.readFileSync('./step_summary.md', 'utf-8');

const existingCommentsOpts = github.issues.listComments.endpoint.merge({
...context.repo, issue_number: issueNumber
});
const existingComments = await github.paginate(existingCommentsOpts);
const TAG = 'execution';
const tagPattern = `<!-- pr_asset_summary_comment "${TAG}" -->`;
const body = `${summaryContent}\n${tagPattern}`;
const preExistingComment = existingComments.find((comment) => comment.body?.includes(tagPattern));
if(preExistingComment) {
await github.issues.updateComment({ ...context.repo, comment_id: preExistingComment.id, body });
} else {
await github.issues.createComment({ ...context.repo, issue_number: issueNumber, body });
}
182 changes: 159 additions & 23 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,7 @@ jobs:
- uses: actions/checkout@v4

- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true
uses: dtolnay/rust-toolchain@stable

- name: Install
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
Expand All @@ -33,13 +29,10 @@ jobs:
- uses: actions/checkout@v4

- name: Install Rust
uses: actions-rs/toolchain@v1
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
profile: minimal
override: true
targets: wasm32-unknown-unknown

- run: rustup target add wasm32-unknown-unknown
- uses: Swatinem/rust-cache@v2

- run: cargo install cargo-all-features
Expand All @@ -53,11 +46,7 @@ jobs:
- uses: actions/checkout@v4

- name: Install Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
profile: minimal
override: true
uses: dtolnay/rust-toolchain@stable

- name: Install
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
Expand All @@ -84,11 +73,8 @@ jobs:
- uses: actions/checkout@v4

- name: Install Rust
uses: actions-rs/toolchain@v1
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
profile: minimal
override: true
components: rustfmt

- uses: Swatinem/rust-cache@v2
Expand All @@ -103,14 +89,164 @@ jobs:
- uses: actions/checkout@v4

- name: Install Rust
uses: actions-rs/toolchain@v1
uses: dtolnay/rust-toolchain@stable
with:
toolchain: stable
profile: minimal
override: true
components: clippy

- uses: Swatinem/rust-cache@v2

- name: "clippy --all"
run: cargo clippy --all --features=full --tests -- -D warnings

node-build-report:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Install Rust
uses: dtolnay/rust-toolchain@stable
with:
targets: wasm32-unknown-unknown

- name: Install
run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh

- uses: Swatinem/rust-cache@v2

- uses: actions/setup-node@v4
with:
node-version: "20"
- uses: awalsh128/cache-apt-pkgs-action@latest
with:
packages: brotli pv parallel jq
version: 1.0

- name: Build bundle
run: ./scripts/report_build.sh
- name: Size Reporting
run: |
ls report_pkg/*/*.wasm | parallel brotli -f -Z {}
mkdir -p ./pr
echo "| Asset | Size | Compressed Size |" >> ./pr/step_summary.md
echo "| ------ | ---- | --------------- |" >> ./pr/step_summary.md
for asset in $(ls report_pkg/*/*.wasm); do
export SIZE=$(stat --format '%s' $asset)
export COMPRESSED_SIZE=$(stat --format '%s' "${asset}.br")
export asset
echo "| ${asset} | $(echo $SIZE | numfmt --to=si --suffix="B") | $(echo $COMPRESSED_SIZE | numfmt --to=si --suffix="B") |" >> ./pr/step_summary.md
echo $(jq -n '{"asset": $ENV.asset, "size": $ENV.SIZE | tonumber, "compressed_size": $ENV.COMPRESSED_SIZE | tonumber}')
done | jq -s 'map({ (.asset|tostring): .}) | add' > ./pr/asset_manifest.json
echo ${{ github.event.number }} > ./pr/NR
if [[ "${{ github.event_type }}" != "pull_request" ]]; then
cat ./pr/step_summary.md > $GITHUB_STEP_SUMMARY
fi;
- uses: actions/upload-artifact@v3
with:
name: pr
path: pr/
delta_generation:
runs-on: ubuntu-latest
if: >
github.event_name == 'pull_request'
needs: node-build-report
steps:
- uses: actions/download-artifact@v3
with:
name: pr
path: pr/
- name: 'Generate size deltas'
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const { execSync } = require('child_process');
const baseContext = {
repo: {
repo: '${{ github.event.pull_request.base.repo.name }}',
owner: '${{ github.event.pull_request.base.repo.owner.login }}'
}
};
const baseWorkflows = await github.rest.actions.listWorkflowRuns({
...baseContext.repo,
branch: '${{ github.event.pull_request.base.ref }}',
status: 'success',
workflow_id: 'test.yml',
});
const matchWorkflow = baseWorkflows.data?.workflow_runs?.[0];
const artifacts = await github.rest.actions.listWorkflowRunArtifacts({
...baseContext.repo,
run_id: matchWorkflow?.id,
});
const matchArtifact = artifacts.data.artifacts.filter((artifact) => {
return artifact.name == "pr"
})[0];
if(matchArtifact) {
const download = await github.rest.actions.downloadArtifact({
...baseContext.repo,
artifact_id: matchArtifact.id,
archive_format: 'zip',
});

fs.writeFileSync('${{github.workspace}}/base.zip', Buffer.from(download.data));
execSync(`unzip -p base.zip asset_manifest.json >base_asset_manifest.json || true`);
}
// now, read in the asset manifests, for the head and base
let baseAssets = {};
try {
baseAssets = JSON.parse(fs.readFileSync('./base_asset_manifest.json'));
} catch (error) {
console.log('No base asset manifest found');
}
const assets = JSON.parse(fs.readFileSync('./pr/asset_manifest.json'));
const unitOptions = {
style: 'unit', unit: 'byte', unitDisplay: 'narrow', notation: 'compact',
maximumSignificantDigits: 3
};
const formatter = new Intl.NumberFormat('en-US', unitOptions);
const signedFormatter = new Intl.NumberFormat('en-US', { ...unitOptions, signDisplay: 'always' });
const percentFormatter = Intl.NumberFormat('en-US', { style: 'percent', signDisplay: 'always' });
const colorMap = {
'-1': 'green',
1: 'red',
0: 'black',
NaN: 'black'
};
// compute deltas and output markdown fragments
const lineFragments = Object.entries(assets).map(([k, v]) => {
const baseAsset = baseAssets[k] ?? {};
const { asset, size, compressed_size, size_delta, compressed_size_delta } = {
...v,
...Object.fromEntries(['size', 'compressed_size'].map(subK => {
// compute the percentage change, NaN if the asset wasn't available
const proportionalDelta = v?.[subK] / baseAsset?.[subK] - 1;
const absoluteDelta = v?.[subK] - baseAsset?.[subK]
const sign = Math.sign(proportionalDelta);
// conditionally color the output via an inline latex block
let fragment = '';
if(Number.isFinite(proportionalDelta)) {
fragment = `${signedFormatter.format(absoluteDelta)} ${percentFormatter.format(proportionalDelta)}`;
} else {
fragment = 'N/A';
}
if(!Number.isFinite(proportionalDelta) || sign === 0) {
return [`${subK}_delta`, fragment]
} else {
const formattedFragment = `$\\color{${colorMap[sign]}}\\text{${fragment.replace('%', '\\%')}}$`;
return [`${subK}_delta`, formattedFragment]
}
}))
};
// output a markdown fragment
const sizeFragment = `${formatter.format(size)} ${size_delta}`
const compressedFragment = `${formatter.format(compressed_size)} ${compressed_size_delta}`
return [asset.replace('report_pkg/', ''), sizeFragment, compressedFragment]
});
await core.summary.addHeading('Asset Sizes').addTable([
[{data: 'Asset', header: true}, {data: 'Uncompressed Size', header: true}, {data: 'Compressed Size', header: true}],
...lineFragments
]).write();
fs.cpSync(process.env.GITHUB_STEP_SUMMARY, './pr/step_summary.md')
- uses: actions/upload-artifact@v3
with:
name: pr
path: pr/
30 changes: 30 additions & 0 deletions scripts/report_build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
rm -rf report_pkg
mkdir -p report_pkg

echo "Building arrow-rs slim"
wasm-pack build \
--release \
--no-pack \
--out-dir report_pkg/slim \
--out-name arrow1 \
--target web \
--features=arrow1 &
echo "Building arrow-rs sync"
wasm-pack build \
--release \
--no-pack \
--out-dir report_pkg/sync \
--out-name arrow1 \
--target web \
--features={arrow1,reader,writer,all_compressions} &

echo "Building arrow-rs async_full"
wasm-pack build \
--release \
--no-pack \
--out-dir report_pkg/async_full \
--out-name arrow1 \
--target web \
--features={arrow1,reader,writer,all_compressions,async} &

wait;