@regressionproof/cli 0.7.4 → 0.9.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/build/cli.js +21 -45
- package/build/commands/CommandRunner.d.ts +7 -0
- package/build/commands/CommandRunner.js +15 -0
- package/build/commands/commands.d.ts +7 -0
- package/build/commands/commands.js +118 -0
- package/build/components/Banner.d.ts +2 -0
- package/build/components/Banner.js +13 -0
- package/build/components/Banner.tsx +20 -0
- package/build/components/CommandHeading.d.ts +6 -0
- package/build/components/CommandHeading.js +5 -0
- package/build/components/CommandHeading.tsx +10 -0
- package/build/components/CommandLayout.d.ts +7 -0
- package/build/components/CommandLayout.js +8 -0
- package/build/components/CommandLayout.tsx +17 -0
- package/build/components/Doctor.d.ts +7 -0
- package/build/components/Doctor.js +43 -0
- package/build/components/Doctor.tsx +63 -0
- package/build/components/Init.js +2 -5
- package/build/components/Init.tsx +2 -10
- package/build/doctor/Doctor.d.ts +8 -0
- package/build/doctor/Doctor.js +55 -0
- package/build/doctor/DoctorContext.d.ts +14 -0
- package/build/doctor/DoctorContext.js +48 -0
- package/build/doctor/DoctorOutput.d.ts +6 -0
- package/build/doctor/DoctorOutput.js +32 -0
- package/build/doctor/DoctorResult.d.ts +8 -0
- package/build/doctor/DoctorResult.js +1 -0
- package/build/doctor/DoctorRunner.d.ts +9 -0
- package/build/doctor/DoctorRunner.js +13 -0
- package/build/doctor/checks/ApiReachabilityCheck.d.ts +5 -0
- package/build/doctor/checks/ApiReachabilityCheck.js +41 -0
- package/build/doctor/checks/CredentialsCheck.d.ts +6 -0
- package/build/doctor/checks/CredentialsCheck.js +55 -0
- package/build/doctor/checks/JestReporterCheck.d.ts +5 -0
- package/build/doctor/checks/JestReporterCheck.js +35 -0
- package/build/doctor/checks/LocalConfigCheck.d.ts +5 -0
- package/build/doctor/checks/LocalConfigCheck.js +17 -0
- package/build/doctor/checks/MirrorAccessCheck.d.ts +12 -0
- package/build/doctor/checks/MirrorAccessCheck.js +210 -0
- package/build/esm/cli.js +21 -45
- package/build/esm/commands/CommandRunner.d.ts +7 -0
- package/build/esm/commands/CommandRunner.js +26 -0
- package/build/esm/commands/commands.d.ts +7 -0
- package/build/esm/commands/commands.js +128 -0
- package/build/esm/components/Banner.d.ts +2 -0
- package/build/esm/components/Banner.js +13 -0
- package/build/esm/components/CommandHeading.d.ts +6 -0
- package/build/esm/components/CommandHeading.js +5 -0
- package/build/esm/components/CommandLayout.d.ts +7 -0
- package/build/esm/components/CommandLayout.js +8 -0
- package/build/esm/components/Doctor.d.ts +7 -0
- package/build/esm/components/Doctor.js +43 -0
- package/build/esm/components/Init.js +2 -5
- package/build/esm/doctor/Doctor.d.ts +8 -0
- package/build/esm/doctor/Doctor.js +67 -0
- package/build/esm/doctor/DoctorContext.d.ts +14 -0
- package/build/esm/doctor/DoctorContext.js +43 -0
- package/build/esm/doctor/DoctorOutput.d.ts +6 -0
- package/build/esm/doctor/DoctorOutput.js +32 -0
- package/build/esm/doctor/DoctorResult.d.ts +8 -0
- package/build/esm/doctor/DoctorResult.js +1 -0
- package/build/esm/doctor/DoctorRunner.d.ts +9 -0
- package/build/esm/doctor/DoctorRunner.js +23 -0
- package/build/esm/doctor/checks/ApiReachabilityCheck.d.ts +5 -0
- package/build/esm/doctor/checks/ApiReachabilityCheck.js +52 -0
- package/build/esm/doctor/checks/CredentialsCheck.d.ts +6 -0
- package/build/esm/doctor/checks/CredentialsCheck.js +66 -0
- package/build/esm/doctor/checks/JestReporterCheck.d.ts +5 -0
- package/build/esm/doctor/checks/JestReporterCheck.js +46 -0
- package/build/esm/doctor/checks/LocalConfigCheck.d.ts +5 -0
- package/build/esm/doctor/checks/LocalConfigCheck.js +28 -0
- package/build/esm/doctor/checks/MirrorAccessCheck.d.ts +12 -0
- package/build/esm/doctor/checks/MirrorAccessCheck.js +224 -0
- package/build/esm/jest/JestConfigurator.js +9 -0
- package/build/esm/jest/JestReporterConfigInspector.d.ts +13 -0
- package/build/esm/jest/JestReporterConfigInspector.js +60 -0
- package/build/esm/utilities/renderBanner.d.ts +1 -0
- package/build/esm/utilities/renderBanner.js +7 -0
- package/build/jest/JestConfigurator.js +9 -0
- package/build/jest/JestReporterConfigInspector.d.ts +13 -0
- package/build/jest/JestReporterConfigInspector.js +56 -0
- package/build/utilities/renderBanner.d.ts +1 -0
- package/build/utilities/renderBanner.js +7 -0
- package/package.json +4 -3
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { render } from 'ink';
|
|
11
|
+
import React from 'react';
|
|
12
|
+
import CommandLayout from '../components/CommandLayout.js.js';
|
|
13
|
+
import Doctor from '../components/Doctor.js.js';
|
|
14
|
+
import Init from '../components/Init.js.js';
|
|
15
|
+
import DoctorOutput from '../doctor/DoctorOutput.js.js';
|
|
16
|
+
import DoctorRunner from '../doctor/DoctorRunner.js.js';
|
|
17
|
+
import acceptInvite from './invite/AcceptInvite.js.js';
|
|
18
|
+
import createInvite from './invite/CreateInvite.js.js';
|
|
19
|
+
import listInvites from './invite/ListInvites.js.js';
|
|
20
|
+
import revokeInvite from './invite/RevokeInvite.js.js';
|
|
21
|
+
const commandHandlers = {
|
|
22
|
+
init: {
|
|
23
|
+
heading: 'RegressionProof Init',
|
|
24
|
+
isInk: true,
|
|
25
|
+
run: (args) => {
|
|
26
|
+
const projectName = args[0];
|
|
27
|
+
render(React.createElement(CommandLayout, { heading: 'RegressionProof Init' }, React.createElement(Init, { projectName })));
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
invite: {
|
|
31
|
+
heading: 'RegressionProof Invite',
|
|
32
|
+
run: (args) => __awaiter(void 0, void 0, void 0, function* () {
|
|
33
|
+
const subcommand = args[0];
|
|
34
|
+
const arg = args[1];
|
|
35
|
+
if (subcommand === 'create') {
|
|
36
|
+
const noteArg = args.find((value) => value.startsWith('--note='));
|
|
37
|
+
const note = noteArg
|
|
38
|
+
? noteArg.replace('--note=', '')
|
|
39
|
+
: undefined;
|
|
40
|
+
yield createInvite(arg, note);
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (subcommand === 'accept') {
|
|
44
|
+
if (!arg) {
|
|
45
|
+
throw new Error('Usage: regressionproof invite accept <token>');
|
|
46
|
+
}
|
|
47
|
+
yield acceptInvite(arg);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (subcommand === 'list') {
|
|
51
|
+
yield listInvites(arg);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (subcommand === 'revoke') {
|
|
55
|
+
if (!arg) {
|
|
56
|
+
throw new Error('Usage: regressionproof invite revoke <token>');
|
|
57
|
+
}
|
|
58
|
+
yield revokeInvite(arg);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
throw new Error('Usage: regressionproof invite <create|accept|list|revoke>');
|
|
62
|
+
}),
|
|
63
|
+
},
|
|
64
|
+
doctor: {
|
|
65
|
+
heading: 'RegressionProof Doctor',
|
|
66
|
+
isInk: true,
|
|
67
|
+
run: (args) => __awaiter(void 0, void 0, void 0, function* () {
|
|
68
|
+
const options = parseDoctorArgs(args);
|
|
69
|
+
if (options.help) {
|
|
70
|
+
console.log('RegressionProof Doctor');
|
|
71
|
+
console.log('');
|
|
72
|
+
console.log('Usage:\n regressionproof doctor [--fix] [--json] [--cwd <path>]');
|
|
73
|
+
console.log('');
|
|
74
|
+
console.log('Options:');
|
|
75
|
+
console.log(' --fix Attempt safe fixes (currently only mirror sync)');
|
|
76
|
+
console.log(' --json Output machine-readable JSON');
|
|
77
|
+
console.log(' --cwd Run checks for a specific directory');
|
|
78
|
+
console.log(' --help Show this help');
|
|
79
|
+
process.exit(0);
|
|
80
|
+
}
|
|
81
|
+
const results = yield DoctorRunner.run({
|
|
82
|
+
cwd: options.cwd,
|
|
83
|
+
fix: options.fix,
|
|
84
|
+
});
|
|
85
|
+
if (options.json) {
|
|
86
|
+
console.log(JSON.stringify(results, null, 2));
|
|
87
|
+
process.exit(DoctorOutput.exitCode(results));
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const { waitUntilExit } = render(React.createElement(CommandLayout, { heading: 'RegressionProof Doctor' }, React.createElement(Doctor, { results })));
|
|
91
|
+
yield waitUntilExit();
|
|
92
|
+
process.exit(DoctorOutput.exitCode(results));
|
|
93
|
+
}),
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
export function getCommandHandler(command) {
|
|
97
|
+
var _a;
|
|
98
|
+
if (!command) {
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
return (_a = commandHandlers[command]) !== null && _a !== void 0 ? _a : null;
|
|
102
|
+
}
|
|
103
|
+
function parseDoctorArgs(args) {
|
|
104
|
+
const options = { json: false, fix: false, help: false };
|
|
105
|
+
for (let i = 0; i < args.length; i++) {
|
|
106
|
+
const arg = args[i];
|
|
107
|
+
if (arg === '--json') {
|
|
108
|
+
options.json = true;
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
if (arg === '--fix') {
|
|
112
|
+
options.fix = true;
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
115
|
+
if (arg === '--help' || arg === '-h') {
|
|
116
|
+
options.help = true;
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (arg === '--cwd') {
|
|
120
|
+
const value = args[i + 1];
|
|
121
|
+
if (value) {
|
|
122
|
+
options.cwd = value;
|
|
123
|
+
i++;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return options;
|
|
128
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Box, Text } from 'ink';
|
|
2
|
+
import BigText from 'ink-big-text';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { getCliVersion } from '../utilities/version.js.js';
|
|
5
|
+
export default function Banner() {
|
|
6
|
+
const version = getCliVersion();
|
|
7
|
+
return (React.createElement(Box, { flexDirection: "column", padding: 1 },
|
|
8
|
+
React.createElement(BigText, { text: "regressionproof.ai", font: "tiny", colors: ['magenta', 'cyan'] }),
|
|
9
|
+
React.createElement(Text, { color: "gray" }, "Teaching LLMs to write better code."),
|
|
10
|
+
React.createElement(Text, { color: "gray" },
|
|
11
|
+
"CLI v",
|
|
12
|
+
version)));
|
|
13
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Box } from 'ink';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import CommandHeading from './CommandHeading.js.js';
|
|
4
|
+
export default function CommandLayout(props) {
|
|
5
|
+
return (React.createElement(Box, { flexDirection: "column", paddingX: 1, paddingBottom: 1 },
|
|
6
|
+
React.createElement(CommandHeading, { heading: props.heading }),
|
|
7
|
+
React.createElement(Box, { marginTop: 1 }, props.children)));
|
|
8
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Box, Text } from 'ink';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
class DoctorComponent extends React.Component {
|
|
4
|
+
render() {
|
|
5
|
+
return (React.createElement(Box, { flexDirection: "column" },
|
|
6
|
+
React.createElement(Box, { flexDirection: "column", paddingX: 1, paddingBottom: 1 }, this.props.results.map((result) => (React.createElement(Box, { key: result.name, flexDirection: "column" },
|
|
7
|
+
React.createElement(Box, null,
|
|
8
|
+
React.createElement(Text, { color: this.colorFor(result.status) }, this.labelFor(result.status)),
|
|
9
|
+
React.createElement(Text, null,
|
|
10
|
+
" ",
|
|
11
|
+
result.name)),
|
|
12
|
+
result.details.map((detail) => (React.createElement(Text, { key: detail },
|
|
13
|
+
" ",
|
|
14
|
+
detail))),
|
|
15
|
+
result.fix ? (React.createElement(Text, null,
|
|
16
|
+
" Fix: ",
|
|
17
|
+
result.fix)) : null,
|
|
18
|
+
React.createElement(Text, null, " ")))))));
|
|
19
|
+
}
|
|
20
|
+
labelFor(status) {
|
|
21
|
+
switch (status) {
|
|
22
|
+
case 'ok':
|
|
23
|
+
return 'OK';
|
|
24
|
+
case 'warn':
|
|
25
|
+
return 'WARN';
|
|
26
|
+
case 'fail':
|
|
27
|
+
return 'FAIL';
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
colorFor(status) {
|
|
31
|
+
switch (status) {
|
|
32
|
+
case 'ok':
|
|
33
|
+
return 'green';
|
|
34
|
+
case 'warn':
|
|
35
|
+
return 'yellow';
|
|
36
|
+
case 'fail':
|
|
37
|
+
return 'red';
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export default function Doctor(props) {
|
|
42
|
+
return React.createElement(DoctorComponent, { results: props.results });
|
|
43
|
+
}
|
|
@@ -12,7 +12,6 @@ import { spawnSync } from 'node:child_process';
|
|
|
12
12
|
import { existsSync, readFileSync } from 'node:fs';
|
|
13
13
|
import { buildRegressionProofClient } from '@regressionproof/client';
|
|
14
14
|
import { Box, Text, useApp } from 'ink';
|
|
15
|
-
import BigText from 'ink-big-text';
|
|
16
15
|
import TextInput from 'ink-text-input';
|
|
17
16
|
import React from 'react';
|
|
18
17
|
import ConfigManager from '../config/ConfigManager.js.js';
|
|
@@ -268,10 +267,8 @@ class InitComponent extends React.Component {
|
|
|
268
267
|
}
|
|
269
268
|
renderInput() {
|
|
270
269
|
const { name } = this.state;
|
|
271
|
-
return (React.createElement(Box, { flexDirection: "column"
|
|
272
|
-
React.createElement(
|
|
273
|
-
React.createElement(Text, { color: "gray" }, "Teaching LLMs to write better code."),
|
|
274
|
-
React.createElement(Box, { marginTop: 1, flexDirection: "column" },
|
|
270
|
+
return (React.createElement(Box, { flexDirection: "column" },
|
|
271
|
+
React.createElement(Box, { padding: 1, flexDirection: "column" },
|
|
275
272
|
React.createElement(Text, { bold: true }, "Project name:"),
|
|
276
273
|
React.createElement(Box, null,
|
|
277
274
|
React.createElement(TextInput, { value: name, onChange: this.handleNameChange, onSubmit: this.handleSubmit, placeholder: "my-awesome-project" }),
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import ApiReachabilityCheck from './checks/ApiReachabilityCheck.js.js';
|
|
11
|
+
import CredentialsCheck from './checks/CredentialsCheck.js.js';
|
|
12
|
+
import JestReporterCheck from './checks/JestReporterCheck.js.js';
|
|
13
|
+
import LocalConfigCheck from './checks/LocalConfigCheck.js.js';
|
|
14
|
+
import MirrorAccessCheck from './checks/MirrorAccessCheck.js.js';
|
|
15
|
+
export default class Doctor {
|
|
16
|
+
constructor(context) {
|
|
17
|
+
this.context = context;
|
|
18
|
+
}
|
|
19
|
+
run() {
|
|
20
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
21
|
+
const checks = [
|
|
22
|
+
new LocalConfigCheck(),
|
|
23
|
+
new JestReporterCheck(),
|
|
24
|
+
new CredentialsCheck(),
|
|
25
|
+
new MirrorAccessCheck(),
|
|
26
|
+
new ApiReachabilityCheck(),
|
|
27
|
+
];
|
|
28
|
+
const results = [];
|
|
29
|
+
if (this.context.fix) {
|
|
30
|
+
return this.runFixOnly(checks, results);
|
|
31
|
+
}
|
|
32
|
+
for (const check of checks) {
|
|
33
|
+
results.push(yield check.run(this.context));
|
|
34
|
+
}
|
|
35
|
+
return results;
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
runFixOnly(checks, results) {
|
|
39
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
40
|
+
let mirrorResult;
|
|
41
|
+
for (const check of checks) {
|
|
42
|
+
if (check instanceof MirrorAccessCheck) {
|
|
43
|
+
mirrorResult = yield check.run(this.context);
|
|
44
|
+
results.push(mirrorResult);
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
if (!mirrorResult) {
|
|
49
|
+
throw new Error('Doctor --fix not implemented (missing mirror check).');
|
|
50
|
+
}
|
|
51
|
+
if (mirrorResult.status === 'fail') {
|
|
52
|
+
throw new Error('Doctor --fix not implemented for this mirror issue.');
|
|
53
|
+
}
|
|
54
|
+
for (const check of checks) {
|
|
55
|
+
if (check instanceof MirrorAccessCheck) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
const result = yield check.run(this.context);
|
|
59
|
+
results.push(result);
|
|
60
|
+
if (result.status !== 'ok') {
|
|
61
|
+
throw new Error(`Doctor --fix not implemented for ${result.name}.`);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return results;
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export default class DoctorContext {
|
|
2
|
+
cwd: string;
|
|
3
|
+
projectName: string | null;
|
|
4
|
+
localConfigPath: string | null;
|
|
5
|
+
homeConfigDir: string;
|
|
6
|
+
apiUrl: string;
|
|
7
|
+
fix: boolean;
|
|
8
|
+
constructor(cwd: string, projectName: string | null, localConfigPath: string | null, homeConfigDir: string, apiUrl: string, fix: boolean);
|
|
9
|
+
static fromCwd(cwd: string, apiUrl: string, fix: boolean): DoctorContext;
|
|
10
|
+
get configDir(): string | null;
|
|
11
|
+
get credentialsPath(): string | null;
|
|
12
|
+
get mirrorPath(): string | null;
|
|
13
|
+
private static readProjectName;
|
|
14
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import os from 'os';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
export default class DoctorContext {
|
|
5
|
+
constructor(cwd, projectName, localConfigPath, homeConfigDir, apiUrl, fix) {
|
|
6
|
+
this.cwd = cwd;
|
|
7
|
+
this.projectName = projectName;
|
|
8
|
+
this.localConfigPath = localConfigPath;
|
|
9
|
+
this.homeConfigDir = homeConfigDir;
|
|
10
|
+
this.apiUrl = apiUrl;
|
|
11
|
+
this.fix = fix;
|
|
12
|
+
}
|
|
13
|
+
static fromCwd(cwd, apiUrl, fix) {
|
|
14
|
+
const localConfigPath = path.join(cwd, '.regressionproof.json');
|
|
15
|
+
const localExists = fs.existsSync(localConfigPath);
|
|
16
|
+
const projectName = localExists
|
|
17
|
+
? DoctorContext.readProjectName(localConfigPath)
|
|
18
|
+
: null;
|
|
19
|
+
const homeConfigDir = path.join(os.homedir(), '.regressionproof');
|
|
20
|
+
return new DoctorContext(cwd, projectName, localExists ? localConfigPath : null, homeConfigDir, apiUrl, fix);
|
|
21
|
+
}
|
|
22
|
+
get configDir() {
|
|
23
|
+
return this.projectName
|
|
24
|
+
? path.join(this.homeConfigDir, this.projectName)
|
|
25
|
+
: null;
|
|
26
|
+
}
|
|
27
|
+
get credentialsPath() {
|
|
28
|
+
return this.configDir ? path.join(this.configDir, 'config.json') : null;
|
|
29
|
+
}
|
|
30
|
+
get mirrorPath() {
|
|
31
|
+
return this.configDir ? path.join(this.configDir, 'mirror') : null;
|
|
32
|
+
}
|
|
33
|
+
static readProjectName(localConfigPath) {
|
|
34
|
+
var _a;
|
|
35
|
+
try {
|
|
36
|
+
const raw = JSON.parse(fs.readFileSync(localConfigPath, 'utf-8'));
|
|
37
|
+
return (_a = raw.projectName) !== null && _a !== void 0 ? _a : null;
|
|
38
|
+
}
|
|
39
|
+
catch (_b) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export default class DoctorOutput {
|
|
2
|
+
static print(results) {
|
|
3
|
+
console.log('===================================');
|
|
4
|
+
console.log(' RegressionProof Doctor');
|
|
5
|
+
console.log('===================================');
|
|
6
|
+
console.log('');
|
|
7
|
+
for (const result of results) {
|
|
8
|
+
const label = DoctorOutput.formatStatus(result.status);
|
|
9
|
+
console.log(`${label} ${result.name}`);
|
|
10
|
+
for (const detail of result.details) {
|
|
11
|
+
console.log(` ${detail}`);
|
|
12
|
+
}
|
|
13
|
+
if (result.fix) {
|
|
14
|
+
console.log(` Fix: ${result.fix}`);
|
|
15
|
+
}
|
|
16
|
+
console.log('');
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
static exitCode(results) {
|
|
20
|
+
return results.some((result) => result.status === 'fail') ? 1 : 0;
|
|
21
|
+
}
|
|
22
|
+
static formatStatus(status) {
|
|
23
|
+
switch (status) {
|
|
24
|
+
case 'ok':
|
|
25
|
+
return 'OK ';
|
|
26
|
+
case 'warn':
|
|
27
|
+
return 'WARN';
|
|
28
|
+
case 'fail':
|
|
29
|
+
return 'FAIL';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import Doctor from './Doctor.js.js';
|
|
11
|
+
import DoctorContext from './DoctorContext.js.js';
|
|
12
|
+
export default class DoctorRunner {
|
|
13
|
+
static run(options) {
|
|
14
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
15
|
+
var _a, _b, _c, _d;
|
|
16
|
+
const cwd = (_a = options === null || options === void 0 ? void 0 : options.cwd) !== null && _a !== void 0 ? _a : process.cwd();
|
|
17
|
+
const apiUrl = (_c = (_b = options === null || options === void 0 ? void 0 : options.apiUrl) !== null && _b !== void 0 ? _b : process.env.REGRESSIONPROOF_API_URL) !== null && _c !== void 0 ? _c : 'https://api.regressionproof.ai';
|
|
18
|
+
const fix = (_d = options === null || options === void 0 ? void 0 : options.fix) !== null && _d !== void 0 ? _d : false;
|
|
19
|
+
const context = DoctorContext.fromCwd(cwd, apiUrl, fix);
|
|
20
|
+
return new Doctor(context).run();
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
export default class ApiReachabilityCheck {
|
|
11
|
+
run(context) {
|
|
12
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
13
|
+
const checkName = `rp-doctor-${Date.now()}`;
|
|
14
|
+
const url = `${context.apiUrl}/check-name/${checkName}`;
|
|
15
|
+
try {
|
|
16
|
+
const response = yield fetch(url, { method: 'GET' });
|
|
17
|
+
if (response.ok) {
|
|
18
|
+
return {
|
|
19
|
+
status: 'ok',
|
|
20
|
+
name: 'API reachability',
|
|
21
|
+
details: [`API responded at ${context.apiUrl}.`],
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
if (response.status === 502) {
|
|
25
|
+
return {
|
|
26
|
+
status: 'warn',
|
|
27
|
+
name: 'API reachability',
|
|
28
|
+
details: [
|
|
29
|
+
'API is reachable, but Git server communication failed (502).',
|
|
30
|
+
],
|
|
31
|
+
fix: 'Check the Git server connectivity or API configuration.',
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
status: 'warn',
|
|
36
|
+
name: 'API reachability',
|
|
37
|
+
details: [`API responded with status ${response.status}.`],
|
|
38
|
+
fix: 'Verify REGRESSIONPROOF_API_URL or check API logs.',
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
43
|
+
return {
|
|
44
|
+
status: 'fail',
|
|
45
|
+
name: 'API reachability',
|
|
46
|
+
details: [`API request failed: ${message}`],
|
|
47
|
+
fix: 'Check your network or verify REGRESSIONPROOF_API_URL.',
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import fs from 'fs';
|
|
11
|
+
import ConfigManager from '../../config/ConfigManager.js.js';
|
|
12
|
+
export default class CredentialsCheck {
|
|
13
|
+
run(context) {
|
|
14
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
15
|
+
var _a, _b;
|
|
16
|
+
if (!context.projectName) {
|
|
17
|
+
return {
|
|
18
|
+
name: 'Credentials for project',
|
|
19
|
+
status: 'warn',
|
|
20
|
+
details: [
|
|
21
|
+
'Unable to resolve project name from .regressionproof.json.',
|
|
22
|
+
],
|
|
23
|
+
fix: 'Run `npx regressionproof init` from the project root.',
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
const configManager = new ConfigManager();
|
|
27
|
+
const configPath = context.credentialsPath;
|
|
28
|
+
if (!configPath || !fs.existsSync(configPath)) {
|
|
29
|
+
return {
|
|
30
|
+
name: `Credentials for project "${context.projectName}"`,
|
|
31
|
+
status: 'fail',
|
|
32
|
+
details: [
|
|
33
|
+
`Missing credentials at ${configPath !== null && configPath !== void 0 ? configPath : configManager.getConfigDir(context.projectName)}.`,
|
|
34
|
+
],
|
|
35
|
+
fix: 'Run `npx regressionproof invite accept <token>` (teammate) or `npx regressionproof init` (owner).',
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
const parsed = this.safeReadJson(configPath);
|
|
39
|
+
const url = (_a = parsed === null || parsed === void 0 ? void 0 : parsed.remote) === null || _a === void 0 ? void 0 : _a.url;
|
|
40
|
+
const token = (_b = parsed === null || parsed === void 0 ? void 0 : parsed.remote) === null || _b === void 0 ? void 0 : _b.token;
|
|
41
|
+
if (!url || !token) {
|
|
42
|
+
return {
|
|
43
|
+
name: `Credentials for project "${context.projectName}"`,
|
|
44
|
+
status: 'fail',
|
|
45
|
+
details: [
|
|
46
|
+
'Credentials file is missing remote.url or remote.token.',
|
|
47
|
+
],
|
|
48
|
+
fix: 'Run `npx regressionproof invite accept <token>` to refresh credentials.',
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
name: `Credentials for project "${context.projectName}"`,
|
|
53
|
+
status: 'ok',
|
|
54
|
+
details: [`Credentials found at ${configPath}.`],
|
|
55
|
+
};
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
safeReadJson(filePath) {
|
|
59
|
+
try {
|
|
60
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
61
|
+
}
|
|
62
|
+
catch (_a) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|