@datatruck/cli 0.16.0 → 0.16.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.
@@ -23,10 +23,17 @@ export declare class BackupAction<TRequired extends boolean = true> {
23
23
  protected repoErrors: Record<string, Error[]>;
24
24
  constructor(config: ConfigType, options?: IfRequireKeys<TRequired, BackupActionOptionsType>);
25
25
  protected init(session: BackupSessionManager): Promise<[SnapshotType, PackageConfigType[]]>;
26
- protected execTask(session: BackupSessionManager, pkg: PackageConfigType, task: TaskConfigType, snapshot: SnapshotType, targetPath: string | undefined): Promise<boolean>;
27
- protected execRepository(session: BackupSessionManager, pkg: PackageConfigType, repo: RepositoryConfigType, snapshot: SnapshotType, targetPath: string | undefined): Promise<boolean>;
28
- protected execCopyRepository(session: BackupSessionManager, pkg: PackageConfigType, repo: RepositoryConfigType, mirrorRepo: RepositoryConfigType, snapshot: SnapshotType): Promise<boolean>;
26
+ protected task(session: BackupSessionManager, pkg: PackageConfigType, task: TaskConfigType, snapshot: SnapshotType, targetPath: string | undefined): Promise<boolean>;
27
+ protected backup(session: BackupSessionManager, pkg: PackageConfigType, repo: RepositoryConfigType, snapshot: SnapshotType, targetPath: string | undefined): Promise<boolean>;
28
+ protected copyBackup(session: BackupSessionManager, pkg: PackageConfigType, repo: RepositoryConfigType, mirrorRepo: RepositoryConfigType, snapshot: SnapshotType): Promise<boolean>;
29
29
  protected getError(pkg: PackageConfigType): AppError | null;
30
+ protected splitRepositories(repositoryNames: string[]): {
31
+ repoNames: string[];
32
+ mirrors: {
33
+ sourceName: string;
34
+ name: string;
35
+ }[];
36
+ };
30
37
  exec(session: BackupSessionManager): Promise<{
31
38
  total: number;
32
39
  errors: number;
@@ -53,7 +53,7 @@ class BackupAction {
53
53
  }
54
54
  return [snapshot, packages];
55
55
  }
56
- async execTask(session, pkg, task, snapshot, targetPath) {
56
+ async task(session, pkg, task, snapshot, targetPath) {
57
57
  const taskId = session.findTaskId({
58
58
  packageName: pkg.name,
59
59
  taskName: task.name,
@@ -94,7 +94,7 @@ class BackupAction {
94
94
  });
95
95
  return error ? false : true;
96
96
  }
97
- async execRepository(session, pkg, repo, snapshot, targetPath) {
97
+ async backup(session, pkg, repo, snapshot, targetPath) {
98
98
  const repositoryId = session.findRepositoryId({
99
99
  packageName: pkg.name,
100
100
  repositoryName: repo.name,
@@ -136,7 +136,7 @@ class BackupAction {
136
136
  });
137
137
  return error ? false : true;
138
138
  }
139
- async execCopyRepository(session, pkg, repo, mirrorRepo, snapshot) {
139
+ async copyBackup(session, pkg, repo, mirrorRepo, snapshot) {
140
140
  const repositoryId = session.findRepositoryId({
141
141
  packageName: pkg.name,
142
142
  repositoryName: mirrorRepo.name,
@@ -192,12 +192,33 @@ class BackupAction {
192
192
  return null;
193
193
  }
194
194
  }
195
+ splitRepositories(repositoryNames) {
196
+ const mirrorRepoMap = {};
197
+ const allMirrorRepoNames = [];
198
+ const repoNames = repositoryNames ?? [];
199
+ for (const repoName of repoNames) {
200
+ const repo = (0, config_util_1.findRepositoryOrFail)(this.config, repoName);
201
+ if (repo.mirrorRepoNames)
202
+ mirrorRepoMap[repoName] = repo.mirrorRepoNames.filter((mirrorRepoName) => {
203
+ allMirrorRepoNames.push(mirrorRepoName);
204
+ return repoNames.includes(mirrorRepoName);
205
+ });
206
+ }
207
+ return {
208
+ repoNames: repoNames.filter((v) => !allMirrorRepoNames.includes(v)),
209
+ mirrors: repoNames.flatMap((sourceName) => {
210
+ const mirrorNames = mirrorRepoMap[sourceName] || [];
211
+ return mirrorNames.map((name) => ({
212
+ sourceName,
213
+ name,
214
+ }));
215
+ }),
216
+ };
217
+ }
195
218
  async exec(session) {
196
219
  const [snapshot, packages] = await this.init(session);
197
- let total = 0;
198
220
  let errors = 0;
199
221
  for (const pkg of packages) {
200
- total++;
201
222
  const id = session.findId({
202
223
  packageName: pkg.name,
203
224
  });
@@ -212,34 +233,17 @@ class BackupAction {
212
233
  package: pkg,
213
234
  snapshot,
214
235
  });
215
- await this.execTask(session, pkg, pkg.task, snapshot, (targetPath = result?.targetPath));
236
+ await this.task(session, pkg, pkg.task, snapshot, (targetPath = result?.targetPath));
216
237
  }
217
- const mirrorRepoMap = {};
218
- const allMirrorRepoNames = [];
219
- const repoNames = pkg.repositoryNames ?? [];
238
+ const { repoNames, mirrors } = this.splitRepositories(pkg.repositoryNames ?? []);
220
239
  for (const repoName of repoNames) {
221
240
  const repo = (0, config_util_1.findRepositoryOrFail)(this.config, repoName);
222
- if (repo.mirrorRepoNames)
223
- mirrorRepoMap[repoName] = repo.mirrorRepoNames.filter((mirrorRepoName) => {
224
- allMirrorRepoNames.push(mirrorRepoName);
225
- return repoNames.includes(mirrorRepoName);
226
- });
241
+ await this.backup(session, pkg, repo, snapshot, targetPath);
227
242
  }
228
- for (const repoName of repoNames) {
229
- if (allMirrorRepoNames.includes(repoName))
230
- continue;
231
- const repo = (0, config_util_1.findRepositoryOrFail)(this.config, repoName);
232
- await this.execRepository(session, pkg, repo, snapshot, targetPath);
233
- }
234
- for (const repoName of repoNames) {
235
- const repo = (0, config_util_1.findRepositoryOrFail)(this.config, repoName);
236
- const mirrorRepoNames = mirrorRepoMap[repoName];
237
- if (mirrorRepoNames) {
238
- for (const mirrorRepoName of mirrorRepoNames) {
239
- const mirrorRepo = (0, config_util_1.findRepositoryOrFail)(this.config, mirrorRepoName);
240
- await this.execCopyRepository(session, pkg, repo, mirrorRepo, snapshot);
241
- }
242
- }
243
+ for (const mirror of mirrors) {
244
+ const repo = (0, config_util_1.findRepositoryOrFail)(this.config, mirror.sourceName);
245
+ const mirrorRepo = (0, config_util_1.findRepositoryOrFail)(this.config, mirror.name);
246
+ await this.copyBackup(session, pkg, repo, mirrorRepo, snapshot);
243
247
  }
244
248
  const error = this.getError(pkg);
245
249
  if (error)
@@ -253,7 +257,7 @@ class BackupAction {
253
257
  snapshotId: snapshot.id.slice(0, 8),
254
258
  });
255
259
  return {
256
- total: total,
260
+ total: packages.length,
257
261
  errors: errors,
258
262
  };
259
263
  }
@@ -30,8 +30,8 @@ export declare class RestoreAction<TRequired extends boolean = true> {
30
30
  protected init(session: RestoreSessionManager, snapshotId: string, snapshots: SnapshotAndConfigType[]): Promise<void>;
31
31
  protected findSnapshots(): Promise<SnapshotType[]>;
32
32
  protected groupSnapshots(snapshots: SnapshotType[]): SnapshotType[];
33
- protected execTask(session: RestoreSessionManager, pkg: PackageConfigType, task: TaskConfigType, snapshot: SnapshotType, targetPath: string | undefined): Promise<boolean>;
34
- protected execRepository(session: RestoreSessionManager, pkg: PackageConfigType, repo: RepositoryConfigType, snapshot: SnapshotType, targetPath: string | undefined): Promise<boolean>;
33
+ protected task(session: RestoreSessionManager, pkg: PackageConfigType, task: TaskConfigType, snapshot: SnapshotType, targetPath: string | undefined): Promise<boolean>;
34
+ protected restore(session: RestoreSessionManager, pkg: PackageConfigType, repo: RepositoryConfigType, snapshot: SnapshotType, targetPath: string | undefined): Promise<boolean>;
35
35
  protected getError(pkg: PackageConfigType): AppError | null;
36
36
  exec(session: RestoreSessionManager): Promise<boolean>;
37
37
  }
@@ -88,7 +88,7 @@ class RestoreAction {
88
88
  return true;
89
89
  });
90
90
  }
91
- async execTask(session, pkg, task, snapshot, targetPath) {
91
+ async task(session, pkg, task, snapshot, targetPath) {
92
92
  const taskId = session.findTaskId({
93
93
  packageName: pkg.name,
94
94
  taskName: task.name,
@@ -131,7 +131,7 @@ class RestoreAction {
131
131
  });
132
132
  return error ? false : true;
133
133
  }
134
- async execRepository(session, pkg, repo, snapshot, targetPath) {
134
+ async restore(session, pkg, repo, snapshot, targetPath) {
135
135
  const repositoryId = session.findRepositoryId({
136
136
  packageName: pkg.name,
137
137
  repositoryName: repo.name,
@@ -232,9 +232,9 @@ class RestoreAction {
232
232
  });
233
233
  targetPath = result?.targetPath;
234
234
  }
235
- await this.execRepository(session, pkg, repo, snapshot, targetPath);
235
+ await this.restore(session, pkg, repo, snapshot, targetPath);
236
236
  if (pkg.task)
237
- await this.execTask(session, pkg, pkg.task, snapshot, targetPath);
237
+ await this.task(session, pkg, pkg.task, snapshot, targetPath);
238
238
  const error = this.getError(pkg);
239
239
  await session.end({
240
240
  id,
@@ -123,30 +123,29 @@ class DatatruckRepository extends RepositoryAbstract_1.RepositoryAbstract {
123
123
  object.current += data.current;
124
124
  }
125
125
  },
126
+ updateProgress: async (end) => {
127
+ const currentTime = perf_hooks_1.performance.now();
128
+ const diff = currentTime - lastTime;
129
+ if (end || diff > 1000) {
130
+ await options.onProgress({
131
+ relative: {
132
+ description: end ? "Scanned files" : "Scanning files",
133
+ payload: object.total.toString(),
134
+ },
135
+ });
136
+ lastTime = currentTime;
137
+ }
138
+ },
126
139
  start: async (cb) => {
127
140
  for await (const entry of (0, fs_util_1.pathIterator)(stream)) {
128
141
  if (!options.disableCounting)
129
142
  object.total++;
130
- const currentTime = perf_hooks_1.performance.now();
131
- const diff = currentTime - lastTime;
132
- if (diff > 1000) {
133
- await options.onProgress({
134
- relative: {
135
- description: "Scanning files",
136
- payload: object.total.toString(),
137
- },
138
- });
139
- lastTime = currentTime;
140
- }
143
+ await object.updateProgress();
141
144
  if (cb)
142
145
  await cb(entry);
143
146
  }
144
- await options.onProgress({
145
- relative: {
146
- description: "Scanned files",
147
- payload: object.total.toString(),
148
- },
149
- });
147
+ if (!options.disableEndProgress)
148
+ await object.updateProgress(true);
150
149
  },
151
150
  };
152
151
  await options.onProgress({
@@ -439,6 +438,7 @@ class DatatruckRepository extends RepositoryAbstract_1.RepositoryAbstract {
439
438
  cwd: sourcePath,
440
439
  },
441
440
  onProgress: data.onProgress,
441
+ disableEndProgress: true,
442
442
  });
443
443
  await scanner.start();
444
444
  const it = await (0, promises_1.opendir)(sourcePath);
@@ -446,6 +446,7 @@ class DatatruckRepository extends RepositoryAbstract_1.RepositoryAbstract {
446
446
  const path = (0, path_1.join)(sourcePath, dirent.name);
447
447
  if (dirent.name === "permissions.txt") {
448
448
  scanner.total++;
449
+ await scanner.updateProgress();
449
450
  }
450
451
  else if (dirent.name.endsWith(".zip")) {
451
452
  await (0, zip_util_1.listZip)({
@@ -455,10 +456,12 @@ class DatatruckRepository extends RepositoryAbstract_1.RepositoryAbstract {
455
456
  const isDir = item.Folder === "+";
456
457
  if (!isDir)
457
458
  scanner.total++;
459
+ await scanner.updateProgress();
458
460
  },
459
461
  });
460
462
  }
461
463
  }
464
+ await scanner.updateProgress(true);
462
465
  if (data.options.verbose)
463
466
  (0, cli_util_1.logExec)(`Copying files to ${restorePath}`);
464
467
  await (0, fs_util_1.cpy)({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datatruck/cli",
3
- "version": "0.16.0",
3
+ "version": "0.16.1",
4
4
  "dependencies": {
5
5
  "ajv": "^8.11.0",
6
6
  "async": "^3.2.4",
package/util/zip-util.js CHANGED
@@ -173,9 +173,9 @@ async function zip(data) {
173
173
  exports.zip = zip;
174
174
  function parseUnzipLine(line) {
175
175
  let matches = null;
176
- if ((matches = /^\s*(\d+)% (\d+) \-/.exec(line))) {
177
- const progress = Number(matches[1]);
178
- const files = Number(matches[2]);
176
+ if ((matches = /^\s*(?<percent>\d+)%(?: (?<files>\d+))? \-/.exec(line))) {
177
+ const progress = Number(matches.groups["percent"]);
178
+ const files = matches.groups["files"] ? Number(matches[2]) : 1;
179
179
  const path = line.slice(line.indexOf("-") + 1).trim();
180
180
  return {
181
181
  type: "progress",
@@ -204,20 +204,22 @@ async function unzip(data) {
204
204
  stderr: { toExitCode: true },
205
205
  stdout: {
206
206
  ...((data.onStream || data.onProgress) && {
207
- parseLines: true,
208
- onData: async (line) => {
209
- const stream = parseUnzipLine(line);
210
- if (stream) {
211
- if (stream.type === "progress") {
212
- const current = Math.max(0, stream.data.files - 1);
213
- summary.files = stream.data.files;
214
- await data.onProgress?.({
215
- current,
216
- percent: stream.data.percent,
217
- path: stream.data.path,
218
- });
207
+ onData: async (chunk) => {
208
+ const lines = chunk.replaceAll("\b", "").split(/\r?\n/);
209
+ for (const line of lines) {
210
+ const stream = parseUnzipLine(line);
211
+ if (stream) {
212
+ if (stream.type === "progress") {
213
+ const current = Math.max(0, stream.data.files - 1);
214
+ summary.files = stream.data.files;
215
+ await data.onProgress?.({
216
+ current,
217
+ percent: stream.data.percent,
218
+ path: stream.data.path,
219
+ });
220
+ }
221
+ await data.onStream?.(stream);
219
222
  }
220
- await data.onStream?.(stream);
221
223
  }
222
224
  },
223
225
  }),