@flakiness/sdk 0.149.0 → 0.150.0
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/README.md +9 -4
- package/lib/_internalUtils.js +2 -68
- package/lib/browser.js +2 -149
- package/lib/ciUtils.js +0 -1
- package/lib/createEnvironment.js +1 -105
- package/lib/createTestStepSnippets.js +1 -19
- package/lib/flakinessProjectConfig.js +6 -317
- package/lib/gitWorktree.js +8 -112
- package/lib/index.js +9 -1244
- package/lib/normalizeReport.js +2 -3
- package/lib/reportUtils.js +9 -384
- package/lib/reportUtilsBrowser.js +3 -132
- package/lib/showReport.js +3 -612
- package/lib/staticServer.js +7 -8
- package/lib/stripAnsi.js +1 -2
- package/lib/systemUtilizationSampler.js +2 -3
- package/lib/uploadReport.js +33 -148
- package/lib/visitTests.js +0 -1
- package/lib/writeReport.js +0 -1
- package/package.json +14 -5
- package/types/src/_internalUtils.d.ts +13 -0
- package/types/src/_internalUtils.d.ts.map +1 -0
- package/types/src/browser.d.ts +3 -0
- package/types/src/browser.d.ts.map +1 -0
- package/types/src/ciUtils.d.ts +33 -0
- package/types/src/ciUtils.d.ts.map +1 -0
- package/types/src/createEnvironment.d.ts +40 -0
- package/types/src/createEnvironment.d.ts.map +1 -0
- package/types/src/createTestStepSnippets.d.ts +30 -0
- package/types/src/createTestStepSnippets.d.ts.map +1 -0
- package/types/src/flakinessProjectConfig.d.ts +103 -0
- package/types/src/flakinessProjectConfig.d.ts.map +1 -0
- package/types/src/gitWorktree.d.ts +139 -0
- package/types/src/gitWorktree.d.ts.map +1 -0
- package/types/src/index.d.ts +10 -0
- package/types/src/index.d.ts.map +1 -0
- package/types/src/normalizeReport.d.ts +26 -0
- package/types/src/normalizeReport.d.ts.map +1 -0
- package/types/src/reportUtils.d.ts +7 -0
- package/types/src/reportUtils.d.ts.map +1 -0
- package/types/src/reportUtilsBrowser.d.ts +4 -0
- package/types/src/reportUtilsBrowser.d.ts.map +1 -0
- package/types/src/showReport.d.ts +18 -0
- package/types/src/showReport.d.ts.map +1 -0
- package/types/src/staticServer.d.ts +16 -0
- package/types/src/staticServer.d.ts.map +1 -0
- package/types/src/stripAnsi.d.ts +19 -0
- package/types/src/stripAnsi.d.ts.map +1 -0
- package/types/src/systemUtilizationSampler.d.ts +43 -0
- package/types/src/systemUtilizationSampler.d.ts.map +1 -0
- package/types/src/uploadReport.d.ts +196 -0
- package/types/src/uploadReport.d.ts.map +1 -0
- package/types/src/visitTests.d.ts +28 -0
- package/types/src/visitTests.d.ts.map +1 -0
- package/types/src/writeReport.d.ts +45 -0
- package/types/src/writeReport.d.ts.map +1 -0
- package/types/tsconfig.tsbuildinfo +0 -1
package/lib/uploadReport.js
CHANGED
|
@@ -1,129 +1,7 @@
|
|
|
1
|
-
// src/uploadReport.ts
|
|
2
1
|
import assert from "assert";
|
|
3
|
-
import fs2 from "fs";
|
|
4
|
-
import { URL } from "url";
|
|
5
|
-
|
|
6
|
-
// src/_internalUtils.ts
|
|
7
|
-
import crypto from "crypto";
|
|
8
2
|
import fs from "fs";
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
import util from "util";
|
|
12
|
-
import zlib from "zlib";
|
|
13
|
-
var asyncBrotliCompress = util.promisify(zlib.brotliCompress);
|
|
14
|
-
async function compressTextAsync(text) {
|
|
15
|
-
return asyncBrotliCompress(text, {
|
|
16
|
-
chunkSize: 32 * 1024,
|
|
17
|
-
params: {
|
|
18
|
-
[zlib.constants.BROTLI_PARAM_QUALITY]: 6,
|
|
19
|
-
[zlib.constants.BROTLI_PARAM_MODE]: zlib.constants.BROTLI_MODE_TEXT
|
|
20
|
-
}
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
var FLAKINESS_DBG = !!process.env.FLAKINESS_DBG;
|
|
24
|
-
function errorText(error) {
|
|
25
|
-
return FLAKINESS_DBG ? error.stack : error.message;
|
|
26
|
-
}
|
|
27
|
-
async function retryWithBackoff(job, backoff = []) {
|
|
28
|
-
for (const timeout of backoff) {
|
|
29
|
-
try {
|
|
30
|
-
return await job();
|
|
31
|
-
} catch (e) {
|
|
32
|
-
if (e instanceof AggregateError)
|
|
33
|
-
console.error(`[flakiness.io err]`, errorText(e.errors[0]));
|
|
34
|
-
else if (e instanceof Error)
|
|
35
|
-
console.error(`[flakiness.io err]`, errorText(e));
|
|
36
|
-
else
|
|
37
|
-
console.error(`[flakiness.io err]`, e);
|
|
38
|
-
await new Promise((x) => setTimeout(x, timeout));
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
return await job();
|
|
42
|
-
}
|
|
43
|
-
var httpUtils;
|
|
44
|
-
((httpUtils2) => {
|
|
45
|
-
function createRequest({ url, method = "get", headers = {} }) {
|
|
46
|
-
let resolve;
|
|
47
|
-
let reject;
|
|
48
|
-
const responseDataPromise = new Promise((a, b) => {
|
|
49
|
-
resolve = a;
|
|
50
|
-
reject = b;
|
|
51
|
-
});
|
|
52
|
-
const protocol = url.startsWith("https") ? https : http;
|
|
53
|
-
headers = Object.fromEntries(Object.entries(headers).filter(([key, value]) => value !== void 0));
|
|
54
|
-
const request = protocol.request(url, { method, headers }, (res) => {
|
|
55
|
-
const chunks = [];
|
|
56
|
-
res.on("data", (chunk) => chunks.push(chunk));
|
|
57
|
-
res.on("end", () => {
|
|
58
|
-
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300)
|
|
59
|
-
resolve(Buffer.concat(chunks));
|
|
60
|
-
else
|
|
61
|
-
reject(new Error(`Request to ${url} failed with ${res.statusCode}`));
|
|
62
|
-
});
|
|
63
|
-
res.on("error", (error) => reject(error));
|
|
64
|
-
});
|
|
65
|
-
request.on("error", reject);
|
|
66
|
-
return { request, responseDataPromise };
|
|
67
|
-
}
|
|
68
|
-
httpUtils2.createRequest = createRequest;
|
|
69
|
-
async function getBuffer(url, backoff) {
|
|
70
|
-
return await retryWithBackoff(async () => {
|
|
71
|
-
const { request, responseDataPromise } = createRequest({ url });
|
|
72
|
-
request.end();
|
|
73
|
-
return await responseDataPromise;
|
|
74
|
-
}, backoff);
|
|
75
|
-
}
|
|
76
|
-
httpUtils2.getBuffer = getBuffer;
|
|
77
|
-
async function getText(url, backoff) {
|
|
78
|
-
const buffer = await getBuffer(url, backoff);
|
|
79
|
-
return buffer.toString("utf-8");
|
|
80
|
-
}
|
|
81
|
-
httpUtils2.getText = getText;
|
|
82
|
-
async function getJSON(url) {
|
|
83
|
-
return JSON.parse(await getText(url));
|
|
84
|
-
}
|
|
85
|
-
httpUtils2.getJSON = getJSON;
|
|
86
|
-
async function postText(url, text, backoff) {
|
|
87
|
-
const headers = {
|
|
88
|
-
"Content-Type": "application/json",
|
|
89
|
-
"Content-Length": Buffer.byteLength(text) + ""
|
|
90
|
-
};
|
|
91
|
-
return await retryWithBackoff(async () => {
|
|
92
|
-
const { request, responseDataPromise } = createRequest({ url, headers, method: "post" });
|
|
93
|
-
request.write(text);
|
|
94
|
-
request.end();
|
|
95
|
-
return await responseDataPromise;
|
|
96
|
-
}, backoff);
|
|
97
|
-
}
|
|
98
|
-
httpUtils2.postText = postText;
|
|
99
|
-
async function postJSON(url, json, backoff) {
|
|
100
|
-
const buffer = await postText(url, JSON.stringify(json), backoff);
|
|
101
|
-
return JSON.parse(buffer.toString("utf-8"));
|
|
102
|
-
}
|
|
103
|
-
httpUtils2.postJSON = postJSON;
|
|
104
|
-
})(httpUtils || (httpUtils = {}));
|
|
105
|
-
function sha1Text(data) {
|
|
106
|
-
const hash = crypto.createHash("sha1");
|
|
107
|
-
hash.update(data);
|
|
108
|
-
return hash.digest("hex");
|
|
109
|
-
}
|
|
110
|
-
function sha1File(filePath) {
|
|
111
|
-
return new Promise((resolve, reject) => {
|
|
112
|
-
const hash = crypto.createHash("sha1");
|
|
113
|
-
const stream = fs.createReadStream(filePath);
|
|
114
|
-
stream.on("data", (chunk) => {
|
|
115
|
-
hash.update(chunk);
|
|
116
|
-
});
|
|
117
|
-
stream.on("end", () => {
|
|
118
|
-
resolve(hash.digest("hex"));
|
|
119
|
-
});
|
|
120
|
-
stream.on("error", (err) => {
|
|
121
|
-
reject(err);
|
|
122
|
-
});
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// src/uploadReport.ts
|
|
3
|
+
import { URL } from "url";
|
|
4
|
+
import { compressTextAsync, retryWithBackoff, sha1File, sha1Text } from "./_internalUtils.js";
|
|
127
5
|
async function createFileAttachment(contentType, filePath) {
|
|
128
6
|
return {
|
|
129
7
|
type: "file",
|
|
@@ -170,8 +48,8 @@ async function uploadReport(report, attachments, options) {
|
|
|
170
48
|
return { status: "failed", error: errorMessage };
|
|
171
49
|
}
|
|
172
50
|
}
|
|
173
|
-
|
|
174
|
-
|
|
51
|
+
const HTTP_BACKOFF = [100, 500, 1e3, 1e3, 1e3, 1e3];
|
|
52
|
+
class ReportUpload {
|
|
175
53
|
_report;
|
|
176
54
|
_attachments;
|
|
177
55
|
_options;
|
|
@@ -232,14 +110,15 @@ var ReportUpload = class {
|
|
|
232
110
|
"Content-Encoding": "br"
|
|
233
111
|
};
|
|
234
112
|
await retryWithBackoff(async () => {
|
|
235
|
-
const
|
|
236
|
-
|
|
113
|
+
const response = await fetch(uploadUrl, {
|
|
114
|
+
method: "PUT",
|
|
237
115
|
headers,
|
|
238
|
-
|
|
116
|
+
body: Buffer.from(compressed)
|
|
239
117
|
});
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
118
|
+
if (!response.ok) {
|
|
119
|
+
throw new Error(`Request to ${uploadUrl} failed with ${response.status}`);
|
|
120
|
+
}
|
|
121
|
+
await response.arrayBuffer();
|
|
243
122
|
}, HTTP_BACKOFF);
|
|
244
123
|
}
|
|
245
124
|
async _uploadAttachment(attachment, uploadUrl) {
|
|
@@ -247,41 +126,47 @@ var ReportUpload = class {
|
|
|
247
126
|
const compressable = mimeType.startsWith("text/") || mimeType.endsWith("+json") || mimeType.endsWith("+text") || mimeType.endsWith("+xml");
|
|
248
127
|
if (!compressable && attachment.type === "file") {
|
|
249
128
|
await retryWithBackoff(async () => {
|
|
250
|
-
const
|
|
251
|
-
|
|
129
|
+
const fileBuffer = await fs.promises.readFile(attachment.path);
|
|
130
|
+
const response = await fetch(uploadUrl, {
|
|
131
|
+
method: "PUT",
|
|
252
132
|
headers: {
|
|
253
133
|
"Content-Type": attachment.contentType,
|
|
254
|
-
"Content-Length":
|
|
134
|
+
"Content-Length": fileBuffer.length + ""
|
|
255
135
|
},
|
|
256
|
-
|
|
136
|
+
body: new Uint8Array(fileBuffer)
|
|
257
137
|
});
|
|
258
|
-
|
|
259
|
-
|
|
138
|
+
if (!response.ok) {
|
|
139
|
+
throw new Error(`Request to ${uploadUrl} failed with ${response.status}`);
|
|
140
|
+
}
|
|
141
|
+
await response.arrayBuffer();
|
|
260
142
|
}, HTTP_BACKOFF);
|
|
261
143
|
return;
|
|
262
144
|
}
|
|
263
|
-
let buffer = attachment.type === "buffer" ? attachment.body : await
|
|
145
|
+
let buffer = attachment.type === "buffer" ? attachment.body : await fs.promises.readFile(attachment.path);
|
|
264
146
|
assert(buffer);
|
|
265
147
|
const encoding = compressable ? "br" : void 0;
|
|
266
148
|
if (compressable)
|
|
267
149
|
buffer = await compressTextAsync(buffer);
|
|
268
150
|
const headers = {
|
|
269
151
|
"Content-Type": attachment.contentType,
|
|
270
|
-
"Content-Length": Buffer.byteLength(buffer) + ""
|
|
271
|
-
"Content-Encoding": encoding
|
|
152
|
+
"Content-Length": Buffer.byteLength(buffer) + ""
|
|
272
153
|
};
|
|
154
|
+
if (encoding) {
|
|
155
|
+
headers["Content-Encoding"] = encoding;
|
|
156
|
+
}
|
|
273
157
|
await retryWithBackoff(async () => {
|
|
274
|
-
const
|
|
275
|
-
|
|
158
|
+
const response = await fetch(uploadUrl, {
|
|
159
|
+
method: "PUT",
|
|
276
160
|
headers,
|
|
277
|
-
|
|
161
|
+
body: new Uint8Array(buffer)
|
|
278
162
|
});
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
163
|
+
if (!response.ok) {
|
|
164
|
+
throw new Error(`Request to ${uploadUrl} failed with ${response.status}`);
|
|
165
|
+
}
|
|
166
|
+
await response.arrayBuffer();
|
|
282
167
|
}, HTTP_BACKOFF);
|
|
283
168
|
}
|
|
284
|
-
}
|
|
169
|
+
}
|
|
285
170
|
export {
|
|
286
171
|
createDataAttachment,
|
|
287
172
|
createFileAttachment,
|
package/lib/visitTests.js
CHANGED
package/lib/writeReport.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@flakiness/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.150.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -17,18 +17,27 @@
|
|
|
17
17
|
"type": "module",
|
|
18
18
|
"description": "",
|
|
19
19
|
"types": "./types/index.d.ts",
|
|
20
|
-
"scripts": {
|
|
20
|
+
"scripts": {
|
|
21
|
+
"minor": "./version.mjs minor",
|
|
22
|
+
"patch": "./version.mjs patch"
|
|
23
|
+
},
|
|
21
24
|
"keywords": [],
|
|
22
25
|
"author": "Degu Labs, Inc",
|
|
23
|
-
"license": "
|
|
26
|
+
"license": "MIT",
|
|
24
27
|
"devDependencies": {
|
|
25
|
-
"@types/babel__code-frame": "^7.0.6"
|
|
28
|
+
"@types/babel__code-frame": "^7.0.6",
|
|
29
|
+
"@types/debug": "^4.1.12",
|
|
30
|
+
"@types/node": "^25.0.3",
|
|
31
|
+
"esbuild": "^0.27.0",
|
|
32
|
+
"kubik": "^0.24.0",
|
|
33
|
+
"tsx": "^4.21.0",
|
|
34
|
+
"typescript": "^5.6.2"
|
|
26
35
|
},
|
|
27
36
|
"dependencies": {
|
|
28
37
|
"@babel/code-frame": "^7.26.2",
|
|
29
38
|
"@flakiness/flakiness-report": "^0.16.0",
|
|
30
39
|
"chalk": "^5.6.2",
|
|
31
|
-
"debug": "^4.3
|
|
40
|
+
"debug": "^4.4.3",
|
|
32
41
|
"open": "^10.2.0",
|
|
33
42
|
"stable-hash": "^0.0.6"
|
|
34
43
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { SpawnSyncOptionsWithStringEncoding } from 'child_process';
|
|
2
|
+
import crypto from 'crypto';
|
|
3
|
+
export declare function compressTextAsync(text: string | Buffer): Promise<Buffer>;
|
|
4
|
+
export type Brand<T, Brand extends string> = T & {
|
|
5
|
+
readonly [B in Brand as `__${B}_brand`]: never;
|
|
6
|
+
};
|
|
7
|
+
export declare function errorText(error: Error): string | undefined;
|
|
8
|
+
export declare function retryWithBackoff<T>(job: () => Promise<T>, backoff?: number[]): Promise<T>;
|
|
9
|
+
export declare function shell(command: string, args?: string[], options?: SpawnSyncOptionsWithStringEncoding): string | undefined;
|
|
10
|
+
export declare function sha1Text(data: crypto.BinaryLike): string;
|
|
11
|
+
export declare function sha1File(filePath: string): Promise<string>;
|
|
12
|
+
export declare function randomUUIDBase62(): string;
|
|
13
|
+
//# sourceMappingURL=_internalUtils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_internalUtils.d.ts","sourceRoot":"","sources":["../../src/_internalUtils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,kCAAkC,EAAE,MAAM,eAAe,CAAC;AAC9E,OAAO,MAAM,MAAM,QAAQ,CAAC;AAQ5B,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAQ5E;AAED,MAAM,MAAM,KAAK,CAAC,CAAC,EAAE,KAAK,SAAS,MAAM,IAAI,CAAC,GAAG;IAC/C,QAAQ,EAAE,CAAC,IAAI,KAAK,IAAI,KAAK,CAAC,QAAQ,GAAG,KAAK;CAC/C,CAAC;AAGF,wBAAgB,SAAS,CAAC,KAAK,EAAE,KAAK,sBAErC;AAED,wBAAsB,gBAAgB,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,GAAE,MAAM,EAAO,GAAG,OAAO,CAAC,CAAC,CAAC,CAenG;AAED,wBAAgB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,kCAAkC,sBAWnG;AAED,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,UAAU,UAI/C;AAED,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAc1D;AAED,wBAAgB,gBAAgB,IAAI,MAAM,CAczC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../src/browser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAE9D,OAAO,KAAK,WAAW,MAAM,yBAAyB,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A collection of utilities to extract information about continuous integration providers.
|
|
3
|
+
*
|
|
4
|
+
* This namespace provides functions to automatically detect and extract useful information
|
|
5
|
+
* from various CI/CD environments, including GitHub Actions, Azure DevOps, Jenkins, and others.
|
|
6
|
+
*/
|
|
7
|
+
export declare namespace CIUtils {
|
|
8
|
+
/**
|
|
9
|
+
* Automatically extracts the run URL for common continuous integration providers.
|
|
10
|
+
*
|
|
11
|
+
* This function attempts to detect the current CI environment and construct the appropriate
|
|
12
|
+
* URL that links to the specific build/run where tests are being executed.
|
|
13
|
+
*
|
|
14
|
+
* Supported CI providers (checked in order):
|
|
15
|
+
* - GitHub Actions
|
|
16
|
+
* - Azure DevOps
|
|
17
|
+
* - GitLab CI (via `CI_JOB_URL` environment variable)
|
|
18
|
+
* - Jenkins (via `BUILD_URL` environment variable)
|
|
19
|
+
*
|
|
20
|
+
* @returns {string | undefined} The constructed CI run URL, or `undefined` if no supported
|
|
21
|
+
* CI environment is detected or required environment variables are missing.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```typescript
|
|
25
|
+
* const report: FlakinessReport.Report = {
|
|
26
|
+
* // ... other report properties
|
|
27
|
+
* url: CIUtils.runUrl(),
|
|
28
|
+
* };
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
function runUrl(): string | undefined;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=ciUtils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ciUtils.d.ts","sourceRoot":"","sources":["../../src/ciUtils.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,yBAAiB,OAAO,CAAC;IACvB;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,SAAgB,MAAM,IAAI,MAAM,GAAG,SAAS,CAE3C;CACF"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { FlakinessReport } from '@flakiness/flakiness-report';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a Flakiness environment object with system information and user data.
|
|
4
|
+
*
|
|
5
|
+
* Automatically detects operating system details (name, architecture, version) and merges
|
|
6
|
+
* environment variables prefixed with `FK_ENV_` into the user-supplied data. This function
|
|
7
|
+
* is essential for creating environment metadata that helps identify test execution contexts
|
|
8
|
+
* in Flakiness reports.
|
|
9
|
+
*
|
|
10
|
+
* @param {Object} options - Configuration object for the environment.
|
|
11
|
+
* @param {string} options.name - Human-readable name for the environment (e.g., 'CI', 'Local Dev', 'Staging').
|
|
12
|
+
* @param {Record<string, string>} [options.userSuppliedData] - Additional key-value pairs to include
|
|
13
|
+
* in the environment data. These are merged with `FK_ENV_*` environment variables.
|
|
14
|
+
* @param {any} [options.opaqueData] - Optional opaque data object that will be stored with the
|
|
15
|
+
* environment but not used for environment deduplication.
|
|
16
|
+
*
|
|
17
|
+
* @returns {FlakinessReport.Environment} Environment object containing:
|
|
18
|
+
* - `name` - The provided environment name
|
|
19
|
+
* - `systemData` - Automatically detected OS information (arch, name, version)
|
|
20
|
+
* - `userSuppliedData` - Merged data from `FK_ENV_*` variables and `userSuppliedData` option
|
|
21
|
+
* - `opaqueData` - The provided opaque data, if any
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```typescript
|
|
25
|
+
* // Basic usage
|
|
26
|
+
* const env = createEnvironment({ name: 'CI' });
|
|
27
|
+
*
|
|
28
|
+
* // With custom data
|
|
29
|
+
* const env = createEnvironment({
|
|
30
|
+
* name: 'Staging',
|
|
31
|
+
* userSuppliedData: { region: 'us-east-1', instance: 'large' }
|
|
32
|
+
* });
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export declare function createEnvironment(options: {
|
|
36
|
+
name: string;
|
|
37
|
+
userSuppliedData?: Record<string, string>;
|
|
38
|
+
opaqueData?: any;
|
|
39
|
+
}): FlakinessReport.Environment;
|
|
40
|
+
//# sourceMappingURL=createEnvironment.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createEnvironment.d.ts","sourceRoot":"","sources":["../../src/createEnvironment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAwD9D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,UAAU,CAAC,EAAE,GAAG,CAAC;CAClB,GAAG,eAAe,CAAC,WAAW,CAe9B"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { FlakinessReport } from '@flakiness/flakiness-report';
|
|
2
|
+
import { GitWorktree } from './gitWorktree.js';
|
|
3
|
+
/**
|
|
4
|
+
* Generates code snippets for test steps and attaches them to the report in-place.
|
|
5
|
+
*
|
|
6
|
+
* This function reads source files from the git worktree and creates highlighted code snippets
|
|
7
|
+
* for each test step that has a location. The snippets include 3 lines of context (1 before,
|
|
8
|
+
* the line itself, 1 after) with syntax highlighting and a visual indicator pointing to the
|
|
9
|
+
* exact column position.
|
|
10
|
+
*
|
|
11
|
+
* The snippets are attached directly to the `step.snippet` property of each test step in the
|
|
12
|
+
* report object. Steps without locations or with invalid file paths are silently skipped.
|
|
13
|
+
*
|
|
14
|
+
* @param {GitWorktree} worktree - Git worktree instance used to resolve file paths from
|
|
15
|
+
* git-relative paths to absolute paths for reading source files.
|
|
16
|
+
*
|
|
17
|
+
* @param {FlakinessReport.Report} report - Flakiness report to process. The report is modified
|
|
18
|
+
* in-place by adding `snippet` properties to test steps.
|
|
19
|
+
*
|
|
20
|
+
* @returns {void} This function modifies the report in-place and does not return a value.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```typescript
|
|
24
|
+
* const worktree = GitWorktree.create(process.cwd());
|
|
25
|
+
* createTestStepSnippetsInplace(worktree, report);
|
|
26
|
+
* // Report steps now have .snippet properties with highlighted code
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export declare function createTestStepSnippetsInplace(worktree: GitWorktree, report: FlakinessReport.Report): void;
|
|
30
|
+
//# sourceMappingURL=createTestStepSnippets.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createTestStepSnippets.d.ts","sourceRoot":"","sources":["../../src/createTestStepSnippets.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAE9D,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAG/C;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,6BAA6B,CAAC,QAAQ,EAAE,WAAW,EAAE,MAAM,EAAE,eAAe,CAAC,MAAM,QA4ClG"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
type JSONConfig = {
|
|
2
|
+
projectPublicId?: string;
|
|
3
|
+
customReportViewerUrl?: string;
|
|
4
|
+
};
|
|
5
|
+
/**
|
|
6
|
+
* Manages Flakiness project configuration stored in `.flakiness/config.json`.
|
|
7
|
+
*
|
|
8
|
+
* The configuration file is automatically located by searching upward from the current working
|
|
9
|
+
* directory for an existing `.flakiness` folder, or by placing it at the git repository root
|
|
10
|
+
* if no existing config is found.
|
|
11
|
+
*/
|
|
12
|
+
export declare class FlakinessProjectConfig {
|
|
13
|
+
private _configPath;
|
|
14
|
+
private _config;
|
|
15
|
+
/**
|
|
16
|
+
* Loads the Flakiness project configuration from disk.
|
|
17
|
+
*
|
|
18
|
+
* Searches for an existing `.flakiness/config.json` file starting from the current working
|
|
19
|
+
* directory and walking up the directory tree. If no config exists, it determines the
|
|
20
|
+
* appropriate location (git root or current directory) for future saves.
|
|
21
|
+
*
|
|
22
|
+
* @returns {Promise<FlakinessProjectConfig>} Promise that resolves to a FlakinessProjectConfig
|
|
23
|
+
* instance. If no config file exists, returns an instance with default/empty values.
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```typescript
|
|
27
|
+
* const config = await FlakinessProjectConfig.load();
|
|
28
|
+
* const projectId = config.projectPublicId();
|
|
29
|
+
* ```
|
|
30
|
+
*/
|
|
31
|
+
static load(): Promise<FlakinessProjectConfig>;
|
|
32
|
+
/**
|
|
33
|
+
* Creates a new empty Flakiness project configuration.
|
|
34
|
+
*
|
|
35
|
+
* Creates a configuration instance with no values set. Use this when you want to build
|
|
36
|
+
* a configuration from scratch. Call `save()` to persist it to disk.
|
|
37
|
+
*
|
|
38
|
+
* @returns {FlakinessProjectConfig} A new empty configuration instance.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```typescript
|
|
42
|
+
* const config = FlakinessProjectConfig.createEmpty();
|
|
43
|
+
* config.setProjectPublicId('my-project-id');
|
|
44
|
+
* await config.save();
|
|
45
|
+
* ```
|
|
46
|
+
*/
|
|
47
|
+
static createEmpty(): FlakinessProjectConfig;
|
|
48
|
+
constructor(_configPath: string, _config: JSONConfig);
|
|
49
|
+
/**
|
|
50
|
+
* Returns the absolute path to the configuration file.
|
|
51
|
+
*
|
|
52
|
+
* @returns {string} Absolute path to `.flakiness/config.json`.
|
|
53
|
+
*/
|
|
54
|
+
path(): string;
|
|
55
|
+
/**
|
|
56
|
+
* Returns the project's public ID, if configured.
|
|
57
|
+
*
|
|
58
|
+
* The project public ID is used to associate reports with a specific Flakiness.io project.
|
|
59
|
+
*
|
|
60
|
+
* @returns {string | undefined} Project public ID, or `undefined` if not set.
|
|
61
|
+
*/
|
|
62
|
+
projectPublicId(): string | undefined;
|
|
63
|
+
/**
|
|
64
|
+
* Returns the report viewer URL, either custom or default.
|
|
65
|
+
*
|
|
66
|
+
* @returns {string} Custom report viewer URL if configured, otherwise the default
|
|
67
|
+
* `https://report.flakiness.io`.
|
|
68
|
+
*/
|
|
69
|
+
reportViewerUrl(): string;
|
|
70
|
+
/**
|
|
71
|
+
* Sets or clears the custom report viewer URL.
|
|
72
|
+
*
|
|
73
|
+
* @param {string | undefined} url - Custom report viewer URL to use, or `undefined` to
|
|
74
|
+
* clear and use the default URL.
|
|
75
|
+
*/
|
|
76
|
+
setCustomReportViewerUrl(url: string | undefined): void;
|
|
77
|
+
/**
|
|
78
|
+
* Sets the project's public ID.
|
|
79
|
+
*
|
|
80
|
+
* @param {string | undefined} projectId - Project public ID to set, or `undefined` to clear.
|
|
81
|
+
*/
|
|
82
|
+
setProjectPublicId(projectId: string | undefined): void;
|
|
83
|
+
/**
|
|
84
|
+
* Saves the configuration to disk.
|
|
85
|
+
*
|
|
86
|
+
* Writes the current configuration values to `.flakiness/config.json`. Creates the
|
|
87
|
+
* `.flakiness` directory if it doesn't exist.
|
|
88
|
+
*
|
|
89
|
+
* @returns {Promise<void>} Promise that resolves when the file has been written.
|
|
90
|
+
*
|
|
91
|
+
* @throws {Error} Throws if unable to create directories or write the file.
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* ```typescript
|
|
95
|
+
* const config = await FlakinessProjectConfig.load();
|
|
96
|
+
* config.setProjectPublicId('my-project');
|
|
97
|
+
* await config.save();
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
100
|
+
save(): Promise<void>;
|
|
101
|
+
}
|
|
102
|
+
export {};
|
|
103
|
+
//# sourceMappingURL=flakinessProjectConfig.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flakinessProjectConfig.d.ts","sourceRoot":"","sources":["../../src/flakinessProjectConfig.ts"],"names":[],"mappings":"AAmCA,KAAK,UAAU,GAAG;IAChB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAChC,CAAA;AAED;;;;;;GAMG;AACH,qBAAa,sBAAsB;IA4C/B,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,OAAO;IA5CjB;;;;;;;;;;;;;;;OAeG;WACU,IAAI,IAAI,OAAO,CAAC,sBAAsB,CAAC;IAOpD;;;;;;;;;;;;;;OAcG;IACH,MAAM,CAAC,WAAW;gBAKR,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,UAAU;IAG7B;;;;OAIG;IACH,IAAI;IAIJ;;;;;;OAMG;IACH,eAAe;IAIf;;;;;OAKG;IACH,eAAe;IAIf;;;;;OAKG;IACH,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAC,SAAS;IAO9C;;;;OAIG;IACH,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAC,SAAS;IAI9C;;;;;;;;;;;;;;;;OAgBG;IACG,IAAI;CAIX"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { FlakinessReport } from '@flakiness/flakiness-report';
|
|
2
|
+
/**
|
|
3
|
+
* Represents a git commit with its metadata.
|
|
4
|
+
*
|
|
5
|
+
* This type is returned by `GitWorktree.listCommits()` and contains all the information
|
|
6
|
+
* needed to identify and reference a commit in a git repository.
|
|
7
|
+
*/
|
|
8
|
+
export type GitCommit = {
|
|
9
|
+
/** Full commit hash (SHA-1, 40 characters) */
|
|
10
|
+
commitId: FlakinessReport.CommitId;
|
|
11
|
+
/** Commit timestamp in milliseconds since Unix epoch */
|
|
12
|
+
timestamp: FlakinessReport.UnixTimestampMS;
|
|
13
|
+
/** Commit message (subject line only) */
|
|
14
|
+
message: string;
|
|
15
|
+
/** Author name, if available */
|
|
16
|
+
author?: string;
|
|
17
|
+
/** Array of parent commit IDs (empty for initial commit, typically one for normal commits, multiple for merges) */
|
|
18
|
+
parents: FlakinessReport.CommitId[];
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Utilities for working with git repositories and converting between git-relative paths
|
|
22
|
+
* and absolute native paths. Essential for creating Flakiness Reports where all paths
|
|
23
|
+
* must be relative to the git root.
|
|
24
|
+
*/
|
|
25
|
+
export declare class GitWorktree {
|
|
26
|
+
private _gitRoot;
|
|
27
|
+
/**
|
|
28
|
+
* Creates a GitWorktree instance from any path inside a git repository.
|
|
29
|
+
*
|
|
30
|
+
* @param {string} somePathInsideGitRepo - Any path (file or directory) within a git repository.
|
|
31
|
+
* Can be absolute or relative. The function will locate the git root directory.
|
|
32
|
+
*
|
|
33
|
+
* @returns {GitWorktree} A new GitWorktree instance bound to the discovered git root.
|
|
34
|
+
*
|
|
35
|
+
* @throws {Error} Throws if the path is not inside a git repository or if git commands fail.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```typescript
|
|
39
|
+
* const worktree = GitWorktree.create('./src/my-test.ts');
|
|
40
|
+
* const gitRoot = worktree.rootPath();
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
static create(somePathInsideGitRepo: string): GitWorktree;
|
|
44
|
+
private _posixGitRoot;
|
|
45
|
+
constructor(_gitRoot: string);
|
|
46
|
+
/**
|
|
47
|
+
* Returns the native absolute path of the git repository root directory.
|
|
48
|
+
*
|
|
49
|
+
* @returns {string} Native absolute path to the git root. Format matches the current platform
|
|
50
|
+
* (Windows or POSIX).
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```typescript
|
|
54
|
+
* const root = worktree.rootPath();
|
|
55
|
+
* // On Windows: 'D:\project'
|
|
56
|
+
* // On Unix: '/project'
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
rootPath(): string;
|
|
60
|
+
/**
|
|
61
|
+
* Returns the commit ID (SHA-1 hash) of the current HEAD commit.
|
|
62
|
+
*
|
|
63
|
+
* @returns {FlakinessReport.CommitId} Full 40-character commit hash of the HEAD commit.
|
|
64
|
+
*
|
|
65
|
+
* @throws {Error} Throws if git command fails or repository is in an invalid state.
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```typescript
|
|
69
|
+
* const commitId = worktree.headCommitId();
|
|
70
|
+
* // Returns: 'a1b2c3d4e5f6...' (40-character SHA-1)
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
headCommitId(): FlakinessReport.CommitId;
|
|
74
|
+
/**
|
|
75
|
+
* Converts a native absolute path to a git-relative POSIX path.
|
|
76
|
+
*
|
|
77
|
+
* Takes any absolute path (Windows or POSIX format) and converts it to a POSIX path
|
|
78
|
+
* relative to the git repository root. This is essential for Flakiness reports where
|
|
79
|
+
* all file paths must be git-relative and use POSIX separators.
|
|
80
|
+
*
|
|
81
|
+
* @param {string} absolutePath - Native absolute path to convert. Can be in Windows format
|
|
82
|
+
* (e.g., `D:\project\src\test.ts`) or POSIX format (e.g., `/project/src/test.ts`).
|
|
83
|
+
*
|
|
84
|
+
* @returns {FlakinessReport.GitFilePath} POSIX path relative to git root (e.g., `src/test.ts`).
|
|
85
|
+
* Returns an empty string if the path is the git root itself.
|
|
86
|
+
*
|
|
87
|
+
* @example
|
|
88
|
+
* ```typescript
|
|
89
|
+
* const gitPath = worktree.gitPath('/Users/project/src/test.ts');
|
|
90
|
+
* // Returns: 'src/test.ts'
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
gitPath(absolutePath: string): FlakinessReport.GitFilePath;
|
|
94
|
+
/**
|
|
95
|
+
* Converts a git-relative POSIX path to a native absolute path.
|
|
96
|
+
*
|
|
97
|
+
* Takes a POSIX path relative to the git root and converts it to the native absolute path
|
|
98
|
+
* format for the current platform (Windows or POSIX). This is the inverse of `gitPath()`.
|
|
99
|
+
*
|
|
100
|
+
* @param {FlakinessReport.GitFilePath} relativePath - POSIX path relative to git root
|
|
101
|
+
* (e.g., `src/test.ts`).
|
|
102
|
+
*
|
|
103
|
+
* @returns {string} Native absolute path. On Windows, returns Windows format (e.g., `D:\project\src\test.ts`).
|
|
104
|
+
* On POSIX systems, returns POSIX format (e.g., `/project/src/test.ts`).
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```typescript
|
|
108
|
+
* const absolutePath = worktree.absolutePath('src/test.ts');
|
|
109
|
+
* // On Windows: 'D:\project\src\test.ts'
|
|
110
|
+
* // On Unix: '/project/src/test.ts'
|
|
111
|
+
* ```
|
|
112
|
+
*/
|
|
113
|
+
absolutePath(relativePath: FlakinessReport.GitFilePath): string;
|
|
114
|
+
/**
|
|
115
|
+
* Lists recent commits from the repository.
|
|
116
|
+
*
|
|
117
|
+
* Retrieves commit information including commit ID, timestamp, author, message, and parent commits.
|
|
118
|
+
* Note: CI environments often have shallow checkouts with limited history, which may affect
|
|
119
|
+
* the number of commits returned.
|
|
120
|
+
*
|
|
121
|
+
* @param {number} count - Maximum number of commits to retrieve, starting from HEAD.
|
|
122
|
+
*
|
|
123
|
+
* @returns {Promise<GitCommit[]>} Promise that resolves to an array of commit objects, ordered
|
|
124
|
+
* from most recent to oldest. Each commit includes:
|
|
125
|
+
* - `commitId` - Full commit hash
|
|
126
|
+
* - `timestamp` - Commit timestamp in milliseconds since Unix epoch
|
|
127
|
+
* - `message` - Commit message (subject line)
|
|
128
|
+
* - `author` - Author name
|
|
129
|
+
* - `parents` - Array of parent commit IDs
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* ```typescript
|
|
133
|
+
* const commits = await worktree.listCommits(10);
|
|
134
|
+
* console.log(`Latest commit: ${commits[0].message}`);
|
|
135
|
+
* ```
|
|
136
|
+
*/
|
|
137
|
+
listCommits(count: number): Promise<GitCommit[]>;
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=gitWorktree.d.ts.map
|