@regressionproof/snapshotter 0.3.6 → 0.3.8
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/.spruce/errors/errors.types.d.ts +35 -0
- package/build/.spruce/errors/errors.types.js +2 -0
- package/build/.spruce/errors/options.types.d.ts +7 -0
- package/build/.spruce/errors/options.types.js +2 -0
- package/build/.spruce/errors/regressionproofSnapshotter/execCommandFailed.schema.d.ts +3 -0
- package/build/.spruce/errors/regressionproofSnapshotter/execCommandFailed.schema.js +29 -0
- package/build/.spruce/schemas/fields/fieldClassMap.d.ts +2 -0
- package/build/.spruce/schemas/fields/fieldClassMap.js +4 -0
- package/{src/.spruce/schemas/fields/fields.types.ts → build/.spruce/schemas/fields/fields.types.d.ts} +1 -1
- package/build/.spruce/schemas/fields/fields.types.js +2 -0
- package/build/errors/SpruceError.d.ts +6 -0
- package/build/errors/SpruceError.js +31 -0
- package/build/errors/execCommandFailed.builder.d.ts +18 -0
- package/build/errors/execCommandFailed.builder.js +20 -0
- package/build/esm/.spruce/errors/errors.types.d.ts +35 -0
- package/build/esm/.spruce/errors/errors.types.js +1 -0
- package/build/esm/.spruce/errors/options.types.d.ts +7 -0
- package/build/esm/.spruce/errors/options.types.js +1 -0
- package/build/esm/errors/SpruceError.d.ts +6 -0
- package/build/esm/errors/SpruceError.js +26 -0
- package/build/esm/errors/execCommandFailed.builder.d.ts +18 -0
- package/{src/errors/execCommandFailed.builder.ts → build/esm/errors/execCommandFailed.builder.js} +2 -3
- package/build/esm/git.d.ts +4 -0
- package/build/esm/git.js +65 -0
- package/build/esm/index.d.ts +2 -0
- package/build/esm/index.js +2 -0
- package/build/esm/scripts/testManual.d.ts +1 -0
- package/build/esm/scripts/testManual.js +60 -0
- package/build/esm/snapshot.d.ts +2 -0
- package/build/esm/snapshot.js +54 -0
- package/{src/snapshotter.types.ts → build/esm/snapshotter.types.d.ts} +21 -26
- package/build/esm/snapshotter.types.js +1 -0
- package/build/esm/sync.d.ts +1 -0
- package/build/esm/sync.js +40 -0
- package/build/git.d.ts +4 -0
- package/build/git.js +56 -0
- package/build/index.d.ts +2 -0
- package/build/index.js +20 -0
- package/build/scripts/testManual.d.ts +1 -0
- package/{src/scripts/testManual.ts → build/scripts/testManual.js} +19 -23
- package/build/snapshot.d.ts +2 -0
- package/build/snapshot.js +46 -0
- package/build/snapshotter.types.d.ts +54 -0
- package/build/snapshotter.types.js +2 -0
- package/build/sync.d.ts +1 -0
- package/build/sync.js +35 -0
- package/package.json +5 -2
- package/.nvmrc +0 -1
- package/.vscode/launch.json +0 -58
- package/.vscode/settings.json +0 -67
- package/.vscode/tasks.json +0 -112
- package/CHANGELOG.md +0 -320
- package/eslint.config.mjs +0 -3
- package/src/.spruce/errors/errors.types.ts +0 -52
- package/src/.spruce/errors/options.types.ts +0 -10
- package/src/.spruce/errors/regressionproofSnapshotter/execCommandFailed.schema.ts +0 -33
- package/src/.spruce/schemas/fields/fieldClassMap.ts +0 -6
- package/src/errors/SpruceError.ts +0 -29
- package/src/git.ts +0 -75
- package/src/index.ts +0 -2
- package/src/snapshot.ts +0 -51
- package/src/sync.ts +0 -35
- package/tsconfig.dist.json +0 -26
- package/tsconfig.json +0 -27
- /package/{src → build}/.spruce/settings.json +0 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { default as SchemaEntity } from '@sprucelabs/schema';
|
|
2
|
+
import * as SpruceSchema from '@sprucelabs/schema';
|
|
3
|
+
export declare namespace SpruceErrors.RegressionproofSnapshotter {
|
|
4
|
+
interface ExecCommandFailed {
|
|
5
|
+
/** . The command that was attempted */
|
|
6
|
+
'command': string;
|
|
7
|
+
'stdout'?: string | undefined | null;
|
|
8
|
+
'stderr'?: string | undefined | null;
|
|
9
|
+
}
|
|
10
|
+
interface ExecCommandFailedSchema extends SpruceSchema.Schema {
|
|
11
|
+
id: 'execCommandFailed';
|
|
12
|
+
namespace: 'RegressionproofSnapshotter';
|
|
13
|
+
name: 'Exec command failed';
|
|
14
|
+
fields: {
|
|
15
|
+
/** . The command that was attempted */
|
|
16
|
+
'command': {
|
|
17
|
+
type: 'text';
|
|
18
|
+
isRequired: true;
|
|
19
|
+
hint: 'The command that was attempted';
|
|
20
|
+
options: undefined;
|
|
21
|
+
};
|
|
22
|
+
/** . */
|
|
23
|
+
'stdout': {
|
|
24
|
+
type: 'text';
|
|
25
|
+
options: undefined;
|
|
26
|
+
};
|
|
27
|
+
/** . */
|
|
28
|
+
'stderr': {
|
|
29
|
+
type: 'text';
|
|
30
|
+
options: undefined;
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
type ExecCommandFailedEntity = SchemaEntity<SpruceErrors.RegressionproofSnapshotter.ExecCommandFailedSchema>;
|
|
35
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { SpruceErrors } from "./errors.types";
|
|
2
|
+
import { ErrorOptions as ISpruceErrorOptions } from "@sprucelabs/error";
|
|
3
|
+
export interface ExecCommandFailedErrorOptions extends SpruceErrors.RegressionproofSnapshotter.ExecCommandFailed, ISpruceErrorOptions {
|
|
4
|
+
code: 'EXEC_COMMAND_FAILED';
|
|
5
|
+
}
|
|
6
|
+
type ErrorOptions = ExecCommandFailedErrorOptions;
|
|
7
|
+
export default ErrorOptions;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const schema_1 = require("@sprucelabs/schema");
|
|
4
|
+
const execCommandFailedSchema = {
|
|
5
|
+
id: 'execCommandFailed',
|
|
6
|
+
namespace: 'RegressionproofSnapshotter',
|
|
7
|
+
name: 'Exec command failed',
|
|
8
|
+
fields: {
|
|
9
|
+
/** . The command that was attempted */
|
|
10
|
+
'command': {
|
|
11
|
+
type: 'text',
|
|
12
|
+
isRequired: true,
|
|
13
|
+
hint: 'The command that was attempted',
|
|
14
|
+
options: undefined
|
|
15
|
+
},
|
|
16
|
+
/** . */
|
|
17
|
+
'stdout': {
|
|
18
|
+
type: 'text',
|
|
19
|
+
options: undefined
|
|
20
|
+
},
|
|
21
|
+
/** . */
|
|
22
|
+
'stderr': {
|
|
23
|
+
type: 'text',
|
|
24
|
+
options: undefined
|
|
25
|
+
},
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
schema_1.SchemaRegistry.getInstance().trackSchema(execCommandFailedSchema);
|
|
29
|
+
exports.default = execCommandFailedSchema;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { FieldDefinitions, FieldDefinitionMap, FieldValueTypeGeneratorMap, FieldMap } from '@sprucelabs/schema'
|
|
1
|
+
export { FieldDefinitions, FieldDefinitionMap, FieldValueTypeGeneratorMap, FieldMap } from '@sprucelabs/schema';
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import BaseSpruceError from '@sprucelabs/error';
|
|
2
|
+
import ErrorOptions from './../.spruce/errors/options.types';
|
|
3
|
+
export default class SpruceError extends BaseSpruceError<ErrorOptions> {
|
|
4
|
+
/** an easy to understand version of the errors */
|
|
5
|
+
friendlyMessage(): string;
|
|
6
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
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 error_1 = __importDefault(require("@sprucelabs/error"));
|
|
7
|
+
class SpruceError extends error_1.default {
|
|
8
|
+
/** an easy to understand version of the errors */
|
|
9
|
+
friendlyMessage() {
|
|
10
|
+
const { options } = this;
|
|
11
|
+
let message;
|
|
12
|
+
switch (options?.code) {
|
|
13
|
+
case 'EXEC_COMMAND_FAILED':
|
|
14
|
+
message = `The command "${options.command}" failed to execute.
|
|
15
|
+
|
|
16
|
+
Stdout:
|
|
17
|
+
${options.stdout ?? '<no stdout>'}
|
|
18
|
+
|
|
19
|
+
Stderr:
|
|
20
|
+
${options.stderr ?? '<no stderr>'}`;
|
|
21
|
+
break;
|
|
22
|
+
default:
|
|
23
|
+
message = super.friendlyMessage();
|
|
24
|
+
}
|
|
25
|
+
const fullMessage = options.friendlyMessage
|
|
26
|
+
? options.friendlyMessage
|
|
27
|
+
: message;
|
|
28
|
+
return fullMessage;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
exports.default = SpruceError;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
declare const _default: {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
fields: {
|
|
5
|
+
command: {
|
|
6
|
+
type: "text";
|
|
7
|
+
isRequired: true;
|
|
8
|
+
hint: string;
|
|
9
|
+
};
|
|
10
|
+
stdout: {
|
|
11
|
+
type: "text";
|
|
12
|
+
};
|
|
13
|
+
stderr: {
|
|
14
|
+
type: "text";
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
export default _default;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const schema_1 = require("@sprucelabs/schema");
|
|
4
|
+
exports.default = (0, schema_1.buildErrorSchema)({
|
|
5
|
+
id: 'execCommandFailed',
|
|
6
|
+
name: 'Exec command failed',
|
|
7
|
+
fields: {
|
|
8
|
+
command: {
|
|
9
|
+
type: 'text',
|
|
10
|
+
isRequired: true,
|
|
11
|
+
hint: 'The command that was attempted',
|
|
12
|
+
},
|
|
13
|
+
stdout: {
|
|
14
|
+
type: 'text',
|
|
15
|
+
},
|
|
16
|
+
stderr: {
|
|
17
|
+
type: 'text',
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { default as SchemaEntity } from '@sprucelabs/schema';
|
|
2
|
+
import * as SpruceSchema from '@sprucelabs/schema';
|
|
3
|
+
export declare namespace SpruceErrors.RegressionproofSnapshotter {
|
|
4
|
+
interface ExecCommandFailed {
|
|
5
|
+
/** . The command that was attempted */
|
|
6
|
+
'command': string;
|
|
7
|
+
'stdout'?: string | undefined | null;
|
|
8
|
+
'stderr'?: string | undefined | null;
|
|
9
|
+
}
|
|
10
|
+
interface ExecCommandFailedSchema extends SpruceSchema.Schema {
|
|
11
|
+
id: 'execCommandFailed';
|
|
12
|
+
namespace: 'RegressionproofSnapshotter';
|
|
13
|
+
name: 'Exec command failed';
|
|
14
|
+
fields: {
|
|
15
|
+
/** . The command that was attempted */
|
|
16
|
+
'command': {
|
|
17
|
+
type: 'text';
|
|
18
|
+
isRequired: true;
|
|
19
|
+
hint: 'The command that was attempted';
|
|
20
|
+
options: undefined;
|
|
21
|
+
};
|
|
22
|
+
/** . */
|
|
23
|
+
'stdout': {
|
|
24
|
+
type: 'text';
|
|
25
|
+
options: undefined;
|
|
26
|
+
};
|
|
27
|
+
/** . */
|
|
28
|
+
'stderr': {
|
|
29
|
+
type: 'text';
|
|
30
|
+
options: undefined;
|
|
31
|
+
};
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
type ExecCommandFailedEntity = SchemaEntity<SpruceErrors.RegressionproofSnapshotter.ExecCommandFailedSchema>;
|
|
35
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { SpruceErrors } from "./errors.types";
|
|
2
|
+
import { ErrorOptions as ISpruceErrorOptions } from "@sprucelabs/error";
|
|
3
|
+
export interface ExecCommandFailedErrorOptions extends SpruceErrors.RegressionproofSnapshotter.ExecCommandFailed, ISpruceErrorOptions {
|
|
4
|
+
code: 'EXEC_COMMAND_FAILED';
|
|
5
|
+
}
|
|
6
|
+
type ErrorOptions = ExecCommandFailedErrorOptions;
|
|
7
|
+
export default ErrorOptions;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import BaseSpruceError from '@sprucelabs/error';
|
|
2
|
+
import ErrorOptions from './../.spruce/errors/options.types';
|
|
3
|
+
export default class SpruceError extends BaseSpruceError<ErrorOptions> {
|
|
4
|
+
/** an easy to understand version of the errors */
|
|
5
|
+
friendlyMessage(): string;
|
|
6
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import BaseSpruceError from '@sprucelabs/error';
|
|
2
|
+
export default class SpruceError extends BaseSpruceError {
|
|
3
|
+
/** an easy to understand version of the errors */
|
|
4
|
+
friendlyMessage() {
|
|
5
|
+
var _a, _b;
|
|
6
|
+
const { options } = this;
|
|
7
|
+
let message;
|
|
8
|
+
switch (options === null || options === void 0 ? void 0 : options.code) {
|
|
9
|
+
case 'EXEC_COMMAND_FAILED':
|
|
10
|
+
message = `The command "${options.command}" failed to execute.
|
|
11
|
+
|
|
12
|
+
Stdout:
|
|
13
|
+
${(_a = options.stdout) !== null && _a !== void 0 ? _a : '<no stdout>'}
|
|
14
|
+
|
|
15
|
+
Stderr:
|
|
16
|
+
${(_b = options.stderr) !== null && _b !== void 0 ? _b : '<no stderr>'}`;
|
|
17
|
+
break;
|
|
18
|
+
default:
|
|
19
|
+
message = super.friendlyMessage();
|
|
20
|
+
}
|
|
21
|
+
const fullMessage = options.friendlyMessage
|
|
22
|
+
? options.friendlyMessage
|
|
23
|
+
: message;
|
|
24
|
+
return fullMessage;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
declare const _default: {
|
|
2
|
+
id: string;
|
|
3
|
+
name: string;
|
|
4
|
+
fields: {
|
|
5
|
+
command: {
|
|
6
|
+
type: "text";
|
|
7
|
+
isRequired: true;
|
|
8
|
+
hint: string;
|
|
9
|
+
};
|
|
10
|
+
stdout: {
|
|
11
|
+
type: "text";
|
|
12
|
+
};
|
|
13
|
+
stderr: {
|
|
14
|
+
type: "text";
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
};
|
|
18
|
+
export default _default;
|
package/{src/errors/execCommandFailed.builder.ts → build/esm/errors/execCommandFailed.builder.js}
RENAMED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { buildErrorSchema } from '@sprucelabs/schema'
|
|
2
|
-
|
|
1
|
+
import { buildErrorSchema } from '@sprucelabs/schema';
|
|
3
2
|
export default buildErrorSchema({
|
|
4
3
|
id: 'execCommandFailed',
|
|
5
4
|
name: 'Exec command failed',
|
|
@@ -16,4 +15,4 @@ export default buildErrorSchema({
|
|
|
16
15
|
type: 'text',
|
|
17
16
|
},
|
|
18
17
|
},
|
|
19
|
-
})
|
|
18
|
+
});
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { Log } from '@sprucelabs/spruce-skill-utils';
|
|
2
|
+
import { RemoteOptions } from './snapshotter.types.js';
|
|
3
|
+
export declare function gitCommit(mirrorPath: string, log?: Log): Promise<boolean>;
|
|
4
|
+
export declare function gitPush(mirrorPath: string, remote: RemoteOptions, log?: Log): Promise<void>;
|
package/build/esm/git.js
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
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 { exec } from 'child_process';
|
|
11
|
+
import { existsSync } from 'fs';
|
|
12
|
+
import path from 'path';
|
|
13
|
+
import { promisify } from 'util';
|
|
14
|
+
import SpruceError from './errors/SpruceError.js.js';
|
|
15
|
+
const execAsync = promisify(exec);
|
|
16
|
+
export function gitCommit(mirrorPath, log) {
|
|
17
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
18
|
+
const gitDir = path.join(mirrorPath, '.git');
|
|
19
|
+
if (!existsSync(gitDir)) {
|
|
20
|
+
yield execOrThrow(`git -C "${mirrorPath}" init`, log);
|
|
21
|
+
}
|
|
22
|
+
yield execOrThrow(`git -C "${mirrorPath}" add -A`, log);
|
|
23
|
+
const { stdout } = yield execOrThrow(`git -C "${mirrorPath}" status --porcelain`);
|
|
24
|
+
if (!stdout.trim()) {
|
|
25
|
+
log === null || log === void 0 ? void 0 : log.info('No changes detected for commit', mirrorPath);
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
const message = `Snapshot ${new Date().toISOString()}`;
|
|
29
|
+
yield execOrThrow(`git -C "${mirrorPath}" commit -m "${message}"`, log);
|
|
30
|
+
return true;
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
export function gitPush(mirrorPath, remote, log) {
|
|
34
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
35
|
+
const authedUrl = remote.url.replace('://', `://${remote.token}@`);
|
|
36
|
+
try {
|
|
37
|
+
yield execOrThrow(`git -C "${mirrorPath}" remote get-url origin`, log);
|
|
38
|
+
yield execOrThrow(`git -C "${mirrorPath}" remote set-url origin "${authedUrl}"`);
|
|
39
|
+
}
|
|
40
|
+
catch (_a) {
|
|
41
|
+
yield execOrThrow(`git -C "${mirrorPath}" remote add origin "${authedUrl}"`);
|
|
42
|
+
}
|
|
43
|
+
yield execOrThrow(`git -C "${mirrorPath}" push -u origin HEAD`, log);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
function execOrThrow(command, log) {
|
|
47
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
48
|
+
var _a, _b;
|
|
49
|
+
try {
|
|
50
|
+
return yield execAsync(command);
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
const error = err;
|
|
54
|
+
const stdout = (_a = error.stdout) !== null && _a !== void 0 ? _a : '';
|
|
55
|
+
const stderr = (_b = error.stderr) !== null && _b !== void 0 ? _b : '';
|
|
56
|
+
log === null || log === void 0 ? void 0 : log.error('Command failed', command, stderr);
|
|
57
|
+
throw new SpruceError({
|
|
58
|
+
code: 'EXEC_COMMAND_FAILED',
|
|
59
|
+
command,
|
|
60
|
+
stdout,
|
|
61
|
+
stderr,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,60 @@
|
|
|
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 { snapshot } from '../index.js';
|
|
11
|
+
function main() {
|
|
12
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
13
|
+
const sourcePath = process.env.SOURCE_PATH;
|
|
14
|
+
const mirrorPath = process.env.MIRROR_PATH;
|
|
15
|
+
const remoteUrl = process.env.REMOTE_URL;
|
|
16
|
+
const remoteToken = process.env.REMOTE_TOKEN;
|
|
17
|
+
if (!sourcePath || !mirrorPath || !remoteUrl || !remoteToken) {
|
|
18
|
+
console.error('Missing required env vars. See .env.example');
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
console.log('Running snapshot...');
|
|
22
|
+
console.log(' Source:', sourcePath);
|
|
23
|
+
console.log(' Mirror:', mirrorPath);
|
|
24
|
+
console.log(' Remote:', remoteUrl);
|
|
25
|
+
const result = yield snapshot({
|
|
26
|
+
sourcePath,
|
|
27
|
+
mirrorPath,
|
|
28
|
+
testResults: {
|
|
29
|
+
timestamp: new Date().toISOString(),
|
|
30
|
+
summary: {
|
|
31
|
+
totalSuites: 1,
|
|
32
|
+
passedSuites: 1,
|
|
33
|
+
failedSuites: 0,
|
|
34
|
+
totalTests: 2,
|
|
35
|
+
passedTests: 2,
|
|
36
|
+
failedTests: 0,
|
|
37
|
+
},
|
|
38
|
+
suites: [
|
|
39
|
+
{
|
|
40
|
+
path: 'src/__tests__/Example.test.ts',
|
|
41
|
+
passed: true,
|
|
42
|
+
tests: [
|
|
43
|
+
{ name: 'should work', passed: true },
|
|
44
|
+
{ name: 'should also work', passed: true },
|
|
45
|
+
],
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
},
|
|
49
|
+
remote: {
|
|
50
|
+
url: remoteUrl,
|
|
51
|
+
token: remoteToken,
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
console.log('Snapshot created:', result);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
main().catch((err) => {
|
|
58
|
+
console.error('Error:', err.message);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
});
|
|
@@ -0,0 +1,54 @@
|
|
|
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 { mkdirSync, 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
|
+
class Snapshotter {
|
|
16
|
+
constructor() {
|
|
17
|
+
this.log = buildLog('Snapshotter');
|
|
18
|
+
}
|
|
19
|
+
snapshot(options) {
|
|
20
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
21
|
+
var _a;
|
|
22
|
+
const sourcePath = (_a = options.sourcePath) !== null && _a !== void 0 ? _a : process.cwd();
|
|
23
|
+
const { mirrorPath, testResults, remote } = options;
|
|
24
|
+
this.log.info('Starting snapshot', sourcePath, mirrorPath);
|
|
25
|
+
try {
|
|
26
|
+
yield syncFiles(sourcePath, mirrorPath);
|
|
27
|
+
this.log.info('Files synced', mirrorPath);
|
|
28
|
+
const snapshotterDir = path.join(mirrorPath, '.snapshotter');
|
|
29
|
+
mkdirSync(snapshotterDir, { recursive: true });
|
|
30
|
+
writeFileSync(path.join(snapshotterDir, 'testResults.json'), JSON.stringify(testResults, null, 2));
|
|
31
|
+
this.log.info('Test results saved', snapshotterDir);
|
|
32
|
+
const committed = yield gitCommit(mirrorPath, this.log);
|
|
33
|
+
if (!committed) {
|
|
34
|
+
this.log.info('No changes to commit', mirrorPath);
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
this.log.info('Commit created, pushing', remote.url);
|
|
38
|
+
yield gitPush(mirrorPath, remote, this.log);
|
|
39
|
+
this.log.info('Push completed', remote.url);
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
catch (err) {
|
|
43
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
44
|
+
this.log.error('Snapshot failed', message);
|
|
45
|
+
throw err;
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
export function snapshot(options) {
|
|
51
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
52
|
+
return new Snapshotter().snapshot(options);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
@@ -1,59 +1,54 @@
|
|
|
1
1
|
export interface TestResult {
|
|
2
2
|
/** Name of the individual test */
|
|
3
|
-
name: string
|
|
3
|
+
name: string;
|
|
4
4
|
/** Whether this test passed */
|
|
5
|
-
passed: boolean
|
|
5
|
+
passed: boolean;
|
|
6
6
|
/** Full error message + callstack (only present if failed) */
|
|
7
|
-
error?: string
|
|
7
|
+
error?: string;
|
|
8
8
|
}
|
|
9
|
-
|
|
10
9
|
export interface SuiteResult {
|
|
11
10
|
/** Path to test file, relative to project root */
|
|
12
|
-
path: string
|
|
11
|
+
path: string;
|
|
13
12
|
/** Whether all tests in suite passed */
|
|
14
|
-
passed: boolean
|
|
13
|
+
passed: boolean;
|
|
15
14
|
/** Array of test results */
|
|
16
|
-
tests: TestResult[]
|
|
15
|
+
tests: TestResult[];
|
|
17
16
|
}
|
|
18
|
-
|
|
19
17
|
export interface TestResultsSummary {
|
|
20
18
|
/** Total number of test suites (files) */
|
|
21
|
-
totalSuites: number
|
|
19
|
+
totalSuites: number;
|
|
22
20
|
/** Number of suites with all tests passing */
|
|
23
|
-
passedSuites: number
|
|
21
|
+
passedSuites: number;
|
|
24
22
|
/** Number of suites with at least one failure */
|
|
25
|
-
failedSuites: number
|
|
23
|
+
failedSuites: number;
|
|
26
24
|
/** Total number of individual tests */
|
|
27
|
-
totalTests: number
|
|
25
|
+
totalTests: number;
|
|
28
26
|
/** Number of passing tests */
|
|
29
|
-
passedTests: number
|
|
27
|
+
passedTests: number;
|
|
30
28
|
/** Number of failing tests */
|
|
31
|
-
failedTests: number
|
|
29
|
+
failedTests: number;
|
|
32
30
|
}
|
|
33
|
-
|
|
34
31
|
export interface TestResults {
|
|
35
32
|
/** ISO 8601 timestamp of when test run completed */
|
|
36
|
-
timestamp: string
|
|
33
|
+
timestamp: string;
|
|
37
34
|
/** Summary counts */
|
|
38
|
-
summary: TestResultsSummary
|
|
35
|
+
summary: TestResultsSummary;
|
|
39
36
|
/** Array of suite results */
|
|
40
|
-
suites: SuiteResult[]
|
|
37
|
+
suites: SuiteResult[];
|
|
41
38
|
}
|
|
42
|
-
|
|
43
39
|
export interface RemoteOptions {
|
|
44
40
|
/** Gitea repo URL to push to */
|
|
45
|
-
url: string
|
|
41
|
+
url: string;
|
|
46
42
|
/** Gitea access token for authentication */
|
|
47
|
-
token: string
|
|
43
|
+
token: string;
|
|
48
44
|
}
|
|
49
|
-
|
|
50
45
|
export interface SnapshotOptions {
|
|
51
46
|
/** Source project path to sync from. Defaults to process.cwd() */
|
|
52
|
-
sourcePath?: string
|
|
47
|
+
sourcePath?: string;
|
|
53
48
|
/** Mirror directory path where the isolated git repo lives */
|
|
54
|
-
mirrorPath: string
|
|
49
|
+
mirrorPath: string;
|
|
55
50
|
/** Test results from the completed test run */
|
|
56
|
-
testResults: TestResults
|
|
51
|
+
testResults: TestResults;
|
|
57
52
|
/** Remote Gitea repo to push to */
|
|
58
|
-
remote: RemoteOptions
|
|
53
|
+
remote: RemoteOptions;
|
|
59
54
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function syncFiles(sourcePath: string, mirrorPath: string): Promise<void>;
|
|
@@ -0,0 +1,40 @@
|
|
|
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 { exec } from 'child_process';
|
|
11
|
+
import { existsSync } from 'fs';
|
|
12
|
+
import path from 'path';
|
|
13
|
+
import { promisify } from 'util';
|
|
14
|
+
const execAsync = promisify(exec);
|
|
15
|
+
const DEFAULT_EXCLUDES = [
|
|
16
|
+
'node_modules',
|
|
17
|
+
'build',
|
|
18
|
+
'*.env*',
|
|
19
|
+
'*.pem',
|
|
20
|
+
'*.key',
|
|
21
|
+
'*.p12',
|
|
22
|
+
'*.pfx',
|
|
23
|
+
'*credentials*',
|
|
24
|
+
'*secret*',
|
|
25
|
+
'*.local',
|
|
26
|
+
];
|
|
27
|
+
export function syncFiles(sourcePath, mirrorPath) {
|
|
28
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
29
|
+
const hasGitIgnore = existsSync(path.join(sourcePath, '.gitignore'));
|
|
30
|
+
const excludes = DEFAULT_EXCLUDES.map((e) => `--exclude='${e}'`).join(' ');
|
|
31
|
+
if (hasGitIgnore) {
|
|
32
|
+
const cmd = `cd "${sourcePath}" && git ls-files --cached --others --exclude-standard -z | rsync -av --files-from=- --from0 ${excludes} . "${mirrorPath}/"`;
|
|
33
|
+
yield execAsync(cmd);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
const cmd = `rsync -av --delete ${excludes} "${sourcePath}/" "${mirrorPath}/"`;
|
|
37
|
+
yield execAsync(cmd);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
}
|
package/build/git.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { Log } from '@sprucelabs/spruce-skill-utils';
|
|
2
|
+
import { RemoteOptions } from './snapshotter.types.js';
|
|
3
|
+
export declare function gitCommit(mirrorPath: string, log?: Log): Promise<boolean>;
|
|
4
|
+
export declare function gitPush(mirrorPath: string, remote: RemoteOptions, log?: Log): Promise<void>;
|