flakiness 0.213.0 → 0.214.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 +134 -22
- package/package.json +3 -3
- package/types/tsconfig.tsbuildinfo +1 -1
package/lib/cli/cli.js
CHANGED
|
@@ -1023,12 +1023,12 @@ var WireTypes;
|
|
|
1023
1023
|
// src/cli/cli.ts
|
|
1024
1024
|
import { Command, Option } from "commander";
|
|
1025
1025
|
import debug2 from "debug";
|
|
1026
|
-
import
|
|
1026
|
+
import path7 from "path";
|
|
1027
1027
|
|
|
1028
1028
|
// ../package.json
|
|
1029
1029
|
var package_default = {
|
|
1030
1030
|
name: "flakiness",
|
|
1031
|
-
version: "0.
|
|
1031
|
+
version: "0.214.0",
|
|
1032
1032
|
type: "module",
|
|
1033
1033
|
private: true,
|
|
1034
1034
|
scripts: {
|
|
@@ -1233,8 +1233,8 @@ var TypedHTTP;
|
|
|
1233
1233
|
}
|
|
1234
1234
|
TypedHTTP2.Router = Router;
|
|
1235
1235
|
function createClient(base, fetchCallback) {
|
|
1236
|
-
function buildUrl(method,
|
|
1237
|
-
const url = new URL(
|
|
1236
|
+
function buildUrl(method, path8, input, options) {
|
|
1237
|
+
const url = new URL(path8.join("/"), base);
|
|
1238
1238
|
const signal = options?.signal;
|
|
1239
1239
|
let body = void 0;
|
|
1240
1240
|
if (method === "GET" && input)
|
|
@@ -1249,8 +1249,8 @@ var TypedHTTP;
|
|
|
1249
1249
|
signal
|
|
1250
1250
|
};
|
|
1251
1251
|
}
|
|
1252
|
-
function fetcher(method,
|
|
1253
|
-
const options = buildUrl(method,
|
|
1252
|
+
function fetcher(method, path8, input, methodOptions) {
|
|
1253
|
+
const options = buildUrl(method, path8, input, methodOptions);
|
|
1254
1254
|
return fetchCallback(options.url, {
|
|
1255
1255
|
method: options.method,
|
|
1256
1256
|
body: options.body,
|
|
@@ -1273,17 +1273,17 @@ var TypedHTTP;
|
|
|
1273
1273
|
}
|
|
1274
1274
|
});
|
|
1275
1275
|
}
|
|
1276
|
-
function createProxy(
|
|
1276
|
+
function createProxy(path8 = []) {
|
|
1277
1277
|
return new Proxy({}, {
|
|
1278
1278
|
get(target, prop) {
|
|
1279
1279
|
if (typeof prop === "symbol")
|
|
1280
1280
|
return void 0;
|
|
1281
1281
|
if (allHttpMethods.includes(prop)) {
|
|
1282
|
-
const f = fetcher.bind(null, prop,
|
|
1283
|
-
f.prepare = buildUrl.bind(null, prop,
|
|
1282
|
+
const f = fetcher.bind(null, prop, path8);
|
|
1283
|
+
f.prepare = buildUrl.bind(null, prop, path8);
|
|
1284
1284
|
return f;
|
|
1285
1285
|
}
|
|
1286
|
-
const newPath = [...
|
|
1286
|
+
const newPath = [...path8, prop];
|
|
1287
1287
|
return createProxy(newPath);
|
|
1288
1288
|
}
|
|
1289
1289
|
});
|
|
@@ -1919,6 +1919,18 @@ function humanReadableMs(ms) {
|
|
|
1919
1919
|
days %= 7;
|
|
1920
1920
|
return `${weeks} weeks ${days} days ${hours} h`;
|
|
1921
1921
|
}
|
|
1922
|
+
function durationTrendScale(currentMs, baseMs) {
|
|
1923
|
+
const trend = baseMs > 0 ? currentMs / baseMs : 1;
|
|
1924
|
+
if (trend > 1e3)
|
|
1925
|
+
return ">x1000";
|
|
1926
|
+
if (trend > 2)
|
|
1927
|
+
return `x${trend.toFixed(2)}`;
|
|
1928
|
+
if (trend > 1.01)
|
|
1929
|
+
return `+${(trend - 1) * 100 | 0}%`;
|
|
1930
|
+
if (trend > 0.99)
|
|
1931
|
+
return "\xB10%";
|
|
1932
|
+
return `-${(1 - trend) * 100 | 0}%`;
|
|
1933
|
+
}
|
|
1922
1934
|
|
|
1923
1935
|
// src/cli/cmd-list-tests.ts
|
|
1924
1936
|
async function cmdListTests(options) {
|
|
@@ -1930,9 +1942,15 @@ async function cmdListTests(options) {
|
|
|
1930
1942
|
accessToken: options.accessToken,
|
|
1931
1943
|
flakinessProject: options.flakinessProject
|
|
1932
1944
|
});
|
|
1945
|
+
let preset;
|
|
1946
|
+
if (options.pr !== void 0)
|
|
1947
|
+
preset = { type: "pr", number: options.pr };
|
|
1948
|
+
else if (options.branch !== void 0)
|
|
1949
|
+
preset = { type: "branch", name: options.branch };
|
|
1933
1950
|
const result = await api.report.testStats.POST({
|
|
1934
1951
|
orgSlug,
|
|
1935
1952
|
projectSlug,
|
|
1953
|
+
preset,
|
|
1936
1954
|
pageOptions: {
|
|
1937
1955
|
// CLI pages are 1-based while backend pages are 0-based.
|
|
1938
1956
|
number: options.page - 1,
|
|
@@ -1945,8 +1963,13 @@ async function cmdListTests(options) {
|
|
|
1945
1963
|
historyBuckets: 10,
|
|
1946
1964
|
fql: options.fql
|
|
1947
1965
|
});
|
|
1966
|
+
let scope = "default branch";
|
|
1967
|
+
if (options.pr !== void 0)
|
|
1968
|
+
scope = `PR #${options.pr}`;
|
|
1969
|
+
else if (options.branch !== void 0)
|
|
1970
|
+
scope = `branch \`${options.branch}\``;
|
|
1948
1971
|
const lines = [
|
|
1949
|
-
`# Tests for \`${orgSlug}/${projectSlug}
|
|
1972
|
+
`# Tests for \`${orgSlug}/${projectSlug}\` (${scope})`,
|
|
1950
1973
|
"",
|
|
1951
1974
|
`Total tests: **${result.totalElements}**`,
|
|
1952
1975
|
`Showing page: **${result.pageNumber + 1}/${Math.max(result.totalPages, 1)}** (page size ${result.pageSize})`,
|
|
@@ -1963,11 +1986,14 @@ async function cmdListTests(options) {
|
|
|
1963
1986
|
const error = extractError(testStats);
|
|
1964
1987
|
const status = outcomeToStatus(testStats.outcome);
|
|
1965
1988
|
const duration = humanReadableMs(testStats.durationMs);
|
|
1989
|
+
const trend = formatDurationTrend(testStats.durationChangeMs, testStats.durationMs);
|
|
1966
1990
|
const flipRate = formatFlipRate(testStats.flipRate);
|
|
1991
|
+
const env = formatEnv(testStats.env);
|
|
1967
1992
|
lines.push(`### ${index + 1}. ${fullName}`);
|
|
1968
1993
|
lines.push(`- Location: ${asInlineCode(location)}`);
|
|
1994
|
+
lines.push(`- Env: ${asInlineCode(env)}`);
|
|
1969
1995
|
lines.push(`- Status: ${asInlineCode(status)}`);
|
|
1970
|
-
lines.push(`- Duration: ${asInlineCode(duration)}`);
|
|
1996
|
+
lines.push(`- Duration: ${asInlineCode(duration)} (${asInlineCode(trend)})`);
|
|
1971
1997
|
lines.push(`- Flip Rate: ${asInlineCode(flipRate)}`);
|
|
1972
1998
|
lines.push(`- Error: ${error ? asInlineCode(error) : "None"}`);
|
|
1973
1999
|
lines.push("");
|
|
@@ -1994,6 +2020,27 @@ function outcomeToStatus(outcome) {
|
|
|
1994
2020
|
return "flaked";
|
|
1995
2021
|
}
|
|
1996
2022
|
}
|
|
2023
|
+
function formatEnv(env) {
|
|
2024
|
+
const parts = [];
|
|
2025
|
+
const { systemData } = env;
|
|
2026
|
+
if (systemData.osName)
|
|
2027
|
+
parts.push(`os=${systemData.osName}`);
|
|
2028
|
+
if (systemData.osVersion)
|
|
2029
|
+
parts.push(`osVersion=${systemData.osVersion}`);
|
|
2030
|
+
if (systemData.osArch)
|
|
2031
|
+
parts.push(`arch=${systemData.osArch}`);
|
|
2032
|
+
if (env.userSuppliedData) {
|
|
2033
|
+
for (const [key, value] of Object.entries(env.userSuppliedData))
|
|
2034
|
+
parts.push(`${key}=${value}`);
|
|
2035
|
+
}
|
|
2036
|
+
return parts.join(", ") || env.name || "unknown";
|
|
2037
|
+
}
|
|
2038
|
+
function formatDurationTrend(changeMs, currentMs) {
|
|
2039
|
+
const baseMs = currentMs - changeMs;
|
|
2040
|
+
const scale = durationTrendScale(currentMs, baseMs);
|
|
2041
|
+
const sign = changeMs >= 0 ? "+" : "-";
|
|
2042
|
+
return `${sign}${humanReadableMs(Math.abs(changeMs))} (${scale})`;
|
|
2043
|
+
}
|
|
1997
2044
|
function formatFlipRate(flipRate) {
|
|
1998
2045
|
if (flipRate === void 0 || !Number.isFinite(flipRate))
|
|
1999
2046
|
return "n/a";
|
|
@@ -2005,14 +2052,65 @@ function asInlineCode(text) {
|
|
|
2005
2052
|
return `${ticks}${normalized}${ticks}`;
|
|
2006
2053
|
}
|
|
2007
2054
|
|
|
2055
|
+
// src/cli/cmd-skills-install.ts
|
|
2056
|
+
import { execSync } from "child_process";
|
|
2057
|
+
import chalk3 from "chalk";
|
|
2058
|
+
import fs5 from "fs";
|
|
2059
|
+
import path5 from "path";
|
|
2060
|
+
|
|
2061
|
+
// src/generated/bundledSkillsData.ts
|
|
2062
|
+
var BUNDLED_SKILLS = [
|
|
2063
|
+
{
|
|
2064
|
+
"name": "flakiness-investigation",
|
|
2065
|
+
"description": "Use when querying Flakiness.io test data, writing FQL filters, finding flaky or failed tests, investigating regressions, analyzing test health, or fixing PR test failures with the flakiness CLI.",
|
|
2066
|
+
"files": [
|
|
2067
|
+
{
|
|
2068
|
+
"path": "SKILL.md",
|
|
2069
|
+
"content": "---\nname: flakiness-investigation\ndescription: Use when querying Flakiness.io test data, writing FQL filters, finding flaky or failed tests, investigating regressions, analyzing test health, or fixing PR test failures with the flakiness CLI.\n---\n\n# Flakiness Investigation\n\nUse the `flakiness` CLI to query and analyze test data from Flakiness.io.\n\n## Prerequisites\n\n- The `flakiness` CLI must be available in PATH (installed via `npm install -g flakiness` or `npx flakiness`).\n- A project must be specified: `--project <org/project>` or set `FLAKINESS_PROJECT` env var.\n- Authentication: run `flakiness auth login` first, or set `FLAKINESS_ACCESS_TOKEN`.\n\n## Core command\n\n```bash\nflakiness list tests --project <org/project> [--pr <number>] [--branch <name>] [--fql <query>] [--sort <axis>] [--sort-dir asc|desc] [--page <n>] [--page-size <n>]\n```\n\n### Scope options\n\n| Flag | Effect |\n|------|--------|\n| *(none)* | Default branch \u2014 tests from the day of its head commit (in the project's timezone) |\n| `--pr <number>` | Pull request \u2014 tests from the merge-commit of the PR branch into the target branch |\n| `--branch <name>` | Named branch \u2014 tests from the day of its head commit (in the project's timezone) |\n\n`--pr` and `--branch` are mutually exclusive.\n\n### Status semantics with `--pr`\n\nWhen querying a PR, the status values have specific meaning:\n\n| Status | Meaning |\n|--------|---------|\n| `regressed` | Test was passing on the target branch but fails in this PR \u2014 **caused by the PR** |\n| `failed` | Test also fails on the target branch \u2014 **pre-existing failure, not caused by the PR** |\n| `flaked` | Test failed but passed on retry \u2014 flaky, not a real failure |\n| `passed` | Test passes |\n| `skipped` | Test was skipped |\n\n### Sort axes\n\n- `outcome` \u2014 sort by test outcome severity (default)\n- `flip_rate` \u2014 sort by flip rate (how often a test flips between pass/fail)\n- `duration` \u2014 sort by test duration\n- `duration_trend` \u2014 sort by duration trend\n- `name` \u2014 sort alphabetically\n\n### Common queries\n\n| Goal | FQL + flags |\n|------|-------------|\n| Flaky tests | `--fql 'flip>0%' --sort flip_rate --sort-dir desc` |\n| Very flaky (>50%) | `--fql 'flip>50%' --sort flip_rate --sort-dir desc` |\n| Failed tests | `--fql 's:failed' --sort outcome --sort-dir desc` |\n| Regressions | `--fql 's:regressed'` |\n| All broken tests | `--fql 'status:(failed, regressed)'` |\n| Slow tests | `--fql 'd>5s' --sort duration --sort-dir desc` |\n| Tests matching error | `--fql '$timeout'` |\n| Tests in file | `--fql 'f:login.spec.ts'` |\n\n## FQL (Filter Query Language)\n\nFull reference: [references/fql.md](references/fql.md)\n\nKey rules:\n- Multiple tokens combine with AND: `s:failed f:e2e` means \"failed AND in e2e files\"\n- Prefix with `-` to exclude: `-#smoke` excludes smoke-tagged tests\n- Same filter type uses OR: `status:(failed, regressed)` means \"failed OR regressed\"\n- Quote values with spaces: `f:'tests/e2e checkout'`\n\n### Filter types\n\n| Filter | Syntax | Example |\n|--------|--------|---------|\n| Text search | `<text>` | `login` |\n| Status | `s:<status>` | `s:failed`, `status:(failed, regressed)` |\n| File | `f:<path>` | `f:login.spec.ts` |\n| Error | `$<text>` | `$timeout` |\n| Tag | `#<tag>` | `#smoke` |\n| Duration | `d><time>` | `d>2s`, `d<=500ms` |\n| Flip rate | `flip><pct>` | `flip>0%`, `fr>50%` |\n| Annotation | `@<type>` | `@skip` |\n\n### Status values\n\n`passed`, `failed`, `flaked`, `skipped`, `regressed`\n\n## Workflow: Fix My PR Tests\n\nWhen a user asks to fix failing tests in a PR, follow these steps:\n\n1. **Fetch regressions from the PR:**\n ```bash\n flakiness list tests --project <org/project> --pr <number> --fql 's:regressed' --page-size 50\n ```\n Tests with status `regressed` were passing on the target branch but fail in this PR \u2014 these are **caused by the PR** and must be fixed.\n\n2. **Analyze the output:** Look at the error messages, file paths, and test names to understand what the PR broke.\n\n3. **Fix the regressions** by reading the reported file paths and error messages, then making targeted code changes.\n\n4. **Optionally check pre-existing failures:** Tests with status `failed` also fail on the target branch and are not caused by the PR. You can list them with:\n ```bash\n flakiness list tests --project <org/project> --pr <number> --fql 's:failed' --page-size 50\n ```\n These are informational \u2014 fixing them is a bonus, not a requirement.\n\n5. **Ignore flakes:** Tests with status `flaked` failed but passed on retry \u2014 they are flaky and not actionable in the context of a PR fix.\n\n## Workflow tips\n\n1. Start broad: `flakiness list tests --project <org/project> --page-size 20`\n2. Filter down with FQL based on what you're investigating\n3. Use `--page-size 50` or higher to see more results at once\n4. Combine filters: `--fql 's:failed $timeout f:e2e -#smoke'`\n\nMore recipes: [references/recipes.md](references/recipes.md)\n"
|
|
2070
|
+
},
|
|
2071
|
+
{
|
|
2072
|
+
"path": "references/fql.md",
|
|
2073
|
+
"content": "# FQL Reference\n\nUse `--fql <query>` with `flakiness list tests` to filter tests before sorting and pagination.\n\n## Core rules\n\n- Multiple tokens are combined with `AND` by default.\n- Prefix a token with `-` to exclude matches.\n- Use quotes for spaces or special characters.\n- Repeating the same filter type behaves like `OR`, for example `status:(failed, regressed)`.\n\n## Filter types\n\n| Filter | Examples | Meaning |\n| --- | --- | --- |\n| Test text | `login` | Matches test titles, suite titles, and file path |\n| Status | `s:failed`, `status:(failed, regressed)` | Matches test status |\n| File | `f:login.spec.ts`, `file:e2e` | Matches file path |\n| Line | `:120`, `>200`, `<=50` | Matches line number |\n| Error | `$timeout`, `$'network error'` | Matches error text |\n| Annotation | `@skip`, `@'fails in ci'` | Matches annotation type or description |\n| Tag | `#smoke` | Matches test tags |\n| Duration | `d>2s`, `duration<=500ms` | Matches duration |\n| Flip rate | `flip>0%`, `fr<=50%` | Matches flip rate |\n\n## Status values\n\n- `passed`\n- `failed`\n- `flaked`\n- `skipped`\n- `regressed`\n\n## Examples\n\n```fql\ns:flaked\nstatus:(failed, regressed)\nf:e2e d>5s\nflip>0%\nfr>50%\n$timeout -#smoke\n@skip f:'tests/e2e checkout'\n```\n\n## Quoting\n\n- Single and double quotes both work.\n- Escape quotes and backslashes with JSON-style escaping.\n\n```fql\nf:'some path'\n$\"say \\\"hello\\\"\"\n'can\\'t touch this'\n```\n\n## Flip rate\n\nValues are percentages (0\u2013100). The `%` suffix is optional.\n\n```fql\nflip>0%\nflip>=50\nfr<=25%\n```\n\n## Duration units\n\n- `ns`\n- `ms` or no suffix\n- `s`\n- `m`\n- `h`\n\n```fql\nd>100\nd>1.5s\nduration<=2m\n```\n"
|
|
2074
|
+
},
|
|
2075
|
+
{
|
|
2076
|
+
"path": "references/recipes.md",
|
|
2077
|
+
"content": "# Investigation Recipes\n\nReplace `myorg/myproject` with the target project slug, or set `FLAKINESS_PROJECT`.\n\n## Query a pull request\n\nShows tests from the merge-commit of the PR branch into the target branch.\n\n```bash\nflakiness list tests --project myorg/myproject --pr 42\n```\n\n## Find PR regressions (caused by the PR)\n\nTests marked `regressed` were passing on the target branch but fail in this PR.\n\n```bash\nflakiness list tests --project myorg/myproject --pr 42 --fql 's:regressed'\n```\n\n## Find pre-existing failures in a PR\n\nTests marked `failed` also fail on the target branch \u2014 not caused by the PR.\n\n```bash\nflakiness list tests --project myorg/myproject --pr 42 --fql 's:failed'\n```\n\n## Find all broken tests in a PR\n\nIncludes both PR-caused regressions and pre-existing failures.\n\n```bash\nflakiness list tests --project myorg/myproject --pr 42 --fql 'status:(failed, regressed)' --page-size 50\n```\n\n## Query a specific branch\n\nShows tests from the day of the branch's head commit, in the project's timezone.\n\n```bash\nflakiness list tests --project myorg/myproject --branch feature/login\n```\n\n## Find all flaky tests\n\n```bash\nflakiness list tests --project myorg/myproject --fql 'flip>0%' --sort flip_rate --sort-dir desc\n```\n\n## Find the most flaky tests (>50% flip rate)\n\n```bash\nflakiness list tests --project myorg/myproject --fql 'flip>50%' --sort flip_rate --sort-dir desc\n```\n\n## Show failed tests\n\n```bash\nflakiness list tests --project myorg/myproject --fql 's:failed' --sort outcome --sort-dir desc\n```\n\n## Show regressions\n\n```bash\nflakiness list tests --project myorg/myproject --fql 's:regressed'\n```\n\n## Show all currently broken tests\n\n```bash\nflakiness list tests --project myorg/myproject --fql 'status:(failed, regressed)' --sort outcome --sort-dir desc\n```\n\n## Show flaked tests (passed on retry)\n\n```bash\nflakiness list tests --project myorg/myproject --fql 's:flaked' --sort flip_rate --sort-dir desc\n```\n\n## Find slow tests\n\n```bash\nflakiness list tests --project myorg/myproject --fql 'd>5s' --sort duration --sort-dir desc\n```\n\n## Find tests by error text\n\n```bash\nflakiness list tests --project myorg/myproject --fql '$timeout'\nflakiness list tests --project myorg/myproject --fql '$\"network error\"'\n```\n\n## Combine filters\n\nFailed tests in e2e files, excluding smoke-tagged tests:\n\n```bash\nflakiness list tests --project myorg/myproject --fql 's:failed f:e2e -#smoke'\n```\n\n## Narrow to a specific file\n\n```bash\nflakiness list tests --project myorg/myproject --fql 'f:login.spec.ts'\n```\n\n## Find tests with a specific annotation\n\n```bash\nflakiness list tests --project myorg/myproject --fql '@skip'\nflakiness list tests --project myorg/myproject --fql '@fixme'\n```\n"
|
|
2078
|
+
}
|
|
2079
|
+
]
|
|
2080
|
+
}
|
|
2081
|
+
];
|
|
2082
|
+
|
|
2083
|
+
// src/cli/cmd-skills-install.ts
|
|
2084
|
+
var AGENTS = ["claude", "codex", "cursor"];
|
|
2085
|
+
function projectRoot() {
|
|
2086
|
+
try {
|
|
2087
|
+
return execSync("git rev-parse --show-toplevel", { encoding: "utf-8" }).trim();
|
|
2088
|
+
} catch {
|
|
2089
|
+
return process.cwd();
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
async function cmdSkillsInstall(options) {
|
|
2093
|
+
for (const skill of BUNDLED_SKILLS) {
|
|
2094
|
+
const dest = path5.join(projectRoot(), `.${options.agent}`, "skills", skill.name);
|
|
2095
|
+
await fs5.promises.rm(dest, { recursive: true, force: true });
|
|
2096
|
+
for (const file of skill.files) {
|
|
2097
|
+
const filePath = path5.join(dest, file.path);
|
|
2098
|
+
await fs5.promises.mkdir(path5.dirname(filePath), { recursive: true });
|
|
2099
|
+
await fs5.promises.writeFile(filePath, file.content);
|
|
2100
|
+
}
|
|
2101
|
+
console.log(`${chalk3.green("\u2713")} Installed ${chalk3.bold(skill.name)} \u2192 ${chalk3.dim(dest)}`);
|
|
2102
|
+
}
|
|
2103
|
+
console.log("\nRestart your agent to pick up new skills.");
|
|
2104
|
+
}
|
|
2105
|
+
|
|
2008
2106
|
// src/cli/cmd-upload.ts
|
|
2009
2107
|
import { readReport, uploadReport } from "@flakiness/sdk";
|
|
2010
|
-
import
|
|
2011
|
-
import
|
|
2108
|
+
import chalk4 from "chalk";
|
|
2109
|
+
import fs6 from "fs/promises";
|
|
2012
2110
|
import ora2 from "ora";
|
|
2013
|
-
import
|
|
2014
|
-
var warn = (txt) => console.warn(
|
|
2015
|
-
var err = (txt) => console.error(
|
|
2111
|
+
import path6 from "path";
|
|
2112
|
+
var warn = (txt) => console.warn(chalk4.yellow(`[flakiness.io] WARN: ${txt}`));
|
|
2113
|
+
var err = (txt) => console.error(chalk4.red(`[flakiness.io] Error: ${txt}`));
|
|
2016
2114
|
async function cmdUpload(relativePaths, options) {
|
|
2017
2115
|
const total = relativePaths.length;
|
|
2018
2116
|
const spinner = total > 1 ? ora2("Uploading reports:").start() : void 0;
|
|
@@ -2029,14 +2127,14 @@ async function cmdUpload(relativePaths, options) {
|
|
|
2029
2127
|
return sharedAuth;
|
|
2030
2128
|
}
|
|
2031
2129
|
for (const relativePath of relativePaths) {
|
|
2032
|
-
const fullPath =
|
|
2033
|
-
const stat = await
|
|
2130
|
+
const fullPath = path6.resolve(relativePath);
|
|
2131
|
+
const stat = await fs6.stat(fullPath).catch(() => void 0);
|
|
2034
2132
|
if (!stat) {
|
|
2035
2133
|
spinner?.stop();
|
|
2036
2134
|
err(`Path ${fullPath} is not accessible!`);
|
|
2037
2135
|
process.exit(1);
|
|
2038
2136
|
}
|
|
2039
|
-
const reportDir = stat.isDirectory() ? fullPath :
|
|
2137
|
+
const reportDir = stat.isDirectory() ? fullPath : path6.dirname(fullPath);
|
|
2040
2138
|
const { report, attachments, missingAttachments } = await readReport(reportDir);
|
|
2041
2139
|
if (missingAttachments.length)
|
|
2042
2140
|
warn(`Missing ${missingAttachments.length} attachments`);
|
|
@@ -2099,13 +2197,22 @@ var optTestsPageSize = new Option("--page-size <page-size>", "Number of tests pe
|
|
|
2099
2197
|
return parsed;
|
|
2100
2198
|
}).default(20);
|
|
2101
2199
|
var optTestsFQL = new Option("--fql <query>", "Filter Query Language (FQL) expression for tests. Learn about FQL: https://flakiness.io/docs/concepts/fql/");
|
|
2102
|
-
|
|
2200
|
+
var optTestsPR = new Option("--pr <number>", "Show tests from a specific pull request").argParser((value) => {
|
|
2201
|
+
const parsed = parseInt(value, 10);
|
|
2202
|
+
if (isNaN(parsed) || parsed < 1)
|
|
2203
|
+
throw new Error("PR number must be a positive integer");
|
|
2204
|
+
return parsed;
|
|
2205
|
+
}).conflicts("branch");
|
|
2206
|
+
var optTestsBranch = new Option("--branch <name>", "Show tests from a specific branch").conflicts("pr");
|
|
2207
|
+
list.command("tests").description("Query tests data. Defaults to the default branch (day of head commit in project timezone). Use --pr for PR merge-commit tests, or --branch for a named branch.").addOption(optTestsPage).addOption(optTestsPageSize).addOption(optTestsSort).addOption(optTestsSortDir).addOption(optTestsFQL).addOption(optTestsPR).addOption(optTestsBranch).addOption(mustFlakinessProject).addOption(optEndpoint).addOption(optAccessToken).action(async (options) => runCommand(async () => {
|
|
2103
2208
|
await cmdListTests({
|
|
2104
2209
|
page: options.page,
|
|
2105
2210
|
pageSize: options.pageSize,
|
|
2106
2211
|
sort: options.sort,
|
|
2107
2212
|
sortDir: options.sortDir,
|
|
2108
2213
|
fql: options.fql,
|
|
2214
|
+
pr: options.pr,
|
|
2215
|
+
branch: options.branch,
|
|
2109
2216
|
endpoint: options.endpoint,
|
|
2110
2217
|
accessToken: options.accessToken,
|
|
2111
2218
|
flakinessProject: options.project
|
|
@@ -2121,6 +2228,11 @@ auth.command("logout").description("Logout from current session").action(async (
|
|
|
2121
2228
|
auth.command("whoami").description("Show current logged in user information").action(async () => runCommand(async () => {
|
|
2122
2229
|
await cmdAuthWhoami();
|
|
2123
2230
|
}));
|
|
2231
|
+
var skills = program.command("skills").description("Manage agent skills");
|
|
2232
|
+
var optAgent = new Option("--agent <agent>", "Target agent").choices(AGENTS).makeOptionMandatory();
|
|
2233
|
+
skills.command("install").description("Install bundled skills into the project").addOption(optAgent).action(async (options) => runCommand(async () => {
|
|
2234
|
+
await cmdSkillsInstall({ agent: options.agent });
|
|
2235
|
+
}));
|
|
2124
2236
|
program.command("access").description("Check access to a Flakiness.io project").addOption(mustFlakinessProject).addOption(optAccessToken).addOption(optEndpoint).option("--json", "Output result as JSON").option("-q, --quiet", "Suppress output, only set exit code").action(async (options) => runCommand(async () => {
|
|
2125
2237
|
await cmdAccess({
|
|
2126
2238
|
flakinessProject: options.project,
|
|
@@ -2172,7 +2284,7 @@ program.command("upload").description("Upload Flakiness report to the flakiness.
|
|
|
2172
2284
|
});
|
|
2173
2285
|
var optViewerUrl = new Option("-e, --viewer-url <url>", "A URL where report viewer is deployed").default("https://report.flakiness.io").env("FLAKINESS_ENDPOINT");
|
|
2174
2286
|
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)").addOption(optViewerUrl).action(async (arg, options) => runCommand(async () => {
|
|
2175
|
-
const dir =
|
|
2287
|
+
const dir = path7.resolve(arg ?? "flakiness-report");
|
|
2176
2288
|
await showReport(dir, {
|
|
2177
2289
|
reportViewerUrl: options.viewerUrl
|
|
2178
2290
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "flakiness",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.214.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"bin": {
|
|
6
6
|
"flakiness": "./lib/cli/cli.js"
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
"@playwright/test": "^1.57.0",
|
|
23
23
|
"@types/debug": "^4.1.12",
|
|
24
24
|
"@types/express": "^4.17.20",
|
|
25
|
-
"@flakiness/shared": "0.
|
|
26
|
-
"@flakiness/server": "0.
|
|
25
|
+
"@flakiness/shared": "0.214.0",
|
|
26
|
+
"@flakiness/server": "0.214.0"
|
|
27
27
|
},
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@flakiness/flakiness-report": "^0.28.0",
|