flakiness 0.148.0 → 0.149.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/cli/cli.js CHANGED
@@ -738,16 +738,16 @@ var Ranges;
738
738
 
739
739
  // src/cli/cli.ts
740
740
  import { TypedHTTP as TypedHTTP2 } from "@flakiness/shared/common/typedHttp.js";
741
- import assert3 from "assert";
741
+ import assert2 from "assert";
742
742
  import { Command, Option } from "commander";
743
- import fs8 from "fs";
743
+ import fs7 from "fs";
744
744
  import ora from "ora";
745
- import path8 from "path";
745
+ import path7 from "path";
746
746
 
747
747
  // ../package.json
748
748
  var package_default = {
749
749
  name: "flakiness",
750
- version: "0.148.0",
750
+ version: "0.149.0",
751
751
  private: true,
752
752
  scripts: {
753
753
  minor: "./version.mjs minor",
@@ -790,7 +790,7 @@ var package_default = {
790
790
 
791
791
  // src/flakinessSession.ts
792
792
  import fs2 from "fs/promises";
793
- import os2 from "os";
793
+ import os from "os";
794
794
  import path2 from "path";
795
795
 
796
796
  // src/serverapi.ts
@@ -798,23 +798,11 @@ import { TypedHTTP } from "@flakiness/shared/common/typedHttp.js";
798
798
 
799
799
  // src/utils.ts
800
800
  import { ReportUtils } from "@flakiness/sdk";
801
- import assert from "assert";
802
- import { spawnSync } from "child_process";
803
801
  import crypto from "crypto";
804
802
  import fs from "fs";
805
803
  import http from "http";
806
804
  import https from "https";
807
- import os from "os";
808
- import path, { posix as posixPath, win32 as win32Path } from "path";
809
- async function existsAsync(aPath) {
810
- return fs.promises.stat(aPath).then(() => true).catch((e) => false);
811
- }
812
- function extractEnvConfiguration() {
813
- const ENV_PREFIX = "FK_ENV_";
814
- return Object.fromEntries(
815
- Object.entries(process.env).filter(([key]) => key.toUpperCase().startsWith(ENV_PREFIX.toUpperCase())).map(([key, value]) => [key.substring(ENV_PREFIX.length).toLowerCase(), (value ?? "").trim().toLowerCase()])
816
- );
817
- }
805
+ import path from "path";
818
806
  function sha1File(filePath) {
819
807
  return new Promise((resolve, reject) => {
820
808
  const hash = crypto.createHash("sha1");
@@ -918,96 +906,6 @@ var httpUtils;
918
906
  httpUtils2.postJSON = postJSON;
919
907
  })(httpUtils || (httpUtils = {}));
920
908
  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");
921
- function stripAnsi(str) {
922
- return str.replace(ansiRegex, "");
923
- }
924
- async function saveReportAndAttachments(report, attachments, outputFolder) {
925
- const reportPath = path.join(outputFolder, "report.json");
926
- const attachmentsFolder = path.join(outputFolder, "attachments");
927
- await fs.promises.rm(outputFolder, { recursive: true, force: true });
928
- await fs.promises.mkdir(outputFolder, { recursive: true });
929
- await fs.promises.writeFile(reportPath, JSON.stringify(report), "utf-8");
930
- if (attachments.length)
931
- await fs.promises.mkdir(attachmentsFolder);
932
- const movedAttachments = [];
933
- for (const attachment of attachments) {
934
- const attachmentPath = path.join(attachmentsFolder, attachment.id);
935
- if (attachment.path)
936
- await fs.promises.cp(attachment.path, attachmentPath);
937
- else if (attachment.body)
938
- await fs.promises.writeFile(attachmentPath, attachment.body);
939
- movedAttachments.push({
940
- contentType: attachment.contentType,
941
- id: attachment.id,
942
- path: attachmentPath
943
- });
944
- }
945
- return movedAttachments;
946
- }
947
- function shell(command, args, options) {
948
- try {
949
- const result = spawnSync(command, args, { encoding: "utf-8", ...options });
950
- if (result.status !== 0) {
951
- return void 0;
952
- }
953
- return result.stdout.trim();
954
- } catch (e) {
955
- console.error(e);
956
- return void 0;
957
- }
958
- }
959
- function readLinuxOSRelease() {
960
- const osReleaseText = fs.readFileSync("/etc/os-release", "utf-8");
961
- return new Map(osReleaseText.toLowerCase().split("\n").filter((line) => line.includes("=")).map((line) => {
962
- line = line.trim();
963
- let [key, value] = line.split("=");
964
- if (value.startsWith('"') && value.endsWith('"'))
965
- value = value.substring(1, value.length - 1);
966
- return [key, value];
967
- }));
968
- }
969
- function osLinuxInfo() {
970
- const arch = shell(`uname`, [`-m`]);
971
- const osReleaseMap = readLinuxOSRelease();
972
- const name = osReleaseMap.get("name") ?? shell(`uname`);
973
- const version = osReleaseMap.get("version_id");
974
- return { name, arch, version };
975
- }
976
- function osDarwinInfo() {
977
- const name = "macos";
978
- const arch = shell(`uname`, [`-m`]);
979
- const version = shell(`sw_vers`, [`-productVersion`]);
980
- return { name, arch, version };
981
- }
982
- function osWinInfo() {
983
- const name = "win";
984
- const arch = process.arch;
985
- const version = os.release();
986
- return { name, arch, version };
987
- }
988
- function getOSInfo() {
989
- if (process.platform === "darwin")
990
- return osDarwinInfo();
991
- if (process.platform === "win32")
992
- return osWinInfo();
993
- return osLinuxInfo();
994
- }
995
- function inferRunUrl() {
996
- if (process.env.GITHUB_REPOSITORY && process.env.GITHUB_RUN_ID)
997
- return `https://github.com/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`;
998
- return void 0;
999
- }
1000
- function parseStringDate(dateString) {
1001
- return +new Date(dateString);
1002
- }
1003
- function gitCommitInfo(gitRepo) {
1004
- const sha = shell(`git`, ["rev-parse", "HEAD"], {
1005
- cwd: gitRepo,
1006
- encoding: "utf-8"
1007
- });
1008
- assert(sha, `FAILED: git rev-parse HEAD @ ${gitRepo}`);
1009
- return sha.trim();
1010
- }
1011
909
  async function resolveAttachmentPaths(report, attachmentsDir) {
1012
910
  const attachmentFiles = await listFilesRecursively(attachmentsDir);
1013
911
  const filenameToPath = new Map(attachmentFiles.map((file) => [path.basename(file), file]));
@@ -1023,7 +921,8 @@ async function resolveAttachmentPaths(report, attachmentsDir) {
1023
921
  attachmentIdToPath.set(attachment.id, {
1024
922
  contentType: attachment.contentType,
1025
923
  id: attachment.id,
1026
- path: attachmentPath
924
+ path: attachmentPath,
925
+ type: "file"
1027
926
  });
1028
927
  }
1029
928
  }
@@ -1042,65 +941,6 @@ async function listFilesRecursively(dir, result = []) {
1042
941
  }
1043
942
  return result;
1044
943
  }
1045
- function computeGitRoot(somePathInsideGitRepo) {
1046
- const root = shell(`git`, ["rev-parse", "--show-toplevel"], {
1047
- cwd: somePathInsideGitRepo,
1048
- encoding: "utf-8"
1049
- });
1050
- assert(root, `FAILED: git rev-parse --show-toplevel HEAD @ ${somePathInsideGitRepo}`);
1051
- return normalizePath(root);
1052
- }
1053
- var IS_WIN32_PATH = new RegExp("^[a-zA-Z]:\\\\", "i");
1054
- var IS_ALMOST_POSIX_PATH = new RegExp("^[a-zA-Z]:/", "i");
1055
- function normalizePath(aPath) {
1056
- if (IS_WIN32_PATH.test(aPath)) {
1057
- aPath = aPath.split(win32Path.sep).join(posixPath.sep);
1058
- }
1059
- if (IS_ALMOST_POSIX_PATH.test(aPath))
1060
- return "/" + aPath[0] + aPath.substring(2);
1061
- return aPath;
1062
- }
1063
- function gitFilePath(gitRoot, absolutePath) {
1064
- return posixPath.relative(gitRoot, absolutePath);
1065
- }
1066
- function parseDurationMS(value) {
1067
- if (isNaN(value))
1068
- throw new Error("Duration cannot be NaN");
1069
- if (value < 0)
1070
- throw new Error(`Duration cannot be less than 0, found ${value}`);
1071
- return value | 0;
1072
- }
1073
- function createEnvironments(projects) {
1074
- const envConfiguration = extractEnvConfiguration();
1075
- const osInfo = getOSInfo();
1076
- let uniqueNames = /* @__PURE__ */ new Set();
1077
- const result = /* @__PURE__ */ new Map();
1078
- for (const project of projects) {
1079
- let defaultName = project.name;
1080
- if (!defaultName.trim())
1081
- defaultName = "anonymous";
1082
- let name = defaultName;
1083
- for (let i = 2; uniqueNames.has(name); ++i)
1084
- name = `${defaultName}-${i}`;
1085
- uniqueNames.add(defaultName);
1086
- result.set(project, {
1087
- name,
1088
- systemData: {
1089
- osArch: osInfo.arch,
1090
- osName: osInfo.name,
1091
- osVersion: osInfo.version
1092
- },
1093
- userSuppliedData: {
1094
- ...envConfiguration,
1095
- ...project.metadata
1096
- },
1097
- opaqueData: {
1098
- project
1099
- }
1100
- });
1101
- }
1102
- return result;
1103
- }
1104
944
 
1105
945
  // src/serverapi.ts
1106
946
  function createServerAPI(endpoint, options) {
@@ -1119,7 +959,7 @@ function createServerAPI(endpoint, options) {
1119
959
 
1120
960
  // src/flakinessSession.ts
1121
961
  var CONFIG_DIR = (() => {
1122
- const configDir = process.platform === "darwin" ? path2.join(os2.homedir(), "Library", "Application Support", "flakiness") : process.platform === "win32" ? path2.join(os2.homedir(), "AppData", "Roaming", "flakiness") : path2.join(os2.homedir(), ".config", "flakiness");
962
+ const configDir = process.platform === "darwin" ? path2.join(os.homedir(), "Library", "Application Support", "flakiness") : process.platform === "win32" ? path2.join(os.homedir(), "AppData", "Roaming", "flakiness") : path2.join(os.homedir(), ".config", "flakiness");
1123
963
  return configDir;
1124
964
  })();
1125
965
  var CONFIG_PATH = path2.join(CONFIG_DIR, "config.json");
@@ -1161,13 +1001,14 @@ var FlakinessSession = class _FlakinessSession {
1161
1001
  };
1162
1002
 
1163
1003
  // src/cli/cmd-convert.ts
1004
+ import { GitWorktree, writeReport } from "@flakiness/sdk";
1164
1005
  import fs4 from "fs/promises";
1165
1006
  import path4 from "path";
1166
1007
 
1167
1008
  // src/junit.ts
1168
1009
  import { ReportUtils as ReportUtils2 } from "@flakiness/sdk";
1169
1010
  import { parseXml, XmlElement, XmlText } from "@rgrove/parse-xml";
1170
- import assert2 from "assert";
1011
+ import assert from "assert";
1171
1012
  import fs3 from "fs";
1172
1013
  import path3 from "path";
1173
1014
  function getProperties(element) {
@@ -1217,13 +1058,15 @@ async function parseAttachment(value) {
1217
1058
  return {
1218
1059
  contentType: "image/png",
1219
1060
  path: absolutePath,
1220
- id
1061
+ id,
1062
+ type: "file"
1221
1063
  };
1222
1064
  }
1223
1065
  return {
1224
1066
  contentType: "text/plain",
1225
1067
  id: sha1Buffer(value),
1226
- body: Buffer.from(value)
1068
+ body: Buffer.from(value),
1069
+ type: "buffer"
1227
1070
  };
1228
1071
  }
1229
1072
  async function traverseJUnitReport(context, node) {
@@ -1264,7 +1107,7 @@ async function traverseJUnitReport(context, node) {
1264
1107
  currentEnvIndex = report.environments.push(currentEnv) - 1;
1265
1108
  }
1266
1109
  } else if (element.name === "testcase") {
1267
- assert2(currentSuite);
1110
+ assert(currentSuite);
1268
1111
  const file = element.attributes["file"];
1269
1112
  const name = element.attributes["name"];
1270
1113
  const line = parseInt(element.attributes["line"], 10);
@@ -1284,7 +1127,7 @@ async function traverseJUnitReport(context, node) {
1284
1127
  id: attachment.id,
1285
1128
  contentType: attachment.contentType,
1286
1129
  //TODO: better default names for attachments?
1287
- name: attachment.path ? path3.basename(attachment.path) : `attachment`
1130
+ name: attachment.type === "file" ? path3.basename(attachment.path) : `attachment`
1288
1131
  });
1289
1132
  } else {
1290
1133
  annotations.push({
@@ -1389,7 +1232,8 @@ async function cmdConvert(junitPath, options) {
1389
1232
  commitId = options.commitId;
1390
1233
  } else {
1391
1234
  try {
1392
- commitId = gitCommitInfo(process.cwd());
1235
+ const worktree = GitWorktree.create(process.cwd());
1236
+ commitId = worktree.headCommitId();
1393
1237
  } catch (e) {
1394
1238
  console.error("Failed to get git commit info. Please provide --commit-id option.");
1395
1239
  process.exit(1);
@@ -1401,7 +1245,7 @@ async function cmdConvert(junitPath, options) {
1401
1245
  runStartTimestamp: Date.now(),
1402
1246
  runDuration: 0
1403
1247
  });
1404
- await saveReportAndAttachments(report, attachments, options.outputDir);
1248
+ await writeReport(report, attachments, options.outputDir);
1405
1249
  console.log(`\u2713 Saved to ${options.outputDir}`);
1406
1250
  }
1407
1251
  async function findXmlFiles(dir, result = []) {
@@ -1481,7 +1325,7 @@ var KNOWN_CLIENT_IDS = {
1481
1325
 
1482
1326
  // src/cli/cmd-login.ts
1483
1327
  import open from "open";
1484
- import os3 from "os";
1328
+ import os2 from "os";
1485
1329
 
1486
1330
  // src/cli/cmd-logout.ts
1487
1331
  async function cmdLogout() {
@@ -1501,7 +1345,7 @@ async function cmdLogin(endpoint = DEFAULT_FLAKINESS_ENDPOINT) {
1501
1345
  const api = createServerAPI(endpoint);
1502
1346
  const data = await api.deviceauth.createRequest.POST({
1503
1347
  clientId: KNOWN_CLIENT_IDS.OFFICIAL_CLI,
1504
- name: os3.hostname()
1348
+ name: os2.hostname()
1505
1349
  });
1506
1350
  await open(new URL(data.verificationUrl, endpoint).href);
1507
1351
  console.log(`Please navigate to ${new URL(data.verificationUrl, endpoint)}`);
@@ -1566,246 +1410,31 @@ async function cmdUnlink() {
1566
1410
  await config.save();
1567
1411
  }
1568
1412
 
1569
- // src/cli/cmd-upload-playwright-json.ts
1570
- import { ReportUploader } from "@flakiness/sdk";
1571
- import fs6 from "fs/promises";
1572
- import path6 from "path";
1573
-
1574
- // src/playwrightJSONReport.ts
1575
- import { FlakinessReport as FK2, ReportUtils as ReportUtils3 } from "@flakiness/sdk";
1576
- import debug from "debug";
1577
- import { posix as posixPath2 } from "path";
1578
- var dlog = debug("flakiness:json-report");
1579
- var PlaywrightJSONReport;
1580
- ((PlaywrightJSONReport2) => {
1581
- function collectMetadata(somePathInsideProject = process.cwd()) {
1582
- const commitId = gitCommitInfo(somePathInsideProject);
1583
- const osInfo = getOSInfo();
1584
- const metadata = {
1585
- gitRoot: computeGitRoot(somePathInsideProject),
1586
- commitId,
1587
- osName: osInfo.name,
1588
- arch: osInfo.arch,
1589
- osVersion: osInfo.version,
1590
- runURL: inferRunUrl()
1591
- };
1592
- dlog(`metadata directory: ${somePathInsideProject}`);
1593
- dlog(`metadata: ${JSON.stringify(metadata)}`);
1594
- dlog(`commit info: ${JSON.stringify(commitId)}`);
1595
- dlog(`os info: ${JSON.stringify(osInfo)}`);
1596
- return metadata;
1597
- }
1598
- PlaywrightJSONReport2.collectMetadata = collectMetadata;
1599
- async function parse(metadata, jsonReport, options) {
1600
- const context = {
1601
- projectId2environmentIdx: /* @__PURE__ */ new Map(),
1602
- testBaseDir: normalizePath(jsonReport.config.rootDir),
1603
- gitRoot: metadata.gitRoot,
1604
- attachments: /* @__PURE__ */ new Map(),
1605
- unaccessibleAttachmentPaths: [],
1606
- extractAttachments: options.extractAttachments
1607
- };
1608
- const configPath = jsonReport.config.configFile ? gitFilePath(context.gitRoot, normalizePath(jsonReport.config.configFile)) : void 0;
1609
- const report = {
1610
- category: FK2.CATEGORY_PLAYWRIGHT,
1611
- commitId: metadata.commitId,
1612
- configPath,
1613
- url: metadata.runURL,
1614
- environments: [],
1615
- suites: [],
1616
- opaqueData: jsonReport.config,
1617
- unattributedErrors: jsonReport.errors.map((error) => parseJSONError(context, error)),
1618
- // The report.stats is a releatively new addition to Playwright's JSONReport,
1619
- // so we have to polyfill with some reasonable values when it's missing.
1620
- duration: jsonReport.stats?.duration && jsonReport.stats?.duration > 0 ? parseDurationMS(jsonReport.stats.duration) : 0,
1621
- startTimestamp: jsonReport.stats && jsonReport.stats.startTime ? parseStringDate(jsonReport.stats.startTime) : Date.now()
1622
- };
1623
- report.environments = [...createEnvironments(jsonReport.config.projects).values()];
1624
- for (let envIdx = 0; envIdx < report.environments.length; ++envIdx)
1625
- context.projectId2environmentIdx.set(jsonReport.config.projects[envIdx].id, envIdx);
1626
- report.suites = await Promise.all(jsonReport.suites.map((suite) => parseJSONSuite(context, suite)));
1627
- return {
1628
- report: ReportUtils3.normalizeReport(report),
1629
- attachments: [...context.attachments.values()],
1630
- unaccessibleAttachmentPaths: context.unaccessibleAttachmentPaths
1631
- };
1632
- }
1633
- PlaywrightJSONReport2.parse = parse;
1634
- })(PlaywrightJSONReport || (PlaywrightJSONReport = {}));
1635
- async function parseJSONSuite(context, jsonSuite) {
1636
- let type = "suite";
1637
- if (jsonSuite.column === 0 && jsonSuite.line === 0)
1638
- type = "file";
1639
- else if (!jsonSuite.title)
1640
- type = "anonymous suite";
1641
- const suite = {
1642
- type,
1643
- title: jsonSuite.title,
1644
- location: {
1645
- file: gitFilePath(context.gitRoot, normalizePath(jsonSuite.file)),
1646
- line: jsonSuite.line,
1647
- column: jsonSuite.column
1648
- }
1649
- };
1650
- if (jsonSuite.suites && jsonSuite.suites.length)
1651
- suite.suites = await Promise.all(jsonSuite.suites.map((suite2) => parseJSONSuite(context, suite2)));
1652
- if (jsonSuite.specs && jsonSuite.specs.length)
1653
- suite.tests = await Promise.all(jsonSuite.specs.map((spec) => parseJSONSpec(context, spec)));
1654
- return suite;
1655
- }
1656
- async function parseJSONSpec(context, jsonSpec) {
1657
- const test = {
1658
- title: jsonSpec.title,
1659
- tags: jsonSpec.tags,
1660
- location: {
1661
- file: gitFilePath(context.gitRoot, normalizePath(posixPath2.join(context.testBaseDir, normalizePath(jsonSpec.file)))),
1662
- line: jsonSpec.line,
1663
- column: jsonSpec.column
1664
- },
1665
- attempts: []
1666
- };
1667
- for (const jsonTest of jsonSpec.tests) {
1668
- const environmentIdx = context.projectId2environmentIdx.get(jsonTest.projectId);
1669
- if (environmentIdx === void 0)
1670
- throw new Error("Inconsistent report - no project for a test found!");
1671
- const testResults = jsonTest.results.filter((result) => result.status !== void 0);
1672
- if (!testResults.length)
1673
- continue;
1674
- test.attempts.push(...await Promise.all(testResults.map((jsonTestResult) => parseJSONTestResult(context, jsonTest, environmentIdx, jsonTestResult))));
1675
- }
1676
- return test;
1677
- }
1678
- function createLocation(context, location) {
1679
- return {
1680
- file: gitFilePath(context.gitRoot, normalizePath(location.file)),
1681
- line: location.line,
1682
- column: location.column
1683
- };
1684
- }
1685
- async function parseJSONTestResult(context, jsonTest, environmentIdx, jsonTestResult) {
1686
- const attachments = [];
1687
- const attempt = {
1688
- timeout: parseDurationMS(jsonTest.timeout),
1689
- annotations: jsonTest.annotations.map((annotation) => ({
1690
- type: annotation.type,
1691
- description: annotation.description,
1692
- location: annotation.location ? createLocation(context, annotation.location) : void 0
1693
- })),
1694
- environmentIdx,
1695
- expectedStatus: jsonTest.expectedStatus,
1696
- parallelIndex: jsonTestResult.parallelIndex,
1697
- status: jsonTestResult.status,
1698
- errors: jsonTestResult.errors && jsonTestResult.errors.length ? jsonTestResult.errors.map((error) => parseJSONError(context, error)) : void 0,
1699
- stdout: jsonTestResult.stdout && jsonTestResult.stdout.length ? jsonTestResult.stdout : void 0,
1700
- stderr: jsonTestResult.stderr && jsonTestResult.stderr.length ? jsonTestResult.stderr : void 0,
1701
- steps: jsonTestResult.steps ? jsonTestResult.steps.map((jsonTestStep) => parseJSONTestStep(context, jsonTestStep)) : void 0,
1702
- startTimestamp: parseStringDate(jsonTestResult.startTime),
1703
- duration: jsonTestResult.duration && jsonTestResult.duration > 0 ? parseDurationMS(jsonTestResult.duration) : 0,
1704
- attachments
1705
- };
1706
- if (context.extractAttachments) {
1707
- await Promise.all((jsonTestResult.attachments ?? []).map(async (jsonAttachment) => {
1708
- if (jsonAttachment.path && !await existsAsync(jsonAttachment.path)) {
1709
- context.unaccessibleAttachmentPaths.push(jsonAttachment.path);
1710
- return;
1711
- }
1712
- const id = jsonAttachment.path ? await sha1File(jsonAttachment.path) : sha1Buffer(jsonAttachment.body ?? "");
1713
- context.attachments.set(id, {
1714
- contentType: jsonAttachment.contentType,
1715
- id,
1716
- body: jsonAttachment.body ? Buffer.from(jsonAttachment.body) : void 0,
1717
- path: jsonAttachment.path
1718
- });
1719
- attachments.push({
1720
- id,
1721
- name: jsonAttachment.name,
1722
- contentType: jsonAttachment.contentType
1723
- });
1724
- }));
1725
- }
1726
- return attempt;
1727
- }
1728
- function parseJSONTestStep(context, jsonStep) {
1729
- const step = {
1730
- // NOTE: jsonStep.duration was -1 in some playwright versions
1731
- duration: parseDurationMS(Math.max(jsonStep.duration, 0)),
1732
- title: jsonStep.title
1733
- };
1734
- if (jsonStep.error)
1735
- step.error = parseJSONError(context, jsonStep.error);
1736
- if (jsonStep.steps)
1737
- step.steps = jsonStep.steps.map((childJSONStep) => parseJSONTestStep(context, childJSONStep));
1738
- return step;
1739
- }
1740
- function parseJSONError(context, error) {
1741
- return {
1742
- location: error.location ? createLocation(context, error.location) : void 0,
1743
- message: error.message ? stripAnsi(error.message).split("\n")[0] : void 0,
1744
- stack: error.stack,
1745
- value: error.value
1746
- };
1747
- }
1748
-
1749
- // src/cli/cmd-upload-playwright-json.ts
1750
- async function cmdUploadPlaywrightJson(relativePath, options) {
1751
- const fullPath = path6.resolve(relativePath);
1752
- if (!await fs6.access(fullPath, fs6.constants.F_OK).then(() => true).catch(() => false)) {
1753
- console.error(`Error: path ${fullPath} is not accessible`);
1754
- process.exit(1);
1755
- }
1756
- const text = await fs6.readFile(fullPath, "utf-8");
1757
- const playwrightJson = JSON.parse(text);
1758
- const { attachments, report, unaccessibleAttachmentPaths } = await PlaywrightJSONReport.parse(PlaywrightJSONReport.collectMetadata(), playwrightJson, {
1759
- extractAttachments: true
1760
- });
1761
- for (const unaccessibleAttachment of unaccessibleAttachmentPaths)
1762
- console.warn(`WARN: cannot access attachment ${unaccessibleAttachment}`);
1763
- const uploader = new ReportUploader({
1764
- flakinessAccessToken: options.accessToken,
1765
- flakinessEndpoint: options.endpoint
1766
- });
1767
- const upload = uploader.createUpload(report, attachments);
1768
- const uploadResult = await upload.upload();
1769
- if (!uploadResult.success) {
1770
- console.log(`[flakiness.io] X Failed to upload to ${options.endpoint}: ${uploadResult.message}`);
1771
- } else {
1772
- console.log(`[flakiness.io] \u2713 Report uploaded ${uploadResult.reportUrl ?? uploadResult.message ?? ""}`);
1773
- }
1774
- }
1775
-
1776
1413
  // src/cli/cmd-upload.ts
1777
- import { ReportUploader as ReportUploader2 } from "@flakiness/sdk";
1414
+ import { uploadReport } from "@flakiness/sdk";
1778
1415
  import chalk from "chalk";
1779
- import fs7 from "fs/promises";
1780
- import path7 from "path";
1416
+ import fs6 from "fs/promises";
1417
+ import path6 from "path";
1781
1418
  var warn = (txt) => console.warn(chalk.yellow(`[flakiness.io] WARN: ${txt}`));
1782
1419
  var err = (txt) => console.error(chalk.red(`[flakiness.io] Error: ${txt}`));
1783
- var log = (txt) => console.log(`[flakiness.io] ${txt}`);
1784
1420
  async function cmdUpload(relativePaths, options) {
1785
- const uploader = new ReportUploader2({
1786
- flakinessAccessToken: options.accessToken,
1787
- flakinessEndpoint: options.endpoint
1788
- });
1789
1421
  for (const relativePath of relativePaths) {
1790
- const fullPath = path7.resolve(relativePath);
1791
- if (!await fs7.access(fullPath, fs7.constants.F_OK).then(() => true).catch(() => false)) {
1422
+ const fullPath = path6.resolve(relativePath);
1423
+ if (!await fs6.access(fullPath, fs6.constants.F_OK).then(() => true).catch(() => false)) {
1792
1424
  err(`Path ${fullPath} is not accessible!`);
1793
1425
  process.exit(1);
1794
1426
  }
1795
- const text = await fs7.readFile(fullPath, "utf-8");
1427
+ const text = await fs6.readFile(fullPath, "utf-8");
1796
1428
  const report = JSON.parse(text);
1797
- const attachmentsDir = options.attachmentsDir ?? path7.dirname(fullPath);
1429
+ const attachmentsDir = options.attachmentsDir ?? path6.dirname(fullPath);
1798
1430
  const { attachmentIdToPath, missingAttachments } = await resolveAttachmentPaths(report, attachmentsDir);
1799
1431
  if (missingAttachments.length) {
1800
1432
  warn(`Missing ${missingAttachments.length} attachments`);
1801
1433
  }
1802
- const upload = uploader.createUpload(report, Array.from(attachmentIdToPath.values()));
1803
- const uploadResult = await upload.upload();
1804
- if (!uploadResult.success) {
1805
- err(`Failed to upload to ${options.endpoint}: ${uploadResult.message}`);
1806
- } else {
1807
- log(`\u2713 Uploaded ${uploadResult.reportUrl ?? uploadResult.message ?? ""}`);
1808
- }
1434
+ await uploadReport(report, Array.from(attachmentIdToPath.values()), {
1435
+ flakinessAccessToken: options.accessToken,
1436
+ flakinessEndpoint: options.endpoint
1437
+ });
1809
1438
  }
1810
1439
  }
1811
1440
 
@@ -1853,15 +1482,12 @@ async function ensureAccessToken(options) {
1853
1482
  }
1854
1483
  }
1855
1484
  }
1856
- assert3(accessToken, `Please either pass FLAKINESS_ACCESS_TOKEN or run login + link`);
1485
+ assert2(accessToken, `Please either pass FLAKINESS_ACCESS_TOKEN or run login + link`);
1857
1486
  return {
1858
1487
  ...options,
1859
1488
  accessToken
1860
1489
  };
1861
1490
  }
1862
- program.command("upload-playwright-json", { hidden: true }).description("Upload Playwright Test JSON report to the flakiness.io service").argument("<relative-path-to-json>", "Path to the Playwright JSON report file").addOption(optAccessToken).addOption(optEndpoint).action(async (relativePath, options) => runCommand(async () => {
1863
- await cmdUploadPlaywrightJson(relativePath, await ensureAccessToken(options));
1864
- }));
1865
1491
  program.command("login").description("Login to the Flakiness.io service").addOption(optEndpoint).action(async (options) => runCommand(async () => {
1866
1492
  await cmdLogin(options.endpoint);
1867
1493
  }));
@@ -1938,7 +1564,7 @@ program.command("download").description("Download run").addOption(optSince).addO
1938
1564
  });
1939
1565
  console.log(`Found ${Ranges.cardinality(runIds)} reports uploaded since ${options.since}`);
1940
1566
  }
1941
- const alreadyExisting = fs8.readdirSync(process.cwd());
1567
+ const alreadyExisting = fs7.readdirSync(process.cwd());
1942
1568
  const downloadedRuns = alreadyExisting.filter((entry) => entry.startsWith("fkrun-")).map((run) => parseInt(run.substring(`fkrun-`.length), 10)).filter((runId) => !isNaN(runId));
1943
1569
  console.log(`Found ${downloadedRuns.length} locally downloaded reports`);
1944
1570
  const toBeDownloaded = Ranges.subtract(runIds, Ranges.fromList(downloadedRuns));
@@ -1967,7 +1593,7 @@ program.command("upload").description("Upload Flakiness report to the flakiness.
1967
1593
  });
1968
1594
  });
1969
1595
  program.command("show").description("Show flakiness report").argument("[relative-path]", "Path to the Flakiness report file or folder that contains `report.json`. (default: flakiness-report)").action(async (arg) => runCommand(async () => {
1970
- const dir = path8.resolve(arg ?? "flakiness-report");
1596
+ const dir = path7.resolve(arg ?? "flakiness-report");
1971
1597
  await showReport(dir);
1972
1598
  }));
1973
1599
  program.command("convert-junit").description("Convert JUnit XML report(s) to Flakiness report format").argument("<junit-root-dir-path>", "Path to JUnit XML file or directory containing XML files").option("--env-name <name>", "Environment name for the report", "junit").option("--commit-id <id>", "Git commit ID (auto-detected if not provided)").option("--output-dir <dir>", "Output directory for the report", "flakiness-report").action(async (junitPath, options) => {