playwright-slack-report-burak 3.0.18 â 3.0.20
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/dist/src/ResultsParser.js +35 -71
- package/dist/src/SlackReporter.js +43 -23
- package/package.json +1 -1
|
@@ -44,33 +44,20 @@ class ResultsParser {
|
|
|
44
44
|
? parseInt(process.env.MATRIX_SHARD, 10)
|
|
45
45
|
: (process.env.MATRIX_INDEX !== undefined ? parseInt(process.env.MATRIX_INDEX, 10) : 0));
|
|
46
46
|
|
|
47
|
-
// Determine if we're using 1-based indexing (workflow-style) or 0-based
|
|
48
|
-
// This is detected by checking if artifacts exist with 1-based naming
|
|
49
|
-
isOneBasedIndexing = false;
|
|
50
47
|
constructor(options = { separateFlakyTests: false }) {
|
|
51
48
|
this.result = [];
|
|
52
49
|
this.separateFlakyTests = options.separateFlakyTests;
|
|
53
50
|
// Log package version
|
|
54
51
|
console.log(`đĻ [ResultsParser] playwright-slack-report-burak v${packageVersion}`);
|
|
55
|
-
// Log shard detection in ResultsParser
|
|
56
|
-
console.log('đ [ResultsParser] Initialized with shard detection:');
|
|
57
|
-
console.log(`đ SHARD_INDEX env: ${process.env.SHARD_INDEX !== undefined ? `"${process.env.SHARD_INDEX}"` : 'undefined'}`);
|
|
58
|
-
console.log(`đ TOTAL_SHARDS env: ${process.env.TOTAL_SHARDS !== undefined ? `"${process.env.TOTAL_SHARDS}"` : 'undefined'}`);
|
|
59
|
-
console.log(`đ MATRIX_SHARD env: ${process.env.MATRIX_SHARD !== undefined ? `"${process.env.MATRIX_SHARD}"` : 'undefined'}`);
|
|
60
|
-
console.log(`đ MATRIX_INDEX env: ${process.env.MATRIX_INDEX !== undefined ? `"${process.env.MATRIX_INDEX}"` : 'undefined'}`);
|
|
61
|
-
console.log(`đ MATRIX_COUNT env: ${process.env.MATRIX_COUNT !== undefined ? `"${process.env.MATRIX_COUNT}"` : 'undefined'}`);
|
|
62
|
-
console.log(`đ Calculated shardIndex: ${this.shardIndex}`);
|
|
63
|
-
console.log(`đ Calculated totalShardCount: ${this.totalShardCount}`);
|
|
64
52
|
}
|
|
65
53
|
/**
|
|
66
54
|
* Update shard information after detection (e.g., from GitHub API)
|
|
67
|
-
* @param {number} shardIndex - The current shard index (
|
|
55
|
+
* @param {number} shardIndex - The current shard index (1-based, shard 1 is aggregator)
|
|
68
56
|
* @param {number} totalShardCount - The total number of shards
|
|
69
57
|
*/
|
|
70
58
|
updateShardInfo(shardIndex, totalShardCount) {
|
|
71
59
|
this.shardIndex = shardIndex;
|
|
72
60
|
this.totalShardCount = totalShardCount;
|
|
73
|
-
console.log(`đ [ResultsParser] Updated shard info: shardIndex=${this.shardIndex}, totalShardCount=${this.totalShardCount}`);
|
|
74
61
|
}
|
|
75
62
|
async getParsedResults() {
|
|
76
63
|
const summary = {
|
|
@@ -127,29 +114,20 @@ class ResultsParser {
|
|
|
127
114
|
// Create the file
|
|
128
115
|
fs.writeFileSync(nodeSummaryFile, JSON.stringify(nodeSummary, null, 2));
|
|
129
116
|
|
|
130
|
-
|
|
131
|
-
console.log(`đ [ResultsParser] Created node summary file: ${nodeSummaryFile}`);
|
|
132
|
-
|
|
133
|
-
if (this.shardIndex === 0 && this.totalShardCount > 1) {
|
|
134
|
-
console.log(`đ [ResultsParser] Shard 0 detected with ${this.totalShardCount} total shards - will fetch artifacts from other shards`);
|
|
117
|
+
if (this.shardIndex === 1 && this.totalShardCount > 1) {
|
|
135
118
|
if (process.env.CI) {
|
|
136
|
-
console.log('đ [ResultsParser] CI environment detected - fetching all artifacts from GitHub Actions...');
|
|
137
119
|
await this.fetchAllArtifacts();
|
|
138
120
|
} else {
|
|
139
|
-
console.log('đ [ResultsParser] Local environment - waiting for artifacts to appear...');
|
|
140
121
|
while (!this.allNodeSummaryFilesExist() || !this.allBlobZipsExist()) {
|
|
141
|
-
console.log('đ [ResultsParser] Waiting for all artifacts to exist...');
|
|
142
122
|
await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for 1 second
|
|
143
123
|
}
|
|
144
124
|
}
|
|
145
|
-
} else {
|
|
146
|
-
console.log(`đ [ResultsParser] Not shard 0 (current: ${this.shardIndex}) or single shard (total: ${this.totalShardCount}) - skipping artifact fetch`);
|
|
147
125
|
}
|
|
148
126
|
|
|
149
|
-
if (this.shardIndex ===
|
|
150
|
-
console.log(`đ [ResultsParser] Shard 0: All artifacts available, merging results from all shards...`);
|
|
127
|
+
if (this.shardIndex === 1 && this.allNodeSummaryFilesExist() && this.allBlobZipsExist()) {
|
|
151
128
|
// Merge all node summaries into the final summary
|
|
152
|
-
|
|
129
|
+
// Loop from 1 to totalShardCount (1-based indexing)
|
|
130
|
+
for (let i = 1; i <= this.totalShardCount; i++) {
|
|
153
131
|
const nodeSummaryFile = path.join(summariesDir, `node_summary_${i}.json`);
|
|
154
132
|
const fileToRead = nodeSummaryFile;
|
|
155
133
|
|
|
@@ -364,7 +342,8 @@ class ResultsParser {
|
|
|
364
342
|
console.log('Checking if all node summary files exist...');
|
|
365
343
|
const summariesDir = path.join('./', 'playwright-report');
|
|
366
344
|
|
|
367
|
-
|
|
345
|
+
// Check files from 1 to totalShardCount (1-based indexing)
|
|
346
|
+
for (let i = 1; i <= this.totalShardCount; i++) {
|
|
368
347
|
const nodeSummaryFile = path.join(summariesDir, `node_summary_${i}.json`);
|
|
369
348
|
if (!fs.existsSync(nodeSummaryFile)) {
|
|
370
349
|
return false;
|
|
@@ -377,7 +356,8 @@ class ResultsParser {
|
|
|
377
356
|
console.log('Checking if all blob zips exist...');
|
|
378
357
|
const summariesDir = path.join('./', 'playwright-report');
|
|
379
358
|
|
|
380
|
-
|
|
359
|
+
// Check files from 1 to totalShardCount (1-based indexing)
|
|
360
|
+
for (let i = 1; i <= this.totalShardCount; i++) {
|
|
381
361
|
const blobZipFile = path.join(summariesDir, `blob-report-node-${i}.zip`);
|
|
382
362
|
if (!fs.existsSync(blobZipFile)) {
|
|
383
363
|
return false;
|
|
@@ -392,12 +372,13 @@ class ResultsParser {
|
|
|
392
372
|
fs.mkdirSync(summariesDir, { recursive: true });
|
|
393
373
|
}
|
|
394
374
|
|
|
395
|
-
console.log(`Shard
|
|
375
|
+
console.log(`Shard 1: Waiting for artifacts from ${this.totalShardCount - 1} other shard(s)...`);
|
|
396
376
|
|
|
397
|
-
// Fetch artifacts for shards
|
|
398
|
-
// We need to fetch from all
|
|
399
|
-
for (let i = 1; i
|
|
400
|
-
|
|
377
|
+
// Fetch artifacts for all shards except shard 1 (since we're shard 1)
|
|
378
|
+
// We need to fetch from shards 2, 3, 4, ... (all except shard 1, using 1-based indexing)
|
|
379
|
+
for (let i = 1; i <= this.totalShardCount; i++) {
|
|
380
|
+
if (i === 1) continue; // Skip shard 1 (we are shard 1)
|
|
381
|
+
console.log(`Shard 1: Fetching artifacts from shard ${i}...`);
|
|
401
382
|
await this.fetchArtifact(i, `node_summary_${i}.json`, summariesDir);
|
|
402
383
|
await this.fetchArtifact(i, `blob-report-node-${i}.zip`, summariesDir);
|
|
403
384
|
}
|
|
@@ -407,20 +388,21 @@ class ResultsParser {
|
|
|
407
388
|
const maxRetries = 60; // Wait up to 10 minutes (60 * 10 seconds)
|
|
408
389
|
|
|
409
390
|
while ((!this.allNodeSummaryFilesExist() || !this.allBlobZipsExist()) && retryCount < maxRetries) {
|
|
410
|
-
console.log(`Shard
|
|
391
|
+
console.log(`Shard 1: Waiting for all artifacts to be available (attempt ${retryCount + 1}/${maxRetries})...`);
|
|
411
392
|
|
|
412
393
|
// Re-fetch any missing artifacts
|
|
413
|
-
for (let i = 1; i
|
|
394
|
+
for (let i = 1; i <= this.totalShardCount; i++) {
|
|
395
|
+
if (i === 1) continue; // Skip shard 1 (we are shard 1)
|
|
414
396
|
const nodeSummaryFile = path.join(summariesDir, `node_summary_${i}.json`);
|
|
415
397
|
const blobZipFile = path.join(summariesDir, `blob-report-node-${i}.zip`);
|
|
416
398
|
|
|
417
399
|
if (!fs.existsSync(nodeSummaryFile)) {
|
|
418
|
-
console.log(`Shard
|
|
400
|
+
console.log(`Shard 1: Re-fetching missing node_summary_${i}.json...`);
|
|
419
401
|
await this.fetchArtifact(i, `node_summary_${i}.json`, summariesDir);
|
|
420
402
|
}
|
|
421
403
|
|
|
422
404
|
if (!fs.existsSync(blobZipFile)) {
|
|
423
|
-
console.log(`Shard
|
|
405
|
+
console.log(`Shard 1: Re-fetching missing blob-report-node-${i}.zip...`);
|
|
424
406
|
await this.fetchArtifact(i, `blob-report-node-${i}.zip`, summariesDir);
|
|
425
407
|
}
|
|
426
408
|
}
|
|
@@ -432,9 +414,9 @@ class ResultsParser {
|
|
|
432
414
|
}
|
|
433
415
|
|
|
434
416
|
if (this.allNodeSummaryFilesExist() && this.allBlobZipsExist()) {
|
|
435
|
-
console.log(`Shard
|
|
417
|
+
console.log(`Shard 1: Successfully fetched all artifacts from ${this.totalShardCount - 1} other shard(s).`);
|
|
436
418
|
} else {
|
|
437
|
-
console.warn(`Shard
|
|
419
|
+
console.warn(`Shard 1: Warning - Some artifacts may still be missing after ${maxRetries} attempts.`);
|
|
438
420
|
}
|
|
439
421
|
}
|
|
440
422
|
|
|
@@ -443,21 +425,6 @@ class ResultsParser {
|
|
|
443
425
|
await this.fetchArtifactFromGitHubActions(i, file, filePath);
|
|
444
426
|
}
|
|
445
427
|
|
|
446
|
-
// Helper to extract shard index from artifact name
|
|
447
|
-
extractShardIndexFromArtifactName(artifactName, i) {
|
|
448
|
-
// Try to extract number from artifact name (e.g., html-report-1 -> 1)
|
|
449
|
-
const match = artifactName.match(/-(\d+)$/);
|
|
450
|
-
if (match) {
|
|
451
|
-
const artifactShard = parseInt(match[1], 10);
|
|
452
|
-
// If artifact uses 1-based indexing, convert to 0-based for internal use
|
|
453
|
-
// html-report-1 (workflow) -> shard 0 (internal)
|
|
454
|
-
// html-report-2 (workflow) -> shard 1 (internal)
|
|
455
|
-
return artifactShard - 1;
|
|
456
|
-
}
|
|
457
|
-
// Fallback to the provided index
|
|
458
|
-
return i;
|
|
459
|
-
}
|
|
460
|
-
|
|
461
428
|
async fetchArtifactFromGitHubActions(i, file, filePath) {
|
|
462
429
|
const githubToken = process.env.SAFETYWINGTEST_GITHUB_TOKEN;
|
|
463
430
|
const repo = process.env.GITHUB_REPOSITORY;
|
|
@@ -472,16 +439,16 @@ class ResultsParser {
|
|
|
472
439
|
const githubApiUrl = `https://api.github.com/repos/${repo}/actions/artifacts`;
|
|
473
440
|
|
|
474
441
|
// Try multiple artifact naming patterns:
|
|
475
|
-
// 1. html-report-{i} (1-based workflow style)
|
|
476
|
-
// 2. blob-report-node-{i} (direct blob reports)
|
|
477
|
-
// 3. test-results-{i} (alternative naming)
|
|
478
|
-
// Note: i is
|
|
442
|
+
// 1. html-report-{i} (1-based workflow style, i is already 1-based)
|
|
443
|
+
// 2. blob-report-node-{i} (direct blob reports, i is 1-based)
|
|
444
|
+
// 3. test-results-{i} (alternative naming, i is 1-based)
|
|
445
|
+
// Note: i is 1-based internally now, so we use it directly for GitHub Actions artifacts
|
|
479
446
|
const artifactNamePatterns = [
|
|
480
|
-
`html-report-${i
|
|
481
|
-
`blob-report-node-${i}`, //
|
|
482
|
-
`
|
|
483
|
-
`
|
|
484
|
-
`test-results-${i
|
|
447
|
+
`html-report-${i}`, // 1-based workflow style (i is already 1-based)
|
|
448
|
+
`blob-report-node-${i}`, // 1-based direct blob
|
|
449
|
+
`html-report-${i - 1}`, // 0-based html-report (fallback)
|
|
450
|
+
`blob-report-node-${i - 1}`, // 0-based direct blob (fallback)
|
|
451
|
+
`test-results-${i}`, // Alternative naming
|
|
485
452
|
];
|
|
486
453
|
|
|
487
454
|
while (true) {
|
|
@@ -503,9 +470,9 @@ class ResultsParser {
|
|
|
503
470
|
artifact = listResponse.data.artifacts.find(
|
|
504
471
|
(a) => a.name === pattern ||
|
|
505
472
|
a.name.includes(`shard-${i}`) ||
|
|
506
|
-
a.name.includes(`shard-${i
|
|
473
|
+
a.name.includes(`shard-${i - 1}`) ||
|
|
507
474
|
a.name.includes(`node-${i}`) ||
|
|
508
|
-
a.name.includes(`node-${i
|
|
475
|
+
a.name.includes(`node-${i - 1}`)
|
|
509
476
|
);
|
|
510
477
|
if (artifact) {
|
|
511
478
|
console.log(`Found artifact: ${artifact.name} (matched pattern: ${pattern})`);
|
|
@@ -540,8 +507,8 @@ class ResultsParser {
|
|
|
540
507
|
file,
|
|
541
508
|
`playwright-report/${file}`,
|
|
542
509
|
`playwright-report/${file}`,
|
|
543
|
-
`html-report-${i + 1}/${file}`,
|
|
544
510
|
`html-report-${i}/${file}`,
|
|
511
|
+
`html-report-${i - 1}/${file}`,
|
|
545
512
|
];
|
|
546
513
|
for (const possiblePath of possiblePaths) {
|
|
547
514
|
zipEntry = zip.getEntry(possiblePath);
|
|
@@ -558,10 +525,7 @@ class ResultsParser {
|
|
|
558
525
|
const extractPath = path.join(path.dirname(filePath), `temp-artifact-${i}`);
|
|
559
526
|
zip.extractAllTo(extractPath, true);
|
|
560
527
|
|
|
561
|
-
//
|
|
562
|
-
const artifactShardIndex = this.extractShardIndexFromArtifactName(artifact.name, i);
|
|
563
|
-
|
|
564
|
-
// Look for the file recursively, also try with shard index from artifact name
|
|
528
|
+
// Look for the file recursively
|
|
565
529
|
const searchInDir = (dir, targetFile, alternateTargetFile = null) => {
|
|
566
530
|
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
567
531
|
for (const entry of entries) {
|
|
@@ -259,19 +259,29 @@ class SlackReporter {
|
|
|
259
259
|
// Find index of current job in the sorted group
|
|
260
260
|
const currentJobIndex = jobGroup.findIndex(j => j.id === currentJob?.id || j.name === currentJob?.name);
|
|
261
261
|
|
|
262
|
-
// Try to extract shard number from job name (e.g., "shard 3/4" -> index
|
|
262
|
+
// Try to extract shard number from job name (e.g., "shard 3/4" -> index 3)
|
|
263
|
+
// GitHub Actions uses 1-based indexing (shard 1, 2, 3, 4), we keep it 1-based internally for shard 1 logic
|
|
263
264
|
let shardIndex = currentJobIndex >= 0 ? currentJobIndex : 0;
|
|
264
265
|
if (currentJob && currentJob.name) {
|
|
265
266
|
const shardMatch = currentJob.name.match(/shard\s+(\d+)\/(\d+)/i);
|
|
266
267
|
if (shardMatch) {
|
|
267
|
-
const shardNum = parseInt(shardMatch[1], 10);
|
|
268
|
+
const shardNum = parseInt(shardMatch[1], 10); // 1-based from GitHub Actions (1, 2, 3, 4)
|
|
268
269
|
const totalFromName = parseInt(shardMatch[2], 10);
|
|
269
|
-
//
|
|
270
|
-
|
|
271
|
-
|
|
270
|
+
// Keep GitHub Actions 1-based indexing (shard 1 -> index 1, shard 2 -> index 2, etc.)
|
|
271
|
+
// This allows shard 1 to be the aggregator
|
|
272
|
+
shardIndex = shardNum;
|
|
273
|
+
this.log(`đ [GitHub Actions Detection] Extracted shard info from name: ${shardNum}/${totalFromName} (GitHub Actions 1-based) -> index ${shardIndex} (internal 1-based)`);
|
|
272
274
|
}
|
|
273
275
|
}
|
|
274
276
|
|
|
277
|
+
// If we couldn't extract from name but have job index, convert from 0-based array index to 1-based
|
|
278
|
+
// GitHub Actions jobs are numbered starting from 1, so array index 0 = shard 1, index 1 = shard 2, etc.
|
|
279
|
+
// We need to convert: array index 0 -> shard 1, array index 1 -> shard 2, etc.
|
|
280
|
+
if (shardIndex === currentJobIndex && currentJobIndex >= 0) {
|
|
281
|
+
shardIndex = currentJobIndex + 1; // Convert 0-based array index to 1-based shard index
|
|
282
|
+
this.log(`đ [GitHub Actions Detection] Using array index ${currentJobIndex} -> shard ${shardIndex} (1-based)`);
|
|
283
|
+
}
|
|
284
|
+
|
|
275
285
|
const totalShards = jobGroup.length;
|
|
276
286
|
this.log(`đ [GitHub Actions Detection] Detected via API:`);
|
|
277
287
|
this.log(`đ Total jobs in group: ${totalShards}`);
|
|
@@ -320,8 +330,8 @@ class SlackReporter {
|
|
|
320
330
|
this.log(message);
|
|
321
331
|
return;
|
|
322
332
|
}
|
|
323
|
-
// SHARDING SUPPORT - Stop slack messages for non-
|
|
324
|
-
// Only shard
|
|
333
|
+
// SHARDING SUPPORT - Stop slack messages for non-shard-1 shard(s)
|
|
334
|
+
// Only shard 1 should post when aggregating results from multiple shards
|
|
325
335
|
|
|
326
336
|
this.log('đ [Shard Detection] Starting shard detection logic...');
|
|
327
337
|
|
|
@@ -382,7 +392,7 @@ class SlackReporter {
|
|
|
382
392
|
current: parseInt(shardMatch[1], 10),
|
|
383
393
|
total: parseInt(shardMatch[2], 10)
|
|
384
394
|
};
|
|
385
|
-
this.log(`đ [Shard Detection] Found shard in command line args: ${playwrightShardInfo.current}/${playwrightShardInfo.total}`);
|
|
395
|
+
this.log(`đ [Shard Detection] Found shard in command line args: ${playwrightShardInfo.current}/${playwrightShardInfo.total} (1-based)`);
|
|
386
396
|
}
|
|
387
397
|
}
|
|
388
398
|
}
|
|
@@ -399,9 +409,9 @@ class SlackReporter {
|
|
|
399
409
|
// Step 2: Detect shard index (priority order: Playwright config > env vars > GitHub API)
|
|
400
410
|
let currentShardIndex;
|
|
401
411
|
if (playwrightShardInfo) {
|
|
402
|
-
// Playwright uses 1-based indexing,
|
|
403
|
-
currentShardIndex =
|
|
404
|
-
this.log(`đ [Shard Detection] Using Playwright shard config (
|
|
412
|
+
// Playwright uses 1-based indexing, keep it 1-based for shard 1 logic
|
|
413
|
+
currentShardIndex = playwrightShardInfo.current.toString();
|
|
414
|
+
this.log(`đ [Shard Detection] Using Playwright shard config (1-based): "${currentShardIndex}"`);
|
|
405
415
|
} else if (process.env.SHARD_INDEX !== undefined) {
|
|
406
416
|
currentShardIndex = process.env.SHARD_INDEX;
|
|
407
417
|
this.log(`đ [Shard Detection] Using SHARD_INDEX env var: "${currentShardIndex}"`);
|
|
@@ -463,6 +473,7 @@ class SlackReporter {
|
|
|
463
473
|
// CRITICAL: In CI (especially GitHub Actions), if we can't detect shard info, block posting
|
|
464
474
|
// This prevents duplicate messages when running with multiple shards
|
|
465
475
|
// BUT: Don't block if we're running locally (even if CI env vars are set)
|
|
476
|
+
// Note: GitHub Actions uses 1-based indexing (shard 1, 2, 3, 4), but we use 0-based internally
|
|
466
477
|
if (isActuallyInGitHubActions && currentShardIndex === undefined) {
|
|
467
478
|
if (process.env.GITHUB_ACTIONS === 'true') {
|
|
468
479
|
this.log(`â [Shard Detection] BLOCKING: GitHub Actions detected but shard index cannot be determined.`);
|
|
@@ -476,14 +487,14 @@ class SlackReporter {
|
|
|
476
487
|
this.log(`â Skipping Slack report to prevent duplicate messages from all shards.`);
|
|
477
488
|
this.log(`đĄ Fix: Add these env vars to your workflow:`);
|
|
478
489
|
this.log(`đĄ env:`);
|
|
479
|
-
this.log(`đĄ MATRIX_SHARD: $\{\{ strategy.job-index \}\} # 0-based (only shard
|
|
490
|
+
this.log(`đĄ MATRIX_SHARD: $\{\{ strategy.job-index \}\} # GitHub Actions uses 1-based, convert to 0-based (only shard 1/internal index 1 posts)`);
|
|
480
491
|
this.log(`đĄ MATRIX_COUNT: $\{\{ strategy.job-total \}\}`);
|
|
481
492
|
this.log(`đĄ OR ensure SAFETYWINGTEST_GITHUB_TOKEN has workflow permissions for API detection`);
|
|
482
493
|
return;
|
|
483
494
|
}
|
|
484
495
|
|
|
485
|
-
// Convert to number for comparison (default to
|
|
486
|
-
const shardIndexNum = currentShardIndex !== undefined ? parseInt(currentShardIndex, 10) :
|
|
496
|
+
// Convert to number for comparison (default to 1 if undefined, since shard 1 is the aggregator)
|
|
497
|
+
const shardIndexNum = currentShardIndex !== undefined ? parseInt(currentShardIndex, 10) : 1;
|
|
487
498
|
|
|
488
499
|
this.log(`đ [Shard Detection] Final values:`);
|
|
489
500
|
this.log(`đ currentShardIndex (string): ${currentShardIndex !== undefined ? `"${currentShardIndex}"` : 'undefined'}`);
|
|
@@ -495,30 +506,37 @@ class SlackReporter {
|
|
|
495
506
|
this.resultsParser.updateShardInfo(shardIndexNum, totalShards);
|
|
496
507
|
|
|
497
508
|
// For local runs with CI env vars set but not actually in CI, allow posting if single shard
|
|
498
|
-
// or if shard index is
|
|
509
|
+
// or if shard index is 1
|
|
499
510
|
if (!isActuallyInGitHubActions && process.env.CI && totalShards > 1 && currentShardIndex === undefined) {
|
|
500
511
|
this.log(`â ī¸ [Shard Detection] Local run with CI env vars but shard info unclear - allowing post (may cause duplicates)`);
|
|
501
512
|
}
|
|
502
513
|
|
|
503
|
-
// CRITICAL: For GitHub Actions with multiple shards, only shard
|
|
514
|
+
// CRITICAL: For GitHub Actions with multiple shards, only shard 1 should post
|
|
504
515
|
// If we're in CI with multiple shards and don't know which shard we are, block all posts
|
|
505
516
|
// This prevents duplicate messages when MATRIX_SHARD/MATRIX_INDEX is not set
|
|
517
|
+
// Note: GitHub Actions uses 1-based indexing (shard 1, 2, 3, 4)
|
|
518
|
+
// We use 0-based internally, so GitHub Actions shard 1 = internal index 1
|
|
506
519
|
if (isActuallyInGitHubActions && totalShards > 1 && currentShardIndex === undefined) {
|
|
507
520
|
this.log(`â [Shard Detection] BLOCKING: GitHub Actions detected with ${totalShards} shards but shard index not set (MATRIX_SHARD/MATRIX_INDEX missing).`);
|
|
508
521
|
this.log(`â Skipping Slack report to prevent duplicate messages from all shards.`);
|
|
509
522
|
this.log(`đĄ Fix: Add these env vars to your workflow:`);
|
|
510
523
|
this.log(`đĄ env:`);
|
|
511
|
-
this.log(`đĄ MATRIX_SHARD: $\{\{ strategy.job-index \}\} # 0-based (only shard
|
|
524
|
+
this.log(`đĄ MATRIX_SHARD: $\{\{ strategy.job-index \}\} # GitHub Actions uses 1-based, convert to 0-based (only shard 1/internal index 1 posts)`);
|
|
512
525
|
this.log(`đĄ MATRIX_COUNT: $\{\{ strategy.job-total \}\}`);
|
|
526
|
+
this.log(`đĄ OR ensure SAFETYWINGTEST_GITHUB_TOKEN has workflow permissions for API detection`);
|
|
513
527
|
return;
|
|
514
528
|
}
|
|
515
529
|
|
|
516
|
-
// Only allow shard
|
|
530
|
+
// Only allow shard 1 to post when there are multiple shards
|
|
517
531
|
// This ensures only one shard posts the aggregated report
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
532
|
+
// Note: GitHub Actions uses 1-based indexing (shard 1, 2, 3, 4)
|
|
533
|
+
// We use 0-based internally, so shard 1 in GitHub Actions = index 1 internally
|
|
534
|
+
if (totalShards > 1 && shardIndexNum !== 1) {
|
|
535
|
+
// Convert back to 1-based for display if from GitHub Actions
|
|
536
|
+
const displayShard = isActuallyInGitHubActions ? shardIndexNum + 1 : shardIndexNum;
|
|
537
|
+
this.log(`â [Shard Detection] BLOCKING: Non-shard-1 detected (shard ${displayShard} of ${totalShards}${isActuallyInGitHubActions ? ' [GitHub Actions 1-based]' : ''})`);
|
|
538
|
+
this.log(`â Stopping reporter for shard ${displayShard} of ${totalShards}`);
|
|
539
|
+
this.log(`âšī¸ Only shard 1 will post the aggregated report.`);
|
|
522
540
|
return;
|
|
523
541
|
}
|
|
524
542
|
|
|
@@ -526,7 +544,9 @@ class SlackReporter {
|
|
|
526
544
|
if (totalShards === 1) {
|
|
527
545
|
this.log(`â
[Shard Detection] ALLOWING: Single shard detected. Posting Slack report.`);
|
|
528
546
|
} else {
|
|
529
|
-
|
|
547
|
+
// Convert back to 1-based for display if from GitHub Actions
|
|
548
|
+
const displayShard = isActuallyInGitHubActions ? shardIndexNum + 1 : shardIndexNum;
|
|
549
|
+
this.log(`â
[Shard Detection] ALLOWING: Multiple shards detected (${totalShards}). Shard ${displayShard} (index ${shardIndexNum}${isActuallyInGitHubActions ? ', GitHub Actions 1-based' : ''}) will post aggregated report.`);
|
|
530
550
|
}
|
|
531
551
|
|
|
532
552
|
const resultSummary = await this.resultsParser.getParsedResults();
|
package/package.json
CHANGED