@regressionproof/cli 0.8.0 → 0.9.1
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 +20 -88
- 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.js +12 -18
- package/build/components/Doctor.tsx +0 -12
- package/build/components/Init.js +2 -5
- package/build/components/Init.tsx +2 -10
- package/build/doctor/Doctor.d.ts +1 -0
- package/build/doctor/Doctor.js +32 -0
- package/build/doctor/DoctorContext.d.ts +4 -2
- package/build/doctor/DoctorContext.js +7 -3
- package/build/doctor/DoctorRunner.d.ts +2 -0
- package/build/doctor/DoctorRunner.js +5 -1
- package/build/doctor/checks/ApiReachabilityCheck.d.ts +5 -0
- package/build/doctor/checks/ApiReachabilityCheck.js +41 -0
- package/build/doctor/checks/MirrorAccessCheck.d.ts +3 -0
- package/build/doctor/checks/MirrorAccessCheck.js +83 -2
- package/build/esm/cli.js +20 -88
- 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.js +12 -18
- package/build/esm/components/Init.js +2 -5
- package/build/esm/doctor/Doctor.d.ts +1 -0
- package/build/esm/doctor/Doctor.js +34 -0
- package/build/esm/doctor/DoctorContext.d.ts +4 -2
- package/build/esm/doctor/DoctorContext.js +5 -3
- package/build/esm/doctor/DoctorRunner.d.ts +2 -0
- package/build/esm/doctor/DoctorRunner.js +4 -2
- package/build/esm/doctor/checks/ApiReachabilityCheck.d.ts +5 -0
- package/build/esm/doctor/checks/ApiReachabilityCheck.js +52 -0
- package/build/esm/doctor/checks/MirrorAccessCheck.d.ts +3 -0
- package/build/esm/doctor/checks/MirrorAccessCheck.js +88 -5
- package/build/esm/utilities/renderBanner.d.ts +1 -0
- package/build/esm/utilities/renderBanner.js +7 -0
- package/build/utilities/renderBanner.d.ts +1 -0
- package/build/utilities/renderBanner.js +7 -0
- package/package.json +4 -3
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { execSync } from 'child_process';
|
|
2
2
|
import fs from 'fs';
|
|
3
|
+
import { MirrorSyncer } from '@regressionproof/snapshotter';
|
|
3
4
|
import ConfigManager from '../../config/ConfigManager.js';
|
|
4
5
|
export default class MirrorAccessCheck {
|
|
5
6
|
async run(context) {
|
|
@@ -50,6 +51,54 @@ export default class MirrorAccessCheck {
|
|
|
50
51
|
};
|
|
51
52
|
}
|
|
52
53
|
const pushResult = this.checkPushAccess(mirrorPath, authedUrl);
|
|
54
|
+
if (context.fix && pushResult.status === 'warn') {
|
|
55
|
+
if (pushResult.reason === 'non_fast_forward') {
|
|
56
|
+
const fixResult = await this.trySyncMirror(mirrorPath);
|
|
57
|
+
if (!fixResult.ok) {
|
|
58
|
+
const message = fixResult.message ?? 'Unknown error.';
|
|
59
|
+
if (this.isDivergedBranchError(message)) {
|
|
60
|
+
return {
|
|
61
|
+
name: 'Mirror directory',
|
|
62
|
+
status: 'warn',
|
|
63
|
+
details: [
|
|
64
|
+
'Mirror has diverged; manual merge/rebase required.',
|
|
65
|
+
],
|
|
66
|
+
fix: 'Run `git -C <mirror> pull --rebase` (or merge) to resolve divergence.',
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
name: 'Mirror directory',
|
|
71
|
+
status: 'fail',
|
|
72
|
+
details: ['Unable to sync mirror.', message],
|
|
73
|
+
fix: 'Run `npx regressionproof doctor --fix` again or pull the mirror manually.',
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
const retryResult = this.checkPushAccess(mirrorPath, authedUrl);
|
|
77
|
+
if (retryResult.status === 'ok') {
|
|
78
|
+
return {
|
|
79
|
+
name: 'Mirror directory',
|
|
80
|
+
status: 'ok',
|
|
81
|
+
details: [
|
|
82
|
+
`Mirror directory exists at ${mirrorPath}.`,
|
|
83
|
+
'Mirror synced successfully.',
|
|
84
|
+
'Remote access confirmed (pull/push).',
|
|
85
|
+
],
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
return {
|
|
89
|
+
name: 'Mirror directory',
|
|
90
|
+
status: 'warn',
|
|
91
|
+
details: [
|
|
92
|
+
`Mirror directory exists at ${mirrorPath}.`,
|
|
93
|
+
'Mirror sync completed; recheck push access.',
|
|
94
|
+
],
|
|
95
|
+
fix: 'Re-run `npx regressionproof doctor` to confirm sync results.',
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
throw new Error('Doctor --fix not implemented for this mirror issue.');
|
|
100
|
+
}
|
|
101
|
+
}
|
|
53
102
|
if (pushResult.status === 'fail') {
|
|
54
103
|
return {
|
|
55
104
|
name: 'Mirror directory',
|
|
@@ -69,7 +118,7 @@ export default class MirrorAccessCheck {
|
|
|
69
118
|
'Remote access confirmed (pull).',
|
|
70
119
|
pushResult.message ?? 'Unknown error.',
|
|
71
120
|
],
|
|
72
|
-
fix: 'Run
|
|
121
|
+
fix: 'Run `npx regressionproof doctor --fix` or pull latest changes in the mirror.',
|
|
73
122
|
};
|
|
74
123
|
}
|
|
75
124
|
return {
|
|
@@ -106,9 +155,17 @@ export default class MirrorAccessCheck {
|
|
|
106
155
|
return {
|
|
107
156
|
status: 'warn',
|
|
108
157
|
message: 'No commits in mirror; push check skipped.',
|
|
158
|
+
reason: 'no_commits',
|
|
109
159
|
};
|
|
110
160
|
}
|
|
111
|
-
|
|
161
|
+
if (this.isNonFastForwardError(message)) {
|
|
162
|
+
return {
|
|
163
|
+
status: 'warn',
|
|
164
|
+
message: 'Remote has newer commits; mirror is behind (non-fast-forward).',
|
|
165
|
+
reason: 'non_fast_forward',
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
return { status: 'fail', message, reason: 'other' };
|
|
112
169
|
}
|
|
113
170
|
}
|
|
114
171
|
isNoCommitsError(message) {
|
|
@@ -117,6 +174,30 @@ export default class MirrorAccessCheck {
|
|
|
117
174
|
normalized.includes('does not match any') ||
|
|
118
175
|
normalized.includes('no commits'));
|
|
119
176
|
}
|
|
177
|
+
isNonFastForwardError(message) {
|
|
178
|
+
const normalized = message.toLowerCase();
|
|
179
|
+
return (normalized.includes('fetch first') ||
|
|
180
|
+
normalized.includes('non-fast-forward') ||
|
|
181
|
+
normalized.includes('failed to push some refs'));
|
|
182
|
+
}
|
|
183
|
+
isDivergedBranchError(message) {
|
|
184
|
+
const normalized = message.toLowerCase();
|
|
185
|
+
return (normalized.includes('diverging branches') ||
|
|
186
|
+
normalized.includes('not possible to fast-forward'));
|
|
187
|
+
}
|
|
188
|
+
async trySyncMirror(mirrorPath) {
|
|
189
|
+
try {
|
|
190
|
+
const syncer = new MirrorSyncer();
|
|
191
|
+
await syncer.syncBlocking(mirrorPath);
|
|
192
|
+
return { ok: true };
|
|
193
|
+
}
|
|
194
|
+
catch (err) {
|
|
195
|
+
return {
|
|
196
|
+
ok: false,
|
|
197
|
+
message: this.getErrorMessage(err),
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
}
|
|
120
201
|
getErrorMessage(err) {
|
|
121
202
|
if (err && typeof err === 'object') {
|
|
122
203
|
const error = err;
|
package/build/esm/cli.js
CHANGED
|
@@ -1,96 +1,28 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import 'dotenv/config';
|
|
3
|
-
import {
|
|
4
|
-
import
|
|
5
|
-
import acceptInvite from './commands/invite/AcceptInvite.js.js';
|
|
6
|
-
import createInvite from './commands/invite/CreateInvite.js.js';
|
|
7
|
-
import listInvites from './commands/invite/ListInvites.js.js';
|
|
8
|
-
import revokeInvite from './commands/invite/RevokeInvite.js.js';
|
|
9
|
-
import Doctor from './components/Doctor.js.js';
|
|
10
|
-
import Init from './components/Init.js.js';
|
|
11
|
-
import DoctorOutput from './doctor/DoctorOutput.js.js';
|
|
12
|
-
import DoctorRunner from './doctor/DoctorRunner.js.js';
|
|
3
|
+
import { runCommand } from './commands/CommandRunner.js.js';
|
|
4
|
+
import { getCommandHandler } from './commands/commands.js.js';
|
|
13
5
|
const command = process.argv[2];
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
else if (subcommand === 'accept') {
|
|
27
|
-
if (!arg) {
|
|
28
|
-
console.error('Usage: regressionproof invite accept <token>');
|
|
29
|
-
process.exit(1);
|
|
30
|
-
}
|
|
31
|
-
void acceptInvite(arg);
|
|
32
|
-
}
|
|
33
|
-
else if (subcommand === 'list') {
|
|
34
|
-
void listInvites(arg);
|
|
35
|
-
}
|
|
36
|
-
else if (subcommand === 'revoke') {
|
|
37
|
-
if (!arg) {
|
|
38
|
-
console.error('Usage: regressionproof invite revoke <token>');
|
|
6
|
+
const commandArgs = process.argv.slice(3);
|
|
7
|
+
const handler = getCommandHandler(command);
|
|
8
|
+
if (!handler) {
|
|
9
|
+
void runCommand({
|
|
10
|
+
heading: 'RegressionProof CLI',
|
|
11
|
+
handler: () => {
|
|
12
|
+
console.log('Usage: regressionproof <command>');
|
|
13
|
+
console.log('');
|
|
14
|
+
console.log('Commands:');
|
|
15
|
+
console.log(' init [projectName] Initialize a new project');
|
|
16
|
+
console.log(' invite ... Manage project invites');
|
|
17
|
+
console.log(' doctor Check project configuration');
|
|
39
18
|
process.exit(1);
|
|
40
|
-
}
|
|
41
|
-
void revokeInvite(arg);
|
|
42
|
-
}
|
|
43
|
-
else {
|
|
44
|
-
console.error('Usage: regressionproof invite <create|accept|list|revoke>');
|
|
45
|
-
process.exit(1);
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
else if (command === 'doctor') {
|
|
49
|
-
const options = parseDoctorArgs(process.argv.slice(3));
|
|
50
|
-
void DoctorRunner.run({ cwd: options.cwd })
|
|
51
|
-
.then((results) => {
|
|
52
|
-
if (options.json) {
|
|
53
|
-
console.log(JSON.stringify(results, null, 2));
|
|
54
|
-
}
|
|
55
|
-
else {
|
|
56
|
-
const { waitUntilExit } = render(React.createElement(Doctor, { results }));
|
|
57
|
-
void waitUntilExit().then(() => {
|
|
58
|
-
process.exit(DoctorOutput.exitCode(results));
|
|
59
|
-
});
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
process.exit(DoctorOutput.exitCode(results));
|
|
63
|
-
})
|
|
64
|
-
.catch((err) => {
|
|
65
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
66
|
-
console.error(message);
|
|
67
|
-
process.exit(1);
|
|
19
|
+
},
|
|
68
20
|
});
|
|
69
21
|
}
|
|
70
22
|
else {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
console.log(' doctor Check project configuration');
|
|
77
|
-
process.exit(1);
|
|
78
|
-
}
|
|
79
|
-
function parseDoctorArgs(args) {
|
|
80
|
-
const options = { json: false };
|
|
81
|
-
for (let i = 0; i < args.length; i++) {
|
|
82
|
-
const arg = args[i];
|
|
83
|
-
if (arg === '--json') {
|
|
84
|
-
options.json = true;
|
|
85
|
-
continue;
|
|
86
|
-
}
|
|
87
|
-
if (arg === '--cwd') {
|
|
88
|
-
const value = args[i + 1];
|
|
89
|
-
if (value) {
|
|
90
|
-
options.cwd = value;
|
|
91
|
-
i++;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
return options;
|
|
23
|
+
void runCommand({
|
|
24
|
+
heading: handler.heading,
|
|
25
|
+
isInk: handler.isInk,
|
|
26
|
+
handler: () => handler.run(commandArgs),
|
|
27
|
+
});
|
|
96
28
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
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 { renderBanner } from '../utilities/renderBanner.js.js';
|
|
11
|
+
export function runCommand(options) {
|
|
12
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
13
|
+
renderBanner();
|
|
14
|
+
if (!options.isInk && options.heading) {
|
|
15
|
+
console.log(options.heading);
|
|
16
|
+
}
|
|
17
|
+
try {
|
|
18
|
+
yield options.handler();
|
|
19
|
+
}
|
|
20
|
+
catch (err) {
|
|
21
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
22
|
+
console.error(message);
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -1,27 +1,21 @@
|
|
|
1
1
|
import { Box, Text } from 'ink';
|
|
2
|
-
import BigText from 'ink-big-text';
|
|
3
2
|
import React from 'react';
|
|
4
3
|
class DoctorComponent extends React.Component {
|
|
5
4
|
render() {
|
|
6
5
|
return (React.createElement(Box, { flexDirection: "column" },
|
|
7
|
-
React.createElement(Box, { flexDirection: "column",
|
|
8
|
-
React.createElement(
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
React.createElement(Text, { bold: true }, "RegressionProof Doctor"),
|
|
12
|
-
this.props.results.map((result) => (React.createElement(Box, { key: result.name, flexDirection: "column" },
|
|
13
|
-
React.createElement(Box, null,
|
|
14
|
-
React.createElement(Text, { color: this.colorFor(result.status) }, this.labelFor(result.status)),
|
|
15
|
-
React.createElement(Text, null,
|
|
16
|
-
" ",
|
|
17
|
-
result.name)),
|
|
18
|
-
result.details.map((detail) => (React.createElement(Text, { key: detail },
|
|
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,
|
|
19
10
|
" ",
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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, " ")))))));
|
|
25
19
|
}
|
|
26
20
|
labelFor(status) {
|
|
27
21
|
switch (status) {
|
|
@@ -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" }),
|
|
@@ -7,6 +7,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
7
7
|
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
8
|
});
|
|
9
9
|
};
|
|
10
|
+
import ApiReachabilityCheck from './checks/ApiReachabilityCheck.js.js';
|
|
10
11
|
import CredentialsCheck from './checks/CredentialsCheck.js.js';
|
|
11
12
|
import JestReporterCheck from './checks/JestReporterCheck.js.js';
|
|
12
13
|
import LocalConfigCheck from './checks/LocalConfigCheck.js.js';
|
|
@@ -22,12 +23,45 @@ export default class Doctor {
|
|
|
22
23
|
new JestReporterCheck(),
|
|
23
24
|
new CredentialsCheck(),
|
|
24
25
|
new MirrorAccessCheck(),
|
|
26
|
+
new ApiReachabilityCheck(),
|
|
25
27
|
];
|
|
26
28
|
const results = [];
|
|
29
|
+
if (this.context.fix) {
|
|
30
|
+
return this.runFixOnly(checks, results);
|
|
31
|
+
}
|
|
27
32
|
for (const check of checks) {
|
|
28
33
|
results.push(yield check.run(this.context));
|
|
29
34
|
}
|
|
30
35
|
return results;
|
|
31
36
|
});
|
|
32
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
|
+
}
|
|
33
67
|
}
|
|
@@ -3,8 +3,10 @@ export default class DoctorContext {
|
|
|
3
3
|
projectName: string | null;
|
|
4
4
|
localConfigPath: string | null;
|
|
5
5
|
homeConfigDir: string;
|
|
6
|
-
|
|
7
|
-
|
|
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;
|
|
8
10
|
get configDir(): string | null;
|
|
9
11
|
get credentialsPath(): string | null;
|
|
10
12
|
get mirrorPath(): string | null;
|
|
@@ -2,20 +2,22 @@ import fs from 'fs';
|
|
|
2
2
|
import os from 'os';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
export default class DoctorContext {
|
|
5
|
-
constructor(cwd, projectName, localConfigPath, homeConfigDir) {
|
|
5
|
+
constructor(cwd, projectName, localConfigPath, homeConfigDir, apiUrl, fix) {
|
|
6
6
|
this.cwd = cwd;
|
|
7
7
|
this.projectName = projectName;
|
|
8
8
|
this.localConfigPath = localConfigPath;
|
|
9
9
|
this.homeConfigDir = homeConfigDir;
|
|
10
|
+
this.apiUrl = apiUrl;
|
|
11
|
+
this.fix = fix;
|
|
10
12
|
}
|
|
11
|
-
static fromCwd(cwd) {
|
|
13
|
+
static fromCwd(cwd, apiUrl, fix) {
|
|
12
14
|
const localConfigPath = path.join(cwd, '.regressionproof.json');
|
|
13
15
|
const localExists = fs.existsSync(localConfigPath);
|
|
14
16
|
const projectName = localExists
|
|
15
17
|
? DoctorContext.readProjectName(localConfigPath)
|
|
16
18
|
: null;
|
|
17
19
|
const homeConfigDir = path.join(os.homedir(), '.regressionproof');
|
|
18
|
-
return new DoctorContext(cwd, projectName, localExists ? localConfigPath : null, homeConfigDir);
|
|
20
|
+
return new DoctorContext(cwd, projectName, localExists ? localConfigPath : null, homeConfigDir, apiUrl, fix);
|
|
19
21
|
}
|
|
20
22
|
get configDir() {
|
|
21
23
|
return this.projectName
|
|
@@ -12,9 +12,11 @@ import DoctorContext from './DoctorContext.js.js';
|
|
|
12
12
|
export default class DoctorRunner {
|
|
13
13
|
static run(options) {
|
|
14
14
|
return __awaiter(this, void 0, void 0, function* () {
|
|
15
|
-
var _a;
|
|
15
|
+
var _a, _b, _c, _d;
|
|
16
16
|
const cwd = (_a = options === null || options === void 0 ? void 0 : options.cwd) !== null && _a !== void 0 ? _a : process.cwd();
|
|
17
|
-
const
|
|
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);
|
|
18
20
|
return new Doctor(context).run();
|
|
19
21
|
});
|
|
20
22
|
}
|