@datatruck/cli 0.0.5 → 0.2.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.
@@ -19,6 +19,7 @@ export declare type SnapshotsActionOptionsType = {
19
19
  groupBy?: SnapshotGroupByType[];
20
20
  };
21
21
  export declare type SnapshotExtendedType = {
22
+ shortId: string;
22
23
  repositoryName: string;
23
24
  repositoryType: string;
24
25
  } & SnapshotResultType;
@@ -23,6 +23,7 @@ class SnapshotsAction {
23
23
  });
24
24
  const extentedItems = snapshots.map((item) => ({
25
25
  ...item,
26
+ shortId: item.id.slice(0, 8),
26
27
  repositoryName: repo.name,
27
28
  repositoryType: repo.type,
28
29
  }));
package/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # @datatruck/cli
2
2
 
3
+ ## 0.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [`120460c`](https://github.com/swordev/datatruck/commit/120460c8824cef4184e43f571a4cc0798b899b66) Thanks [@juanrgm](https://github.com/juanrgm)! - Enable `include` option in restic repository
8
+
9
+ ### Patch Changes
10
+
11
+ - [`e30ede3`](https://github.com/swordev/datatruck/commit/e30ede371bc7ab3fc1cd47758fdac7a28e8e2705) Thanks [@juanrgm](https://github.com/juanrgm)! - Resolve `RESTIC_PASSWORD_FILE` path
12
+
13
+ * [`8539d28`](https://github.com/swordev/datatruck/commit/8539d285b2c51d700aa811cd772d573fa0d613eb) Thanks [@juanrgm](https://github.com/juanrgm)! - Allow empty backup in restic repository
14
+
15
+ ## 0.1.0
16
+
17
+ ### Minor Changes
18
+
19
+ - [`88d46cd`](https://github.com/swordev/datatruck/commit/88d46cd56293df4c6fc21a9ad61d6236ac91f325) Thanks [@juanrgm](https://github.com/juanrgm)! - Add `custom` output format
20
+
21
+ ### Patch Changes
22
+
23
+ - [`24a1e5e`](https://github.com/swordev/datatruck/commit/24a1e5e86336e7a92556287e49548dc542f0e579) Thanks [@juanrgm](https://github.com/juanrgm)! - Update dependencies
24
+
25
+ ## 0.0.6
26
+
27
+ ### Patch Changes
28
+
29
+ - [`8de6e6c`](https://github.com/swordev/datatruck/commit/8de6e6ceddb59635cb4634d884e7690eeaf59bac) Thanks [@juanrgm](https://github.com/juanrgm)! - Publish migrations
30
+
3
31
  ## 0.0.5
4
32
 
5
33
  ### Patch Changes
@@ -8,15 +8,12 @@ const string_util_1 = require("../util/string-util");
8
8
  const CommandAbstract_1 = require("./CommandAbstract");
9
9
  class SnapshotsCommand extends CommandAbstract_1.CommandAbstract {
10
10
  onOptions() {
11
+ const groupByValues = ["packageName", "repositoryName", "repositoryType"];
11
12
  return this.returnsOptions({
12
13
  groupBy: {
13
14
  option: "-g,--group-by <values>",
14
- description: "Group by values",
15
- parser: (v) => (0, string_util_1.parseStringList)(v, [
16
- "packageName",
17
- "repositoryName",
18
- "repositoryType",
19
- ]),
15
+ description: `Group by values (${groupByValues.join(", ")})`,
16
+ parser: (v) => (0, string_util_1.parseStringList)(v, groupByValues),
20
17
  },
21
18
  id: {
22
19
  option: "-i,--id <ids>",
package/Config/Config.js CHANGED
@@ -7,6 +7,7 @@ exports.configDefinition = {
7
7
  required: ["repositories", "packages"],
8
8
  additionalProperties: false,
9
9
  properties: {
10
+ $schema: { type: "string" },
10
11
  repositories: {
11
12
  type: "array",
12
13
  items: (0, DefinitionEnum_1.makeRef)(DefinitionEnum_1.DefinitionEnum.repository),
@@ -1,13 +1,18 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
6
  exports.ResticRepository = exports.resticPackageRepositoryDefinition = exports.resticRepositoryDefinition = exports.resticRepositoryName = void 0;
4
7
  const AppError_1 = require("../Error/AppError");
5
8
  const ResticUtil_1 = require("../util/ResticUtil");
9
+ const cli_util_1 = require("../util/cli-util");
6
10
  const paths_util_1 = require("../util/datatruck/paths-util");
7
11
  const fs_util_1 = require("../util/fs-util");
8
12
  const string_util_1 = require("../util/string-util");
9
13
  const RepositoryAbstract_1 = require("./RepositoryAbstract");
10
14
  const assert_1 = require("assert");
15
+ const fast_glob_1 = __importDefault(require("fast-glob"));
11
16
  const micromatch_1 = require("micromatch");
12
17
  const path_1 = require("path");
13
18
  exports.resticRepositoryName = "restic";
@@ -54,7 +59,7 @@ class ResticRepository extends RepositoryAbstract_1.RepositoryAbstract {
54
59
  if (this.env)
55
60
  return this.env;
56
61
  return (this.env = {
57
- RESTIC_PASSWORD_FILE: this.config.passwordFile,
62
+ RESTIC_PASSWORD_FILE: (0, path_1.resolve)(this.config.passwordFile),
58
63
  RESTIC_REPOSITORY: await ResticUtil_1.ResticUtil.formatRepository(this.config.repository),
59
64
  });
60
65
  }
@@ -150,14 +155,28 @@ class ResticRepository extends RepositoryAbstract_1.RepositoryAbstract {
150
155
  const pkg = data.package;
151
156
  const sourcePath = data.targetPath ?? data.package.path;
152
157
  (0, assert_1.ok)(sourcePath);
153
- const include = (await (0, paths_util_1.parsePaths)(pkg.include ?? [], {
158
+ const include = await (0, paths_util_1.parsePaths)(pkg.include ?? ["**"], {
154
159
  cwd: sourcePath,
155
160
  verbose: data.options.verbose,
156
- })).map(path_1.normalize);
157
- // https://github.com/restic/restic/issues/233
158
- // https://github.com/restic/restic/pull/2311
159
- if (include.length)
160
- throw new AppError_1.AppError(`Include is not supported`);
161
+ });
162
+ const exclude = pkg.exclude
163
+ ? await (0, paths_util_1.parsePaths)(pkg.exclude, {
164
+ cwd: sourcePath,
165
+ verbose: data.options.verbose,
166
+ })
167
+ : undefined;
168
+ const stream = fast_glob_1.default.stream(include, {
169
+ cwd: sourcePath,
170
+ ignore: exclude,
171
+ dot: true,
172
+ onlyFiles: true,
173
+ markDirectories: true,
174
+ });
175
+ if (data.options.verbose)
176
+ (0, cli_util_1.logExec)(`Writing paths lists`);
177
+ const gitignorePath = await (0, fs_util_1.writeGitIgnoreList)({
178
+ paths: stream,
179
+ });
161
180
  if (data.options.tags?.some((tag) => tag.startsWith(ResticRepository.refPrefix)))
162
181
  throw new AppError_1.AppError(`Tag prefix is not allowed`);
163
182
  const packageTag = ResticRepository.buildSnapshotTag(RepositoryAbstract_1.SnapshotTagEnum.PACKAGE, data.package.name);
@@ -170,15 +189,13 @@ class ResticRepository extends RepositoryAbstract_1.RepositoryAbstract {
170
189
  await restic.backup({
171
190
  cwd: sourcePath,
172
191
  paths: ["."],
192
+ allowEmptySnapshot: true,
193
+ excludeFile: [gitignorePath],
173
194
  parent: lastSnapshot?.id,
174
195
  // https://github.com/restic/restic/pull/3200
175
196
  ...((await restic.checkBackupSetPathSupport()) && {
176
197
  setPaths: [`/datatruck/${data.package.name}`],
177
198
  }),
178
- exclude: (await (0, paths_util_1.parsePaths)(pkg.exclude ?? [], {
179
- cwd: sourcePath,
180
- verbose: data.options.verbose,
181
- })).map(path_1.normalize),
182
199
  tags: [
183
200
  ResticRepository.buildSnapshotTag(RepositoryAbstract_1.SnapshotTagEnum.ID, data.snapshot.id),
184
201
  ResticRepository.buildSnapshotTag(RepositoryAbstract_1.SnapshotTagEnum.SHORT_ID, data.snapshot.id.slice(0, 8)),
package/cli.js CHANGED
@@ -63,7 +63,7 @@ program.version(version);
63
63
  program.description(description);
64
64
  program.option("-v,--verbose", "Verbose", (_, previous) => previous + 1, 0);
65
65
  program.option("-c,--config <path>", "Config path", process.env["DATATRUCK_CONFIG"] ?? (cwd.endsWith(path_1.sep) ? cwd : `${cwd}${path_1.sep}`));
66
- program.option("-o,--output-format <format>", "Output format (json, pjson, yaml, table)", "table");
66
+ program.option("-o,--output-format <format>", "Output format (json, pjson, yaml, table, custom=$)", "table");
67
67
  makeCommand(CommandFactory_1.CommandEnum.config).alias("c");
68
68
  makeCommand(CommandFactory_1.CommandEnum.init).alias("i");
69
69
  makeCommand(CommandFactory_1.CommandEnum.snapshots).alias("s");
@@ -0,0 +1,816 @@
1
+ {
2
+ "definitions": {
3
+ "stringlist-util": {
4
+ "type": "array",
5
+ "items": {
6
+ "type": "string"
7
+ }
8
+ },
9
+ "repository": {
10
+ "type": "object",
11
+ "additionalProperties": false,
12
+ "required": [
13
+ "type",
14
+ "name"
15
+ ],
16
+ "properties": {
17
+ "type": {
18
+ "type": "string"
19
+ },
20
+ "name": {
21
+ "type": "string"
22
+ },
23
+ "config": {}
24
+ },
25
+ "anyOf": [
26
+ {
27
+ "if": {
28
+ "type": "object",
29
+ "properties": {
30
+ "type": {
31
+ "const": "restic"
32
+ }
33
+ }
34
+ },
35
+ "then": {
36
+ "type": "object",
37
+ "properties": {
38
+ "config": {
39
+ "$ref": "#/definitions/restic-repository"
40
+ }
41
+ }
42
+ },
43
+ "else": false
44
+ },
45
+ {
46
+ "if": {
47
+ "type": "object",
48
+ "properties": {
49
+ "type": {
50
+ "const": "local"
51
+ }
52
+ }
53
+ },
54
+ "then": {
55
+ "type": "object",
56
+ "properties": {
57
+ "config": {
58
+ "$ref": "#/definitions/local-repository"
59
+ }
60
+ }
61
+ },
62
+ "else": false
63
+ },
64
+ {
65
+ "if": {
66
+ "type": "object",
67
+ "properties": {
68
+ "type": {
69
+ "const": "git"
70
+ }
71
+ }
72
+ },
73
+ "then": {
74
+ "type": "object",
75
+ "properties": {
76
+ "config": {
77
+ "$ref": "#/definitions/git-repository"
78
+ }
79
+ }
80
+ },
81
+ "else": false
82
+ }
83
+ ]
84
+ },
85
+ "package": {
86
+ "type": "object",
87
+ "required": [
88
+ "name"
89
+ ],
90
+ "additionalProperties": false,
91
+ "properties": {
92
+ "name": {
93
+ "type": "string"
94
+ },
95
+ "enabled": {
96
+ "type": "boolean"
97
+ },
98
+ "task": {
99
+ "$ref": "#/definitions/task"
100
+ },
101
+ "path": {
102
+ "type": "string"
103
+ },
104
+ "restorePath": {
105
+ "type": "string"
106
+ },
107
+ "restorePermissions": {
108
+ "type": "object",
109
+ "required": [
110
+ "uid",
111
+ "gid"
112
+ ],
113
+ "additionalProperties": false,
114
+ "properties": {
115
+ "uid": {
116
+ "anyOf": [
117
+ {
118
+ "type": "string"
119
+ },
120
+ {
121
+ "type": "integer"
122
+ }
123
+ ]
124
+ },
125
+ "gid": {
126
+ "anyOf": [
127
+ {
128
+ "type": "string"
129
+ },
130
+ {
131
+ "type": "integer"
132
+ }
133
+ ]
134
+ }
135
+ }
136
+ },
137
+ "include": {
138
+ "type": "array",
139
+ "items": {
140
+ "anyOf": [
141
+ {
142
+ "type": "string"
143
+ },
144
+ {
145
+ "$ref": "#/definitions/paths-object"
146
+ }
147
+ ]
148
+ }
149
+ },
150
+ "exclude": {
151
+ "type": "array",
152
+ "items": {
153
+ "anyOf": [
154
+ {
155
+ "type": "string"
156
+ },
157
+ {
158
+ "$ref": "#/definitions/paths-object"
159
+ }
160
+ ]
161
+ }
162
+ },
163
+ "repositoryNames": {
164
+ "$ref": "#/definitions/stringlist-util"
165
+ },
166
+ "repositoryConfigs": {
167
+ "type": "array",
168
+ "items": {
169
+ "$ref": "#/definitions/package-repository"
170
+ }
171
+ },
172
+ "prunePolicy": {
173
+ "$ref": "#/definitions/prune-policy"
174
+ }
175
+ }
176
+ },
177
+ "package-repository": {
178
+ "type": "object",
179
+ "additionalProperties": false,
180
+ "required": [
181
+ "type"
182
+ ],
183
+ "properties": {
184
+ "type": {
185
+ "type": "string"
186
+ },
187
+ "names": {
188
+ "$ref": "#/definitions/stringlist-util"
189
+ },
190
+ "config": {}
191
+ },
192
+ "anyOf": [
193
+ {
194
+ "if": {
195
+ "type": "object",
196
+ "properties": {
197
+ "type": {
198
+ "const": "restic"
199
+ }
200
+ }
201
+ },
202
+ "then": {
203
+ "type": "object",
204
+ "properties": {
205
+ "config": {
206
+ "$ref": "#/definitions/restic-package-repository"
207
+ }
208
+ }
209
+ },
210
+ "else": false
211
+ },
212
+ {
213
+ "if": {
214
+ "type": "object",
215
+ "properties": {
216
+ "type": {
217
+ "const": "local"
218
+ }
219
+ }
220
+ },
221
+ "then": {
222
+ "type": "object",
223
+ "properties": {
224
+ "config": {
225
+ "$ref": "#/definitions/local-package-repository"
226
+ }
227
+ }
228
+ },
229
+ "else": false
230
+ },
231
+ {
232
+ "if": {
233
+ "type": "object",
234
+ "properties": {
235
+ "type": {
236
+ "const": "git"
237
+ }
238
+ }
239
+ },
240
+ "then": {
241
+ "type": "object",
242
+ "properties": {
243
+ "config": {
244
+ "$ref": "#/definitions/git-package-repository"
245
+ }
246
+ }
247
+ },
248
+ "else": false
249
+ }
250
+ ]
251
+ },
252
+ "task": {
253
+ "type": "object",
254
+ "required": [
255
+ "name"
256
+ ],
257
+ "properties": {
258
+ "name": {
259
+ "enum": [
260
+ "git",
261
+ "mariadb",
262
+ "mssql",
263
+ "mysql-dump",
264
+ "postgresql-dump"
265
+ ]
266
+ },
267
+ "config": {}
268
+ },
269
+ "anyOf": [
270
+ {
271
+ "if": {
272
+ "type": "object",
273
+ "properties": {
274
+ "name": {
275
+ "const": "git"
276
+ }
277
+ }
278
+ },
279
+ "then": {
280
+ "type": "object",
281
+ "properties": {
282
+ "config": {
283
+ "$ref": "#/definitions/git-task"
284
+ }
285
+ }
286
+ },
287
+ "else": false
288
+ },
289
+ {
290
+ "if": {
291
+ "type": "object",
292
+ "properties": {
293
+ "name": {
294
+ "const": "mariadb"
295
+ }
296
+ }
297
+ },
298
+ "then": {
299
+ "type": "object",
300
+ "properties": {
301
+ "config": {
302
+ "$ref": "#/definitions/mariadb-task"
303
+ }
304
+ }
305
+ },
306
+ "else": false
307
+ },
308
+ {
309
+ "if": {
310
+ "type": "object",
311
+ "properties": {
312
+ "name": {
313
+ "const": "mssql"
314
+ }
315
+ }
316
+ },
317
+ "then": {
318
+ "type": "object",
319
+ "properties": {
320
+ "config": {
321
+ "$ref": "#/definitions/mssql-task"
322
+ }
323
+ }
324
+ },
325
+ "else": false
326
+ },
327
+ {
328
+ "if": {
329
+ "type": "object",
330
+ "properties": {
331
+ "name": {
332
+ "const": "mysql-dump"
333
+ }
334
+ }
335
+ },
336
+ "then": {
337
+ "type": "object",
338
+ "properties": {
339
+ "config": {
340
+ "$ref": "#/definitions/mysql-dump-task"
341
+ }
342
+ }
343
+ },
344
+ "else": false
345
+ },
346
+ {
347
+ "if": {
348
+ "type": "object",
349
+ "properties": {
350
+ "name": {
351
+ "const": "postgresql-dump"
352
+ }
353
+ }
354
+ },
355
+ "then": {
356
+ "type": "object",
357
+ "properties": {
358
+ "config": {
359
+ "$ref": "#/definitions/postgresql-dump-task"
360
+ }
361
+ }
362
+ },
363
+ "else": false
364
+ }
365
+ ]
366
+ },
367
+ "git-repository": {
368
+ "type": "object",
369
+ "additionalProperties": false,
370
+ "required": [
371
+ "repo"
372
+ ],
373
+ "properties": {
374
+ "repo": {
375
+ "type": "string"
376
+ },
377
+ "branch": {
378
+ "type": "string"
379
+ }
380
+ }
381
+ },
382
+ "git-package-repository": {
383
+ "type": "object",
384
+ "additionalProperties": false,
385
+ "properties": {}
386
+ },
387
+ "local-repository": {
388
+ "type": "object",
389
+ "required": [
390
+ "outPath"
391
+ ],
392
+ "additionalProperties": false,
393
+ "properties": {
394
+ "outPath": {
395
+ "type": "string"
396
+ },
397
+ "compress": {
398
+ "type": "boolean"
399
+ }
400
+ }
401
+ },
402
+ "local-package-repository": {
403
+ "type": "object",
404
+ "additionalProperties": false,
405
+ "properties": {
406
+ "compress": {
407
+ "anyOf": [
408
+ {
409
+ "type": "boolean"
410
+ },
411
+ {
412
+ "type": "object",
413
+ "additionalProperties": false,
414
+ "properties": {
415
+ "packs": {
416
+ "type": "array",
417
+ "items": {
418
+ "type": "object",
419
+ "additionalProperties": false,
420
+ "required": [
421
+ "include"
422
+ ],
423
+ "properties": {
424
+ "include": {
425
+ "$ref": "#/definitions/stringlist-util"
426
+ },
427
+ "exclude": {
428
+ "$ref": "#/definitions/stringlist-util"
429
+ },
430
+ "onePackByResult": {
431
+ "type": "boolean"
432
+ }
433
+ }
434
+ }
435
+ }
436
+ }
437
+ }
438
+ ]
439
+ }
440
+ }
441
+ },
442
+ "restic-repository": {
443
+ "type": "object",
444
+ "required": [
445
+ "passwordFile",
446
+ "repository"
447
+ ],
448
+ "additionalProperties": false,
449
+ "properties": {
450
+ "passwordFile": {
451
+ "type": "string"
452
+ },
453
+ "repository": {
454
+ "type": "object",
455
+ "additionalProperties": false,
456
+ "required": [
457
+ "backend"
458
+ ],
459
+ "properties": {
460
+ "name": {
461
+ "type": "string"
462
+ },
463
+ "env": {
464
+ "type": "object",
465
+ "patternProperties": {
466
+ ".+": {
467
+ "type": "string"
468
+ }
469
+ }
470
+ },
471
+ "backend": {
472
+ "enum": [
473
+ "local",
474
+ "rest",
475
+ "sftp",
476
+ "s3",
477
+ "azure",
478
+ "gs",
479
+ "rclone"
480
+ ]
481
+ },
482
+ "protocol": {
483
+ "enum": [
484
+ "http",
485
+ "https"
486
+ ]
487
+ },
488
+ "host": {
489
+ "type": "string"
490
+ },
491
+ "username": {
492
+ "type": "string"
493
+ },
494
+ "passwordFile": {
495
+ "type": "string"
496
+ },
497
+ "port": {
498
+ "type": "integer"
499
+ },
500
+ "path": {
501
+ "type": "string"
502
+ }
503
+ }
504
+ }
505
+ }
506
+ },
507
+ "restic-package-repository": {
508
+ "type": "object",
509
+ "additionalProperties": false,
510
+ "properties": {}
511
+ },
512
+ "git-task": {
513
+ "type": "object",
514
+ "additionalProperties": false,
515
+ "properties": {
516
+ "command": {
517
+ "type": "string"
518
+ },
519
+ "includeModified": {
520
+ "anyOf": [
521
+ {
522
+ "type": "boolean"
523
+ },
524
+ {
525
+ "$ref": "#/definitions/stringlist-util"
526
+ }
527
+ ]
528
+ },
529
+ "includeUntracked": {
530
+ "anyOf": [
531
+ {
532
+ "type": "boolean"
533
+ },
534
+ {
535
+ "$ref": "#/definitions/stringlist-util"
536
+ }
537
+ ]
538
+ },
539
+ "includeIgnored": {
540
+ "anyOf": [
541
+ {
542
+ "type": "boolean"
543
+ },
544
+ {
545
+ "$ref": "#/definitions/stringlist-util"
546
+ }
547
+ ]
548
+ },
549
+ "includeConfig": {
550
+ "type": "boolean"
551
+ }
552
+ }
553
+ },
554
+ "sqldump-task": {
555
+ "type": "object",
556
+ "required": [
557
+ "password",
558
+ "hostname",
559
+ "username",
560
+ "database"
561
+ ],
562
+ "properties": {
563
+ "password": {
564
+ "anyOf": [
565
+ {
566
+ "type": "string"
567
+ },
568
+ {
569
+ "type": "object",
570
+ "additionalProperties": false,
571
+ "required": [
572
+ "path"
573
+ ],
574
+ "properties": {
575
+ "path": {
576
+ "type": "string"
577
+ }
578
+ }
579
+ }
580
+ ]
581
+ },
582
+ "hostname": {
583
+ "type": "string"
584
+ },
585
+ "port": {
586
+ "type": "integer"
587
+ },
588
+ "username": {
589
+ "type": "string"
590
+ },
591
+ "database": {
592
+ "type": "string"
593
+ },
594
+ "targetDatabase": {
595
+ "type": "object",
596
+ "required": [
597
+ "name"
598
+ ],
599
+ "properties": {
600
+ "name": {
601
+ "type": "string"
602
+ },
603
+ "charset": {
604
+ "type": "string"
605
+ },
606
+ "collate": {
607
+ "type": "string"
608
+ }
609
+ }
610
+ },
611
+ "includeTables": {
612
+ "$ref": "#/definitions/stringlist-util"
613
+ },
614
+ "excludeTables": {
615
+ "$ref": "#/definitions/stringlist-util"
616
+ },
617
+ "oneFileByTable": {
618
+ "type": "boolean"
619
+ }
620
+ }
621
+ },
622
+ "mariadb-task": {
623
+ "type": "object",
624
+ "required": [
625
+ "hostname",
626
+ "username",
627
+ "password"
628
+ ],
629
+ "additionalProperties": false,
630
+ "properties": {
631
+ "command": {
632
+ "type": "string"
633
+ },
634
+ "hostname": {
635
+ "type": "string"
636
+ },
637
+ "username": {
638
+ "type": "string"
639
+ },
640
+ "password": {
641
+ "anyOf": [
642
+ {
643
+ "type": "string"
644
+ },
645
+ {
646
+ "type": "object",
647
+ "additionalProperties": false,
648
+ "required": [
649
+ "path"
650
+ ],
651
+ "properties": {
652
+ "path": {
653
+ "type": "string"
654
+ }
655
+ }
656
+ }
657
+ ]
658
+ },
659
+ "includeTables": {
660
+ "$ref": "#/definitions/stringlist-util"
661
+ },
662
+ "excludeTables": {
663
+ "$ref": "#/definitions/stringlist-util"
664
+ },
665
+ "includeDatabases": {
666
+ "$ref": "#/definitions/stringlist-util"
667
+ },
668
+ "excludeDatabases": {
669
+ "$ref": "#/definitions/stringlist-util"
670
+ }
671
+ }
672
+ },
673
+ "mssql-task": {
674
+ "type": "object",
675
+ "additionalProperties": false,
676
+ "properties": {
677
+ "command": {
678
+ "type": "string"
679
+ },
680
+ "hostname": {
681
+ "type": "string"
682
+ },
683
+ "username": {
684
+ "type": "string"
685
+ },
686
+ "passwordFile": {
687
+ "type": "string"
688
+ },
689
+ "includeDatabases": {
690
+ "$ref": "#/definitions/stringlist-util"
691
+ },
692
+ "excludeDatabases": {
693
+ "$ref": "#/definitions/stringlist-util"
694
+ }
695
+ }
696
+ },
697
+ "mysql-dump-task": {
698
+ "allOf": [
699
+ {
700
+ "$ref": "#/definitions/sqldump-task"
701
+ }
702
+ ]
703
+ },
704
+ "postgresql-dump-task": {
705
+ "allOf": [
706
+ {
707
+ "$ref": "#/definitions/sqldump-task"
708
+ }
709
+ ]
710
+ },
711
+ "config": {
712
+ "type": "object",
713
+ "required": [
714
+ "repositories",
715
+ "packages"
716
+ ],
717
+ "additionalProperties": false,
718
+ "properties": {
719
+ "$schema": {
720
+ "type": "string"
721
+ },
722
+ "repositories": {
723
+ "type": "array",
724
+ "items": {
725
+ "$ref": "#/definitions/repository"
726
+ }
727
+ },
728
+ "packages": {
729
+ "type": "array",
730
+ "items": {
731
+ "$ref": "#/definitions/package"
732
+ }
733
+ }
734
+ }
735
+ },
736
+ "prune-policy": {
737
+ "type": "object",
738
+ "properties": {
739
+ "keepDaily": {
740
+ "type": "integer"
741
+ },
742
+ "keepHourly": {
743
+ "type": "integer"
744
+ },
745
+ "keepMinutely": {
746
+ "type": "integer"
747
+ },
748
+ "keepLast": {
749
+ "type": "integer"
750
+ },
751
+ "keepMonthly": {
752
+ "type": "integer"
753
+ },
754
+ "keepWeekly": {
755
+ "type": "integer"
756
+ },
757
+ "keepYearly": {
758
+ "type": "integer"
759
+ },
760
+ "groupBy": {
761
+ "type": "array",
762
+ "items": {
763
+ "type": "string",
764
+ "enum": [
765
+ "packageName",
766
+ "repositoryName",
767
+ "repositoryType"
768
+ ]
769
+ }
770
+ },
771
+ "tags": {
772
+ "$ref": "#/definitions/stringlist-util"
773
+ }
774
+ }
775
+ },
776
+ "paths-object": {
777
+ "type": "object",
778
+ "required": [
779
+ "type"
780
+ ],
781
+ "properties": {
782
+ "type": {
783
+ "type": "string"
784
+ }
785
+ },
786
+ "anyOf": [
787
+ {
788
+ "if": {
789
+ "type": "object",
790
+ "properties": {
791
+ "type": {
792
+ "const": "spawn"
793
+ }
794
+ }
795
+ },
796
+ "then": {
797
+ "type": "object",
798
+ "required": [
799
+ "command"
800
+ ],
801
+ "properties": {
802
+ "command": {
803
+ "type": "string"
804
+ },
805
+ "args": {
806
+ "$ref": "#/definitions/stringlist-util"
807
+ }
808
+ }
809
+ },
810
+ "else": false
811
+ }
812
+ ]
813
+ }
814
+ },
815
+ "$ref": "#/definitions/config"
816
+ }
@@ -0,0 +1,122 @@
1
+ --------------------------------------------------------------------------------
2
+ -- Up
3
+ --------------------------------------------------------------------------------
4
+
5
+ CREATE TABLE "backup_session" (
6
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
7
+ "creationDate" TEXT NOT NULL,
8
+ "updatingDate" TEXT,
9
+
10
+ "startDate" TEXT,
11
+ "endDate" TEXT,
12
+ "state" TEXT,
13
+ "error" TEXT,
14
+ "progressCurrent" INTEGER,
15
+ "progressTotal" INTEGER,
16
+ "progressPercent" INTEGER,
17
+ "progressStep" TEXT,
18
+ "progressStepPercent" INTEGER,
19
+
20
+ "snapshotId" TEXT NOT NULL,
21
+ "packageName" TEXT NOT NULL,
22
+ -- "componentName" TEXT NOT NULL,
23
+ "tags" TEXT
24
+ );
25
+
26
+ CREATE TABLE "backup_session_task" (
27
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
28
+ "creationDate" TEXT NOT NULL,
29
+ "updatingDate" TEXT,
30
+
31
+ "startDate" TEXT,
32
+ "endDate" TEXT,
33
+ "state" TEXT,
34
+ "error" TEXT,
35
+ "progressCurrent" INTEGER,
36
+ "progressTotal" INTEGER,
37
+ "progressPercent" INTEGER,
38
+ "progressStep" TEXT,
39
+ "progressStepPercent" INTEGER,
40
+
41
+ "sessionId" INTEGER NOT NULL,
42
+ "taskName" TEXT NOT NULL
43
+ );
44
+
45
+ CREATE TABLE "backup_session_repository" (
46
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
47
+ "creationDate" TEXT NOT NULL,
48
+ "updatingDate" TEXT,
49
+
50
+ "startDate" TEXT,
51
+ "endDate" TEXT,
52
+ "state" TEXT,
53
+ "error" TEXT,
54
+ "progressCurrent" INTEGER,
55
+ "progressTotal" INTEGER,
56
+ "progressPercent" INTEGER,
57
+ "progressStep" TEXT,
58
+ "progressStepPercent" INTEGER,
59
+
60
+ "sessionId" INTEGER NOT NULL,
61
+ "repositoryName" TEXT NOT NULL,
62
+ "repositoryType" TEXT NOT NULL
63
+ );
64
+
65
+ CREATE TABLE "restore_session" (
66
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
67
+ "creationDate" TEXT NOT NULL,
68
+ "updatingDate" TEXT,
69
+
70
+ "startDate" TEXT,
71
+ "endDate" TEXT,
72
+ "state" TEXT,
73
+ "error" TEXT,
74
+ "progressCurrent" INTEGER,
75
+ "progressTotal" INTEGER,
76
+ "progressPercent" INTEGER,
77
+ "progressStep" TEXT,
78
+ "progressStepPercent" INTEGER,
79
+
80
+ "snapshotId" TEXT NOT NULL,
81
+ "packageName" TEXT NOT NULL -- ,
82
+ -- "componentName" TEXT NOT NULL
83
+ );
84
+
85
+ CREATE TABLE "restore_session_task" (
86
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
87
+ "creationDate" TEXT NOT NULL,
88
+ "updatingDate" TEXT,
89
+
90
+ "startDate" TEXT,
91
+ "endDate" TEXT,
92
+ "state" TEXT,
93
+ "error" TEXT,
94
+ "progressCurrent" INTEGER,
95
+ "progressTotal" INTEGER,
96
+ "progressPercent" INTEGER,
97
+ "progressStep" TEXT,
98
+ "progressStepPercent" INTEGER,
99
+
100
+ "sessionId" INTEGER NOT NULL,
101
+ "taskName" TEXT NOT NULL
102
+ );
103
+
104
+ CREATE TABLE "restore_session_repository" (
105
+ "id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
106
+ "creationDate" TEXT NOT NULL,
107
+ "updatingDate" TEXT,
108
+
109
+ "startDate" TEXT,
110
+ "endDate" TEXT,
111
+ "state" TEXT,
112
+ "error" TEXT,
113
+ "progressCurrent" INTEGER,
114
+ "progressTotal" INTEGER,
115
+ "progressPercent" INTEGER,
116
+ "progressStep" TEXT,
117
+ "progressStepPercent" INTEGER,
118
+
119
+ "sessionId" INTEGER NOT NULL,
120
+ "repositoryName" TEXT NOT NULL,
121
+ "repositoryType" TEXT NOT NULL
122
+ );
package/package.json CHANGED
@@ -1,22 +1,21 @@
1
1
  {
2
2
  "name": "@datatruck/cli",
3
- "version": "0.0.5",
3
+ "version": "0.2.0",
4
4
  "dependencies": {
5
- "ajv": "^8.10.0",
5
+ "ajv": "^8.11.0",
6
6
  "chalk": "^4.1.2",
7
- "cli-table3": "^0.6.1",
8
- "commander": "^9.0.0",
9
- "concurrently": "^7.0.0",
10
- "dayjs": "^1.10.7",
7
+ "cli-table3": "^0.6.2",
8
+ "commander": "^9.2.0",
9
+ "dayjs": "^1.11.2",
11
10
  "fast-glob": "^3.2.11",
12
- "fs-extra": "^10.0.1",
13
- "micromatch": "^4.0.4",
14
- "sqlite": "^4.0.23",
15
- "sqlite3": "^5.0.2"
11
+ "fs-extra": "^10.1.0",
12
+ "micromatch": "^4.0.5",
13
+ "sqlite": "^4.1.1",
14
+ "sqlite3": "^5.0.8"
16
15
  },
17
16
  "optionalDependencies": {
18
- "ts-node": "^10.5.0",
19
- "yaml": "^1.10.2"
17
+ "ts-node": "^10.7.0",
18
+ "yaml": "^2.1.0"
20
19
  },
21
20
  "engine": {
22
21
  "node": ">=16.0.0"
@@ -1,4 +1,4 @@
1
- export declare type FormatType = "json" | "pjson" | "table" | "yaml";
1
+ export declare type FormatType = "json" | "pjson" | "table" | "yaml" | "custom";
2
2
  export declare class DataFormat<TItem extends Record<string, unknown>> {
3
3
  readonly options: {
4
4
  items: TItem[];
@@ -4,8 +4,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.DataFormat = void 0;
7
+ const AppError_1 = require("../Error/AppError");
7
8
  const cli_table3_1 = __importDefault(require("cli-table3"));
8
9
  const util_1 = require("util");
10
+ const customPrefix = "custom=";
9
11
  class DataFormat {
10
12
  constructor(options) {
11
13
  this.options = options;
@@ -45,6 +47,16 @@ class DataFormat {
45
47
  else if (format === "yaml") {
46
48
  return this.formatToYaml();
47
49
  }
50
+ else if (format.startsWith(customPrefix)) {
51
+ const code = format.slice(customPrefix.length);
52
+ return runCustomCode(this.options.items, code);
53
+ }
54
+ else {
55
+ throw new AppError_1.AppError(`Invalid output format: ${format}`);
56
+ }
48
57
  }
49
58
  }
50
59
  exports.DataFormat = DataFormat;
60
+ function runCustomCode($, __code) {
61
+ return eval(__code);
62
+ }
@@ -1,4 +1,4 @@
1
- import { ExecSettingsInterface } from "./process-util";
1
+ import { ExecResultType, ExecSettingsInterface } from "./process-util";
2
2
  import { UriType } from "./string-util";
3
3
  export declare type RepositoryType = {
4
4
  name?: string;
@@ -43,7 +43,7 @@ export declare class ResticUtil {
43
43
  static formatRepository(input: RepositoryType, hidePassword?: boolean): Promise<string>;
44
44
  exec(args: string[], settings?: ExecSettingsInterface, options?: {
45
45
  cwd?: string;
46
- }): Promise<import("./process-util").ExecResultType>;
46
+ }): Promise<ExecResultType>;
47
47
  checkRepository(): Promise<boolean>;
48
48
  forget(options: {
49
49
  snapshotId?: string;
@@ -81,12 +81,14 @@ export declare class ResticUtil {
81
81
  paths: string[];
82
82
  setPaths?: string[];
83
83
  exclude?: string[];
84
+ excludeFile?: string[];
84
85
  parent?: string;
86
+ allowEmptySnapshot?: boolean;
85
87
  onStream?: (data: BackupStreamType) => Promise<void>;
86
- }): Promise<import("./process-util").ExecResultType>;
88
+ }): Promise<ExecResultType>;
87
89
  restore(options: {
88
90
  id: string;
89
91
  target: string;
90
92
  onStream?: (data: BackupStreamType) => Promise<void>;
91
- }): Promise<import("./process-util").ExecResultType>;
93
+ }): Promise<ExecResultType>;
92
94
  }
@@ -1,9 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ResticUtil = void 0;
4
+ const fs_util_1 = require("./fs-util");
4
5
  const process_util_1 = require("./process-util");
5
6
  const string_util_1 = require("./string-util");
6
7
  const fs_extra_1 = require("fs-extra");
8
+ const promises_1 = require("fs/promises");
7
9
  const path_1 = require("path");
8
10
  class ResticUtil {
9
11
  constructor(options) {
@@ -13,7 +15,7 @@ class ResticUtil {
13
15
  if (input.backend === "local") {
14
16
  if (typeof input.path !== "string")
15
17
  throw new Error(`Invalid path at "${input.name}" repository: ${input.path}`);
16
- return (0, path_1.normalize)(input.path);
18
+ return (0, path_1.resolve)(input.path);
17
19
  }
18
20
  if (input.passwordFile)
19
21
  input = {
@@ -36,7 +38,7 @@ class ResticUtil {
36
38
  stderr: true,
37
39
  colorize: true,
38
40
  allToStderr: true,
39
- envNames: ["RESTIC_REPOSITORY"],
41
+ envNames: ["RESTIC_REPOSITORY", "RESTIC_PASSWORD_FILE"],
40
42
  }
41
43
  : {},
42
44
  ...(settings ?? {}),
@@ -99,10 +101,11 @@ class ResticUtil {
99
101
  return result.stderr.includes("flag needs an argument");
100
102
  }
101
103
  async backup(options) {
102
- return await this.exec([
104
+ const exec = async () => await this.exec([
103
105
  "backup",
104
106
  "--json",
105
107
  ...(options.exclude?.flatMap((v) => ["-e", v]) ?? []),
108
+ ...(options.excludeFile?.flatMap((v) => ["--exclude-file", v]) ?? []),
106
109
  ...(options.tags?.flatMap((v) => ["--tag", v]) ?? []),
107
110
  ...(options.setPaths?.flatMap((v) => ["--set-path", v]) ?? []),
108
111
  ...(options.parent ? ["--parent", options.parent] : []),
@@ -123,6 +126,25 @@ class ResticUtil {
123
126
  }, {
124
127
  cwd: options.cwd,
125
128
  });
129
+ try {
130
+ return await exec();
131
+ }
132
+ catch (error) {
133
+ if (options.allowEmptySnapshot &&
134
+ error.message.includes("unable to save snapshot: snapshot is empty")) {
135
+ const emptyPath = await (0, fs_util_1.mkTmpDir)("empty");
136
+ await (0, promises_1.writeFile)(`${emptyPath}/.empty`, "");
137
+ return await this.backup({
138
+ ...options,
139
+ cwd: emptyPath,
140
+ allowEmptySnapshot: false,
141
+ paths: ["."],
142
+ exclude: [],
143
+ excludeFile: [],
144
+ });
145
+ }
146
+ throw error;
147
+ }
126
148
  }
127
149
  async restore(options) {
128
150
  return await this.exec(["restore", "--json", options.id, "--target", options.target], {
package/util/fs-util.d.ts CHANGED
@@ -23,6 +23,9 @@ export declare function readPartialFile(path: string, positions: [number, number
23
23
  export declare function checkFile(path: string): Promise<boolean>;
24
24
  export declare function checkDir(path: string): Promise<boolean>;
25
25
  export declare function forEachFile(dirPath: string, cb: (path: string, dir: boolean) => void, includeDir?: boolean): Promise<void>;
26
+ export declare function writeGitIgnoreList(options: {
27
+ paths: NodeJS.ReadableStream | string[];
28
+ }): Promise<string>;
26
29
  export declare function writePathLists(options: {
27
30
  paths: NodeJS.ReadableStream | string[];
28
31
  packs?: {
package/util/fs-util.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.writePathLists = exports.forEachFile = exports.checkDir = exports.checkFile = exports.readPartialFile = exports.mkTmpDir = exports.tmpDir = exports.sessionTmpDir = exports.parentTmpDir = exports.existsFile = exports.findFile = exports.parsePackageFile = exports.parseFile = exports.parseFileExtensions = exports.readdirIfExists = exports.writeJSONFile = exports.existsDir = exports.ensureEmptyDir = exports.mkdirIfNotExists = exports.isDirEmpty = exports.isLocalDir = void 0;
3
+ exports.writePathLists = exports.writeGitIgnoreList = exports.forEachFile = exports.checkDir = exports.checkFile = exports.readPartialFile = exports.mkTmpDir = exports.tmpDir = exports.sessionTmpDir = exports.parentTmpDir = exports.existsFile = exports.findFile = exports.parsePackageFile = exports.parseFile = exports.parseFileExtensions = exports.readdirIfExists = exports.writeJSONFile = exports.existsDir = exports.ensureEmptyDir = exports.mkdirIfNotExists = exports.isDirEmpty = exports.isLocalDir = void 0;
4
4
  const path_util_1 = require("./path-util");
5
5
  const crypto_1 = require("crypto");
6
6
  const fs_1 = require("fs");
@@ -190,6 +190,40 @@ async function forEachFile(dirPath, cb, includeDir) {
190
190
  }
191
191
  }
192
192
  exports.forEachFile = forEachFile;
193
+ async function writeGitIgnoreList(options) {
194
+ const tempDir = await mkTmpDir("gitignore-list");
195
+ const path = (0, path_1.join)(tempDir, `.gitignore`);
196
+ const stream = (0, fs_2.createWriteStream)(path);
197
+ const dirs = new Set();
198
+ stream.write("*\n");
199
+ for await (const value of options.paths) {
200
+ const dir = (0, path_1.dirname)(value.toString());
201
+ if (dir !== ".") {
202
+ let parentPath;
203
+ for (const value of dir.split("/")) {
204
+ if (!parentPath) {
205
+ parentPath = `!${value}`;
206
+ }
207
+ else {
208
+ parentPath += `/${value}`;
209
+ }
210
+ if (!dirs.has(parentPath)) {
211
+ stream.write(`${parentPath}\n`);
212
+ stream.write(`${parentPath.slice(1)}/*\n`);
213
+ dirs.add(parentPath);
214
+ }
215
+ }
216
+ }
217
+ stream.write(`!${value}\n`);
218
+ }
219
+ await new Promise((resolve, reject) => {
220
+ stream.close();
221
+ stream.on("close", resolve);
222
+ stream.on("error", reject);
223
+ });
224
+ return path;
225
+ }
226
+ exports.writeGitIgnoreList = writeGitIgnoreList;
193
227
  async function writePathLists(options) {
194
228
  const tempDir = await mkTmpDir("path-lists");
195
229
  const includedPaths = [];