@regressionproof/snapshotter 0.6.4 → 0.7.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/esm/index.d.ts +1 -1
- package/build/esm/index.js +1 -1
- package/build/esm/scripts/runSnapshot.d.ts +1 -0
- package/build/esm/scripts/runSnapshot.js +125 -0
- package/build/esm/snapshot.d.ts +1 -0
- package/build/esm/snapshot.js +32 -121
- package/build/index.d.ts +1 -1
- package/build/index.js +2 -1
- package/build/scripts/runSnapshot.d.ts +1 -0
- package/build/scripts/runSnapshot.js +120 -0
- package/build/snapshot.d.ts +1 -0
- package/build/snapshot.js +33 -113
- package/package.json +2 -2
package/build/esm/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { snapshot } from './snapshot.js';
|
|
1
|
+
export { snapshot, checkForPreviousSnapshotFailure } from './snapshot.js';
|
|
2
2
|
export * from './snapshotter.types.js';
|
package/build/esm/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { snapshot } from './snapshot.js.js';
|
|
1
|
+
export { snapshot, checkForPreviousSnapshotFailure } from './snapshot.js.js';
|
|
2
2
|
export * from './snapshotter.types.js.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,125 @@
|
|
|
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 { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync, } from 'fs';
|
|
11
|
+
import path from 'path';
|
|
12
|
+
import { buildLog } from '@sprucelabs/spruce-skill-utils';
|
|
13
|
+
import { gitCommit, gitPush } from '../git.js.js';
|
|
14
|
+
import { syncFiles } from '../sync.js.js';
|
|
15
|
+
const ERROR_FILE_NAME = 'lastError.json';
|
|
16
|
+
const LOCK_FILE_NAME = 'snapshot.lock';
|
|
17
|
+
const PENDING_FILE_NAME = 'pending.json';
|
|
18
|
+
const log = buildLog('Snapshotter');
|
|
19
|
+
function main() {
|
|
20
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
21
|
+
const snapshotterDir = process.argv[2];
|
|
22
|
+
if (!snapshotterDir) {
|
|
23
|
+
console.error('Usage: runSnapshot <snapshotter-dir>');
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
const lockPath = path.join(snapshotterDir, LOCK_FILE_NAME);
|
|
27
|
+
if (existsSync(lockPath)) {
|
|
28
|
+
log.info('Another snapshot is running, exiting');
|
|
29
|
+
process.exit(0);
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
writeFileSync(lockPath, process.pid.toString());
|
|
33
|
+
yield processLoop(snapshotterDir);
|
|
34
|
+
}
|
|
35
|
+
finally {
|
|
36
|
+
if (existsSync(lockPath)) {
|
|
37
|
+
unlinkSync(lockPath);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
function processLoop(snapshotterDir) {
|
|
43
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
44
|
+
const pendingPath = path.join(snapshotterDir, PENDING_FILE_NAME);
|
|
45
|
+
while (existsSync(pendingPath)) {
|
|
46
|
+
const options = JSON.parse(readFileSync(pendingPath, 'utf-8'));
|
|
47
|
+
unlinkSync(pendingPath);
|
|
48
|
+
yield executeSnapshot(options);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
function executeSnapshot(options) {
|
|
53
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
54
|
+
var _a;
|
|
55
|
+
const sourcePath = (_a = options.sourcePath) !== null && _a !== void 0 ? _a : process.cwd();
|
|
56
|
+
const { mirrorPath, testResults, remote } = options;
|
|
57
|
+
log.info('Starting snapshot', sourcePath, mirrorPath);
|
|
58
|
+
try {
|
|
59
|
+
yield syncFiles(sourcePath, mirrorPath);
|
|
60
|
+
log.info('Files synced', mirrorPath);
|
|
61
|
+
const snapshotterDir = path.join(mirrorPath, '.snapshotter');
|
|
62
|
+
mkdirSync(snapshotterDir, { recursive: true });
|
|
63
|
+
writeFileSync(path.join(snapshotterDir, 'testResults.json'), JSON.stringify(sortTestResults(testResults), null, 2));
|
|
64
|
+
log.info('Test results saved', snapshotterDir);
|
|
65
|
+
const committed = yield gitCommit(mirrorPath, log);
|
|
66
|
+
if (!committed) {
|
|
67
|
+
log.info('No changes to commit', mirrorPath);
|
|
68
|
+
clearError(mirrorPath);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
log.info('Commit created, pushing', remote.url);
|
|
72
|
+
yield gitPush(mirrorPath, remote, log);
|
|
73
|
+
log.info('Push completed', remote.url);
|
|
74
|
+
clearError(mirrorPath);
|
|
75
|
+
}
|
|
76
|
+
catch (err) {
|
|
77
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
78
|
+
log.error('Snapshot failed (will surface on next test run)', message);
|
|
79
|
+
persistError(mirrorPath, err);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
function persistError(mirrorPath, err) {
|
|
84
|
+
const snapshotterDir = path.join(mirrorPath, '.snapshotter');
|
|
85
|
+
mkdirSync(snapshotterDir, { recursive: true });
|
|
86
|
+
const errorPath = getErrorFilePath(mirrorPath);
|
|
87
|
+
const errorData = {
|
|
88
|
+
message: err instanceof Error ? err.message : String(err),
|
|
89
|
+
stack: err instanceof Error ? err.stack : undefined,
|
|
90
|
+
timestamp: new Date().toISOString(),
|
|
91
|
+
};
|
|
92
|
+
writeFileSync(errorPath, JSON.stringify(errorData, null, 2));
|
|
93
|
+
}
|
|
94
|
+
function clearError(mirrorPath) {
|
|
95
|
+
const errorPath = getErrorFilePath(mirrorPath);
|
|
96
|
+
if (existsSync(errorPath)) {
|
|
97
|
+
unlinkSync(errorPath);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function getErrorFilePath(mirrorPath) {
|
|
101
|
+
return path.join(mirrorPath, '.snapshotter', ERROR_FILE_NAME);
|
|
102
|
+
}
|
|
103
|
+
function sortTestResults(testResults) {
|
|
104
|
+
const suites = [...testResults.suites].map((suite) => (Object.assign(Object.assign({}, suite), { tests: [...suite.tests].sort((left, right) => left.name.localeCompare(right.name)) })));
|
|
105
|
+
suites.sort((left, right) => left.path.localeCompare(right.path));
|
|
106
|
+
const typeErrors = testResults.typeErrors
|
|
107
|
+
? [...testResults.typeErrors].sort((left, right) => {
|
|
108
|
+
const fileCompare = left.file.localeCompare(right.file);
|
|
109
|
+
if (fileCompare !== 0) {
|
|
110
|
+
return fileCompare;
|
|
111
|
+
}
|
|
112
|
+
const lineCompare = left.line - right.line;
|
|
113
|
+
if (lineCompare !== 0) {
|
|
114
|
+
return lineCompare;
|
|
115
|
+
}
|
|
116
|
+
return left.column - right.column;
|
|
117
|
+
})
|
|
118
|
+
: undefined;
|
|
119
|
+
return Object.assign(Object.assign({}, testResults), { suites,
|
|
120
|
+
typeErrors });
|
|
121
|
+
}
|
|
122
|
+
main().catch((err) => {
|
|
123
|
+
console.error('Snapshot script failed:', err);
|
|
124
|
+
process.exit(1);
|
|
125
|
+
});
|
package/build/esm/snapshot.d.ts
CHANGED
package/build/esm/snapshot.js
CHANGED
|
@@ -1,129 +1,40 @@
|
|
|
1
|
-
|
|
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
|
-
};
|
|
1
|
+
import { spawn } from 'child_process';
|
|
10
2
|
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync, } from 'fs';
|
|
11
3
|
import path from 'path';
|
|
12
4
|
import { buildLog } from '@sprucelabs/spruce-skill-utils';
|
|
13
|
-
import { gitCommit, gitPush } from './git.js.js';
|
|
14
|
-
import { syncFiles } from './sync.js.js';
|
|
15
5
|
const ERROR_FILE_NAME = 'lastError.json';
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
`Timestamp: ${errorData.timestamp}\n` +
|
|
33
|
-
`This error was from a background snapshot that failed. ` +
|
|
34
|
-
`The snapshot has been retried - if this error persists, check your configuration.`);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
enqueue(options) {
|
|
38
|
-
this.queue.push(options);
|
|
39
|
-
this.log.info('Snapshot queued', options.mirrorPath);
|
|
40
|
-
void this.processQueue();
|
|
41
|
-
}
|
|
42
|
-
processQueue() {
|
|
43
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
44
|
-
if (this.isProcessing || this.queue.length === 0) {
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
this.isProcessing = true;
|
|
48
|
-
while (this.queue.length > 0) {
|
|
49
|
-
const options = this.queue.shift();
|
|
50
|
-
yield this.executeSnapshot(options);
|
|
51
|
-
}
|
|
52
|
-
this.isProcessing = false;
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
executeSnapshot(options) {
|
|
56
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
57
|
-
var _a;
|
|
58
|
-
const sourcePath = (_a = options.sourcePath) !== null && _a !== void 0 ? _a : process.cwd();
|
|
59
|
-
const { mirrorPath, testResults, remote } = options;
|
|
60
|
-
this.log.info('Starting snapshot', sourcePath, mirrorPath);
|
|
61
|
-
try {
|
|
62
|
-
yield syncFiles(sourcePath, mirrorPath);
|
|
63
|
-
this.log.info('Files synced', mirrorPath);
|
|
64
|
-
const snapshotterDir = path.join(mirrorPath, '.snapshotter');
|
|
65
|
-
mkdirSync(snapshotterDir, { recursive: true });
|
|
66
|
-
writeFileSync(path.join(snapshotterDir, 'testResults.json'), JSON.stringify(sortTestResults(testResults), null, 2));
|
|
67
|
-
this.log.info('Test results saved', snapshotterDir);
|
|
68
|
-
const committed = yield gitCommit(mirrorPath, this.log);
|
|
69
|
-
if (!committed) {
|
|
70
|
-
this.log.info('No changes to commit', mirrorPath);
|
|
71
|
-
this.clearError(mirrorPath);
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
this.log.info('Commit created, pushing', remote.url);
|
|
75
|
-
yield gitPush(mirrorPath, remote, this.log);
|
|
76
|
-
this.log.info('Push completed', remote.url);
|
|
77
|
-
this.clearError(mirrorPath);
|
|
78
|
-
}
|
|
79
|
-
catch (err) {
|
|
80
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
81
|
-
this.log.error('Snapshot failed (will surface on next test run)', message);
|
|
82
|
-
this.persistError(mirrorPath, err);
|
|
83
|
-
}
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
persistError(mirrorPath, err) {
|
|
87
|
-
const snapshotterDir = path.join(mirrorPath, '.snapshotter');
|
|
88
|
-
mkdirSync(snapshotterDir, { recursive: true });
|
|
89
|
-
const errorPath = this.getErrorFilePath(mirrorPath);
|
|
90
|
-
const errorData = {
|
|
91
|
-
message: err instanceof Error ? err.message : String(err),
|
|
92
|
-
stack: err instanceof Error ? err.stack : undefined,
|
|
93
|
-
timestamp: new Date().toISOString(),
|
|
94
|
-
};
|
|
95
|
-
writeFileSync(errorPath, JSON.stringify(errorData, null, 2));
|
|
96
|
-
}
|
|
97
|
-
clearError(mirrorPath) {
|
|
98
|
-
const errorPath = this.getErrorFilePath(mirrorPath);
|
|
99
|
-
if (existsSync(errorPath)) {
|
|
100
|
-
unlinkSync(errorPath);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
getErrorFilePath(mirrorPath) {
|
|
104
|
-
return path.join(mirrorPath, '.snapshotter', ERROR_FILE_NAME);
|
|
6
|
+
const log = buildLog('Snapshotter');
|
|
7
|
+
const scriptPath = path.join(__dirname, 'scripts', 'runSnapshot.js');
|
|
8
|
+
export function snapshot(options) {
|
|
9
|
+
const snapshotterDir = writeOptionsFile(options);
|
|
10
|
+
spawnSnapshotProcess(snapshotterDir);
|
|
11
|
+
log.info('Snapshot queued (running in background)', options.mirrorPath);
|
|
12
|
+
}
|
|
13
|
+
export function checkForPreviousSnapshotFailure(mirrorPath) {
|
|
14
|
+
const errorPath = getErrorFilePath(mirrorPath);
|
|
15
|
+
if (existsSync(errorPath)) {
|
|
16
|
+
const errorData = JSON.parse(readFileSync(errorPath, 'utf-8'));
|
|
17
|
+
unlinkSync(errorPath);
|
|
18
|
+
throw new Error(`Previous snapshot failed: ${errorData.message}\n` +
|
|
19
|
+
`Timestamp: ${errorData.timestamp}\n` +
|
|
20
|
+
`This error was from a background snapshot that failed. ` +
|
|
21
|
+
`The snapshot has been retried - if this error persists, check your configuration.`);
|
|
105
22
|
}
|
|
106
23
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
24
|
+
function writeOptionsFile(options) {
|
|
25
|
+
const snapshotterDir = path.join(options.mirrorPath, '.snapshotter');
|
|
26
|
+
mkdirSync(snapshotterDir, { recursive: true });
|
|
27
|
+
const optionsPath = path.join(snapshotterDir, 'pending.json');
|
|
28
|
+
writeFileSync(optionsPath, JSON.stringify(options, null, 2));
|
|
29
|
+
return snapshotterDir;
|
|
30
|
+
}
|
|
31
|
+
function spawnSnapshotProcess(snapshotterDir) {
|
|
32
|
+
const child = spawn('node', [scriptPath, snapshotterDir], {
|
|
33
|
+
detached: true,
|
|
34
|
+
stdio: 'ignore',
|
|
35
|
+
});
|
|
36
|
+
child.unref();
|
|
110
37
|
}
|
|
111
|
-
function
|
|
112
|
-
|
|
113
|
-
suites.sort((left, right) => left.path.localeCompare(right.path));
|
|
114
|
-
const typeErrors = testResults.typeErrors
|
|
115
|
-
? [...testResults.typeErrors].sort((left, right) => {
|
|
116
|
-
const fileCompare = left.file.localeCompare(right.file);
|
|
117
|
-
if (fileCompare !== 0) {
|
|
118
|
-
return fileCompare;
|
|
119
|
-
}
|
|
120
|
-
const lineCompare = left.line - right.line;
|
|
121
|
-
if (lineCompare !== 0) {
|
|
122
|
-
return lineCompare;
|
|
123
|
-
}
|
|
124
|
-
return left.column - right.column;
|
|
125
|
-
})
|
|
126
|
-
: undefined;
|
|
127
|
-
return Object.assign(Object.assign({}, testResults), { suites,
|
|
128
|
-
typeErrors });
|
|
38
|
+
function getErrorFilePath(mirrorPath) {
|
|
39
|
+
return path.join(mirrorPath, '.snapshotter', ERROR_FILE_NAME);
|
|
129
40
|
}
|
package/build/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { snapshot } from './snapshot.js';
|
|
1
|
+
export { snapshot, checkForPreviousSnapshotFailure } from './snapshot.js';
|
|
2
2
|
export * from './snapshotter.types.js';
|
package/build/index.js
CHANGED
|
@@ -14,7 +14,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.snapshot = void 0;
|
|
17
|
+
exports.checkForPreviousSnapshotFailure = exports.snapshot = void 0;
|
|
18
18
|
var snapshot_js_1 = require("./snapshot.js");
|
|
19
19
|
Object.defineProperty(exports, "snapshot", { enumerable: true, get: function () { return snapshot_js_1.snapshot; } });
|
|
20
|
+
Object.defineProperty(exports, "checkForPreviousSnapshotFailure", { enumerable: true, get: function () { return snapshot_js_1.checkForPreviousSnapshotFailure; } });
|
|
20
21
|
__exportStar(require("./snapshotter.types.js"), exports);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const fs_1 = require("fs");
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const spruce_skill_utils_1 = require("@sprucelabs/spruce-skill-utils");
|
|
9
|
+
const git_js_1 = require("../git.js");
|
|
10
|
+
const sync_js_1 = require("../sync.js");
|
|
11
|
+
const ERROR_FILE_NAME = 'lastError.json';
|
|
12
|
+
const LOCK_FILE_NAME = 'snapshot.lock';
|
|
13
|
+
const PENDING_FILE_NAME = 'pending.json';
|
|
14
|
+
const log = (0, spruce_skill_utils_1.buildLog)('Snapshotter');
|
|
15
|
+
async function main() {
|
|
16
|
+
const snapshotterDir = process.argv[2];
|
|
17
|
+
if (!snapshotterDir) {
|
|
18
|
+
console.error('Usage: runSnapshot <snapshotter-dir>');
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
const lockPath = path_1.default.join(snapshotterDir, LOCK_FILE_NAME);
|
|
22
|
+
if ((0, fs_1.existsSync)(lockPath)) {
|
|
23
|
+
log.info('Another snapshot is running, exiting');
|
|
24
|
+
process.exit(0);
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
(0, fs_1.writeFileSync)(lockPath, process.pid.toString());
|
|
28
|
+
await processLoop(snapshotterDir);
|
|
29
|
+
}
|
|
30
|
+
finally {
|
|
31
|
+
if ((0, fs_1.existsSync)(lockPath)) {
|
|
32
|
+
(0, fs_1.unlinkSync)(lockPath);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async function processLoop(snapshotterDir) {
|
|
37
|
+
const pendingPath = path_1.default.join(snapshotterDir, PENDING_FILE_NAME);
|
|
38
|
+
while ((0, fs_1.existsSync)(pendingPath)) {
|
|
39
|
+
const options = JSON.parse((0, fs_1.readFileSync)(pendingPath, 'utf-8'));
|
|
40
|
+
(0, fs_1.unlinkSync)(pendingPath);
|
|
41
|
+
await executeSnapshot(options);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async function executeSnapshot(options) {
|
|
45
|
+
const sourcePath = options.sourcePath ?? process.cwd();
|
|
46
|
+
const { mirrorPath, testResults, remote } = options;
|
|
47
|
+
log.info('Starting snapshot', sourcePath, mirrorPath);
|
|
48
|
+
try {
|
|
49
|
+
await (0, sync_js_1.syncFiles)(sourcePath, mirrorPath);
|
|
50
|
+
log.info('Files synced', mirrorPath);
|
|
51
|
+
const snapshotterDir = path_1.default.join(mirrorPath, '.snapshotter');
|
|
52
|
+
(0, fs_1.mkdirSync)(snapshotterDir, { recursive: true });
|
|
53
|
+
(0, fs_1.writeFileSync)(path_1.default.join(snapshotterDir, 'testResults.json'), JSON.stringify(sortTestResults(testResults), null, 2));
|
|
54
|
+
log.info('Test results saved', snapshotterDir);
|
|
55
|
+
const committed = await (0, git_js_1.gitCommit)(mirrorPath, log);
|
|
56
|
+
if (!committed) {
|
|
57
|
+
log.info('No changes to commit', mirrorPath);
|
|
58
|
+
clearError(mirrorPath);
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
log.info('Commit created, pushing', remote.url);
|
|
62
|
+
await (0, git_js_1.gitPush)(mirrorPath, remote, log);
|
|
63
|
+
log.info('Push completed', remote.url);
|
|
64
|
+
clearError(mirrorPath);
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
68
|
+
log.error('Snapshot failed (will surface on next test run)', message);
|
|
69
|
+
persistError(mirrorPath, err);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function persistError(mirrorPath, err) {
|
|
73
|
+
const snapshotterDir = path_1.default.join(mirrorPath, '.snapshotter');
|
|
74
|
+
(0, fs_1.mkdirSync)(snapshotterDir, { recursive: true });
|
|
75
|
+
const errorPath = getErrorFilePath(mirrorPath);
|
|
76
|
+
const errorData = {
|
|
77
|
+
message: err instanceof Error ? err.message : String(err),
|
|
78
|
+
stack: err instanceof Error ? err.stack : undefined,
|
|
79
|
+
timestamp: new Date().toISOString(),
|
|
80
|
+
};
|
|
81
|
+
(0, fs_1.writeFileSync)(errorPath, JSON.stringify(errorData, null, 2));
|
|
82
|
+
}
|
|
83
|
+
function clearError(mirrorPath) {
|
|
84
|
+
const errorPath = getErrorFilePath(mirrorPath);
|
|
85
|
+
if ((0, fs_1.existsSync)(errorPath)) {
|
|
86
|
+
(0, fs_1.unlinkSync)(errorPath);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function getErrorFilePath(mirrorPath) {
|
|
90
|
+
return path_1.default.join(mirrorPath, '.snapshotter', ERROR_FILE_NAME);
|
|
91
|
+
}
|
|
92
|
+
function sortTestResults(testResults) {
|
|
93
|
+
const suites = [...testResults.suites].map((suite) => ({
|
|
94
|
+
...suite,
|
|
95
|
+
tests: [...suite.tests].sort((left, right) => left.name.localeCompare(right.name)),
|
|
96
|
+
}));
|
|
97
|
+
suites.sort((left, right) => left.path.localeCompare(right.path));
|
|
98
|
+
const typeErrors = testResults.typeErrors
|
|
99
|
+
? [...testResults.typeErrors].sort((left, right) => {
|
|
100
|
+
const fileCompare = left.file.localeCompare(right.file);
|
|
101
|
+
if (fileCompare !== 0) {
|
|
102
|
+
return fileCompare;
|
|
103
|
+
}
|
|
104
|
+
const lineCompare = left.line - right.line;
|
|
105
|
+
if (lineCompare !== 0) {
|
|
106
|
+
return lineCompare;
|
|
107
|
+
}
|
|
108
|
+
return left.column - right.column;
|
|
109
|
+
})
|
|
110
|
+
: undefined;
|
|
111
|
+
return {
|
|
112
|
+
...testResults,
|
|
113
|
+
suites,
|
|
114
|
+
typeErrors,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
main().catch((err) => {
|
|
118
|
+
console.error('Snapshot script failed:', err);
|
|
119
|
+
process.exit(1);
|
|
120
|
+
});
|
package/build/snapshot.d.ts
CHANGED
package/build/snapshot.js
CHANGED
|
@@ -4,124 +4,44 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.snapshot = snapshot;
|
|
7
|
+
exports.checkForPreviousSnapshotFailure = checkForPreviousSnapshotFailure;
|
|
8
|
+
const child_process_1 = require("child_process");
|
|
7
9
|
const fs_1 = require("fs");
|
|
8
10
|
const path_1 = __importDefault(require("path"));
|
|
9
11
|
const spruce_skill_utils_1 = require("@sprucelabs/spruce-skill-utils");
|
|
10
|
-
const git_js_1 = require("./git.js");
|
|
11
|
-
const sync_js_1 = require("./sync.js");
|
|
12
12
|
const ERROR_FILE_NAME = 'lastError.json';
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
`Timestamp: ${errorData.timestamp}\n` +
|
|
30
|
-
`This error was from a background snapshot that failed. ` +
|
|
31
|
-
`The snapshot has been retried - if this error persists, check your configuration.`);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
enqueue(options) {
|
|
35
|
-
this.queue.push(options);
|
|
36
|
-
this.log.info('Snapshot queued', options.mirrorPath);
|
|
37
|
-
void this.processQueue();
|
|
38
|
-
}
|
|
39
|
-
async processQueue() {
|
|
40
|
-
if (this.isProcessing || this.queue.length === 0) {
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
this.isProcessing = true;
|
|
44
|
-
while (this.queue.length > 0) {
|
|
45
|
-
const options = this.queue.shift();
|
|
46
|
-
await this.executeSnapshot(options);
|
|
47
|
-
}
|
|
48
|
-
this.isProcessing = false;
|
|
49
|
-
}
|
|
50
|
-
async executeSnapshot(options) {
|
|
51
|
-
const sourcePath = options.sourcePath ?? process.cwd();
|
|
52
|
-
const { mirrorPath, testResults, remote } = options;
|
|
53
|
-
this.log.info('Starting snapshot', sourcePath, mirrorPath);
|
|
54
|
-
try {
|
|
55
|
-
await (0, sync_js_1.syncFiles)(sourcePath, mirrorPath);
|
|
56
|
-
this.log.info('Files synced', mirrorPath);
|
|
57
|
-
const snapshotterDir = path_1.default.join(mirrorPath, '.snapshotter');
|
|
58
|
-
(0, fs_1.mkdirSync)(snapshotterDir, { recursive: true });
|
|
59
|
-
(0, fs_1.writeFileSync)(path_1.default.join(snapshotterDir, 'testResults.json'), JSON.stringify(sortTestResults(testResults), null, 2));
|
|
60
|
-
this.log.info('Test results saved', snapshotterDir);
|
|
61
|
-
const committed = await (0, git_js_1.gitCommit)(mirrorPath, this.log);
|
|
62
|
-
if (!committed) {
|
|
63
|
-
this.log.info('No changes to commit', mirrorPath);
|
|
64
|
-
this.clearError(mirrorPath);
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
this.log.info('Commit created, pushing', remote.url);
|
|
68
|
-
await (0, git_js_1.gitPush)(mirrorPath, remote, this.log);
|
|
69
|
-
this.log.info('Push completed', remote.url);
|
|
70
|
-
this.clearError(mirrorPath);
|
|
71
|
-
}
|
|
72
|
-
catch (err) {
|
|
73
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
74
|
-
this.log.error('Snapshot failed (will surface on next test run)', message);
|
|
75
|
-
this.persistError(mirrorPath, err);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
persistError(mirrorPath, err) {
|
|
79
|
-
const snapshotterDir = path_1.default.join(mirrorPath, '.snapshotter');
|
|
80
|
-
(0, fs_1.mkdirSync)(snapshotterDir, { recursive: true });
|
|
81
|
-
const errorPath = this.getErrorFilePath(mirrorPath);
|
|
82
|
-
const errorData = {
|
|
83
|
-
message: err instanceof Error ? err.message : String(err),
|
|
84
|
-
stack: err instanceof Error ? err.stack : undefined,
|
|
85
|
-
timestamp: new Date().toISOString(),
|
|
86
|
-
};
|
|
87
|
-
(0, fs_1.writeFileSync)(errorPath, JSON.stringify(errorData, null, 2));
|
|
88
|
-
}
|
|
89
|
-
clearError(mirrorPath) {
|
|
90
|
-
const errorPath = this.getErrorFilePath(mirrorPath);
|
|
91
|
-
if ((0, fs_1.existsSync)(errorPath)) {
|
|
92
|
-
(0, fs_1.unlinkSync)(errorPath);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
getErrorFilePath(mirrorPath) {
|
|
96
|
-
return path_1.default.join(mirrorPath, '.snapshotter', ERROR_FILE_NAME);
|
|
13
|
+
const log = (0, spruce_skill_utils_1.buildLog)('Snapshotter');
|
|
14
|
+
const scriptPath = path_1.default.join(__dirname, 'scripts', 'runSnapshot.js');
|
|
15
|
+
function snapshot(options) {
|
|
16
|
+
const snapshotterDir = writeOptionsFile(options);
|
|
17
|
+
spawnSnapshotProcess(snapshotterDir);
|
|
18
|
+
log.info('Snapshot queued (running in background)', options.mirrorPath);
|
|
19
|
+
}
|
|
20
|
+
function checkForPreviousSnapshotFailure(mirrorPath) {
|
|
21
|
+
const errorPath = getErrorFilePath(mirrorPath);
|
|
22
|
+
if ((0, fs_1.existsSync)(errorPath)) {
|
|
23
|
+
const errorData = JSON.parse((0, fs_1.readFileSync)(errorPath, 'utf-8'));
|
|
24
|
+
(0, fs_1.unlinkSync)(errorPath);
|
|
25
|
+
throw new Error(`Previous snapshot failed: ${errorData.message}\n` +
|
|
26
|
+
`Timestamp: ${errorData.timestamp}\n` +
|
|
27
|
+
`This error was from a background snapshot that failed. ` +
|
|
28
|
+
`The snapshot has been retried - if this error persists, check your configuration.`);
|
|
97
29
|
}
|
|
98
30
|
}
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
31
|
+
function writeOptionsFile(options) {
|
|
32
|
+
const snapshotterDir = path_1.default.join(options.mirrorPath, '.snapshotter');
|
|
33
|
+
(0, fs_1.mkdirSync)(snapshotterDir, { recursive: true });
|
|
34
|
+
const optionsPath = path_1.default.join(snapshotterDir, 'pending.json');
|
|
35
|
+
(0, fs_1.writeFileSync)(optionsPath, JSON.stringify(options, null, 2));
|
|
36
|
+
return snapshotterDir;
|
|
37
|
+
}
|
|
38
|
+
function spawnSnapshotProcess(snapshotterDir) {
|
|
39
|
+
const child = (0, child_process_1.spawn)('node', [scriptPath, snapshotterDir], {
|
|
40
|
+
detached: true,
|
|
41
|
+
stdio: 'ignore',
|
|
42
|
+
});
|
|
43
|
+
child.unref();
|
|
102
44
|
}
|
|
103
|
-
function
|
|
104
|
-
|
|
105
|
-
...suite,
|
|
106
|
-
tests: [...suite.tests].sort((left, right) => left.name.localeCompare(right.name)),
|
|
107
|
-
}));
|
|
108
|
-
suites.sort((left, right) => left.path.localeCompare(right.path));
|
|
109
|
-
const typeErrors = testResults.typeErrors
|
|
110
|
-
? [...testResults.typeErrors].sort((left, right) => {
|
|
111
|
-
const fileCompare = left.file.localeCompare(right.file);
|
|
112
|
-
if (fileCompare !== 0) {
|
|
113
|
-
return fileCompare;
|
|
114
|
-
}
|
|
115
|
-
const lineCompare = left.line - right.line;
|
|
116
|
-
if (lineCompare !== 0) {
|
|
117
|
-
return lineCompare;
|
|
118
|
-
}
|
|
119
|
-
return left.column - right.column;
|
|
120
|
-
})
|
|
121
|
-
: undefined;
|
|
122
|
-
return {
|
|
123
|
-
...testResults,
|
|
124
|
-
suites,
|
|
125
|
-
typeErrors,
|
|
126
|
-
};
|
|
45
|
+
function getErrorFilePath(mirrorPath) {
|
|
46
|
+
return path_1.default.join(mirrorPath, '.snapshotter', ERROR_FILE_NAME);
|
|
127
47
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@regressionproof/snapshotter",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -94,5 +94,5 @@
|
|
|
94
94
|
"@sprucelabs/spruce-core-schemas": "^42.1.3",
|
|
95
95
|
"@sprucelabs/spruce-skill-utils": "^34.0.3"
|
|
96
96
|
},
|
|
97
|
-
"gitHead": "
|
|
97
|
+
"gitHead": "860c3695ab884487c654e5cf794262bff8ea1570"
|
|
98
98
|
}
|