@sun-asterisk/sunlint 1.3.22 → 1.3.23

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, use PR-specific logic
127
+ // If in PR context and no explicit baseRef, try PR-specific logic
128
128
  if (prContext && !baseRef) {
129
- return this.getPRChangedFiles(prContext, gitRoot);
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
- const baseRef = this.findBaseRef(baseBranch, gitRoot);
192
+ let baseRef = this.findBaseRef(baseBranch, gitRoot);
182
193
 
183
194
  if (!baseRef) {
184
- throw new Error(`Cannot find base branch: ${baseBranch}`);
185
- }
195
+ console.log(`⚠️ Base ref not found locally, attempting to fetch origin/${baseBranch}...`);
186
196
 
187
- // Ensure we have the latest base branch
188
- this.ensureBaseRefExists(baseRef, gitRoot);
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(`Warning: Could not find merge-base, using direct diff with ${baseRef}`);
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 remote = baseRef.split('/')[0];
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(`Fetching ${remote}...`);
265
- execSync(`git fetch ${remote} --depth=1`, {
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: 'inherit'
339
+ stdio: 'ignore'
268
340
  });
341
+
342
+ console.log(`✅ Successfully fetched ${baseRef}`);
343
+ return true;
269
344
  } catch (fetchError) {
270
- console.warn(`Warning: Failed to fetch ${remote}: ${fetchError.message}`);
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
 
@@ -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
- - name: Run SunLint on Changed Files (Auto-detect)
99
- run: sunlint --all --changed-files --github-annotate
100
- env:
101
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
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 chỉ định `--diff-base` nữa!** SunLint sẽ tự động xử lý.
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
- **Fallback**: Nếu không phát hiện được PR context, sẽ fallback về logic (so sánh với HEAD hoặc origin/main)
163
+ **Recommendation**: Dùng `fetch-depth: 0` trừ khi bạn 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sun-asterisk/sunlint",
3
- "version": "1.3.22",
3
+ "version": "1.3.23",
4
4
  "description": "☀️ SunLint - Multi-language static analysis tool for code quality and security | Sun* Engineering Standards",
5
5
  "main": "cli.js",
6
6
  "bin": {