Skip to content

Commit 3b8df9f

Browse files
fix(ci): scope format checks to changed files
1 parent 005b89a commit 3b8df9f

File tree

3 files changed

+195
-5
lines changed

3 files changed

+195
-5
lines changed

.github/workflows/build-and-test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ jobs:
5353
run: npx cypress install
5454

5555
- name: Check code format
56-
run: git ls-files -z | xargs -0 pnpm exec prettier --check --ignore-unknown
56+
run: node tools/scripts/check-format-changed.mjs
5757

5858
- name: Verify Rslib Template Publint Wiring
5959
run: node packages/create-module-federation/scripts/verify-rslib-templates.mjs
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
#!/usr/bin/env node
2+
import { readFileSync } from 'node:fs';
3+
import { dirname, resolve } from 'node:path';
4+
import { spawnSync } from 'node:child_process';
5+
import { fileURLToPath } from 'node:url';
6+
7+
const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url));
8+
const ROOT = resolve(SCRIPT_DIR, '../..');
9+
const CHECK_BATCH_SIZE = 100;
10+
11+
main();
12+
13+
function main() {
14+
const requestedBase = parseBaseArg(process.argv.slice(2));
15+
const baseRef = resolveBaseRef(requestedBase);
16+
17+
if (!baseRef) {
18+
console.warn(
19+
'[format-check] Unable to resolve a base ref. Skipping format check.',
20+
);
21+
process.exit(0);
22+
}
23+
24+
const changedFiles = getChangedFiles(baseRef, 'HEAD');
25+
if (changedFiles.length === 0) {
26+
console.log(
27+
`[format-check] No changed files detected between ${baseRef}...HEAD. Skipping format check.`,
28+
);
29+
process.exit(0);
30+
}
31+
32+
console.log(
33+
`[format-check] Checking formatting for ${changedFiles.length} changed file(s) from ${baseRef}...HEAD.`,
34+
);
35+
36+
for (let index = 0; index < changedFiles.length; index += CHECK_BATCH_SIZE) {
37+
const batch = changedFiles.slice(index, index + CHECK_BATCH_SIZE);
38+
const result = spawnSync(
39+
'pnpm',
40+
['exec', 'prettier', '--check', '--ignore-unknown', ...batch],
41+
{
42+
cwd: ROOT,
43+
stdio: 'inherit',
44+
env: process.env,
45+
},
46+
);
47+
48+
if (result.status !== 0) {
49+
process.exit(result.status ?? 1);
50+
}
51+
}
52+
}
53+
54+
function parseBaseArg(argv) {
55+
for (let i = 0; i < argv.length; i += 1) {
56+
const token = argv[i];
57+
if (token === '--base' && argv[i + 1]) {
58+
return argv[i + 1];
59+
}
60+
if (token.startsWith('--base=')) {
61+
return token.slice('--base='.length);
62+
}
63+
}
64+
return null;
65+
}
66+
67+
function resolveBaseRef(preferredRef) {
68+
const headCommit = resolveGitCommit('HEAD');
69+
if (hasGitRef(preferredRef)) {
70+
const preferredCommit = resolveGitCommit(preferredRef);
71+
if (!headCommit || preferredCommit !== headCommit) {
72+
return preferredRef;
73+
}
74+
}
75+
76+
const refs = [];
77+
if (process.env.CI_BASE_REF) {
78+
refs.push(process.env.CI_BASE_REF);
79+
}
80+
if (process.env.CI_LOCAL_BASE_REF) {
81+
refs.push(process.env.CI_LOCAL_BASE_REF);
82+
}
83+
if (process.env.GITHUB_BASE_REF) {
84+
refs.push(`origin/${process.env.GITHUB_BASE_REF}`);
85+
refs.push(process.env.GITHUB_BASE_REF);
86+
}
87+
if (process.env.GITHUB_EVENT_BEFORE) {
88+
refs.push(process.env.GITHUB_EVENT_BEFORE);
89+
}
90+
refs.push(...getEventBaseCandidates());
91+
refs.push('origin/main', 'main', 'HEAD~1');
92+
93+
for (const ref of refs) {
94+
if (!hasGitRef(ref)) {
95+
continue;
96+
}
97+
98+
const candidateCommit = resolveGitCommit(ref);
99+
if (!headCommit || candidateCommit !== headCommit) {
100+
return ref;
101+
}
102+
}
103+
104+
return null;
105+
}
106+
107+
function getEventBaseCandidates() {
108+
const refs = new Set();
109+
const eventPath = process.env.GITHUB_EVENT_PATH;
110+
if (!eventPath) {
111+
return [];
112+
}
113+
114+
try {
115+
const payload = JSON.parse(readFileSync(eventPath, 'utf-8'));
116+
if (payload?.pull_request?.base?.sha) {
117+
refs.add(payload.pull_request.base.sha);
118+
}
119+
if (payload?.pull_request?.base?.ref) {
120+
refs.add(`origin/${payload.pull_request.base.ref}`);
121+
refs.add(payload.pull_request.base.ref);
122+
}
123+
if (payload?.before) {
124+
refs.add(payload.before);
125+
}
126+
} catch {
127+
return [];
128+
}
129+
130+
return Array.from(refs);
131+
}
132+
133+
function getChangedFiles(baseRef, headRef) {
134+
const result = spawnSync(
135+
'git',
136+
['diff', '--name-only', '--diff-filter=ACMR', `${baseRef}...${headRef}`],
137+
{
138+
cwd: ROOT,
139+
stdio: 'pipe',
140+
encoding: 'utf-8',
141+
},
142+
);
143+
144+
if (result.status !== 0) {
145+
throw new Error(
146+
`[format-check] Failed to compute changed files for ${baseRef}...${headRef}: ${result.stderr || result.stdout}`,
147+
);
148+
}
149+
150+
return result.stdout
151+
.split('\n')
152+
.map((entry) => entry.trim())
153+
.filter(Boolean);
154+
}
155+
156+
function hasGitRef(ref) {
157+
if (!ref) {
158+
return false;
159+
}
160+
161+
const result = spawnSync(
162+
'git',
163+
['rev-parse', '--verify', '--quiet', `${ref}^{commit}`],
164+
{
165+
cwd: ROOT,
166+
stdio: 'ignore',
167+
},
168+
);
169+
170+
return result.status === 0;
171+
}
172+
173+
function resolveGitCommit(ref) {
174+
if (!ref) {
175+
return null;
176+
}
177+
178+
const result = spawnSync(
179+
'git',
180+
['rev-parse', '--verify', '--quiet', `${ref}^{commit}`],
181+
{
182+
cwd: ROOT,
183+
stdio: 'pipe',
184+
encoding: 'utf-8',
185+
},
186+
);
187+
188+
if (result.status !== 0) {
189+
return null;
190+
}
191+
192+
return result.stdout.trim() || null;
193+
}

tools/scripts/ci-local.mjs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,7 @@ const jobs = [
4646
runCommand('npx', ['cypress', 'install'], ctx),
4747
),
4848
step('Check code format', (ctx) =>
49-
runShell(
50-
'git ls-files -z | xargs -0 pnpm exec prettier --check --ignore-unknown',
51-
ctx,
52-
),
49+
runCommand('node', ['tools/scripts/check-format-changed.mjs'], ctx),
5350
),
5451
step('Verify Rslib Template Publint Wiring', (ctx) =>
5552
runCommand(

0 commit comments

Comments
 (0)