@solana-mobile/dapp-store-cli 0.15.0 → 0.16.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/dapp-store.js +3 -1
- package/lib/CliSetup.js +304 -505
- package/lib/CliUtils.js +6 -376
- package/lib/__tests__/CliSetupTest.js +484 -74
- package/lib/cli/__tests__/parseErrors.test.js +25 -0
- package/lib/cli/__tests__/signer.test.js +436 -0
- package/lib/cli/constants.js +23 -0
- package/lib/cli/messages.js +21 -0
- package/lib/cli/parseErrors.js +41 -0
- package/lib/{commands/publish/PublishCliSupport.js → cli/selfUpdate.js} +72 -38
- package/lib/{commands/publish/PublishCliRemove.js → cli/signer.js} +35 -56
- package/lib/index.js +96 -5
- package/lib/package.json +5 -24
- package/lib/portal/__tests__/releaseMetadata.test.js +647 -0
- package/lib/portal/__tests__/translators.test.js +76 -0
- package/lib/portal/__tests__/workflowClient.test.js +457 -0
- package/lib/portal/attestationClient.js +143 -0
- package/lib/portal/files.js +64 -0
- package/lib/portal/http.js +364 -0
- package/lib/portal/records.js +64 -0
- package/lib/portal/releaseMetadata.js +748 -0
- package/lib/portal/translators.js +460 -0
- package/lib/portal/types.js +1 -0
- package/lib/portal/workflowClient.js +704 -0
- package/lib/publication/PublicationProgressReporter.js +1051 -0
- package/lib/publication/__tests__/PublicationProgressReporter.test.js +174 -0
- package/lib/{commands/ValidateCommand.js → publication/__tests__/fundingPreflight.test.js} +90 -66
- package/lib/publication/__tests__/publicationSummary.test.js +26 -0
- package/lib/publication/cliValidation.js +482 -0
- package/lib/publication/fundingPreflight.js +246 -0
- package/lib/publication/publicationSummary.js +99 -0
- package/lib/{commands/utils.js → publication/runPublicationWorkflow.js} +16 -46
- package/package.json +5 -24
- package/src/CliSetup.ts +370 -505
- package/src/CliUtils.ts +9 -233
- package/src/__tests__/CliSetupTest.ts +272 -120
- package/src/cli/__tests__/parseErrors.test.ts +34 -0
- package/src/cli/__tests__/signer.test.ts +359 -0
- package/src/cli/constants.ts +3 -0
- package/src/cli/messages.ts +27 -0
- package/src/cli/parseErrors.ts +62 -0
- package/src/cli/selfUpdate.ts +59 -0
- package/src/cli/signer.ts +38 -0
- package/src/index.ts +31 -4
- package/src/portal/__tests__/releaseMetadata.test.ts +508 -0
- package/src/portal/__tests__/translators.test.ts +82 -0
- package/src/portal/__tests__/workflowClient.test.ts +278 -0
- package/src/portal/attestationClient.ts +19 -0
- package/src/portal/files.ts +73 -0
- package/src/portal/http.ts +170 -0
- package/src/portal/records.ts +38 -0
- package/src/portal/releaseMetadata.ts +489 -0
- package/src/portal/translators.ts +750 -0
- package/src/portal/types.ts +27 -0
- package/src/portal/workflowClient.ts +575 -0
- package/src/publication/PublicationProgressReporter.ts +1026 -0
- package/src/publication/__tests__/PublicationProgressReporter.test.ts +210 -0
- package/src/publication/__tests__/fundingPreflight.test.ts +78 -0
- package/src/publication/__tests__/publicationSummary.test.ts +30 -0
- package/src/publication/cliValidation.ts +264 -0
- package/src/publication/fundingPreflight.ts +123 -0
- package/src/publication/publicationSummary.ts +26 -0
- package/src/publication/runPublicationWorkflow.ts +46 -0
- package/lib/commands/create/CreateCliApp.js +0 -223
- package/lib/commands/create/CreateCliRelease.js +0 -290
- package/lib/commands/create/index.js +0 -40
- package/lib/commands/index.js +0 -3
- package/lib/commands/publish/PublishCliSubmit.js +0 -208
- package/lib/commands/publish/PublishCliUpdate.js +0 -211
- package/lib/commands/publish/index.js +0 -22
- package/lib/commands/scaffolding/ScaffoldInit.js +0 -15
- package/lib/commands/scaffolding/index.js +0 -1
- package/lib/config/EnvVariables.js +0 -59
- package/lib/config/PublishDetails.js +0 -915
- package/lib/config/S3StorageManager.js +0 -93
- package/lib/config/index.js +0 -2
- package/lib/generated/config_obj.json +0 -1
- package/lib/generated/config_schema.json +0 -1
- package/lib/prebuild_schema/publishing_source.yaml +0 -64
- package/lib/prebuild_schema/schemagen.js +0 -25
- package/lib/upload/CachedStorageDriver.js +0 -293
- package/lib/upload/TurboStorageDriver.js +0 -718
- package/lib/upload/index.js +0 -2
- package/src/commands/ValidateCommand.ts +0 -82
- package/src/commands/create/CreateCliApp.ts +0 -93
- package/src/commands/create/CreateCliRelease.ts +0 -149
- package/src/commands/create/index.ts +0 -47
- package/src/commands/index.ts +0 -3
- package/src/commands/publish/PublishCliRemove.ts +0 -66
- package/src/commands/publish/PublishCliSubmit.ts +0 -93
- package/src/commands/publish/PublishCliSupport.ts +0 -66
- package/src/commands/publish/PublishCliUpdate.ts +0 -101
- package/src/commands/publish/index.ts +0 -29
- package/src/commands/scaffolding/ScaffoldInit.ts +0 -20
- package/src/commands/scaffolding/index.ts +0 -1
- package/src/commands/utils.ts +0 -33
- package/src/config/EnvVariables.ts +0 -39
- package/src/config/PublishDetails.ts +0 -456
- package/src/config/S3StorageManager.ts +0 -47
- package/src/config/index.ts +0 -2
- package/src/prebuild_schema/publishing_source.yaml +0 -64
- package/src/prebuild_schema/schemagen.js +0 -31
- package/src/upload/CachedStorageDriver.ts +0 -99
- package/src/upload/TurboStorageDriver.ts +0 -277
- package/src/upload/index.ts +0 -2
|
@@ -1,20 +1,231 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
function _array_like_to_array(arr, len) {
|
|
2
|
+
if (len == null || len > arr.length) len = arr.length;
|
|
3
|
+
for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i];
|
|
4
|
+
return arr2;
|
|
5
|
+
}
|
|
6
|
+
function _array_with_holes(arr) {
|
|
7
|
+
if (Array.isArray(arr)) return arr;
|
|
8
|
+
}
|
|
9
|
+
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
|
|
10
|
+
try {
|
|
11
|
+
var info = gen[key](arg);
|
|
12
|
+
var value = info.value;
|
|
13
|
+
} catch (error) {
|
|
14
|
+
reject(error);
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
if (info.done) {
|
|
18
|
+
resolve(value);
|
|
19
|
+
} else {
|
|
20
|
+
Promise.resolve(value).then(_next, _throw);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function _async_to_generator(fn) {
|
|
24
|
+
return function() {
|
|
25
|
+
var self = this, args = arguments;
|
|
26
|
+
return new Promise(function(resolve, reject) {
|
|
27
|
+
var gen = fn.apply(self, args);
|
|
28
|
+
function _next(value) {
|
|
29
|
+
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
|
|
30
|
+
}
|
|
31
|
+
function _throw(err) {
|
|
32
|
+
asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
|
|
33
|
+
}
|
|
34
|
+
_next(undefined);
|
|
35
|
+
});
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function _iterable_to_array_limit(arr, i) {
|
|
39
|
+
var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
|
|
40
|
+
if (_i == null) return;
|
|
41
|
+
var _arr = [];
|
|
42
|
+
var _n = true;
|
|
43
|
+
var _d = false;
|
|
44
|
+
var _s, _e;
|
|
45
|
+
try {
|
|
46
|
+
for(_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true){
|
|
47
|
+
_arr.push(_s.value);
|
|
48
|
+
if (i && _arr.length === i) break;
|
|
49
|
+
}
|
|
50
|
+
} catch (err) {
|
|
51
|
+
_d = true;
|
|
52
|
+
_e = err;
|
|
53
|
+
} finally{
|
|
54
|
+
try {
|
|
55
|
+
if (!_n && _i["return"] != null) _i["return"]();
|
|
56
|
+
} finally{
|
|
57
|
+
if (_d) throw _e;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return _arr;
|
|
61
|
+
}
|
|
62
|
+
function _non_iterable_rest() {
|
|
63
|
+
throw new TypeError("Invalid attempt to destructure non-iterable instance.\\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
|
|
64
|
+
}
|
|
65
|
+
function _sliced_to_array(arr, i) {
|
|
66
|
+
return _array_with_holes(arr) || _iterable_to_array_limit(arr, i) || _unsupported_iterable_to_array(arr, i) || _non_iterable_rest();
|
|
67
|
+
}
|
|
68
|
+
function _unsupported_iterable_to_array(o, minLen) {
|
|
69
|
+
if (!o) return;
|
|
70
|
+
if (typeof o === "string") return _array_like_to_array(o, minLen);
|
|
71
|
+
var n = Object.prototype.toString.call(o).slice(8, -1);
|
|
72
|
+
if (n === "Object" && o.constructor) n = o.constructor.name;
|
|
73
|
+
if (n === "Map" || n === "Set") return Array.from(n);
|
|
74
|
+
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _array_like_to_array(o, minLen);
|
|
75
|
+
}
|
|
76
|
+
function _ts_generator(thisArg, body) {
|
|
77
|
+
var f, y, t, _ = {
|
|
78
|
+
label: 0,
|
|
79
|
+
sent: function() {
|
|
80
|
+
if (t[0] & 1) throw t[1];
|
|
81
|
+
return t[1];
|
|
82
|
+
},
|
|
83
|
+
trys: [],
|
|
84
|
+
ops: []
|
|
85
|
+
}, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
86
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() {
|
|
87
|
+
return this;
|
|
88
|
+
}), g;
|
|
89
|
+
function verb(n) {
|
|
90
|
+
return function(v) {
|
|
91
|
+
return step([
|
|
92
|
+
n,
|
|
93
|
+
v
|
|
94
|
+
]);
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function step(op) {
|
|
98
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
99
|
+
while(g && (g = 0, op[0] && (_ = 0)), _)try {
|
|
100
|
+
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;
|
|
101
|
+
if (y = 0, t) op = [
|
|
102
|
+
op[0] & 2,
|
|
103
|
+
t.value
|
|
104
|
+
];
|
|
105
|
+
switch(op[0]){
|
|
106
|
+
case 0:
|
|
107
|
+
case 1:
|
|
108
|
+
t = op;
|
|
109
|
+
break;
|
|
110
|
+
case 4:
|
|
111
|
+
_.label++;
|
|
112
|
+
return {
|
|
113
|
+
value: op[1],
|
|
114
|
+
done: false
|
|
115
|
+
};
|
|
116
|
+
case 5:
|
|
117
|
+
_.label++;
|
|
118
|
+
y = op[1];
|
|
119
|
+
op = [
|
|
120
|
+
0
|
|
121
|
+
];
|
|
122
|
+
continue;
|
|
123
|
+
case 7:
|
|
124
|
+
op = _.ops.pop();
|
|
125
|
+
_.trys.pop();
|
|
126
|
+
continue;
|
|
127
|
+
default:
|
|
128
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {
|
|
129
|
+
_ = 0;
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) {
|
|
133
|
+
_.label = op[1];
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
if (op[0] === 6 && _.label < t[1]) {
|
|
137
|
+
_.label = t[1];
|
|
138
|
+
t = op;
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
if (t && _.label < t[2]) {
|
|
142
|
+
_.label = t[2];
|
|
143
|
+
_.ops.push(op);
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
if (t[2]) _.ops.pop();
|
|
147
|
+
_.trys.pop();
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
op = body.call(thisArg, _);
|
|
151
|
+
} catch (e) {
|
|
152
|
+
op = [
|
|
153
|
+
6,
|
|
154
|
+
e
|
|
155
|
+
];
|
|
156
|
+
y = 0;
|
|
157
|
+
} finally{
|
|
158
|
+
f = t = 0;
|
|
159
|
+
}
|
|
160
|
+
if (op[0] & 5) throw op[1];
|
|
161
|
+
return {
|
|
162
|
+
value: op[0] ? op[1] : void 0,
|
|
163
|
+
done: true
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
import fs from "node:fs";
|
|
168
|
+
import os from "node:os";
|
|
169
|
+
import path from "node:path";
|
|
170
|
+
import { afterEach, beforeEach, describe, expect, test } from "@jest/globals";
|
|
171
|
+
import { mainCli } from "../CliSetup";
|
|
172
|
+
import { DEFAULT_API_KEY_ENV, DEFAULT_LOCAL_PORTAL_URL, DEFAULT_PRODUCTION_PORTAL_URL, UPDATED_PUBLISHING_CLI_DOCS_URL, formatUpdatedCliUsageError, resolveApiKey, resolvePortalTargets, validateNewVersionArgs, validateResumeArgs } from "../publication/cliValidation";
|
|
173
|
+
describe("CLI surface", function() {
|
|
174
|
+
var createTempApkFile = function createTempApkFile() {
|
|
175
|
+
var fileName = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : "app-release.apk";
|
|
176
|
+
var apkPath = path.join(tempDir, fileName);
|
|
177
|
+
fs.writeFileSync(apkPath, "apk");
|
|
178
|
+
return apkPath;
|
|
179
|
+
};
|
|
5
180
|
var outputHelpReference = "(outputHelp)";
|
|
181
|
+
var trackedEnvKeys = [
|
|
182
|
+
DEFAULT_API_KEY_ENV,
|
|
183
|
+
"ALT_DAPP_STORE_API_KEY",
|
|
184
|
+
"DAPP_STORE_PORTAL_URL",
|
|
185
|
+
"DAPP_STORE_PORTAL_WEB_URL",
|
|
186
|
+
"DAPP_STORE_PORTAL_API_BASE_URL"
|
|
187
|
+
];
|
|
6
188
|
var errorOutput = "";
|
|
7
189
|
var otherOutput = "";
|
|
190
|
+
var tempDir = "";
|
|
191
|
+
var originalEnv = {};
|
|
8
192
|
beforeEach(function() {
|
|
9
193
|
errorOutput = "";
|
|
10
194
|
otherOutput = "";
|
|
195
|
+
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "dapp-store-cli-test-"));
|
|
196
|
+
originalEnv = Object.fromEntries(trackedEnvKeys.map(function(key) {
|
|
197
|
+
return [
|
|
198
|
+
key,
|
|
199
|
+
process.env[key]
|
|
200
|
+
];
|
|
201
|
+
}));
|
|
202
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
203
|
+
try {
|
|
204
|
+
for(var _iterator = trackedEnvKeys[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
205
|
+
var key = _step.value;
|
|
206
|
+
delete process.env[key];
|
|
207
|
+
}
|
|
208
|
+
} catch (err) {
|
|
209
|
+
_didIteratorError = true;
|
|
210
|
+
_iteratorError = err;
|
|
211
|
+
} finally{
|
|
212
|
+
try {
|
|
213
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
214
|
+
_iterator.return();
|
|
215
|
+
}
|
|
216
|
+
} finally{
|
|
217
|
+
if (_didIteratorError) {
|
|
218
|
+
throw _iteratorError;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
11
222
|
mainCli.exitOverride();
|
|
12
223
|
mainCli.configureOutput({
|
|
13
224
|
getOutHelpWidth: function getOutHelpWidth() {
|
|
14
|
-
return
|
|
225
|
+
return 200;
|
|
15
226
|
},
|
|
16
227
|
getErrHelpWidth: function getErrHelpWidth() {
|
|
17
|
-
return
|
|
228
|
+
return 200;
|
|
18
229
|
},
|
|
19
230
|
writeOut: function writeOut(str) {
|
|
20
231
|
otherOutput += str;
|
|
@@ -23,95 +234,294 @@ describe("Cli Setup & Execution", function() {
|
|
|
23
234
|
errorOutput += str;
|
|
24
235
|
}
|
|
25
236
|
});
|
|
237
|
+
process.exitCode = 0;
|
|
26
238
|
});
|
|
27
|
-
|
|
239
|
+
afterEach(function() {
|
|
240
|
+
fs.rmSync(tempDir, {
|
|
241
|
+
recursive: true,
|
|
242
|
+
force: true
|
|
243
|
+
});
|
|
244
|
+
var _iteratorNormalCompletion = true, _didIteratorError = false, _iteratorError = undefined;
|
|
245
|
+
try {
|
|
246
|
+
for(var _iterator = Object.entries(originalEnv)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true){
|
|
247
|
+
var _step_value = _sliced_to_array(_step.value, 2), key = _step_value[0], value = _step_value[1];
|
|
248
|
+
if (typeof value === "undefined") {
|
|
249
|
+
delete process.env[key];
|
|
250
|
+
} else {
|
|
251
|
+
process.env[key] = value;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
} catch (err) {
|
|
255
|
+
_didIteratorError = true;
|
|
256
|
+
_iteratorError = err;
|
|
257
|
+
} finally{
|
|
258
|
+
try {
|
|
259
|
+
if (!_iteratorNormalCompletion && _iterator.return != null) {
|
|
260
|
+
_iterator.return();
|
|
261
|
+
}
|
|
262
|
+
} finally{
|
|
263
|
+
if (_didIteratorError) {
|
|
264
|
+
throw _iteratorError;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
});
|
|
269
|
+
test("version reports the package version", function() {
|
|
28
270
|
expect(function() {
|
|
29
271
|
mainCli.parse([
|
|
30
|
-
"
|
|
272
|
+
"node",
|
|
31
273
|
"dapp-store",
|
|
32
274
|
"-V"
|
|
33
275
|
]);
|
|
34
|
-
}).toThrow(
|
|
276
|
+
}).toThrow();
|
|
35
277
|
});
|
|
36
|
-
test("
|
|
278
|
+
test("help advertises the default publication surface", function() {
|
|
37
279
|
expect(function() {
|
|
38
280
|
mainCli.parse([
|
|
39
|
-
"
|
|
40
|
-
"dapp-store"
|
|
281
|
+
"node",
|
|
282
|
+
"dapp-store",
|
|
283
|
+
"--help"
|
|
41
284
|
]);
|
|
42
285
|
}).toThrow(outputHelpReference);
|
|
43
|
-
expect(
|
|
286
|
+
expect(otherOutput).not.toContain("--new-version");
|
|
287
|
+
expect(otherOutput).toContain("resume");
|
|
288
|
+
expect(otherOutput).toContain("--apk-file");
|
|
289
|
+
expect(otherOutput).toContain("--apk-url");
|
|
290
|
+
expect(otherOutput).toContain("--keypair");
|
|
291
|
+
expect(otherOutput).toContain("--portal-url");
|
|
292
|
+
expect(otherOutput).not.toContain("--dapp-id");
|
|
293
|
+
expect(otherOutput).not.toContain("--fee-payer-keypair");
|
|
294
|
+
expect(otherOutput).not.toContain("--signer-keypair");
|
|
295
|
+
expect(errorOutput).toBe("");
|
|
44
296
|
});
|
|
45
|
-
test("
|
|
46
|
-
initCliCmd.exitOverride();
|
|
297
|
+
test("new-version validation rejects ambiguous APK sources", function() {
|
|
47
298
|
expect(function() {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
299
|
+
return validateNewVersionArgs({
|
|
300
|
+
apkFile: createTempApkFile(),
|
|
301
|
+
apkUrl: "https://example.com/app.apk",
|
|
302
|
+
whatsNew: "Fixes",
|
|
303
|
+
keypair: "/tmp/signer.json"
|
|
304
|
+
});
|
|
305
|
+
}).toThrow("exactly one of `--apk-file` or `--apk-url`");
|
|
55
306
|
});
|
|
56
|
-
test("
|
|
57
|
-
createCliCmd.exitOverride();
|
|
307
|
+
test("new-version validation rejects missing APK source", function() {
|
|
58
308
|
expect(function() {
|
|
59
|
-
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
|
|
63
|
-
}).toThrow(
|
|
64
|
-
expect(errorOutput).toEqual(createHelp);
|
|
309
|
+
return validateNewVersionArgs({
|
|
310
|
+
whatsNew: "Fixes",
|
|
311
|
+
keypair: "/tmp/signer.json"
|
|
312
|
+
});
|
|
313
|
+
}).toThrow("exactly one of `--apk-file` or `--apk-url`");
|
|
65
314
|
});
|
|
66
|
-
test("
|
|
67
|
-
createAppCliCmd.exitOverride();
|
|
315
|
+
test("new-version validation rejects blank release notes", function() {
|
|
68
316
|
expect(function() {
|
|
69
|
-
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
|
|
74
|
-
}).toThrow(
|
|
317
|
+
return validateNewVersionArgs({
|
|
318
|
+
apkUrl: "https://example.com/app.apk",
|
|
319
|
+
whatsNew: " ",
|
|
320
|
+
keypair: "/tmp/signer.json"
|
|
321
|
+
});
|
|
322
|
+
}).toThrow("`--whats-new` is required.");
|
|
75
323
|
});
|
|
76
|
-
test("
|
|
77
|
-
createAppCliCmd.exitOverride();
|
|
324
|
+
test("new-version validation rejects missing keypair", function() {
|
|
78
325
|
expect(function() {
|
|
79
|
-
|
|
80
|
-
"
|
|
81
|
-
"
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
]);
|
|
85
|
-
}).toThrow(outputHelpReference);
|
|
86
|
-
expect(otherOutput).toEqual(createAppHelp);
|
|
326
|
+
return validateNewVersionArgs({
|
|
327
|
+
apkUrl: "https://example.com/app.apk",
|
|
328
|
+
whatsNew: "Fixes"
|
|
329
|
+
});
|
|
330
|
+
}).toThrow("`--keypair` is required.");
|
|
87
331
|
});
|
|
88
|
-
test("
|
|
89
|
-
createReleaseCliCmd.exitOverride();
|
|
332
|
+
test("new-version validation rejects a missing local APK file", function() {
|
|
90
333
|
expect(function() {
|
|
91
|
-
|
|
92
|
-
"
|
|
93
|
-
"
|
|
94
|
-
"
|
|
95
|
-
|
|
96
|
-
}).toThrow(
|
|
334
|
+
return validateNewVersionArgs({
|
|
335
|
+
apkFile: path.join(tempDir, "missing.apk"),
|
|
336
|
+
whatsNew: "Fixes",
|
|
337
|
+
keypair: "/tmp/signer.json"
|
|
338
|
+
});
|
|
339
|
+
}).toThrow("APK file not found");
|
|
97
340
|
});
|
|
98
|
-
test("
|
|
99
|
-
createReleaseCliCmd.exitOverride();
|
|
341
|
+
test("new-version validation rejects non-HTTPS APK URLs", function() {
|
|
100
342
|
expect(function() {
|
|
101
|
-
|
|
102
|
-
"
|
|
103
|
-
"
|
|
104
|
-
"
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
343
|
+
return validateNewVersionArgs({
|
|
344
|
+
apkUrl: "http://example.com/app.apk",
|
|
345
|
+
whatsNew: "Fixes",
|
|
346
|
+
keypair: "/tmp/signer.json"
|
|
347
|
+
});
|
|
348
|
+
}).toThrow("`--apk-url` must use HTTPS.");
|
|
349
|
+
});
|
|
350
|
+
test("new-version validation accepts an existing APK file", function() {
|
|
351
|
+
expect(function() {
|
|
352
|
+
return validateNewVersionArgs({
|
|
353
|
+
apkFile: createTempApkFile(),
|
|
354
|
+
whatsNew: "Fixes",
|
|
355
|
+
keypair: "/tmp/signer.json"
|
|
356
|
+
});
|
|
357
|
+
}).not.toThrow();
|
|
358
|
+
});
|
|
359
|
+
test("new-version validation accepts a single HTTPS APK URL", function() {
|
|
360
|
+
expect(function() {
|
|
361
|
+
return validateNewVersionArgs({
|
|
362
|
+
apkUrl: "https://example.com/app.apk",
|
|
363
|
+
whatsNew: "Fixes",
|
|
364
|
+
keypair: "/tmp/signer.json"
|
|
365
|
+
});
|
|
366
|
+
}).not.toThrow();
|
|
367
|
+
});
|
|
368
|
+
test("resume validation requires a single target", function() {
|
|
369
|
+
expect(function() {
|
|
370
|
+
return validateResumeArgs({
|
|
371
|
+
releaseId: "release-1",
|
|
372
|
+
sessionId: "session-1",
|
|
373
|
+
keypair: "/tmp/signer.json"
|
|
374
|
+
});
|
|
375
|
+
}).toThrow("exactly one of `--release-id` or `--session-id`");
|
|
376
|
+
});
|
|
377
|
+
test("resume validation accepts alias flags", function() {
|
|
378
|
+
expect(function() {
|
|
379
|
+
return validateResumeArgs({
|
|
380
|
+
resumeRelease: "release-1",
|
|
381
|
+
keypair: "/tmp/signer.json"
|
|
382
|
+
});
|
|
383
|
+
}).not.toThrow();
|
|
384
|
+
expect(function() {
|
|
385
|
+
return validateResumeArgs({
|
|
386
|
+
resumeSession: "session-1",
|
|
387
|
+
keypair: "/tmp/signer.json"
|
|
388
|
+
});
|
|
389
|
+
}).not.toThrow();
|
|
390
|
+
});
|
|
391
|
+
test("resume validation rejects conflicting release aliases", function() {
|
|
392
|
+
expect(function() {
|
|
393
|
+
return validateResumeArgs({
|
|
394
|
+
releaseId: "release-1",
|
|
395
|
+
resumeRelease: "release-2",
|
|
396
|
+
keypair: "/tmp/signer.json"
|
|
397
|
+
});
|
|
398
|
+
}).toThrow("Conflicting values were provided for --release-id and --resume-release.");
|
|
399
|
+
});
|
|
400
|
+
test("resume validation accepts a release id", function() {
|
|
401
|
+
expect(function() {
|
|
402
|
+
return validateResumeArgs({
|
|
403
|
+
releaseId: "release-1",
|
|
404
|
+
keypair: "/tmp/signer.json"
|
|
405
|
+
});
|
|
406
|
+
}).not.toThrow();
|
|
407
|
+
});
|
|
408
|
+
test("portal targets default to production when unset", function() {
|
|
409
|
+
var targets = resolvePortalTargets({});
|
|
410
|
+
expect(targets.apiBaseUrl).toBe("".concat(DEFAULT_PRODUCTION_PORTAL_URL, "/api"));
|
|
411
|
+
});
|
|
412
|
+
test("portal targets default to localhost in local-dev mode", function() {
|
|
413
|
+
var targets = resolvePortalTargets({
|
|
414
|
+
localDev: true
|
|
415
|
+
});
|
|
416
|
+
expect(targets.apiBaseUrl).toBe("".concat(DEFAULT_LOCAL_PORTAL_URL, "/api"));
|
|
417
|
+
});
|
|
418
|
+
test("portal targets derive the API base URL from the configured portal URL", function() {
|
|
419
|
+
var targets = resolvePortalTargets({
|
|
420
|
+
portalUrl: "https://staging.publish.solanamobile.com"
|
|
421
|
+
});
|
|
422
|
+
expect(targets.apiBaseUrl).toBe("https://staging.publish.solanamobile.com/api");
|
|
423
|
+
});
|
|
424
|
+
test("portal targets preserve portal subpaths when deriving /api", function() {
|
|
425
|
+
var targets = resolvePortalTargets({
|
|
426
|
+
portalUrl: "https://portal.example.com/publishing"
|
|
427
|
+
});
|
|
428
|
+
expect(targets.apiBaseUrl).toBe("https://portal.example.com/publishing/api");
|
|
429
|
+
});
|
|
430
|
+
test("portal targets honor DAPP_STORE_PORTAL_URL from the environment", function() {
|
|
431
|
+
process.env.DAPP_STORE_PORTAL_URL = "https://env.publish.solanamobile.com";
|
|
432
|
+
var targets = resolvePortalTargets({});
|
|
433
|
+
expect(targets.apiBaseUrl).toBe("https://env.publish.solanamobile.com/api");
|
|
434
|
+
});
|
|
435
|
+
test("portal targets ignore removed legacy portal env vars", function() {
|
|
436
|
+
process.env.DAPP_STORE_PORTAL_WEB_URL = "https://legacy-web.publish.solanamobile.com";
|
|
437
|
+
process.env.DAPP_STORE_PORTAL_API_BASE_URL = "https://legacy.publish.solanamobile.com/root/api";
|
|
438
|
+
var targets = resolvePortalTargets({});
|
|
439
|
+
expect(targets.apiBaseUrl).toBe("".concat(DEFAULT_PRODUCTION_PORTAL_URL, "/api"));
|
|
440
|
+
});
|
|
441
|
+
test("local-dev mode rejects non-local portal URLs", function() {
|
|
442
|
+
expect(function() {
|
|
443
|
+
return resolvePortalTargets({
|
|
444
|
+
localDev: true,
|
|
445
|
+
portalUrl: "https://portal.example.com"
|
|
446
|
+
});
|
|
447
|
+
}).toThrow("only allows localhost portal endpoints");
|
|
448
|
+
});
|
|
449
|
+
test("non-local portal endpoints must use HTTPS", function() {
|
|
450
|
+
expect(function() {
|
|
451
|
+
return resolvePortalTargets({
|
|
452
|
+
portalUrl: "http://portal.example.com"
|
|
453
|
+
});
|
|
454
|
+
}).toThrow("Portal endpoints must use HTTPS unless --local-dev is set.");
|
|
455
|
+
});
|
|
456
|
+
test("portal targets honor the configured API key env name", function() {
|
|
457
|
+
expect(DEFAULT_API_KEY_ENV).toBe("DAPP_STORE_API_KEY");
|
|
458
|
+
});
|
|
459
|
+
test("resolveApiKey reads the default API key env var", function() {
|
|
460
|
+
return _async_to_generator(function() {
|
|
461
|
+
return _ts_generator(this, function(_state) {
|
|
462
|
+
switch(_state.label){
|
|
463
|
+
case 0:
|
|
464
|
+
process.env.DAPP_STORE_API_KEY = "portal-secret";
|
|
465
|
+
return [
|
|
466
|
+
4,
|
|
467
|
+
expect(resolveApiKey({})).resolves.toBe("portal-secret")
|
|
468
|
+
];
|
|
469
|
+
case 1:
|
|
470
|
+
_state.sent();
|
|
471
|
+
return [
|
|
472
|
+
2
|
|
473
|
+
];
|
|
474
|
+
}
|
|
475
|
+
});
|
|
476
|
+
})();
|
|
477
|
+
});
|
|
478
|
+
test("resolveApiKey reads a custom API key env var", function() {
|
|
479
|
+
return _async_to_generator(function() {
|
|
480
|
+
return _ts_generator(this, function(_state) {
|
|
481
|
+
switch(_state.label){
|
|
482
|
+
case 0:
|
|
483
|
+
process.env.ALT_DAPP_STORE_API_KEY = "alt-secret";
|
|
484
|
+
return [
|
|
485
|
+
4,
|
|
486
|
+
expect(resolveApiKey({
|
|
487
|
+
apiKeyEnv: "ALT_DAPP_STORE_API_KEY"
|
|
488
|
+
})).resolves.toBe("alt-secret")
|
|
489
|
+
];
|
|
490
|
+
case 1:
|
|
491
|
+
_state.sent();
|
|
492
|
+
return [
|
|
493
|
+
2
|
|
494
|
+
];
|
|
495
|
+
}
|
|
496
|
+
});
|
|
497
|
+
})();
|
|
498
|
+
});
|
|
499
|
+
test("resolveApiKey rejects when no API key is available", function() {
|
|
500
|
+
return _async_to_generator(function() {
|
|
501
|
+
return _ts_generator(this, function(_state) {
|
|
502
|
+
switch(_state.label){
|
|
503
|
+
case 0:
|
|
504
|
+
return [
|
|
505
|
+
4,
|
|
506
|
+
expect(resolveApiKey({})).rejects.toThrow("Portal API key is required.")
|
|
507
|
+
];
|
|
508
|
+
case 1:
|
|
509
|
+
_state.sent();
|
|
510
|
+
return [
|
|
511
|
+
4,
|
|
512
|
+
expect(resolveApiKey({})).rejects.toThrow(UPDATED_PUBLISHING_CLI_DOCS_URL)
|
|
513
|
+
];
|
|
514
|
+
case 2:
|
|
515
|
+
_state.sent();
|
|
516
|
+
return [
|
|
517
|
+
2
|
|
518
|
+
];
|
|
519
|
+
}
|
|
520
|
+
});
|
|
521
|
+
})();
|
|
522
|
+
});
|
|
523
|
+
test("formatUpdatedCliUsageError converts unknown-option errors into docs guidance", function() {
|
|
524
|
+
expect(formatUpdatedCliUsageError("error: unknown option '-k'")).toContain("Unknown option '-k'.");
|
|
525
|
+
expect(formatUpdatedCliUsageError("error: unknown option '-k'")).toContain(UPDATED_PUBLISHING_CLI_DOCS_URL);
|
|
526
|
+
});
|
|
117
527
|
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { describe, expect, test } from "@jest/globals";
|
|
2
|
+
import { getCommanderUserFacingError, isCommanderLifecycleExit } from "../parseErrors";
|
|
3
|
+
import { UPDATED_PUBLISHING_CLI_DOCS_URL } from "../../publication/cliValidation";
|
|
4
|
+
describe("CLI parse error handling", function() {
|
|
5
|
+
test("legacy parse errors point to the updated publishing docs", function() {
|
|
6
|
+
var error = getCommanderUserFacingError({
|
|
7
|
+
code: "commander.unknownOption",
|
|
8
|
+
exitCode: 1,
|
|
9
|
+
message: "error: unknown option '-k'",
|
|
10
|
+
name: "CommanderError"
|
|
11
|
+
});
|
|
12
|
+
expect(error).not.toBeNull();
|
|
13
|
+
expect(error === null || error === void 0 ? void 0 : error.exitCode).toBe(1);
|
|
14
|
+
expect(error === null || error === void 0 ? void 0 : error.message).toContain("Unknown option '-k'.");
|
|
15
|
+
expect(error === null || error === void 0 ? void 0 : error.message).toContain(UPDATED_PUBLISHING_CLI_DOCS_URL);
|
|
16
|
+
});
|
|
17
|
+
test("help exits are ignored", function() {
|
|
18
|
+
expect(isCommanderLifecycleExit({
|
|
19
|
+
code: "commander.helpDisplayed",
|
|
20
|
+
exitCode: 0,
|
|
21
|
+
message: "(outputHelp)",
|
|
22
|
+
name: "CommanderError"
|
|
23
|
+
})).toBe(true);
|
|
24
|
+
});
|
|
25
|
+
});
|