firebase-tools 9.18.0 → 9.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +3 -6
- package/lib/api.js +3 -0
- package/lib/apiv2.js +8 -5
- package/lib/command.js +1 -1
- package/lib/commands/crashlytics-symbols-upload.js +146 -0
- package/lib/commands/deploy.js +9 -1
- package/lib/commands/ext-configure.js +9 -2
- package/lib/commands/ext-dev-deprecate.js +63 -0
- package/lib/commands/ext-dev-extension-delete.js +2 -1
- package/lib/commands/ext-dev-publish.js +10 -4
- package/lib/commands/ext-dev-undeprecate.js +56 -0
- package/lib/commands/ext-dev-unpublish.js +12 -4
- package/lib/commands/ext-export.js +44 -0
- package/lib/commands/ext-install.js +50 -13
- package/lib/commands/ext-uninstall.js +6 -0
- package/lib/commands/ext-update.js +60 -18
- package/lib/commands/functions-config-export.js +115 -0
- package/lib/commands/functions-delete.js +47 -25
- package/lib/commands/functions-list.js +12 -12
- package/lib/commands/index.js +9 -0
- package/lib/commands/init.js +3 -0
- package/lib/config.js +3 -2
- package/lib/deploy/extensions/args.js +2 -0
- package/lib/deploy/extensions/deploy.js +49 -0
- package/lib/deploy/extensions/deploymentSummary.js +52 -0
- package/lib/deploy/extensions/errors.js +31 -0
- package/lib/deploy/extensions/index.js +8 -0
- package/lib/deploy/extensions/planner.js +95 -0
- package/lib/deploy/extensions/prepare.js +103 -0
- package/lib/deploy/extensions/release.js +43 -0
- package/lib/deploy/extensions/secrets.js +150 -0
- package/lib/deploy/extensions/tasks.js +98 -0
- package/lib/deploy/extensions/validate.js +17 -0
- package/lib/deploy/functions/backend.js +93 -115
- package/lib/deploy/functions/checkIam.js +8 -8
- package/lib/deploy/functions/containerCleaner.js +82 -22
- package/lib/deploy/functions/deploy.js +4 -10
- package/lib/deploy/functions/functionsDeployHelper.js +3 -68
- package/lib/deploy/functions/prepare.js +62 -27
- package/lib/deploy/functions/pricing.js +17 -17
- package/lib/deploy/functions/prompts.js +22 -21
- package/lib/deploy/functions/release/executor.js +39 -0
- package/lib/deploy/functions/release/fabricator.js +422 -0
- package/lib/deploy/functions/release/index.js +73 -0
- package/lib/deploy/functions/release/planner.js +162 -0
- package/lib/deploy/functions/release/reporter.js +165 -0
- package/lib/deploy/functions/release/sourceTokenScraper.js +28 -0
- package/lib/deploy/functions/release/timer.js +14 -0
- package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +129 -126
- package/lib/deploy/functions/runtimes/node/parseTriggers.js +41 -45
- package/lib/deploy/functions/triggerRegionHelper.js +40 -0
- package/lib/deploy/functions/validate.js +1 -24
- package/lib/deploy/index.js +1 -0
- package/lib/downloadUtils.js +37 -0
- package/lib/emulator/auth/apiSpec.js +1788 -403
- package/lib/emulator/auth/handlers.js +6 -5
- package/lib/emulator/auth/operations.js +439 -40
- package/lib/emulator/auth/server.js +32 -11
- package/lib/emulator/auth/state.js +205 -5
- package/lib/emulator/auth/widget_ui.js +2 -2
- package/lib/emulator/download.js +2 -31
- package/lib/emulator/downloadableEmulators.js +7 -7
- package/lib/emulator/emulatorLogger.js +0 -3
- package/lib/emulator/events/types.js +16 -0
- package/lib/emulator/functionsEmulator.js +120 -21
- package/lib/emulator/functionsEmulatorRuntime.js +46 -121
- package/lib/emulator/functionsEmulatorShared.js +51 -7
- package/lib/emulator/functionsEmulatorShell.js +1 -1
- package/lib/emulator/pubsubEmulator.js +61 -40
- package/lib/emulator/storage/cloudFunctions.js +37 -7
- package/lib/extensions/askUserForConsent.js +14 -1
- package/lib/extensions/askUserForParam.js +81 -4
- package/lib/extensions/billingMigrationHelper.js +1 -11
- package/lib/extensions/changelog.js +2 -1
- package/lib/extensions/checkProjectBilling.js +7 -7
- package/lib/extensions/displayExtensionInfo.js +35 -33
- package/lib/extensions/emulator/optionsHelper.js +3 -3
- package/lib/extensions/emulator/triggerHelper.js +2 -32
- package/lib/extensions/export.js +107 -0
- package/lib/extensions/extensionsApi.js +149 -97
- package/lib/extensions/extensionsHelper.js +36 -32
- package/lib/extensions/listExtensions.js +16 -11
- package/lib/extensions/paramHelper.js +73 -40
- package/lib/extensions/provisioningHelper.js +16 -3
- package/lib/extensions/refs.js +67 -0
- package/lib/extensions/secretsUtils.js +59 -0
- package/lib/extensions/updateHelper.js +33 -47
- package/lib/extensions/versionHelper.js +14 -0
- package/lib/extensions/warnings.js +33 -1
- package/lib/functional.js +64 -0
- package/lib/functions/env.js +26 -13
- package/lib/functions/runtimeConfigExport.js +137 -0
- package/lib/gcp/artifactregistry.js +16 -0
- package/lib/gcp/cloudfunctions.js +65 -35
- package/lib/gcp/cloudfunctionsv2.js +56 -43
- package/lib/gcp/cloudscheduler.js +22 -16
- package/lib/gcp/cloudtasks.js +143 -0
- package/lib/gcp/docker.js +7 -1
- package/lib/gcp/proto.js +2 -2
- package/lib/gcp/pubsub.js +1 -9
- package/lib/gcp/secretManager.js +132 -0
- package/lib/gcp/storage.js +16 -0
- package/lib/previews.js +1 -1
- package/lib/requireInteractive.js +12 -0
- package/lib/utils.js +30 -1
- package/package.json +6 -4
- package/schema/firebase-config.json +9 -0
- package/lib/deploy/functions/deploymentPlanner.js +0 -113
- package/lib/deploy/functions/deploymentTimer.js +0 -23
- package/lib/deploy/functions/errorHandler.js +0 -75
- package/lib/deploy/functions/release.js +0 -116
- package/lib/deploy/functions/tasks.js +0 -324
- package/lib/functions/listFunctions.js +0 -10
- package/lib/functionsDelete.js +0 -60
package/lib/config.js
CHANGED
|
@@ -144,10 +144,10 @@ class Config {
|
|
|
144
144
|
fs.ensureFileSync(this.path(p));
|
|
145
145
|
fs.writeFileSync(this.path(p), content, "utf8");
|
|
146
146
|
}
|
|
147
|
-
askWriteProjectFile(p, content) {
|
|
147
|
+
askWriteProjectFile(p, content, force) {
|
|
148
148
|
const writeTo = this.path(p);
|
|
149
149
|
let next;
|
|
150
|
-
if (fsutils.fileExistsSync(writeTo)) {
|
|
150
|
+
if (fsutils.fileExistsSync(writeTo) && !force) {
|
|
151
151
|
next = prompt_1.promptOnce({
|
|
152
152
|
type: "confirm",
|
|
153
153
|
message: "File " + clc.underline(p) + " already exists. Overwrite?",
|
|
@@ -203,6 +203,7 @@ Config.FILENAME = "firebase.json";
|
|
|
203
203
|
Config.MATERIALIZE_TARGETS = [
|
|
204
204
|
"database",
|
|
205
205
|
"emulators",
|
|
206
|
+
"extensions",
|
|
206
207
|
"firestore",
|
|
207
208
|
"functions",
|
|
208
209
|
"hosting",
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.deploy = void 0;
|
|
4
|
+
const tasks = require("./tasks");
|
|
5
|
+
const queue_1 = require("../../throttler/queue");
|
|
6
|
+
const error_1 = require("../../error");
|
|
7
|
+
const errors_1 = require("./errors");
|
|
8
|
+
const projectUtils_1 = require("../../projectUtils");
|
|
9
|
+
const provisioningHelper_1 = require("../../extensions/provisioningHelper");
|
|
10
|
+
const secrets_1 = require("./secrets");
|
|
11
|
+
const validate_1 = require("./validate");
|
|
12
|
+
async function deploy(context, options, payload) {
|
|
13
|
+
var _a, _b, _c, _d, _e, _f;
|
|
14
|
+
const projectId = projectUtils_1.needProjectId(options);
|
|
15
|
+
await validate_1.checkBilling(projectId, options.nonInteractive);
|
|
16
|
+
await provisioningHelper_1.bulkCheckProductsProvisioned(projectId, [
|
|
17
|
+
...((_a = payload.instancesToCreate) !== null && _a !== void 0 ? _a : []),
|
|
18
|
+
...((_b = payload.instancesToUpdate) !== null && _b !== void 0 ? _b : []),
|
|
19
|
+
...((_c = payload.instancesToConfigure) !== null && _c !== void 0 ? _c : []),
|
|
20
|
+
]);
|
|
21
|
+
await secrets_1.handleSecretParams(payload, context.have, options.nonInteractive);
|
|
22
|
+
const errorHandler = new errors_1.ErrorHandler();
|
|
23
|
+
const validationQueue = new queue_1.default({
|
|
24
|
+
retries: 5,
|
|
25
|
+
concurrency: 5,
|
|
26
|
+
handler: tasks.extensionsDeploymentHandler(errorHandler),
|
|
27
|
+
});
|
|
28
|
+
for (const create of (_d = payload.instancesToCreate) !== null && _d !== void 0 ? _d : []) {
|
|
29
|
+
const task = tasks.createExtensionInstanceTask(projectId, create, true);
|
|
30
|
+
void validationQueue.run(task);
|
|
31
|
+
}
|
|
32
|
+
for (const update of (_e = payload.instancesToUpdate) !== null && _e !== void 0 ? _e : []) {
|
|
33
|
+
const task = tasks.updateExtensionInstanceTask(projectId, update, true);
|
|
34
|
+
void validationQueue.run(task);
|
|
35
|
+
}
|
|
36
|
+
for (const configure of (_f = payload.instancesToConfigure) !== null && _f !== void 0 ? _f : []) {
|
|
37
|
+
const task = tasks.configureExtensionInstanceTask(projectId, configure, true);
|
|
38
|
+
void validationQueue.run(task);
|
|
39
|
+
}
|
|
40
|
+
const validationPromise = validationQueue.wait();
|
|
41
|
+
validationQueue.process();
|
|
42
|
+
validationQueue.close();
|
|
43
|
+
await validationPromise;
|
|
44
|
+
if (errorHandler.hasErrors()) {
|
|
45
|
+
errorHandler.print();
|
|
46
|
+
throw new error_1.FirebaseError(`Extensions deployment failed validation. No changes have been made to the Extension instances on ${projectId}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
exports.deploy = deploy;
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.deletesSummary = exports.configuresSummary = exports.updatesSummary = exports.createsSummary = exports.humanReadable = void 0;
|
|
4
|
+
const clc = require("cli-color");
|
|
5
|
+
const refs = require("../../extensions/refs");
|
|
6
|
+
exports.humanReadable = (dep) => `${clc.bold(dep.instanceId)} (${dep.ref ? `${refs.toExtensionVersionRef(dep.ref)}` : `Installed from local source`})`;
|
|
7
|
+
const humanReadableUpdate = (from, to) => {
|
|
8
|
+
var _a, _b, _c, _d, _e;
|
|
9
|
+
if (((_a = from.ref) === null || _a === void 0 ? void 0 : _a.publisherId) == ((_b = to.ref) === null || _b === void 0 ? void 0 : _b.publisherId) &&
|
|
10
|
+
((_c = from.ref) === null || _c === void 0 ? void 0 : _c.extensionId) == ((_d = to.ref) === null || _d === void 0 ? void 0 : _d.extensionId)) {
|
|
11
|
+
return `\t${clc.bold(from.instanceId)} (${refs.toExtensionVersionRef(from.ref)} => ${(_e = to.ref) === null || _e === void 0 ? void 0 : _e.version})`;
|
|
12
|
+
}
|
|
13
|
+
else {
|
|
14
|
+
const fromRef = from.ref
|
|
15
|
+
? `${refs.toExtensionVersionRef(from.ref)}`
|
|
16
|
+
: `Installed from local source`;
|
|
17
|
+
return `\t${clc.bold(from.instanceId)} (${fromRef} => ${refs.toExtensionVersionRef(to.ref)})`;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
function createsSummary(toCreate) {
|
|
21
|
+
const instancesToCreate = toCreate.map((s) => `\t${exports.humanReadable(s)}`).join("\n");
|
|
22
|
+
return toCreate.length
|
|
23
|
+
? `The following extension instances will be created:\n${instancesToCreate}\n`
|
|
24
|
+
: "";
|
|
25
|
+
}
|
|
26
|
+
exports.createsSummary = createsSummary;
|
|
27
|
+
function updatesSummary(toUpdate, have) {
|
|
28
|
+
const instancesToUpdate = toUpdate
|
|
29
|
+
.map((to) => {
|
|
30
|
+
const from = have.find((exists) => exists.instanceId == to.instanceId);
|
|
31
|
+
return humanReadableUpdate(from, to);
|
|
32
|
+
})
|
|
33
|
+
.join("\n");
|
|
34
|
+
return toUpdate.length
|
|
35
|
+
? `The following extension instances will be updated:\n${instancesToUpdate}\n`
|
|
36
|
+
: "";
|
|
37
|
+
}
|
|
38
|
+
exports.updatesSummary = updatesSummary;
|
|
39
|
+
function configuresSummary(toConfigure) {
|
|
40
|
+
const instancesToConfigure = toConfigure.map((s) => `\t${exports.humanReadable(s)}`).join("\n");
|
|
41
|
+
return toConfigure.length
|
|
42
|
+
? `The following extension instances will be configured:\n${instancesToConfigure}\n`
|
|
43
|
+
: "";
|
|
44
|
+
}
|
|
45
|
+
exports.configuresSummary = configuresSummary;
|
|
46
|
+
function deletesSummary(toDelete) {
|
|
47
|
+
const instancesToDelete = toDelete.map((s) => `\t${exports.humanReadable(s)}`).join("\n");
|
|
48
|
+
return toDelete.length
|
|
49
|
+
? `The following extension instances are not listed in 'firebase.json':\n${instancesToDelete}\n`
|
|
50
|
+
: "";
|
|
51
|
+
}
|
|
52
|
+
exports.deletesSummary = deletesSummary;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ErrorHandler = void 0;
|
|
4
|
+
const clc = require("cli-color");
|
|
5
|
+
const logger_1 = require("../../logger");
|
|
6
|
+
class ErrorHandler {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.errors = [];
|
|
9
|
+
}
|
|
10
|
+
record(instanceId, type, message) {
|
|
11
|
+
this.errors.push({
|
|
12
|
+
instanceId,
|
|
13
|
+
type,
|
|
14
|
+
message: message,
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
print() {
|
|
18
|
+
logger_1.logger.info("");
|
|
19
|
+
logger_1.logger.info("Extensions deploy had errors:");
|
|
20
|
+
logger_1.logger.info("");
|
|
21
|
+
for (const err of this.errors) {
|
|
22
|
+
logger_1.logger.info(`- ${err.type} ${clc.bold(err.instanceId)}`);
|
|
23
|
+
logger_1.logger.info(err.message);
|
|
24
|
+
logger_1.logger.info("");
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
hasErrors() {
|
|
28
|
+
return this.errors.length > 0;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
exports.ErrorHandler = ErrorHandler;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
var prepare_1 = require("./prepare");
|
|
4
|
+
Object.defineProperty(exports, "prepare", { enumerable: true, get: function () { return prepare_1.prepare; } });
|
|
5
|
+
var deploy_1 = require("./deploy");
|
|
6
|
+
Object.defineProperty(exports, "deploy", { enumerable: true, get: function () { return deploy_1.deploy; } });
|
|
7
|
+
var release_1 = require("./release");
|
|
8
|
+
Object.defineProperty(exports, "release", { enumerable: true, get: function () { return release_1.release; } });
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveVersion = exports.want = exports.have = exports.getExtension = exports.getExtensionVersion = void 0;
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const semver = require("semver");
|
|
6
|
+
const error_1 = require("../../error");
|
|
7
|
+
const extensionsApi = require("../../extensions/extensionsApi");
|
|
8
|
+
const extensionsHelper_1 = require("../../extensions/extensionsHelper");
|
|
9
|
+
const refs = require("../../extensions/refs");
|
|
10
|
+
const paramHelper_1 = require("../../extensions/paramHelper");
|
|
11
|
+
const logger_1 = require("../../logger");
|
|
12
|
+
async function getExtensionVersion(i) {
|
|
13
|
+
if (!i.extensionVersion) {
|
|
14
|
+
if (!i.ref) {
|
|
15
|
+
throw new error_1.FirebaseError(`Can't get ExtensionVersion for ${i.instanceId} because it has no ref`);
|
|
16
|
+
}
|
|
17
|
+
i.extensionVersion = await extensionsApi.getExtensionVersion(refs.toExtensionVersionRef(i.ref));
|
|
18
|
+
}
|
|
19
|
+
return i.extensionVersion;
|
|
20
|
+
}
|
|
21
|
+
exports.getExtensionVersion = getExtensionVersion;
|
|
22
|
+
async function getExtension(i) {
|
|
23
|
+
if (!i.ref) {
|
|
24
|
+
throw new error_1.FirebaseError(`Can't get Extensionfor ${i.instanceId} because it has no ref`);
|
|
25
|
+
}
|
|
26
|
+
if (!i.extension) {
|
|
27
|
+
i.extension = await extensionsApi.getExtension(refs.toExtensionRef(i.ref));
|
|
28
|
+
}
|
|
29
|
+
return i.extension;
|
|
30
|
+
}
|
|
31
|
+
exports.getExtension = getExtension;
|
|
32
|
+
const ENV_DIRECTORY = "extensions";
|
|
33
|
+
async function have(projectId) {
|
|
34
|
+
const instances = await extensionsApi.listInstances(projectId);
|
|
35
|
+
return instances.map((i) => {
|
|
36
|
+
const dep = {
|
|
37
|
+
instanceId: i.name.split("/").pop(),
|
|
38
|
+
params: i.config.params,
|
|
39
|
+
};
|
|
40
|
+
if (i.config.extensionRef) {
|
|
41
|
+
const ref = refs.parse(i.config.extensionRef);
|
|
42
|
+
dep.ref = ref;
|
|
43
|
+
dep.ref.version = i.config.extensionVersion;
|
|
44
|
+
}
|
|
45
|
+
return dep;
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
exports.have = have;
|
|
49
|
+
async function want(projectId, projectDir, extensions) {
|
|
50
|
+
const instanceSpecs = [];
|
|
51
|
+
const errors = [];
|
|
52
|
+
for (const e of Object.entries(extensions)) {
|
|
53
|
+
try {
|
|
54
|
+
const instanceId = e[0];
|
|
55
|
+
const ref = refs.parse(e[1]);
|
|
56
|
+
ref.version = await resolveVersion(ref);
|
|
57
|
+
const params = readParams(projectDir, instanceId);
|
|
58
|
+
const autoPopulatedParams = await extensionsHelper_1.getFirebaseProjectParams(projectId);
|
|
59
|
+
const subbedParams = extensionsHelper_1.substituteParams(params, autoPopulatedParams);
|
|
60
|
+
instanceSpecs.push({
|
|
61
|
+
instanceId,
|
|
62
|
+
ref,
|
|
63
|
+
params: subbedParams,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
logger_1.logger.debug(`Got error reading extensions entry ${e}: ${err}`);
|
|
68
|
+
errors.push(err);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (errors.length) {
|
|
72
|
+
const messages = errors.map((err) => `- ${err.message}`).join("\n");
|
|
73
|
+
throw new error_1.FirebaseError(`Errors while reading 'extensions' in 'firebase.json'\n${messages}`);
|
|
74
|
+
}
|
|
75
|
+
return instanceSpecs;
|
|
76
|
+
}
|
|
77
|
+
exports.want = want;
|
|
78
|
+
async function resolveVersion(ref) {
|
|
79
|
+
if (!ref.version || ref.version == "latest") {
|
|
80
|
+
return "latest";
|
|
81
|
+
}
|
|
82
|
+
const extensionRef = refs.toExtensionRef(ref);
|
|
83
|
+
const versions = await extensionsApi.listExtensionVersions(extensionRef);
|
|
84
|
+
const maxSatisfying = semver.maxSatisfying(versions.map((ev) => ev.spec.version), ref.version);
|
|
85
|
+
if (!maxSatisfying) {
|
|
86
|
+
throw new error_1.FirebaseError(`No version of ${extensionRef} matches requested version ${ref.version}`);
|
|
87
|
+
}
|
|
88
|
+
return maxSatisfying;
|
|
89
|
+
}
|
|
90
|
+
exports.resolveVersion = resolveVersion;
|
|
91
|
+
function readParams(projectDir, instanceId) {
|
|
92
|
+
const paramPath = path.join(projectDir, ENV_DIRECTORY, `${instanceId}.env`);
|
|
93
|
+
const params = paramHelper_1.readEnvFile(paramPath);
|
|
94
|
+
return params;
|
|
95
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.prepare = void 0;
|
|
4
|
+
const planner = require("./planner");
|
|
5
|
+
const deploymentSummary = require("./deploymentSummary");
|
|
6
|
+
const prompt = require("../../prompt");
|
|
7
|
+
const refs = require("../../extensions/refs");
|
|
8
|
+
const projectUtils_1 = require("../../projectUtils");
|
|
9
|
+
const logger_1 = require("../../logger");
|
|
10
|
+
const error_1 = require("../../error");
|
|
11
|
+
const requirePermissions_1 = require("../../requirePermissions");
|
|
12
|
+
const extensionsHelper_1 = require("../../extensions/extensionsHelper");
|
|
13
|
+
const secretsUtils_1 = require("../../extensions/secretsUtils");
|
|
14
|
+
const secrets_1 = require("./secrets");
|
|
15
|
+
const warnings_1 = require("../../extensions/warnings");
|
|
16
|
+
async function prepare(context, options, payload) {
|
|
17
|
+
var _a;
|
|
18
|
+
const projectId = projectUtils_1.needProjectId(options);
|
|
19
|
+
await extensionsHelper_1.ensureExtensionsApiEnabled(options);
|
|
20
|
+
await requirePermissions_1.requirePermissions(options, ["firebaseextensions.instances.list"]);
|
|
21
|
+
context.have = await planner.have(projectId);
|
|
22
|
+
context.want = await planner.want(projectId, options.config.projectDir, options.config.get("extensions"));
|
|
23
|
+
const usingSecrets = await Promise.all((_a = context.have) === null || _a === void 0 ? void 0 : _a.map(secrets_1.checkSpecForSecrets));
|
|
24
|
+
if (usingSecrets.some((i) => i)) {
|
|
25
|
+
await secretsUtils_1.ensureSecretManagerApiEnabled(options);
|
|
26
|
+
}
|
|
27
|
+
payload.instancesToCreate = context.want.filter((i) => { var _a; return !((_a = context.have) === null || _a === void 0 ? void 0 : _a.some(matchesInstanceId(i))); });
|
|
28
|
+
payload.instancesToConfigure = context.want.filter((i) => { var _a; return (_a = context.have) === null || _a === void 0 ? void 0 : _a.some(isConfigure(i)); });
|
|
29
|
+
payload.instancesToUpdate = context.want.filter((i) => { var _a; return (_a = context.have) === null || _a === void 0 ? void 0 : _a.some(isUpdate(i)); });
|
|
30
|
+
payload.instancesToDelete = context.have.filter((i) => { var _a; return !((_a = context.want) === null || _a === void 0 ? void 0 : _a.some(matchesInstanceId(i))); });
|
|
31
|
+
if (await warnings_1.displayWarningsForDeploy(payload.instancesToCreate)) {
|
|
32
|
+
if (!options.force && options.nonInteractive) {
|
|
33
|
+
throw new error_1.FirebaseError("Pass the --force flag to acknowledge these terms in non-interactive mode");
|
|
34
|
+
}
|
|
35
|
+
else if (!options.force &&
|
|
36
|
+
!options.nonInteractive &&
|
|
37
|
+
!(await prompt.promptOnce({
|
|
38
|
+
type: "confirm",
|
|
39
|
+
message: `Do you wish to continue deploying these extensions?`,
|
|
40
|
+
default: true,
|
|
41
|
+
}))) {
|
|
42
|
+
throw new error_1.FirebaseError("Deployment cancelled");
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (await warnings_1.displayWarningsForDeploy(payload.instancesToCreate)) {
|
|
46
|
+
if (!options.force && options.nonInteractive) {
|
|
47
|
+
throw new error_1.FirebaseError("Pass the --force flag to acknowledge these terms in non-interactive mode");
|
|
48
|
+
}
|
|
49
|
+
else if (!options.force &&
|
|
50
|
+
!options.nonInteractive &&
|
|
51
|
+
!(await prompt.promptOnce({
|
|
52
|
+
type: "confirm",
|
|
53
|
+
message: `Do you wish to continue deploying these extensions?`,
|
|
54
|
+
default: true,
|
|
55
|
+
}))) {
|
|
56
|
+
throw new error_1.FirebaseError("Deployment cancelled");
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const permissionsNeeded = [];
|
|
60
|
+
if (payload.instancesToCreate.length) {
|
|
61
|
+
permissionsNeeded.push("firebaseextensions.instances.create");
|
|
62
|
+
logger_1.logger.info(deploymentSummary.createsSummary(payload.instancesToCreate));
|
|
63
|
+
}
|
|
64
|
+
if (payload.instancesToUpdate.length) {
|
|
65
|
+
permissionsNeeded.push("firebaseextensions.instances.update");
|
|
66
|
+
logger_1.logger.info(deploymentSummary.updatesSummary(payload.instancesToUpdate, context.have));
|
|
67
|
+
}
|
|
68
|
+
if (payload.instancesToConfigure.length) {
|
|
69
|
+
permissionsNeeded.push("firebaseextensions.instances.update");
|
|
70
|
+
logger_1.logger.info(deploymentSummary.configuresSummary(payload.instancesToConfigure));
|
|
71
|
+
}
|
|
72
|
+
if (payload.instancesToDelete.length) {
|
|
73
|
+
logger_1.logger.info(deploymentSummary.deletesSummary(payload.instancesToDelete));
|
|
74
|
+
if (!options.force && options.nonInteractive) {
|
|
75
|
+
throw new error_1.FirebaseError("Pass the --force flag to use this command in non-interactive mode");
|
|
76
|
+
}
|
|
77
|
+
else if (!options.force &&
|
|
78
|
+
!options.nonInteractive &&
|
|
79
|
+
!(await prompt.promptOnce({
|
|
80
|
+
type: "confirm",
|
|
81
|
+
message: `Would you like to delete ${payload.instancesToDelete
|
|
82
|
+
.map((i) => i.instanceId)
|
|
83
|
+
.join(", ")}?`,
|
|
84
|
+
default: false,
|
|
85
|
+
}))) {
|
|
86
|
+
payload.instancesToDelete = [];
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
permissionsNeeded.push("firebaseextensions.instances.delete");
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
await requirePermissions_1.requirePermissions(options, permissionsNeeded);
|
|
93
|
+
}
|
|
94
|
+
exports.prepare = prepare;
|
|
95
|
+
const matchesInstanceId = (dep) => (test) => {
|
|
96
|
+
return dep.instanceId === test.instanceId;
|
|
97
|
+
};
|
|
98
|
+
const isUpdate = (dep) => (test) => {
|
|
99
|
+
return dep.instanceId === test.instanceId && !refs.equal(dep.ref, test.ref);
|
|
100
|
+
};
|
|
101
|
+
const isConfigure = (dep) => (test) => {
|
|
102
|
+
return dep.instanceId === test.instanceId && refs.equal(dep.ref, test.ref);
|
|
103
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.release = void 0;
|
|
4
|
+
const queue_1 = require("../../throttler/queue");
|
|
5
|
+
const tasks = require("./tasks");
|
|
6
|
+
const error_1 = require("../../error");
|
|
7
|
+
const errors_1 = require("./errors");
|
|
8
|
+
const projectUtils_1 = require("../../projectUtils");
|
|
9
|
+
async function release(context, options, payload) {
|
|
10
|
+
var _a, _b, _c, _d;
|
|
11
|
+
const projectId = projectUtils_1.needProjectId(options);
|
|
12
|
+
const errorHandler = new errors_1.ErrorHandler();
|
|
13
|
+
const deploymentQueue = new queue_1.default({
|
|
14
|
+
retries: 5,
|
|
15
|
+
concurrency: 5,
|
|
16
|
+
handler: tasks.extensionsDeploymentHandler(errorHandler),
|
|
17
|
+
});
|
|
18
|
+
for (const creation of (_a = payload.instancesToCreate) !== null && _a !== void 0 ? _a : []) {
|
|
19
|
+
const task = tasks.createExtensionInstanceTask(projectId, creation);
|
|
20
|
+
void deploymentQueue.run(task);
|
|
21
|
+
}
|
|
22
|
+
for (const update of (_b = payload.instancesToUpdate) !== null && _b !== void 0 ? _b : []) {
|
|
23
|
+
const task = tasks.updateExtensionInstanceTask(projectId, update);
|
|
24
|
+
void deploymentQueue.run(task);
|
|
25
|
+
}
|
|
26
|
+
for (const update of (_c = payload.instancesToConfigure) !== null && _c !== void 0 ? _c : []) {
|
|
27
|
+
const task = tasks.configureExtensionInstanceTask(projectId, update);
|
|
28
|
+
void deploymentQueue.run(task);
|
|
29
|
+
}
|
|
30
|
+
for (const deletion of (_d = payload.instancesToDelete) !== null && _d !== void 0 ? _d : []) {
|
|
31
|
+
const task = tasks.deleteExtensionInstanceTask(projectId, deletion);
|
|
32
|
+
void deploymentQueue.run(task);
|
|
33
|
+
}
|
|
34
|
+
const deploymentPromise = deploymentQueue.wait();
|
|
35
|
+
deploymentQueue.process();
|
|
36
|
+
deploymentQueue.close();
|
|
37
|
+
await deploymentPromise;
|
|
38
|
+
if (errorHandler.hasErrors()) {
|
|
39
|
+
errorHandler.print();
|
|
40
|
+
throw new error_1.FirebaseError(`Extensions deployment failed.`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
exports.release = release;
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkSpecForSecrets = exports.handleSecretParams = void 0;
|
|
4
|
+
const clc = require("cli-color");
|
|
5
|
+
const secretUtils = require("../../extensions/secretsUtils");
|
|
6
|
+
const secretManager = require("../../gcp/secretManager");
|
|
7
|
+
const planner_1 = require("./planner");
|
|
8
|
+
const askUserForParam_1 = require("../../extensions/askUserForParam");
|
|
9
|
+
const extensionsApi_1 = require("../../extensions/extensionsApi");
|
|
10
|
+
const error_1 = require("../../error");
|
|
11
|
+
const logger_1 = require("../../logger");
|
|
12
|
+
const utils_1 = require("../../utils");
|
|
13
|
+
async function handleSecretParams(payload, have, nonInteractive) {
|
|
14
|
+
var _a, _b, _c;
|
|
15
|
+
for (const i of (_a = payload.instancesToCreate) !== null && _a !== void 0 ? _a : []) {
|
|
16
|
+
if (await checkSpecForSecrets(i)) {
|
|
17
|
+
utils_1.logLabeledBullet("extensions", `Verifying secret params for ${clc.bold(i.instanceId)}`);
|
|
18
|
+
await handleSecretsCreateInstance(i, nonInteractive);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
const updates = [...((_b = payload.instancesToUpdate) !== null && _b !== void 0 ? _b : []), ...((_c = payload.instancesToConfigure) !== null && _c !== void 0 ? _c : [])];
|
|
22
|
+
for (const i of updates) {
|
|
23
|
+
if (await checkSpecForSecrets(i)) {
|
|
24
|
+
utils_1.logLabeledBullet("extensions", `Verifying secret params for ${clc.bold(i.instanceId)}`);
|
|
25
|
+
const previousSpec = have.find((h) => h.instanceId === i.instanceId);
|
|
26
|
+
await handleSecretsUpdateInstance(i, previousSpec, nonInteractive);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
exports.handleSecretParams = handleSecretParams;
|
|
31
|
+
async function checkSpecForSecrets(i) {
|
|
32
|
+
const extensionVersion = await planner_1.getExtensionVersion(i);
|
|
33
|
+
return secretUtils.usesSecrets(extensionVersion.spec);
|
|
34
|
+
}
|
|
35
|
+
exports.checkSpecForSecrets = checkSpecForSecrets;
|
|
36
|
+
const secretsInSpec = (spec) => {
|
|
37
|
+
return spec.params.filter((p) => p.type === extensionsApi_1.ParamType.SECRET);
|
|
38
|
+
};
|
|
39
|
+
async function handleSecretsCreateInstance(i, nonInteractive) {
|
|
40
|
+
const extensionVersion = await planner_1.getExtensionVersion(i);
|
|
41
|
+
const secretParams = secretsInSpec(extensionVersion.spec);
|
|
42
|
+
for (const s of secretParams) {
|
|
43
|
+
await handleSecretParamForCreate(s, i, nonInteractive);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
async function handleSecretsUpdateInstance(i, prevSpec, nonInteractive) {
|
|
47
|
+
const extensionVersion = await planner_1.getExtensionVersion(i);
|
|
48
|
+
const prevExtensionVersion = await planner_1.getExtensionVersion(prevSpec);
|
|
49
|
+
const secretParams = secretsInSpec(extensionVersion.spec);
|
|
50
|
+
for (const s of secretParams) {
|
|
51
|
+
const prevParam = prevExtensionVersion.spec.params.find((p) => p.param === s.param);
|
|
52
|
+
if ((prevParam === null || prevParam === void 0 ? void 0 : prevParam.type) === extensionsApi_1.ParamType.SECRET && prevSpec.params[prevParam === null || prevParam === void 0 ? void 0 : prevParam.param]) {
|
|
53
|
+
await handleSecretParamForUpdate(s, i, prevSpec.params[prevParam === null || prevParam === void 0 ? void 0 : prevParam.param], nonInteractive);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
await handleSecretParamForCreate(s, i, nonInteractive);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async function handleSecretParamForCreate(secretParam, i, nonInteractive) {
|
|
61
|
+
var _a;
|
|
62
|
+
const providedValue = i.params[secretParam.param];
|
|
63
|
+
if (!providedValue) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const [, projectId, , secretName, , version] = providedValue.split("/");
|
|
67
|
+
if (!projectId || !secretName || !version) {
|
|
68
|
+
throw new error_1.FirebaseError(`${clc.bold(i.instanceId)}: Found '${providedValue}' for secret param ${secretParam.param}, but expected a secret version.`);
|
|
69
|
+
}
|
|
70
|
+
const secretInfo = await getSecretInfo(projectId, secretName, version);
|
|
71
|
+
if (!secretInfo.secret) {
|
|
72
|
+
await promptForCreateSecret({
|
|
73
|
+
projectId,
|
|
74
|
+
secretName,
|
|
75
|
+
instanceId: i.instanceId,
|
|
76
|
+
secretParam,
|
|
77
|
+
nonInteractive,
|
|
78
|
+
});
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
else if (!secretInfo.secretVersion) {
|
|
82
|
+
throw new error_1.FirebaseError(`${clc.bold(i.instanceId)}: Found '${providedValue}' for secret param ${secretParam.param}. ` +
|
|
83
|
+
`projects/${projectId}/secrets/${secretName} exists, but version ${version} does not. ` +
|
|
84
|
+
`See more information about this secret at ${secretManager.secretManagerConsoleUri(projectId)}`);
|
|
85
|
+
}
|
|
86
|
+
if (!!((_a = secretInfo === null || secretInfo === void 0 ? void 0 : secretInfo.secret) === null || _a === void 0 ? void 0 : _a.labels) &&
|
|
87
|
+
!!(secretInfo === null || secretInfo === void 0 ? void 0 : secretInfo.secret.labels[secretUtils.SECRET_LABEL]) &&
|
|
88
|
+
secretInfo.secret.labels[secretUtils.SECRET_LABEL] !== i.instanceId) {
|
|
89
|
+
throw new error_1.FirebaseError(`${clc.bold(i.instanceId)}: Found '${providedValue}' for secret param ${secretParam.param}. ` +
|
|
90
|
+
`projects/${projectId}/secrets/${secretName} is managed by a different extension instance (${secretInfo.secret.labels[secretUtils.SECRET_LABEL]}), so reusing it here can lead to unexpected behavior. ` +
|
|
91
|
+
"Please choose a different name for this secret, and rerun this command.");
|
|
92
|
+
}
|
|
93
|
+
await secretUtils.grantFirexServiceAgentSecretAdminRole(secretInfo.secret);
|
|
94
|
+
}
|
|
95
|
+
async function handleSecretParamForUpdate(secretParam, i, prevValue, nonInteractive) {
|
|
96
|
+
const providedValue = i.params[secretParam.param];
|
|
97
|
+
if (!providedValue) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const [, projectId, , secretName, , version] = providedValue.split("/");
|
|
101
|
+
if (!projectId || !secretName || !version) {
|
|
102
|
+
throw new error_1.FirebaseError(`${clc.bold(i.instanceId)}: Found '${providedValue}' for secret param ${secretParam.param}, but expected a secret version.`);
|
|
103
|
+
}
|
|
104
|
+
const [, prevProjectId, , prevSecretName] = prevValue.split("/");
|
|
105
|
+
if (prevSecretName !== secretName) {
|
|
106
|
+
throw new error_1.FirebaseError(`${clc.bold(i.instanceId)}: Found '${providedValue}' for secret param ${secretParam.param}, ` +
|
|
107
|
+
`but this instance was previously using a different secret projects/${prevProjectId}/secrets/${prevSecretName}.\n` +
|
|
108
|
+
`Changing secrets is not supported. If you want to change the value of this secret, ` +
|
|
109
|
+
`use a new version of projects/${prevProjectId}/secrets/${prevSecretName}.` +
|
|
110
|
+
`You can create a new version at ${secretManager.secretManagerConsoleUri(projectId)}`);
|
|
111
|
+
}
|
|
112
|
+
const secretInfo = await getSecretInfo(projectId, secretName, version);
|
|
113
|
+
if (!secretInfo.secret) {
|
|
114
|
+
i.params[secretParam.param] = await promptForCreateSecret({
|
|
115
|
+
projectId,
|
|
116
|
+
secretName,
|
|
117
|
+
instanceId: i.instanceId,
|
|
118
|
+
secretParam,
|
|
119
|
+
nonInteractive,
|
|
120
|
+
});
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
else if (!secretInfo.secretVersion) {
|
|
124
|
+
throw new error_1.FirebaseError(`${clc.bold(i.instanceId)}: Found '${providedValue}' for secret param ${secretParam.param}. ` +
|
|
125
|
+
`projects/${projectId}/secrets/${secretName} exists, but version ${version} does not. ` +
|
|
126
|
+
`See more information about this secret at ${secretManager.secretManagerConsoleUri(projectId)}`);
|
|
127
|
+
}
|
|
128
|
+
i.params[secretParam.param] = secretManager.toSecretVersionResourceName(secretInfo.secretVersion);
|
|
129
|
+
await secretUtils.grantFirexServiceAgentSecretAdminRole(secretInfo.secret);
|
|
130
|
+
}
|
|
131
|
+
async function getSecretInfo(projectId, secretName, version) {
|
|
132
|
+
const secretInfo = {};
|
|
133
|
+
try {
|
|
134
|
+
secretInfo.secret = await secretManager.getSecret(projectId, secretName);
|
|
135
|
+
secretInfo.secretVersion = await secretManager.getSecretVersion(projectId, secretName, version);
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
if (err.status !== 404) {
|
|
139
|
+
throw err;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return secretInfo;
|
|
143
|
+
}
|
|
144
|
+
async function promptForCreateSecret(args) {
|
|
145
|
+
logger_1.logger.info(`${clc.bold(args.instanceId)}: Secret ${args.projectId}/${args.secretName} doesn't exist yet.`);
|
|
146
|
+
if (args.nonInteractive) {
|
|
147
|
+
throw new error_1.FirebaseError(`To create this secret, run this command in interactive mode, or go to ${secretManager.secretManagerConsoleUri(args.projectId)}`);
|
|
148
|
+
}
|
|
149
|
+
return askUserForParam_1.promptCreateSecret(args.projectId, args.instanceId, args.secretParam, args.secretName);
|
|
150
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.deleteExtensionInstanceTask = exports.configureExtensionInstanceTask = exports.updateExtensionInstanceTask = exports.createExtensionInstanceTask = exports.extensionsDeploymentHandler = void 0;
|
|
4
|
+
const clc = require("cli-color");
|
|
5
|
+
const extensionsApi = require("../../extensions/extensionsApi");
|
|
6
|
+
const refs = require("../../extensions/refs");
|
|
7
|
+
const utils = require("../../utils");
|
|
8
|
+
const isRetryable = (err) => err.status == 429 || err.status == 409;
|
|
9
|
+
function extensionsDeploymentHandler(errorHandler) {
|
|
10
|
+
return async (task) => {
|
|
11
|
+
var _a, _b, _c, _d;
|
|
12
|
+
let result;
|
|
13
|
+
try {
|
|
14
|
+
result = await task.run();
|
|
15
|
+
}
|
|
16
|
+
catch (err) {
|
|
17
|
+
if (isRetryable(err)) {
|
|
18
|
+
throw err;
|
|
19
|
+
}
|
|
20
|
+
errorHandler.record(task.spec.instanceId, task.type, (_d = (_c = (_b = (_a = err.context) === null || _a === void 0 ? void 0 : _a.body) === null || _b === void 0 ? void 0 : _b.error) === null || _c === void 0 ? void 0 : _c.message) !== null && _d !== void 0 ? _d : err);
|
|
21
|
+
}
|
|
22
|
+
return result;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
exports.extensionsDeploymentHandler = extensionsDeploymentHandler;
|
|
26
|
+
function createExtensionInstanceTask(projectId, instanceSpec, validateOnly = false) {
|
|
27
|
+
const run = async () => {
|
|
28
|
+
const res = await extensionsApi.createInstance({
|
|
29
|
+
projectId,
|
|
30
|
+
instanceId: instanceSpec.instanceId,
|
|
31
|
+
params: instanceSpec.params,
|
|
32
|
+
extensionVersionRef: refs.toExtensionVersionRef(instanceSpec.ref),
|
|
33
|
+
validateOnly,
|
|
34
|
+
});
|
|
35
|
+
printSuccess(instanceSpec.instanceId, "create", validateOnly);
|
|
36
|
+
return;
|
|
37
|
+
};
|
|
38
|
+
return {
|
|
39
|
+
run,
|
|
40
|
+
spec: instanceSpec,
|
|
41
|
+
type: "create",
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
exports.createExtensionInstanceTask = createExtensionInstanceTask;
|
|
45
|
+
function updateExtensionInstanceTask(projectId, instanceSpec, validateOnly = false) {
|
|
46
|
+
const run = async () => {
|
|
47
|
+
const res = await extensionsApi.updateInstanceFromRegistry({
|
|
48
|
+
projectId,
|
|
49
|
+
instanceId: instanceSpec.instanceId,
|
|
50
|
+
extRef: refs.toExtensionVersionRef(instanceSpec.ref),
|
|
51
|
+
params: instanceSpec.params,
|
|
52
|
+
validateOnly,
|
|
53
|
+
});
|
|
54
|
+
printSuccess(instanceSpec.instanceId, "update", validateOnly);
|
|
55
|
+
return;
|
|
56
|
+
};
|
|
57
|
+
return {
|
|
58
|
+
run,
|
|
59
|
+
spec: instanceSpec,
|
|
60
|
+
type: "update",
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
exports.updateExtensionInstanceTask = updateExtensionInstanceTask;
|
|
64
|
+
function configureExtensionInstanceTask(projectId, instanceSpec, validateOnly = false) {
|
|
65
|
+
const run = async () => {
|
|
66
|
+
const res = await extensionsApi.configureInstance({
|
|
67
|
+
projectId,
|
|
68
|
+
instanceId: instanceSpec.instanceId,
|
|
69
|
+
params: instanceSpec.params,
|
|
70
|
+
validateOnly,
|
|
71
|
+
});
|
|
72
|
+
printSuccess(instanceSpec.instanceId, "configure", validateOnly);
|
|
73
|
+
return;
|
|
74
|
+
};
|
|
75
|
+
return {
|
|
76
|
+
run,
|
|
77
|
+
spec: instanceSpec,
|
|
78
|
+
type: "configure",
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
exports.configureExtensionInstanceTask = configureExtensionInstanceTask;
|
|
82
|
+
function deleteExtensionInstanceTask(projectId, instanceSpec) {
|
|
83
|
+
const run = async () => {
|
|
84
|
+
const res = await extensionsApi.deleteInstance(projectId, instanceSpec.instanceId);
|
|
85
|
+
printSuccess(instanceSpec.instanceId, "delete", false);
|
|
86
|
+
return;
|
|
87
|
+
};
|
|
88
|
+
return {
|
|
89
|
+
run,
|
|
90
|
+
spec: instanceSpec,
|
|
91
|
+
type: "delete",
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
exports.deleteExtensionInstanceTask = deleteExtensionInstanceTask;
|
|
95
|
+
function printSuccess(instanceId, type, validateOnly) {
|
|
96
|
+
const action = validateOnly ? `validated ${type} for` : `${type}d`;
|
|
97
|
+
utils.logSuccess(clc.bold.green("extensions") + ` Successfully ${action} ${instanceId}`);
|
|
98
|
+
}
|