@flakiness/sdk 0.145.0 → 0.146.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/lib/{flakinessConfig.js → flakinessProjectConfig.js} +10 -17
- package/lib/playwright-test.js +243 -294
- package/lib/{cli/cmd-show-report.js → showReport.js} +37 -106
- package/package.json +14 -17
- package/types/tsconfig.tsbuildinfo +1 -1
- package/lib/cli/cli.js +0 -2382
- package/lib/cli/cmd-convert.js +0 -421
- package/lib/cli/cmd-download.js +0 -42
- package/lib/cli/cmd-link.js +0 -207
- package/lib/cli/cmd-login.js +0 -223
- package/lib/cli/cmd-logout.js +0 -170
- package/lib/cli/cmd-status.js +0 -273
- package/lib/cli/cmd-unlink.js +0 -199
- package/lib/cli/cmd-upload-playwright-json.js +0 -614
- package/lib/cli/cmd-upload.js +0 -321
- package/lib/cli/cmd-whoami.js +0 -173
- package/lib/flakinessSession.js +0 -159
- package/lib/junit.js +0 -310
- package/lib/playwrightJSONReport.js +0 -430
package/lib/cli/cmd-login.js
DELETED
|
@@ -1,223 +0,0 @@
|
|
|
1
|
-
// ../server/lib/common/knownClientIds.js
|
|
2
|
-
var KNOWN_CLIENT_IDS = {
|
|
3
|
-
OFFICIAL_WEB: "flakiness-io-official-cli",
|
|
4
|
-
OFFICIAL_CLI: "flakiness-io-official-website"
|
|
5
|
-
};
|
|
6
|
-
|
|
7
|
-
// src/cli/cmd-login.ts
|
|
8
|
-
import open from "open";
|
|
9
|
-
import os2 from "os";
|
|
10
|
-
|
|
11
|
-
// src/flakinessSession.ts
|
|
12
|
-
import fs from "fs/promises";
|
|
13
|
-
import os from "os";
|
|
14
|
-
import path from "path";
|
|
15
|
-
|
|
16
|
-
// src/serverapi.ts
|
|
17
|
-
import { TypedHTTP } from "@flakiness/shared/common/typedHttp.js";
|
|
18
|
-
|
|
19
|
-
// src/utils.ts
|
|
20
|
-
import { ReportUtils } from "@flakiness/report";
|
|
21
|
-
import http from "http";
|
|
22
|
-
import https from "https";
|
|
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
|
-
var ansiRegex = new RegExp("[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))", "g");
|
|
106
|
-
var IS_WIN32_PATH = new RegExp("^[a-zA-Z]:\\\\", "i");
|
|
107
|
-
var IS_ALMOST_POSIX_PATH = new RegExp("^[a-zA-Z]:/", "i");
|
|
108
|
-
|
|
109
|
-
// src/serverapi.ts
|
|
110
|
-
function createServerAPI(endpoint, options) {
|
|
111
|
-
endpoint += "/api/";
|
|
112
|
-
const fetcher = options?.auth ? (url, init) => fetch(url, {
|
|
113
|
-
...init,
|
|
114
|
-
headers: {
|
|
115
|
-
...init.headers,
|
|
116
|
-
"Authorization": `Bearer ${options.auth}`
|
|
117
|
-
}
|
|
118
|
-
}) : fetch;
|
|
119
|
-
if (options?.retries)
|
|
120
|
-
return TypedHTTP.createClient(endpoint, (url, init) => retryWithBackoff(() => fetcher(url, init), options.retries));
|
|
121
|
-
return TypedHTTP.createClient(endpoint, fetcher);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// src/flakinessSession.ts
|
|
125
|
-
var CONFIG_DIR = (() => {
|
|
126
|
-
const configDir = process.platform === "darwin" ? path.join(os.homedir(), "Library", "Application Support", "flakiness") : process.platform === "win32" ? path.join(os.homedir(), "AppData", "Roaming", "flakiness") : path.join(os.homedir(), ".config", "flakiness");
|
|
127
|
-
return configDir;
|
|
128
|
-
})();
|
|
129
|
-
var CONFIG_PATH = path.join(CONFIG_DIR, "config.json");
|
|
130
|
-
var FlakinessSession = class _FlakinessSession {
|
|
131
|
-
constructor(_config) {
|
|
132
|
-
this._config = _config;
|
|
133
|
-
this.api = createServerAPI(this._config.endpoint, { auth: this._config.token });
|
|
134
|
-
}
|
|
135
|
-
static async loadOrDie() {
|
|
136
|
-
const session = await _FlakinessSession.load();
|
|
137
|
-
if (!session)
|
|
138
|
-
throw new Error(`Please login first with 'npx flakiness login'`);
|
|
139
|
-
return session;
|
|
140
|
-
}
|
|
141
|
-
static async load() {
|
|
142
|
-
const data = await fs.readFile(CONFIG_PATH, "utf-8").catch((e) => void 0);
|
|
143
|
-
if (!data)
|
|
144
|
-
return void 0;
|
|
145
|
-
const json = JSON.parse(data);
|
|
146
|
-
return new _FlakinessSession(json);
|
|
147
|
-
}
|
|
148
|
-
static async remove() {
|
|
149
|
-
await fs.unlink(CONFIG_PATH).catch((e) => void 0);
|
|
150
|
-
}
|
|
151
|
-
api;
|
|
152
|
-
endpoint() {
|
|
153
|
-
return this._config.endpoint;
|
|
154
|
-
}
|
|
155
|
-
path() {
|
|
156
|
-
return CONFIG_PATH;
|
|
157
|
-
}
|
|
158
|
-
sessionToken() {
|
|
159
|
-
return this._config.token;
|
|
160
|
-
}
|
|
161
|
-
async save() {
|
|
162
|
-
await fs.mkdir(CONFIG_DIR, { recursive: true });
|
|
163
|
-
await fs.writeFile(CONFIG_PATH, JSON.stringify(this._config, null, 2));
|
|
164
|
-
}
|
|
165
|
-
};
|
|
166
|
-
|
|
167
|
-
// src/cli/cmd-logout.ts
|
|
168
|
-
async function cmdLogout() {
|
|
169
|
-
const session = await FlakinessSession.load();
|
|
170
|
-
if (!session)
|
|
171
|
-
return;
|
|
172
|
-
const currentSession = await session.api.user.currentSession.GET().catch((e) => void 0);
|
|
173
|
-
if (currentSession)
|
|
174
|
-
await session.api.user.logoutSession.POST({ sessionId: currentSession.sessionPublicId }).catch((e) => void 0);
|
|
175
|
-
await FlakinessSession.remove();
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
// src/cli/cmd-login.ts
|
|
179
|
-
var DEFAULT_FLAKINESS_ENDPOINT = "https://flakiness.io";
|
|
180
|
-
async function cmdLogin(endpoint = DEFAULT_FLAKINESS_ENDPOINT) {
|
|
181
|
-
await cmdLogout();
|
|
182
|
-
const api = createServerAPI(endpoint);
|
|
183
|
-
const data = await api.deviceauth.createRequest.POST({
|
|
184
|
-
clientId: KNOWN_CLIENT_IDS.OFFICIAL_CLI,
|
|
185
|
-
name: os2.hostname()
|
|
186
|
-
});
|
|
187
|
-
await open(new URL(data.verificationUrl, endpoint).href);
|
|
188
|
-
console.log(`Please navigate to ${new URL(data.verificationUrl, endpoint)}`);
|
|
189
|
-
let token;
|
|
190
|
-
while (Date.now() < data.deadline) {
|
|
191
|
-
await new Promise((x) => setTimeout(x, 2e3));
|
|
192
|
-
const result = await api.deviceauth.getToken.GET({ deviceCode: data.deviceCode }).catch((e) => void 0);
|
|
193
|
-
if (!result) {
|
|
194
|
-
console.error(`Authorization request was rejected.`);
|
|
195
|
-
process.exit(1);
|
|
196
|
-
}
|
|
197
|
-
token = result.token;
|
|
198
|
-
if (token)
|
|
199
|
-
break;
|
|
200
|
-
}
|
|
201
|
-
if (!token) {
|
|
202
|
-
console.log(`Failed to login.`);
|
|
203
|
-
process.exit(1);
|
|
204
|
-
}
|
|
205
|
-
const session = new FlakinessSession({
|
|
206
|
-
endpoint,
|
|
207
|
-
token
|
|
208
|
-
});
|
|
209
|
-
try {
|
|
210
|
-
const user = await session.api.user.whoami.GET();
|
|
211
|
-
await session.save();
|
|
212
|
-
console.log(`\u2713 Logged in as ${user.userName} (${user.userLogin})`);
|
|
213
|
-
} catch (e) {
|
|
214
|
-
const message = e instanceof Error ? e.message : String(e);
|
|
215
|
-
console.error(`x Failed to login:`, message);
|
|
216
|
-
}
|
|
217
|
-
return session;
|
|
218
|
-
}
|
|
219
|
-
export {
|
|
220
|
-
DEFAULT_FLAKINESS_ENDPOINT,
|
|
221
|
-
cmdLogin
|
|
222
|
-
};
|
|
223
|
-
//# sourceMappingURL=cmd-login.js.map
|
package/lib/cli/cmd-logout.js
DELETED
|
@@ -1,170 +0,0 @@
|
|
|
1
|
-
// src/flakinessSession.ts
|
|
2
|
-
import fs from "fs/promises";
|
|
3
|
-
import os from "os";
|
|
4
|
-
import path from "path";
|
|
5
|
-
|
|
6
|
-
// src/serverapi.ts
|
|
7
|
-
import { TypedHTTP } from "@flakiness/shared/common/typedHttp.js";
|
|
8
|
-
|
|
9
|
-
// src/utils.ts
|
|
10
|
-
import { ReportUtils } from "@flakiness/report";
|
|
11
|
-
import http from "http";
|
|
12
|
-
import https from "https";
|
|
13
|
-
var FLAKINESS_DBG = !!process.env.FLAKINESS_DBG;
|
|
14
|
-
function errorText(error) {
|
|
15
|
-
return FLAKINESS_DBG ? error.stack : error.message;
|
|
16
|
-
}
|
|
17
|
-
async function retryWithBackoff(job, backoff = []) {
|
|
18
|
-
for (const timeout of backoff) {
|
|
19
|
-
try {
|
|
20
|
-
return await job();
|
|
21
|
-
} catch (e) {
|
|
22
|
-
if (e instanceof AggregateError)
|
|
23
|
-
console.error(`[flakiness.io err]`, errorText(e.errors[0]));
|
|
24
|
-
else if (e instanceof Error)
|
|
25
|
-
console.error(`[flakiness.io err]`, errorText(e));
|
|
26
|
-
else
|
|
27
|
-
console.error(`[flakiness.io err]`, e);
|
|
28
|
-
await new Promise((x) => setTimeout(x, timeout));
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
return await job();
|
|
32
|
-
}
|
|
33
|
-
var httpUtils;
|
|
34
|
-
((httpUtils2) => {
|
|
35
|
-
function createRequest({ url, method = "get", headers = {} }) {
|
|
36
|
-
let resolve;
|
|
37
|
-
let reject;
|
|
38
|
-
const responseDataPromise = new Promise((a, b) => {
|
|
39
|
-
resolve = a;
|
|
40
|
-
reject = b;
|
|
41
|
-
});
|
|
42
|
-
const protocol = url.startsWith("https") ? https : http;
|
|
43
|
-
headers = Object.fromEntries(Object.entries(headers).filter(([key, value]) => value !== void 0));
|
|
44
|
-
const request = protocol.request(url, { method, headers }, (res) => {
|
|
45
|
-
const chunks = [];
|
|
46
|
-
res.on("data", (chunk) => chunks.push(chunk));
|
|
47
|
-
res.on("end", () => {
|
|
48
|
-
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300)
|
|
49
|
-
resolve(Buffer.concat(chunks));
|
|
50
|
-
else
|
|
51
|
-
reject(new Error(`Request to ${url} failed with ${res.statusCode}`));
|
|
52
|
-
});
|
|
53
|
-
res.on("error", (error) => reject(error));
|
|
54
|
-
});
|
|
55
|
-
request.on("error", reject);
|
|
56
|
-
return { request, responseDataPromise };
|
|
57
|
-
}
|
|
58
|
-
httpUtils2.createRequest = createRequest;
|
|
59
|
-
async function getBuffer(url, backoff) {
|
|
60
|
-
return await retryWithBackoff(async () => {
|
|
61
|
-
const { request, responseDataPromise } = createRequest({ url });
|
|
62
|
-
request.end();
|
|
63
|
-
return await responseDataPromise;
|
|
64
|
-
}, backoff);
|
|
65
|
-
}
|
|
66
|
-
httpUtils2.getBuffer = getBuffer;
|
|
67
|
-
async function getText(url, backoff) {
|
|
68
|
-
const buffer = await getBuffer(url, backoff);
|
|
69
|
-
return buffer.toString("utf-8");
|
|
70
|
-
}
|
|
71
|
-
httpUtils2.getText = getText;
|
|
72
|
-
async function getJSON(url) {
|
|
73
|
-
return JSON.parse(await getText(url));
|
|
74
|
-
}
|
|
75
|
-
httpUtils2.getJSON = getJSON;
|
|
76
|
-
async function postText(url, text, backoff) {
|
|
77
|
-
const headers = {
|
|
78
|
-
"Content-Type": "application/json",
|
|
79
|
-
"Content-Length": Buffer.byteLength(text) + ""
|
|
80
|
-
};
|
|
81
|
-
return await retryWithBackoff(async () => {
|
|
82
|
-
const { request, responseDataPromise } = createRequest({ url, headers, method: "post" });
|
|
83
|
-
request.write(text);
|
|
84
|
-
request.end();
|
|
85
|
-
return await responseDataPromise;
|
|
86
|
-
}, backoff);
|
|
87
|
-
}
|
|
88
|
-
httpUtils2.postText = postText;
|
|
89
|
-
async function postJSON(url, json, backoff) {
|
|
90
|
-
const buffer = await postText(url, JSON.stringify(json), backoff);
|
|
91
|
-
return JSON.parse(buffer.toString("utf-8"));
|
|
92
|
-
}
|
|
93
|
-
httpUtils2.postJSON = postJSON;
|
|
94
|
-
})(httpUtils || (httpUtils = {}));
|
|
95
|
-
var ansiRegex = new RegExp("[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))", "g");
|
|
96
|
-
var IS_WIN32_PATH = new RegExp("^[a-zA-Z]:\\\\", "i");
|
|
97
|
-
var IS_ALMOST_POSIX_PATH = new RegExp("^[a-zA-Z]:/", "i");
|
|
98
|
-
|
|
99
|
-
// src/serverapi.ts
|
|
100
|
-
function createServerAPI(endpoint, options) {
|
|
101
|
-
endpoint += "/api/";
|
|
102
|
-
const fetcher = options?.auth ? (url, init) => fetch(url, {
|
|
103
|
-
...init,
|
|
104
|
-
headers: {
|
|
105
|
-
...init.headers,
|
|
106
|
-
"Authorization": `Bearer ${options.auth}`
|
|
107
|
-
}
|
|
108
|
-
}) : fetch;
|
|
109
|
-
if (options?.retries)
|
|
110
|
-
return TypedHTTP.createClient(endpoint, (url, init) => retryWithBackoff(() => fetcher(url, init), options.retries));
|
|
111
|
-
return TypedHTTP.createClient(endpoint, fetcher);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// src/flakinessSession.ts
|
|
115
|
-
var CONFIG_DIR = (() => {
|
|
116
|
-
const configDir = process.platform === "darwin" ? path.join(os.homedir(), "Library", "Application Support", "flakiness") : process.platform === "win32" ? path.join(os.homedir(), "AppData", "Roaming", "flakiness") : path.join(os.homedir(), ".config", "flakiness");
|
|
117
|
-
return configDir;
|
|
118
|
-
})();
|
|
119
|
-
var CONFIG_PATH = path.join(CONFIG_DIR, "config.json");
|
|
120
|
-
var FlakinessSession = class _FlakinessSession {
|
|
121
|
-
constructor(_config) {
|
|
122
|
-
this._config = _config;
|
|
123
|
-
this.api = createServerAPI(this._config.endpoint, { auth: this._config.token });
|
|
124
|
-
}
|
|
125
|
-
static async loadOrDie() {
|
|
126
|
-
const session = await _FlakinessSession.load();
|
|
127
|
-
if (!session)
|
|
128
|
-
throw new Error(`Please login first with 'npx flakiness login'`);
|
|
129
|
-
return session;
|
|
130
|
-
}
|
|
131
|
-
static async load() {
|
|
132
|
-
const data = await fs.readFile(CONFIG_PATH, "utf-8").catch((e) => void 0);
|
|
133
|
-
if (!data)
|
|
134
|
-
return void 0;
|
|
135
|
-
const json = JSON.parse(data);
|
|
136
|
-
return new _FlakinessSession(json);
|
|
137
|
-
}
|
|
138
|
-
static async remove() {
|
|
139
|
-
await fs.unlink(CONFIG_PATH).catch((e) => void 0);
|
|
140
|
-
}
|
|
141
|
-
api;
|
|
142
|
-
endpoint() {
|
|
143
|
-
return this._config.endpoint;
|
|
144
|
-
}
|
|
145
|
-
path() {
|
|
146
|
-
return CONFIG_PATH;
|
|
147
|
-
}
|
|
148
|
-
sessionToken() {
|
|
149
|
-
return this._config.token;
|
|
150
|
-
}
|
|
151
|
-
async save() {
|
|
152
|
-
await fs.mkdir(CONFIG_DIR, { recursive: true });
|
|
153
|
-
await fs.writeFile(CONFIG_PATH, JSON.stringify(this._config, null, 2));
|
|
154
|
-
}
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
// src/cli/cmd-logout.ts
|
|
158
|
-
async function cmdLogout() {
|
|
159
|
-
const session = await FlakinessSession.load();
|
|
160
|
-
if (!session)
|
|
161
|
-
return;
|
|
162
|
-
const currentSession = await session.api.user.currentSession.GET().catch((e) => void 0);
|
|
163
|
-
if (currentSession)
|
|
164
|
-
await session.api.user.logoutSession.POST({ sessionId: currentSession.sessionPublicId }).catch((e) => void 0);
|
|
165
|
-
await FlakinessSession.remove();
|
|
166
|
-
}
|
|
167
|
-
export {
|
|
168
|
-
cmdLogout
|
|
169
|
-
};
|
|
170
|
-
//# sourceMappingURL=cmd-logout.js.map
|
package/lib/cli/cmd-status.js
DELETED
|
@@ -1,273 +0,0 @@
|
|
|
1
|
-
// src/flakinessConfig.ts
|
|
2
|
-
import fs from "fs";
|
|
3
|
-
import path2 from "path";
|
|
4
|
-
|
|
5
|
-
// src/utils.ts
|
|
6
|
-
import { ReportUtils } from "@flakiness/report";
|
|
7
|
-
import assert from "assert";
|
|
8
|
-
import { spawnSync } from "child_process";
|
|
9
|
-
import http from "http";
|
|
10
|
-
import https from "https";
|
|
11
|
-
import path, { posix as posixPath, win32 as win32Path } from "path";
|
|
12
|
-
var FLAKINESS_DBG = !!process.env.FLAKINESS_DBG;
|
|
13
|
-
function errorText(error) {
|
|
14
|
-
return FLAKINESS_DBG ? error.stack : error.message;
|
|
15
|
-
}
|
|
16
|
-
async function retryWithBackoff(job, backoff = []) {
|
|
17
|
-
for (const timeout of backoff) {
|
|
18
|
-
try {
|
|
19
|
-
return await job();
|
|
20
|
-
} catch (e) {
|
|
21
|
-
if (e instanceof AggregateError)
|
|
22
|
-
console.error(`[flakiness.io err]`, errorText(e.errors[0]));
|
|
23
|
-
else if (e instanceof Error)
|
|
24
|
-
console.error(`[flakiness.io err]`, errorText(e));
|
|
25
|
-
else
|
|
26
|
-
console.error(`[flakiness.io err]`, e);
|
|
27
|
-
await new Promise((x) => setTimeout(x, timeout));
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
return await job();
|
|
31
|
-
}
|
|
32
|
-
var httpUtils;
|
|
33
|
-
((httpUtils2) => {
|
|
34
|
-
function createRequest({ url, method = "get", headers = {} }) {
|
|
35
|
-
let resolve;
|
|
36
|
-
let reject;
|
|
37
|
-
const responseDataPromise = new Promise((a, b) => {
|
|
38
|
-
resolve = a;
|
|
39
|
-
reject = b;
|
|
40
|
-
});
|
|
41
|
-
const protocol = url.startsWith("https") ? https : http;
|
|
42
|
-
headers = Object.fromEntries(Object.entries(headers).filter(([key, value]) => value !== void 0));
|
|
43
|
-
const request = protocol.request(url, { method, headers }, (res) => {
|
|
44
|
-
const chunks = [];
|
|
45
|
-
res.on("data", (chunk) => chunks.push(chunk));
|
|
46
|
-
res.on("end", () => {
|
|
47
|
-
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300)
|
|
48
|
-
resolve(Buffer.concat(chunks));
|
|
49
|
-
else
|
|
50
|
-
reject(new Error(`Request to ${url} failed with ${res.statusCode}`));
|
|
51
|
-
});
|
|
52
|
-
res.on("error", (error) => reject(error));
|
|
53
|
-
});
|
|
54
|
-
request.on("error", reject);
|
|
55
|
-
return { request, responseDataPromise };
|
|
56
|
-
}
|
|
57
|
-
httpUtils2.createRequest = createRequest;
|
|
58
|
-
async function getBuffer(url, backoff) {
|
|
59
|
-
return await retryWithBackoff(async () => {
|
|
60
|
-
const { request, responseDataPromise } = createRequest({ url });
|
|
61
|
-
request.end();
|
|
62
|
-
return await responseDataPromise;
|
|
63
|
-
}, backoff);
|
|
64
|
-
}
|
|
65
|
-
httpUtils2.getBuffer = getBuffer;
|
|
66
|
-
async function getText(url, backoff) {
|
|
67
|
-
const buffer = await getBuffer(url, backoff);
|
|
68
|
-
return buffer.toString("utf-8");
|
|
69
|
-
}
|
|
70
|
-
httpUtils2.getText = getText;
|
|
71
|
-
async function getJSON(url) {
|
|
72
|
-
return JSON.parse(await getText(url));
|
|
73
|
-
}
|
|
74
|
-
httpUtils2.getJSON = getJSON;
|
|
75
|
-
async function postText(url, text, backoff) {
|
|
76
|
-
const headers = {
|
|
77
|
-
"Content-Type": "application/json",
|
|
78
|
-
"Content-Length": Buffer.byteLength(text) + ""
|
|
79
|
-
};
|
|
80
|
-
return await retryWithBackoff(async () => {
|
|
81
|
-
const { request, responseDataPromise } = createRequest({ url, headers, method: "post" });
|
|
82
|
-
request.write(text);
|
|
83
|
-
request.end();
|
|
84
|
-
return await responseDataPromise;
|
|
85
|
-
}, backoff);
|
|
86
|
-
}
|
|
87
|
-
httpUtils2.postText = postText;
|
|
88
|
-
async function postJSON(url, json, backoff) {
|
|
89
|
-
const buffer = await postText(url, JSON.stringify(json), backoff);
|
|
90
|
-
return JSON.parse(buffer.toString("utf-8"));
|
|
91
|
-
}
|
|
92
|
-
httpUtils2.postJSON = postJSON;
|
|
93
|
-
})(httpUtils || (httpUtils = {}));
|
|
94
|
-
var ansiRegex = new RegExp("[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))", "g");
|
|
95
|
-
function shell(command, args, options) {
|
|
96
|
-
try {
|
|
97
|
-
const result = spawnSync(command, args, { encoding: "utf-8", ...options });
|
|
98
|
-
if (result.status !== 0) {
|
|
99
|
-
return void 0;
|
|
100
|
-
}
|
|
101
|
-
return result.stdout.trim();
|
|
102
|
-
} catch (e) {
|
|
103
|
-
console.error(e);
|
|
104
|
-
return void 0;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
function computeGitRoot(somePathInsideGitRepo) {
|
|
108
|
-
const root = shell(`git`, ["rev-parse", "--show-toplevel"], {
|
|
109
|
-
cwd: somePathInsideGitRepo,
|
|
110
|
-
encoding: "utf-8"
|
|
111
|
-
});
|
|
112
|
-
assert(root, `FAILED: git rev-parse --show-toplevel HEAD @ ${somePathInsideGitRepo}`);
|
|
113
|
-
return normalizePath(root);
|
|
114
|
-
}
|
|
115
|
-
var IS_WIN32_PATH = new RegExp("^[a-zA-Z]:\\\\", "i");
|
|
116
|
-
var IS_ALMOST_POSIX_PATH = new RegExp("^[a-zA-Z]:/", "i");
|
|
117
|
-
function normalizePath(aPath) {
|
|
118
|
-
if (IS_WIN32_PATH.test(aPath)) {
|
|
119
|
-
aPath = aPath.split(win32Path.sep).join(posixPath.sep);
|
|
120
|
-
}
|
|
121
|
-
if (IS_ALMOST_POSIX_PATH.test(aPath))
|
|
122
|
-
return "/" + aPath[0] + aPath.substring(2);
|
|
123
|
-
return aPath;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// src/flakinessConfig.ts
|
|
127
|
-
function createConfigPath(dir) {
|
|
128
|
-
return path2.join(dir, ".flakiness", "config.json");
|
|
129
|
-
}
|
|
130
|
-
var gConfigPath;
|
|
131
|
-
function ensureConfigPath() {
|
|
132
|
-
if (!gConfigPath)
|
|
133
|
-
gConfigPath = computeConfigPath();
|
|
134
|
-
return gConfigPath;
|
|
135
|
-
}
|
|
136
|
-
function computeConfigPath() {
|
|
137
|
-
for (let p = process.cwd(); p !== path2.resolve(p, ".."); p = path2.resolve(p, "..")) {
|
|
138
|
-
const configPath = createConfigPath(p);
|
|
139
|
-
if (fs.existsSync(configPath))
|
|
140
|
-
return configPath;
|
|
141
|
-
}
|
|
142
|
-
try {
|
|
143
|
-
const gitRoot = computeGitRoot(process.cwd());
|
|
144
|
-
return createConfigPath(gitRoot);
|
|
145
|
-
} catch (e) {
|
|
146
|
-
return createConfigPath(process.cwd());
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
var FlakinessConfig = class _FlakinessConfig {
|
|
150
|
-
constructor(_configPath, _config) {
|
|
151
|
-
this._configPath = _configPath;
|
|
152
|
-
this._config = _config;
|
|
153
|
-
}
|
|
154
|
-
static async load() {
|
|
155
|
-
const configPath = ensureConfigPath();
|
|
156
|
-
const data = await fs.promises.readFile(configPath, "utf-8").catch((e) => void 0);
|
|
157
|
-
const json = data ? JSON.parse(data) : {};
|
|
158
|
-
return new _FlakinessConfig(configPath, json);
|
|
159
|
-
}
|
|
160
|
-
static async projectOrDie(session) {
|
|
161
|
-
const config = await _FlakinessConfig.load();
|
|
162
|
-
const projectPublicId = config.projectPublicId();
|
|
163
|
-
if (!projectPublicId)
|
|
164
|
-
throw new Error(`Please link to flakiness project with 'npx flakiness link'`);
|
|
165
|
-
const project = await session.api.project.getProject.GET({ projectPublicId }).catch((e) => void 0);
|
|
166
|
-
if (!project)
|
|
167
|
-
throw new Error(`Failed to fetch linked project; please re-link with 'npx flakiness link'`);
|
|
168
|
-
return project;
|
|
169
|
-
}
|
|
170
|
-
static createEmpty() {
|
|
171
|
-
return new _FlakinessConfig(ensureConfigPath(), {});
|
|
172
|
-
}
|
|
173
|
-
path() {
|
|
174
|
-
return this._configPath;
|
|
175
|
-
}
|
|
176
|
-
projectPublicId() {
|
|
177
|
-
return this._config.projectPublicId;
|
|
178
|
-
}
|
|
179
|
-
setProjectPublicId(projectId) {
|
|
180
|
-
this._config.projectPublicId = projectId;
|
|
181
|
-
}
|
|
182
|
-
async save() {
|
|
183
|
-
await fs.promises.mkdir(path2.dirname(this._configPath), { recursive: true });
|
|
184
|
-
await fs.promises.writeFile(this._configPath, JSON.stringify(this._config, null, 2));
|
|
185
|
-
}
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
// src/flakinessSession.ts
|
|
189
|
-
import fs2 from "fs/promises";
|
|
190
|
-
import os from "os";
|
|
191
|
-
import path3 from "path";
|
|
192
|
-
|
|
193
|
-
// src/serverapi.ts
|
|
194
|
-
import { TypedHTTP } from "@flakiness/shared/common/typedHttp.js";
|
|
195
|
-
function createServerAPI(endpoint, options) {
|
|
196
|
-
endpoint += "/api/";
|
|
197
|
-
const fetcher = options?.auth ? (url, init) => fetch(url, {
|
|
198
|
-
...init,
|
|
199
|
-
headers: {
|
|
200
|
-
...init.headers,
|
|
201
|
-
"Authorization": `Bearer ${options.auth}`
|
|
202
|
-
}
|
|
203
|
-
}) : fetch;
|
|
204
|
-
if (options?.retries)
|
|
205
|
-
return TypedHTTP.createClient(endpoint, (url, init) => retryWithBackoff(() => fetcher(url, init), options.retries));
|
|
206
|
-
return TypedHTTP.createClient(endpoint, fetcher);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// src/flakinessSession.ts
|
|
210
|
-
var CONFIG_DIR = (() => {
|
|
211
|
-
const configDir = process.platform === "darwin" ? path3.join(os.homedir(), "Library", "Application Support", "flakiness") : process.platform === "win32" ? path3.join(os.homedir(), "AppData", "Roaming", "flakiness") : path3.join(os.homedir(), ".config", "flakiness");
|
|
212
|
-
return configDir;
|
|
213
|
-
})();
|
|
214
|
-
var CONFIG_PATH = path3.join(CONFIG_DIR, "config.json");
|
|
215
|
-
var FlakinessSession = class _FlakinessSession {
|
|
216
|
-
constructor(_config) {
|
|
217
|
-
this._config = _config;
|
|
218
|
-
this.api = createServerAPI(this._config.endpoint, { auth: this._config.token });
|
|
219
|
-
}
|
|
220
|
-
static async loadOrDie() {
|
|
221
|
-
const session = await _FlakinessSession.load();
|
|
222
|
-
if (!session)
|
|
223
|
-
throw new Error(`Please login first with 'npx flakiness login'`);
|
|
224
|
-
return session;
|
|
225
|
-
}
|
|
226
|
-
static async load() {
|
|
227
|
-
const data = await fs2.readFile(CONFIG_PATH, "utf-8").catch((e) => void 0);
|
|
228
|
-
if (!data)
|
|
229
|
-
return void 0;
|
|
230
|
-
const json = JSON.parse(data);
|
|
231
|
-
return new _FlakinessSession(json);
|
|
232
|
-
}
|
|
233
|
-
static async remove() {
|
|
234
|
-
await fs2.unlink(CONFIG_PATH).catch((e) => void 0);
|
|
235
|
-
}
|
|
236
|
-
api;
|
|
237
|
-
endpoint() {
|
|
238
|
-
return this._config.endpoint;
|
|
239
|
-
}
|
|
240
|
-
path() {
|
|
241
|
-
return CONFIG_PATH;
|
|
242
|
-
}
|
|
243
|
-
sessionToken() {
|
|
244
|
-
return this._config.token;
|
|
245
|
-
}
|
|
246
|
-
async save() {
|
|
247
|
-
await fs2.mkdir(CONFIG_DIR, { recursive: true });
|
|
248
|
-
await fs2.writeFile(CONFIG_PATH, JSON.stringify(this._config, null, 2));
|
|
249
|
-
}
|
|
250
|
-
};
|
|
251
|
-
|
|
252
|
-
// src/cli/cmd-status.ts
|
|
253
|
-
async function cmdStatus() {
|
|
254
|
-
const session = await FlakinessSession.load();
|
|
255
|
-
if (!session) {
|
|
256
|
-
console.log(`user: not logged in`);
|
|
257
|
-
return;
|
|
258
|
-
}
|
|
259
|
-
const user = await session.api.user.whoami.GET();
|
|
260
|
-
console.log(`user: ${user.userName} (${user.userLogin})`);
|
|
261
|
-
const config = await FlakinessConfig.load();
|
|
262
|
-
const projectPublicId = config.projectPublicId();
|
|
263
|
-
if (!projectPublicId) {
|
|
264
|
-
console.log(`project: <not linked>`);
|
|
265
|
-
return;
|
|
266
|
-
}
|
|
267
|
-
const project = await session.api.project.getProject.GET({ projectPublicId });
|
|
268
|
-
console.log(`project: ${session.endpoint()}/${project.org.orgSlug}/${project.projectSlug}`);
|
|
269
|
-
}
|
|
270
|
-
export {
|
|
271
|
-
cmdStatus
|
|
272
|
-
};
|
|
273
|
-
//# sourceMappingURL=cmd-status.js.map
|