branch-never 0.2.0 → 0.3.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 +23 -0
- package/dist/baseline.d.ts +13 -0
- package/dist/baseline.js +78 -0
- package/dist/baseline.js.map +1 -1
- package/dist/blame.d.ts +16 -0
- package/dist/blame.js +56 -0
- package/dist/blame.js.map +1 -0
- package/dist/formatter.d.ts +2 -0
- package/dist/formatter.js +24 -0
- package/dist/formatter.js.map +1 -1
- package/dist/index.js +44 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -33,6 +33,8 @@ Options:
|
|
|
33
33
|
--threshold <pct> Fail if coverage below N% (default: 0, fail on any)
|
|
34
34
|
--report <file> Write an HTML report
|
|
35
35
|
--baseline <ref> Compare current uncovered branches against a git ref
|
|
36
|
+
--blame Show git blame metadata for each unreachable branch
|
|
37
|
+
--pr-check Compare current unreachable branches against a baseline JSON file
|
|
36
38
|
```
|
|
37
39
|
|
|
38
40
|
Example output:
|
|
@@ -61,6 +63,27 @@ Baseline comparison:
|
|
|
61
63
|
branch-never src --tests test --baseline main
|
|
62
64
|
```
|
|
63
65
|
|
|
66
|
+
Blame mode:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
branch-never src --tests test --blame
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
PR check mode:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
branch-never src --tests test --json --no-fail > .branch-never-baseline.json
|
|
76
|
+
branch-never src --tests test --pr-check --baseline .branch-never-baseline.json
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
GitHub Actions:
|
|
80
|
+
|
|
81
|
+
```yaml
|
|
82
|
+
- run: pnpm build
|
|
83
|
+
- run: pnpm test
|
|
84
|
+
- run: node dist/index.js src --tests test --pr-check --baseline .branch-never-baseline.json
|
|
85
|
+
```
|
|
86
|
+
|
|
64
87
|
## What It Detects
|
|
65
88
|
|
|
66
89
|
The extractor looks for these branch shapes using regex:
|
package/dist/baseline.d.ts
CHANGED
|
@@ -15,9 +15,22 @@ export interface BaselineComparison {
|
|
|
15
15
|
newlyCovered: BranchCoverage[];
|
|
16
16
|
netChange: number;
|
|
17
17
|
}
|
|
18
|
+
export interface PrCheckComparison {
|
|
19
|
+
baselinePath: string;
|
|
20
|
+
baseline: FormatPayload;
|
|
21
|
+
current: FormatPayload;
|
|
22
|
+
newUnreachable: BranchCoverage[];
|
|
23
|
+
resolvedUnreachable: BranchCoverage[];
|
|
24
|
+
}
|
|
18
25
|
export declare function analyzeBranchCoverage(options: AnalysisOptions): Promise<FormatPayload>;
|
|
19
26
|
export declare function compareUncoveredBranches(baselineRef: string, baseline: FormatPayload, current: FormatPayload): BaselineComparison;
|
|
20
27
|
export declare function analyzeAgainstBaseline(options: AnalysisOptions & {
|
|
21
28
|
baselineRef: string;
|
|
22
29
|
}): Promise<BaselineComparison>;
|
|
30
|
+
export declare function analyzePrCheck(options: AnalysisOptions & {
|
|
31
|
+
baselinePath: string;
|
|
32
|
+
}): Promise<PrCheckComparison>;
|
|
23
33
|
export declare function formatBaselineComparison(comparison: BaselineComparison): string;
|
|
34
|
+
export declare function comparePrBaseline(baselinePath: string, baseline: FormatPayload, current: FormatPayload): PrCheckComparison;
|
|
35
|
+
export declare function loadBaselinePayload(baselinePath: string): Promise<FormatPayload>;
|
|
36
|
+
export declare function formatPrCheckComparison(comparison: PrCheckComparison): string;
|
package/dist/baseline.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
1
2
|
import { promisify } from "node:util";
|
|
2
3
|
import { execFile as execFileCallback } from "node:child_process";
|
|
3
4
|
import { extractBranchesFromDirectory } from "./extractor.js";
|
|
@@ -55,6 +56,11 @@ export async function analyzeAgainstBaseline(options) {
|
|
|
55
56
|
}
|
|
56
57
|
return compareUncoveredBranches(options.baselineRef, baseline, current);
|
|
57
58
|
}
|
|
59
|
+
export async function analyzePrCheck(options) {
|
|
60
|
+
const current = await analyzeBranchCoverage(options);
|
|
61
|
+
const baseline = await loadBaselinePayload(options.baselinePath);
|
|
62
|
+
return comparePrBaseline(options.baselinePath, baseline, current);
|
|
63
|
+
}
|
|
58
64
|
export function formatBaselineComparison(comparison) {
|
|
59
65
|
const lines = [];
|
|
60
66
|
lines.push(`Comparing branch coverage: ${comparison.baselineRef} \u2192 HEAD`);
|
|
@@ -84,6 +90,60 @@ export function formatBaselineComparison(comparison) {
|
|
|
84
90
|
lines.push(`Net: ${signed} branches (${direction})`);
|
|
85
91
|
return lines.join("\n");
|
|
86
92
|
}
|
|
93
|
+
export function comparePrBaseline(baselinePath, baseline, current) {
|
|
94
|
+
const baselineKeys = new Map(baseline.uncovered.map((branch) => [branchIdentity(branch), branch]));
|
|
95
|
+
const currentKeys = new Map(current.uncovered.map((branch) => [branchIdentity(branch), branch]));
|
|
96
|
+
const newUnreachable = [...currentKeys.entries()]
|
|
97
|
+
.filter(([key]) => !baselineKeys.has(key))
|
|
98
|
+
.map(([, branch]) => branch)
|
|
99
|
+
.sort(compareBranches);
|
|
100
|
+
const resolvedUnreachable = [...baselineKeys.entries()]
|
|
101
|
+
.filter(([key]) => !currentKeys.has(key))
|
|
102
|
+
.map(([, branch]) => branch)
|
|
103
|
+
.sort(compareBranches);
|
|
104
|
+
return {
|
|
105
|
+
baselinePath,
|
|
106
|
+
baseline,
|
|
107
|
+
current,
|
|
108
|
+
newUnreachable,
|
|
109
|
+
resolvedUnreachable
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
export async function loadBaselinePayload(baselinePath) {
|
|
113
|
+
const raw = await readFile(baselinePath, "utf8");
|
|
114
|
+
const parsed = JSON.parse(raw);
|
|
115
|
+
if (isFormatPayload(parsed)) {
|
|
116
|
+
return parsed;
|
|
117
|
+
}
|
|
118
|
+
if (Array.isArray(parsed) && parsed.every(isBranchCoverage)) {
|
|
119
|
+
return {
|
|
120
|
+
covered: [],
|
|
121
|
+
uncovered: parsed,
|
|
122
|
+
summary: calculateCoverage(parsed)
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
throw new Error(`Invalid baseline file: ${baselinePath}`);
|
|
126
|
+
}
|
|
127
|
+
export function formatPrCheckComparison(comparison) {
|
|
128
|
+
const lines = [];
|
|
129
|
+
lines.push("Comparing against baseline...");
|
|
130
|
+
lines.push(` Baseline: ${comparison.baseline.uncovered.length} unreachable branches`);
|
|
131
|
+
lines.push(` Current: ${comparison.current.uncovered.length} unreachable branches`);
|
|
132
|
+
lines.push("");
|
|
133
|
+
if (comparison.newUnreachable.length === 0) {
|
|
134
|
+
lines.push("No new unreachable branches introduced in this PR.");
|
|
135
|
+
lines.push("");
|
|
136
|
+
lines.push("\u2705 PR check passed");
|
|
137
|
+
return lines.join("\n");
|
|
138
|
+
}
|
|
139
|
+
lines.push("New unreachable branches introduced in this PR:");
|
|
140
|
+
for (const branch of comparison.newUnreachable) {
|
|
141
|
+
lines.push(` ${branch.file}:${branch.line} ${branch.conditionText} \u2190 NEW`);
|
|
142
|
+
}
|
|
143
|
+
lines.push("");
|
|
144
|
+
lines.push(`\u274c PR check failed: ${comparison.newUnreachable.length} new unreachable branches`);
|
|
145
|
+
return lines.join("\n");
|
|
146
|
+
}
|
|
87
147
|
async function resolveRestoreRef(cwd) {
|
|
88
148
|
const branch = (await git(["symbolic-ref", "--quiet", "--short", "HEAD"], cwd, true)).stdout.trim();
|
|
89
149
|
if (branch) {
|
|
@@ -127,4 +187,22 @@ function compareBranches(left, right) {
|
|
|
127
187
|
}
|
|
128
188
|
return left.file.localeCompare(right.file);
|
|
129
189
|
}
|
|
190
|
+
function isFormatPayload(value) {
|
|
191
|
+
if (!value || typeof value !== "object") {
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
const candidate = value;
|
|
195
|
+
return Array.isArray(candidate.covered) && Array.isArray(candidate.uncovered) && candidate.summary !== undefined;
|
|
196
|
+
}
|
|
197
|
+
function isBranchCoverage(value) {
|
|
198
|
+
if (!value || typeof value !== "object") {
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
const candidate = value;
|
|
202
|
+
return (typeof candidate.file === "string" &&
|
|
203
|
+
typeof candidate.line === "number" &&
|
|
204
|
+
typeof candidate.conditionText === "string" &&
|
|
205
|
+
typeof candidate.pattern === "string" &&
|
|
206
|
+
typeof candidate.kind === "string");
|
|
207
|
+
}
|
|
130
208
|
//# sourceMappingURL=baseline.js.map
|
package/dist/baseline.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"baseline.js","sourceRoot":"","sources":["../src/baseline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,QAAQ,IAAI,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAClE,OAAO,EAAE,4BAA4B,EAAoB,MAAM,gBAAgB,CAAC;AAEhF,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAuB,MAAM,cAAc,CAAC;AACxF,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,QAAQ,GAAG,SAAS,CAAC,gBAAgB,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"baseline.js","sourceRoot":"","sources":["../src/baseline.ts"],"names":[],"mappings":"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"}
|
package/dist/blame.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { BranchCoverage } from "./matcher.js";
|
|
2
|
+
export interface BlameInfo {
|
|
3
|
+
authorName: string;
|
|
4
|
+
authorEmail: string;
|
|
5
|
+
authorTime: number;
|
|
6
|
+
authorDate: string;
|
|
7
|
+
relativeDays: number;
|
|
8
|
+
commitSummary: string;
|
|
9
|
+
commitHash: string;
|
|
10
|
+
}
|
|
11
|
+
export interface BlamedBranch extends BranchCoverage {
|
|
12
|
+
blame: BlameInfo | null;
|
|
13
|
+
}
|
|
14
|
+
export declare function attachBlameToBranches(branches: BranchCoverage[], cwd?: string, now?: Date): Promise<BlamedBranch[]>;
|
|
15
|
+
export declare function loadBlameForBranch(branch: Pick<BranchCoverage, "file" | "line">, cwd?: string, now?: Date): Promise<BlameInfo | null>;
|
|
16
|
+
export declare function parseBlamePorcelain(output: string, now?: Date): BlameInfo | null;
|
package/dist/blame.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { promisify } from "node:util";
|
|
2
|
+
import { execFile as execFileCallback } from "node:child_process";
|
|
3
|
+
const execFile = promisify(execFileCallback);
|
|
4
|
+
export async function attachBlameToBranches(branches, cwd = process.cwd(), now = new Date()) {
|
|
5
|
+
return Promise.all(branches.map(async (branch) => ({
|
|
6
|
+
...branch,
|
|
7
|
+
blame: await loadBlameForBranch(branch, cwd, now)
|
|
8
|
+
})));
|
|
9
|
+
}
|
|
10
|
+
export async function loadBlameForBranch(branch, cwd = process.cwd(), now = new Date()) {
|
|
11
|
+
try {
|
|
12
|
+
const { stdout } = await execFile("git", ["blame", "--line-porcelain", "-L", `${branch.line},${branch.line}`, "--", branch.file], { cwd });
|
|
13
|
+
return parseBlamePorcelain(stdout, now);
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export function parseBlamePorcelain(output, now = new Date()) {
|
|
20
|
+
const lines = output.split("\n");
|
|
21
|
+
const header = lines[0]?.trim() ?? "";
|
|
22
|
+
const authorName = readBlameField(lines, "author");
|
|
23
|
+
const authorEmail = stripAngleBrackets(readBlameField(lines, "author-mail"));
|
|
24
|
+
const authorTimeText = readBlameField(lines, "author-time");
|
|
25
|
+
const commitSummary = readBlameField(lines, "summary");
|
|
26
|
+
const commitHash = header.split(" ")[0] ?? "";
|
|
27
|
+
const authorTime = Number(authorTimeText);
|
|
28
|
+
if (!commitHash || !authorName || !authorEmail || !Number.isFinite(authorTime) || !commitSummary) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
authorName,
|
|
33
|
+
authorEmail,
|
|
34
|
+
authorTime,
|
|
35
|
+
authorDate: formatDate(authorTime),
|
|
36
|
+
relativeDays: calculateRelativeDays(authorTime, now),
|
|
37
|
+
commitSummary,
|
|
38
|
+
commitHash
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function readBlameField(lines, field) {
|
|
42
|
+
const prefix = `${field} `;
|
|
43
|
+
const match = lines.find((line) => line.startsWith(prefix));
|
|
44
|
+
return match?.slice(prefix.length).trim() ?? "";
|
|
45
|
+
}
|
|
46
|
+
function stripAngleBrackets(value) {
|
|
47
|
+
return value.replace(/^<|>$/g, "");
|
|
48
|
+
}
|
|
49
|
+
function formatDate(unixSeconds) {
|
|
50
|
+
return new Date(unixSeconds * 1000).toISOString().slice(0, 10);
|
|
51
|
+
}
|
|
52
|
+
function calculateRelativeDays(unixSeconds, now) {
|
|
53
|
+
const diffMs = now.getTime() - unixSeconds * 1000;
|
|
54
|
+
return Math.max(0, Math.floor(diffMs / 86_400_000));
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=blame.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"blame.js","sourceRoot":"","sources":["../src/blame.ts"],"names":[],"mappings":"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"}
|
package/dist/formatter.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { BlamedBranch } from "./blame.js";
|
|
1
2
|
import type { BranchCoverage } from "./matcher.js";
|
|
2
3
|
import type { CoverageSummary } from "./scorer.js";
|
|
3
4
|
export interface FormatPayload {
|
|
@@ -7,3 +8,4 @@ export interface FormatPayload {
|
|
|
7
8
|
}
|
|
8
9
|
export declare function formatText(payload: FormatPayload): string;
|
|
9
10
|
export declare function formatJson(payload: unknown): string;
|
|
11
|
+
export declare function formatBlameText(branches: BlamedBranch[]): string;
|
package/dist/formatter.js
CHANGED
|
@@ -30,6 +30,30 @@ export function formatText(payload) {
|
|
|
30
30
|
export function formatJson(payload) {
|
|
31
31
|
return JSON.stringify(payload, null, 2);
|
|
32
32
|
}
|
|
33
|
+
export function formatBlameText(branches) {
|
|
34
|
+
const lines = [];
|
|
35
|
+
lines.push(chalk.bold("Unreachable branches with blame:"));
|
|
36
|
+
lines.push("");
|
|
37
|
+
if (branches.length === 0) {
|
|
38
|
+
lines.push(` ${chalk.green("None")}`);
|
|
39
|
+
return lines.join("\n");
|
|
40
|
+
}
|
|
41
|
+
for (const branch of branches) {
|
|
42
|
+
lines.push(` ${chalk.red(`${branch.file}:${branch.line}`)} ${branch.conditionText}`);
|
|
43
|
+
if (branch.blame) {
|
|
44
|
+
lines.push(` \u2192 Author: ${branch.blame.authorName} <${branch.blame.authorEmail}>`);
|
|
45
|
+
lines.push(` \u2192 Last modified: ${branch.blame.authorDate} (${branch.blame.relativeDays} days ago)`);
|
|
46
|
+
lines.push(` \u2192 Commit: "${branch.blame.commitSummary}"`);
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
lines.push(" \u2192 Author: unavailable");
|
|
50
|
+
lines.push(" \u2192 Last modified: unavailable");
|
|
51
|
+
lines.push(" \u2192 Commit: unavailable");
|
|
52
|
+
}
|
|
53
|
+
lines.push("");
|
|
54
|
+
}
|
|
55
|
+
return lines.join("\n").trimEnd();
|
|
56
|
+
}
|
|
33
57
|
function describeUncovered(branch) {
|
|
34
58
|
switch (branch.pattern) {
|
|
35
59
|
case "env":
|
package/dist/formatter.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formatter.js","sourceRoot":"","sources":["../src/formatter.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"formatter.js","sourceRoot":"","sources":["../src/formatter.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { writeFile } from "node:fs/promises";
|
|
3
|
+
import { attachBlameToBranches } from "./blame.js";
|
|
3
4
|
import { Command } from "commander";
|
|
4
|
-
import { analyzeAgainstBaseline, analyzeBranchCoverage, formatBaselineComparison } from "./baseline.js";
|
|
5
|
-
import { formatJson, formatText } from "./formatter.js";
|
|
5
|
+
import { analyzeAgainstBaseline, analyzeBranchCoverage, analyzePrCheck, formatBaselineComparison, formatPrCheckComparison } from "./baseline.js";
|
|
6
|
+
import { formatBlameText, formatJson, formatText } from "./formatter.js";
|
|
6
7
|
import { generateHtmlReport } from "./html-report.js";
|
|
7
8
|
const program = new Command();
|
|
8
9
|
program
|
|
@@ -15,6 +16,8 @@ program
|
|
|
15
16
|
.option("--threshold <pct>", "Fail if coverage below N% (default: 0, fail on any)", "0")
|
|
16
17
|
.option("--report <output-file>", "Write an HTML report to a file")
|
|
17
18
|
.option("--baseline <branch>", "Compare current uncovered branches against a git ref")
|
|
19
|
+
.option("--blame", "Show git blame metadata for each unreachable branch", false)
|
|
20
|
+
.option("--pr-check", "Compare current unreachable branches against a baseline JSON file", false)
|
|
18
21
|
.action(async (srcDir, options) => {
|
|
19
22
|
const threshold = Number(options.threshold);
|
|
20
23
|
if (!Number.isFinite(threshold) || threshold < 0 || threshold > 100) {
|
|
@@ -29,7 +32,23 @@ program
|
|
|
29
32
|
}
|
|
30
33
|
let payload;
|
|
31
34
|
let comparison;
|
|
32
|
-
|
|
35
|
+
let prCheck;
|
|
36
|
+
let blamedUncovered = null;
|
|
37
|
+
if (options.prCheck && !options.baseline) {
|
|
38
|
+
console.error("Missing --baseline value. --pr-check expects a baseline JSON file path.");
|
|
39
|
+
process.exitCode = 2;
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
if (options.prCheck && options.baseline) {
|
|
43
|
+
prCheck = await analyzePrCheck({
|
|
44
|
+
srcDir,
|
|
45
|
+
testsDir: options.tests,
|
|
46
|
+
pattern: options.pattern,
|
|
47
|
+
baselinePath: options.baseline
|
|
48
|
+
});
|
|
49
|
+
payload = prCheck.current;
|
|
50
|
+
}
|
|
51
|
+
else if (options.baseline) {
|
|
33
52
|
comparison = await analyzeAgainstBaseline({
|
|
34
53
|
srcDir,
|
|
35
54
|
testsDir: options.tests,
|
|
@@ -45,13 +64,28 @@ program
|
|
|
45
64
|
pattern: options.pattern
|
|
46
65
|
});
|
|
47
66
|
}
|
|
67
|
+
if (options.blame) {
|
|
68
|
+
blamedUncovered = await attachBlameToBranches(payload.uncovered);
|
|
69
|
+
}
|
|
48
70
|
const shouldPrintText = !options.report || Boolean(options.baseline);
|
|
49
71
|
if (options.json) {
|
|
50
|
-
|
|
72
|
+
const basePayload = blamedUncovered ? { ...payload, uncovered: blamedUncovered } : payload;
|
|
73
|
+
if (prCheck) {
|
|
74
|
+
console.log(formatJson({ ...basePayload, prCheck }));
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
console.log(formatJson(comparison ? { ...basePayload, comparison } : basePayload));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
else if (prCheck && shouldPrintText) {
|
|
81
|
+
console.log(formatPrCheckComparison(prCheck));
|
|
51
82
|
}
|
|
52
83
|
else if (comparison && shouldPrintText) {
|
|
53
84
|
console.log(formatBaselineComparison(comparison));
|
|
54
85
|
}
|
|
86
|
+
else if (blamedUncovered && shouldPrintText) {
|
|
87
|
+
console.log(formatBlameText(blamedUncovered));
|
|
88
|
+
}
|
|
55
89
|
else if (shouldPrintText) {
|
|
56
90
|
console.log(formatText(payload));
|
|
57
91
|
}
|
|
@@ -67,7 +101,12 @@ program
|
|
|
67
101
|
}
|
|
68
102
|
}
|
|
69
103
|
const thresholdTriggered = threshold > 0 ? payload.summary.coveragePercent < threshold : payload.uncovered.length > 0;
|
|
70
|
-
if (
|
|
104
|
+
if (prCheck) {
|
|
105
|
+
if (prCheck.newUnreachable.length > 0) {
|
|
106
|
+
process.exitCode = 1;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
else if (options.fail && thresholdTriggered) {
|
|
71
110
|
process.exitCode = 1;
|
|
72
111
|
}
|
|
73
112
|
});
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|