Skip to content

Commit 10315fd

Browse files
committed
feat: apply binary size check for mac and windows
1 parent 7d4a1a3 commit 10315fd

File tree

3 files changed

+178
-50
lines changed

3 files changed

+178
-50
lines changed

.github/actions/binary-limit/action.yml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,27 @@ inputs:
77
description: "the increase size limit in bytes, default is 50kb"
88
default: "51200"
99
required: false
10+
platform:
11+
description: "target triple name used to locate the binary"
12+
default: "x86_64-unknown-linux-gnu"
13+
required: false
14+
15+
outputs:
16+
result:
17+
description: "JSON encoded size report"
18+
value: ${{ steps.size-report.outputs.result }}
1019

1120
runs:
1221
using: composite
1322

1423
steps:
1524
- name: GitHub Script
25+
id: size-report
1626
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd
1727
with:
1828
script: |
1929
const limit = parseInt("${{ inputs.size-threshold }}") || 51200;
30+
const platform = inputs.platform;
2031
const action = require("./.github/actions/binary-limit/binary-limit-script.js");
21-
await action({github, context, limit});
32+
const result = await action({github, context, limit, platform});
33+
core.setOutput("result", JSON.stringify(result));

.github/actions/binary-limit/binary-limit-script.js

Lines changed: 83 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,37 @@
11
const fs = require("node:fs");
22

3+
const SIZE_LIMIT_HEADING = "## 📦 Binary Size-limit";
4+
const DEFAULT_PLATFORM = "x86_64-unknown-linux-gnu";
5+
6+
const BINARY_PATHS = {
7+
"x86_64-unknown-linux-gnu": "rspack.linux-x64-gnu.node",
8+
"aarch64-unknown-linux-gnu": "rspack.linux-arm64-gnu.node",
9+
"x86_64-unknown-linux-musl": "rspack.linux-x64-musl.node",
10+
"aarch64-unknown-linux-musl": "rspack.linux-arm64-musl.node",
11+
"x86_64-apple-darwin": "rspack.darwin-x64.node",
12+
"aarch64-apple-darwin": "rspack.darwin-arm64.node",
13+
"x86_64-pc-windows-msvc": "rspack.win32-x64-msvc.node",
14+
"i686-pc-windows-msvc": "rspack.win32-ia32-msvc.node",
15+
"aarch64-pc-windows-msvc": "rspack.win32-arm64-msvc.node"
16+
};
17+
18+
const PLATFORM_LABELS = {
19+
"x86_64-unknown-linux-gnu": "Linux x64 (glibc)",
20+
"aarch64-unknown-linux-gnu": "Linux arm64 (glibc)",
21+
"x86_64-unknown-linux-musl": "Linux x64 (musl)",
22+
"aarch64-unknown-linux-musl": "Linux arm64 (musl)",
23+
"x86_64-apple-darwin": "macOS x64",
24+
"aarch64-apple-darwin": "macOS arm64",
25+
"x86_64-pc-windows-msvc": "Windows x64",
26+
"i686-pc-windows-msvc": "Windows x86",
27+
"aarch64-pc-windows-msvc": "Windows arm64"
28+
};
29+
330
/**
4-
* @param {import("@octokit/rest")} github
5-
* @param {Number} limit
31+
* @param {{ github: import('@octokit/rest'), context: any, limit: number, platform?: string }} options
632
*/
7-
module.exports = async function action({ github, context, limit }) {
33+
async function run({ github, context, limit, platform }) {
34+
const target = platform || DEFAULT_PLATFORM;
835
const commits = await github.rest.repos.listCommits({
936
owner: context.repo.owner,
1037
repo: context.repo.repo,
@@ -18,10 +45,13 @@ module.exports = async function action({ github, context, limit }) {
1845
console.log(commit.sha);
1946
try {
2047
const data = await fetchDataBySha(commit.sha);
21-
if (data?.size) {
48+
const size = data?.sizes?.[target] ?? data?.size;
49+
if (typeof size === "number") {
2250
baseCommit = commit;
23-
baseSize = data.size;
24-
console.log(`Commit ${commit.sha} has binary size: ${data.size}`);
51+
baseSize = size;
52+
console.log(
53+
`Commit ${commit.sha} has binary size (${target}): ${size}`
54+
);
2555
break;
2656
}
2757
} catch (e) {
@@ -35,48 +65,49 @@ module.exports = async function action({ github, context, limit }) {
3565
throw new Error(error);
3666
}
3767

38-
const headSize = fs.statSync(
39-
"./crates/node_binding/rspack.linux-x64-gnu.node"
40-
).size;
68+
const file = getBinaryPath(target);
69+
console.log(`Checking binary size for ${file}`);
70+
const headSize = fs.statSync(file).size;
4171

42-
console.log(`Base commit size: ${baseSize}`);
43-
console.log(`Head commit size: ${headSize}`);
44-
45-
const comment = compareBinarySize(headSize, baseSize, context, baseCommit);
46-
47-
try {
48-
await commentToPullRequest(github, context, comment);
49-
} catch (e) {
50-
console.error("Failed to comment on pull request:", e);
51-
}
72+
console.log(`Base commit size (${target}): ${baseSize}`);
73+
console.log(`Head commit size (${target}): ${headSize}`);
5274

5375
const increasedSize = headSize - baseSize;
54-
if (increasedSize > limit) {
55-
throw new Error(
56-
`Binary size increased by ${increasedSize} bytes, exceeding the limit of ${limit} bytes`
57-
);
58-
}
59-
};
76+
return {
77+
platform: target,
78+
baseSize,
79+
headSize,
80+
increasedSize,
81+
exceeded: increasedSize > limit,
82+
comment: compareBinarySize(headSize, baseSize, context, baseCommit)
83+
};
84+
}
85+
86+
module.exports = run;
87+
module.exports.commentToPullRequest = commentToPullRequest;
88+
module.exports.formatReport = formatReport;
89+
module.exports.getBinaryPath = getBinaryPath;
90+
module.exports.SIZE_LIMIT_HEADING = SIZE_LIMIT_HEADING;
6091

61-
async function commentToPullRequest(github, context, comment) {
92+
async function commentToPullRequest(github, context, body) {
6293
const { data: comments } = await github.rest.issues.listComments({
6394
owner: context.repo.owner,
6495
repo: context.repo.repo,
6596
issue_number: context.payload.number
6697
});
6798

68-
const prevComment = comments.filter(
99+
const prevComment = comments.find(
69100
comment =>
70101
comment.user.login === "github-actions[bot]" &&
71102
comment.body.startsWith(SIZE_LIMIT_HEADING)
72-
)[0];
103+
);
73104

74105
if (prevComment) {
75106
await github.rest.issues.updateComment({
76107
owner: context.repo.owner,
77108
repo: context.repo.repo,
78109
comment_id: prevComment.id,
79-
body: `${SIZE_LIMIT_HEADING}\n${comment}`
110+
body
80111
});
81112
return;
82113
}
@@ -85,8 +116,19 @@ async function commentToPullRequest(github, context, comment) {
85116
owner: context.repo.owner,
86117
repo: context.repo.repo,
87118
issue_number: context.payload.number,
88-
body: `${SIZE_LIMIT_HEADING}\n${comment}`
119+
body
120+
});
121+
}
122+
123+
function formatReport(entries) {
124+
const ordered = [...entries].sort((a, b) =>
125+
a.platform.localeCompare(b.platform)
126+
);
127+
const sections = ordered.map(entry => {
128+
const title = PLATFORM_LABELS[entry.platform] || entry.platform;
129+
return `### ${title}\n${entry.comment}`;
89130
});
131+
return `${SIZE_LIMIT_HEADING}\n\n${sections.join("\n\n")}`.trim();
90132
}
91133

92134
function fetchDataBySha(sha) {
@@ -95,8 +137,6 @@ function fetchDataBySha(sha) {
95137
return fetch(dataUrl).then(res => res.json());
96138
}
97139

98-
const SIZE_LIMIT_HEADING = "## 📦 Binary Size-limit";
99-
100140
const DATA_URL_BASE =
101141
"https://raw.githubusercontent.com/web-infra-dev/rspack-ecosystem-benchmark/data";
102142

@@ -108,7 +148,8 @@ function compareBinarySize(headSize, baseSize, context, baseCommit) {
108148
const info = `> Comparing [\`${headSha.slice(0, 7)}\`](${context.payload.repository.html_url}/commit/${headSha}) to [${message} by ${author}](${baseCommit.html_url})\n\n`;
109149

110150
const diff = headSize - baseSize;
111-
const percentage = (Math.abs(diff / baseSize) * 100).toFixed(2);
151+
const percentage =
152+
baseSize === 0 ? "0.00" : (Math.abs(diff / baseSize) * 100).toFixed(2);
112153
if (diff > 0) {
113154
return `${info}❌ Size increased by ${toHumanReadable(diff)} from ${toHumanReadable(baseSize)} to ${toHumanReadable(headSize)} (⬆️${percentage}%)`;
114155
}
@@ -118,6 +159,15 @@ function compareBinarySize(headSize, baseSize, context, baseCommit) {
118159
return `${info}🙈 Size remains the same at ${toHumanReadable(headSize)}`;
119160
}
120161

162+
function getBinaryPath(platform) {
163+
const target = platform || DEFAULT_PLATFORM;
164+
const filename = BINARY_PATHS[target];
165+
if (!filename) {
166+
throw new Error(`Unsupported platform: ${target}`);
167+
}
168+
return `./crates/node_binding/${filename}`;
169+
}
170+
121171
function toHumanReadable(size) {
122172
if (size < 1024) {
123173
return `${size}bytes`;

.github/workflows/size-limit.yml

Lines changed: 82 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,20 @@ permissions:
1010
jobs:
1111
size-limit:
1212
name: Binding Size Limit
13-
runs-on: ${{ fromJSON(vars.LINUX_SELF_HOSTED_RUNNER_LABELS || '"ubuntu-22.04"') }}
13+
strategy:
14+
fail-fast: false
15+
matrix:
16+
include:
17+
- runner: ${{ fromJSON(vars.LINUX_SELF_HOSTED_RUNNER_LABELS || '"ubuntu-22.04"') }}
18+
target: x86_64-unknown-linux-gnu
19+
id: linux
20+
- runner: macos-14
21+
target: aarch64-apple-darwin
22+
id: mac
23+
- runner: windows-2022
24+
target: x86_64-pc-windows-msvc
25+
id: win
26+
runs-on: ${{ matrix.runner }}
1427
steps:
1528
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
1629

@@ -27,13 +40,11 @@ jobs:
2740
- name: Install Rust Toolchain
2841
uses: ./.github/actions/rustup
2942
with:
30-
key: x86_64-unknown-linux-gnu-release
31-
# don't need use cache in self-hosted windows; benefits of build with cargo build are wasted by cache restore
43+
key: ${{ matrix.target }}-release
3244
save-if: ${{ runner.environment != 'self-hosted' || runner.os != 'Windows' }}
3345

34-
# setup rust target for native runner
3546
- name: Setup Rust Target
36-
run: rustup target add x86_64-unknown-linux-gnu
47+
run: rustup target add ${{ matrix.target }}
3748

3849
- name: Run Cargo codegen
3950
run: cargo codegen
@@ -43,20 +54,75 @@ jobs:
4354
echo $'\n' >> .cargo/config.toml
4455
echo 'trim-paths = true' >> .cargo/config.toml
4556
46-
# Fix: Resolve disk space error "ENOSPC: no space left on device" on GitHub Actions runners
47-
- name: Free disk cache
48-
if: runner.environment == 'github-hosted'
49-
uses: xc2/free-disk-space@fbe203b3788f2bebe2c835a15925da303eaa5efe # v1.0.0
50-
with:
51-
tool-cache: fals
52-
53-
- name: Build x86_64-unknown-linux-gnu native
57+
- name: Build native binary
5458
run: |
55-
rustup target add x86_64-unknown-linux-gnu
56-
RUST_TARGET=x86_64-unknown-linux-gnu pnpm build:binding:release
59+
rustup target add ${{ matrix.target }}
60+
RUST_TARGET=${{ matrix.target }} pnpm build:binding:release
5761
5862
- name: Binary Size-limit
63+
id: size
5964
uses: ./.github/actions/binary-limit
6065
with:
61-
# 50k 50*1024
6266
size-threshold: 51200
67+
platform: ${{ matrix.target }}
68+
69+
- name: Save size result
70+
if: steps.size.outputs.result != ''
71+
shell: bash
72+
env:
73+
RESULT: ${{ steps.size.outputs.result }}
74+
run: |
75+
printf '%s' "$RESULT" > size-result.json
76+
77+
- name: Upload size result
78+
if: steps.size.outputs.result != ''
79+
uses: actions/upload-artifact@5d89cf3d7f2a3f3fa99c1c427cf419d1c8e0d43c # v4
80+
with:
81+
name: binary-size-${{ matrix.id }}
82+
path: size-result.json
83+
if-no-files-found: warn
84+
85+
gather-results:
86+
name: Gather Binary Size Results
87+
needs: size-limit
88+
if: always()
89+
runs-on: ubuntu-22.04
90+
steps:
91+
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5
92+
93+
- name: Download size reports
94+
uses: actions/[email protected]
95+
with:
96+
pattern: binary-size-*
97+
merge-multiple: true
98+
path: size-results
99+
if-no-files-found: warn
100+
101+
- name: Post combined report
102+
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd
103+
env:
104+
RESULTS_DIR: size-results
105+
with:
106+
script: |
107+
const fs = require("node:fs");
108+
const path = require("node:path");
109+
const action = require("./.github/actions/binary-limit/binary-limit-script.js");
110+
const dir = process.env.RESULTS_DIR;
111+
if (!fs.existsSync(dir)) {
112+
core.info("No size reports found");
113+
return;
114+
}
115+
const files = fs.readdirSync(dir).filter(name => name.endsWith(".json"));
116+
if (!files.length) {
117+
core.info("No size reports to process");
118+
return;
119+
}
120+
const entries = files.map(file => {
121+
const raw = fs.readFileSync(path.join(dir, file), "utf8");
122+
return JSON.parse(raw);
123+
});
124+
const body = action.formatReport(entries);
125+
await action.commentToPullRequest(github, context, body);
126+
if (entries.some(entry => entry.exceeded)) {
127+
core.setFailed("Binary size limit exceeded");
128+
}

0 commit comments

Comments
 (0)