defang 1.1.16 → 1.1.18
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/bin/cli.js +2 -4
- package/bin/clilib.js +185 -365
- package/package.json +11 -9
- package/test/clilib.spec.ts +0 -208
package/bin/cli.js
CHANGED
package/bin/clilib.js
CHANGED
|
@@ -1,215 +1,82 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
if (k2 === undefined) k2 = k;
|
|
22
|
-
o[k2] = m[k];
|
|
23
|
-
}));
|
|
24
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
25
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
26
|
-
}) : function(o, v) {
|
|
27
|
-
o["default"] = v;
|
|
28
|
-
});
|
|
29
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
30
|
-
var ownKeys = function(o) {
|
|
31
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
32
|
-
var ar = [];
|
|
33
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
34
|
-
return ar;
|
|
35
|
-
};
|
|
36
|
-
return ownKeys(o);
|
|
37
|
-
};
|
|
38
|
-
return function (mod) {
|
|
39
|
-
if (mod && mod.__esModule) return mod;
|
|
40
|
-
var result = {};
|
|
41
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
42
|
-
__setModuleDefault(result, mod);
|
|
43
|
-
return result;
|
|
44
|
-
};
|
|
45
|
-
})();
|
|
46
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
47
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
48
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
49
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
50
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
51
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
52
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
53
|
-
});
|
|
54
|
-
};
|
|
55
|
-
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
56
|
-
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
57
|
-
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
58
|
-
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
59
|
-
function step(op) {
|
|
60
|
-
if (f) throw new TypeError("Generator is already executing.");
|
|
61
|
-
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
62
|
-
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
63
|
-
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
64
|
-
switch (op[0]) {
|
|
65
|
-
case 0: case 1: t = op; break;
|
|
66
|
-
case 4: _.label++; return { value: op[1], done: false };
|
|
67
|
-
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
68
|
-
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
69
|
-
default:
|
|
70
|
-
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
71
|
-
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
72
|
-
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
73
|
-
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
74
|
-
if (t[2]) _.ops.pop();
|
|
75
|
-
_.trys.pop(); continue;
|
|
76
|
-
}
|
|
77
|
-
op = body.call(thisArg, _);
|
|
78
|
-
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
79
|
-
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
1
|
+
import AdmZip from "adm-zip";
|
|
2
|
+
import axios from "axios";
|
|
3
|
+
import * as child_process from "child_process";
|
|
4
|
+
import * as fs from "fs";
|
|
5
|
+
import * as path from "path";
|
|
6
|
+
import * as tar from "tar";
|
|
7
|
+
import { promisify } from "util";
|
|
8
|
+
import { dirname } from 'path';
|
|
9
|
+
import { fileURLToPath } from 'url';
|
|
10
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
11
|
+
const __dirname = dirname(__filename);
|
|
12
|
+
const SEMVER_REGEX = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;
|
|
13
|
+
const EXECUTABLE = "defang";
|
|
14
|
+
const URL_LATEST_RELEASE = "https://api.github.com/repos/DefangLabs/defang/releases/latest";
|
|
15
|
+
const HTTP_STATUS_OK = 200;
|
|
16
|
+
const exec = promisify(child_process.exec);
|
|
17
|
+
async function getLatestVersion() {
|
|
18
|
+
const response = await axios.get(URL_LATEST_RELEASE);
|
|
19
|
+
if (response.status !== HTTP_STATUS_OK) {
|
|
20
|
+
throw new Error(`Failed to get latest version from GitHub. Status code: ${response.status}`);
|
|
80
21
|
}
|
|
81
|
-
|
|
82
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
83
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
84
|
-
};
|
|
85
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
86
|
-
exports.install = install;
|
|
87
|
-
exports.run = run;
|
|
88
|
-
var adm_zip_1 = __importDefault(require("adm-zip"));
|
|
89
|
-
var axios_1 = __importDefault(require("axios"));
|
|
90
|
-
var child_process = __importStar(require("child_process"));
|
|
91
|
-
var fs = __importStar(require("fs"));
|
|
92
|
-
var path = __importStar(require("path"));
|
|
93
|
-
var tar = __importStar(require("tar"));
|
|
94
|
-
var util_1 = require("util");
|
|
95
|
-
var SEMVER_REGEX = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;
|
|
96
|
-
var EXECUTABLE = "defang";
|
|
97
|
-
var URL_LATEST_RELEASE = "https://api.github.com/repos/DefangLabs/defang/releases/latest";
|
|
98
|
-
var HTTP_STATUS_OK = 200;
|
|
99
|
-
var exec = (0, util_1.promisify)(child_process.exec);
|
|
100
|
-
function getLatestVersion() {
|
|
101
|
-
return __awaiter(this, void 0, void 0, function () {
|
|
102
|
-
var response;
|
|
103
|
-
var _a, _b;
|
|
104
|
-
return __generator(this, function (_c) {
|
|
105
|
-
switch (_c.label) {
|
|
106
|
-
case 0: return [4, axios_1.default.get(URL_LATEST_RELEASE)];
|
|
107
|
-
case 1:
|
|
108
|
-
response = _c.sent();
|
|
109
|
-
if (response.status !== HTTP_STATUS_OK) {
|
|
110
|
-
throw new Error("Failed to get latest version from GitHub. Status code: ".concat(response.status));
|
|
111
|
-
}
|
|
112
|
-
return [2, (_b = (_a = response.data) === null || _a === void 0 ? void 0 : _a.tag_name) === null || _b === void 0 ? void 0 : _b.replace("v", "").trim()];
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
});
|
|
22
|
+
return response.data?.tag_name?.replace("v", "").trim();
|
|
116
23
|
}
|
|
117
|
-
function downloadAppArchive(archiveFilename, outputPath) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
switch (_a.label) {
|
|
122
|
-
case 0:
|
|
123
|
-
downloadUrl = "https://s.defang.io/".concat(archiveFilename, "?x-defang-source=npm");
|
|
124
|
-
downloadTargetFile = path.join(outputPath, archiveFilename);
|
|
125
|
-
return [4, downloadFile(downloadUrl, downloadTargetFile)];
|
|
126
|
-
case 1: return [2, _a.sent()];
|
|
127
|
-
}
|
|
128
|
-
});
|
|
129
|
-
});
|
|
24
|
+
async function downloadAppArchive(archiveFilename, outputPath) {
|
|
25
|
+
const downloadUrl = `https://s.defang.io/${archiveFilename}?x-defang-source=npm`;
|
|
26
|
+
const downloadTargetFile = path.join(outputPath, archiveFilename);
|
|
27
|
+
return await downloadFile(downloadUrl, downloadTargetFile);
|
|
130
28
|
}
|
|
131
|
-
function downloadFile(downloadUrl, downloadTargetFile) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
return [4, axios_1.default.get(downloadUrl, {
|
|
139
|
-
responseType: "arraybuffer",
|
|
140
|
-
headers: {
|
|
141
|
-
"Content-Type": "application/octet-stream",
|
|
142
|
-
},
|
|
143
|
-
})];
|
|
144
|
-
case 1:
|
|
145
|
-
response = _a.sent();
|
|
146
|
-
if (response.data == null) {
|
|
147
|
-
throw new Error("Failed to download ".concat(downloadUrl, ". No data in response."));
|
|
148
|
-
}
|
|
149
|
-
return [4, fs.promises.writeFile(downloadTargetFile, response.data)];
|
|
150
|
-
case 2:
|
|
151
|
-
_a.sent();
|
|
152
|
-
return [2, downloadTargetFile];
|
|
153
|
-
case 3:
|
|
154
|
-
error_1 = _a.sent();
|
|
155
|
-
console.error(error_1);
|
|
156
|
-
return [4, fs.promises.unlink(downloadTargetFile)];
|
|
157
|
-
case 4:
|
|
158
|
-
_a.sent();
|
|
159
|
-
return [2, null];
|
|
160
|
-
case 5: return [2];
|
|
161
|
-
}
|
|
29
|
+
async function downloadFile(downloadUrl, downloadTargetFile) {
|
|
30
|
+
try {
|
|
31
|
+
const response = await axios.get(downloadUrl, {
|
|
32
|
+
responseType: "arraybuffer",
|
|
33
|
+
headers: {
|
|
34
|
+
"Content-Type": "application/octet-stream",
|
|
35
|
+
},
|
|
162
36
|
});
|
|
163
|
-
|
|
37
|
+
if (response.data == null) {
|
|
38
|
+
throw new Error(`Failed to download ${downloadUrl}. No data in response.`);
|
|
39
|
+
}
|
|
40
|
+
await fs.promises.writeFile(downloadTargetFile, response.data);
|
|
41
|
+
return downloadTargetFile;
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
console.error(error);
|
|
45
|
+
await fs.promises.unlink(downloadTargetFile);
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
164
48
|
}
|
|
165
|
-
function extractArchive(archiveFilePath, outputPath) {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
case 1: return [4, extractZip(archiveFilePath, outputPath)];
|
|
180
|
-
case 2:
|
|
181
|
-
result = _b.sent();
|
|
182
|
-
return [3, 5];
|
|
183
|
-
case 3:
|
|
184
|
-
result = extractTarGz(archiveFilePath, outputPath);
|
|
185
|
-
return [3, 5];
|
|
186
|
-
case 4: return [2, false];
|
|
187
|
-
case 5: return [2, result];
|
|
188
|
-
}
|
|
189
|
-
});
|
|
190
|
-
});
|
|
49
|
+
async function extractArchive(archiveFilePath, outputPath) {
|
|
50
|
+
let result = false;
|
|
51
|
+
const ext = path.extname(archiveFilePath).toLocaleLowerCase();
|
|
52
|
+
switch (ext) {
|
|
53
|
+
case ".zip":
|
|
54
|
+
result = await extractZip(archiveFilePath, outputPath);
|
|
55
|
+
break;
|
|
56
|
+
case ".gz":
|
|
57
|
+
result = extractTarGz(archiveFilePath, outputPath);
|
|
58
|
+
break;
|
|
59
|
+
default:
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
return result;
|
|
191
63
|
}
|
|
192
|
-
function extractZip(zipPath, outputPath) {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
return [2, false];
|
|
209
|
-
case 3: return [2];
|
|
210
|
-
}
|
|
211
|
-
});
|
|
212
|
-
});
|
|
64
|
+
async function extractZip(zipPath, outputPath) {
|
|
65
|
+
try {
|
|
66
|
+
const zip = new AdmZip(zipPath);
|
|
67
|
+
let extension = "";
|
|
68
|
+
if (["win32", "cygwin"].includes(process.platform)) {
|
|
69
|
+
extension = ".exe";
|
|
70
|
+
}
|
|
71
|
+
const executableFullName = EXECUTABLE + extension;
|
|
72
|
+
const result = zip.extractEntryTo(executableFullName, outputPath, true, true);
|
|
73
|
+
await fs.promises.chmod(path.join(outputPath, executableFullName), 755);
|
|
74
|
+
return result;
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
console.error(`An error occurred during zip extraction: ${error}`);
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
213
80
|
}
|
|
214
81
|
function extractTarGz(tarGzFilePath, outputPath) {
|
|
215
82
|
try {
|
|
@@ -222,26 +89,17 @@ function extractTarGz(tarGzFilePath, outputPath) {
|
|
|
222
89
|
return true;
|
|
223
90
|
}
|
|
224
91
|
catch (error) {
|
|
225
|
-
console.error(
|
|
92
|
+
console.error(`An error occurred during tar.gz extraction: ${error}`);
|
|
226
93
|
return false;
|
|
227
94
|
}
|
|
228
95
|
}
|
|
229
|
-
function deleteArchive(archiveFilePath) {
|
|
230
|
-
|
|
231
|
-
return __generator(this, function (_a) {
|
|
232
|
-
switch (_a.label) {
|
|
233
|
-
case 0: return [4, fs.promises.unlink(archiveFilePath)];
|
|
234
|
-
case 1:
|
|
235
|
-
_a.sent();
|
|
236
|
-
return [2];
|
|
237
|
-
}
|
|
238
|
-
});
|
|
239
|
-
});
|
|
96
|
+
async function deleteArchive(archiveFilePath) {
|
|
97
|
+
await fs.promises.unlink(archiveFilePath);
|
|
240
98
|
}
|
|
241
99
|
function getAppArchiveFilename(version, platform, arch) {
|
|
242
|
-
|
|
100
|
+
let compression = "zip";
|
|
243
101
|
if (!SEMVER_REGEX.test(version)) {
|
|
244
|
-
throw new Error(
|
|
102
|
+
throw new Error(`Unsupported version: ${version}`);
|
|
245
103
|
}
|
|
246
104
|
switch (platform) {
|
|
247
105
|
case "win32":
|
|
@@ -256,7 +114,7 @@ function getAppArchiveFilename(version, platform, arch) {
|
|
|
256
114
|
platform = "macOS";
|
|
257
115
|
break;
|
|
258
116
|
default:
|
|
259
|
-
throw new Error(
|
|
117
|
+
throw new Error(`Unsupported operating system: ${platform}`);
|
|
260
118
|
}
|
|
261
119
|
switch (arch) {
|
|
262
120
|
case "x64":
|
|
@@ -266,81 +124,70 @@ function getAppArchiveFilename(version, platform, arch) {
|
|
|
266
124
|
arch = "arm64";
|
|
267
125
|
break;
|
|
268
126
|
default:
|
|
269
|
-
throw new Error(
|
|
127
|
+
throw new Error(`Unsupported architecture: ${arch}`);
|
|
270
128
|
}
|
|
271
129
|
if (platform === "macOS") {
|
|
272
|
-
return
|
|
130
|
+
return `defang_${version}_${platform}.${compression}`;
|
|
273
131
|
}
|
|
274
|
-
return
|
|
132
|
+
return `defang_${version}_${platform}_${arch}.${compression}`;
|
|
275
133
|
}
|
|
276
134
|
function getPathToExecutable() {
|
|
277
|
-
|
|
135
|
+
let extension = "";
|
|
278
136
|
if (["win32", "cygwin"].includes(process.platform)) {
|
|
279
137
|
extension = ".exe";
|
|
280
138
|
}
|
|
281
|
-
|
|
139
|
+
const executablePath = path.join(__dirname, `${EXECUTABLE}${extension}`);
|
|
282
140
|
try {
|
|
283
|
-
return
|
|
141
|
+
return fs.existsSync(executablePath) ? executablePath : null;
|
|
284
142
|
}
|
|
285
143
|
catch (e) {
|
|
286
144
|
return null;
|
|
287
145
|
}
|
|
288
146
|
}
|
|
289
147
|
function extractCLIVersions(versionInfo) {
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
148
|
+
const regex = /^([A-Za-z ]+):\s*v?(\d+\.\d+\.\d+(?:-[\w.-]+)?)$/gm;
|
|
149
|
+
const versions = {
|
|
150
|
+
defangCLI: null,
|
|
151
|
+
latestCLI: null,
|
|
152
|
+
};
|
|
153
|
+
for (const [, label, version] of versionInfo.matchAll(regex)) {
|
|
154
|
+
const key = label.trim().toLowerCase();
|
|
155
|
+
if (key === "defang cli")
|
|
156
|
+
versions.defangCLI = version;
|
|
157
|
+
else if (key === "latest cli")
|
|
158
|
+
versions.latestCLI = version;
|
|
300
159
|
}
|
|
160
|
+
return versions;
|
|
301
161
|
}
|
|
302
|
-
function getVersionInfo() {
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
verInfo = extractCLIVersions(versionInfo.stdout);
|
|
322
|
-
result.current = verInfo.defangCLI;
|
|
323
|
-
result.latest = verInfo.latestCLI;
|
|
324
|
-
return [3, 6];
|
|
325
|
-
case 5:
|
|
326
|
-
error_3 = _a.sent();
|
|
327
|
-
console.error(error_3);
|
|
328
|
-
return [3, 6];
|
|
329
|
-
case 6: return [2, result];
|
|
330
|
-
}
|
|
331
|
-
});
|
|
332
|
-
});
|
|
162
|
+
async function getVersionInfo() {
|
|
163
|
+
let result = { current: null, latest: null };
|
|
164
|
+
try {
|
|
165
|
+
const execPath = getPathToExecutable();
|
|
166
|
+
if (!execPath) {
|
|
167
|
+
const latestVersion = await getLatestVersion();
|
|
168
|
+
return { current: null, latest: latestVersion };
|
|
169
|
+
}
|
|
170
|
+
const versionInfo = await exec(quoteIfNeeded(execPath) + " version");
|
|
171
|
+
const verInfo = extractCLIVersions(versionInfo.stdout);
|
|
172
|
+
result.current = verInfo.defangCLI;
|
|
173
|
+
result.latest = verInfo.latestCLI;
|
|
174
|
+
verInfo.defangCLI ?? console.warn("Defang CLI version not found");
|
|
175
|
+
verInfo.latestCLI ?? console.warn("Latest CLI version not found");
|
|
176
|
+
}
|
|
177
|
+
catch (error) {
|
|
178
|
+
console.error(error);
|
|
179
|
+
}
|
|
180
|
+
return result;
|
|
333
181
|
}
|
|
334
182
|
function extractCLIWrapperArgs(args) {
|
|
335
|
-
|
|
183
|
+
const cliParams = {
|
|
336
184
|
uselatest: true,
|
|
337
185
|
};
|
|
338
|
-
|
|
339
|
-
for (
|
|
340
|
-
|
|
341
|
-
var argLower = arg.toLowerCase().replaceAll(" ", "");
|
|
186
|
+
const outArgs = [];
|
|
187
|
+
for (const arg of args) {
|
|
188
|
+
const argLower = arg.toLowerCase().replaceAll(" ", "");
|
|
342
189
|
if (argLower.startsWith("--use-latest")) {
|
|
343
|
-
|
|
190
|
+
const startOfValue = argLower.indexOf("=");
|
|
344
191
|
if (startOfValue >= 0) {
|
|
345
192
|
if (argLower.slice(startOfValue + 1) == "false") {
|
|
346
193
|
cliParams.uselatest = false;
|
|
@@ -351,103 +198,76 @@ function extractCLIWrapperArgs(args) {
|
|
|
351
198
|
outArgs.push(arg);
|
|
352
199
|
}
|
|
353
200
|
}
|
|
354
|
-
return { cliParams
|
|
201
|
+
return { cliParams, outArgs };
|
|
355
202
|
}
|
|
356
203
|
function getEndNameFromPath(pathLine) {
|
|
357
|
-
|
|
204
|
+
const executableName = path.basename(pathLine);
|
|
358
205
|
return executableName.split(".")[0];
|
|
359
206
|
}
|
|
360
|
-
function install(version, saveDirectory, os) {
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
if (result === false) {
|
|
379
|
-
throw new Error("Failed to install binaries!");
|
|
380
|
-
}
|
|
381
|
-
return [4, deleteArchive(archiveFile)];
|
|
382
|
-
case 3:
|
|
383
|
-
_a.sent();
|
|
384
|
-
return [3, 5];
|
|
385
|
-
case 4:
|
|
386
|
-
error_4 = _a.sent();
|
|
387
|
-
console.error(error_4);
|
|
388
|
-
throw error_4;
|
|
389
|
-
case 5: return [2];
|
|
390
|
-
}
|
|
391
|
-
});
|
|
392
|
-
});
|
|
207
|
+
export async function install(version, saveDirectory, os) {
|
|
208
|
+
try {
|
|
209
|
+
console.log(`Getting latest defang cli`);
|
|
210
|
+
const filename = getAppArchiveFilename(version, os.platform, os.arch);
|
|
211
|
+
const archiveFile = await downloadAppArchive(filename, saveDirectory);
|
|
212
|
+
if (archiveFile == null || archiveFile.length === 0) {
|
|
213
|
+
throw new Error(`Failed to download ${filename}`);
|
|
214
|
+
}
|
|
215
|
+
const result = await extractArchive(archiveFile, saveDirectory);
|
|
216
|
+
if (result === false) {
|
|
217
|
+
throw new Error(`Failed to install binaries!`);
|
|
218
|
+
}
|
|
219
|
+
await deleteArchive(archiveFile);
|
|
220
|
+
}
|
|
221
|
+
catch (error) {
|
|
222
|
+
console.error(error);
|
|
223
|
+
throw error;
|
|
224
|
+
}
|
|
393
225
|
}
|
|
394
|
-
function
|
|
395
|
-
return
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
if (!(latest != null && (current == null || current != latest))) return [3, 3];
|
|
408
|
-
return [4, install(latest, __dirname, {
|
|
409
|
-
platform: process.platform,
|
|
410
|
-
arch: process.arch,
|
|
411
|
-
})];
|
|
412
|
-
case 2:
|
|
413
|
-
_d.sent();
|
|
414
|
-
_d.label = 3;
|
|
415
|
-
case 3:
|
|
416
|
-
pathToExec = getPathToExecutable();
|
|
417
|
-
if (!pathToExec) {
|
|
418
|
-
throw new Error("Could not find the defang executable.");
|
|
419
|
-
}
|
|
420
|
-
commandline = ["npx", getEndNameFromPath(pathToExec)]
|
|
421
|
-
.join(" ")
|
|
422
|
-
.trim();
|
|
423
|
-
processResult = child_process.spawnSync(pathToExec, args, {
|
|
424
|
-
stdio: "inherit",
|
|
425
|
-
env: __assign(__assign({}, process.env), { DEFANG_COMMAND_EXECUTOR: commandline }),
|
|
426
|
-
});
|
|
427
|
-
processResult.error && console.error(processResult.error);
|
|
428
|
-
process.exitCode = (_c = processResult.status) !== null && _c !== void 0 ? _c : 1;
|
|
429
|
-
return [3, 5];
|
|
430
|
-
case 4:
|
|
431
|
-
error_5 = _d.sent();
|
|
432
|
-
console.error(error_5);
|
|
433
|
-
process.exitCode = 2;
|
|
434
|
-
return [3, 5];
|
|
435
|
-
case 5: return [2];
|
|
226
|
+
function quoteIfNeeded(p) {
|
|
227
|
+
return /\s/.test(p) ? `"${p}"` : p;
|
|
228
|
+
}
|
|
229
|
+
export async function run() {
|
|
230
|
+
try {
|
|
231
|
+
const { cliParams, outArgs: args } = extractCLIWrapperArgs(process.argv.slice(2));
|
|
232
|
+
if (cliParams.uselatest) {
|
|
233
|
+
const { current, latest } = await getVersionInfo();
|
|
234
|
+
if (latest != null && (current == null || current != latest)) {
|
|
235
|
+
await install(latest, __dirname, {
|
|
236
|
+
platform: process.platform,
|
|
237
|
+
arch: process.arch,
|
|
238
|
+
});
|
|
436
239
|
}
|
|
240
|
+
}
|
|
241
|
+
const pathToExec = getPathToExecutable();
|
|
242
|
+
if (!pathToExec) {
|
|
243
|
+
throw new Error("Could not find the defang executable.");
|
|
244
|
+
}
|
|
245
|
+
const commandline = ["npx", quoteIfNeeded(getEndNameFromPath(pathToExec))]
|
|
246
|
+
.join(" ")
|
|
247
|
+
.trim();
|
|
248
|
+
const processResult = child_process.spawnSync(pathToExec, args, {
|
|
249
|
+
stdio: "inherit",
|
|
250
|
+
env: { ...process.env, DEFANG_COMMAND_EXECUTOR: commandline },
|
|
437
251
|
});
|
|
438
|
-
|
|
252
|
+
processResult.error && console.error(processResult.error);
|
|
253
|
+
process.exitCode = processResult.status ?? 1;
|
|
254
|
+
}
|
|
255
|
+
catch (error) {
|
|
256
|
+
console.error(error);
|
|
257
|
+
process.exitCode = 2;
|
|
258
|
+
}
|
|
439
259
|
}
|
|
440
|
-
|
|
441
|
-
deleteArchive
|
|
442
|
-
downloadAppArchive
|
|
443
|
-
downloadFile
|
|
444
|
-
extractArchive
|
|
445
|
-
extractCLIVersions
|
|
446
|
-
extractCLIWrapperArgs
|
|
447
|
-
getAppArchiveFilename
|
|
448
|
-
getEndNameFromPath
|
|
449
|
-
getLatestVersion
|
|
450
|
-
getVersionInfo
|
|
451
|
-
getPathToExecutable
|
|
260
|
+
const clilib = {
|
|
261
|
+
deleteArchive,
|
|
262
|
+
downloadAppArchive,
|
|
263
|
+
downloadFile,
|
|
264
|
+
extractArchive,
|
|
265
|
+
extractCLIVersions,
|
|
266
|
+
extractCLIWrapperArgs,
|
|
267
|
+
getAppArchiveFilename,
|
|
268
|
+
getEndNameFromPath,
|
|
269
|
+
getLatestVersion,
|
|
270
|
+
getVersionInfo,
|
|
271
|
+
getPathToExecutable,
|
|
452
272
|
};
|
|
453
|
-
|
|
273
|
+
export default clilib;
|
package/package.json
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "defang",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.18",
|
|
4
4
|
"author": "Defang Software Labs Inc.",
|
|
5
5
|
"description": "CLI to take your app from Docker Compose to a secure and scalable deployment on your favorite cloud in minutes",
|
|
6
6
|
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"ts-node": {
|
|
9
|
+
"esm": true
|
|
10
|
+
},
|
|
7
11
|
"bin": {
|
|
8
12
|
"defang": "bin/cli.js"
|
|
9
13
|
},
|
|
@@ -30,9 +34,9 @@
|
|
|
30
34
|
"recursive": true
|
|
31
35
|
},
|
|
32
36
|
"scripts": {
|
|
33
|
-
"build": "tsc",
|
|
37
|
+
"build": "tsc -p tsconfig.json",
|
|
34
38
|
"postbuild": "chmod +x bin/cli.js",
|
|
35
|
-
"test": "
|
|
39
|
+
"test": "tsx --tsconfig tsconfig.test.json mocha.ts"
|
|
36
40
|
},
|
|
37
41
|
"dependencies": {
|
|
38
42
|
"adm-zip": "^0.5.14",
|
|
@@ -43,19 +47,17 @@
|
|
|
43
47
|
"@types/adm-zip": "^0.5.5",
|
|
44
48
|
"@types/chai": "^5.1.1",
|
|
45
49
|
"@types/chai-as-promised": "^8.0.0",
|
|
46
|
-
"@types/mocha": "^10.0.
|
|
50
|
+
"@types/mocha": "^10.0.10",
|
|
47
51
|
"@types/node": "^20.12.7",
|
|
48
52
|
"@types/sinon": "^17.0.3",
|
|
49
|
-
"@types/source-map-support": "^0.5.10",
|
|
50
53
|
"@types/tar": "^6.1.13",
|
|
51
54
|
"chai": "^5.1.1",
|
|
52
55
|
"chai-as-promised": "^8.0.0",
|
|
53
|
-
"
|
|
54
|
-
"mocha": "^10.6.0",
|
|
56
|
+
"mocha": "^10.8.2",
|
|
55
57
|
"sinon": "^18.0.0",
|
|
56
|
-
"source-map-support": "^0.5.21",
|
|
57
58
|
"ts-node": "^10.9.2",
|
|
58
|
-
"
|
|
59
|
+
"tsx": "^4.19.3",
|
|
60
|
+
"typescript": "^5.8.3"
|
|
59
61
|
},
|
|
60
62
|
"main": "./bin/cli.js"
|
|
61
63
|
}
|
package/test/clilib.spec.ts
DELETED
|
@@ -1,208 +0,0 @@
|
|
|
1
|
-
import axios, { AxiosResponse } from "axios";
|
|
2
|
-
import * as chai from "chai";
|
|
3
|
-
import chaiAsPromised from "chai-as-promised";
|
|
4
|
-
import fs from "fs";
|
|
5
|
-
import "mocha";
|
|
6
|
-
import * as sinon from "sinon";
|
|
7
|
-
import clilib from "../src/clilib";
|
|
8
|
-
|
|
9
|
-
chai.use(chaiAsPromised);
|
|
10
|
-
const { assert, expect } = chai;
|
|
11
|
-
|
|
12
|
-
var sandbox: sinon.SinonSandbox;
|
|
13
|
-
|
|
14
|
-
describe("Testing getLatestVersion()", () => {
|
|
15
|
-
beforeEach(() => {
|
|
16
|
-
sandbox = sinon.createSandbox();
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
afterEach(() => {
|
|
20
|
-
sandbox.restore();
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
it("sanity", async () => {
|
|
24
|
-
const mockResponse: AxiosResponse = {
|
|
25
|
-
status: 200,
|
|
26
|
-
data: {
|
|
27
|
-
tag_name: "v0.5.32",
|
|
28
|
-
},
|
|
29
|
-
} as AxiosResponse;
|
|
30
|
-
sandbox.stub(axios, "get").returns(Promise.resolve(mockResponse));
|
|
31
|
-
|
|
32
|
-
await expect(clilib.getLatestVersion()).to.eventually.equal("0.5.32");
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
it("bad HTTP Status", async () => {
|
|
36
|
-
const mockResponse: AxiosResponse = {
|
|
37
|
-
status: 500,
|
|
38
|
-
} as AxiosResponse;
|
|
39
|
-
sandbox.stub(axios, "get").returns(Promise.reject(mockResponse));
|
|
40
|
-
|
|
41
|
-
await expect(clilib.getLatestVersion()).to.be.rejected;
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it("empty tag_name", async () => {
|
|
45
|
-
const mockResponse: AxiosResponse = {
|
|
46
|
-
status: 200,
|
|
47
|
-
data: {
|
|
48
|
-
tag_name: "",
|
|
49
|
-
},
|
|
50
|
-
} as AxiosResponse;
|
|
51
|
-
sandbox.stub(axios, "get").returns(Promise.resolve(mockResponse));
|
|
52
|
-
|
|
53
|
-
await expect(clilib.getLatestVersion()).to.eventually.equal("");
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
it("ill-formed tag_name", async () => {
|
|
57
|
-
const mockResponse: AxiosResponse = {
|
|
58
|
-
status: 200,
|
|
59
|
-
data: {},
|
|
60
|
-
} as AxiosResponse;
|
|
61
|
-
sandbox.stub(axios, "get").returns(Promise.resolve(mockResponse));
|
|
62
|
-
|
|
63
|
-
await expect(clilib.getLatestVersion()).to.eventually.be.undefined;
|
|
64
|
-
});
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
describe("Testing downloadFile()", () => {
|
|
68
|
-
let axiosGetStub: sinon.SinonStub;
|
|
69
|
-
let writeStub: sinon.SinonStub;
|
|
70
|
-
let unlinkStub: sinon.SinonStub;
|
|
71
|
-
|
|
72
|
-
let downloadFileName = "target";
|
|
73
|
-
const url = "url";
|
|
74
|
-
const header = {
|
|
75
|
-
responseType: "arraybuffer",
|
|
76
|
-
headers: {
|
|
77
|
-
"Content-Type": "application/octet-stream",
|
|
78
|
-
},
|
|
79
|
-
};
|
|
80
|
-
let mockResponse: AxiosResponse;
|
|
81
|
-
|
|
82
|
-
beforeEach(() => {
|
|
83
|
-
sandbox = sinon.createSandbox();
|
|
84
|
-
|
|
85
|
-
downloadFileName = "target";
|
|
86
|
-
mockResponse = {
|
|
87
|
-
status: 200,
|
|
88
|
-
data: {},
|
|
89
|
-
} as AxiosResponse;
|
|
90
|
-
|
|
91
|
-
axiosGetStub = sandbox
|
|
92
|
-
.stub(axios, "get")
|
|
93
|
-
.returns(Promise.resolve(mockResponse));
|
|
94
|
-
writeStub = sandbox
|
|
95
|
-
.stub(fs.promises, "writeFile")
|
|
96
|
-
.callsFake(() => Promise.resolve());
|
|
97
|
-
unlinkStub = sandbox
|
|
98
|
-
.stub(fs.promises, "unlink")
|
|
99
|
-
.callsFake(() => Promise.resolve());
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
afterEach(() => {
|
|
103
|
-
sandbox.restore();
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it("sanity", async () => {
|
|
107
|
-
await expect(
|
|
108
|
-
clilib.downloadFile(url, downloadFileName)
|
|
109
|
-
).to.eventually.equal(downloadFileName);
|
|
110
|
-
|
|
111
|
-
sinon.assert.calledWith(axiosGetStub, url, header);
|
|
112
|
-
sinon.assert.calledWith(writeStub, downloadFileName, mockResponse.data);
|
|
113
|
-
sinon.assert.notCalled(unlinkStub);
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
it("download fails path", async () => {
|
|
117
|
-
axiosGetStub.returns(Promise.reject("failed"));
|
|
118
|
-
const targetFile = await clilib.downloadFile(url, downloadFileName);
|
|
119
|
-
await expect(
|
|
120
|
-
clilib.downloadFile(url, downloadFileName)
|
|
121
|
-
).to.eventually.equal(targetFile);
|
|
122
|
-
|
|
123
|
-
sinon.assert.calledWith(axiosGetStub, url, header);
|
|
124
|
-
sinon.assert.notCalled(writeStub);
|
|
125
|
-
sinon.assert.calledWith(unlinkStub, downloadFileName);
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
it("write failed", async () => {
|
|
129
|
-
writeStub.returns(Promise.reject("failed"));
|
|
130
|
-
await expect(clilib.downloadFile(url, downloadFileName)).to.eventually.null;
|
|
131
|
-
sinon.assert.calledWith(axiosGetStub, url, header);
|
|
132
|
-
sinon.assert.calledWith(unlinkStub, downloadFileName);
|
|
133
|
-
});
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
describe("Testing getAppArchiveFilename()", () => {
|
|
137
|
-
it("returns expected filename", () => {
|
|
138
|
-
const iterationData = [
|
|
139
|
-
["0.0.0", "win32", "x64", "defang_0.0.0_windows_amd64.zip"],
|
|
140
|
-
["0.1.0", "windows", "x64", "defang_0.1.0_windows_amd64.zip"],
|
|
141
|
-
["0.2.9", "windows", "arm64", "defang_0.2.9_windows_arm64.zip"],
|
|
142
|
-
["0.3.10", "linux", "x64", "defang_0.3.10_linux_amd64.tar.gz"],
|
|
143
|
-
["0.4.21", "linux", "arm64", "defang_0.4.21_linux_arm64.tar.gz"],
|
|
144
|
-
["0.5.45", "darwin", "arm64", "defang_0.5.45_macOS.zip"],
|
|
145
|
-
["0.5.45", "darwin", "x64", "defang_0.5.45_macOS.zip"],
|
|
146
|
-
] as const;
|
|
147
|
-
const testFunc = (
|
|
148
|
-
version: string,
|
|
149
|
-
platform: string,
|
|
150
|
-
arch: string,
|
|
151
|
-
expected: string
|
|
152
|
-
) =>
|
|
153
|
-
expect(clilib.getAppArchiveFilename(version, platform, arch)).to.be.equal(
|
|
154
|
-
expected
|
|
155
|
-
);
|
|
156
|
-
iterationData.forEach((testData) => testFunc.call(null, ...testData));
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
it("check error cases", () => {
|
|
160
|
-
const iterationData = [
|
|
161
|
-
["", "windows", "x64"],
|
|
162
|
-
["0.2.9", "windows", "risc64"],
|
|
163
|
-
["0.5.45", "darwin", "powerpc"],
|
|
164
|
-
] as const;
|
|
165
|
-
const testFunc = (version: string, platform: string, arch: string) =>
|
|
166
|
-
expect(() =>
|
|
167
|
-
clilib.getAppArchiveFilename(version, platform, arch)
|
|
168
|
-
).to.throw();
|
|
169
|
-
iterationData.forEach((testData) => testFunc.call(null, ...testData));
|
|
170
|
-
});
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
describe("Testing extractCLIVersions()", () => {
|
|
174
|
-
it("sanity", async () => {
|
|
175
|
-
const versionInfo =
|
|
176
|
-
"Defang CLI: v0.5.24\nLatest CLI: v0.5.32\nDefang Fabric: v0.5.0-643";
|
|
177
|
-
const expected = { defangCLI: "0.5.24", latestCLI: "0.5.32" };
|
|
178
|
-
|
|
179
|
-
expect(clilib.extractCLIVersions(versionInfo)).to.be.deep.equal(expected);
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
it("missing v in version text", async () => {
|
|
183
|
-
const versionInfo =
|
|
184
|
-
"Defang CLI: 0.5.24\nLatest CLI: 0.5.32\nDefang Fabric: v0.5.0-643";
|
|
185
|
-
const expected = { defangCLI: "0.5.24", latestCLI: "0.5.32" };
|
|
186
|
-
|
|
187
|
-
expect(clilib.extractCLIVersions(versionInfo)).to.be.deep.equal(expected);
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
it("missing Defang CLI", () => {
|
|
191
|
-
const versionInfo =
|
|
192
|
-
"Defang CLI: \nLatest CLI: v0.5.32\nDefang Fabric: v0.5.0-643";
|
|
193
|
-
assert.doesNotThrow(() => clilib.extractCLIVersions(versionInfo));
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
it("missing Latest CLI", () => {
|
|
197
|
-
const versionInfo =
|
|
198
|
-
"Defang CLI: v0.5.24\nLatest CLI: \nDefang Fabric: v0.5.0-643";
|
|
199
|
-
assert.doesNotThrow(() => clilib.extractCLIVersions(versionInfo));
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
it("no fabric version in input", async () => {
|
|
203
|
-
const versionInfo = "Defang CLI: v0.5.24\nLatest CLI: v0.5.32\n";
|
|
204
|
-
const expected = { defangCLI: "0.5.24", latestCLI: "0.5.32" };
|
|
205
|
-
|
|
206
|
-
expect(clilib.extractCLIVersions(versionInfo)).to.be.deep.equal(expected);
|
|
207
|
-
});
|
|
208
|
-
});
|