@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
|
@@ -1,321 +1,10 @@
|
|
|
1
|
-
// src/flakinessProjectConfig.ts
|
|
2
1
|
import fs from "fs";
|
|
3
2
|
import path from "path";
|
|
4
|
-
|
|
5
|
-
// src/gitWorktree.ts
|
|
6
|
-
import assert from "assert";
|
|
7
|
-
import { exec } from "child_process";
|
|
8
|
-
import debug from "debug";
|
|
9
|
-
import { posix as posixPath, win32 as win32Path } from "path";
|
|
10
|
-
import { promisify } from "util";
|
|
11
|
-
|
|
12
|
-
// src/_internalUtils.ts
|
|
13
|
-
import { spawnSync } from "child_process";
|
|
14
|
-
import http from "http";
|
|
15
|
-
import https from "https";
|
|
16
|
-
import util from "util";
|
|
17
|
-
import zlib from "zlib";
|
|
18
|
-
var asyncBrotliCompress = util.promisify(zlib.brotliCompress);
|
|
19
|
-
var FLAKINESS_DBG = !!process.env.FLAKINESS_DBG;
|
|
20
|
-
function errorText(error) {
|
|
21
|
-
return FLAKINESS_DBG ? error.stack : error.message;
|
|
22
|
-
}
|
|
23
|
-
async function retryWithBackoff(job, backoff = []) {
|
|
24
|
-
for (const timeout of backoff) {
|
|
25
|
-
try {
|
|
26
|
-
return await job();
|
|
27
|
-
} catch (e) {
|
|
28
|
-
if (e instanceof AggregateError)
|
|
29
|
-
console.error(`[flakiness.io err]`, errorText(e.errors[0]));
|
|
30
|
-
else if (e instanceof Error)
|
|
31
|
-
console.error(`[flakiness.io err]`, errorText(e));
|
|
32
|
-
else
|
|
33
|
-
console.error(`[flakiness.io err]`, e);
|
|
34
|
-
await new Promise((x) => setTimeout(x, timeout));
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
return await job();
|
|
38
|
-
}
|
|
39
|
-
var httpUtils;
|
|
40
|
-
((httpUtils2) => {
|
|
41
|
-
function createRequest({ url, method = "get", headers = {} }) {
|
|
42
|
-
let resolve;
|
|
43
|
-
let reject;
|
|
44
|
-
const responseDataPromise = new Promise((a, b) => {
|
|
45
|
-
resolve = a;
|
|
46
|
-
reject = b;
|
|
47
|
-
});
|
|
48
|
-
const protocol = url.startsWith("https") ? https : http;
|
|
49
|
-
headers = Object.fromEntries(Object.entries(headers).filter(([key, value]) => value !== void 0));
|
|
50
|
-
const request = protocol.request(url, { method, headers }, (res) => {
|
|
51
|
-
const chunks = [];
|
|
52
|
-
res.on("data", (chunk) => chunks.push(chunk));
|
|
53
|
-
res.on("end", () => {
|
|
54
|
-
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300)
|
|
55
|
-
resolve(Buffer.concat(chunks));
|
|
56
|
-
else
|
|
57
|
-
reject(new Error(`Request to ${url} failed with ${res.statusCode}`));
|
|
58
|
-
});
|
|
59
|
-
res.on("error", (error) => reject(error));
|
|
60
|
-
});
|
|
61
|
-
request.on("error", reject);
|
|
62
|
-
return { request, responseDataPromise };
|
|
63
|
-
}
|
|
64
|
-
httpUtils2.createRequest = createRequest;
|
|
65
|
-
async function getBuffer(url, backoff) {
|
|
66
|
-
return await retryWithBackoff(async () => {
|
|
67
|
-
const { request, responseDataPromise } = createRequest({ url });
|
|
68
|
-
request.end();
|
|
69
|
-
return await responseDataPromise;
|
|
70
|
-
}, backoff);
|
|
71
|
-
}
|
|
72
|
-
httpUtils2.getBuffer = getBuffer;
|
|
73
|
-
async function getText(url, backoff) {
|
|
74
|
-
const buffer = await getBuffer(url, backoff);
|
|
75
|
-
return buffer.toString("utf-8");
|
|
76
|
-
}
|
|
77
|
-
httpUtils2.getText = getText;
|
|
78
|
-
async function getJSON(url) {
|
|
79
|
-
return JSON.parse(await getText(url));
|
|
80
|
-
}
|
|
81
|
-
httpUtils2.getJSON = getJSON;
|
|
82
|
-
async function postText(url, text, backoff) {
|
|
83
|
-
const headers = {
|
|
84
|
-
"Content-Type": "application/json",
|
|
85
|
-
"Content-Length": Buffer.byteLength(text) + ""
|
|
86
|
-
};
|
|
87
|
-
return await retryWithBackoff(async () => {
|
|
88
|
-
const { request, responseDataPromise } = createRequest({ url, headers, method: "post" });
|
|
89
|
-
request.write(text);
|
|
90
|
-
request.end();
|
|
91
|
-
return await responseDataPromise;
|
|
92
|
-
}, backoff);
|
|
93
|
-
}
|
|
94
|
-
httpUtils2.postText = postText;
|
|
95
|
-
async function postJSON(url, json, backoff) {
|
|
96
|
-
const buffer = await postText(url, JSON.stringify(json), backoff);
|
|
97
|
-
return JSON.parse(buffer.toString("utf-8"));
|
|
98
|
-
}
|
|
99
|
-
httpUtils2.postJSON = postJSON;
|
|
100
|
-
})(httpUtils || (httpUtils = {}));
|
|
101
|
-
function shell(command, args, options) {
|
|
102
|
-
try {
|
|
103
|
-
const result = spawnSync(command, args, { encoding: "utf-8", ...options });
|
|
104
|
-
if (result.status !== 0) {
|
|
105
|
-
return void 0;
|
|
106
|
-
}
|
|
107
|
-
return result.stdout.trim();
|
|
108
|
-
} catch (e) {
|
|
109
|
-
console.error(e);
|
|
110
|
-
return void 0;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// src/gitWorktree.ts
|
|
115
|
-
var log = debug("fk:git");
|
|
116
|
-
var execAsync = promisify(exec);
|
|
117
|
-
var IS_WIN32_PATH = new RegExp("^[a-zA-Z]:\\\\", "i");
|
|
118
|
-
var IS_ALMOST_POSIX_PATH = new RegExp("^[a-zA-Z]:/", "i");
|
|
119
|
-
function toPosixAbsolutePath(absolutePath) {
|
|
120
|
-
if (IS_WIN32_PATH.test(absolutePath)) {
|
|
121
|
-
absolutePath = absolutePath.split(win32Path.sep).join(posixPath.sep);
|
|
122
|
-
}
|
|
123
|
-
if (IS_ALMOST_POSIX_PATH.test(absolutePath))
|
|
124
|
-
return "/" + absolutePath[0] + absolutePath.substring(2);
|
|
125
|
-
return absolutePath;
|
|
126
|
-
}
|
|
127
|
-
function toNativeAbsolutePath(posix) {
|
|
128
|
-
if (process.platform !== "win32")
|
|
129
|
-
return posix;
|
|
130
|
-
assert(posix.startsWith("/"), "The path must be absolute");
|
|
131
|
-
const m = posix.match(/^\/([a-zA-Z])(\/.*)?$/);
|
|
132
|
-
assert(m, `Invalid POSIX path: ${posix}`);
|
|
133
|
-
const drive = m[1];
|
|
134
|
-
const rest = (m[2] ?? "").split(posixPath.sep).join(win32Path.sep);
|
|
135
|
-
return drive.toUpperCase() + ":" + rest;
|
|
136
|
-
}
|
|
137
|
-
var GitWorktree = class _GitWorktree {
|
|
138
|
-
constructor(_gitRoot) {
|
|
139
|
-
this._gitRoot = _gitRoot;
|
|
140
|
-
this._posixGitRoot = toPosixAbsolutePath(this._gitRoot);
|
|
141
|
-
}
|
|
142
|
-
/**
|
|
143
|
-
* Creates a GitWorktree instance from any path inside a git repository.
|
|
144
|
-
*
|
|
145
|
-
* @param {string} somePathInsideGitRepo - Any path (file or directory) within a git repository.
|
|
146
|
-
* Can be absolute or relative. The function will locate the git root directory.
|
|
147
|
-
*
|
|
148
|
-
* @returns {GitWorktree} A new GitWorktree instance bound to the discovered git root.
|
|
149
|
-
*
|
|
150
|
-
* @throws {Error} Throws if the path is not inside a git repository or if git commands fail.
|
|
151
|
-
*
|
|
152
|
-
* @example
|
|
153
|
-
* ```typescript
|
|
154
|
-
* const worktree = GitWorktree.create('./src/my-test.ts');
|
|
155
|
-
* const gitRoot = worktree.rootPath();
|
|
156
|
-
* ```
|
|
157
|
-
*/
|
|
158
|
-
static create(somePathInsideGitRepo) {
|
|
159
|
-
const root = shell(`git`, ["rev-parse", "--show-toplevel"], {
|
|
160
|
-
cwd: somePathInsideGitRepo,
|
|
161
|
-
encoding: "utf-8"
|
|
162
|
-
});
|
|
163
|
-
assert(root, `FAILED: git rev-parse --show-toplevel HEAD @ ${somePathInsideGitRepo}`);
|
|
164
|
-
return new _GitWorktree(root);
|
|
165
|
-
}
|
|
166
|
-
_posixGitRoot;
|
|
167
|
-
/**
|
|
168
|
-
* Returns the native absolute path of the git repository root directory.
|
|
169
|
-
*
|
|
170
|
-
* @returns {string} Native absolute path to the git root. Format matches the current platform
|
|
171
|
-
* (Windows or POSIX).
|
|
172
|
-
*
|
|
173
|
-
* @example
|
|
174
|
-
* ```typescript
|
|
175
|
-
* const root = worktree.rootPath();
|
|
176
|
-
* // On Windows: 'D:\project'
|
|
177
|
-
* // On Unix: '/project'
|
|
178
|
-
* ```
|
|
179
|
-
*/
|
|
180
|
-
rootPath() {
|
|
181
|
-
return this._gitRoot;
|
|
182
|
-
}
|
|
183
|
-
/**
|
|
184
|
-
* Returns the commit ID (SHA-1 hash) of the current HEAD commit.
|
|
185
|
-
*
|
|
186
|
-
* @returns {FlakinessReport.CommitId} Full 40-character commit hash of the HEAD commit.
|
|
187
|
-
*
|
|
188
|
-
* @throws {Error} Throws if git command fails or repository is in an invalid state.
|
|
189
|
-
*
|
|
190
|
-
* @example
|
|
191
|
-
* ```typescript
|
|
192
|
-
* const commitId = worktree.headCommitId();
|
|
193
|
-
* // Returns: 'a1b2c3d4e5f6...' (40-character SHA-1)
|
|
194
|
-
* ```
|
|
195
|
-
*/
|
|
196
|
-
headCommitId() {
|
|
197
|
-
const sha = shell(`git`, ["rev-parse", "HEAD"], {
|
|
198
|
-
cwd: this._gitRoot,
|
|
199
|
-
encoding: "utf-8"
|
|
200
|
-
});
|
|
201
|
-
assert(sha, `FAILED: git rev-parse HEAD @ ${this._gitRoot}`);
|
|
202
|
-
return sha.trim();
|
|
203
|
-
}
|
|
204
|
-
/**
|
|
205
|
-
* Converts a native absolute path to a git-relative POSIX path.
|
|
206
|
-
*
|
|
207
|
-
* Takes any absolute path (Windows or POSIX format) and converts it to a POSIX path
|
|
208
|
-
* relative to the git repository root. This is essential for Flakiness reports where
|
|
209
|
-
* all file paths must be git-relative and use POSIX separators.
|
|
210
|
-
*
|
|
211
|
-
* @param {string} absolutePath - Native absolute path to convert. Can be in Windows format
|
|
212
|
-
* (e.g., `D:\project\src\test.ts`) or POSIX format (e.g., `/project/src/test.ts`).
|
|
213
|
-
*
|
|
214
|
-
* @returns {FlakinessReport.GitFilePath} POSIX path relative to git root (e.g., `src/test.ts`).
|
|
215
|
-
* Returns an empty string if the path is the git root itself.
|
|
216
|
-
*
|
|
217
|
-
* @example
|
|
218
|
-
* ```typescript
|
|
219
|
-
* const gitPath = worktree.gitPath('/Users/project/src/test.ts');
|
|
220
|
-
* // Returns: 'src/test.ts'
|
|
221
|
-
* ```
|
|
222
|
-
*/
|
|
223
|
-
gitPath(absolutePath) {
|
|
224
|
-
return posixPath.relative(this._posixGitRoot, toPosixAbsolutePath(absolutePath));
|
|
225
|
-
}
|
|
226
|
-
/**
|
|
227
|
-
* Converts a git-relative POSIX path to a native absolute path.
|
|
228
|
-
*
|
|
229
|
-
* Takes a POSIX path relative to the git root and converts it to the native absolute path
|
|
230
|
-
* format for the current platform (Windows or POSIX). This is the inverse of `gitPath()`.
|
|
231
|
-
*
|
|
232
|
-
* @param {FlakinessReport.GitFilePath} relativePath - POSIX path relative to git root
|
|
233
|
-
* (e.g., `src/test.ts`).
|
|
234
|
-
*
|
|
235
|
-
* @returns {string} Native absolute path. On Windows, returns Windows format (e.g., `D:\project\src\test.ts`).
|
|
236
|
-
* On POSIX systems, returns POSIX format (e.g., `/project/src/test.ts`).
|
|
237
|
-
*
|
|
238
|
-
* @example
|
|
239
|
-
* ```typescript
|
|
240
|
-
* const absolutePath = worktree.absolutePath('src/test.ts');
|
|
241
|
-
* // On Windows: 'D:\project\src\test.ts'
|
|
242
|
-
* // On Unix: '/project/src/test.ts'
|
|
243
|
-
* ```
|
|
244
|
-
*/
|
|
245
|
-
absolutePath(relativePath) {
|
|
246
|
-
return toNativeAbsolutePath(posixPath.join(this._posixGitRoot, relativePath));
|
|
247
|
-
}
|
|
248
|
-
/**
|
|
249
|
-
* Lists recent commits from the repository.
|
|
250
|
-
*
|
|
251
|
-
* Retrieves commit information including commit ID, timestamp, author, message, and parent commits.
|
|
252
|
-
* Note: CI environments often have shallow checkouts with limited history, which may affect
|
|
253
|
-
* the number of commits returned.
|
|
254
|
-
*
|
|
255
|
-
* @param {number} count - Maximum number of commits to retrieve, starting from HEAD.
|
|
256
|
-
*
|
|
257
|
-
* @returns {Promise<GitCommit[]>} Promise that resolves to an array of commit objects, ordered
|
|
258
|
-
* from most recent to oldest. Each commit includes:
|
|
259
|
-
* - `commitId` - Full commit hash
|
|
260
|
-
* - `timestamp` - Commit timestamp in milliseconds since Unix epoch
|
|
261
|
-
* - `message` - Commit message (subject line)
|
|
262
|
-
* - `author` - Author name
|
|
263
|
-
* - `parents` - Array of parent commit IDs
|
|
264
|
-
*
|
|
265
|
-
* @example
|
|
266
|
-
* ```typescript
|
|
267
|
-
* const commits = await worktree.listCommits(10);
|
|
268
|
-
* console.log(`Latest commit: ${commits[0].message}`);
|
|
269
|
-
* ```
|
|
270
|
-
*/
|
|
271
|
-
async listCommits(count) {
|
|
272
|
-
return await listCommits(this._gitRoot, "HEAD", count);
|
|
273
|
-
}
|
|
274
|
-
};
|
|
275
|
-
async function listCommits(gitRoot, head, count) {
|
|
276
|
-
const FIELD_SEPARATOR = "|~|";
|
|
277
|
-
const RECORD_SEPARATOR = "\0";
|
|
278
|
-
const prettyFormat = [
|
|
279
|
-
"%H",
|
|
280
|
-
// Full commit hash
|
|
281
|
-
"%ct",
|
|
282
|
-
// Commit timestamp (Unix seconds)
|
|
283
|
-
"%an",
|
|
284
|
-
// Author name
|
|
285
|
-
"%s",
|
|
286
|
-
// Subject line
|
|
287
|
-
"%P"
|
|
288
|
-
// Parent hashes (space-separated)
|
|
289
|
-
].join(FIELD_SEPARATOR);
|
|
290
|
-
const command = `git log ${head} -n ${count} --pretty=format:"${prettyFormat}" -z`;
|
|
291
|
-
try {
|
|
292
|
-
const { stdout } = await execAsync(command, { cwd: gitRoot });
|
|
293
|
-
if (!stdout) {
|
|
294
|
-
return [];
|
|
295
|
-
}
|
|
296
|
-
return stdout.trim().split(RECORD_SEPARATOR).filter((record) => record).map((record) => {
|
|
297
|
-
const [commitId, timestampStr, author, message, parentsStr] = record.split(FIELD_SEPARATOR);
|
|
298
|
-
const parents = parentsStr ? parentsStr.split(" ").filter((p) => p) : [];
|
|
299
|
-
return {
|
|
300
|
-
commitId,
|
|
301
|
-
timestamp: parseInt(timestampStr, 10) * 1e3,
|
|
302
|
-
author,
|
|
303
|
-
message,
|
|
304
|
-
parents,
|
|
305
|
-
walkIndex: 0
|
|
306
|
-
};
|
|
307
|
-
});
|
|
308
|
-
} catch (error) {
|
|
309
|
-
log(`Failed to list commits for repository at ${gitRoot}:`, error);
|
|
310
|
-
return [];
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
// src/flakinessProjectConfig.ts
|
|
3
|
+
import { GitWorktree } from "./gitWorktree.js";
|
|
315
4
|
function createConfigPath(dir) {
|
|
316
5
|
return path.join(dir, ".flakiness", "config.json");
|
|
317
6
|
}
|
|
318
|
-
|
|
7
|
+
let gConfigPath;
|
|
319
8
|
function ensureConfigPath() {
|
|
320
9
|
if (!gConfigPath)
|
|
321
10
|
gConfigPath = computeConfigPath();
|
|
@@ -334,7 +23,7 @@ function computeConfigPath() {
|
|
|
334
23
|
return createConfigPath(process.cwd());
|
|
335
24
|
}
|
|
336
25
|
}
|
|
337
|
-
|
|
26
|
+
class FlakinessProjectConfig {
|
|
338
27
|
constructor(_configPath, _config) {
|
|
339
28
|
this._configPath = _configPath;
|
|
340
29
|
this._config = _config;
|
|
@@ -359,7 +48,7 @@ var FlakinessProjectConfig = class _FlakinessProjectConfig {
|
|
|
359
48
|
const configPath = ensureConfigPath();
|
|
360
49
|
const data = await fs.promises.readFile(configPath, "utf-8").catch((e) => void 0);
|
|
361
50
|
const json = data ? JSON.parse(data) : {};
|
|
362
|
-
return new
|
|
51
|
+
return new FlakinessProjectConfig(configPath, json);
|
|
363
52
|
}
|
|
364
53
|
/**
|
|
365
54
|
* Creates a new empty Flakiness project configuration.
|
|
@@ -377,7 +66,7 @@ var FlakinessProjectConfig = class _FlakinessProjectConfig {
|
|
|
377
66
|
* ```
|
|
378
67
|
*/
|
|
379
68
|
static createEmpty() {
|
|
380
|
-
return new
|
|
69
|
+
return new FlakinessProjectConfig(ensureConfigPath(), {});
|
|
381
70
|
}
|
|
382
71
|
/**
|
|
383
72
|
* Returns the absolute path to the configuration file.
|
|
@@ -447,7 +136,7 @@ var FlakinessProjectConfig = class _FlakinessProjectConfig {
|
|
|
447
136
|
await fs.promises.mkdir(path.dirname(this._configPath), { recursive: true });
|
|
448
137
|
await fs.promises.writeFile(this._configPath, JSON.stringify(this._config, null, 2));
|
|
449
138
|
}
|
|
450
|
-
}
|
|
139
|
+
}
|
|
451
140
|
export {
|
|
452
141
|
FlakinessProjectConfig
|
|
453
142
|
};
|
package/lib/gitWorktree.js
CHANGED
|
@@ -1,117 +1,13 @@
|
|
|
1
|
-
// src/gitWorktree.ts
|
|
2
1
|
import assert from "assert";
|
|
3
2
|
import { exec } from "child_process";
|
|
4
3
|
import debug from "debug";
|
|
5
4
|
import { posix as posixPath, win32 as win32Path } from "path";
|
|
6
5
|
import { promisify } from "util";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
import util from "util";
|
|
13
|
-
import zlib from "zlib";
|
|
14
|
-
var asyncBrotliCompress = util.promisify(zlib.brotliCompress);
|
|
15
|
-
var FLAKINESS_DBG = !!process.env.FLAKINESS_DBG;
|
|
16
|
-
function errorText(error) {
|
|
17
|
-
return FLAKINESS_DBG ? error.stack : error.message;
|
|
18
|
-
}
|
|
19
|
-
async function retryWithBackoff(job, backoff = []) {
|
|
20
|
-
for (const timeout of backoff) {
|
|
21
|
-
try {
|
|
22
|
-
return await job();
|
|
23
|
-
} catch (e) {
|
|
24
|
-
if (e instanceof AggregateError)
|
|
25
|
-
console.error(`[flakiness.io err]`, errorText(e.errors[0]));
|
|
26
|
-
else if (e instanceof Error)
|
|
27
|
-
console.error(`[flakiness.io err]`, errorText(e));
|
|
28
|
-
else
|
|
29
|
-
console.error(`[flakiness.io err]`, e);
|
|
30
|
-
await new Promise((x) => setTimeout(x, timeout));
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
return await job();
|
|
34
|
-
}
|
|
35
|
-
var httpUtils;
|
|
36
|
-
((httpUtils2) => {
|
|
37
|
-
function createRequest({ url, method = "get", headers = {} }) {
|
|
38
|
-
let resolve;
|
|
39
|
-
let reject;
|
|
40
|
-
const responseDataPromise = new Promise((a, b) => {
|
|
41
|
-
resolve = a;
|
|
42
|
-
reject = b;
|
|
43
|
-
});
|
|
44
|
-
const protocol = url.startsWith("https") ? https : http;
|
|
45
|
-
headers = Object.fromEntries(Object.entries(headers).filter(([key, value]) => value !== void 0));
|
|
46
|
-
const request = protocol.request(url, { method, headers }, (res) => {
|
|
47
|
-
const chunks = [];
|
|
48
|
-
res.on("data", (chunk) => chunks.push(chunk));
|
|
49
|
-
res.on("end", () => {
|
|
50
|
-
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300)
|
|
51
|
-
resolve(Buffer.concat(chunks));
|
|
52
|
-
else
|
|
53
|
-
reject(new Error(`Request to ${url} failed with ${res.statusCode}`));
|
|
54
|
-
});
|
|
55
|
-
res.on("error", (error) => reject(error));
|
|
56
|
-
});
|
|
57
|
-
request.on("error", reject);
|
|
58
|
-
return { request, responseDataPromise };
|
|
59
|
-
}
|
|
60
|
-
httpUtils2.createRequest = createRequest;
|
|
61
|
-
async function getBuffer(url, backoff) {
|
|
62
|
-
return await retryWithBackoff(async () => {
|
|
63
|
-
const { request, responseDataPromise } = createRequest({ url });
|
|
64
|
-
request.end();
|
|
65
|
-
return await responseDataPromise;
|
|
66
|
-
}, backoff);
|
|
67
|
-
}
|
|
68
|
-
httpUtils2.getBuffer = getBuffer;
|
|
69
|
-
async function getText(url, backoff) {
|
|
70
|
-
const buffer = await getBuffer(url, backoff);
|
|
71
|
-
return buffer.toString("utf-8");
|
|
72
|
-
}
|
|
73
|
-
httpUtils2.getText = getText;
|
|
74
|
-
async function getJSON(url) {
|
|
75
|
-
return JSON.parse(await getText(url));
|
|
76
|
-
}
|
|
77
|
-
httpUtils2.getJSON = getJSON;
|
|
78
|
-
async function postText(url, text, backoff) {
|
|
79
|
-
const headers = {
|
|
80
|
-
"Content-Type": "application/json",
|
|
81
|
-
"Content-Length": Buffer.byteLength(text) + ""
|
|
82
|
-
};
|
|
83
|
-
return await retryWithBackoff(async () => {
|
|
84
|
-
const { request, responseDataPromise } = createRequest({ url, headers, method: "post" });
|
|
85
|
-
request.write(text);
|
|
86
|
-
request.end();
|
|
87
|
-
return await responseDataPromise;
|
|
88
|
-
}, backoff);
|
|
89
|
-
}
|
|
90
|
-
httpUtils2.postText = postText;
|
|
91
|
-
async function postJSON(url, json, backoff) {
|
|
92
|
-
const buffer = await postText(url, JSON.stringify(json), backoff);
|
|
93
|
-
return JSON.parse(buffer.toString("utf-8"));
|
|
94
|
-
}
|
|
95
|
-
httpUtils2.postJSON = postJSON;
|
|
96
|
-
})(httpUtils || (httpUtils = {}));
|
|
97
|
-
function shell(command, args, options) {
|
|
98
|
-
try {
|
|
99
|
-
const result = spawnSync(command, args, { encoding: "utf-8", ...options });
|
|
100
|
-
if (result.status !== 0) {
|
|
101
|
-
return void 0;
|
|
102
|
-
}
|
|
103
|
-
return result.stdout.trim();
|
|
104
|
-
} catch (e) {
|
|
105
|
-
console.error(e);
|
|
106
|
-
return void 0;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// src/gitWorktree.ts
|
|
111
|
-
var log = debug("fk:git");
|
|
112
|
-
var execAsync = promisify(exec);
|
|
113
|
-
var IS_WIN32_PATH = new RegExp("^[a-zA-Z]:\\\\", "i");
|
|
114
|
-
var IS_ALMOST_POSIX_PATH = new RegExp("^[a-zA-Z]:/", "i");
|
|
6
|
+
import { shell } from "./_internalUtils.js";
|
|
7
|
+
const log = debug("fk:git");
|
|
8
|
+
const execAsync = promisify(exec);
|
|
9
|
+
const IS_WIN32_PATH = new RegExp("^[a-zA-Z]:\\\\", "i");
|
|
10
|
+
const IS_ALMOST_POSIX_PATH = new RegExp("^[a-zA-Z]:/", "i");
|
|
115
11
|
function toPosixAbsolutePath(absolutePath) {
|
|
116
12
|
if (IS_WIN32_PATH.test(absolutePath)) {
|
|
117
13
|
absolutePath = absolutePath.split(win32Path.sep).join(posixPath.sep);
|
|
@@ -130,7 +26,7 @@ function toNativeAbsolutePath(posix) {
|
|
|
130
26
|
const rest = (m[2] ?? "").split(posixPath.sep).join(win32Path.sep);
|
|
131
27
|
return drive.toUpperCase() + ":" + rest;
|
|
132
28
|
}
|
|
133
|
-
|
|
29
|
+
class GitWorktree {
|
|
134
30
|
constructor(_gitRoot) {
|
|
135
31
|
this._gitRoot = _gitRoot;
|
|
136
32
|
this._posixGitRoot = toPosixAbsolutePath(this._gitRoot);
|
|
@@ -157,7 +53,7 @@ var GitWorktree = class _GitWorktree {
|
|
|
157
53
|
encoding: "utf-8"
|
|
158
54
|
});
|
|
159
55
|
assert(root, `FAILED: git rev-parse --show-toplevel HEAD @ ${somePathInsideGitRepo}`);
|
|
160
|
-
return new
|
|
56
|
+
return new GitWorktree(root);
|
|
161
57
|
}
|
|
162
58
|
_posixGitRoot;
|
|
163
59
|
/**
|
|
@@ -267,7 +163,7 @@ var GitWorktree = class _GitWorktree {
|
|
|
267
163
|
async listCommits(count) {
|
|
268
164
|
return await listCommits(this._gitRoot, "HEAD", count);
|
|
269
165
|
}
|
|
270
|
-
}
|
|
166
|
+
}
|
|
271
167
|
async function listCommits(gitRoot, head, count) {
|
|
272
168
|
const FIELD_SEPARATOR = "|~|";
|
|
273
169
|
const RECORD_SEPARATOR = "\0";
|