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.
@@ -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 (0-based)
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
- console.log(`🔍 [ResultsParser] Current shard: ${this.shardIndex}, Total shards: ${this.totalShardCount}`);
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 === 0 && this.allNodeSummaryFilesExist() && this.allBlobZipsExist()) {
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
- for (let i = 0; i < this.totalShardCount; i++) {
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
- for (let i = 0; i < this.totalShardCount; i++) {
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
- for (let i = 0; i < this.totalShardCount; i++) {
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 0: Waiting for artifacts from ${this.totalShardCount - 1} other shard(s)...`);
375
+ console.log(`Shard 1: Waiting for artifacts from ${this.totalShardCount - 1} other shard(s)...`);
396
376
 
397
- // Fetch artifacts for shards 1 to totalShardCount-1 (since we're shard 0)
398
- // We need to fetch from all other shards before proceeding
399
- for (let i = 1; i < this.totalShardCount; i++) {
400
- console.log(`Shard 0: Fetching artifacts from shard ${i}...`);
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 0: Waiting for all artifacts to be available (attempt ${retryCount + 1}/${maxRetries})...`);
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 < this.totalShardCount; 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 0: Re-fetching missing node_summary_${i}.json...`);
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 0: Re-fetching missing blob-report-node-${i}.zip...`);
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 0: Successfully fetched all artifacts from ${this.totalShardCount - 1} other shard(s).`);
417
+ console.log(`Shard 1: Successfully fetched all artifacts from ${this.totalShardCount - 1} other shard(s).`);
436
418
  } else {
437
- console.warn(`Shard 0: Warning - Some artifacts may still be missing after ${maxRetries} attempts.`);
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 0-based internally, so for 1-based workflows we need i+1
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 + 1}`, // 1-based workflow style (most common)
481
- `blob-report-node-${i}`, // 0-based direct blob
482
- `blob-report-node-${i + 1}`, // 1-based direct blob
483
- `html-report-${i}`, // 0-based html-report
484
- `test-results-${i + 1}`, // Alternative naming
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 + 1}`) ||
473
+ a.name.includes(`shard-${i - 1}`) ||
507
474
  a.name.includes(`node-${i}`) ||
508
- a.name.includes(`node-${i + 1}`)
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
- // Extract shard index from artifact name to handle files named with index 0
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 2)
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
- // Convert to 0-based index (shard 3/4 -> index 2)
270
- shardIndex = shardNum - 1;
271
- this.log(`🔍 [GitHub Actions Detection] Extracted shard info from name: ${shardNum}/${totalFromName} -> index ${shardIndex}`);
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-zero index shard(s)
324
- // Only shard 0 (0-based) should post when aggregating results from multiple shards
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, convert to 0-based
403
- currentShardIndex = (playwrightShardInfo.current - 1).toString();
404
- this.log(`🔍 [Shard Detection] Using Playwright shard config (converted to 0-based): "${currentShardIndex}"`);
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 0 posts)`);
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 0 if undefined)
486
- const shardIndexNum = currentShardIndex !== undefined ? parseInt(currentShardIndex, 10) : 0;
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 0
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 0 should post
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 0 posts)`);
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 0 (0-based) to post when there are multiple shards
530
+ // Only allow shard 1 to post when there are multiple shards
517
531
  // This ensures only one shard posts the aggregated report
518
- if (totalShards > 1 && shardIndexNum !== 0) {
519
- this.log(`❌ [Shard Detection] BLOCKING: Non-zero shard detected (shard ${currentShardIndex} of ${totalShards})`);
520
- this.log(`❌ Stopping reporter for non-zero index shard ${currentShardIndex} of ${totalShards}`);
521
- this.log(`â„šī¸ Only shard 0 will post the aggregated report.`);
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
- this.log(`✅ [Shard Detection] ALLOWING: Multiple shards detected (${totalShards}). Shard ${currentShardIndex} (index ${shardIndexNum}) will post aggregated report.`);
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
@@ -32,7 +32,7 @@
32
32
  "lint-fix": "npx eslint . --ext .ts --fix"
33
33
  },
34
34
  "name": "playwright-slack-report-burak",
35
- "version": "3.0.18",
35
+ "version": "3.0.20",
36
36
  "main": "index.js",
37
37
  "types": "dist/index.d.ts",
38
38
  "author": "Burak B. <burak.boluk@hotmail.com>",