firebase-tools 9.20.0 → 9.23.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/CHANGELOG.md +5 -1
- package/lib/api.js +2 -0
- package/lib/apiv2.js +7 -4
- package/lib/commands/crashlytics-symbols-upload.js +2 -2
- package/lib/commands/deploy.js +9 -1
- package/lib/commands/ext-configure.js +1 -1
- package/lib/commands/ext-dev-deprecate.js +63 -0
- package/lib/commands/ext-dev-undeprecate.js +56 -0
- package/lib/commands/ext-dev-unpublish.js +10 -3
- package/lib/commands/ext-export.js +44 -0
- package/lib/commands/ext-install.js +1 -1
- package/lib/commands/ext-update.js +1 -1
- package/lib/commands/functions-delete.js +55 -42
- package/lib/commands/functions-list.js +11 -11
- package/lib/commands/index.js +6 -5
- 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/params.js +39 -0
- package/lib/deploy/extensions/planner.js +94 -0
- package/lib/deploy/extensions/prepare.js +111 -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 +84 -115
- package/lib/deploy/functions/checkIam.js +73 -12
- package/lib/deploy/functions/containerCleaner.js +97 -50
- package/lib/deploy/functions/deploy.js +4 -10
- package/lib/deploy/functions/eventTypes.js +10 -0
- package/lib/deploy/functions/functionsDeployHelper.js +3 -68
- package/lib/deploy/functions/prepare.js +72 -29
- 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 +425 -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 +32 -54
- package/lib/deploy/functions/services/index.js +38 -0
- package/lib/deploy/functions/services/storage.js +43 -0
- package/lib/deploy/functions/triggerRegionHelper.js +9 -25
- package/lib/deploy/functions/validate.js +1 -24
- package/lib/deploy/index.js +10 -1
- package/lib/emulator/auth/apiSpec.js +37 -6
- package/lib/emulator/auth/operations.js +45 -17
- package/lib/emulator/auth/server.js +16 -2
- package/lib/emulator/auth/state.js +34 -15
- package/lib/emulator/auth/widget_ui.js +14 -0
- package/lib/emulator/downloadableEmulators.js +7 -7
- package/lib/emulator/functionsEmulator.js +18 -4
- package/lib/emulator/storage/cloudFunctions.js +37 -7
- package/lib/ensureApiEnabled.js +10 -12
- package/lib/extensions/askUserForParam.js +14 -6
- package/lib/extensions/checkProjectBilling.js +7 -7
- package/lib/extensions/export.js +107 -0
- package/lib/extensions/extensionsApi.js +103 -21
- package/lib/extensions/extensionsHelper.js +5 -2
- package/lib/extensions/listExtensions.js +16 -11
- package/lib/extensions/paramHelper.js +6 -4
- package/lib/extensions/provisioningHelper.js +16 -3
- package/lib/extensions/refs.js +9 -1
- package/lib/extensions/secretsUtils.js +10 -9
- package/lib/extensions/updateHelper.js +12 -2
- package/lib/extensions/versionHelper.js +14 -0
- package/lib/extensions/warnings.js +33 -1
- package/lib/gcp/artifactregistry.js +16 -0
- package/lib/gcp/cloudfunctions.js +25 -72
- package/lib/gcp/cloudfunctionsv2.js +46 -98
- package/lib/gcp/cloudscheduler.js +22 -16
- package/lib/gcp/cloudtasks.js +143 -0
- package/lib/gcp/docker.js +36 -2
- package/lib/gcp/location.js +44 -0
- package/lib/gcp/proto.js +2 -2
- package/lib/gcp/pubsub.js +1 -9
- package/lib/gcp/secretManager.js +27 -6
- package/lib/gcp/storage.js +48 -32
- package/lib/init/features/project.js +2 -1
- package/lib/previews.js +1 -1
- package/lib/projectUtils.js +10 -1
- package/lib/utils.js +30 -1
- package/package.json +5 -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/CHANGELOG.md
CHANGED
|
@@ -1 +1,5 @@
|
|
|
1
|
-
-
|
|
1
|
+
- Corrects a bug where containers in Artifact Registry would not be deleted if a function has an upper case character in its name (#3918)
|
|
2
|
+
- Fixes issue where providing the `--project` flag during `init` would not be recognized with a default project already set. (#3870)
|
|
3
|
+
- Fixes issue with setting memory limits for some functions (#3924)
|
|
4
|
+
- New HTTPS functions only allow secure traffic. (#3923)
|
|
5
|
+
- No longer default-enable AR and don't send builds to AR unless an experiment is enabled (#3935)
|
package/lib/api.js
CHANGED
|
@@ -72,6 +72,7 @@ var api = {
|
|
|
72
72
|
cloudbillingOrigin: utils.envOverride("FIREBASE_CLOUDBILLING_URL", "https://cloudbilling.googleapis.com"),
|
|
73
73
|
cloudloggingOrigin: utils.envOverride("FIREBASE_CLOUDLOGGING_URL", "https://logging.googleapis.com"),
|
|
74
74
|
containerRegistryDomain: utils.envOverride("CONTAINER_REGISTRY_DOMAIN", "gcr.io"),
|
|
75
|
+
artifactRegistryDomain: utils.envOverride("ARTIFACT_REGISTRY_DOMAIN", "https://artifactregistry.googleapis.com"),
|
|
75
76
|
appDistributionOrigin: utils.envOverride("FIREBASE_APP_DISTRIBUTION_URL", "https://firebaseappdistribution.googleapis.com"),
|
|
76
77
|
appengineOrigin: utils.envOverride("FIREBASE_APPENGINE_URL", "https://appengine.googleapis.com"),
|
|
77
78
|
authOrigin: utils.envOverride("FIREBASE_AUTH_URL", "https://accounts.google.com"),
|
|
@@ -95,6 +96,7 @@ var api = {
|
|
|
95
96
|
functionsUploadRegion: utils.envOverride("FIREBASE_FUNCTIONS_UPLOAD_REGION", "us-central1"),
|
|
96
97
|
functionsDefaultRegion: utils.envOverride("FIREBASE_FUNCTIONS_DEFAULT_REGION", "us-central1"),
|
|
97
98
|
cloudschedulerOrigin: utils.envOverride("FIREBASE_CLOUDSCHEDULER_URL", "https://cloudscheduler.googleapis.com"),
|
|
99
|
+
cloudTasksOrigin: utils.envOverride("FIREBASE_CLOUD_TAKS_URL", "https://cloudtasks.googleapis.com"),
|
|
98
100
|
pubsubOrigin: utils.envOverride("FIREBASE_PUBSUB_URL", "https://pubsub.googleapis.com"),
|
|
99
101
|
googleOrigin: utils.envOverride("FIREBASE_TOKEN_URL", utils.envOverride("FIREBASE_GOOGLE_URL", "https://www.googleapis.com")),
|
|
100
102
|
hostingOrigin: utils.envOverride("FIREBASE_HOSTING_URL", "https://web.app"),
|
package/lib/apiv2.js
CHANGED
|
@@ -118,8 +118,10 @@ class Client {
|
|
|
118
118
|
reqOptions.headers.set("User-Agent", `FirebaseCLI/${CLI_VERSION}`);
|
|
119
119
|
}
|
|
120
120
|
reqOptions.headers.set("X-Client-Version", `FirebaseCLI/${CLI_VERSION}`);
|
|
121
|
-
if (reqOptions.
|
|
122
|
-
reqOptions.
|
|
121
|
+
if (!reqOptions.headers.has("Content-Type")) {
|
|
122
|
+
if (reqOptions.responseType === "json") {
|
|
123
|
+
reqOptions.headers.set("Content-Type", "application/json");
|
|
124
|
+
}
|
|
123
125
|
}
|
|
124
126
|
return reqOptions;
|
|
125
127
|
}
|
|
@@ -217,11 +219,12 @@ class Client {
|
|
|
217
219
|
}
|
|
218
220
|
let body;
|
|
219
221
|
if (options.responseType === "json") {
|
|
220
|
-
|
|
222
|
+
const text = await res.text();
|
|
223
|
+
if (!text.length) {
|
|
221
224
|
body = undefined;
|
|
222
225
|
}
|
|
223
226
|
else {
|
|
224
|
-
body =
|
|
227
|
+
body = JSON.parse(text);
|
|
225
228
|
}
|
|
226
229
|
}
|
|
227
230
|
else if (options.responseType === "stream") {
|
|
@@ -20,7 +20,7 @@ const SYMBOL_CACHE_ROOT_DIR = process.env.FIREBASE_CRASHLYTICS_CACHE_PATH || os.
|
|
|
20
20
|
const JAR_CACHE_DIR = process.env.FIREBASE_CRASHLYTICS_BUILDTOOLS_PATH ||
|
|
21
21
|
path.join(os.homedir(), ".cache", "firebase", "crashlytics", "buildtools");
|
|
22
22
|
const JAR_VERSION = "2.8.0";
|
|
23
|
-
const JAR_URL = `https://
|
|
23
|
+
const JAR_URL = `https://dl.google.com/android/maven2/com/google/firebase/firebase-crashlytics-buildtools/${JAR_VERSION}/firebase-crashlytics-buildtools-${JAR_VERSION}.jar`;
|
|
24
24
|
exports.default = new command_1.Command("crashlytics:symbols:upload <symbolFiles...>")
|
|
25
25
|
.description("Upload symbols for native code, to symbolicate stack traces.")
|
|
26
26
|
.option("--app <appID>", "the app id of your Firebase app")
|
|
@@ -40,7 +40,7 @@ exports.default = new command_1.Command("crashlytics:symbols:upload <symbolFiles
|
|
|
40
40
|
jarFile,
|
|
41
41
|
app,
|
|
42
42
|
generator,
|
|
43
|
-
cachePath: path.join(SYMBOL_CACHE_ROOT_DIR, `crashlytics-${uuid.v4()}`, "nativeSymbols", app, generator),
|
|
43
|
+
cachePath: path.join(SYMBOL_CACHE_ROOT_DIR, `crashlytics-${uuid.v4()}`, "nativeSymbols", app.replace(/:/g, "-"), generator),
|
|
44
44
|
symbolFile: "",
|
|
45
45
|
generate: true,
|
|
46
46
|
};
|
package/lib/commands/deploy.js
CHANGED
|
@@ -9,7 +9,15 @@ const deploy = require("../deploy");
|
|
|
9
9
|
const requireConfig = require("../requireConfig");
|
|
10
10
|
const { filterTargets } = require("../filterTargets");
|
|
11
11
|
const { requireHostingSite } = require("../requireHostingSite");
|
|
12
|
-
const VALID_TARGETS = [
|
|
12
|
+
const VALID_TARGETS = [
|
|
13
|
+
"database",
|
|
14
|
+
"storage",
|
|
15
|
+
"firestore",
|
|
16
|
+
"functions",
|
|
17
|
+
"hosting",
|
|
18
|
+
"remoteconfig",
|
|
19
|
+
"extensions",
|
|
20
|
+
];
|
|
13
21
|
const TARGET_PERMISSIONS = {
|
|
14
22
|
database: ["firebasedatabase.instances.update"],
|
|
15
23
|
hosting: ["firebasehosting.sites.update"],
|
|
@@ -69,7 +69,7 @@ exports.default = new command_1.Command("ext:configure <extensionInstanceId>")
|
|
|
69
69
|
", uninstall the extension, then install a new instance of this extension.");
|
|
70
70
|
}
|
|
71
71
|
spinner.start();
|
|
72
|
-
const res = await extensionsApi.configureInstance(projectId, instanceId, params);
|
|
72
|
+
const res = await extensionsApi.configureInstance({ projectId, instanceId, params });
|
|
73
73
|
spinner.stop();
|
|
74
74
|
utils.logLabeledSuccess(extensionsHelper_1.logPrefix, `successfully configured ${clc.bold(instanceId)}.`);
|
|
75
75
|
utils.logLabeledBullet(extensionsHelper_1.logPrefix, marked(`You can view your reconfigured instance in the Firebase console: ${utils.consoleUrl(projectId, `/extensions/instances/${instanceId}?tab=config`)}`));
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const clc = require("cli-color");
|
|
4
|
+
const semver = require("semver");
|
|
5
|
+
const refs = require("../extensions/refs");
|
|
6
|
+
const utils = require("../utils");
|
|
7
|
+
const command_1 = require("../command");
|
|
8
|
+
const prompt_1 = require("../prompt");
|
|
9
|
+
const extensionsHelper_1 = require("../extensions/extensionsHelper");
|
|
10
|
+
const extensionsApi_1 = require("../extensions/extensionsApi");
|
|
11
|
+
const versionHelper_1 = require("../extensions/versionHelper");
|
|
12
|
+
const requireAuth_1 = require("../requireAuth");
|
|
13
|
+
const error_1 = require("../error");
|
|
14
|
+
exports.default = new command_1.Command("ext:dev:deprecate <extensionRef> <versionPredicate>")
|
|
15
|
+
.description("deprecate extension versions that match the version predicate")
|
|
16
|
+
.option("-m, --message <deprecationMessage>", "deprecation message")
|
|
17
|
+
.option("-f, --force", "override deprecation message for existing deprecated extension versions that match")
|
|
18
|
+
.before(requireAuth_1.requireAuth)
|
|
19
|
+
.before(extensionsHelper_1.ensureExtensionsApiEnabled)
|
|
20
|
+
.action(async (extensionRef, versionPredicate, options) => {
|
|
21
|
+
const { publisherId, extensionId, version } = refs.parse(extensionRef);
|
|
22
|
+
if (version) {
|
|
23
|
+
throw new error_1.FirebaseError(`The input extension reference must be of the format ${clc.bold("<publisherId>/<extensionId>")}. Version should be supplied in the version predicate argument.`);
|
|
24
|
+
}
|
|
25
|
+
if (!publisherId || !extensionId) {
|
|
26
|
+
throw new error_1.FirebaseError(`Error parsing publisher ID and extension ID from extension reference '${clc.bold(extensionRef)}'. Please use the format '${clc.bold("<publisherId>/<extensionId>")}'.`);
|
|
27
|
+
}
|
|
28
|
+
const { comparator, targetSemVer } = versionHelper_1.parseVersionPredicate(versionPredicate);
|
|
29
|
+
const filter = `id${comparator}"${targetSemVer}"`;
|
|
30
|
+
const extensionVersions = await extensionsApi_1.listExtensionVersions(extensionRef, filter);
|
|
31
|
+
const filteredExtensionVersions = extensionVersions
|
|
32
|
+
.sort((ev1, ev2) => {
|
|
33
|
+
return -semver.compare(ev1.spec.version, ev2.spec.version);
|
|
34
|
+
})
|
|
35
|
+
.filter((extensionVersion) => {
|
|
36
|
+
if (extensionVersion.state === "DEPRECATED" && !options.force) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
const message = extensionVersion.state === "DEPRECATED" ? ", will overwrite deprecation message" : "";
|
|
40
|
+
utils.logLabeledBullet(extensionVersion.ref, extensionVersion.state + message);
|
|
41
|
+
return true;
|
|
42
|
+
});
|
|
43
|
+
if (filteredExtensionVersions.length > 0) {
|
|
44
|
+
if (!options.force) {
|
|
45
|
+
const confirmMessage = "You are about to deprecate these extension version(s). Do you wish to continue?";
|
|
46
|
+
const consent = await prompt_1.promptOnce({
|
|
47
|
+
type: "confirm",
|
|
48
|
+
message: confirmMessage,
|
|
49
|
+
default: false,
|
|
50
|
+
});
|
|
51
|
+
if (!consent) {
|
|
52
|
+
throw new error_1.FirebaseError("Deprecation canceled.");
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
throw new error_1.FirebaseError("No extension versions matched the version predicate.");
|
|
58
|
+
}
|
|
59
|
+
await utils.allSettled(filteredExtensionVersions.map(async (extensionVersion) => {
|
|
60
|
+
await extensionsApi_1.deprecateExtensionVersion(extensionVersion.ref, options.deprecationMessage);
|
|
61
|
+
}));
|
|
62
|
+
utils.logLabeledSuccess(extensionsHelper_1.logPrefix, "successfully deprecated extension version(s).");
|
|
63
|
+
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const clc = require("cli-color");
|
|
4
|
+
const semver = require("semver");
|
|
5
|
+
const refs = require("../extensions/refs");
|
|
6
|
+
const utils = require("../utils");
|
|
7
|
+
const command_1 = require("../command");
|
|
8
|
+
const prompt_1 = require("../prompt");
|
|
9
|
+
const extensionsHelper_1 = require("../extensions/extensionsHelper");
|
|
10
|
+
const extensionsApi_1 = require("../extensions/extensionsApi");
|
|
11
|
+
const versionHelper_1 = require("../extensions/versionHelper");
|
|
12
|
+
const requireAuth_1 = require("../requireAuth");
|
|
13
|
+
const error_1 = require("../error");
|
|
14
|
+
exports.default = new command_1.Command("ext:dev:undeprecate <extensionRef> <versionPredicate>")
|
|
15
|
+
.description("undeprecate extension versions that match the version predicate")
|
|
16
|
+
.before(requireAuth_1.requireAuth)
|
|
17
|
+
.before(extensionsHelper_1.ensureExtensionsApiEnabled)
|
|
18
|
+
.action(async (extensionRef, versionPredicate, options) => {
|
|
19
|
+
const { publisherId, extensionId, version } = refs.parse(extensionRef);
|
|
20
|
+
if (version) {
|
|
21
|
+
throw new error_1.FirebaseError(`The input extension reference must be of the format ${clc.bold("<publisherId>/<extensionId>")}. Version should be supplied in the version predicate argument.`);
|
|
22
|
+
}
|
|
23
|
+
if (!publisherId || !extensionId) {
|
|
24
|
+
throw new error_1.FirebaseError(`Error parsing publisher ID and extension ID from extension reference '${clc.bold(extensionRef)}'. Please use the format '${clc.bold("<publisherId>/<extensionId>")}'.`);
|
|
25
|
+
}
|
|
26
|
+
const { comparator, targetSemVer } = versionHelper_1.parseVersionPredicate(versionPredicate);
|
|
27
|
+
const filter = `id${comparator}"${targetSemVer}"`;
|
|
28
|
+
const extensionVersions = await extensionsApi_1.listExtensionVersions(extensionRef, filter);
|
|
29
|
+
extensionVersions
|
|
30
|
+
.sort((ev1, ev2) => {
|
|
31
|
+
return -semver.compare(ev1.spec.version, ev2.spec.version);
|
|
32
|
+
})
|
|
33
|
+
.forEach((extensionVersion) => {
|
|
34
|
+
utils.logLabeledBullet(extensionVersion.ref, extensionVersion.state);
|
|
35
|
+
});
|
|
36
|
+
if (extensionVersions.length > 0) {
|
|
37
|
+
if (!options.force) {
|
|
38
|
+
const confirmMessage = "You are about to undeprecate these extension version(s). Do you wish to continue?";
|
|
39
|
+
const consent = await prompt_1.promptOnce({
|
|
40
|
+
type: "confirm",
|
|
41
|
+
message: confirmMessage,
|
|
42
|
+
default: false,
|
|
43
|
+
});
|
|
44
|
+
if (!consent) {
|
|
45
|
+
throw new error_1.FirebaseError("Undeprecation canceled.");
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
throw new error_1.FirebaseError("No extension versions matched the version predicate.");
|
|
51
|
+
}
|
|
52
|
+
await utils.allSettled(extensionVersions.map(async (extensionVersion) => {
|
|
53
|
+
await extensionsApi_1.undeprecateExtensionVersion(extensionVersion.ref);
|
|
54
|
+
}));
|
|
55
|
+
utils.logLabeledSuccess(extensionsHelper_1.logPrefix, "successfully undeprecated extension version(s).");
|
|
56
|
+
});
|
|
@@ -12,11 +12,12 @@ const error_1 = require("../error");
|
|
|
12
12
|
const checkMinRequiredVersion_1 = require("../checkMinRequiredVersion");
|
|
13
13
|
module.exports = new command_1.Command("ext:dev:unpublish <extensionRef>")
|
|
14
14
|
.description("unpublish an extension")
|
|
15
|
+
.withForce()
|
|
15
16
|
.help("use this command to unpublish an extension, and make it unavailable for developers to install or reconfigure. " +
|
|
16
17
|
"Specify the extension you want to unpublish using the format '<publisherId>/<extensionId>.")
|
|
17
18
|
.before(requireAuth_1.requireAuth)
|
|
18
19
|
.before(checkMinRequiredVersion_1.checkMinRequiredVersion, "extDevMinVersion")
|
|
19
|
-
.action(async (extensionRef) => {
|
|
20
|
+
.action(async (extensionRef, options) => {
|
|
20
21
|
const { publisherId, extensionId, version } = refs.parse(extensionRef);
|
|
21
22
|
utils.logLabeledWarning(extensionsHelper_1.logPrefix, "If you unpublish this extension, developers won't be able to install it. For developers who currently have this extension installed, it will continue to run and will appear as unpublished when listed in the Firebase console or Firebase CLI.");
|
|
22
23
|
utils.logLabeledWarning("This is a permanent action", `Once unpublished, you may never use the extension name '${clc.bold(extensionId)}' again.`);
|
|
@@ -24,14 +25,20 @@ module.exports = new command_1.Command("ext:dev:unpublish <extensionRef>")
|
|
|
24
25
|
throw new error_1.FirebaseError(`Unpublishing a single version is not currently supported. You can only unpublish ${clc.bold("ALL versions")} of an extension. To unpublish all versions, please remove the version from the reference.`);
|
|
25
26
|
}
|
|
26
27
|
await extensionsApi_1.getExtension(extensionRef);
|
|
27
|
-
const consent = await comfirmUnpublish(publisherId, extensionId);
|
|
28
|
+
const consent = await comfirmUnpublish(publisherId, extensionId, options);
|
|
28
29
|
if (!consent) {
|
|
29
30
|
throw new error_1.FirebaseError("unpublishing cancelled.");
|
|
30
31
|
}
|
|
31
32
|
await extensionsApi_1.unpublishExtension(extensionRef);
|
|
32
33
|
utils.logLabeledSuccess(extensionsHelper_1.logPrefix, "successfully unpublished all versions of this extension.");
|
|
33
34
|
});
|
|
34
|
-
async function comfirmUnpublish(publisherId, extensionId) {
|
|
35
|
+
async function comfirmUnpublish(publisherId, extensionId, options) {
|
|
36
|
+
if (options.nonInteractive && !options.force) {
|
|
37
|
+
throw new error_1.FirebaseError("Pass the --force flag to use this command in non-interactive mode");
|
|
38
|
+
}
|
|
39
|
+
if (options.nonInteractive && options.force) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
35
42
|
const message = `You are about to unpublish ALL versions of ${clc.green(`${publisherId}/${extensionId}`)}.\nDo you wish to continue? `;
|
|
36
43
|
return prompt_1.promptOnce({
|
|
37
44
|
type: "confirm",
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const checkMinRequiredVersion_1 = require("../checkMinRequiredVersion");
|
|
4
|
+
const command_1 = require("../command");
|
|
5
|
+
const planner = require("../deploy/extensions/planner");
|
|
6
|
+
const export_1 = require("../extensions/export");
|
|
7
|
+
const extensionsHelper_1 = require("../extensions/extensionsHelper");
|
|
8
|
+
const functional_1 = require("../functional");
|
|
9
|
+
const getProjectNumber_1 = require("../getProjectNumber");
|
|
10
|
+
const logger_1 = require("../logger");
|
|
11
|
+
const projectUtils_1 = require("../projectUtils");
|
|
12
|
+
const prompt_1 = require("../prompt");
|
|
13
|
+
const requirePermissions_1 = require("../requirePermissions");
|
|
14
|
+
module.exports = new command_1.Command("ext:export")
|
|
15
|
+
.description("export all Extension instances installed on a project to a local Firebase directory")
|
|
16
|
+
.before(requirePermissions_1.requirePermissions, ["firebaseextensions.instances.list"])
|
|
17
|
+
.before(extensionsHelper_1.ensureExtensionsApiEnabled)
|
|
18
|
+
.before(checkMinRequiredVersion_1.checkMinRequiredVersion, "extMinVersion")
|
|
19
|
+
.withForce()
|
|
20
|
+
.action(async (options) => {
|
|
21
|
+
const projectId = projectUtils_1.needProjectId(options);
|
|
22
|
+
const projectNumber = await getProjectNumber_1.getProjectNumber(options);
|
|
23
|
+
const have = await Promise.all((await planner.have(projectId)).map(async (i) => {
|
|
24
|
+
const subbed = await export_1.setSecretParamsToLatest(i);
|
|
25
|
+
return export_1.parameterizeProject(projectId, projectNumber, subbed);
|
|
26
|
+
}));
|
|
27
|
+
if (have.length == 0) {
|
|
28
|
+
logger_1.logger.info(`No extension instances installed on ${projectId}, so there is nothing to export.`);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const [withRef, withoutRef] = functional_1.partition(have, (s) => !!s.ref);
|
|
32
|
+
export_1.displayExportInfo(withRef, withoutRef);
|
|
33
|
+
if (!options.nonInteractive &&
|
|
34
|
+
!options.force &&
|
|
35
|
+
!(await prompt_1.promptOnce({
|
|
36
|
+
message: "Do you wish to add these Extension instances to firebase.json?",
|
|
37
|
+
type: "confirm",
|
|
38
|
+
default: true,
|
|
39
|
+
}))) {
|
|
40
|
+
logger_1.logger.info("Exiting. No changes made.");
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
await export_1.writeFiles(withRef, options);
|
|
44
|
+
});
|
|
@@ -47,7 +47,7 @@ async function installExtension(options) {
|
|
|
47
47
|
}
|
|
48
48
|
else if (!enabled) {
|
|
49
49
|
await billingMigrationHelper_1.displayNode10CreateBillingNotice(spec, false);
|
|
50
|
-
await checkProjectBilling_1.enableBilling(projectId
|
|
50
|
+
await checkProjectBilling_1.enableBilling(projectId);
|
|
51
51
|
}
|
|
52
52
|
else {
|
|
53
53
|
await billingMigrationHelper_1.displayNode10CreateBillingNotice(spec, !nonInteractive);
|
|
@@ -138,7 +138,7 @@ exports.default = new command_1.Command("ext:update <extensionInstanceId> [updat
|
|
|
138
138
|
}
|
|
139
139
|
if (!enabled) {
|
|
140
140
|
if (!options.nonInteractive) {
|
|
141
|
-
await checkProjectBilling_1.enableBilling(projectId
|
|
141
|
+
await checkProjectBilling_1.enableBilling(projectId);
|
|
142
142
|
}
|
|
143
143
|
else {
|
|
144
144
|
throw new error_1.FirebaseError("The extension requires your project to be upgraded to the Blaze plan. " +
|
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const command_1 = require("../command");
|
|
4
3
|
const clc = require("cli-color");
|
|
5
4
|
const functionsConfig = require("../functionsConfig");
|
|
6
|
-
const
|
|
5
|
+
const command_1 = require("../command");
|
|
6
|
+
const error_1 = require("../error");
|
|
7
7
|
const projectUtils_1 = require("../projectUtils");
|
|
8
8
|
const prompt_1 = require("../prompt");
|
|
9
|
-
const
|
|
9
|
+
const functional_1 = require("../functional");
|
|
10
10
|
const requirePermissions_1 = require("../requirePermissions");
|
|
11
|
+
const helper = require("../deploy/functions/functionsDeployHelper");
|
|
11
12
|
const utils = require("../utils");
|
|
12
13
|
const backend = require("../deploy/functions/backend");
|
|
13
|
-
const
|
|
14
|
+
const planner = require("../deploy/functions/release/planner");
|
|
15
|
+
const fabricator = require("../deploy/functions/release/fabricator");
|
|
16
|
+
const executor = require("../deploy/functions/release/executor");
|
|
17
|
+
const reporter = require("../deploy/functions/release/reporter");
|
|
18
|
+
const containerCleaner = require("../deploy/functions/containerCleaner");
|
|
14
19
|
exports.default = new command_1.Command("functions:delete [filters...]")
|
|
15
20
|
.description("delete one or more Cloud Functions by name or group name.")
|
|
16
21
|
.option("--region <region>", "Specify region of the function to be deleted. " +
|
|
@@ -23,48 +28,55 @@ exports.default = new command_1.Command("functions:delete [filters...]")
|
|
|
23
28
|
}
|
|
24
29
|
const context = {
|
|
25
30
|
projectId: projectUtils_1.needProjectId(options),
|
|
31
|
+
filters: filters.map((f) => f.split(".")),
|
|
26
32
|
};
|
|
27
|
-
const
|
|
28
|
-
|
|
33
|
+
const [config, existingBackend] = await Promise.all([
|
|
34
|
+
functionsConfig.getFirebaseConfig(options),
|
|
35
|
+
backend.existingBackend(context),
|
|
36
|
+
]);
|
|
37
|
+
await backend.checkAvailability(context, backend.empty());
|
|
38
|
+
const appEngineLocation = functionsConfig.getAppEngineLocation(config);
|
|
39
|
+
if (options.region) {
|
|
40
|
+
existingBackend.endpoints = { [options.region]: existingBackend.endpoints[options.region] };
|
|
41
|
+
}
|
|
42
|
+
const plan = planner.createDeploymentPlan(backend.empty(), existingBackend, {
|
|
43
|
+
filters: context.filters,
|
|
44
|
+
deleteAll: true,
|
|
45
|
+
});
|
|
46
|
+
const allEpToDelete = Object.values(plan)
|
|
47
|
+
.map((changes) => changes.endpointsToDelete)
|
|
48
|
+
.reduce(functional_1.reduceFlat, [])
|
|
49
|
+
.sort(backend.compareFunctions);
|
|
50
|
+
if (allEpToDelete.length === 0) {
|
|
51
|
+
throw new error_1.FirebaseError(`The specified filters do not match any existing functions in project ${clc.bold(context.projectId)}.`);
|
|
52
|
+
}
|
|
53
|
+
const deleteList = allEpToDelete.map((func) => `\t${helper.getFunctionLabel(func)}`).join("\n");
|
|
54
|
+
const confirmDeletion = await prompt_1.promptOnce({
|
|
55
|
+
type: "confirm",
|
|
56
|
+
name: "force",
|
|
57
|
+
default: false,
|
|
58
|
+
message: "You are about to delete the following Cloud Functions:\n" +
|
|
59
|
+
deleteList +
|
|
60
|
+
"\n Are you sure?",
|
|
61
|
+
}, options);
|
|
62
|
+
if (!confirmDeletion) {
|
|
63
|
+
throw new error_1.FirebaseError("Command aborted.");
|
|
64
|
+
}
|
|
65
|
+
const functionExecutor = new executor.QueueExecutor({
|
|
66
|
+
retries: 30,
|
|
67
|
+
backoff: 20000,
|
|
68
|
+
concurrency: 40,
|
|
69
|
+
maxBackoff: 40000,
|
|
29
70
|
});
|
|
30
71
|
try {
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
await backend.checkAvailability(context, backend.empty());
|
|
36
|
-
const appEngineLocation = functionsConfig.getAppEngineLocation(config);
|
|
37
|
-
const functionsToDelete = existingBackend.cloudFunctions.filter((fn) => {
|
|
38
|
-
const regionMatches = options.region ? fn.region === options.region : true;
|
|
39
|
-
const nameMatches = helper.functionMatchesAnyGroup(fn, filterChunks);
|
|
40
|
-
return regionMatches && nameMatches;
|
|
41
|
-
});
|
|
42
|
-
if (functionsToDelete.length === 0) {
|
|
43
|
-
throw new Error(`The specified filters do not match any existing functions in project ${clc.bold(context.projectId)}.`);
|
|
44
|
-
}
|
|
45
|
-
const schedulesToDelete = existingBackend.schedules.filter((schedule) => {
|
|
46
|
-
functionsToDelete.some(backend.sameFunctionName(schedule.targetService));
|
|
47
|
-
});
|
|
48
|
-
const topicsToDelete = existingBackend.topics.filter((topic) => {
|
|
49
|
-
functionsToDelete.some(backend.sameFunctionName(topic.targetService));
|
|
72
|
+
const fab = new fabricator.Fabricator({
|
|
73
|
+
functionExecutor,
|
|
74
|
+
executor: new executor.QueueExecutor({}),
|
|
75
|
+
appEngineLocation,
|
|
50
76
|
});
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
})
|
|
55
|
-
.join("\n");
|
|
56
|
-
const confirmDeletion = await prompt_1.promptOnce({
|
|
57
|
-
type: "confirm",
|
|
58
|
-
name: "force",
|
|
59
|
-
default: false,
|
|
60
|
-
message: "You are about to delete the following Cloud Functions:\n" +
|
|
61
|
-
deleteList +
|
|
62
|
-
"\n Are you sure?",
|
|
63
|
-
}, options);
|
|
64
|
-
if (!confirmDeletion) {
|
|
65
|
-
throw new Error("Command aborted.");
|
|
66
|
-
}
|
|
67
|
-
return await functionsDelete_1.deleteFunctions(functionsToDelete, schedulesToDelete, topicsToDelete, appEngineLocation);
|
|
77
|
+
const summary = await fab.applyPlan(plan);
|
|
78
|
+
await reporter.logAndTrackDeployStats(summary);
|
|
79
|
+
reporter.printErrors(summary);
|
|
68
80
|
}
|
|
69
81
|
catch (err) {
|
|
70
82
|
throw new error_1.FirebaseError("Failed to delete functions", {
|
|
@@ -72,4 +84,5 @@ exports.default = new command_1.Command("functions:delete [filters...]")
|
|
|
72
84
|
exit: 1,
|
|
73
85
|
});
|
|
74
86
|
}
|
|
87
|
+
await containerCleaner.cleanupBuildImages([], allEpToDelete);
|
|
75
88
|
});
|
|
@@ -5,7 +5,6 @@ const error_1 = require("../error");
|
|
|
5
5
|
const projectUtils_1 = require("../projectUtils");
|
|
6
6
|
const requirePermissions_1 = require("../requirePermissions");
|
|
7
7
|
const backend = require("../deploy/functions/backend");
|
|
8
|
-
const listFunctions_1 = require("../functions/listFunctions");
|
|
9
8
|
const previews_1 = require("../previews");
|
|
10
9
|
const logger_1 = require("../logger");
|
|
11
10
|
const Table = require("cli-table");
|
|
@@ -17,7 +16,8 @@ exports.default = new command_1.Command("functions:list")
|
|
|
17
16
|
const context = {
|
|
18
17
|
projectId: projectUtils_1.needProjectId(options),
|
|
19
18
|
};
|
|
20
|
-
const
|
|
19
|
+
const existing = await backend.existingBackend(context);
|
|
20
|
+
const endpointsList = backend.allEndpoints(existing).sort(backend.compareFunctions);
|
|
21
21
|
const table = previews_1.previews.functionsv2
|
|
22
22
|
? new Table({
|
|
23
23
|
head: ["Function", "Version", "Trigger", "Location", "Memory", "Runtime"],
|
|
@@ -27,23 +27,23 @@ exports.default = new command_1.Command("functions:list")
|
|
|
27
27
|
head: ["Function", "Trigger", "Location", "Memory", "Runtime"],
|
|
28
28
|
style: { head: ["yellow"] },
|
|
29
29
|
});
|
|
30
|
-
for (const
|
|
31
|
-
const trigger = backend.
|
|
32
|
-
const availableMemoryMb =
|
|
30
|
+
for (const endpoint of endpointsList) {
|
|
31
|
+
const trigger = backend.endpointTriggerType(endpoint);
|
|
32
|
+
const availableMemoryMb = endpoint.availableMemoryMb || "---";
|
|
33
33
|
const entry = previews_1.previews.functionsv2
|
|
34
34
|
? [
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
endpoint.id,
|
|
36
|
+
endpoint.platform === "gcfv2" ? "v2" : "v1",
|
|
37
37
|
trigger,
|
|
38
|
-
|
|
38
|
+
endpoint.region,
|
|
39
39
|
availableMemoryMb,
|
|
40
|
-
|
|
40
|
+
endpoint.runtime,
|
|
41
41
|
]
|
|
42
|
-
: [
|
|
42
|
+
: [endpoint.id, trigger, endpoint.region, availableMemoryMb, endpoint.runtime];
|
|
43
43
|
table.push(entry);
|
|
44
44
|
}
|
|
45
45
|
logger_1.logger.info(table.toString());
|
|
46
|
-
return
|
|
46
|
+
return endpointsList;
|
|
47
47
|
}
|
|
48
48
|
catch (err) {
|
|
49
49
|
throw new error_1.FirebaseError("Failed to list functions", {
|
package/lib/commands/index.js
CHANGED
|
@@ -26,11 +26,9 @@ module.exports = function (client) {
|
|
|
26
26
|
client.auth = {};
|
|
27
27
|
client.auth.export = loadCommand("auth-export");
|
|
28
28
|
client.auth.upload = loadCommand("auth-import");
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
client.crashlytics.symbols.upload = loadCommand("crashlytics-symbols-upload");
|
|
33
|
-
}
|
|
29
|
+
client.crashlytics = {};
|
|
30
|
+
client.crashlytics.symbols = {};
|
|
31
|
+
client.crashlytics.symbols.upload = loadCommand("crashlytics-symbols-upload");
|
|
34
32
|
client.database = {};
|
|
35
33
|
client.database.get = loadCommand("database-get");
|
|
36
34
|
client.database.instances = {};
|
|
@@ -63,6 +61,7 @@ module.exports = function (client) {
|
|
|
63
61
|
client.ext = loadCommand("ext");
|
|
64
62
|
client.ext.configure = loadCommand("ext-configure");
|
|
65
63
|
client.ext.info = loadCommand("ext-info");
|
|
64
|
+
client.ext.export = loadCommand("ext-export");
|
|
66
65
|
client.ext.install = loadCommand("ext-install");
|
|
67
66
|
client.ext.list = loadCommand("ext-list");
|
|
68
67
|
client.ext.uninstall = loadCommand("ext-uninstall");
|
|
@@ -79,6 +78,8 @@ module.exports = function (client) {
|
|
|
79
78
|
client.ext.dev.emulators = {};
|
|
80
79
|
client.ext.dev.emulators.start = loadCommand("ext-dev-emulators-start");
|
|
81
80
|
client.ext.dev.emulators.exec = loadCommand("ext-dev-emulators-exec");
|
|
81
|
+
client.ext.dev.deprecate = loadCommand("ext-dev-deprecate");
|
|
82
|
+
client.ext.dev.undeprecate = loadCommand("ext-dev-undeprecate");
|
|
82
83
|
client.ext.dev.unpublish = loadCommand("ext-dev-unpublish");
|
|
83
84
|
client.ext.dev.publish = loadCommand("ext-dev-publish");
|
|
84
85
|
client.ext.dev.delete = loadCommand("ext-dev-extension-delete");
|
package/lib/commands/init.js
CHANGED
|
@@ -155,6 +155,9 @@ module.exports = new Command("init [feature]")
|
|
|
155
155
|
if (allAccounts.length > 1) {
|
|
156
156
|
setup.features.unshift("account");
|
|
157
157
|
}
|
|
158
|
+
if (setup.features.includes("hosting") && setup.features.includes("hosting:github")) {
|
|
159
|
+
setup.features = setup.features.filter((f) => f != "hosting:github");
|
|
160
|
+
}
|
|
158
161
|
return init(setup, config, options);
|
|
159
162
|
})
|
|
160
163
|
.then(function () {
|
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;
|