@datatruck/cli 0.31.0 → 0.32.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.
@@ -25,10 +25,6 @@ type Context = {
25
25
  snapshot: {
26
26
  id: string;
27
27
  };
28
- prune: {
29
- total: number;
30
- pruned: number;
31
- };
32
28
  task: {
33
29
  taskName: string;
34
30
  packageName: string;
@@ -43,6 +39,11 @@ type Context = {
43
39
  repositoryName: string;
44
40
  mirrorRepositoryName: string;
45
41
  };
42
+ prune: {
43
+ packageName: string;
44
+ total: number;
45
+ pruned: number;
46
+ };
46
47
  report: {
47
48
  type: string;
48
49
  };
@@ -15,6 +15,7 @@ const list_1 = require("../utils/list");
15
15
  const progress_1 = require("../utils/progress");
16
16
  const steps_1 = require("../utils/steps");
17
17
  const temp_1 = require("../utils/temp");
18
+ const PruneAction_1 = require("./PruneAction");
18
19
  const assert_1 = require("assert");
19
20
  const chalk_1 = __importDefault(require("chalk"));
20
21
  const crypto_1 = require("crypto");
@@ -102,7 +103,7 @@ class BackupAction {
102
103
  const renderData = (item, color, result = []) => {
103
104
  const g = (v) => (color ? `${chalk_1.default.gray(`(${v})`)}` : `(${v})`);
104
105
  return item.key === "prune"
105
- ? `${item.data.pruned}/${item.data.total}`
106
+ ? `${item.data.packageName} ${g(`${item.data.pruned}/${item.data.total}`)}`
106
107
  : item.key === "snapshot"
107
108
  ? item.data.id
108
109
  : item.key === "task"
@@ -117,7 +118,14 @@ class BackupAction {
117
118
  backups: result.filter((r) => !r.error && r.key === "backup")
118
119
  .length,
119
120
  copies: result.filter((r) => !r.error && r.key === "copy").length,
120
- })
121
+ prunes: result
122
+ .filter((r) => !r.error && r.key === "prune")
123
+ .reduce((result, item) => {
124
+ if (item.key === "prune")
125
+ result += item.data.pruned;
126
+ return result;
127
+ }, 0),
128
+ }, color)
121
129
  : item.key === "report"
122
130
  ? item.data.type
123
131
  : "";
@@ -285,7 +293,34 @@ class BackupAction {
285
293
  onProgress: (p) => pm.update(p, (t) => (task.output = t)),
286
294
  });
287
295
  },
288
- })));
296
+ })), !!this.options.prune &&
297
+ l.$task({
298
+ title: {
299
+ initial: `Prune: ${pkg.name}`,
300
+ started: `Pruning: ${pkg.name}`,
301
+ completed: `Pruned: ${pkg.name}`,
302
+ failed: `Prune failed: ${pkg.name}`,
303
+ },
304
+ exitOnError: false,
305
+ key: "prune",
306
+ keyIndex: [pkg.name],
307
+ data: {
308
+ pruned: 0,
309
+ total: 0,
310
+ packageName: pkg.name,
311
+ },
312
+ run: async (_, data) => {
313
+ const prune = new PruneAction_1.PruneAction(this.config, {
314
+ repositoryNames: this.options.repositoryNames,
315
+ repositoryTypes: this.options.repositoryTypes,
316
+ packageNames: [pkg.name],
317
+ groupBy: ["packageName", "repositoryName"],
318
+ });
319
+ const result = await prune.exec();
320
+ data.total = result.total;
321
+ data.pruned = result.prune;
322
+ },
323
+ }));
289
324
  }),
290
325
  ...(this.config.reports || []).map((report, index) => {
291
326
  const reportIndex = index + 1;
@@ -312,14 +347,10 @@ class BackupAction {
312
347
  return task.skip(`Report send skipped: ${reportIndex}`);
313
348
  const text = this.dataFormat(result).format(report.format ?? "list");
314
349
  await (0, steps_1.runSteps)(report.run, {
315
- verbose: this.options.verbose,
316
- process: { vars: { DTT_REPORT: text } },
317
- telegram: { vars: { TEXT: text } },
318
- node: {
319
- vars: {
320
- dtt: { report: text, result },
321
- },
350
+ vars: {
351
+ dtt: { title: "DTT Backup", text, result, success },
322
352
  },
353
+ verbose: this.options.verbose,
323
354
  });
324
355
  },
325
356
  });
@@ -11,6 +11,7 @@ export declare class ConfigAction<TRequired extends boolean = true> {
11
11
  static validate(config: ConfigType): void;
12
12
  static check(config: ConfigType): void;
13
13
  static normalize(config: ConfigType): ConfigType;
14
+ static parseFile(path: string): Promise<ConfigType>;
14
15
  static fromGlobalOptionsWithPath(globalOptions: GlobalOptions<true>): Promise<{
15
16
  path: string;
16
17
  data: ConfigType;
@@ -63,6 +63,12 @@ class ConfigAction {
63
63
  });
64
64
  return config;
65
65
  }
66
+ static async parseFile(path) {
67
+ const config = await (0, fs_1.parseFile)(path, "config");
68
+ ConfigAction.validate(config);
69
+ ConfigAction.check(config);
70
+ return ConfigAction.normalize(config);
71
+ }
66
72
  static async fromGlobalOptionsWithPath(globalOptions) {
67
73
  if (typeof globalOptions.config !== "string")
68
74
  return {
@@ -81,13 +87,8 @@ class ConfigAction {
81
87
  }
82
88
  async exec() {
83
89
  const path = await (0, fs_1.findFile)(this.options.path, "datatruck.config", fs_1.parseFileExtensions, "Config path not found");
84
- const config = await (0, fs_1.parseFile)(path, "config");
85
- ConfigAction.validate(config);
86
- ConfigAction.check(config);
87
- return {
88
- path,
89
- data: ConfigAction.normalize(config),
90
- };
90
+ const data = await ConfigAction.parseFile(path);
91
+ return { path, data };
91
92
  }
92
93
  }
93
94
  exports.ConfigAction = ConfigAction;
@@ -40,7 +40,7 @@ class CopyAction {
40
40
  errors: item.data.errors,
41
41
  copied: items.filter((i) => i.key === "copy" && !i.error && !i.data.skipped).length,
42
42
  skipped: items.filter((i) => i.key === "copy" && !i.error && i.data.skipped).length,
43
- })
43
+ }, color)
44
44
  : "";
45
45
  };
46
46
  return new DataFormat_1.DataFormat({
@@ -114,7 +114,7 @@ class RestoreAction {
114
114
  errors: item.data.errors,
115
115
  restores: result.filter((r) => !r.error && r.key === "restore")
116
116
  .length,
117
- })
117
+ }, color)
118
118
  : "";
119
119
  };
120
120
  return new DataFormat_1.DataFormat({
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # @datatruck/cli
2
2
 
3
+ ## 0.32.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [`a479805`](https://github.com/swordev/datatruck/commit/a479805dd5c206d53eb5610f3f9db8cebe3e697a) Thanks [@juanrgm](https://github.com/juanrgm)! - Fix `prune` option in the `backup` command
8
+
9
+ ## 0.32.0
10
+
11
+ ### Minor Changes
12
+
13
+ - [`7d755ba`](https://github.com/swordev/datatruck/commit/7d755bac0edf7aea719446f6bfcee5bea0fe9a90) Thanks [@juanrgm](https://github.com/juanrgm)! - Reload repository server config
14
+
15
+ - [`9dba106`](https://github.com/swordev/datatruck/commit/9dba106865da2d4327282d65deecee5a03e49b49) Thanks [@juanrgm](https://github.com/juanrgm)! - Update to Node.js 20
16
+
17
+ - [`113ee82`](https://github.com/swordev/datatruck/commit/113ee8258951028d54b798eaaa813982222c20e8) Thanks [@juanrgm](https://github.com/juanrgm)! - Add `ntfy` step
18
+
19
+ ### Patch Changes
20
+
21
+ - [`c24eea2`](https://github.com/swordev/datatruck/commit/c24eea21b41d78451d5eabd65923daa26dcad78a) Thanks [@juanrgm](https://github.com/juanrgm)! - Simplify step configs
22
+
3
23
  ## 0.31.0
4
24
 
5
25
  ### Minor Changes
@@ -17,6 +17,7 @@ class StartServerCommand extends CommandAbstract_1.CommandAbstract {
17
17
  if (repositoryOptions.enabled ?? true) {
18
18
  const server = (0, repository_server_1.createDatatruckRepositoryServer)(repositoryOptions, {
19
19
  log,
20
+ configPath: this.configPath,
20
21
  });
21
22
  const port = repositoryOptions.listen?.port ?? 8888;
22
23
  const address = repositoryOptions.listen?.address ?? "127.0.0.1";
@@ -39,6 +40,8 @@ class StartServerCommand extends CommandAbstract_1.CommandAbstract {
39
40
  server.start();
40
41
  console.info(`Cron server started`);
41
42
  }
43
+ process.on("SIGINT", () => process.exit(1));
44
+ process.on("SIGTERM", () => process.exit(1));
42
45
  await new Promise(() => setInterval(() => { }, 60000));
43
46
  return 0;
44
47
  }
@@ -20,22 +20,13 @@ export declare enum ScriptTaskDefinitionEnum {
20
20
  step = "step",
21
21
  processStepConfig = "processStepConfig",
22
22
  nodeStepConfig = "nodeStepConfig",
23
- telegramMessageStepConfig = "telegramMessageStepConfig"
23
+ telegramMessageStepConfig = "telegramMessageStepConfig",
24
+ ntfyStepConfig = "ntfyStepConfig"
24
25
  }
25
26
  export declare const scriptTaskName = "script";
26
27
  export declare const scriptTaskDefinition: JSONSchema7;
27
28
  export declare class ScriptTask extends TaskAbstract<ScriptTaskConfigType> {
28
29
  protected verbose?: boolean;
29
- protected getVars(data: TaskBackupData | TaskRestoreData): {
30
- process: {
31
- DTT_SNAPSHOT_ID: string;
32
- DTT_SNAPSHOT_DATE: string;
33
- DTT_PACKAGE_NAME: string;
34
- DTT_PACKAGE_PATH: string | undefined;
35
- DTT_SNAPSHOT_PATH: string | undefined;
36
- };
37
- node: NodeVars;
38
- };
39
30
  backup(data: TaskBackupData): Promise<{
40
31
  snapshotPath: string;
41
32
  }>;
@@ -15,11 +15,13 @@ var ScriptTaskDefinitionEnum;
15
15
  ScriptTaskDefinitionEnum["processStepConfig"] = "processStepConfig";
16
16
  ScriptTaskDefinitionEnum["nodeStepConfig"] = "nodeStepConfig";
17
17
  ScriptTaskDefinitionEnum["telegramMessageStepConfig"] = "telegramMessageStepConfig";
18
+ ScriptTaskDefinitionEnum["ntfyStepConfig"] = "ntfyStepConfig";
18
19
  })(ScriptTaskDefinitionEnum || (exports.ScriptTaskDefinitionEnum = ScriptTaskDefinitionEnum = {}));
19
20
  const stepTypes = {
20
21
  process: ScriptTaskDefinitionEnum.processStepConfig,
21
22
  node: ScriptTaskDefinitionEnum.nodeStepConfig,
22
23
  "telegram-message": ScriptTaskDefinitionEnum.telegramMessageStepConfig,
24
+ ntfy: ScriptTaskDefinitionEnum.ntfyStepConfig,
23
25
  };
24
26
  exports.scriptTaskName = "script";
25
27
  exports.scriptTaskDefinition = {
@@ -85,6 +87,15 @@ exports.scriptTaskDefinition = {
85
87
  text: { type: "string" },
86
88
  },
87
89
  },
90
+ ntfyStepConfig: {
91
+ type: "object",
92
+ required: ["token"],
93
+ properties: {
94
+ token: { type: "string" },
95
+ topic: { type: "string" },
96
+ text: { type: "string" },
97
+ },
98
+ },
88
99
  },
89
100
  type: "object",
90
101
  additionalProperties: false,
@@ -108,41 +119,22 @@ exports.scriptTaskDefinition = {
108
119
  };
109
120
  class ScriptTask extends TaskAbstract_1.TaskAbstract {
110
121
  verbose;
111
- getVars(data) {
112
- return {
113
- process: {
114
- DTT_SNAPSHOT_ID: data.snapshot.id,
115
- DTT_SNAPSHOT_DATE: data.snapshot.date,
116
- DTT_PACKAGE_NAME: data.package.name,
117
- DTT_PACKAGE_PATH: data.package.path,
118
- DTT_SNAPSHOT_PATH: data.snapshotPath,
119
- },
120
- node: {
121
- dtt: {
122
- snapshot: data.snapshot,
123
- package: data.package,
124
- snapshotPath: data.snapshotPath,
125
- },
126
- },
127
- };
128
- }
129
122
  async backup(data) {
130
123
  const config = this.config;
131
124
  const snapshotPath = data.package.path ??
132
125
  (await (0, temp_1.mkTmpDir)(exports.scriptTaskName, "task", "backup", "snapshot"));
133
- const vars = this.getVars({
134
- ...data,
135
- snapshotPath,
136
- });
137
126
  await (0, steps_1.runSteps)(config.backupSteps, {
138
127
  env: config.env,
128
+ vars: {
129
+ dtt: {
130
+ snapshot: data.snapshot,
131
+ snapshotPath: snapshotPath,
132
+ package: data.package,
133
+ },
134
+ },
139
135
  cwd: snapshotPath,
140
136
  verbose: data.options.verbose,
141
- process: { vars: vars.process },
142
- node: {
143
- tempDir: () => (0, temp_1.mkTmpDir)(exports.scriptTaskName, "task", "backup", "nodeStep"),
144
- vars: vars.node,
145
- },
137
+ tempDir: () => (0, temp_1.mkTmpDir)(exports.scriptTaskName, "task", "backup", "nodeStep"),
146
138
  });
147
139
  return { snapshotPath };
148
140
  }
@@ -154,15 +146,17 @@ class ScriptTask extends TaskAbstract_1.TaskAbstract {
154
146
  }
155
147
  async restore(data) {
156
148
  const config = this.config;
157
- const vars = this.getVars(data);
158
149
  await (0, steps_1.runSteps)(config.restoreSteps, {
159
150
  env: config.env,
160
- verbose: data.options.verbose,
161
- process: { vars: vars.process },
162
- node: {
163
- tempDir: () => (0, temp_1.mkTmpDir)(exports.scriptTaskName, "task", "restore", "nodeStep"),
164
- vars: vars.node,
151
+ vars: {
152
+ dtt: {
153
+ snapshot: data.snapshot,
154
+ snapshotPath: data.snapshotPath,
155
+ package: data.package,
156
+ },
165
157
  },
158
+ verbose: data.options.verbose,
159
+ tempDir: () => (0, temp_1.mkTmpDir)(exports.scriptTaskName, "task", "restore", "nodeStep"),
166
160
  });
167
161
  }
168
162
  }
@@ -1346,7 +1346,8 @@
1346
1346
  "enum": [
1347
1347
  "process",
1348
1348
  "node",
1349
- "telegram-message"
1349
+ "telegram-message",
1350
+ "ntfy"
1350
1351
  ]
1351
1352
  },
1352
1353
  "config": {}
@@ -1408,6 +1409,25 @@
1408
1409
  }
1409
1410
  },
1410
1411
  "else": false
1412
+ },
1413
+ {
1414
+ "if": {
1415
+ "type": "object",
1416
+ "properties": {
1417
+ "type": {
1418
+ "const": "ntfy"
1419
+ }
1420
+ }
1421
+ },
1422
+ "then": {
1423
+ "type": "object",
1424
+ "properties": {
1425
+ "config": {
1426
+ "$ref": "#/definitions/script-task_ntfyStepConfig"
1427
+ }
1428
+ }
1429
+ },
1430
+ "else": false
1411
1431
  }
1412
1432
  ]
1413
1433
  },
@@ -1482,6 +1502,23 @@
1482
1502
  "type": "string"
1483
1503
  }
1484
1504
  }
1505
+ },
1506
+ "script-task_ntfyStepConfig": {
1507
+ "type": "object",
1508
+ "required": [
1509
+ "token"
1510
+ ],
1511
+ "properties": {
1512
+ "token": {
1513
+ "type": "string"
1514
+ },
1515
+ "topic": {
1516
+ "type": "string"
1517
+ },
1518
+ "text": {
1519
+ "type": "string"
1520
+ }
1521
+ }
1485
1522
  }
1486
1523
  },
1487
1524
  "$ref": "#/definitions/config"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datatruck/cli",
3
- "version": "0.31.0",
3
+ "version": "0.32.1",
4
4
  "dependencies": {
5
5
  "@supercharge/promise-pool": "^3.1.0",
6
6
  "ajv": "^8.12.0",
@@ -14,11 +14,11 @@
14
14
  "listr2": "^7.0.1",
15
15
  "micromatch": "^4.0.5",
16
16
  "mysql2": "^3.6.1",
17
- "tty-table": "^4.2.2"
17
+ "tty-table": "^4.2.2",
18
+ "yaml": "^2.3.2"
18
19
  },
19
20
  "optionalDependencies": {
20
- "ts-node": "^10.9.1",
21
- "yaml": "^2.3.2"
21
+ "ts-node": "^10.9.1"
22
22
  },
23
23
  "engine": {
24
24
  "node": ">=16.0.0"
package/pkg.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ declare const pkg: {
2
+ name: string;
3
+ version: string;
4
+ description: string;
5
+ };
6
+ export { pkg };
package/pkg.js ADDED
@@ -0,0 +1,10 @@
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
+ exports.pkg = void 0;
7
+ // @ts-ignore
8
+ const package_json_1 = __importDefault(require("./../package.json"));
9
+ const pkg = package_json_1.default;
10
+ exports.pkg = pkg;
package/utils/cli.d.ts CHANGED
@@ -4,7 +4,7 @@ export declare function renderProgressBar(progress: number, size?: number, subpr
4
4
  export declare function logExec(command: string, argv?: string[], env?: NodeJS.ProcessEnv, logToStderr?: boolean): void;
5
5
  export declare function renderResult(error: Error | null | string | boolean | undefined, color?: boolean): string;
6
6
  export declare function renderError(error: Error | null | string | undefined, verbose?: number): string;
7
- export declare function renderObject(object: Record<string, any>): string;
7
+ export declare function renderObject(object: Record<string, any>, color?: boolean): string;
8
8
  export type OptionsType<T1, T2 extends {
9
9
  [K in keyof T1]: unknown;
10
10
  }> = {
package/utils/cli.js CHANGED
@@ -75,10 +75,10 @@ function renderError(error, verbose) {
75
75
  return chalk_1.default.red(message.trim());
76
76
  }
77
77
  exports.renderError = renderError;
78
- function renderObject(object) {
78
+ function renderObject(object, color) {
79
79
  const values = [];
80
80
  for (const key in object)
81
- values.push(`${key}: ${(0, chalk_2.grey)(object[key])}`);
81
+ values.push(`${key}: ${color ? (0, chalk_2.grey)(object[key]) : object[key]}`);
82
82
  return values.join(` `);
83
83
  }
84
84
  exports.renderObject = renderObject;
@@ -10,7 +10,7 @@ async function parsePaths(values, options) {
10
10
  }
11
11
  else {
12
12
  await (0, steps_1.runSteps)(value, {
13
- node: { tempDir: options.tempDir },
13
+ tempDir: options.tempDir,
14
14
  verbose: options.verbose,
15
15
  onLine: (path) => paths.push(path),
16
16
  });
@@ -24,11 +24,9 @@ async function parseBackupPaths(paths, options) {
24
24
  cwd: options.path,
25
25
  verbose: options.verbose,
26
26
  vars: {
27
- dtt: {
28
- package: options.package,
29
- snapshot: options.snapshot,
30
- path: options.path,
31
- },
27
+ package: options.package,
28
+ snapshot: options.snapshot,
29
+ path: options.path,
32
30
  },
33
31
  });
34
32
  }
@@ -31,7 +31,8 @@ export declare const headerKey: {
31
31
  user: string;
32
32
  password: string;
33
33
  };
34
- export declare function createDatatruckRepositoryServer(options: Omit<DatatruckRepositoryServerOptions, "listen">, config?: {
34
+ export declare function createDatatruckRepositoryServer(inOptions: Omit<DatatruckRepositoryServerOptions, "listen">, config?: {
35
35
  log?: boolean;
36
+ configPath?: string;
36
37
  }): import("node:http").Server<typeof IncomingMessage, typeof import("node:http").ServerResponse>;
37
38
  export {};
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createDatatruckRepositoryServer = exports.headerKey = void 0;
4
+ const ConfigAction_1 = require("../../Action/ConfigAction");
4
5
  const http_1 = require("../http");
5
6
  const virtual_fs_1 = require("../virtual-fs");
6
7
  const fs_1 = require("fs");
@@ -55,7 +56,7 @@ const getRemoteAddress = (req, options) => {
55
56
  : req.headers[options.trustProxy.remoteAddressHeader]?.toString()
56
57
  : undefined) ?? req.socket.remoteAddress);
57
58
  };
58
- function createDatatruckRepositoryServer(options, config = {}) {
59
+ function createDatatruckRepositoryServer(inOptions, config = {}) {
59
60
  return (0, http_2.createServer)(async (req, res) => {
60
61
  try {
61
62
  if (req.url === "/" || req.url === "/favicon.ico")
@@ -65,6 +66,10 @@ function createDatatruckRepositoryServer(options, config = {}) {
65
66
  res.statusCode = 404;
66
67
  return res.end();
67
68
  }
69
+ const fileOptions = config.configPath
70
+ ? (await ConfigAction_1.ConfigAction.parseFile(config.configPath)).server?.repository
71
+ : undefined;
72
+ const options = fileOptions ?? inOptions;
68
73
  const backend = findRepositoryBackend(req, repository, options);
69
74
  if (!backend) {
70
75
  res.statusCode = 401;
package/utils/fs.js CHANGED
@@ -4,9 +4,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.asFile = exports.groupFiles = exports.ensureFreeDiskSpace = exports.checkFreeDiskSpace = exports.fetchDiskStats = exports.initEmptyDir = exports.tryRm = exports.safeRename = exports.fetchData = exports.countFileLines = exports.createWriteStreamPool = exports.createFileScanner = exports.createProgress = exports.cpy = exports.isNotFoundError = exports.updateFileStats = exports.copyFileWithStreams = exports.waitForClose = exports.writeGitIgnoreList = exports.fastglobToGitIgnore = exports.forEachFile = exports.readDir = exports.readPartialFile = exports.fastFolderSizeAsync = exports.findFile = exports.parsePackageFile = exports.parseFile = exports.parseFileExtensions = exports.writeJSONFile = exports.existsFile = exports.existsDir = exports.safeStat = exports.ensureExistsDir = exports.ensureSingleFile = exports.ensureEmptyDir = exports.mkdirIfNotExists = exports.isLocalDir = exports.isEmptyDir = exports.isWSLSystem = void 0;
7
+ const pkg_1 = require("../pkg");
7
8
  const bytes_1 = require("./bytes");
8
9
  const math_1 = require("./math");
9
- const path_1 = require("./path");
10
10
  const string_1 = require("./string");
11
11
  const temp_1 = require("./temp");
12
12
  const async_1 = require("async");
@@ -16,8 +16,8 @@ const fs_1 = require("fs");
16
16
  const fs_2 = require("fs");
17
17
  const promises_1 = require("fs/promises");
18
18
  const os_1 = require("os");
19
+ const path_1 = require("path");
19
20
  const path_2 = require("path");
20
- const path_3 = require("path");
21
21
  const readline_1 = require("readline");
22
22
  const util_1 = require("util");
23
23
  exports.isWSLSystem = (0, os_1.release)().includes("microsoft-standard-WSL");
@@ -59,12 +59,12 @@ async function ensureSingleFile(path) {
59
59
  if (files.length !== 1)
60
60
  throw new Error(`Dir has not one file: ${files.length}`);
61
61
  const [file] = files;
62
- return (0, path_2.join)(path, file);
62
+ return (0, path_1.join)(path, file);
63
63
  }
64
64
  exports.ensureSingleFile = ensureSingleFile;
65
65
  async function ensureExistsDir(path) {
66
66
  if (!(await existsDir(path)))
67
- throw new Error(`Dir is not crated: ${path}`);
67
+ throw new Error(`Dir is not created: ${path}`);
68
68
  }
69
69
  exports.ensureExistsDir = ensureExistsDir;
70
70
  async function safeStat(path) {
@@ -88,10 +88,11 @@ async function writeJSONFile(path, json) {
88
88
  exports.writeJSONFile = writeJSONFile;
89
89
  exports.parseFileExtensions = ["json", "js", "ts", "yaml", "yml"];
90
90
  async function parseFile(path, jsKey) {
91
- if (!(0, path_3.isAbsolute)(path))
92
- path = (0, path_2.join)(process.cwd(), path);
91
+ if (!(0, path_2.isAbsolute)(path))
92
+ path = (0, path_1.join)(process.cwd(), path);
93
+ const $require = require;
93
94
  if (path.endsWith(".ts"))
94
- require("ts-node").register();
95
+ $require("ts-node").register();
95
96
  if (path.endsWith(".yaml") || path.endsWith("yml")) {
96
97
  const contents = await (0, promises_1.readFile)(path);
97
98
  return require("yaml").parse(contents.toString());
@@ -107,7 +108,7 @@ async function parseFile(path, jsKey) {
107
108
  }
108
109
  exports.parseFile = parseFile;
109
110
  function parsePackageFile() {
110
- return require(`${path_1.rootPath}/package.json`);
111
+ return pkg_1.pkg;
111
112
  }
112
113
  exports.parsePackageFile = parsePackageFile;
113
114
  async function findFile(sourcePath, baseName, extensions, errorMessage = "Path not found") {
@@ -115,7 +116,7 @@ async function findFile(sourcePath, baseName, extensions, errorMessage = "Path n
115
116
  let path;
116
117
  if (info.isDirectory()) {
117
118
  for (const ext of extensions) {
118
- const extPath = (0, path_2.join)(sourcePath, baseName) + "." + ext;
119
+ const extPath = (0, path_1.join)(sourcePath, baseName) + "." + ext;
119
120
  if (await existsFile(extPath)) {
120
121
  path = extPath;
121
122
  break;
@@ -183,7 +184,7 @@ exports.readDir = readDir;
183
184
  async function forEachFile(dirPath, cb, includeDir) {
184
185
  const files = await readDir(dirPath);
185
186
  for (const file of files) {
186
- const filePath = (0, path_2.join)(dirPath, file);
187
+ const filePath = (0, path_1.join)(dirPath, file);
187
188
  if ((await (0, promises_1.stat)(filePath)).isDirectory()) {
188
189
  if (includeDir)
189
190
  cb(filePath, true);
@@ -206,12 +207,12 @@ function fastglobToGitIgnore(patterns, baseDir) {
206
207
  exports.fastglobToGitIgnore = fastglobToGitIgnore;
207
208
  async function writeGitIgnoreList(options) {
208
209
  const { outDir } = options;
209
- const path = (0, path_2.join)(outDir, `.gitignore`);
210
+ const path = (0, path_1.join)(outDir, `.gitignore`);
210
211
  const stream = (0, fs_2.createWriteStream)(path);
211
212
  const dirs = new Set();
212
213
  stream.write("*\n");
213
214
  for await (const value of options.paths) {
214
- const dir = (0, path_2.dirname)(value.toString());
215
+ const dir = (0, path_1.dirname)(value.toString());
215
216
  if (dir !== ".") {
216
217
  let parentPath;
217
218
  for (const value of dir.split("/")) {
@@ -290,9 +291,9 @@ async function cpy(options) {
290
291
  const task = async (rawEntryPath, basePath) => {
291
292
  [rawEntryPath] = rawEntryPath.split(":");
292
293
  const isDir = rawEntryPath.endsWith("/");
293
- const entryPath = (0, path_2.normalize)(rawEntryPath);
294
- const entrySourcePath = (0, path_2.resolve)((0, path_2.join)(basePath, rawEntryPath));
295
- const entryTargetPath = (0, path_2.resolve)((0, path_2.join)(options.outPath, rawEntryPath));
294
+ const entryPath = (0, path_1.normalize)(rawEntryPath);
295
+ const entrySourcePath = (0, path_1.resolve)((0, path_1.join)(basePath, rawEntryPath));
296
+ const entryTargetPath = (0, path_1.resolve)((0, path_1.join)(options.outPath, rawEntryPath));
296
297
  const onPathResult = await options?.onPath?.({
297
298
  isDir,
298
299
  entryPath,
@@ -307,7 +308,7 @@ async function cpy(options) {
307
308
  await makeRecursiveDir(entryTargetPath);
308
309
  }
309
310
  else {
310
- const dir = (0, path_2.dirname)(entryTargetPath);
311
+ const dir = (0, path_1.dirname)(entryTargetPath);
311
312
  await makeRecursiveDir(dir);
312
313
  await options.onProgress?.({
313
314
  current: stats.files,
@@ -447,7 +448,7 @@ function createWriteStreamPool(options) {
447
448
  const item = {
448
449
  key,
449
450
  lines: 0,
450
- stream: (0, fs_2.createWriteStream)((0, path_2.join)(options.path, options.onStreamPath ? options.onStreamPath(key) : key)),
451
+ stream: (0, fs_2.createWriteStream)((0, path_1.join)(options.path, options.onStreamPath ? options.onStreamPath(key) : key)),
451
452
  finished: false,
452
453
  };
453
454
  item.stream
@@ -612,7 +613,7 @@ exports.groupFiles = groupFiles;
612
613
  async function asFile(input) {
613
614
  if (typeof input === "string") {
614
615
  const dir = await (0, temp_1.mkTmpDir)("text-as-file");
615
- const path = (0, path_2.join)(dir, "contents.txt");
616
+ const path = (0, path_1.join)(dir, "contents.txt");
616
617
  await (0, promises_1.writeFile)(path, input);
617
618
  return [
618
619
  path,
package/utils/steps.d.ts CHANGED
@@ -14,6 +14,11 @@ export type TelegramMessageStepConfig = {
14
14
  chatId: number;
15
15
  text?: string;
16
16
  };
17
+ export type NtfyStepConfig = {
18
+ token: string;
19
+ topic?: string;
20
+ text?: string;
21
+ };
17
22
  export type Step = {
18
23
  type: "process";
19
24
  config: ProcessStepConfig;
@@ -23,20 +28,15 @@ export type Step = {
23
28
  } | {
24
29
  type: "telegram-message";
25
30
  config: TelegramMessageStepConfig;
31
+ } | {
32
+ type: "ntfy";
33
+ config: NtfyStepConfig;
26
34
  };
27
35
  export type StepOptions = {
28
36
  env?: Record<string, string | undefined>;
29
- process?: {
30
- vars?: Record<string, string | undefined>;
31
- };
32
- node?: {
33
- vars?: Record<string, any>;
34
- tempDir?: () => Promise<string>;
35
- };
36
- telegram?: {
37
- vars?: Record<string, string>;
38
- };
37
+ vars?: Record<string, any>;
39
38
  cwd?: string;
39
+ tempDir?: () => Promise<string>;
40
40
  onLine?: (p: string) => any;
41
41
  verbose?: boolean;
42
42
  };
package/utils/steps.js CHANGED
@@ -9,9 +9,10 @@ const promises_1 = require("fs/promises");
9
9
  const path_1 = require("path");
10
10
  async function runSteps(input, options) {
11
11
  const steps = Array.isArray(input) ? input : [input];
12
+ const vars = options?.vars || {};
12
13
  for (const step of steps) {
13
14
  if (step.type === "process") {
14
- await (0, process_1.exec)(step.config.command, (step.config.args || []).map((v) => (0, string_1.render)(v, options.process?.vars || {})), {
15
+ await (0, process_1.exec)(step.config.command, (step.config.args || []).map((v) => (0, string_1.render)(v, vars)), {
15
16
  cwd: options.cwd,
16
17
  env: {
17
18
  ...process.env,
@@ -30,19 +31,19 @@ async function runSteps(input, options) {
30
31
  }
31
32
  else if (step.type === "node") {
32
33
  let tempDir;
33
- if (options.node?.tempDir) {
34
- tempDir = await options.node.tempDir();
34
+ if (options?.tempDir) {
35
+ tempDir = await options.tempDir();
35
36
  }
36
37
  else {
37
38
  tempDir = await (0, temp_1.mkTmpDir)("node-step");
38
39
  }
39
40
  const scriptPath = (0, path_1.join)(tempDir, "script.js");
40
- const vars = {
41
+ const nodeVars = {
41
42
  ...step.config.vars,
42
- ...options.node?.vars,
43
+ ...vars,
43
44
  };
44
- const varKeys = Object.keys(vars);
45
- const varJson = JSON.stringify(vars);
45
+ const varKeys = Object.keys(nodeVars);
46
+ const varJson = JSON.stringify(nodeVars);
46
47
  const code = Array.isArray(step.config.code)
47
48
  ? [...step.config.code].join(";\n")
48
49
  : step.config.code;
@@ -67,7 +68,7 @@ async function runSteps(input, options) {
67
68
  else if (step.type === "telegram-message") {
68
69
  await (0, http_1.post)(`https://api.telegram.org/bot${step.config.bot}/sendMessage`, JSON.stringify({
69
70
  chat_id: step.config.chatId.toString(),
70
- text: (0, string_1.render)(step.config.text ?? `{TEXT}`, options.telegram?.vars || {}),
71
+ text: (0, string_1.render)(step.config.text ?? `{dtt.text}`, vars),
71
72
  disable_notification: true,
72
73
  }), {
73
74
  headers: {
@@ -75,6 +76,19 @@ async function runSteps(input, options) {
75
76
  },
76
77
  });
77
78
  }
79
+ else if (step.type === "ntfy") {
80
+ const topic = [step.config.token, step.config.topic]
81
+ .filter(Boolean)
82
+ .join("-");
83
+ if (topic.length < 32)
84
+ throw new Error(`Topic is less than 32 characters: ${topic}`);
85
+ await (0, http_1.post)(`https://ntfy.sh/${topic}`, (0, string_1.render)(step.config.text ?? `{dtt.text}`, vars), {
86
+ headers: {
87
+ Title: (0, string_1.render)("{dtt.title}", vars),
88
+ Priority: vars.success ? "default" : "high",
89
+ },
90
+ });
91
+ }
78
92
  else {
79
93
  throw new Error(`Invalid step type: ${step.type}`);
80
94
  }
package/utils/string.js CHANGED
@@ -8,17 +8,33 @@ function snakeCase(value, char = "_") {
8
8
  }
9
9
  exports.snakeCase = snakeCase;
10
10
  function render(subject, vars) {
11
- return subject.replace(/{([\w/]*)}/g, function (match, name) {
11
+ return subject.replace(/{([\w\./]*)}/g, function (match, name) {
12
12
  if (!name.length) {
13
13
  return "{";
14
14
  }
15
15
  else if (name === "/") {
16
16
  return "}";
17
17
  }
18
- const value = vars[name];
19
- if (typeof value === "undefined")
20
- throw new AppError_1.AppError(`Variable is not defined: '${subject}' (${name})`);
21
- return value;
18
+ let ref = vars;
19
+ for (const key of name.split(".")) {
20
+ if (!!ref && typeof ref === "object") {
21
+ ref = ref[key];
22
+ }
23
+ else {
24
+ ref = undefined;
25
+ break;
26
+ }
27
+ }
28
+ if (typeof ref !== "string" &&
29
+ typeof ref !== "number" &&
30
+ typeof ref !== "boolean")
31
+ throw new Error(`Variable is not valid: ${name}`, {
32
+ cause: {
33
+ vars,
34
+ value: ref,
35
+ },
36
+ });
37
+ return ref.toString();
22
38
  });
23
39
  }
24
40
  exports.render = render;