playwright-slack-report-burak 3.0.5 → 3.0.7

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.
@@ -10,7 +10,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
10
10
 
11
11
  // Helper function to get build URL and CI platform name
12
12
  const getBuildInfo = () => {
13
- if (process.env.GITHUB_ACTIONS || process.env.GITHUB_TOKEN) {
13
+ if (process.env.GITHUB_ACTIONS || process.env.safetywingtest_GITHUB_TOKEN) {
14
14
  const repo = process.env.GITHUB_REPOSITORY;
15
15
  const runId = process.env.GITHUB_RUN_ID;
16
16
  const serverUrl = process.env.GITHUB_SERVER_URL || 'https://github.com';
@@ -429,12 +429,12 @@ class ResultsParser {
429
429
  }
430
430
 
431
431
  async fetchArtifactFromGitHubActions(i, file, filePath) {
432
- const githubToken = process.env.GITHUB_TOKEN;
432
+ const githubToken = process.env.safetywingtest_GITHUB_TOKEN;
433
433
  const repo = process.env.GITHUB_REPOSITORY;
434
434
  const runId = process.env.GITHUB_RUN_ID;
435
435
 
436
436
  if (!githubToken || !repo) {
437
- console.error('GitHub Actions: Missing GITHUB_TOKEN or GITHUB_REPOSITORY');
437
+ console.error('GitHub Actions: Missing safetywingtest_GITHUB_TOKEN or GITHUB_REPOSITORY');
438
438
  return;
439
439
  }
440
440
 
@@ -6,6 +6,13 @@ const webhook_1 = require("@slack/webhook");
6
6
  const ResultsParser_1 = require("./ResultsParser");
7
7
  const SlackClient_1 = require("./SlackClient");
8
8
  const SlackWebhookClient_1 = require("./SlackWebhookClient");
9
+ let axios;
10
+ try {
11
+ axios = require('axios');
12
+ } catch (e) {
13
+ // axios optional for GitHub Actions API detection
14
+ axios = null;
15
+ }
9
16
  class SlackReporter {
10
17
  customLayout;
11
18
  customLayoutAsync;
@@ -64,6 +71,112 @@ class SlackReporter {
64
71
  onTestEnd(test, result) {
65
72
  this.resultsParser.addTestResult(test.parent.title, test, this.browsers);
66
73
  }
74
+ /**
75
+ * Detect GitHub Actions shard information using GitHub API
76
+ * Returns { shardIndex: number, totalShards: number } or null if detection fails
77
+ */
78
+ async detectGitHubActionsShardInfo() {
79
+ // Check if we're in GitHub Actions
80
+ if (!process.env.GITHUB_ACTIONS || process.env.GITHUB_ACTIONS !== 'true') {
81
+ return null;
82
+ }
83
+ const githubToken = process.env.safetywingtest_GITHUB_TOKEN;
84
+ const repo = process.env.GITHUB_REPOSITORY;
85
+ const runId = process.env.GITHUB_RUN_ID;
86
+ const currentJobId = process.env.GITHUB_JOB;
87
+ if (!githubToken || !repo || !runId) {
88
+ this.log(`🔍 [GitHub Actions Detection] Missing required env vars: safetywingtest_GITHUB_TOKEN=${!!githubToken}, GITHUB_REPOSITORY=${!!repo}, GITHUB_RUN_ID=${!!runId}`);
89
+ return null;
90
+ }
91
+ if (!axios) {
92
+ this.log(`🔍 [GitHub Actions Detection] axios not available, skipping API detection`);
93
+ return null;
94
+ }
95
+ try {
96
+ // Get workflow run details
97
+ const apiUrl = `https://api.github.com/repos/${repo}/actions/runs/${runId}/jobs`;
98
+ const response = await axios.get(apiUrl, {
99
+ headers: {
100
+ 'Authorization': `token ${githubToken}`,
101
+ 'Accept': 'application/vnd.github.v3+json'
102
+ },
103
+ params: {
104
+ per_page: 100
105
+ }
106
+ });
107
+ const jobs = response.data.jobs || [];
108
+ if (jobs.length === 0) {
109
+ this.log(`🔍 [GitHub Actions Detection] No jobs found in workflow run`);
110
+ return null;
111
+ }
112
+ // Filter jobs that are part of the same workflow run and have the same name (matrix jobs)
113
+ // Group by job name to find matrix jobs
114
+ const jobGroups = {};
115
+ for (const job of jobs) {
116
+ const jobName = job.name || '';
117
+ if (!jobGroups[jobName]) {
118
+ jobGroups[jobName] = [];
119
+ }
120
+ jobGroups[jobName].push(job);
121
+ }
122
+ // Find the job group that contains the current job
123
+ let currentJob = null;
124
+ let jobGroup = null;
125
+ for (const [jobName, jobsInGroup] of Object.entries(jobGroups)) {
126
+ currentJob = jobsInGroup.find(j => j.id.toString() === currentJobId || j.name === currentJobId);
127
+ if (currentJob) {
128
+ jobGroup = jobsInGroup;
129
+ break;
130
+ }
131
+ }
132
+ // If we can't find current job by ID, try to match by name from GITHUB_JOB
133
+ if (!currentJob && currentJobId) {
134
+ for (const [jobName, jobsInGroup] of Object.entries(jobGroups)) {
135
+ if (jobName.includes(currentJobId) || currentJobId.includes(jobName)) {
136
+ jobGroup = jobsInGroup;
137
+ // Try to find the job by looking at run_attempt or other identifiers
138
+ currentJob = jobsInGroup[0];
139
+ break;
140
+ }
141
+ }
142
+ }
143
+ // If still no match, use all jobs (might be a single job or we can't determine)
144
+ if (!jobGroup || jobGroup.length === 0) {
145
+ jobGroup = jobs;
146
+ currentJob = jobs[0];
147
+ }
148
+ // Sort jobs by name or ID to get consistent ordering
149
+ jobGroup.sort((a, b) => {
150
+ if (a.name && b.name) {
151
+ return a.name.localeCompare(b.name);
152
+ }
153
+ return a.id - b.id;
154
+ });
155
+ // Find index of current job
156
+ const currentJobIndex = jobGroup.findIndex(j =>
157
+ j.id === currentJob?.id ||
158
+ j.name === currentJob?.name ||
159
+ (currentJobId && (j.name === currentJobId || j.name?.includes(currentJobId)))
160
+ );
161
+ const shardIndex = currentJobIndex >= 0 ? currentJobIndex : 0;
162
+ const totalShards = jobGroup.length;
163
+ this.log(`🔍 [GitHub Actions Detection] Detected via API:`);
164
+ this.log(`🔍 Total jobs in group: ${totalShards}`);
165
+ this.log(`🔍 Current job index: ${shardIndex}`);
166
+ this.log(`🔍 Current job ID: ${currentJobId}`);
167
+ this.log(`🔍 Current job name: ${currentJob?.name || 'unknown'}`);
168
+ return {
169
+ shardIndex,
170
+ totalShards
171
+ };
172
+ } catch (error) {
173
+ this.log(`🔍 [GitHub Actions Detection] Failed to detect via API: ${error.message}`);
174
+ if (error.response) {
175
+ this.log(`🔍 Status: ${error.response.status}, Data: ${JSON.stringify(error.response.data)}`);
176
+ }
177
+ return null;
178
+ }
179
+ }
67
180
  async onEnd() {
68
181
  const { okToProceed, message } = this.preChecks();
69
182
  if (!okToProceed) {
@@ -78,22 +191,48 @@ class SlackReporter {
78
191
  // Log all relevant environment variables
79
192
  this.log(`🔍 [Shard Detection] Environment variables:`);
80
193
  this.log(`🔍 CI: ${process.env.CI !== undefined ? `"${process.env.CI}"` : 'undefined'}`);
194
+ this.log(`🔍 GITHUB_ACTIONS: ${process.env.GITHUB_ACTIONS !== undefined ? `"${process.env.GITHUB_ACTIONS}"` : 'undefined'}`);
195
+ this.log(`🔍 GITHUB_JOB: ${process.env.GITHUB_JOB !== undefined ? `"${process.env.GITHUB_JOB}"` : 'undefined'}`);
81
196
  this.log(`🔍 MATRIX_SHARD: ${process.env.MATRIX_SHARD !== undefined ? `"${process.env.MATRIX_SHARD}"` : 'undefined'}`);
82
197
  this.log(`🔍 MATRIX_INDEX: ${process.env.MATRIX_INDEX !== undefined ? `"${process.env.MATRIX_INDEX}"` : 'undefined'}`);
83
198
  this.log(`🔍 MATRIX_COUNT: ${process.env.MATRIX_COUNT !== undefined ? `"${process.env.MATRIX_COUNT}"` : 'undefined'}`);
84
199
 
85
- // Detect shard index from multiple sources (GitHub Actions compatible)
86
- // Note: In GitHub Actions workflow, you need to set these env vars:
87
- // env:
88
- // MATRIX_SHARD: ${{ strategy.job-index }} # 0-based
89
- // MATRIX_COUNT: ${{ strategy.job-total }}
200
+ // Step 1: Try GitHub Actions API detection (most robust for GitHub Actions)
201
+ let githubApiShardInfo = null;
202
+ if (process.env.GITHUB_ACTIONS === 'true') {
203
+ this.log(`🔍 [Shard Detection] Attempting GitHub Actions API detection...`);
204
+ githubApiShardInfo = await this.detectGitHubActionsShardInfo();
205
+ }
206
+
207
+ // Step 2: Detect shard index from environment variables (priority)
90
208
  let currentShardIndex = process.env.MATRIX_SHARD !== undefined
91
209
  ? process.env.MATRIX_SHARD
92
210
  : (process.env.MATRIX_INDEX !== undefined ? process.env.MATRIX_INDEX : undefined);
93
211
 
212
+ // Step 3: Use GitHub API detection if env vars not set
213
+ if (currentShardIndex === undefined && githubApiShardInfo) {
214
+ currentShardIndex = githubApiShardInfo.shardIndex.toString();
215
+ this.log(`🔍 [Shard Detection] Using GitHub API detected shardIndex: "${currentShardIndex}"`);
216
+ }
217
+
94
218
  this.log(`🔍 [Shard Detection] Initial currentShardIndex: ${currentShardIndex !== undefined ? `"${currentShardIndex}"` : 'undefined'}`);
95
219
 
96
- // Get shard info from ResultsParser which might have detected it
220
+ // Step 4: Get shard count from multiple sources (priority order)
221
+ let totalShards;
222
+ if (process.env.MATRIX_COUNT !== undefined) {
223
+ totalShards = parseInt(process.env.MATRIX_COUNT, 10);
224
+ this.log(`🔍 [Shard Detection] Using MATRIX_COUNT env var: ${totalShards}`);
225
+ } else if (githubApiShardInfo && githubApiShardInfo.totalShards > 1) {
226
+ totalShards = githubApiShardInfo.totalShards;
227
+ this.log(`🔍 [Shard Detection] Using GitHub API detected totalShards: ${totalShards}`);
228
+ } else {
229
+ // Fallback to ResultsParser
230
+ const parserTotalShards = this.resultsParser.totalShardCount;
231
+ totalShards = parserTotalShards;
232
+ this.log(`🔍 [Shard Detection] Using ResultsParser totalShards: ${totalShards}`);
233
+ }
234
+
235
+ // Get shard info from ResultsParser for fallback
97
236
  const parserShardIndex = this.resultsParser.shardIndex;
98
237
  const parserTotalShards = this.resultsParser.totalShardCount;
99
238
 
@@ -101,15 +240,38 @@ class SlackReporter {
101
240
  this.log(`🔍 parserShardIndex: ${parserShardIndex !== undefined ? parserShardIndex : 'undefined'}`);
102
241
  this.log(`🔍 parserTotalShards: ${parserTotalShards !== undefined ? parserTotalShards : 'undefined'}`);
103
242
 
104
- // Use parser values if env vars not set
105
- const totalShards = process.env.MATRIX_COUNT ? parseInt(process.env.MATRIX_COUNT, 10) : parserTotalShards;
106
-
107
243
  this.log(`🔍 [Shard Detection] Calculated totalShards: ${totalShards}`);
108
244
 
109
- // If we still don't have shard index, try to infer from parser
110
- if (currentShardIndex === undefined && parserShardIndex !== undefined) {
111
- currentShardIndex = parserShardIndex.toString();
112
- this.log(`🔍 [Shard Detection] Using parserShardIndex as fallback: "${currentShardIndex}"`);
245
+ // If we still don't have shard index, try to infer from parser or GitHub API
246
+ if (currentShardIndex === undefined) {
247
+ if (githubApiShardInfo && githubApiShardInfo.shardIndex !== undefined) {
248
+ currentShardIndex = githubApiShardInfo.shardIndex.toString();
249
+ this.log(`🔍 [Shard Detection] Using GitHub API shardIndex as fallback: "${currentShardIndex}"`);
250
+ } else if (parserShardIndex !== undefined) {
251
+ currentShardIndex = parserShardIndex.toString();
252
+ this.log(`🔍 [Shard Detection] Using parserShardIndex as fallback: "${currentShardIndex}"`);
253
+ }
254
+ }
255
+
256
+ // CRITICAL: In CI (especially GitHub Actions), if we can't detect shard info, block posting
257
+ // This prevents duplicate messages when running with multiple shards
258
+ if (process.env.CI && currentShardIndex === undefined) {
259
+ if (process.env.GITHUB_ACTIONS === 'true') {
260
+ this.log(`❌ [Shard Detection] BLOCKING: GitHub Actions detected but shard index cannot be determined.`);
261
+ this.log(`❌ Attempted: GitHub API detection ${githubApiShardInfo ? 'succeeded' : 'failed'}, env vars not set`);
262
+ this.log(`❌ This prevents duplicate messages when running with multiple shards.`);
263
+ } else {
264
+ this.log(`❌ [Shard Detection] BLOCKING: CI environment detected but shard info not set.`);
265
+ this.log(`❌ MATRIX_COUNT: undefined, MATRIX_SHARD: undefined`);
266
+ this.log(`❌ This prevents duplicate messages when running with multiple shards.`);
267
+ }
268
+ this.log(`❌ Skipping Slack report to prevent duplicate messages from all shards.`);
269
+ this.log(`💡 Fix: Add these env vars to your workflow:`);
270
+ this.log(`💡 env:`);
271
+ this.log(`💡 MATRIX_SHARD: $\{\{ strategy.job-index \}\} # 0-based (only shard 0 posts)`);
272
+ this.log(`💡 MATRIX_COUNT: $\{\{ strategy.job-total \}\}`);
273
+ this.log(`💡 OR ensure safetywingtest_GITHUB_TOKEN has workflow permissions for API detection`);
274
+ return;
113
275
  }
114
276
 
115
277
  // Convert to number for comparison (default to 0 if undefined)
package/package.json CHANGED
@@ -3,7 +3,8 @@
3
3
  "@slack/web-api": "^6.8.1",
4
4
  "@slack/webhook": "^6.1.0",
5
5
  "https-proxy-agent": "^7.0.1",
6
- "adm-zip": "^0.5.10"
6
+ "adm-zip": "^0.5.10",
7
+ "axios": "^1.0.0"
7
8
  },
8
9
  "devDependencies": {
9
10
  "@playwright/test": "^1.23.3",
@@ -31,7 +32,7 @@
31
32
  "lint-fix": "npx eslint . --ext .ts --fix"
32
33
  },
33
34
  "name": "playwright-slack-report-burak",
34
- "version": "3.0.5",
35
+ "version": "3.0.7",
35
36
  "main": "index.js",
36
37
  "types": "dist/index.d.ts",
37
38
  "author": "Burak B. <burak.boluk@hotmail.com>",