uzdu 1.0.17 → 1.1.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.
- package/README.md +7 -7
- package/lib/{chunk-VO6FBXH2.js → chunk-463DKYHI.js} +110 -36
- package/lib/{chunk-7B56UNA6.js → chunk-LFEIDM4S.js} +1 -1
- package/lib/{chunk-OIXJ4D3Z.js → chunk-WWXWWCCX.js} +32 -0
- package/lib/uzdu-copy.js +1 -1
- package/lib/uzdu-download.js +2 -2
- package/lib/uzdu-metadata.js +1 -1
- package/lib/uzdu-unzip.js +1 -1
- package/lib/uzdu-upload.js +7 -10
- package/lib/uzdu-zip.js +1 -1
- package/lib/uzdu.d.ts +49 -5
- package/lib/uzdu.js +5 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -29,19 +29,19 @@ bunx uzdu -h
|
|
|
29
29
|
|
|
30
30
|
### uploading
|
|
31
31
|
|
|
32
|
-
- [Amazon S3](https://docs.aws.amazon.com/s3/) `npx uzdu upload aws -
|
|
33
|
-
- [Azure Blob Storage](https://azure.microsoft.com/en-us/products/storage/blobs) `npx uzdu upload
|
|
34
|
-
- [Nexus](https://support.sonatype.com/hc/en-us/articles/115006744008-Repository-How-can-I-programmatically-upload-files-into-Nexus-3#DirectUploadusingHTTPPUTtotheRepositoryPath) `npx
|
|
35
|
-
- SSH/
|
|
32
|
+
- [Amazon S3](https://docs.aws.amazon.com/s3/) `npx uzdu upload aws --dotenv /projects/environments/test.env -- build/index.html -- uzdu:ru-central1-d:http://storage.yandexcloud.net`
|
|
33
|
+
- [Azure Blob Storage](https://azure.microsoft.com/en-us/products/storage/blobs) `AZURE_STORAGE_CONNECTION_STRING=...; npx uzdu upload azure build/ $web`
|
|
34
|
+
- [Nexus](https://support.sonatype.com/hc/en-us/articles/115006744008-Repository-How-can-I-programmatically-upload-files-into-Nexus-3#DirectUploadusingHTTPPUTtotheRepositoryPath) `npx upload http --header "Authorization: Basic TOKEN=" -- website.zip https://nexus/repository/private-raw/dist/test-uzdu/website.zip",`
|
|
35
|
+
- SSH/SFTP `npx uzdu upload ssh /projects/website/build/ sftp://root:password@example.localtest.me/var/www/html/`
|
|
36
36
|
|
|
37
37
|
### downloading
|
|
38
38
|
|
|
39
|
-
- http `npx uzdu download http -
|
|
39
|
+
- http `npx uzdu download http --dotenv --header \"Authorization: Basic TOKEN=\" https://nexus/repository/private-raw/dist/test-uzdu/website.zip website.zip`
|
|
40
40
|
|
|
41
41
|
### working with zip-archives
|
|
42
42
|
|
|
43
|
-
- zip `npx uzdu zip
|
|
44
|
-
- unzip `npx uzdu unzip
|
|
43
|
+
- zip `npx uzdu zip build/ ./build.zip`
|
|
44
|
+
- unzip `npx uzdu unzip /tmp/repo.zip ./src`
|
|
45
45
|
|
|
46
46
|
|
|
47
47
|
## For developers
|
|
@@ -3,8 +3,9 @@ import {
|
|
|
3
3
|
getEnvironment,
|
|
4
4
|
initEnvironment,
|
|
5
5
|
listFiles,
|
|
6
|
-
resolvePath
|
|
7
|
-
|
|
6
|
+
resolvePath,
|
|
7
|
+
runSequentially
|
|
8
|
+
} from "./chunk-WWXWWCCX.js";
|
|
8
9
|
|
|
9
10
|
// src/azure.ts
|
|
10
11
|
var azure_exports = {};
|
|
@@ -95,7 +96,12 @@ async function upload2(dir, s3config, metadataFile = ".metadata.json") {
|
|
|
95
96
|
}
|
|
96
97
|
await Promise.all(Object.entries(files).map(async ([file, absFile]) => {
|
|
97
98
|
const filePath = absFile;
|
|
98
|
-
const fileContent =
|
|
99
|
+
const fileContent = await new Promise((resolve, reject) => {
|
|
100
|
+
fs2.readFile(filePath, (err, data) => {
|
|
101
|
+
if (err) reject(err);
|
|
102
|
+
else resolve(data);
|
|
103
|
+
});
|
|
104
|
+
});
|
|
99
105
|
const params = {
|
|
100
106
|
Bucket: s3config.bucket,
|
|
101
107
|
Key: file,
|
|
@@ -126,6 +132,7 @@ async function upload2(dir, s3config, metadataFile = ".metadata.json") {
|
|
|
126
132
|
// src/ssh.ts
|
|
127
133
|
var ssh_exports = {};
|
|
128
134
|
__export(ssh_exports, {
|
|
135
|
+
execute: () => execute,
|
|
129
136
|
getConnectConfig: () => getConnectConfig,
|
|
130
137
|
getCredentials: () => getCredentials,
|
|
131
138
|
getDirMap: () => getDirMap,
|
|
@@ -137,26 +144,29 @@ import { Client } from "ssh2";
|
|
|
137
144
|
import fs3 from "fs";
|
|
138
145
|
import path3 from "path";
|
|
139
146
|
import deepmerge from "deepmerge";
|
|
140
|
-
async function upload3(source, sftpUrl,
|
|
147
|
+
async function upload3(source, sftpUrl, options) {
|
|
141
148
|
await new Promise((resolve, reject) => {
|
|
142
149
|
fs3.stat(source, async (err, stats) => {
|
|
150
|
+
if (err) {
|
|
151
|
+
reject(err);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
143
154
|
if (stats.isSymbolicLink()) {
|
|
144
155
|
reject(new Error(`${source} is symlink`));
|
|
145
156
|
} else {
|
|
146
157
|
let sshConnection;
|
|
147
158
|
try {
|
|
148
159
|
const _connectConfig = getConnectConfig(sftpUrl);
|
|
149
|
-
const _sshCredentials =
|
|
160
|
+
const _sshCredentials = _connectConfig.password ? void 0 : getCredentials(options);
|
|
150
161
|
const connectConfig = { ..._connectConfig, ..._sshCredentials };
|
|
151
162
|
sshConnection = await connect(connectConfig);
|
|
152
163
|
const files = await listFiles(source);
|
|
153
|
-
const destination =
|
|
164
|
+
const destination = normilizeSftpPath(_connectConfig.path) || "";
|
|
154
165
|
const _source = source.replace(/\/+$/, "");
|
|
155
166
|
await mkdirs(sshConnection, destination, files);
|
|
156
167
|
await uploadFiles(files, _source, destination, sshConnection);
|
|
157
168
|
resolve();
|
|
158
169
|
} catch (e) {
|
|
159
|
-
console.error("SFTP error", e);
|
|
160
170
|
reject(e);
|
|
161
171
|
} finally {
|
|
162
172
|
sshConnection?.destroy();
|
|
@@ -165,22 +175,69 @@ async function upload3(source, sftpUrl, sshCredentials) {
|
|
|
165
175
|
});
|
|
166
176
|
});
|
|
167
177
|
}
|
|
178
|
+
async function execute(sshAddress, commands, options) {
|
|
179
|
+
await new Promise(async (resolve, reject) => {
|
|
180
|
+
let sshConnection;
|
|
181
|
+
try {
|
|
182
|
+
const _connectConfig = getConnectConfig(sshAddress);
|
|
183
|
+
const _sshCredentials = _connectConfig.password ? void 0 : getCredentials(options);
|
|
184
|
+
const connectConfig = { ..._connectConfig, ..._sshCredentials };
|
|
185
|
+
sshConnection = await connect(connectConfig);
|
|
186
|
+
const shellOptions = {
|
|
187
|
+
...options,
|
|
188
|
+
cwd: normilizeSftpPath(_connectConfig.path)
|
|
189
|
+
};
|
|
190
|
+
await shellExec(sshConnection, commands, shellOptions);
|
|
191
|
+
resolve();
|
|
192
|
+
} catch (e) {
|
|
193
|
+
reject(e);
|
|
194
|
+
} finally {
|
|
195
|
+
sshConnection?.destroy();
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
}
|
|
168
199
|
async function mkdirs(sshConnection, destination, sources) {
|
|
169
200
|
const fileMap = getDirMap(sources);
|
|
170
201
|
const makeDirs = getMakeDirs(fileMap, destination);
|
|
171
202
|
const commands = makeDirs ? makeDirs.map((dir) => `mkdir -p "${dir}"`) : [`mkdir -p "${destination}"`];
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
203
|
+
await shellExec(sshConnection, commands);
|
|
204
|
+
}
|
|
205
|
+
async function shellExec(sshConnection, commands, options) {
|
|
206
|
+
if (!commands) {
|
|
207
|
+
return Promise.reject("shellExec did not get any command");
|
|
208
|
+
}
|
|
209
|
+
if (options?.cwd) {
|
|
210
|
+
const command = `set -e; cd ${options.cwd}; set +e; ${commands.join(";")}`;
|
|
211
|
+
await shellCommand(sshConnection, command, options);
|
|
212
|
+
} else {
|
|
213
|
+
const shellCommands = commands.map((command) => () => shellCommand(sshConnection, command, options));
|
|
214
|
+
await runSequentially(shellCommands);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
async function shellCommand(sshClient, command, options) {
|
|
218
|
+
return new Promise((res, rej) => {
|
|
219
|
+
const decoder = new TextDecoder();
|
|
220
|
+
sshClient.exec(command, (err, stream) => {
|
|
221
|
+
if (err) rej(err);
|
|
222
|
+
stream.on("close", (code, signal) => {
|
|
223
|
+
if (code != 0) {
|
|
224
|
+
options?.callback?.({ error: `closing SSH by signal=${signal} and exit code=${code}`, code, signal });
|
|
225
|
+
rej(new Error(`Close code ${code}`));
|
|
226
|
+
} else {
|
|
227
|
+
res(code);
|
|
228
|
+
}
|
|
229
|
+
}).on("exit", (code, signal) => {
|
|
230
|
+
if (code != 0) {
|
|
231
|
+
options?.callback?.({ error: `Exit command signal=${signal} and exit code=${code}`, code, signal });
|
|
232
|
+
rej(new Error(`Exit code ${code}`));
|
|
233
|
+
} else {
|
|
234
|
+
res(code);
|
|
235
|
+
}
|
|
236
|
+
}).on("data", (message) => {
|
|
237
|
+
options?.callback?.({ message: decoder.decode(message).replace(/\n$/, "") });
|
|
238
|
+
}).stderr.on("data", (error) => {
|
|
239
|
+
options?.callback?.({ error: decoder.decode(error) });
|
|
240
|
+
});
|
|
184
241
|
});
|
|
185
242
|
});
|
|
186
243
|
}
|
|
@@ -210,16 +267,14 @@ function uploadFiles(sourceFiles, source, destination, sshConnection) {
|
|
|
210
267
|
return new Promise((resolve, reject) => {
|
|
211
268
|
sshConnection.sftp(async (err, sftp) => {
|
|
212
269
|
if (err) {
|
|
213
|
-
console.error("uploadFiles error");
|
|
214
270
|
reject(err);
|
|
215
271
|
} else {
|
|
216
272
|
if (Object.keys(sourceFiles).length == 1) {
|
|
217
273
|
const lstat2 = fs3.lstatSync(source);
|
|
218
274
|
if (lstat2.isFile()) {
|
|
219
|
-
const dest = path3.join(destination, sourceFiles[0]).replace(/\\/g, "/");
|
|
275
|
+
const dest = path3.join(destination, Object.keys(sourceFiles)[0]).replace(/\\/g, "/");
|
|
220
276
|
const src = source;
|
|
221
277
|
await _uploadFile(src, dest, sftp).then(() => resolve()).catch((e) => {
|
|
222
|
-
console.error(src);
|
|
223
278
|
reject(e);
|
|
224
279
|
});
|
|
225
280
|
return;
|
|
@@ -235,8 +290,6 @@ function uploadFiles(sourceFiles, source, destination, sshConnection) {
|
|
|
235
290
|
const dest = path3.join(destination, baseName).replace(/\\/g, "/");
|
|
236
291
|
const promise = new Promise((res, rej) => {
|
|
237
292
|
_uploadFile(absPath, dest, sftp).then(() => res()).catch((e) => {
|
|
238
|
-
console.error(absPath);
|
|
239
|
-
console.error(e);
|
|
240
293
|
rej(e);
|
|
241
294
|
});
|
|
242
295
|
});
|
|
@@ -253,7 +306,7 @@ async function connect(sshConfig) {
|
|
|
253
306
|
try {
|
|
254
307
|
return await new Promise((resolve, reject) => {
|
|
255
308
|
conn.on("error", (e) => {
|
|
256
|
-
reject(
|
|
309
|
+
reject(e);
|
|
257
310
|
}).on("ready", () => {
|
|
258
311
|
resolve(conn);
|
|
259
312
|
}).connect({
|
|
@@ -273,7 +326,6 @@ async function connect(sshConfig) {
|
|
|
273
326
|
});
|
|
274
327
|
});
|
|
275
328
|
} catch (e) {
|
|
276
|
-
console.error("Connection failed", e);
|
|
277
329
|
conn.destroy();
|
|
278
330
|
throw e;
|
|
279
331
|
}
|
|
@@ -333,7 +385,7 @@ function getCredentials(options) {
|
|
|
333
385
|
}
|
|
334
386
|
} else {
|
|
335
387
|
const uzduPassword = process.env.UZDU_SSH_PASSWORD;
|
|
336
|
-
if (!uzduPassword) throw new Error("Specify
|
|
388
|
+
if (!uzduPassword) throw new Error("Specify password in SFTP URL. Otherwise consider using one of environment variables: UZDU_SSH_KEY_PATH, UZDU_SSH_KEY, UZDU_SSH_PASSWORD");
|
|
337
389
|
password = uzduPassword;
|
|
338
390
|
}
|
|
339
391
|
}
|
|
@@ -345,17 +397,34 @@ function getCredentials(options) {
|
|
|
345
397
|
return authConfig;
|
|
346
398
|
}
|
|
347
399
|
var sftpUrlRegex = /^sftp:\/\/(?:(?<username>[\w\.\-]{1,32})(?::(?<password>.+))?@)?(?:(?<host>[\w\.\-]+)|\[(?<ipv6>[\d:]+)\])(?::(?<port>\d{1,5}))?\/(?<path>.*)$/g;
|
|
348
|
-
|
|
400
|
+
var sshUrlRegex = /^(?:(?<username>[\w\.\-]{1,32})(?::(?<password>.+))?@)?(?:(?<host>[\w\.\-]+)|\[(?<ipv6>[\d:]+)\])(?::(?<port>\d{1,5}))?$/g;
|
|
401
|
+
var suspectSftpRegex = /^sftp:\/\/.+/g;
|
|
402
|
+
function getConnectConfig(url) {
|
|
403
|
+
let execArray;
|
|
404
|
+
sshUrlRegex.lastIndex = 0;
|
|
349
405
|
sftpUrlRegex.lastIndex = 0;
|
|
350
|
-
|
|
406
|
+
if (sftpUrlRegex.test(url)) {
|
|
407
|
+
sftpUrlRegex.lastIndex = 0;
|
|
408
|
+
execArray = sftpUrlRegex.exec(url);
|
|
409
|
+
} else if (sshUrlRegex.test(url)) {
|
|
410
|
+
sshUrlRegex.lastIndex = 0;
|
|
411
|
+
suspectSftpRegex.lastIndex = 0;
|
|
412
|
+
if (suspectSftpRegex.test(url)) {
|
|
413
|
+
throw new Error(`SSH URL starts with "sftp://". If it is sftp URL, then add trailing slash after hostname[:port] - ${url}/, if it is SSH URL, consider password that does not start with 2 slashes.`);
|
|
414
|
+
}
|
|
415
|
+
execArray = sshUrlRegex.exec(url);
|
|
416
|
+
} else {
|
|
417
|
+
throw new Error(`Not an SFTP or SSH address: ${url}`);
|
|
418
|
+
}
|
|
351
419
|
const { groups } = execArray ?? {};
|
|
352
420
|
const host = groups?.host || groups?.ipv6;
|
|
353
|
-
if (!host) throw new Error(`Wrong URL "${
|
|
421
|
+
if (!host) throw new Error(`Wrong URL "${url}": host or ivp6 is not specified`);
|
|
354
422
|
const username = groups?.username;
|
|
355
423
|
const password = groups?.password;
|
|
424
|
+
const path4 = groups?.path;
|
|
356
425
|
const _port = parseInt(groups.port);
|
|
357
426
|
const port = isNaN(_port) ? void 0 : _port;
|
|
358
|
-
const connectConfig = { username, password, host, port };
|
|
427
|
+
const connectConfig = { username, password, host, port, path: path4 };
|
|
359
428
|
return connectConfig;
|
|
360
429
|
}
|
|
361
430
|
function getRemoteDestination(sftpUrl) {
|
|
@@ -364,21 +433,27 @@ function getRemoteDestination(sftpUrl) {
|
|
|
364
433
|
if (!execArray) throw new Error("Wrong sftp URL");
|
|
365
434
|
if (!execArray.groups) throw new Error("Wrong URL: path is not specified");
|
|
366
435
|
const path4 = execArray.groups.path;
|
|
367
|
-
|
|
436
|
+
return normilizeSftpPath(execArray.groups?.path) || "";
|
|
437
|
+
}
|
|
438
|
+
function normilizeSftpPath(path4) {
|
|
439
|
+
if (path4 == void 0) return void 0;
|
|
440
|
+
const re = /^(?<first>[^\/]*)(?<root>\/)?(?<second>.*)?/g;
|
|
368
441
|
re.lastIndex = 0;
|
|
369
442
|
const execPathArray = re.exec(path4);
|
|
370
443
|
const { groups } = execPathArray ?? {};
|
|
371
444
|
const first = groups?.first;
|
|
372
445
|
const second = groups?.second;
|
|
446
|
+
const root = groups?.root;
|
|
373
447
|
let dest;
|
|
374
448
|
if (first) {
|
|
375
449
|
const execTild = /^(?<tild>~)/.exec(first);
|
|
376
450
|
const { groups: groups2 } = execTild ?? {};
|
|
377
|
-
dest = groups2?.tild ?
|
|
451
|
+
dest = groups2?.tild ? `${second ? second : ""}` : `/${first}${second ? `/${second}` : ""}`;
|
|
378
452
|
} else {
|
|
379
|
-
dest =
|
|
453
|
+
if (!root && !second) dest = "/";
|
|
454
|
+
else dest = path4;
|
|
380
455
|
}
|
|
381
|
-
const destination = dest.replace(/\/+$/, "");
|
|
456
|
+
const destination = dest != "/" ? dest.replace(/\/+$/, "") : dest;
|
|
382
457
|
return destination;
|
|
383
458
|
}
|
|
384
459
|
|
|
@@ -388,6 +463,5 @@ export {
|
|
|
388
463
|
upload2,
|
|
389
464
|
s3_exports,
|
|
390
465
|
upload3,
|
|
391
|
-
getCredentials,
|
|
392
466
|
ssh_exports
|
|
393
467
|
};
|
|
@@ -7,6 +7,7 @@ var __export = (target, all) => {
|
|
|
7
7
|
// src/utils.ts
|
|
8
8
|
var utils_exports = {};
|
|
9
9
|
__export(utils_exports, {
|
|
10
|
+
SequentilRunError: () => SequentilRunError,
|
|
10
11
|
addMetadata: () => addMetadata,
|
|
11
12
|
checkIsFile: () => checkIsFile,
|
|
12
13
|
doUnzip: () => doUnzip,
|
|
@@ -17,6 +18,7 @@ __export(utils_exports, {
|
|
|
17
18
|
makeZip: () => makeZip,
|
|
18
19
|
outputConfiguration: () => outputConfiguration,
|
|
19
20
|
resolvePath: () => resolvePath,
|
|
21
|
+
runSequentially: () => runSequentially,
|
|
20
22
|
safeIndex: () => safeIndex,
|
|
21
23
|
shouldBeDirectory: () => shouldBeDirectory,
|
|
22
24
|
shouldBeFile: () => shouldBeFile
|
|
@@ -230,6 +232,35 @@ function parseEnvironment(src) {
|
|
|
230
232
|
}
|
|
231
233
|
return obj;
|
|
232
234
|
}
|
|
235
|
+
async function runSequentially(tasks, breaksOnError = true) {
|
|
236
|
+
const results = [];
|
|
237
|
+
for (let i = 0; i < tasks.length; i++) {
|
|
238
|
+
try {
|
|
239
|
+
const result = await tasks[i]();
|
|
240
|
+
results.push(result);
|
|
241
|
+
} catch (err) {
|
|
242
|
+
if (breaksOnError) throw new SequentilRunError(i, `${err.message || err}`);
|
|
243
|
+
else {
|
|
244
|
+
console.error(`Error in #${i}: ${err.message || err}`);
|
|
245
|
+
results.push(null);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
;
|
|
250
|
+
return results;
|
|
251
|
+
}
|
|
252
|
+
var SequentilRunError = class extends Error {
|
|
253
|
+
/**
|
|
254
|
+
*
|
|
255
|
+
* @param message Text of the error
|
|
256
|
+
* @param index where in the sequense the error happened
|
|
257
|
+
*/
|
|
258
|
+
constructor(index, message = "Sequential Error") {
|
|
259
|
+
super(message);
|
|
260
|
+
this.index = index;
|
|
261
|
+
this.message = message;
|
|
262
|
+
}
|
|
263
|
+
};
|
|
233
264
|
|
|
234
265
|
export {
|
|
235
266
|
__export,
|
|
@@ -243,5 +274,6 @@ export {
|
|
|
243
274
|
shouldBeDirectory,
|
|
244
275
|
outputConfiguration,
|
|
245
276
|
resolvePath,
|
|
277
|
+
runSequentially,
|
|
246
278
|
utils_exports
|
|
247
279
|
};
|
package/lib/uzdu-copy.js
CHANGED
package/lib/uzdu-download.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
download
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-LFEIDM4S.js";
|
|
4
4
|
import {
|
|
5
5
|
getEnvironment,
|
|
6
6
|
initEnvironment,
|
|
7
7
|
outputConfiguration
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-WWXWWCCX.js";
|
|
9
9
|
|
|
10
10
|
// src/uzdu-download.ts
|
|
11
11
|
import { Command, Option } from "commander";
|
package/lib/uzdu-metadata.js
CHANGED
package/lib/uzdu-unzip.js
CHANGED
package/lib/uzdu-upload.js
CHANGED
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
|
-
getCredentials,
|
|
3
2
|
upload as upload2,
|
|
4
3
|
upload2 as upload3,
|
|
5
4
|
upload3 as upload4
|
|
6
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-463DKYHI.js";
|
|
7
6
|
import {
|
|
8
7
|
upload
|
|
9
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-LFEIDM4S.js";
|
|
10
9
|
import {
|
|
11
10
|
getEnvironment,
|
|
12
11
|
initEnvironment,
|
|
13
12
|
outputConfiguration,
|
|
14
13
|
resolvePath,
|
|
15
14
|
shouldBeDirectory
|
|
16
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-WWXWWCCX.js";
|
|
17
16
|
|
|
18
17
|
// src/uzdu-upload.ts
|
|
19
18
|
import { Argument, Command, Option } from "commander";
|
|
@@ -38,8 +37,8 @@ command.command("aws").description("upload to AWS S3").argument("<from>", "the d
|
|
|
38
37
|
const optConfig = { bucket: bucketName, endpoint };
|
|
39
38
|
if (region) optConfig.region = region;
|
|
40
39
|
const config = Object.assign(env, optConfig);
|
|
41
|
-
if (!config.accessKeyId) throw new Error("AWS Access Key ID is not specified. Provide an environement variable
|
|
42
|
-
if (!config.secretAccessKey) throw new Error("AWS Secret Key is not specified. Provide an environment variable
|
|
40
|
+
if (!config.accessKeyId) throw new Error("AWS Access Key ID is not specified. Provide an environement variable AWS_ACCESS_KEY_ID.");
|
|
41
|
+
if (!config.secretAccessKey) throw new Error("AWS Secret Key is not specified. Provide an environment variable AWS_SECRET_ACCESS_KEY.");
|
|
43
42
|
if (!config.region) throw new Error("AWS region is not specified. Provide it in a bucket address or as an envronment variable S3_REGION.");
|
|
44
43
|
await upload3(from, config);
|
|
45
44
|
} catch (e) {
|
|
@@ -72,7 +71,7 @@ command.command("azure").alias("az").description("upload to Azure Blob Storage")
|
|
|
72
71
|
}
|
|
73
72
|
const connectionString = process.env.AZURE_STORAGE_CONNECTION_STRING;
|
|
74
73
|
if (!connectionString) {
|
|
75
|
-
throw new Error("AZURE_STORAGE_CONNECTION_STRING is absent in environment variables. Consider option --dotenv to
|
|
74
|
+
throw new Error("AZURE_STORAGE_CONNECTION_STRING is absent in environment variables. Consider the command option --dotenv to load it from a file.");
|
|
76
75
|
}
|
|
77
76
|
shouldBeDirectory(from);
|
|
78
77
|
const azOpt = {
|
|
@@ -88,10 +87,8 @@ command.command("ssh").description("upload via SFTP. In addition to sftpURL cons
|
|
|
88
87
|
new Option("-d|--dotenv [file]", 'load environment variables from a property file, i.e. a file with "key=value" lines.').preset(".env")
|
|
89
88
|
).addOption(new Option("--privateKeyPath [path to file]", "Path to SSH private key, fallback is UZDU_SSH_KEY_PATH environment variable. Also consider using UZDU_SSH_KEY to provide SSH private key content or UZDU_SSH_PASSWORD.")).action(async (source, sftpUrl, options, thisCommand) => {
|
|
90
89
|
try {
|
|
91
|
-
|
|
92
|
-
await upload4(resolvePath(source), sftpUrl, sshCredentials);
|
|
90
|
+
await upload4(resolvePath(source), sftpUrl, options);
|
|
93
91
|
} catch (e) {
|
|
94
|
-
console.error(e);
|
|
95
92
|
thisCommand.error(e.message || e, { exitCode: 127, code: "ssh.upload.error" });
|
|
96
93
|
}
|
|
97
94
|
});
|
package/lib/uzdu-zip.js
CHANGED
package/lib/uzdu.d.ts
CHANGED
|
@@ -73,8 +73,28 @@ declare function safeIndex<T>(arr: T[], index: number): T | undefined;
|
|
|
73
73
|
* @return '/home/bob/GitHub/Repo/file.png'
|
|
74
74
|
*/
|
|
75
75
|
declare function resolvePath(filePath: string): string;
|
|
76
|
+
/**
|
|
77
|
+
* Sequential runner
|
|
78
|
+
* @param tasks array of functions to be executed sequentially
|
|
79
|
+
* @param breaksOnError breaks all if any function fails.
|
|
80
|
+
* @returns array of results
|
|
81
|
+
* @throws SequentialRunError
|
|
82
|
+
*/
|
|
83
|
+
declare function runSequentially(tasks: Function[], breaksOnError?: boolean): Promise<any[]>;
|
|
84
|
+
declare class SequentilRunError extends Error {
|
|
85
|
+
readonly index: number;
|
|
86
|
+
readonly message: string;
|
|
87
|
+
/**
|
|
88
|
+
*
|
|
89
|
+
* @param message Text of the error
|
|
90
|
+
* @param index where in the sequense the error happened
|
|
91
|
+
*/
|
|
92
|
+
constructor(index: number, message?: string);
|
|
93
|
+
}
|
|
76
94
|
|
|
77
95
|
type utils_BlobObject = BlobObject;
|
|
96
|
+
type utils_SequentilRunError = SequentilRunError;
|
|
97
|
+
declare const utils_SequentilRunError: typeof SequentilRunError;
|
|
78
98
|
declare const utils_addMetadata: typeof addMetadata;
|
|
79
99
|
declare const utils_checkIsFile: typeof checkIsFile;
|
|
80
100
|
declare const utils_doUnzip: typeof doUnzip;
|
|
@@ -85,11 +105,12 @@ declare const utils_listFiles: typeof listFiles;
|
|
|
85
105
|
declare const utils_makeZip: typeof makeZip;
|
|
86
106
|
declare const utils_outputConfiguration: typeof outputConfiguration;
|
|
87
107
|
declare const utils_resolvePath: typeof resolvePath;
|
|
108
|
+
declare const utils_runSequentially: typeof runSequentially;
|
|
88
109
|
declare const utils_safeIndex: typeof safeIndex;
|
|
89
110
|
declare const utils_shouldBeDirectory: typeof shouldBeDirectory;
|
|
90
111
|
declare const utils_shouldBeFile: typeof shouldBeFile;
|
|
91
112
|
declare namespace utils {
|
|
92
|
-
export { type utils_BlobObject as BlobObject, utils_addMetadata as addMetadata, utils_checkIsFile as checkIsFile, utils_doUnzip as doUnzip, utils_getEnvironment as getEnvironment, utils_initEnvironment as initEnvironment, utils_listBlobs as listBlobs, utils_listFiles as listFiles, utils_makeZip as makeZip, utils_outputConfiguration as outputConfiguration, utils_resolvePath as resolvePath, utils_safeIndex as safeIndex, utils_shouldBeDirectory as shouldBeDirectory, utils_shouldBeFile as shouldBeFile };
|
|
113
|
+
export { type utils_BlobObject as BlobObject, utils_SequentilRunError as SequentilRunError, utils_addMetadata as addMetadata, utils_checkIsFile as checkIsFile, utils_doUnzip as doUnzip, utils_getEnvironment as getEnvironment, utils_initEnvironment as initEnvironment, utils_listBlobs as listBlobs, utils_listFiles as listFiles, utils_makeZip as makeZip, utils_outputConfiguration as outputConfiguration, utils_resolvePath as resolvePath, utils_runSequentially as runSequentially, utils_safeIndex as safeIndex, utils_shouldBeDirectory as shouldBeDirectory, utils_shouldBeFile as shouldBeFile };
|
|
93
114
|
}
|
|
94
115
|
|
|
95
116
|
declare function upload$3(dirOrFile: string, url: URL, headers?: string[]): Promise<void>;
|
|
@@ -132,7 +153,25 @@ type SshCredentials = {
|
|
|
132
153
|
password?: undefined;
|
|
133
154
|
privateKey: Buffer | string;
|
|
134
155
|
};
|
|
135
|
-
|
|
156
|
+
type ShellCallbackParams = {
|
|
157
|
+
message?: string;
|
|
158
|
+
error?: string;
|
|
159
|
+
signal?: number;
|
|
160
|
+
code?: number;
|
|
161
|
+
};
|
|
162
|
+
type ShellCommandCallback = (value: ShellCallbackParams) => void;
|
|
163
|
+
type SftpConnectConfig = ConnectConfig & {
|
|
164
|
+
path?: string;
|
|
165
|
+
};
|
|
166
|
+
declare function upload(source: string, sftpUrl: string, options?: {
|
|
167
|
+
privateKeyPath?: string;
|
|
168
|
+
dotenv?: string;
|
|
169
|
+
}): Promise<void>;
|
|
170
|
+
declare function execute(sshAddress: string, commands: string[], options?: {
|
|
171
|
+
privateKeyPath?: string;
|
|
172
|
+
dotenv?: string;
|
|
173
|
+
callback?: ShellCommandCallback;
|
|
174
|
+
}): Promise<void>;
|
|
136
175
|
type FileMapEntry = {
|
|
137
176
|
[key: string]: false | FileMapEntry;
|
|
138
177
|
};
|
|
@@ -163,12 +202,13 @@ declare function getCredentials(options?: {
|
|
|
163
202
|
* sftp://ubuntu:pa55w0rd@example.com/opt/file
|
|
164
203
|
* sftp://root@[2001:db8::5]:222/opt/file
|
|
165
204
|
* sftp://203.0.113.5/opt/file
|
|
205
|
+
* root:pa55w0rd@example.com
|
|
166
206
|
* ```
|
|
167
|
-
* @param
|
|
207
|
+
* @param url
|
|
168
208
|
*
|
|
169
209
|
* @throws Wrong URL: host or ivp6 is not specified
|
|
170
210
|
*/
|
|
171
|
-
declare function getConnectConfig(
|
|
211
|
+
declare function getConnectConfig(url: string): SftpConnectConfig;
|
|
172
212
|
/**
|
|
173
213
|
*
|
|
174
214
|
* @param sftpUrl
|
|
@@ -178,7 +218,11 @@ declare function getConnectConfig(sftpUrl: string): ConnectConfig;
|
|
|
178
218
|
*/
|
|
179
219
|
declare function getRemoteDestination(sftpUrl: string): string;
|
|
180
220
|
|
|
221
|
+
type ssh_SftpConnectConfig = SftpConnectConfig;
|
|
222
|
+
type ssh_ShellCallbackParams = ShellCallbackParams;
|
|
223
|
+
type ssh_ShellCommandCallback = ShellCommandCallback;
|
|
181
224
|
type ssh_SshCredentials = SshCredentials;
|
|
225
|
+
declare const ssh_execute: typeof execute;
|
|
182
226
|
declare const ssh_getConnectConfig: typeof getConnectConfig;
|
|
183
227
|
declare const ssh_getCredentials: typeof getCredentials;
|
|
184
228
|
declare const ssh_getDirMap: typeof getDirMap;
|
|
@@ -186,7 +230,7 @@ declare const ssh_getMakeDirs: typeof getMakeDirs;
|
|
|
186
230
|
declare const ssh_getRemoteDestination: typeof getRemoteDestination;
|
|
187
231
|
declare const ssh_upload: typeof upload;
|
|
188
232
|
declare namespace ssh {
|
|
189
|
-
export { type ssh_SshCredentials as SshCredentials, ssh_getConnectConfig as getConnectConfig, ssh_getCredentials as getCredentials, ssh_getDirMap as getDirMap, ssh_getMakeDirs as getMakeDirs, ssh_getRemoteDestination as getRemoteDestination, ssh_upload as upload };
|
|
233
|
+
export { type ssh_SftpConnectConfig as SftpConnectConfig, type ssh_ShellCallbackParams as ShellCallbackParams, type ssh_ShellCommandCallback as ShellCommandCallback, type ssh_SshCredentials as SshCredentials, ssh_execute as execute, ssh_getConnectConfig as getConnectConfig, ssh_getCredentials as getCredentials, ssh_getDirMap as getDirMap, ssh_getMakeDirs as getMakeDirs, ssh_getRemoteDestination as getRemoteDestination, ssh_upload as upload };
|
|
190
234
|
}
|
|
191
235
|
|
|
192
236
|
export { azure, http, s3, ssh, utils };
|
package/lib/uzdu.js
CHANGED
|
@@ -3,21 +3,21 @@ import {
|
|
|
3
3
|
azure_exports,
|
|
4
4
|
s3_exports,
|
|
5
5
|
ssh_exports
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-463DKYHI.js";
|
|
7
7
|
import {
|
|
8
8
|
http_exports
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-LFEIDM4S.js";
|
|
10
10
|
import {
|
|
11
11
|
outputConfiguration,
|
|
12
12
|
utils_exports
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-WWXWWCCX.js";
|
|
14
14
|
|
|
15
15
|
// src/uzdu.ts
|
|
16
16
|
import { Command } from "commander";
|
|
17
17
|
var version;
|
|
18
18
|
var description;
|
|
19
19
|
try {
|
|
20
|
-
version = "1.0
|
|
20
|
+
version = "1.1.0";
|
|
21
21
|
description = "UZDU - universal zipper, downloader and uploader. Move files to/from zip, clouds (AWS, Azure), to HTTP PUT (e.g. Nexus) and to SSH";
|
|
22
22
|
} catch (e) {
|
|
23
23
|
if (e instanceof ReferenceError) {
|
|
@@ -33,6 +33,7 @@ program.command("zip", "create zip-archive from a directory or a file");
|
|
|
33
33
|
program.command("unzip", "unzip archive to a directory");
|
|
34
34
|
program.command("copy", "copy files and directories");
|
|
35
35
|
program.command("metadata", "create Amazon S3 metadata file. See https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingMetadata.html.").alias("meta");
|
|
36
|
+
program.command("exec", "execute shell command");
|
|
36
37
|
program.configureOutput(outputConfiguration);
|
|
37
38
|
async function main() {
|
|
38
39
|
await program.parseAsync();
|
package/package.json
CHANGED