uzdu 1.0.18 → 1.1.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.
- package/README.md +7 -7
- package/lib/{chunk-ACV2CMWZ.js → chunk-2E5O5JIN.js} +118 -169
- package/lib/{chunk-7B56UNA6.js → chunk-LFEIDM4S.js} +1 -1
- package/lib/{chunk-OIXJ4D3Z.js → chunk-WWXWWCCX.js} +32 -0
- package/lib/chunk-XU5JZHWK.js +133 -0
- package/lib/uzdu-copy.js +1 -1
- package/lib/uzdu-download.js +2 -2
- package/lib/uzdu-exec.d.ts +2 -0
- package/lib/uzdu-exec.js +28 -0
- package/lib/uzdu-metadata.js +1 -1
- package/lib/uzdu-unzip.js +1 -1
- package/lib/uzdu-upload.js +14 -15
- package/lib/uzdu-zip.js +1 -1
- package/lib/uzdu.d.ts +49 -5
- package/lib/uzdu.js +9 -6
- 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,160 +3,48 @@ import {
|
|
|
3
3
|
getEnvironment,
|
|
4
4
|
initEnvironment,
|
|
5
5
|
listFiles,
|
|
6
|
-
resolvePath
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
// src/azure.ts
|
|
10
|
-
var azure_exports = {};
|
|
11
|
-
__export(azure_exports, {
|
|
12
|
-
default: () => upload
|
|
13
|
-
});
|
|
14
|
-
import { BlobServiceClient } from "@azure/storage-blob";
|
|
15
|
-
import path from "path";
|
|
16
|
-
import fs from "fs";
|
|
17
|
-
async function upload(dir, options, metadataFile = ".metadata.json") {
|
|
18
|
-
if (!options.connectionString) throw Error("Uploader needs connection string for Azure Blob Storage. Provide AZURE_STORAGE_CONNECTION_STRING environment variable!");
|
|
19
|
-
const opts = Object.assign({}, { container: "$web" }, options);
|
|
20
|
-
const blobServiceClient = BlobServiceClient.fromConnectionString(options.connectionString);
|
|
21
|
-
const isDebug = process.env.DEBUG && process.env.DEBUG.toLowerCase() === "true";
|
|
22
|
-
const containerClient = blobServiceClient.getContainerClient(opts.container);
|
|
23
|
-
let dist = path.resolve(process.cwd(), dir);
|
|
24
|
-
const files = await listFiles(dir);
|
|
25
|
-
let metadata;
|
|
26
|
-
try {
|
|
27
|
-
const metadataJson = fs.readFileSync(path.join(dir, metadataFile), { encoding: "utf-8" });
|
|
28
|
-
metadata = JSON.parse(metadataJson);
|
|
29
|
-
} catch (e) {
|
|
30
|
-
}
|
|
31
|
-
if (Object.keys(files).length == 1) {
|
|
32
|
-
const lstat = fs.lstatSync(dist);
|
|
33
|
-
if (lstat.isFile()) {
|
|
34
|
-
dist = path.dirname(dist);
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
await Promise.all(Object.entries(files).map(async ([file, absFile]) => {
|
|
38
|
-
let blobObj;
|
|
39
|
-
if (metadata) {
|
|
40
|
-
blobObj = metadata[file];
|
|
41
|
-
}
|
|
42
|
-
const blockBlobClient = containerClient.getBlockBlobClient(file);
|
|
43
|
-
const blobHTTPHeaders = {};
|
|
44
|
-
if (blobObj?.headers) {
|
|
45
|
-
const { CacheControl, ContentType } = blobObj.headers;
|
|
46
|
-
blobHTTPHeaders.blobCacheControl = CacheControl;
|
|
47
|
-
blobHTTPHeaders.blobContentType = ContentType;
|
|
48
|
-
}
|
|
49
|
-
const localFilePath = absFile;
|
|
50
|
-
await blockBlobClient.uploadFile(localFilePath, { blobHTTPHeaders });
|
|
51
|
-
}));
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// src/s3.ts
|
|
55
|
-
var s3_exports = {};
|
|
56
|
-
__export(s3_exports, {
|
|
57
|
-
default: () => upload2
|
|
58
|
-
});
|
|
59
|
-
import { S3Client } from "@aws-sdk/client-s3";
|
|
60
|
-
import { Upload } from "@aws-sdk/lib-storage";
|
|
61
|
-
import fs2 from "fs";
|
|
62
|
-
import path2 from "path";
|
|
63
|
-
async function upload2(dir, s3config, metadataFile = ".metadata.json") {
|
|
64
|
-
if (!s3config.accessKeyId || !s3config.secretAccessKey) {
|
|
65
|
-
throw new Error("AWS credentials not found in environment variables AWS_KEY_ID and AWS_SECRET_KEY.");
|
|
66
|
-
}
|
|
67
|
-
if (!s3config.region) {
|
|
68
|
-
throw new Error('Neither "region" in the bucket address nor AWS_REGION environment variable was found.');
|
|
69
|
-
}
|
|
70
|
-
if (!s3config.bucket) {
|
|
71
|
-
throw new Error("Amazon S3 bucket name is required");
|
|
72
|
-
}
|
|
73
|
-
const { accessKeyId, secretAccessKey, region, endpoint } = s3config;
|
|
74
|
-
const client = new S3Client({
|
|
75
|
-
credentials: {
|
|
76
|
-
accessKeyId,
|
|
77
|
-
secretAccessKey
|
|
78
|
-
},
|
|
79
|
-
region,
|
|
80
|
-
endpoint
|
|
81
|
-
});
|
|
82
|
-
let dist = path2.resolve(process.cwd(), dir);
|
|
83
|
-
const files = await listFiles(dist);
|
|
84
|
-
let metadata;
|
|
85
|
-
try {
|
|
86
|
-
const metadataJson = fs2.readFileSync(path2.join(dir, metadataFile), { encoding: "utf-8" });
|
|
87
|
-
metadata = JSON.parse(metadataJson);
|
|
88
|
-
} catch (e) {
|
|
89
|
-
}
|
|
90
|
-
if (Object.keys(files).length == 1) {
|
|
91
|
-
const lstat = fs2.lstatSync(dist);
|
|
92
|
-
if (lstat.isFile()) {
|
|
93
|
-
dist = path2.dirname(dist);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
await Promise.all(Object.entries(files).map(async ([file, absFile]) => {
|
|
97
|
-
const filePath = absFile;
|
|
98
|
-
const fileContent = fs2.readFileSync(filePath);
|
|
99
|
-
const params = {
|
|
100
|
-
Bucket: s3config.bucket,
|
|
101
|
-
Key: file,
|
|
102
|
-
Body: fileContent
|
|
103
|
-
};
|
|
104
|
-
if (metadata) {
|
|
105
|
-
const blobObj = metadata[file];
|
|
106
|
-
if (blobObj && blobObj.headers) {
|
|
107
|
-
const { CacheControl, ContentType } = blobObj.headers;
|
|
108
|
-
if (CacheControl) params.CacheControl = CacheControl;
|
|
109
|
-
if (ContentType) params.ContentType = ContentType;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
return new Upload({
|
|
113
|
-
client,
|
|
114
|
-
params,
|
|
115
|
-
tags: [],
|
|
116
|
-
queueSize: 4,
|
|
117
|
-
// optional concurrency configuration
|
|
118
|
-
partSize: 1024 * 1024 * 5,
|
|
119
|
-
// optional size of each part, in bytes, at least 5MB
|
|
120
|
-
leavePartsOnError: false
|
|
121
|
-
// optional manually handle dropped parts
|
|
122
|
-
}).done();
|
|
123
|
-
}));
|
|
124
|
-
}
|
|
6
|
+
resolvePath,
|
|
7
|
+
runSequentially
|
|
8
|
+
} from "./chunk-WWXWWCCX.js";
|
|
125
9
|
|
|
126
10
|
// src/ssh.ts
|
|
127
11
|
var ssh_exports = {};
|
|
128
12
|
__export(ssh_exports, {
|
|
13
|
+
execute: () => execute,
|
|
129
14
|
getConnectConfig: () => getConnectConfig,
|
|
130
15
|
getCredentials: () => getCredentials,
|
|
131
16
|
getDirMap: () => getDirMap,
|
|
132
17
|
getMakeDirs: () => getMakeDirs,
|
|
133
18
|
getRemoteDestination: () => getRemoteDestination,
|
|
134
|
-
upload: () =>
|
|
19
|
+
upload: () => upload
|
|
135
20
|
});
|
|
136
21
|
import { Client } from "ssh2";
|
|
137
|
-
import
|
|
138
|
-
import
|
|
22
|
+
import fs from "fs";
|
|
23
|
+
import path from "path";
|
|
139
24
|
import deepmerge from "deepmerge";
|
|
140
|
-
async function
|
|
25
|
+
async function upload(source, sftpUrl, options) {
|
|
141
26
|
await new Promise((resolve, reject) => {
|
|
142
|
-
|
|
27
|
+
fs.stat(source, async (err, stats) => {
|
|
28
|
+
if (err) {
|
|
29
|
+
reject(err);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
143
32
|
if (stats.isSymbolicLink()) {
|
|
144
33
|
reject(new Error(`${source} is symlink`));
|
|
145
34
|
} else {
|
|
146
35
|
let sshConnection;
|
|
147
36
|
try {
|
|
148
37
|
const _connectConfig = getConnectConfig(sftpUrl);
|
|
149
|
-
const _sshCredentials =
|
|
38
|
+
const _sshCredentials = _connectConfig.password ? void 0 : getCredentials(options);
|
|
150
39
|
const connectConfig = { ..._connectConfig, ..._sshCredentials };
|
|
151
40
|
sshConnection = await connect(connectConfig);
|
|
152
41
|
const files = await listFiles(source);
|
|
153
|
-
const destination =
|
|
42
|
+
const destination = normilizeSftpPath(_connectConfig.path) || "";
|
|
154
43
|
const _source = source.replace(/\/+$/, "");
|
|
155
44
|
await mkdirs(sshConnection, destination, files);
|
|
156
45
|
await uploadFiles(files, _source, destination, sshConnection);
|
|
157
46
|
resolve();
|
|
158
47
|
} catch (e) {
|
|
159
|
-
console.error("SFTP error", e);
|
|
160
48
|
reject(e);
|
|
161
49
|
} finally {
|
|
162
50
|
sshConnection?.destroy();
|
|
@@ -165,22 +53,69 @@ async function upload3(source, sftpUrl, sshCredentials) {
|
|
|
165
53
|
});
|
|
166
54
|
});
|
|
167
55
|
}
|
|
56
|
+
async function execute(sshAddress, commands, options) {
|
|
57
|
+
await new Promise(async (resolve, reject) => {
|
|
58
|
+
let sshConnection;
|
|
59
|
+
try {
|
|
60
|
+
const _connectConfig = getConnectConfig(sshAddress);
|
|
61
|
+
const _sshCredentials = _connectConfig.password ? void 0 : getCredentials(options);
|
|
62
|
+
const connectConfig = { ..._connectConfig, ..._sshCredentials };
|
|
63
|
+
sshConnection = await connect(connectConfig);
|
|
64
|
+
const shellOptions = {
|
|
65
|
+
...options,
|
|
66
|
+
cwd: normilizeSftpPath(_connectConfig.path)
|
|
67
|
+
};
|
|
68
|
+
await shellExec(sshConnection, commands, shellOptions);
|
|
69
|
+
resolve();
|
|
70
|
+
} catch (e) {
|
|
71
|
+
reject(e);
|
|
72
|
+
} finally {
|
|
73
|
+
sshConnection?.destroy();
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}
|
|
168
77
|
async function mkdirs(sshConnection, destination, sources) {
|
|
169
78
|
const fileMap = getDirMap(sources);
|
|
170
79
|
const makeDirs = getMakeDirs(fileMap, destination);
|
|
171
80
|
const commands = makeDirs ? makeDirs.map((dir) => `mkdir -p "${dir}"`) : [`mkdir -p "${destination}"`];
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
81
|
+
await shellExec(sshConnection, commands);
|
|
82
|
+
}
|
|
83
|
+
async function shellExec(sshConnection, commands, options) {
|
|
84
|
+
if (!commands) {
|
|
85
|
+
return Promise.reject("shellExec did not get any command");
|
|
86
|
+
}
|
|
87
|
+
if (options?.cwd) {
|
|
88
|
+
const command = `set -e; cd ${options.cwd}; set +e; ${commands.join(";")}`;
|
|
89
|
+
await shellCommand(sshConnection, command, options);
|
|
90
|
+
} else {
|
|
91
|
+
const shellCommands = commands.map((command) => () => shellCommand(sshConnection, command, options));
|
|
92
|
+
await runSequentially(shellCommands);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
async function shellCommand(sshClient, command, options) {
|
|
96
|
+
return new Promise((res, rej) => {
|
|
97
|
+
const decoder = new TextDecoder();
|
|
98
|
+
sshClient.exec(command, (err, stream) => {
|
|
99
|
+
if (err) rej(err);
|
|
100
|
+
stream.on("close", (code, signal) => {
|
|
101
|
+
if (code != 0) {
|
|
102
|
+
options?.callback?.({ error: `closing SSH by signal=${signal} and exit code=${code}`, code, signal });
|
|
103
|
+
rej(new Error(`Close code ${code}`));
|
|
104
|
+
} else {
|
|
105
|
+
res(code);
|
|
106
|
+
}
|
|
107
|
+
}).on("exit", (code, signal) => {
|
|
108
|
+
if (code != 0) {
|
|
109
|
+
options?.callback?.({ error: `Exit command signal=${signal} and exit code=${code}`, code, signal });
|
|
110
|
+
rej(new Error(`Exit code ${code}`));
|
|
111
|
+
} else {
|
|
112
|
+
res(code);
|
|
113
|
+
}
|
|
114
|
+
}).on("data", (message) => {
|
|
115
|
+
options?.callback?.({ message: decoder.decode(message).replace(/\n$/, "") });
|
|
116
|
+
}).stderr.on("data", (error) => {
|
|
117
|
+
options?.callback?.({ error: decoder.decode(error) });
|
|
118
|
+
});
|
|
184
119
|
});
|
|
185
120
|
});
|
|
186
121
|
}
|
|
@@ -198,7 +133,7 @@ function _uploadFile(source, destination, sftp) {
|
|
|
198
133
|
else resolve();
|
|
199
134
|
});
|
|
200
135
|
} else if (stats.isDirectory()) {
|
|
201
|
-
const f =
|
|
136
|
+
const f = path.basename(source);
|
|
202
137
|
reject(new Error(`Overwriting directory ${destination} with the file ${f} is not allowed. Remove the directory manually.`));
|
|
203
138
|
} else {
|
|
204
139
|
reject(new Error("Remote path is symlink"));
|
|
@@ -210,33 +145,29 @@ function uploadFiles(sourceFiles, source, destination, sshConnection) {
|
|
|
210
145
|
return new Promise((resolve, reject) => {
|
|
211
146
|
sshConnection.sftp(async (err, sftp) => {
|
|
212
147
|
if (err) {
|
|
213
|
-
console.error("uploadFiles error");
|
|
214
148
|
reject(err);
|
|
215
149
|
} else {
|
|
216
150
|
if (Object.keys(sourceFiles).length == 1) {
|
|
217
|
-
const lstat2 =
|
|
151
|
+
const lstat2 = fs.lstatSync(source);
|
|
218
152
|
if (lstat2.isFile()) {
|
|
219
|
-
const dest =
|
|
153
|
+
const dest = path.join(destination, Object.keys(sourceFiles)[0]).replace(/\\/g, "/");
|
|
220
154
|
const src = source;
|
|
221
155
|
await _uploadFile(src, dest, sftp).then(() => resolve()).catch((e) => {
|
|
222
|
-
console.error(src);
|
|
223
156
|
reject(e);
|
|
224
157
|
});
|
|
225
158
|
return;
|
|
226
159
|
}
|
|
227
160
|
}
|
|
228
161
|
let sourceDir = source;
|
|
229
|
-
const lstat =
|
|
162
|
+
const lstat = fs.lstatSync(source);
|
|
230
163
|
if (lstat.isSymbolicLink()) {
|
|
231
|
-
sourceDir =
|
|
164
|
+
sourceDir = fs.readlinkSync(source);
|
|
232
165
|
}
|
|
233
166
|
const promises = [];
|
|
234
167
|
Object.entries(sourceFiles).map(([baseName, absPath]) => {
|
|
235
|
-
const dest =
|
|
168
|
+
const dest = path.join(destination, baseName).replace(/\\/g, "/");
|
|
236
169
|
const promise = new Promise((res, rej) => {
|
|
237
170
|
_uploadFile(absPath, dest, sftp).then(() => res()).catch((e) => {
|
|
238
|
-
console.error(absPath);
|
|
239
|
-
console.error(e);
|
|
240
171
|
rej(e);
|
|
241
172
|
});
|
|
242
173
|
});
|
|
@@ -253,7 +184,7 @@ async function connect(sshConfig) {
|
|
|
253
184
|
try {
|
|
254
185
|
return await new Promise((resolve, reject) => {
|
|
255
186
|
conn.on("error", (e) => {
|
|
256
|
-
reject(
|
|
187
|
+
reject(e);
|
|
257
188
|
}).on("ready", () => {
|
|
258
189
|
resolve(conn);
|
|
259
190
|
}).connect({
|
|
@@ -273,7 +204,6 @@ async function connect(sshConfig) {
|
|
|
273
204
|
});
|
|
274
205
|
});
|
|
275
206
|
} catch (e) {
|
|
276
|
-
console.error("Connection failed", e);
|
|
277
207
|
conn.destroy();
|
|
278
208
|
throw e;
|
|
279
209
|
}
|
|
@@ -309,7 +239,7 @@ function getFileMap(file) {
|
|
|
309
239
|
const parts = theFile.split("/");
|
|
310
240
|
if (parts.length == 1) return { [parts[0]]: false };
|
|
311
241
|
else {
|
|
312
|
-
const aFile =
|
|
242
|
+
const aFile = path.join(...parts.slice(1)).replace(/\\/g, "/");
|
|
313
243
|
const fileMapEntry = getFileMap(aFile);
|
|
314
244
|
return { [parts[0]]: fileMapEntry };
|
|
315
245
|
}
|
|
@@ -327,13 +257,13 @@ function getCredentials(options) {
|
|
|
327
257
|
if (uzduKeyPath) {
|
|
328
258
|
const resolvedKeyPath = resolvePath(uzduKeyPath);
|
|
329
259
|
try {
|
|
330
|
-
privateKey =
|
|
260
|
+
privateKey = fs.readFileSync(resolvedKeyPath);
|
|
331
261
|
} catch (e) {
|
|
332
262
|
throw new Error(`Not found private Key file ${resolvedKeyPath}`);
|
|
333
263
|
}
|
|
334
264
|
} else {
|
|
335
265
|
const uzduPassword = process.env.UZDU_SSH_PASSWORD;
|
|
336
|
-
if (!uzduPassword) throw new Error("Specify
|
|
266
|
+
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
267
|
password = uzduPassword;
|
|
338
268
|
}
|
|
339
269
|
}
|
|
@@ -345,17 +275,34 @@ function getCredentials(options) {
|
|
|
345
275
|
return authConfig;
|
|
346
276
|
}
|
|
347
277
|
var sftpUrlRegex = /^sftp:\/\/(?:(?<username>[\w\.\-]{1,32})(?::(?<password>.+))?@)?(?:(?<host>[\w\.\-]+)|\[(?<ipv6>[\d:]+)\])(?::(?<port>\d{1,5}))?\/(?<path>.*)$/g;
|
|
348
|
-
|
|
278
|
+
var sshUrlRegex = /^(?:(?<username>[\w\.\-]{1,32})(?::(?<password>.+))?@)?(?:(?<host>[\w\.\-]+)|\[(?<ipv6>[\d:]+)\])(?::(?<port>\d{1,5}))?$/g;
|
|
279
|
+
var suspectSftpRegex = /^sftp:\/\/.+/g;
|
|
280
|
+
function getConnectConfig(url) {
|
|
281
|
+
let execArray;
|
|
282
|
+
sshUrlRegex.lastIndex = 0;
|
|
349
283
|
sftpUrlRegex.lastIndex = 0;
|
|
350
|
-
|
|
284
|
+
if (sftpUrlRegex.test(url)) {
|
|
285
|
+
sftpUrlRegex.lastIndex = 0;
|
|
286
|
+
execArray = sftpUrlRegex.exec(url);
|
|
287
|
+
} else if (sshUrlRegex.test(url)) {
|
|
288
|
+
sshUrlRegex.lastIndex = 0;
|
|
289
|
+
suspectSftpRegex.lastIndex = 0;
|
|
290
|
+
if (suspectSftpRegex.test(url)) {
|
|
291
|
+
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.`);
|
|
292
|
+
}
|
|
293
|
+
execArray = sshUrlRegex.exec(url);
|
|
294
|
+
} else {
|
|
295
|
+
throw new Error(`Not an SFTP or SSH address: ${url}`);
|
|
296
|
+
}
|
|
351
297
|
const { groups } = execArray ?? {};
|
|
352
298
|
const host = groups?.host || groups?.ipv6;
|
|
353
|
-
if (!host) throw new Error(`Wrong URL "${
|
|
299
|
+
if (!host) throw new Error(`Wrong URL "${url}": host or ivp6 is not specified`);
|
|
354
300
|
const username = groups?.username;
|
|
355
301
|
const password = groups?.password;
|
|
302
|
+
const path2 = groups?.path;
|
|
356
303
|
const _port = parseInt(groups.port);
|
|
357
304
|
const port = isNaN(_port) ? void 0 : _port;
|
|
358
|
-
const connectConfig = { username, password, host, port };
|
|
305
|
+
const connectConfig = { username, password, host, port, path: path2 };
|
|
359
306
|
return connectConfig;
|
|
360
307
|
}
|
|
361
308
|
function getRemoteDestination(sftpUrl) {
|
|
@@ -363,31 +310,33 @@ function getRemoteDestination(sftpUrl) {
|
|
|
363
310
|
const execArray = sftpUrlRegex.exec(sftpUrl);
|
|
364
311
|
if (!execArray) throw new Error("Wrong sftp URL");
|
|
365
312
|
if (!execArray.groups) throw new Error("Wrong URL: path is not specified");
|
|
366
|
-
const
|
|
367
|
-
|
|
313
|
+
const path2 = execArray.groups.path;
|
|
314
|
+
return normilizeSftpPath(execArray.groups?.path) || "";
|
|
315
|
+
}
|
|
316
|
+
function normilizeSftpPath(path2) {
|
|
317
|
+
if (path2 == void 0) return void 0;
|
|
318
|
+
const re = /^(?<first>[^\/]*)(?<root>\/)?(?<second>.*)?/g;
|
|
368
319
|
re.lastIndex = 0;
|
|
369
|
-
const execPathArray = re.exec(
|
|
320
|
+
const execPathArray = re.exec(path2);
|
|
370
321
|
const { groups } = execPathArray ?? {};
|
|
371
322
|
const first = groups?.first;
|
|
372
323
|
const second = groups?.second;
|
|
324
|
+
const root = groups?.root;
|
|
373
325
|
let dest;
|
|
374
326
|
if (first) {
|
|
375
327
|
const execTild = /^(?<tild>~)/.exec(first);
|
|
376
328
|
const { groups: groups2 } = execTild ?? {};
|
|
377
|
-
dest = groups2?.tild ? `${second}` : `/${first}${second ? `/${second}` : ""}`;
|
|
329
|
+
dest = groups2?.tild ? `${second ? second : ""}` : `/${first}${second ? `/${second}` : ""}`;
|
|
378
330
|
} else {
|
|
379
|
-
dest =
|
|
331
|
+
if (!root && !second) dest = "/";
|
|
332
|
+
else dest = path2;
|
|
380
333
|
}
|
|
381
|
-
const destination = dest.replace(/\/+$/, "");
|
|
334
|
+
const destination = dest != "/" ? dest.replace(/\/+$/, "") : dest;
|
|
382
335
|
return destination;
|
|
383
336
|
}
|
|
384
337
|
|
|
385
338
|
export {
|
|
386
339
|
upload,
|
|
387
|
-
|
|
388
|
-
upload2,
|
|
389
|
-
s3_exports,
|
|
390
|
-
upload3,
|
|
391
|
-
getCredentials,
|
|
340
|
+
execute,
|
|
392
341
|
ssh_exports
|
|
393
342
|
};
|
|
@@ -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
|
};
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import {
|
|
2
|
+
__export,
|
|
3
|
+
listFiles
|
|
4
|
+
} from "./chunk-WWXWWCCX.js";
|
|
5
|
+
|
|
6
|
+
// src/azure.ts
|
|
7
|
+
var azure_exports = {};
|
|
8
|
+
__export(azure_exports, {
|
|
9
|
+
default: () => upload
|
|
10
|
+
});
|
|
11
|
+
import { BlobServiceClient } from "@azure/storage-blob";
|
|
12
|
+
import path from "path";
|
|
13
|
+
import fs from "fs";
|
|
14
|
+
async function upload(dir, options, metadataFile = ".metadata.json") {
|
|
15
|
+
if (!options.connectionString) throw Error("Uploader needs connection string for Azure Blob Storage. Provide AZURE_STORAGE_CONNECTION_STRING environment variable!");
|
|
16
|
+
const opts = Object.assign({}, { container: "$web" }, options);
|
|
17
|
+
const blobServiceClient = BlobServiceClient.fromConnectionString(options.connectionString);
|
|
18
|
+
const isDebug = process.env.DEBUG && process.env.DEBUG.toLowerCase() === "true";
|
|
19
|
+
const containerClient = blobServiceClient.getContainerClient(opts.container);
|
|
20
|
+
let dist = path.resolve(process.cwd(), dir);
|
|
21
|
+
const files = await listFiles(dir);
|
|
22
|
+
let metadata;
|
|
23
|
+
try {
|
|
24
|
+
const metadataJson = fs.readFileSync(path.join(dir, metadataFile), { encoding: "utf-8" });
|
|
25
|
+
metadata = JSON.parse(metadataJson);
|
|
26
|
+
} catch (e) {
|
|
27
|
+
}
|
|
28
|
+
if (Object.keys(files).length == 1) {
|
|
29
|
+
const lstat = fs.lstatSync(dist);
|
|
30
|
+
if (lstat.isFile()) {
|
|
31
|
+
dist = path.dirname(dist);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
await Promise.all(Object.entries(files).map(async ([file, absFile]) => {
|
|
35
|
+
let blobObj;
|
|
36
|
+
if (metadata) {
|
|
37
|
+
blobObj = metadata[file];
|
|
38
|
+
}
|
|
39
|
+
const blockBlobClient = containerClient.getBlockBlobClient(file);
|
|
40
|
+
const blobHTTPHeaders = {};
|
|
41
|
+
if (blobObj?.headers) {
|
|
42
|
+
const { CacheControl, ContentType } = blobObj.headers;
|
|
43
|
+
blobHTTPHeaders.blobCacheControl = CacheControl;
|
|
44
|
+
blobHTTPHeaders.blobContentType = ContentType;
|
|
45
|
+
}
|
|
46
|
+
const localFilePath = absFile;
|
|
47
|
+
await blockBlobClient.uploadFile(localFilePath, { blobHTTPHeaders });
|
|
48
|
+
}));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// src/s3.ts
|
|
52
|
+
var s3_exports = {};
|
|
53
|
+
__export(s3_exports, {
|
|
54
|
+
default: () => upload2
|
|
55
|
+
});
|
|
56
|
+
import { S3Client } from "@aws-sdk/client-s3";
|
|
57
|
+
import { Upload } from "@aws-sdk/lib-storage";
|
|
58
|
+
import fs2 from "fs";
|
|
59
|
+
import path2 from "path";
|
|
60
|
+
async function upload2(dir, s3config, metadataFile = ".metadata.json") {
|
|
61
|
+
if (!s3config.accessKeyId || !s3config.secretAccessKey) {
|
|
62
|
+
throw new Error("AWS credentials not found in environment variables AWS_KEY_ID and AWS_SECRET_KEY.");
|
|
63
|
+
}
|
|
64
|
+
if (!s3config.region) {
|
|
65
|
+
throw new Error('Neither "region" in the bucket address nor AWS_REGION environment variable was found.');
|
|
66
|
+
}
|
|
67
|
+
if (!s3config.bucket) {
|
|
68
|
+
throw new Error("Amazon S3 bucket name is required");
|
|
69
|
+
}
|
|
70
|
+
const { accessKeyId, secretAccessKey, region, endpoint } = s3config;
|
|
71
|
+
const client = new S3Client({
|
|
72
|
+
credentials: {
|
|
73
|
+
accessKeyId,
|
|
74
|
+
secretAccessKey
|
|
75
|
+
},
|
|
76
|
+
region,
|
|
77
|
+
endpoint
|
|
78
|
+
});
|
|
79
|
+
let dist = path2.resolve(process.cwd(), dir);
|
|
80
|
+
const files = await listFiles(dist);
|
|
81
|
+
let metadata;
|
|
82
|
+
try {
|
|
83
|
+
const metadataJson = fs2.readFileSync(path2.join(dir, metadataFile), { encoding: "utf-8" });
|
|
84
|
+
metadata = JSON.parse(metadataJson);
|
|
85
|
+
} catch (e) {
|
|
86
|
+
}
|
|
87
|
+
if (Object.keys(files).length == 1) {
|
|
88
|
+
const lstat = fs2.lstatSync(dist);
|
|
89
|
+
if (lstat.isFile()) {
|
|
90
|
+
dist = path2.dirname(dist);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
await Promise.all(Object.entries(files).map(async ([file, absFile]) => {
|
|
94
|
+
const filePath = absFile;
|
|
95
|
+
const fileContent = await new Promise((resolve, reject) => {
|
|
96
|
+
fs2.readFile(filePath, (err, data) => {
|
|
97
|
+
if (err) reject(err);
|
|
98
|
+
else resolve(data);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
const params = {
|
|
102
|
+
Bucket: s3config.bucket,
|
|
103
|
+
Key: file,
|
|
104
|
+
Body: fileContent
|
|
105
|
+
};
|
|
106
|
+
if (metadata) {
|
|
107
|
+
const blobObj = metadata[file];
|
|
108
|
+
if (blobObj && blobObj.headers) {
|
|
109
|
+
const { CacheControl, ContentType } = blobObj.headers;
|
|
110
|
+
if (CacheControl) params.CacheControl = CacheControl;
|
|
111
|
+
if (ContentType) params.ContentType = ContentType;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return new Upload({
|
|
115
|
+
client,
|
|
116
|
+
params,
|
|
117
|
+
tags: [],
|
|
118
|
+
queueSize: 4,
|
|
119
|
+
// optional concurrency configuration
|
|
120
|
+
partSize: 1024 * 1024 * 5,
|
|
121
|
+
// optional size of each part, in bytes, at least 5MB
|
|
122
|
+
leavePartsOnError: false
|
|
123
|
+
// optional manually handle dropped parts
|
|
124
|
+
}).done();
|
|
125
|
+
}));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export {
|
|
129
|
+
upload,
|
|
130
|
+
azure_exports,
|
|
131
|
+
upload2,
|
|
132
|
+
s3_exports
|
|
133
|
+
};
|
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-exec.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import {
|
|
2
|
+
execute
|
|
3
|
+
} from "./chunk-2E5O5JIN.js";
|
|
4
|
+
import {
|
|
5
|
+
outputConfiguration
|
|
6
|
+
} from "./chunk-WWXWWCCX.js";
|
|
7
|
+
|
|
8
|
+
// src/uzdu-exec.ts
|
|
9
|
+
import { Command, Option } from "commander";
|
|
10
|
+
import { consola } from "consola";
|
|
11
|
+
var command = new Command();
|
|
12
|
+
command.description("Execute commands on remote machine").name("uzdu exec");
|
|
13
|
+
command.command("ssh").description("Execute commands via SSH. In addition to sshUrl consider using environment variables UZDU_SSH_KEY_PATH, UZDU_SSH_KEY, UZDU_SSH_PASSWORD").argument("<sshUrl>", "the URL format ssh://[user[:password]@]host[:port]").argument("command", "single line command").addOption(
|
|
14
|
+
new Option("-d|--dotenv [file]", 'load environment variables from a property file, i.e. a file with "key=value" lines.').preset(".env")
|
|
15
|
+
).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 (sshUrl, command2, options, thisCommand) => {
|
|
16
|
+
try {
|
|
17
|
+
options.callback = (value) => {
|
|
18
|
+
if (value.message) consola.log(value.message);
|
|
19
|
+
if (value.error) consola.error(value.error);
|
|
20
|
+
};
|
|
21
|
+
await execute(sshUrl, [command2], options);
|
|
22
|
+
} catch (e) {
|
|
23
|
+
thisCommand.error(e.message || e, { exitCode: 127, code: "ssh.upload.error" });
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
command.configureOutput(outputConfiguration);
|
|
27
|
+
command.showHelpAfterError(true);
|
|
28
|
+
command.parse();
|
package/lib/uzdu-metadata.js
CHANGED
package/lib/uzdu-unzip.js
CHANGED
package/lib/uzdu-upload.js
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
upload3 as upload4
|
|
6
|
-
} from "./chunk-ACV2CMWZ.js";
|
|
2
|
+
upload as upload3,
|
|
3
|
+
upload2 as upload4
|
|
4
|
+
} from "./chunk-XU5JZHWK.js";
|
|
7
5
|
import {
|
|
8
6
|
upload
|
|
9
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-LFEIDM4S.js";
|
|
8
|
+
import {
|
|
9
|
+
upload as upload2
|
|
10
|
+
} from "./chunk-2E5O5JIN.js";
|
|
10
11
|
import {
|
|
11
12
|
getEnvironment,
|
|
12
13
|
initEnvironment,
|
|
13
14
|
outputConfiguration,
|
|
14
15
|
resolvePath,
|
|
15
16
|
shouldBeDirectory
|
|
16
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-WWXWWCCX.js";
|
|
17
18
|
|
|
18
19
|
// src/uzdu-upload.ts
|
|
19
20
|
import { Argument, Command, Option } from "commander";
|
|
@@ -38,10 +39,10 @@ command.command("aws").description("upload to AWS S3").argument("<from>", "the d
|
|
|
38
39
|
const optConfig = { bucket: bucketName, endpoint };
|
|
39
40
|
if (region) optConfig.region = region;
|
|
40
41
|
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
|
|
42
|
+
if (!config.accessKeyId) throw new Error("AWS Access Key ID is not specified. Provide an environement variable AWS_ACCESS_KEY_ID.");
|
|
43
|
+
if (!config.secretAccessKey) throw new Error("AWS Secret Key is not specified. Provide an environment variable AWS_SECRET_ACCESS_KEY.");
|
|
43
44
|
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
|
-
await
|
|
45
|
+
await upload4(from, config);
|
|
45
46
|
} catch (e) {
|
|
46
47
|
thisCommand.error(e.message || e, { exitCode: 53, code: "aws.upload.error" });
|
|
47
48
|
}
|
|
@@ -72,14 +73,14 @@ command.command("azure").alias("az").description("upload to Azure Blob Storage")
|
|
|
72
73
|
}
|
|
73
74
|
const connectionString = process.env.AZURE_STORAGE_CONNECTION_STRING;
|
|
74
75
|
if (!connectionString) {
|
|
75
|
-
throw new Error("AZURE_STORAGE_CONNECTION_STRING is absent in environment variables. Consider option --dotenv to
|
|
76
|
+
throw new Error("AZURE_STORAGE_CONNECTION_STRING is absent in environment variables. Consider the command option --dotenv to load it from a file.");
|
|
76
77
|
}
|
|
77
78
|
shouldBeDirectory(from);
|
|
78
79
|
const azOpt = {
|
|
79
80
|
connectionString,
|
|
80
81
|
container
|
|
81
82
|
};
|
|
82
|
-
await
|
|
83
|
+
await upload3(from, azOpt);
|
|
83
84
|
} catch (e) {
|
|
84
85
|
thisCommand.error(e.message || e, { exitCode: 43, code: "az.upload.error" });
|
|
85
86
|
}
|
|
@@ -88,10 +89,8 @@ command.command("ssh").description("upload via SFTP. In addition to sftpURL cons
|
|
|
88
89
|
new Option("-d|--dotenv [file]", 'load environment variables from a property file, i.e. a file with "key=value" lines.').preset(".env")
|
|
89
90
|
).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
91
|
try {
|
|
91
|
-
|
|
92
|
-
await upload4(resolvePath(source), sftpUrl, sshCredentials);
|
|
92
|
+
await upload2(resolvePath(source), sftpUrl, options);
|
|
93
93
|
} catch (e) {
|
|
94
|
-
console.error(e);
|
|
95
94
|
thisCommand.error(e.message || e, { exitCode: 127, code: "ssh.upload.error" });
|
|
96
95
|
}
|
|
97
96
|
});
|
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
|
@@ -1,23 +1,25 @@
|
|
|
1
1
|
#! /usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
azure_exports,
|
|
4
|
-
s3_exports
|
|
5
|
-
|
|
6
|
-
} from "./chunk-ACV2CMWZ.js";
|
|
4
|
+
s3_exports
|
|
5
|
+
} from "./chunk-XU5JZHWK.js";
|
|
7
6
|
import {
|
|
8
7
|
http_exports
|
|
9
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-LFEIDM4S.js";
|
|
9
|
+
import {
|
|
10
|
+
ssh_exports
|
|
11
|
+
} from "./chunk-2E5O5JIN.js";
|
|
10
12
|
import {
|
|
11
13
|
outputConfiguration,
|
|
12
14
|
utils_exports
|
|
13
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-WWXWWCCX.js";
|
|
14
16
|
|
|
15
17
|
// src/uzdu.ts
|
|
16
18
|
import { Command } from "commander";
|
|
17
19
|
var version;
|
|
18
20
|
var description;
|
|
19
21
|
try {
|
|
20
|
-
version = "1.
|
|
22
|
+
version = "1.1.1";
|
|
21
23
|
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
24
|
} catch (e) {
|
|
23
25
|
if (e instanceof ReferenceError) {
|
|
@@ -33,6 +35,7 @@ program.command("zip", "create zip-archive from a directory or a file");
|
|
|
33
35
|
program.command("unzip", "unzip archive to a directory");
|
|
34
36
|
program.command("copy", "copy files and directories");
|
|
35
37
|
program.command("metadata", "create Amazon S3 metadata file. See https://docs.aws.amazon.com/AmazonS3/latest/userguide/UsingMetadata.html.").alias("meta");
|
|
38
|
+
program.command("exec", "execute shell command");
|
|
36
39
|
program.configureOutput(outputConfiguration);
|
|
37
40
|
async function main() {
|
|
38
41
|
await program.parseAsync();
|
package/package.json
CHANGED