@sun-asterisk/sunlint 1.3.22 → 1.3.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/core/git-utils.js
CHANGED
|
@@ -124,9 +124,18 @@ class GitUtils {
|
|
|
124
124
|
// Check if we're in PR context
|
|
125
125
|
const prContext = this.detectPRContext();
|
|
126
126
|
|
|
127
|
-
// If in PR context and no explicit baseRef,
|
|
127
|
+
// If in PR context and no explicit baseRef, try PR-specific logic
|
|
128
128
|
if (prContext && !baseRef) {
|
|
129
|
-
|
|
129
|
+
try {
|
|
130
|
+
const prFiles = this.getPRChangedFiles(prContext, gitRoot);
|
|
131
|
+
if (prFiles && prFiles.length >= 0) {
|
|
132
|
+
return prFiles;
|
|
133
|
+
}
|
|
134
|
+
} catch (prError) {
|
|
135
|
+
// Log warning and fallback to standard logic
|
|
136
|
+
console.warn(`⚠️ PR context detected but failed to get changed files: ${prError.message}`);
|
|
137
|
+
console.log(` Falling back to standard git diff logic...`);
|
|
138
|
+
}
|
|
130
139
|
}
|
|
131
140
|
|
|
132
141
|
// Auto-detect base ref if not provided
|
|
@@ -175,17 +184,29 @@ class GitUtils {
|
|
|
175
184
|
*/
|
|
176
185
|
static getPRChangedFiles(prContext, gitRoot) {
|
|
177
186
|
try {
|
|
178
|
-
const { baseBranch } = prContext;
|
|
187
|
+
const { baseBranch, provider } = prContext;
|
|
188
|
+
|
|
189
|
+
console.log(`🔍 Detecting changed files for PR (provider: ${provider}, base: ${baseBranch})`);
|
|
179
190
|
|
|
180
191
|
// Try to find the base branch reference
|
|
181
|
-
|
|
192
|
+
let baseRef = this.findBaseRef(baseBranch, gitRoot);
|
|
182
193
|
|
|
183
194
|
if (!baseRef) {
|
|
184
|
-
|
|
185
|
-
}
|
|
195
|
+
console.log(`⚠️ Base ref not found locally, attempting to fetch origin/${baseBranch}...`);
|
|
186
196
|
|
|
187
|
-
|
|
188
|
-
|
|
197
|
+
// Try to fetch and create the ref
|
|
198
|
+
const fetchSuccess = this.ensureBaseRefExists(`origin/${baseBranch}`, gitRoot);
|
|
199
|
+
|
|
200
|
+
if (fetchSuccess) {
|
|
201
|
+
baseRef = `origin/${baseBranch}`;
|
|
202
|
+
} else {
|
|
203
|
+
throw new Error(`Cannot find or fetch base branch: ${baseBranch}`);
|
|
204
|
+
}
|
|
205
|
+
} else {
|
|
206
|
+
// Ensure we have the latest
|
|
207
|
+
console.log(`✅ Found base ref: ${baseRef}`);
|
|
208
|
+
this.ensureBaseRefExists(baseRef, gitRoot);
|
|
209
|
+
}
|
|
189
210
|
|
|
190
211
|
// Use merge-base to find the common ancestor
|
|
191
212
|
let mergeBase;
|
|
@@ -194,9 +215,10 @@ class GitUtils {
|
|
|
194
215
|
cwd: gitRoot,
|
|
195
216
|
encoding: 'utf8'
|
|
196
217
|
}).trim();
|
|
218
|
+
console.log(`✅ Found merge-base: ${mergeBase.substring(0, 8)}`);
|
|
197
219
|
} catch (error) {
|
|
198
220
|
// If merge-base fails, fall back to direct comparison
|
|
199
|
-
console.warn(
|
|
221
|
+
console.warn(`⚠️ Could not find merge-base, using direct diff with ${baseRef}`);
|
|
200
222
|
mergeBase = baseRef;
|
|
201
223
|
}
|
|
202
224
|
|
|
@@ -210,6 +232,8 @@ class GitUtils {
|
|
|
210
232
|
.map(file => path.resolve(gitRoot, file))
|
|
211
233
|
.filter(file => fs.existsSync(file));
|
|
212
234
|
|
|
235
|
+
console.log(`✅ Found ${changedFiles.length} changed files in PR`);
|
|
236
|
+
|
|
213
237
|
return changedFiles;
|
|
214
238
|
} catch (error) {
|
|
215
239
|
throw new Error(`Failed to get PR changed files: ${error.message}`);
|
|
@@ -256,20 +280,87 @@ class GitUtils {
|
|
|
256
280
|
cwd: gitRoot,
|
|
257
281
|
stdio: 'ignore'
|
|
258
282
|
});
|
|
283
|
+
// Ref exists, return true
|
|
284
|
+
return true;
|
|
259
285
|
} catch (error) {
|
|
260
286
|
// Try to fetch if it doesn't exist
|
|
261
|
-
const
|
|
287
|
+
const parts = baseRef.split('/');
|
|
288
|
+
const remote = parts[0];
|
|
289
|
+
const branch = parts.slice(1).join('/');
|
|
290
|
+
|
|
262
291
|
if (remote === 'origin' || remote === 'upstream') {
|
|
263
292
|
try {
|
|
264
|
-
console.log(
|
|
265
|
-
|
|
293
|
+
console.log(`⬇️ Fetching ${remote}/${branch}...`);
|
|
294
|
+
|
|
295
|
+
// Check if this is a shallow repository (common in GitHub Actions)
|
|
296
|
+
const isShallow = this.isShallowRepository(gitRoot);
|
|
297
|
+
|
|
298
|
+
if (isShallow) {
|
|
299
|
+
console.log(` ℹ️ Detected shallow clone, fetching with additional history...`);
|
|
300
|
+
|
|
301
|
+
// For shallow clones, we need to:
|
|
302
|
+
// 1. Fetch the base branch
|
|
303
|
+
// 2. Get enough history to find merge-base
|
|
304
|
+
try {
|
|
305
|
+
// Unshallow current branch first to get more history
|
|
306
|
+
execSync(`git fetch --deepen=50`, {
|
|
307
|
+
cwd: gitRoot,
|
|
308
|
+
stdio: 'pipe',
|
|
309
|
+
encoding: 'utf8'
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
// Then fetch the base branch with history
|
|
313
|
+
execSync(`git fetch ${remote} ${branch} --depth=50`, {
|
|
314
|
+
cwd: gitRoot,
|
|
315
|
+
stdio: 'pipe',
|
|
316
|
+
encoding: 'utf8'
|
|
317
|
+
});
|
|
318
|
+
} catch (shallowError) {
|
|
319
|
+
// If deepen fails, try direct fetch
|
|
320
|
+
console.log(` ℹ️ Trying direct fetch...`);
|
|
321
|
+
execSync(`git fetch ${remote} ${branch}`, {
|
|
322
|
+
cwd: gitRoot,
|
|
323
|
+
stdio: 'pipe',
|
|
324
|
+
encoding: 'utf8'
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
} else {
|
|
328
|
+
// Normal fetch for non-shallow repos
|
|
329
|
+
execSync(`git fetch ${remote} ${branch}`, {
|
|
330
|
+
cwd: gitRoot,
|
|
331
|
+
stdio: 'pipe',
|
|
332
|
+
encoding: 'utf8'
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Verify it now exists
|
|
337
|
+
execSync(`git rev-parse --verify ${baseRef}`, {
|
|
266
338
|
cwd: gitRoot,
|
|
267
|
-
stdio: '
|
|
339
|
+
stdio: 'ignore'
|
|
268
340
|
});
|
|
341
|
+
|
|
342
|
+
console.log(`✅ Successfully fetched ${baseRef}`);
|
|
343
|
+
return true;
|
|
269
344
|
} catch (fetchError) {
|
|
270
|
-
console.warn(
|
|
345
|
+
console.warn(`⚠️ Failed to fetch ${baseRef}: ${fetchError.message}`);
|
|
346
|
+
return false;
|
|
271
347
|
}
|
|
272
348
|
}
|
|
349
|
+
return false;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Check if repository is shallow
|
|
355
|
+
* @param {string} gitRoot - Git repository root path
|
|
356
|
+
* @returns {boolean} True if shallow
|
|
357
|
+
*/
|
|
358
|
+
static isShallowRepository(gitRoot) {
|
|
359
|
+
try {
|
|
360
|
+
const shallowFile = path.join(gitRoot, '.git', 'shallow');
|
|
361
|
+
return fs.existsSync(shallowFile);
|
|
362
|
+
} catch (error) {
|
|
363
|
+
return false;
|
|
273
364
|
}
|
|
274
365
|
}
|
|
275
366
|
|
|
@@ -380,9 +380,6 @@ async function createReviewsInBatches(octokit, owner, repoName, prNumber, headSh
|
|
|
380
380
|
const batch = batches[i];
|
|
381
381
|
const isLastBatch = i === batches.length - 1;
|
|
382
382
|
|
|
383
|
-
// Only REQUEST_CHANGES on last batch if there are errors
|
|
384
|
-
const eventType = isLastBatch && hasError ? 'REQUEST_CHANGES' : 'COMMENT';
|
|
385
|
-
|
|
386
383
|
try {
|
|
387
384
|
const reviewRes = await withRetry(async () => {
|
|
388
385
|
return await octokit.pulls.createReview({
|
|
@@ -390,7 +387,7 @@ async function createReviewsInBatches(octokit, owner, repoName, prNumber, headSh
|
|
|
390
387
|
repo: repoName,
|
|
391
388
|
pull_number: prNumber,
|
|
392
389
|
commit_id: headSha,
|
|
393
|
-
event:
|
|
390
|
+
event: 'COMMENT',
|
|
394
391
|
body: isLastBatch && batches.length > 1
|
|
395
392
|
? `SunLint found ${comments.length} issue(s) across multiple reviews.`
|
|
396
393
|
: undefined,
|
|
@@ -92,25 +92,75 @@ jobs:
|
|
|
92
92
|
|
|
93
93
|
### 1. Analyze only changed files (Auto-detect PR)
|
|
94
94
|
|
|
95
|
-
**⭐ Tính năng mới**: Tự động phát hiện PR context và sử dụng merge-base để diff chính xác!
|
|
95
|
+
**⭐ Tính năng mới**: Tự động phát hiện PR context, tự động fetch base branch, và sử dụng merge-base để diff chính xác!
|
|
96
96
|
|
|
97
97
|
```yaml
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
98
|
+
steps:
|
|
99
|
+
- name: Checkout code
|
|
100
|
+
uses: actions/checkout@v4
|
|
101
|
+
with:
|
|
102
|
+
fetch-depth: 0 # Recommended: fetch full history for accurate diff
|
|
103
|
+
|
|
104
|
+
- name: Run SunLint on Changed Files (Auto-detect)
|
|
105
|
+
run: sunlint --all --changed-files --github-annotate
|
|
106
|
+
env:
|
|
107
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
102
108
|
```
|
|
103
109
|
|
|
110
|
+
**✨ Đơn giản nhất có thể - không cần config gì thêm!**
|
|
111
|
+
|
|
104
112
|
**Cách hoạt động**:
|
|
113
|
+
|
|
105
114
|
- ✅ Tự động detect GitHub Actions PR event (`GITHUB_EVENT_NAME=pull_request`)
|
|
106
115
|
- ✅ Tự động lấy base branch từ `GITHUB_BASE_REF`
|
|
116
|
+
- ✅ **Tự động fetch base branch nếu chưa có** (không cần thêm step!)
|
|
117
|
+
- ✅ Detect shallow clone và fetch thêm history nếu cần
|
|
107
118
|
- ✅ Sử dụng `git merge-base` để tìm common ancestor
|
|
108
119
|
- ✅ So sánh với merge-base thay vì HEAD → Lấy đúng các file thay đổi trong PR
|
|
109
|
-
- ✅ Tự động fetch base branch nếu cần
|
|
110
120
|
|
|
111
|
-
**Không cần
|
|
121
|
+
**Không cần:**
|
|
122
|
+
|
|
123
|
+
- ❌ Chỉ định `--diff-base`
|
|
124
|
+
- ❌ Thêm step fetch base branch thủ công
|
|
125
|
+
- ❌ Config phức tạp
|
|
126
|
+
|
|
127
|
+
**Fallback**: Nếu không phát hiện được PR context hoặc không fetch được base branch, sẽ fallback về standard git diff logic
|
|
128
|
+
|
|
129
|
+
**❓ Có cần `fetch-depth: 0` không?**
|
|
130
|
+
|
|
131
|
+
**Không bắt buộc!** Nhưng **strongly recommended** vì performance.
|
|
132
|
+
|
|
133
|
+
| Option | Performance | Checkout Time | Total Time | Accuracy |
|
|
134
|
+
|--------|-------------|---------------|------------|----------|
|
|
135
|
+
| **With `fetch-depth: 0`** ⭐ | Fast | +2-3s | **Fastest** | 100% |
|
|
136
|
+
| Without (shallow clone) | Slower | Fast | Slower | ~95% |
|
|
137
|
+
|
|
138
|
+
**Với `fetch-depth: 0` (Recommended):**
|
|
139
|
+
|
|
140
|
+
```yaml
|
|
141
|
+
- uses: actions/checkout@v4
|
|
142
|
+
with:
|
|
143
|
+
fetch-depth: 0 # One-time full fetch
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
- ✅ **Fastest total time** - fetch once, use immediately
|
|
147
|
+
- ✅ **100% accurate** - full history for merge-base
|
|
148
|
+
- ✅ **Simpler** - no runtime fetch needed
|
|
149
|
+
|
|
150
|
+
**Không có `fetch-depth: 0` (Vẫn work):**
|
|
151
|
+
|
|
152
|
+
```yaml
|
|
153
|
+
- uses: actions/checkout@v4 # Shallow clone (depth=1)
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
- ✅ Fast checkout
|
|
157
|
+
- ⚠️ **Slower total time** - SunLint must fetch at runtime:
|
|
158
|
+
- Detect shallow clone
|
|
159
|
+
- `git fetch --deepen=50`
|
|
160
|
+
- `git fetch origin/main --depth=50`
|
|
161
|
+
- ⚠️ May miss history for very large PRs
|
|
112
162
|
|
|
113
|
-
**
|
|
163
|
+
**Recommendation**: Dùng `fetch-depth: 0` trừ khi bạn có lý do đặc biệt (e.g., monorepo cực lớn)
|
|
114
164
|
|
|
115
165
|
### 2. Save report file + annotate
|
|
116
166
|
|
package/package.json
CHANGED