@ps-aux/nodebup 0.11.1 → 0.12.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,14 +5,6 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.AggregateBackupController = void 0;
7
7
 
8
- var _FsSyncer = require("../../fs/fssync/FsSyncer");
9
-
10
- var _Gpg = require("../../tools/gpg/Gpg");
11
-
12
- var _Fs = require("../../fs/Fs");
13
-
14
- var _SshKeyManager = require("../../tools/ssh/SshKeyManager");
15
-
16
8
  var _inversify = require("inversify");
17
9
 
18
10
  var _ContextSymbols = require("../../ctx/ContextSymbols");
@@ -25,6 +17,14 @@ var _BackupStorageProvider = require("../../storage/BackupStorageProvider");
25
17
 
26
18
  var _logging = require("../../log/logging");
27
19
 
20
+ var _SshKeyManager = require("../../tools/ssh/SshKeyManager");
21
+
22
+ var _Gpg = require("../../tools/gpg/Gpg");
23
+
24
+ var _Fs = require("../../fs/Fs");
25
+
26
+ var _FsSyncer = require("../../fs/fssync/FsSyncer");
27
+
28
28
  var _dec, _dec2, _dec3, _dec4, _class;
29
29
 
30
30
  function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
@@ -53,7 +53,7 @@ let AggregateBackupController = (_dec = (0, _inversify.injectable)(), _dec2 = fu
53
53
  } = aggr.sources;
54
54
 
55
55
  try {
56
- this.fs.ensureDir(aggrDir);
56
+ await this.fs.ensureDir(aggrDir);
57
57
  file.forEach(f => this.runFileTask(f, aggrDir));
58
58
  sshKey.forEach(k => this.runSshKeyTask(k, aggrDir));
59
59
  gpgKey.forEach(k => this.runGpgKeyTask(k, aggrDir));
@@ -23,22 +23,22 @@ let DirBackupController = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.
23
23
 
24
24
  _defineProperty(this, "log", (0, _logging.classObjLog)(this));
25
25
 
26
- _defineProperty(this, "backup", inp => {
26
+ _defineProperty(this, "backup", async inp => {
27
27
  const storage = this.storageProvider.provide();
28
28
 
29
29
  const path = _Path.AbsPath.from(inp.path);
30
30
 
31
31
  this.log.info(`Backing up from ${path} to '${storage}'`);
32
- storage.store(path);
32
+ await storage.store(path);
33
33
  });
34
34
 
35
- _defineProperty(this, "restore", inp => {
35
+ _defineProperty(this, "restore", async inp => {
36
36
  const storage = this.storageProvider.provide();
37
37
 
38
38
  const path = _Path.AbsPath.from(inp.path);
39
39
 
40
40
  this.log.info(`Restoring from '${storage}' to ${path}`);
41
- storage.restore(path);
41
+ await storage.restore(path);
42
42
  });
43
43
  }
44
44
 
@@ -5,8 +5,6 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.PgBackupController = void 0;
7
7
 
8
- var _Fs = require("../../fs/Fs");
9
-
10
8
  var _inversify = require("inversify");
11
9
 
12
10
  var _Shell = require("../../tools/shell/Shell");
@@ -19,6 +17,8 @@ var _Zipper = require("../../tools/compression/Zipper");
19
17
 
20
18
  var _logging = require("../../log/logging");
21
19
 
20
+ var _Fs = require("../../fs/Fs");
21
+
22
22
  var _dec, _dec2, _dec3, _class, _class2, _temp;
23
23
 
24
24
  function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
@@ -59,61 +59,123 @@ let PgBackupController = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.m
59
59
 
60
60
  _defineProperty(this, "getVersion", version => version || PgBackupController.defaultPgVersion);
61
61
 
62
- _defineProperty(this, "outFileName", 'backup.sql');
63
-
64
62
  _defineProperty(this, "backup", async ({
65
63
  pgUrl,
66
- pgVersion
64
+ pgBinDir,
65
+ backupName
67
66
  }) => {
68
67
  const storage = this.storageBackendProvider.provide();
69
- const version = this.getVersion(pgVersion); // TODO validate url
68
+ const connParams = parseConnectionUrl(pgUrl); // TODO validate url
70
69
 
71
- this.log.info(`Backing up Postgres database, version=${version}`);
72
- const connParams = parseConnectionUrl(pgUrl);
73
- const pass = connParams.password; // if (3 > 2)
74
- // return
70
+ this.log.info(`Backing up Postgres database @${connParams.host}`);
75
71
 
76
- await this.fs.inTmpDir('pg-backup', async dir => {
77
- const outputDir = _Path.AbsPath.from(dir);
72
+ const dir = _Path.AbsPath.from(`/var/backup/bup/${backupName}/postgres`); // const dir = AbsPath.from('/var/foo')
78
73
 
79
- this.log.info('Processing Postgres backup');
80
- const bashCmds = [`echo "*:*:*:*:${pass}" > ~/.pgpass`, `chmod 400 ~/.pgpass`, `pg_dumpall -d ${pgUrl}`]; // Don't forget that this itself might be run in docker
74
+
75
+ await this.fs.inNewDir(dir, async () => {
76
+ // const outputDir = AbsPath.from(dir)
77
+ this.log.info('Processing Postgres backup'); // Don't forget that this itself might be run in docker
81
78
  // therefore volume mounts are not usable (will apply to the location at host)
82
79
 
83
- const zipOutPath = outputDir.resolve(`pg-backup-${this.now()}.sql.gz`);
84
- this.log.info(`Dumping database to ${zipOutPath.str()}`);
85
- await this.sh.exec(`docker run --rm --network host ` + `postgres:${version} ` + `bash -c '${bashCmds.join(' && ')}' | gzip> ${zipOutPath.str()}`);
80
+ const file = PgBackupController.dumpFileName;
81
+ this.log.info(`Dumping data into ${file}`);
82
+ let cmd = `pg_dumpall -d ${pgUrl} `;
83
+
84
+ if (pgBinDir) {
85
+ cmd = this.addPgBinDir(cmd, pgBinDir);
86
+ }
87
+
88
+ await this.sh.asyncExec(`${cmd} > ${file}`, {
89
+ env: {
90
+ PGPASSWORD: connParams.password
91
+ },
92
+ cwd: dir.str()
93
+ }, {
94
+ stdout: o => this.log.trace(o),
95
+ stderr: o => this.log.error(o)
96
+ });
97
+ this.log.info(`Compressing ${file}`);
98
+ await this.sh.asyncExec(`gzip -v ${file}`, {
99
+ cwd: dir.str()
100
+ }, {
101
+ stdout: o => this.log.trace('gzip: ' + o),
102
+ stderr: o => this.log.info('gzip: ' + o)
103
+ });
86
104
  this.log.info('Uploading');
87
- storage.store(_Path.AbsPath.from(dir));
105
+ await storage.store(dir);
88
106
  });
89
107
  });
90
108
 
109
+ _defineProperty(this, "dumpFromDockerCmd", (pass, pgUrl, version) => {
110
+ const bashCmds = [`echo "*:*:*:*:${pass}" > ~/.pgpass`, `chmod 400 ~/.pgpass`, `pg_dumpall -d ${pgUrl}`]; // Restore docker command
111
+ // this.sh.exec(
112
+ // `gzip --decompress --stdout ${zipFile.str()} | docker run --network host -i ` +
113
+ // `-e PGPASSWORD=${con.password} ` +
114
+ // `postgres:${version} ` +
115
+ // `psql ${connectionArgs} -v ON_ERROR_STOP=0`
116
+ // )
117
+ // TODO consider pulling the docker images first so the Docke daremon info messages about container pulling are not logged as errors
118
+
119
+ return `docker run --rm --network host postgres:${version} ` + `bash -c '${bashCmds.join(' && ')}'`;
120
+ });
121
+
91
122
  _defineProperty(this, "restore", async ({
92
123
  pgUrl,
93
- pgVersion
124
+ pgBinDir
94
125
  }) => {
95
- const version = this.getVersion(pgVersion);
126
+ // const version = this.getVersion(pgVersion)
96
127
  const storage = this.storageBackendProvider.provide(); // To validate
97
128
 
98
129
  const con = parseConnectionUrl(pgUrl);
99
- this.log.info(`Restoring Postgres database, version=${version}`);
130
+ this.log.info(`Restoring Postgres database`);
100
131
  await this.fs.inTmpDir('pg-restore', async dirStr => {
101
132
  const dir = _Path.AbsPath.from(dirStr);
102
133
 
103
- storage.restore(dir); // TODO check if the dir contains the with the expected name
134
+ await storage.restore(dir); // TODO check if the dir contains the with the expected name
104
135
  // TODO add e2e test for docker
105
136
 
106
- const zipFile = this.fs.listFiles(dir)[0];
107
- this.log.debug('Will run psql import from %s', zipFile.str()); // Database is set to 'postgres' so that also users which don't have db created can use db-less URL
137
+ this.log.info('Backup dir restored');
138
+ const file = PgBackupController.dumpFileName;
139
+ const zipFile = file + '.gz';
140
+ const zipPath = this.fs.listFiles(dir).find(n => n.basename() === zipFile);
141
+ if (!zipPath) throw new Error(`Expected to find file ${zipFile} in the restore data`);
142
+ this.log.info(`Decompressing ${zipFile}`);
143
+ await this.sh.asyncExec(`gzip --decompress -v ${zipFile}`, {
144
+ cwd: dir.str()
145
+ }, {
146
+ stdout: o => this.log.trace('gzip: ' + o),
147
+ stderr: o => this.log.info('gzip: ' + o)
148
+ }); // Database is set to 'postgres' so that also users which don't have db created can use db-less URL
108
149
  // Restoring user needs to have the admin access anyway
109
150
 
110
151
  const connectionArgs = `-h ${con.host} -p ${con.port} -U ${con.username} -d postgres`; // Don't forget that this itself might be run in docker
111
152
  // therefore volume mounts are not usable (will apply to the location at host)
112
153
 
113
- this.sh.exec(`gzip --decompress --stdout ${zipFile.str()} | docker run --network host -i ` + `-e PGPASSWORD=${con.password} ` + `postgres:${version} ` + `psql ${connectionArgs} -v ON_ERROR_STOP=0`);
154
+ let cmd = `psql ${connectionArgs} -v ON_ERROR_STOP=0 < ${file}`;
155
+
156
+ if (pgBinDir) {
157
+ cmd = this.addPgBinDir(cmd, pgBinDir);
158
+ }
159
+
160
+ await this.sh.asyncExec(cmd, {
161
+ cwd: dir.str(),
162
+ env: {
163
+ PGPASSWORD: con.password
164
+ }
165
+ }, {
166
+ stdout: o => this.log.trace(o),
167
+ stderr: o => this.log.error(o)
168
+ });
169
+ this.log.info(`Data successfully inserted into database @${con.host}`);
114
170
  });
115
171
  });
172
+
173
+ _defineProperty(this, "addPgBinDir", (cmd, pgBinDir) => {
174
+ this.log.info(`Using PG bin dir ${pgBinDir}`);
175
+ this.fs.ensureIsDir(_Path.AbsPath.from(pgBinDir));
176
+ return pgBinDir + '/' + cmd;
177
+ });
116
178
  }
117
179
 
118
- }, _defineProperty(_class2, "defaultPgVersion", '14.2'), _temp)) || _class) || _class) || _class);
180
+ }, _defineProperty(_class2, "defaultPgVersion", '14.2'), _defineProperty(_class2, "dumpFileName", 'pg-dump.sql'), _temp)) || _class) || _class) || _class);
119
181
  exports.PgBackupController = PgBackupController;
package/lib/cli/app.js CHANGED
@@ -45,6 +45,7 @@ const singleStorageOptions = [storageNameOpt, {
45
45
  }];
46
46
  const backupTagOption = {
47
47
  name: 'backup-tag',
48
+ fromConfig: 'backup.tag',
48
49
  convertCase: true
49
50
  };
50
51
  const backupOptions = [backupTagOption];
@@ -117,6 +118,17 @@ const restic = (0, _nclif.cmdGroup)({
117
118
  snapshots: (0, _nclif.cmd)({
118
119
  run: (_, c, p) => c.get(_ResticController.ResticController).listSnapshots(p.stdout)
119
120
  }),
121
+ env: (0, _nclif.cmd)({
122
+ run: ({
123
+ export: doExport
124
+ }, c) => c.get(_ResticController.ResticController).printEnv(doExport),
125
+ options: [{
126
+ name: 'export',
127
+ type: 'boolean',
128
+ description: 'Add "export" statement to each env variable inoutput',
129
+ convertCase: true
130
+ }]
131
+ }),
120
132
  cmd: (0, _nclif.cmd)({
121
133
  positionals: [{
122
134
  name: 'cmd',
@@ -132,18 +144,30 @@ const pg = (0, _nclif.cmdGroup)({
132
144
  description: 'Postgres backup commands',
133
145
  options: [...singleStorageOptions, {
134
146
  name: 'pg-url',
147
+ description: 'Postgres URL',
135
148
  convertCase: true,
136
149
  fromConfig: 'pg.url',
137
150
  required: true
138
151
  }, {
139
- name: 'pg-version',
152
+ name: 'pg-bin-dir',
153
+ description: 'A directory with Postgres binaries (if the ones on the path should not be used)',
140
154
  convertCase: true,
141
- fromConfig: 'pg.version',
142
- description: `Postgres version - default is ${_PgBackupController.PgBackupController.defaultPgVersion}`
143
- }],
155
+ fromConfig: 'pg.bin-dir'
156
+ } // {
157
+ // name: 'pg-version',
158
+ // convertCase: true,
159
+ // fromConfig: 'pg.version',
160
+ // description: `Postgres version - default is ${PgBackupController.defaultPgVersion}`
161
+ // }
162
+ ],
144
163
  commands: {
145
164
  backup: (0, _nclif.cmd)({
146
- options: backupOptions,
165
+ options: [...backupOptions, {
166
+ name: 'backup-name',
167
+ convertCase: true,
168
+ fromConfig: 'backup.name',
169
+ required: true
170
+ }],
147
171
  run: (inp, c) => c.get(_PgBackupController.PgBackupController).backup(inp)
148
172
  }),
149
173
  restore: (0, _nclif.cmd)({
@@ -5,16 +5,16 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.readConfig = void 0;
7
7
 
8
+ var _expandAndCreatePath = require("./expandAndCreatePath");
9
+
10
+ var _Fs = require("../fs/Fs");
11
+
8
12
  var _Path = require("../fs/path/Path");
9
13
 
10
14
  var _readYaml = require("../fs/readYaml");
11
15
 
12
16
  var _validateConfigAgainstSchema = require("./validateConfigAgainstSchema");
13
17
 
14
- var _expandAndCreatePath = require("./expandAndCreatePath");
15
-
16
- var _Fs = require("../fs/Fs");
17
-
18
18
  const expandPaths = (cfg, exp) => {
19
19
  cfg.aggregates.forEach(a => {
20
20
  a.sources.file.forEach(f => {
@@ -37,7 +37,8 @@ const readFromFile = cwd => {
37
37
  const cfg = fs.exists(cfgPath) ? (0, _readYaml.readYaml)(cfgPath.str()) : {
38
38
  aggregates: [],
39
39
  storage: []
40
- };
40
+ }; // eslint-disable-next-line no-undef
41
+
41
42
  (0, _validateConfigAgainstSchema.validateConfigAgainstSchema)(cfg);
42
43
  return cfg;
43
44
  };
@@ -5,12 +5,12 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.expandStrPath = exports.expandAndCreatePath = void 0;
7
7
 
8
- var _Path = require("../fs/path/Path");
9
-
10
8
  var _os = _interopRequireDefault(require("os"));
11
9
 
12
10
  var _path = _interopRequireDefault(require("path"));
13
11
 
12
+ var _Path = require("../fs/path/Path");
13
+
14
14
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
15
15
 
16
16
  const expandStrPath = (path, pwd) => {
@@ -5,16 +5,6 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.createContext = void 0;
7
7
 
8
- var _Shell = require("../tools/shell/Shell");
9
-
10
- var _FsSyncer = require("../fs/fssync/FsSyncer");
11
-
12
- var _Gpg = require("../tools/gpg/Gpg");
13
-
14
- var _Fs = require("../fs/Fs");
15
-
16
- var _SshKeyManager = require("../tools/ssh/SshKeyManager");
17
-
18
8
  var _inversify = require("inversify");
19
9
 
20
10
  var _ContextSymbols = require("./ContextSymbols");
@@ -39,6 +29,16 @@ var _PgBackupController = require("../bup/pg/PgBackupController");
39
29
 
40
30
  var _Zipper = require("../tools/compression/Zipper");
41
31
 
32
+ var _FsSyncer = require("../fs/fssync/FsSyncer");
33
+
34
+ var _Gpg = require("../tools/gpg/Gpg");
35
+
36
+ var _Shell = require("../tools/shell/Shell");
37
+
38
+ var _Fs = require("../fs/Fs");
39
+
40
+ var _SshKeyManager = require("../tools/ssh/SshKeyManager");
41
+
42
42
  const createContext = (cfg, inp) => {
43
43
  const c = new _inversify.Container();
44
44
  c.bind(_ContextSymbols.AppConfig_).toConstantValue(cfg);
package/lib/fs/Fs.js CHANGED
@@ -7,9 +7,9 @@ exports.Fs = void 0;
7
7
 
8
8
  var _fs = _interopRequireDefault(require("fs"));
9
9
 
10
- var _path = _interopRequireDefault(require("path"));
10
+ var _promises = _interopRequireDefault(require("fs/promises"));
11
11
 
12
- var _Path = require("./path/Path");
12
+ var _path = _interopRequireDefault(require("path"));
13
13
 
14
14
  var _inversify = require("inversify");
15
15
 
@@ -19,6 +19,8 @@ var _os = _interopRequireDefault(require("os"));
19
19
 
20
20
  var _logging = require("../log/logging");
21
21
 
22
+ var _Path = require("./path/Path");
23
+
22
24
  var _dec, _dec2, _dec3, _class;
23
25
 
24
26
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
@@ -73,10 +75,23 @@ let Fs = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.metadata("design:
73
75
  try {
74
76
  await withDir(dir);
75
77
  } finally {
78
+ // TODO handle if not exists?
76
79
  this.rmDir(_Path.AbsPath.from(dir));
77
80
  }
78
81
  });
79
82
 
83
+ _defineProperty(this, "inNewDir", async (path, withDir) => {
84
+ let created = false;
85
+
86
+ try {
87
+ await this.ensureDir(path, true);
88
+ created = true;
89
+ await withDir();
90
+ } finally {
91
+ if (created) this.rmDir(path);
92
+ }
93
+ });
94
+
80
95
  _defineProperty(this, "mkTmpDir", name => {
81
96
  const dir = _fs.default.mkdtempSync(_path.default.resolve(_os.default.tmpdir(), name));
82
97
 
@@ -97,7 +112,7 @@ let Fs = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.metadata("design:
97
112
  return JSON.parse(file);
98
113
  });
99
114
 
100
- _defineProperty(this, "ensureDir", path => {
115
+ _defineProperty(this, "ensureDir", async (path, mustBeEmpty = false) => {
101
116
  if (!this.exists(path)) {
102
117
  this.log.trace('Dir %s does not exist. Wil be created', path.str());
103
118
 
@@ -106,13 +121,18 @@ let Fs = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.metadata("design:
106
121
  });
107
122
  } else {
108
123
  if (this.isFile(path)) throw new Error(`${path.str()} exists and is not a directory`);
124
+
125
+ if (mustBeEmpty) {
126
+ const content = await _promises.default.readdir(path.str());
127
+ if (content.length) throw new Error(`${path.str()} exists but is not empty`);
128
+ }
109
129
  }
110
130
  });
111
131
 
112
- _defineProperty(this, "ensureParentDir", path => {
132
+ _defineProperty(this, "ensureParentDir", async path => {
113
133
  const parent = path.parent();
114
134
  if (this.exists(parent)) return;
115
- this.ensureDir(parent);
135
+ await this.ensureDir(parent);
116
136
  });
117
137
 
118
138
  _defineProperty(this, "exists", path => _fs.default.existsSync(path.str()));
@@ -34,7 +34,7 @@ let FsSyncer = (_dec = (0, _inversify.injectable)(), _dec2 = Reflect.metadata("d
34
34
  if (this.fs.isFile(from)) this.syncFile(from, to);else this.syncDirs(from, to);
35
35
  });
36
36
 
37
- _defineProperty(this, "syncDirs", (from, to) => {
37
+ _defineProperty(this, "syncDirs", async (from, to) => {
38
38
  this.fs.ensureIsDir(from);
39
39
  this.log.info('Syncing dir', from.str(), '==>', to.str());
40
40
  const fromPath = toUnixDirString(from);
@@ -12,15 +12,15 @@ class BackupStorage {
12
12
  this.backend = backend;
13
13
  this.props = props;
14
14
 
15
- _defineProperty(this, "store", from => {
15
+ _defineProperty(this, "store", async from => {
16
16
  const {
17
17
  tag
18
18
  } = this.props;
19
19
  const tags = tag ? [tag] : undefined;
20
- this.backend.backup(from, tags);
20
+ await this.backend.backup(from, tags);
21
21
  });
22
22
 
23
- _defineProperty(this, "restore", to => {
23
+ _defineProperty(this, "restore", async to => {
24
24
  const {
25
25
  snapshotId
26
26
  } = this.props;
@@ -17,7 +17,7 @@ class RcloneBackupBackend {
17
17
 
18
18
  _defineProperty(this, "log", (0, _logging.classObjLog)(this));
19
19
 
20
- _defineProperty(this, "backup", (from, tags) => {
20
+ _defineProperty(this, "backup", async (from, tags) => {
21
21
  if (tags) throw new _nclif.InvalidInputError(`Rclone does not support tags`);
22
22
  this.log.info('Performing backup', {
23
23
  from
@@ -25,14 +25,14 @@ class RcloneBackupBackend {
25
25
  this.rclone.backup(from);
26
26
  });
27
27
 
28
- _defineProperty(this, "restoreLatest", to => {
28
+ _defineProperty(this, "restoreLatest", async to => {
29
29
  this.log.info('Restoring', {
30
30
  to
31
31
  });
32
32
  this.rclone.restore(to);
33
33
  });
34
34
 
35
- _defineProperty(this, "restoreSnapshot", (to, snapshotId) => {
35
+ _defineProperty(this, "restoreSnapshot", async (to, snapshotId) => {
36
36
  throw new _nclif.InvalidInputError(`Rclone does not support snaphosts`);
37
37
  });
38
38
  }
@@ -15,11 +15,12 @@ var _b2TestConfig = require("../../../test/b2TestConfig");
15
15
  var _testHelper = require("../../../test/testHelper");
16
16
 
17
17
  describe('RcloneClient', () => {
18
- const getRestoreDir = (0, _test.testdataDirBuilder)('b2/restore');
18
+ // TODO clean up dir locations more
19
+ const testData = (0, _test.testdataDirBuilder)('rclone');
19
20
  describe('local', () => {
20
21
  const backupDir = (0, _test.testDir)('backup/1');
21
- const restoreDir = getRestoreDir('local');
22
- const storeDir = (0, _test.testdataDir)('b2/store');
22
+ const restoreDir = testData('local');
23
+ const storeDir = testData('store');
23
24
  const cfg = {
24
25
  type: 'local',
25
26
  location: (0, _test.testdataDir)(storeDir),
@@ -68,11 +69,11 @@ describe('RcloneClient', () => {
68
69
  password: 'foo123'
69
70
  }
70
71
  }, new _Shell.Shell());
71
- const restoreDir = getRestoreDir('b2');
72
+ const restoreDir = testData('b2');
72
73
  afterAll(() => {
73
74
  (0, _testHelper.cleanDir)(restoreDir);
74
75
  });
75
- it('push adn restore data', () => {
76
+ it('push and restore data', () => {
76
77
  sut.backup(_Path.AbsPath.from(backupDir));
77
78
  sut.restore(_Path.AbsPath.from(restoreDir));
78
79
  const res = (0, _dirCompare.compareSync)(backupDir, restoreDir);
@@ -17,18 +17,18 @@ class ResticBackupBackend {
17
17
 
18
18
  _defineProperty(this, "log", (0, _logging.classObjLog)(this));
19
19
 
20
- _defineProperty(this, "backup", (from, tags = []) => {
20
+ _defineProperty(this, "backup", async (from, tags = []) => {
21
21
  this.log.info(`Performing backup from=${from}, tags=${tags}`);
22
- this.restic.backup(from, _Path.RelativePath.from('.'), tags);
22
+ await this.restic.backup(from, _Path.RelativePath.from('.'), tags);
23
23
  this.restic.forget();
24
24
  });
25
25
 
26
- _defineProperty(this, "restoreLatest", to => {
26
+ _defineProperty(this, "restoreLatest", async to => {
27
27
  this.log.info(`Restoring latest snapshot to=${to}`);
28
28
  this.restic.restore(to);
29
29
  });
30
30
 
31
- _defineProperty(this, "restoreSnapshot", (to, snapshotId) => {
31
+ _defineProperty(this, "restoreSnapshot", async (to, snapshotId) => {
32
32
  this.log.info(`Restoring snapshot '${snapshotId}'`, {
33
33
  to
34
34
  });
@@ -30,10 +30,13 @@ class ResticClient {
30
30
 
31
31
  _defineProperty(this, "log", (0, _logging.classObjLog)(this));
32
32
 
33
- _defineProperty(this, "prepareRepo", () => {
33
+ _defineProperty(this, "prepareRepo", async () => {
34
34
  this.log.info('Preparing a restic repo at %s', this.url);
35
- this.shell.exec(`restic init`, {
35
+ await this.shell.asyncExec(`restic init`, {
36
36
  env: this.env()
37
+ }, {
38
+ stdout: o => this.log.trace(o),
39
+ stderr: o => this.log.error(o)
37
40
  });
38
41
  });
39
42
 
@@ -43,15 +46,18 @@ class ResticClient {
43
46
  });
44
47
  });
45
48
 
46
- _defineProperty(this, "backup", (cwd, from, tags = []) => {
49
+ _defineProperty(this, "backup", async (cwd, from, tags = []) => {
47
50
  this.log.debug('Running backup for repo=%s', this.url);
48
51
  let cmd = `restic backup ${from.str()}`;
49
52
  tags.forEach(t => {
50
53
  cmd += ` --tag=${t}`;
51
54
  });
52
- this.shell.exec(cmd, {
55
+ await this.shell.asyncExec(cmd, {
53
56
  cwd: cwd.str(),
54
57
  env: this.env()
58
+ }, {
59
+ stdout: o => this.log.trace(o),
60
+ stderr: o => this.log.error(o)
55
61
  });
56
62
  });
57
63
 
@@ -77,14 +83,17 @@ class ResticClient {
77
83
  });
78
84
 
79
85
  _defineProperty(this, "snapshots", () => {
80
- this.log.debug('Listing snapshots of repo=%', this.url);
86
+ this.log.debug(`Listing snapshots of repo=${this.url}`);
81
87
  const out = this.shell.execAndReturnString(`restic snapshots --json`, {
82
88
  env: this.env()
83
89
  });
84
90
  const res = JSON.parse(out);
85
91
  return res.map(r => ({
86
92
  id: r.id,
93
+ short_id: r.short_id,
87
94
  time: new Date(r.time),
95
+ paths: r.paths,
96
+ hostname: r.hostname,
88
97
  tags: r.tags
89
98
  }));
90
99
  });
@@ -30,8 +30,9 @@ describe('ResticClient', () => {
30
30
  (0, _testHelper.cleanDir)(repoDir);
31
31
  (0, _testHelper.cleanDir)(restoreDir);
32
32
  });
33
- it('create repo', () => {
34
- sut.prepareRepo();
33
+ it('create repo', async () => {
34
+ await sut.prepareRepo();
35
+ console.log('repo created');
35
36
  });
36
37
  it('push data & forget', () => {
37
38
  const from = _Path.AbsPath.from(backupDir);
@@ -76,7 +77,7 @@ describe('ResticClient', () => {
76
77
  afterAll(() => {
77
78
  (0, _testHelper.cleanDir)(restoreDir);
78
79
  });
79
- it('push adn restore data', () => {
80
+ it('push and restore data', () => {
80
81
  // sut.prepareRepo()
81
82
  sut.backup(_Path.AbsPath.from(backupDir), _Path.RelativePath.from('.'));
82
83
  sut.restore(_Path.AbsPath.from(restoreDir));
@@ -39,10 +39,11 @@ let ResticController = (_dec = (0, _inversify.injectable)(), _dec2 = function (t
39
39
  return this.restFact.createClient(props);
40
40
  });
41
41
 
42
- _defineProperty(this, "initRepo", () => {
42
+ _defineProperty(this, "initRepo", async () => {
43
43
  const props = this.storageConfigProvider.provide();
44
44
  this.log.info(`Initializing Restic repo ${props.repo}`);
45
- this.client().prepareRepo();
45
+ await this.client().prepareRepo();
46
+ this.log.info('Repo initialized');
46
47
  });
47
48
 
48
49
  _defineProperty(this, "listSnapshots", print => {
@@ -54,6 +55,15 @@ let ResticController = (_dec = (0, _inversify.injectable)(), _dec2 = function (t
54
55
  const res = this.client().runCmd(cmd);
55
56
  print(res.toString());
56
57
  });
58
+
59
+ _defineProperty(this, "printEnv", (doExport = false) => {
60
+ const env = this.client().env();
61
+ const out = Object.entries(env).map(([k, v]) => `${k}=${v}`).map(line => {
62
+ if (!doExport) return line;
63
+ return `export ${line}`;
64
+ }).join('\n');
65
+ console.log(out);
66
+ });
57
67
  }
58
68
 
59
69
  }) || _class) || _class) || _class) || _class);
@@ -48,7 +48,7 @@ let Shell = (_dec = (0, _inversify.injectable)(), _dec(_class = class Shell {
48
48
  });
49
49
  });
50
50
 
51
- _defineProperty(this, "asyncExec", (cmd, onStdout, ops = {}) => {
51
+ _defineProperty(this, "asyncExec", (cmd, ops = {}, on) => {
52
52
  let onDone;
53
53
  let onError;
54
54
  const p = new Promise((res, rej) => {
@@ -60,8 +60,8 @@ let Shell = (_dec = (0, _inversify.injectable)(), _dec(_class = class Shell {
60
60
  cwd: ops.cwd,
61
61
  env: ops.env
62
62
  });
63
- r.stdout.on('data', onStdout);
64
- r.stderr.on('data', data => console.error(data.toString()));
63
+ r.stdout.on('data', o => on.stdout(o.toString()));
64
+ r.stderr.on('data', o => on.stderr(o.toString()));
65
65
  r.on('exit', status => {
66
66
  if (status === 0) onDone();else onError(new Error('Exited with non zero return code: ' + status));
67
67
  });
@@ -14,8 +14,13 @@ it.skip('execAndReturnStream', async () => {
14
14
  expect(() => sh.execAndReturnString(bigStdOutCme())).toThrow('spawnSync /bin/sh ENOBUFS'); //
15
15
 
16
16
  let out = '';
17
- await sh.asyncExec(bigStdOutCme(), data => {
18
- out = data.toString().trim();
17
+ await sh.asyncExec(bigStdOutCme(), {}, {
18
+ stdout: data => {
19
+ out = data.toString().trim();
20
+ },
21
+ stderr: data => {
22
+ console.error(data);
23
+ }
19
24
  });
20
25
  expect(out).toEndWith('Done');
21
26
  }, 5000_000);
@@ -5,14 +5,14 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.SshKeyManager = void 0;
7
7
 
8
- var _Fs = require("../../fs/Fs");
9
-
10
- var _Path = require("../../fs/path/Path");
11
-
12
8
  var _os = _interopRequireDefault(require("os"));
13
9
 
14
10
  var _inversify = require("inversify");
15
11
 
12
+ var _Fs = require("../../fs/Fs");
13
+
14
+ var _Path = require("../../fs/path/Path");
15
+
16
16
  var _dec, _dec2, _dec3, _class;
17
17
 
18
18
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
package/package.json CHANGED
@@ -1,15 +1,16 @@
1
1
  {
2
2
  "name": "@ps-aux/nodebup",
3
- "version": "0.11.1",
3
+ "version": "0.12.1",
4
4
  "description": "",
5
5
  "module": "lib/index.js",
6
6
  "main": "lib/index.js",
7
7
  "scripts": {
8
+ "run": "ts-node ./src/cli/bin.ts",
8
9
  "build": "rm -rf build && babel --extensions '.ts,.js,.md' src -d lib src",
9
10
  "publish:local": "npm run build && chmod +x lib/cli/bin.js && npm link",
10
11
  "pub": "npm publish --access public",
11
- "test": "jest",
12
- "test:ci": "jest --group=-no-ci",
12
+ "test": "jest --runInBand",
13
+ "test:ci": "jest --group=-no-ci --runInBand",
13
14
  "tc": "tsc --noEmit",
14
15
  "format": "prettier \"**/*.{js,ts,tsx}\" --write",
15
16
  "build-n-run": "npm run build && ",
@@ -68,6 +69,7 @@
68
69
  "pg": "^8.7.3",
69
70
  "prettier": "^2.5.1",
70
71
  "ts-jest": "^27.1.2",
72
+ "ts-node": "^10.9.2",
71
73
  "typescript": "^4.5.4"
72
74
  },
73
75
  "lint-staged": {