firebase-tools 10.1.5 → 10.2.2
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/lib/api.js +1 -0
- package/lib/apiv2.js +3 -0
- package/lib/appdistribution/options-parser-util.js +1 -1
- package/lib/auth.js +62 -25
- package/lib/command.js +1 -1
- package/lib/commands/apps-android-sha-create.js +2 -2
- package/lib/commands/apps-sdkconfig.js +1 -1
- package/lib/commands/auth-import.js +1 -1
- package/lib/commands/database-rules-list.js +2 -2
- package/lib/commands/emulators-start.js +1 -1
- package/lib/commands/ext-configure.js +1 -0
- package/lib/commands/ext-dev-init.js +49 -49
- package/lib/commands/ext-export.js +12 -2
- package/lib/commands/ext-install.js +104 -103
- package/lib/commands/ext-uninstall.js +9 -8
- package/lib/commands/ext-update.js +10 -9
- package/lib/commands/functions-config-clone.js +1 -1
- package/lib/commands/functions-config-export.js +1 -1
- package/lib/commands/functions-secrets-access.js +17 -0
- package/lib/commands/functions-secrets-destroy.js +40 -0
- package/lib/commands/functions-secrets-get.js +21 -0
- package/lib/commands/functions-secrets-prune.js +50 -0
- package/lib/commands/functions-secrets-set.js +46 -0
- package/lib/commands/hosting-clone.js +3 -3
- package/lib/commands/index.js +7 -3
- package/lib/commands/login.js +1 -1
- package/lib/commands/remoteconfig-get.js +1 -1
- package/lib/deploy/extensions/deploymentSummary.js +3 -3
- package/lib/deploy/extensions/params.js +3 -0
- package/lib/deploy/extensions/planner.js +2 -1
- package/lib/deploy/extensions/tasks.js +1 -1
- package/lib/deploy/functions/backend.js +20 -5
- package/lib/deploy/functions/checkIam.js +1 -1
- package/lib/deploy/functions/containerCleaner.js +3 -3
- package/lib/deploy/functions/ensure.js +112 -0
- package/lib/deploy/functions/ensureCloudBuildEnabled.js +0 -49
- package/lib/deploy/functions/functionsDeployHelper.js +2 -2
- package/lib/deploy/functions/prepare.js +15 -20
- package/lib/deploy/functions/pricing.js +1 -1
- package/lib/deploy/functions/prompts.js +2 -2
- package/lib/deploy/functions/release/fabricator.js +3 -3
- package/lib/deploy/functions/release/index.js +1 -1
- package/lib/deploy/functions/release/planner.js +11 -8
- package/lib/deploy/functions/release/reporter.js +3 -0
- package/lib/deploy/functions/runtimes/discovery/index.js +6 -6
- package/lib/deploy/functions/runtimes/discovery/parsing.js +1 -1
- package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +17 -11
- package/lib/deploy/functions/runtimes/golang/index.js +2 -2
- package/lib/deploy/functions/runtimes/node/index.js +26 -0
- package/lib/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.js +2 -2
- package/lib/deploy/functions/runtimes/node/parseTriggers.js +40 -7
- package/lib/deploy/functions/runtimes/node/versioning.js +2 -2
- package/lib/deploy/functions/validate.js +58 -3
- package/lib/deploy/hosting/client.js +9 -0
- package/lib/deploy/hosting/convertConfig.js +6 -0
- package/lib/deploy/hosting/deploy.js +2 -2
- package/lib/deploy/hosting/hashcache.js +21 -19
- package/lib/deploy/hosting/index.js +5 -5
- package/lib/deploy/hosting/prepare.js +25 -25
- package/lib/deploy/hosting/release.js +21 -24
- package/lib/deploy/hosting/uploader.js +5 -5
- package/lib/deploy/remoteconfig/functions.js +2 -2
- package/lib/emulator/auth/cloudFunctions.js +1 -1
- package/lib/emulator/auth/operations.js +1 -1
- package/lib/emulator/commandUtils.js +5 -1
- package/lib/emulator/constants.js +3 -0
- package/lib/emulator/controller.js +48 -18
- package/lib/emulator/download.js +18 -1
- package/lib/emulator/downloadableEmulators.js +30 -13
- package/lib/emulator/emulatorLogger.js +19 -1
- package/lib/emulator/extensions/validation.js +35 -0
- package/lib/emulator/extensionsEmulator.js +140 -0
- package/lib/emulator/functionsEmulator.js +175 -86
- package/lib/emulator/functionsEmulatorRuntime.js +108 -83
- package/lib/emulator/functionsEmulatorShared.js +51 -1
- package/lib/emulator/functionsEmulatorShell.js +1 -2
- package/lib/emulator/functionsEmulatorUtils.js +4 -4
- package/lib/emulator/functionsRuntimeWorker.js +3 -3
- package/lib/emulator/hub.js +4 -3
- package/lib/emulator/loggingEmulator.js +1 -1
- package/lib/emulator/pubsubEmulator.js +1 -1
- package/lib/emulator/registry.js +10 -2
- package/lib/emulator/storage/apis/firebase.js +31 -26
- package/lib/emulator/storage/apis/gcloud.js +7 -12
- package/lib/emulator/storage/files.js +36 -34
- package/lib/emulator/storage/index.js +2 -2
- package/lib/emulator/storage/metadata.js +2 -2
- package/lib/emulator/storage/rules/runtime.js +8 -7
- package/lib/emulator/types.js +3 -0
- package/lib/ensureApiEnabled.js +5 -1
- package/lib/error.js +1 -1
- package/lib/extensions/askUserForParam.js +2 -2
- package/lib/extensions/changelog.js +3 -1
- package/lib/extensions/checkProjectBilling.js +1 -1
- package/lib/extensions/diagnose.js +56 -0
- package/lib/extensions/displayExtensionInfo.js +1 -1
- package/lib/extensions/emulator/optionsHelper.js +24 -8
- package/lib/extensions/emulator/specHelper.js +10 -23
- package/lib/extensions/export.js +1 -51
- package/lib/extensions/extensionsApi.js +1 -1
- package/lib/extensions/extensionsHelper.js +23 -10
- package/lib/extensions/listExtensions.js +2 -0
- package/lib/extensions/manifest.js +48 -0
- package/lib/extensions/metricsUtils.js +4 -4
- package/lib/extensions/paramHelper.js +4 -4
- package/lib/extensions/refs.js +1 -1
- package/lib/extensions/secretsUtils.js +4 -4
- package/lib/functional.js +1 -1
- package/lib/functions/env.js +7 -8
- package/lib/functions/secrets.js +112 -0
- package/lib/gcp/cloudfunctions.js +24 -5
- package/lib/gcp/cloudfunctionsv2.js +18 -5
- package/lib/gcp/cloudtasks.js +1 -1
- package/lib/gcp/docker.js +2 -2
- package/lib/gcp/run.js +2 -2
- package/lib/gcp/secretManager.js +128 -46
- package/lib/gcp/storage.js +1 -0
- package/lib/hosting/api.js +1 -1
- package/lib/hosting/functionsProxy.js +15 -5
- package/lib/hosting/proxy.js +2 -2
- package/lib/init/features/account.js +1 -1
- package/lib/management/database.js +1 -1
- package/lib/previews.js +1 -1
- package/lib/responseToError.js +16 -7
- package/lib/serve/functions.js +2 -2
- package/lib/serve/hosting.js +1 -1
- package/lib/utils.js +7 -2
- package/npm-shrinkwrap.json +904 -412
- package/package.json +3 -3
- package/schema/firebase-config.json +32 -0
- package/templates/init/functions/javascript/package.lint.json +3 -3
- package/templates/init/functions/javascript/package.nolint.json +2 -2
- package/templates/init/functions/typescript/package.lint.json +7 -7
- package/templates/init/functions/typescript/package.nolint.json +3 -3
|
@@ -25,15 +25,6 @@ const previews_1 = require("../previews");
|
|
|
25
25
|
marked.setOptions({
|
|
26
26
|
renderer: new TerminalRenderer(),
|
|
27
27
|
});
|
|
28
|
-
function isValidUpdate(existingSourceOrigin, newSourceOrigin) {
|
|
29
|
-
if (existingSourceOrigin === extensionsHelper_1.SourceOrigin.PUBLISHED_EXTENSION) {
|
|
30
|
-
return [extensionsHelper_1.SourceOrigin.PUBLISHED_EXTENSION, extensionsHelper_1.SourceOrigin.PUBLISHED_EXTENSION_VERSION].includes(newSourceOrigin);
|
|
31
|
-
}
|
|
32
|
-
else if (existingSourceOrigin === extensionsHelper_1.SourceOrigin.LOCAL) {
|
|
33
|
-
return [extensionsHelper_1.SourceOrigin.LOCAL, extensionsHelper_1.SourceOrigin.URL].includes(newSourceOrigin);
|
|
34
|
-
}
|
|
35
|
-
return false;
|
|
36
|
-
}
|
|
37
28
|
exports.default = new command_1.Command("ext:update <extensionInstanceId> [updateSource]")
|
|
38
29
|
.description(previews_1.previews.extdev
|
|
39
30
|
? "update an existing extension instance to the latest version or from a local or URL source"
|
|
@@ -44,6 +35,7 @@ exports.default = new command_1.Command("ext:update <extensionInstanceId> [updat
|
|
|
44
35
|
])
|
|
45
36
|
.before(extensionsHelper_1.ensureExtensionsApiEnabled)
|
|
46
37
|
.before(checkMinRequiredVersion_1.checkMinRequiredVersion, "extMinVersion")
|
|
38
|
+
.before(extensionsHelper_1.diagnoseAndFixProject)
|
|
47
39
|
.withForce()
|
|
48
40
|
.option("--params <paramsFile>", "name of params variables file with .env format.")
|
|
49
41
|
.action(async (instanceId, updateSource, options) => {
|
|
@@ -191,3 +183,12 @@ exports.default = new command_1.Command("ext:update <extensionInstanceId> [updat
|
|
|
191
183
|
throw err;
|
|
192
184
|
}
|
|
193
185
|
});
|
|
186
|
+
function isValidUpdate(existingSourceOrigin, newSourceOrigin) {
|
|
187
|
+
if (existingSourceOrigin === extensionsHelper_1.SourceOrigin.PUBLISHED_EXTENSION) {
|
|
188
|
+
return [extensionsHelper_1.SourceOrigin.PUBLISHED_EXTENSION, extensionsHelper_1.SourceOrigin.PUBLISHED_EXTENSION_VERSION].includes(newSourceOrigin);
|
|
189
|
+
}
|
|
190
|
+
else if (existingSourceOrigin === extensionsHelper_1.SourceOrigin.LOCAL) {
|
|
191
|
+
return [extensionsHelper_1.SourceOrigin.LOCAL, extensionsHelper_1.SourceOrigin.URL].includes(newSourceOrigin);
|
|
192
|
+
}
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
@@ -38,7 +38,7 @@ exports.default = new command_1.Command("functions:config:clone")
|
|
|
38
38
|
else if (options.only && options.except) {
|
|
39
39
|
throw new error_1.FirebaseError("Cannot use both --only and --except at the same time.");
|
|
40
40
|
}
|
|
41
|
-
let only
|
|
41
|
+
let only;
|
|
42
42
|
let except = [];
|
|
43
43
|
if (options.only) {
|
|
44
44
|
only = options.only.split(",");
|
|
@@ -96,7 +96,7 @@ exports.default = new command_1.Command("functions:config:export")
|
|
|
96
96
|
throw new error_1.FirebaseError("Exceeded max attempts to fix invalid config keys.");
|
|
97
97
|
}
|
|
98
98
|
const errMsg = configExport.hydrateEnvs(pInfos, prefix);
|
|
99
|
-
if (errMsg.length
|
|
99
|
+
if (errMsg.length === 0) {
|
|
100
100
|
break;
|
|
101
101
|
}
|
|
102
102
|
prefix = await promptForPrefix(errMsg);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const command_1 = require("../command");
|
|
4
|
+
const logger_1 = require("../logger");
|
|
5
|
+
const projectUtils_1 = require("../projectUtils");
|
|
6
|
+
const secretManager_1 = require("../gcp/secretManager");
|
|
7
|
+
exports.default = new command_1.Command("functions:secrets:access <KEY>[@version]")
|
|
8
|
+
.description("Access secret value given secret and its version. Defaults to accessing the latest version.")
|
|
9
|
+
.action(async (key, options) => {
|
|
10
|
+
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
11
|
+
let [name, version] = key.split("@");
|
|
12
|
+
if (!version) {
|
|
13
|
+
version = "latest";
|
|
14
|
+
}
|
|
15
|
+
const value = await (0, secretManager_1.accessSecretVersion)(projectId, name, version);
|
|
16
|
+
logger_1.logger.info(value);
|
|
17
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const command_1 = require("../command");
|
|
4
|
+
const logger_1 = require("../logger");
|
|
5
|
+
const projectUtils_1 = require("../projectUtils");
|
|
6
|
+
const secretManager_1 = require("../gcp/secretManager");
|
|
7
|
+
const prompt_1 = require("../prompt");
|
|
8
|
+
const secrets = require("../functions/secrets");
|
|
9
|
+
exports.default = new command_1.Command("functions:secrets:destroy <KEY>[@version]")
|
|
10
|
+
.description("Destroy a secret. Defaults to destroying the latest version.")
|
|
11
|
+
.withForce("Destroys a secret without confirmation.")
|
|
12
|
+
.action(async (key, options) => {
|
|
13
|
+
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
14
|
+
let [name, version] = key.split("@");
|
|
15
|
+
if (!version) {
|
|
16
|
+
version = "latest";
|
|
17
|
+
}
|
|
18
|
+
const sv = await (0, secretManager_1.getSecretVersion)(projectId, name, version);
|
|
19
|
+
if (!options.force) {
|
|
20
|
+
const confirm = await (0, prompt_1.promptOnce)({
|
|
21
|
+
name: "destroy",
|
|
22
|
+
type: "confirm",
|
|
23
|
+
default: true,
|
|
24
|
+
message: `Are you sure you want to destroy ${sv.secret.name}@${sv.versionId}`,
|
|
25
|
+
}, options);
|
|
26
|
+
if (!confirm) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
await (0, secretManager_1.destroySecretVersion)(projectId, name, version);
|
|
31
|
+
logger_1.logger.info(`Destroyed secret version ${name}@${sv.versionId}`);
|
|
32
|
+
const secret = await (0, secretManager_1.getSecret)(projectId, name);
|
|
33
|
+
if (secrets.isFirebaseManaged(secret)) {
|
|
34
|
+
const versions = await (0, secretManager_1.listSecretVersions)(projectId, name);
|
|
35
|
+
if (versions.filter((v) => v.state === "ENABLED").length === 0) {
|
|
36
|
+
logger_1.logger.info(`No active secret versions left. Destroying secret ${name}`);
|
|
37
|
+
await (0, secretManager_1.deleteSecret)(projectId, name);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const Table = require("cli-table");
|
|
4
|
+
const command_1 = require("../command");
|
|
5
|
+
const logger_1 = require("../logger");
|
|
6
|
+
const projectUtils_1 = require("../projectUtils");
|
|
7
|
+
const secretManager_1 = require("../gcp/secretManager");
|
|
8
|
+
exports.default = new command_1.Command("functions:secrets:get <KEY>")
|
|
9
|
+
.description("Get metadata for secret and its versions")
|
|
10
|
+
.action(async (key, options) => {
|
|
11
|
+
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
12
|
+
const versions = await (0, secretManager_1.listSecretVersions)(projectId, key);
|
|
13
|
+
const table = new Table({
|
|
14
|
+
head: ["Version", "State"],
|
|
15
|
+
style: { head: ["yellow"] },
|
|
16
|
+
});
|
|
17
|
+
for (const version of versions) {
|
|
18
|
+
table.push([version.versionId, version.state]);
|
|
19
|
+
}
|
|
20
|
+
logger_1.logger.info(table.toString());
|
|
21
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const backend = require("../deploy/functions/backend");
|
|
4
|
+
const command_1 = require("../command");
|
|
5
|
+
const projectUtils_1 = require("../projectUtils");
|
|
6
|
+
const secrets_1 = require("../functions/secrets");
|
|
7
|
+
const requirePermissions_1 = require("../requirePermissions");
|
|
8
|
+
const deploymentTool_1 = require("../deploymentTool");
|
|
9
|
+
const utils_1 = require("../utils");
|
|
10
|
+
const prompt_1 = require("../prompt");
|
|
11
|
+
const secretManager_1 = require("../gcp/secretManager");
|
|
12
|
+
exports.default = new command_1.Command("functions:secrets:prune")
|
|
13
|
+
.description("Destroys unused secrets")
|
|
14
|
+
.before(requirePermissions_1.requirePermissions, [
|
|
15
|
+
"cloudfunctions.functions.list",
|
|
16
|
+
"secretmanager.secrets.list",
|
|
17
|
+
"secretmanager.versions.list",
|
|
18
|
+
"secretmanager.versions.destroy",
|
|
19
|
+
])
|
|
20
|
+
.action(async (options) => {
|
|
21
|
+
const projectNumber = await (0, projectUtils_1.needProjectNumber)(options);
|
|
22
|
+
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
23
|
+
(0, utils_1.logBullet)("Loading secrets...");
|
|
24
|
+
const haveBackend = await backend.existingBackend({ projectId });
|
|
25
|
+
const haveEndpoints = backend
|
|
26
|
+
.allEndpoints(haveBackend)
|
|
27
|
+
.filter((e) => (0, deploymentTool_1.isFirebaseManaged)(e.labels || []));
|
|
28
|
+
const pruned = await (0, secrets_1.pruneSecrets)({ projectNumber, projectId }, haveEndpoints);
|
|
29
|
+
if (pruned.length === 0) {
|
|
30
|
+
(0, utils_1.logBullet)("All secrets are in use. Nothing to prune today.");
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
(0, utils_1.logBullet)(`Found ${pruned.length} unused active secret versions:\n\t` +
|
|
34
|
+
pruned.map((sv) => `${sv.secret}@${sv.version}`).join("\n\t"));
|
|
35
|
+
const confirm = await (0, prompt_1.promptOnce)({
|
|
36
|
+
name: "destroy",
|
|
37
|
+
type: "confirm",
|
|
38
|
+
default: true,
|
|
39
|
+
message: `Do you want to destroy unused secret versions?`,
|
|
40
|
+
}, options);
|
|
41
|
+
if (!confirm) {
|
|
42
|
+
(0, utils_1.logBullet)("Run the following commands to destroy each unused secret version:\n\t" +
|
|
43
|
+
pruned
|
|
44
|
+
.map((sv) => `firebase functions:secrets:destroy ${sv.secret}@${sv.version}`)
|
|
45
|
+
.join("\n\t"));
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
await Promise.all(pruned.map((sv) => (0, secretManager_1.destroySecretVersion)(projectId, sv.secret, sv.version)));
|
|
49
|
+
(0, utils_1.logSuccess)("Destroyed all unused secrets!");
|
|
50
|
+
});
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const tty = require("tty");
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const clc = require("cli-color");
|
|
6
|
+
const secrets_1 = require("../functions/secrets");
|
|
7
|
+
const command_1 = require("../command");
|
|
8
|
+
const requirePermissions_1 = require("../requirePermissions");
|
|
9
|
+
const prompt_1 = require("../prompt");
|
|
10
|
+
const utils_1 = require("../utils");
|
|
11
|
+
const projectUtils_1 = require("../projectUtils");
|
|
12
|
+
const secretManager_1 = require("../gcp/secretManager");
|
|
13
|
+
exports.default = new command_1.Command("functions:secrets:set <KEY>")
|
|
14
|
+
.description("Create or update a secret for use in Cloud Functions for Firebase")
|
|
15
|
+
.withForce("Does not ensure input keys are valid or upgrade existing secrets to have Firebase manage them.")
|
|
16
|
+
.before(requirePermissions_1.requirePermissions, [
|
|
17
|
+
"secretmanager.secrets.create",
|
|
18
|
+
"secretmanager.secrets.get",
|
|
19
|
+
"secretmanager.secrets.update",
|
|
20
|
+
"secretmanager.versions.add",
|
|
21
|
+
])
|
|
22
|
+
.option("--data-file <dataFile>", 'File path from which to read secret data. Set to "-" to read the secret data from stdin.')
|
|
23
|
+
.action(async (unvalidatedKey, options) => {
|
|
24
|
+
const projectId = (0, projectUtils_1.needProjectId)(options);
|
|
25
|
+
const key = await (0, secrets_1.ensureValidKey)(unvalidatedKey, options);
|
|
26
|
+
const secret = await (0, secrets_1.ensureSecret)(projectId, key, options);
|
|
27
|
+
let secretValue;
|
|
28
|
+
if ((!options.dataFile || options.dataFile === "-") && tty.isatty(0)) {
|
|
29
|
+
secretValue = await (0, prompt_1.promptOnce)({
|
|
30
|
+
name: key,
|
|
31
|
+
type: "password",
|
|
32
|
+
message: `Enter a value for ${key}`,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
let dataFile = 0;
|
|
37
|
+
if (options.dataFile && options.dataFile !== "-") {
|
|
38
|
+
dataFile = options.dataFile;
|
|
39
|
+
}
|
|
40
|
+
secretValue = fs.readFileSync(dataFile, "utf-8");
|
|
41
|
+
}
|
|
42
|
+
const secretVersion = await (0, secretManager_1.addVersion)(projectId, key, secretValue);
|
|
43
|
+
(0, utils_1.logSuccess)(`Created a new secret version ${(0, secretManager_1.toSecretVersionResourceName)(secretVersion)}`);
|
|
44
|
+
(0, utils_1.logBullet)("Please deploy your functions for the change to take effect by running:\n\t" +
|
|
45
|
+
clc.bold("firebase deploy --only functions"));
|
|
46
|
+
});
|
|
@@ -32,8 +32,8 @@ exports.default = new command_1.Command("hosting:clone <source> <targetChannel>"
|
|
|
32
32
|
if (sourceChannelId) {
|
|
33
33
|
sourceChannelId = (0, api_1.normalizeName)(sourceChannelId);
|
|
34
34
|
}
|
|
35
|
-
const equalSiteIds = sourceSiteId
|
|
36
|
-
const equalChannelIds = sourceChannelId
|
|
35
|
+
const equalSiteIds = sourceSiteId === targetSiteId;
|
|
36
|
+
const equalChannelIds = sourceChannelId === targetChannelId;
|
|
37
37
|
if (equalSiteIds && equalChannelIds) {
|
|
38
38
|
throw new error_1.FirebaseError(`Source and destination cannot be equal. Please pick a different source or desination.`);
|
|
39
39
|
}
|
|
@@ -67,7 +67,7 @@ exports.default = new command_1.Command("hosting:clone <source> <targetChannel>"
|
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
const currentTargetVersionName = (_d = (_c = tChannel.release) === null || _c === void 0 ? void 0 : _c.version) === null || _d === void 0 ? void 0 : _d.name;
|
|
70
|
-
if (equalSiteIds && sourceVersionName
|
|
70
|
+
if (equalSiteIds && sourceVersionName === currentTargetVersionName) {
|
|
71
71
|
utils.logSuccess(`Channels ${(0, cli_color_1.bold)(sourceChannelId)} and ${(0, cli_color_1.bold)(targetChannel)} are serving identical versions. No need to clone.`);
|
|
72
72
|
return;
|
|
73
73
|
}
|
package/lib/commands/index.js
CHANGED
|
@@ -91,9 +91,7 @@ module.exports = function (client) {
|
|
|
91
91
|
client.functions = {};
|
|
92
92
|
client.functions.config = {};
|
|
93
93
|
client.functions.config.clone = loadCommand("functions-config-clone");
|
|
94
|
-
|
|
95
|
-
client.functions.config.export = loadCommand("functions-config-export");
|
|
96
|
-
}
|
|
94
|
+
client.functions.config.export = loadCommand("functions-config-export");
|
|
97
95
|
client.functions.config.get = loadCommand("functions-config-get");
|
|
98
96
|
client.functions.config.set = loadCommand("functions-config-set");
|
|
99
97
|
client.functions.config.unset = loadCommand("functions-config-unset");
|
|
@@ -104,6 +102,12 @@ module.exports = function (client) {
|
|
|
104
102
|
if (previews.deletegcfartifacts) {
|
|
105
103
|
client.functions.deletegcfartifacts = loadCommand("functions-deletegcfartifacts");
|
|
106
104
|
}
|
|
105
|
+
client.functions.secrets = {};
|
|
106
|
+
client.functions.secrets.access = loadCommand("functions-secrets-access");
|
|
107
|
+
client.functions.secrets.destroy = loadCommand("functions-secrets-destroy");
|
|
108
|
+
client.functions.secrets.get = loadCommand("functions-secrets-get");
|
|
109
|
+
client.functions.secrets.prune = loadCommand("functions-secrets-prune");
|
|
110
|
+
client.functions.secrets.set = loadCommand("functions-secrets-set");
|
|
107
111
|
client.help = loadCommand("help");
|
|
108
112
|
client.hosting = {};
|
|
109
113
|
client.hosting.channel = {};
|
package/lib/commands/login.js
CHANGED
|
@@ -12,7 +12,7 @@ const auth = require("../auth");
|
|
|
12
12
|
const utils_1 = require("../utils");
|
|
13
13
|
module.exports = new command_1.Command("login")
|
|
14
14
|
.description("log the CLI into Firebase")
|
|
15
|
-
.option("--no-localhost", "
|
|
15
|
+
.option("--no-localhost", "login from a device without an accessible localhost")
|
|
16
16
|
.option("--reauth", "force reauthentication even if already logged in")
|
|
17
17
|
.action(async (options) => {
|
|
18
18
|
if (options.nonInteractive) {
|
|
@@ -15,7 +15,7 @@ const error_1 = require("../error");
|
|
|
15
15
|
const tableHead = ["Entry Name", "Value"];
|
|
16
16
|
const MAX_DISPLAY_ITEMS = 20;
|
|
17
17
|
function checkValidOptionalNumber(versionNumber) {
|
|
18
|
-
if (!versionNumber || typeof Number(versionNumber)
|
|
18
|
+
if (!versionNumber || typeof Number(versionNumber) === "number") {
|
|
19
19
|
return versionNumber;
|
|
20
20
|
}
|
|
21
21
|
throw new error_1.FirebaseError(`Could not interpret "${versionNumber}" as a valid number.`);
|
|
@@ -7,8 +7,8 @@ const humanReadable = (dep) => `${clc.bold(dep.instanceId)} (${dep.ref ? `${refs
|
|
|
7
7
|
exports.humanReadable = humanReadable;
|
|
8
8
|
const humanReadableUpdate = (from, to) => {
|
|
9
9
|
var _a, _b, _c, _d, _e;
|
|
10
|
-
if (((_a = from.ref) === null || _a === void 0 ? void 0 : _a.publisherId)
|
|
11
|
-
((_c = from.ref) === null || _c === void 0 ? void 0 : _c.extensionId)
|
|
10
|
+
if (((_a = from.ref) === null || _a === void 0 ? void 0 : _a.publisherId) === ((_b = to.ref) === null || _b === void 0 ? void 0 : _b.publisherId) &&
|
|
11
|
+
((_c = from.ref) === null || _c === void 0 ? void 0 : _c.extensionId) === ((_d = to.ref) === null || _d === void 0 ? void 0 : _d.extensionId)) {
|
|
12
12
|
return `\t${clc.bold(from.instanceId)} (${refs.toExtensionVersionRef(from.ref)} => ${(_e = to.ref) === null || _e === void 0 ? void 0 : _e.version})`;
|
|
13
13
|
}
|
|
14
14
|
else {
|
|
@@ -28,7 +28,7 @@ exports.createsSummary = createsSummary;
|
|
|
28
28
|
function updatesSummary(toUpdate, have) {
|
|
29
29
|
const instancesToUpdate = toUpdate
|
|
30
30
|
.map((to) => {
|
|
31
|
-
const from = have.find((exists) => exists.instanceId
|
|
31
|
+
const from = have.find((exists) => exists.instanceId === to.instanceId);
|
|
32
32
|
return humanReadableUpdate(from, to);
|
|
33
33
|
})
|
|
34
34
|
.join("\n");
|
|
@@ -13,6 +13,9 @@ function readParams(args) {
|
|
|
13
13
|
`${args.instanceId}.env.${args.projectNumber}`,
|
|
14
14
|
`${args.instanceId}.env.${args.projectId}`,
|
|
15
15
|
];
|
|
16
|
+
if (args.checkLocal) {
|
|
17
|
+
filesToCheck.push(`${args.instanceId}.env.local`);
|
|
18
|
+
}
|
|
16
19
|
let noFilesFound = true;
|
|
17
20
|
const combinedParams = {};
|
|
18
21
|
for (const fileToCheck of filesToCheck) {
|
|
@@ -58,6 +58,7 @@ async function want(args) {
|
|
|
58
58
|
projectId: args.projectId,
|
|
59
59
|
projectNumber: args.projectNumber,
|
|
60
60
|
aliases: args.aliases,
|
|
61
|
+
checkLocal: args.checkLocal,
|
|
61
62
|
});
|
|
62
63
|
const autoPopulatedParams = await (0, extensionsHelper_1.getFirebaseProjectParams)(args.projectId);
|
|
63
64
|
const subbedParams = (0, extensionsHelper_1.substituteParams)(params, autoPopulatedParams);
|
|
@@ -80,7 +81,7 @@ async function want(args) {
|
|
|
80
81
|
}
|
|
81
82
|
exports.want = want;
|
|
82
83
|
async function resolveVersion(ref) {
|
|
83
|
-
if (!ref.version || ref.version
|
|
84
|
+
if (!ref.version || ref.version === "latest") {
|
|
84
85
|
return "latest";
|
|
85
86
|
}
|
|
86
87
|
const extensionRef = refs.toExtensionRef(ref);
|
|
@@ -5,7 +5,7 @@ const clc = require("cli-color");
|
|
|
5
5
|
const extensionsApi = require("../../extensions/extensionsApi");
|
|
6
6
|
const refs = require("../../extensions/refs");
|
|
7
7
|
const utils = require("../../utils");
|
|
8
|
-
const isRetryable = (err) => err.status
|
|
8
|
+
const isRetryable = (err) => err.status === 429 || err.status === 409;
|
|
9
9
|
function extensionsDeploymentHandler(errorHandler) {
|
|
10
10
|
return async (task) => {
|
|
11
11
|
var _a, _b, _c, _d;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.compareFunctions = exports.missingEndpoint = exports.hasEndpoint = exports.regionalEndpoints = exports.matchingBackend = exports.someEndpoint = exports.allEndpoints = exports.checkAvailability = exports.existingBackend = exports.scheduleIdForFunction = exports.functionName = exports.isEmptyBackend = exports.of = exports.empty = exports.isTaskQueueTriggered = exports.isScheduleTriggered = exports.isEventTriggered = exports.isHttpsTriggered = exports.SCHEDULED_FUNCTION_LABEL = exports.MIN_MEMORY_FOR_CONCURRENCY = exports.DEFAULT_MEMORY = exports.memoryOptionDisplayName = exports.endpointTriggerType = void 0;
|
|
3
|
+
exports.compareFunctions = exports.missingEndpoint = exports.hasEndpoint = exports.regionalEndpoints = exports.matchingBackend = exports.findEndpoint = exports.someEndpoint = exports.allEndpoints = exports.checkAvailability = exports.existingBackend = exports.scheduleIdForFunction = exports.functionName = exports.isEmptyBackend = exports.of = exports.empty = exports.isTaskQueueTriggered = exports.isScheduleTriggered = exports.isEventTriggered = exports.isCallableTriggered = exports.isHttpsTriggered = exports.SCHEDULED_FUNCTION_LABEL = exports.MIN_MEMORY_FOR_CONCURRENCY = exports.DEFAULT_MEMORY = exports.memoryOptionDisplayName = exports.endpointTriggerType = void 0;
|
|
4
4
|
const gcf = require("../../gcp/cloudfunctions");
|
|
5
5
|
const gcfV2 = require("../../gcp/cloudfunctionsv2");
|
|
6
6
|
const utils = require("../../utils");
|
|
@@ -13,6 +13,9 @@ function endpointTriggerType(endpoint) {
|
|
|
13
13
|
else if (isHttpsTriggered(endpoint)) {
|
|
14
14
|
return "https";
|
|
15
15
|
}
|
|
16
|
+
else if (isCallableTriggered(endpoint)) {
|
|
17
|
+
return "callable";
|
|
18
|
+
}
|
|
16
19
|
else if (isEventTriggered(endpoint)) {
|
|
17
20
|
return endpoint.eventTrigger.eventType;
|
|
18
21
|
}
|
|
@@ -43,6 +46,10 @@ function isHttpsTriggered(triggered) {
|
|
|
43
46
|
return {}.hasOwnProperty.call(triggered, "httpsTrigger");
|
|
44
47
|
}
|
|
45
48
|
exports.isHttpsTriggered = isHttpsTriggered;
|
|
49
|
+
function isCallableTriggered(triggered) {
|
|
50
|
+
return {}.hasOwnProperty.call(triggered, "callableTrigger");
|
|
51
|
+
}
|
|
52
|
+
exports.isCallableTriggered = isCallableTriggered;
|
|
46
53
|
function isEventTriggered(triggered) {
|
|
47
54
|
return {}.hasOwnProperty.call(triggered, "eventTrigger");
|
|
48
55
|
}
|
|
@@ -57,7 +64,7 @@ function isTaskQueueTriggered(triggered) {
|
|
|
57
64
|
exports.isTaskQueueTriggered = isTaskQueueTriggered;
|
|
58
65
|
function empty() {
|
|
59
66
|
return {
|
|
60
|
-
requiredAPIs:
|
|
67
|
+
requiredAPIs: [],
|
|
61
68
|
endpoints: {},
|
|
62
69
|
environmentVariables: {},
|
|
63
70
|
};
|
|
@@ -76,7 +83,7 @@ function of(...endpoints) {
|
|
|
76
83
|
}
|
|
77
84
|
exports.of = of;
|
|
78
85
|
function isEmptyBackend(backend) {
|
|
79
|
-
return (Object.keys(backend.requiredAPIs).length
|
|
86
|
+
return (Object.keys(backend.requiredAPIs).length === 0 && Object.keys(backend.endpoints).length === 0);
|
|
80
87
|
}
|
|
81
88
|
exports.isEmptyBackend = isEmptyBackend;
|
|
82
89
|
function functionName(cloudFunction) {
|
|
@@ -140,7 +147,7 @@ async function checkAvailability(context, want) {
|
|
|
140
147
|
const gcfV1Regions = new Set();
|
|
141
148
|
const gcfV2Regions = new Set();
|
|
142
149
|
for (const ep of allEndpoints(want)) {
|
|
143
|
-
if (ep.platform
|
|
150
|
+
if (ep.platform === "gcfv1") {
|
|
144
151
|
gcfV1Regions.add(ep.region);
|
|
145
152
|
}
|
|
146
153
|
else {
|
|
@@ -186,6 +193,14 @@ function someEndpoint(backend, predicate) {
|
|
|
186
193
|
return false;
|
|
187
194
|
}
|
|
188
195
|
exports.someEndpoint = someEndpoint;
|
|
196
|
+
function findEndpoint(backend, predicate) {
|
|
197
|
+
for (const endpoints of Object.values(backend.endpoints)) {
|
|
198
|
+
const endpoint = Object.values(endpoints).find(predicate);
|
|
199
|
+
if (endpoint)
|
|
200
|
+
return endpoint;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
exports.findEndpoint = findEndpoint;
|
|
189
204
|
function matchingBackend(backend, predicate) {
|
|
190
205
|
const filtered = Object.assign({}, empty());
|
|
191
206
|
for (const endpoint of allEndpoints(backend)) {
|
|
@@ -211,7 +226,7 @@ const missingEndpoint = (backend) => (endpoint) => {
|
|
|
211
226
|
};
|
|
212
227
|
exports.missingEndpoint = missingEndpoint;
|
|
213
228
|
function compareFunctions(left, right) {
|
|
214
|
-
if (left.platform
|
|
229
|
+
if (left.platform !== right.platform) {
|
|
215
230
|
return right.platform < left.platform ? -1 : 1;
|
|
216
231
|
}
|
|
217
232
|
if (left.region < right.region) {
|
|
@@ -52,7 +52,7 @@ async function checkHttpIam(context, options, payload) {
|
|
|
52
52
|
return;
|
|
53
53
|
}
|
|
54
54
|
if (!passed) {
|
|
55
|
-
track("Error (User)", "deploy:functions:http_create_missing_iam");
|
|
55
|
+
void track("Error (User)", "deploy:functions:http_create_missing_iam");
|
|
56
56
|
throw new error_1.FirebaseError(`Missing required permission on project ${(0, cli_color_1.bold)(context.projectId)} to deploy new HTTPS functions. The permission ${(0, cli_color_1.bold)(PERMISSION)} is required to deploy the following functions:\n\n- ` +
|
|
57
57
|
newHttpsEndpoints.map((func) => func.id).join("\n- ") +
|
|
58
58
|
`\n\nTo address this error, please ask a project Owner to assign your account the "Cloud Functions Admin" role at the following URL:\n\nhttps://console.cloud.google.com/iam-admin/iam?project=${context.projectId}`);
|
|
@@ -71,7 +71,7 @@ async function cleanupBuildImages(haveFunctions, deletedFunctions, cleaners = {}
|
|
|
71
71
|
let message = "Unhandled error cleaning up build images. This could result in a small monthly bill if not corrected. ";
|
|
72
72
|
message +=
|
|
73
73
|
"You can attempt to delete these images by redeploying or you can delete them manually at";
|
|
74
|
-
if (failedDomains.size
|
|
74
|
+
if (failedDomains.size === 1) {
|
|
75
75
|
message += " " + failedDomains.values().next().value;
|
|
76
76
|
}
|
|
77
77
|
else {
|
|
@@ -205,7 +205,7 @@ async function listGcfPaths(projectId, locations, dockerHelpers = {}) {
|
|
|
205
205
|
.map((results) => results.children)
|
|
206
206
|
.reduce((acc, val) => [...acc, ...val], [])
|
|
207
207
|
.filter((loc) => locationsSet.has(loc));
|
|
208
|
-
if (failedSubdomains.length
|
|
208
|
+
if (failedSubdomains.length === subdomains.size) {
|
|
209
209
|
throw new error_1.FirebaseError("Failed to search all subdomains.");
|
|
210
210
|
}
|
|
211
211
|
else if (failedSubdomains.length > 0) {
|
|
@@ -237,7 +237,7 @@ async function deleteGcfArtifacts(projectId, locations, dockerHelpers = {}) {
|
|
|
237
237
|
}
|
|
238
238
|
});
|
|
239
239
|
await Promise.all(deleteLocations);
|
|
240
|
-
if (failedSubdomains.length
|
|
240
|
+
if (failedSubdomains.length === subdomains.size) {
|
|
241
241
|
throw new error_1.FirebaseError("Failed to search all subdomains.");
|
|
242
242
|
}
|
|
243
243
|
else if (failedSubdomains.length > 0) {
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.secretAccess = exports.maybeEnableAR = exports.cloudBuildEnabled = exports.defaultServiceAccount = void 0;
|
|
4
|
+
const clc = require("cli-color");
|
|
5
|
+
const ensureApiEnabled_1 = require("../../ensureApiEnabled");
|
|
6
|
+
const error_1 = require("../../error");
|
|
7
|
+
const utils_1 = require("../../utils");
|
|
8
|
+
const secretManager_1 = require("../../gcp/secretManager");
|
|
9
|
+
const previews_1 = require("../../previews");
|
|
10
|
+
const projects_1 = require("../../management/projects");
|
|
11
|
+
const functional_1 = require("../../functional");
|
|
12
|
+
const track = require("../../track");
|
|
13
|
+
const backend = require("./backend");
|
|
14
|
+
const ensureApiEnabled = require("../../ensureApiEnabled");
|
|
15
|
+
const FAQ_URL = "https://firebase.google.com/support/faq#functions-runtime";
|
|
16
|
+
const CLOUD_BUILD_API = "cloudbuild.googleapis.com";
|
|
17
|
+
async function defaultServiceAccount(e) {
|
|
18
|
+
const metadata = await (0, projects_1.getFirebaseProject)(e.project);
|
|
19
|
+
if (e.platform === "gcfv1") {
|
|
20
|
+
return `${metadata.projectId}@appspot.gserviceaccount.com`;
|
|
21
|
+
}
|
|
22
|
+
else if (e.platform === "gcfv2") {
|
|
23
|
+
return `${metadata.projectNumber}-compute@developer.gserviceaccount.com`;
|
|
24
|
+
}
|
|
25
|
+
(0, functional_1.assertExhaustive)(e.platform);
|
|
26
|
+
}
|
|
27
|
+
exports.defaultServiceAccount = defaultServiceAccount;
|
|
28
|
+
function nodeBillingError(projectId) {
|
|
29
|
+
void track("functions_runtime_notices", "nodejs10_billing_error");
|
|
30
|
+
return new error_1.FirebaseError(`Cloud Functions deployment requires the pay-as-you-go (Blaze) billing plan. To upgrade your project, visit the following URL:
|
|
31
|
+
|
|
32
|
+
https://console.firebase.google.com/project/${projectId}/usage/details
|
|
33
|
+
|
|
34
|
+
For additional information about this requirement, see Firebase FAQs:
|
|
35
|
+
|
|
36
|
+
${FAQ_URL}`, { exit: 1 });
|
|
37
|
+
}
|
|
38
|
+
function nodePermissionError(projectId) {
|
|
39
|
+
void track("functions_runtime_notices", "nodejs10_permission_error");
|
|
40
|
+
return new error_1.FirebaseError(`Cloud Functions deployment requires the Cloud Build API to be enabled. The current credentials do not have permission to enable APIs for project ${clc.bold(projectId)}.
|
|
41
|
+
|
|
42
|
+
Please ask a project owner to visit the following URL to enable Cloud Build:
|
|
43
|
+
|
|
44
|
+
https://console.cloud.google.com/apis/library/cloudbuild.googleapis.com?project=${projectId}
|
|
45
|
+
|
|
46
|
+
For additional information about this requirement, see Firebase FAQs:
|
|
47
|
+
${FAQ_URL}
|
|
48
|
+
`);
|
|
49
|
+
}
|
|
50
|
+
function isPermissionError(e) {
|
|
51
|
+
var _a, _b, _c;
|
|
52
|
+
return ((_c = (_b = (_a = e.context) === null || _a === void 0 ? void 0 : _a.body) === null || _b === void 0 ? void 0 : _b.error) === null || _c === void 0 ? void 0 : _c.status) === "PERMISSION_DENIED";
|
|
53
|
+
}
|
|
54
|
+
async function cloudBuildEnabled(projectId) {
|
|
55
|
+
try {
|
|
56
|
+
await (0, ensureApiEnabled_1.ensure)(projectId, CLOUD_BUILD_API, "functions");
|
|
57
|
+
}
|
|
58
|
+
catch (e) {
|
|
59
|
+
if ((0, error_1.isBillingError)(e)) {
|
|
60
|
+
throw nodeBillingError(projectId);
|
|
61
|
+
}
|
|
62
|
+
else if (isPermissionError(e)) {
|
|
63
|
+
throw nodePermissionError(projectId);
|
|
64
|
+
}
|
|
65
|
+
throw e;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
exports.cloudBuildEnabled = cloudBuildEnabled;
|
|
69
|
+
async function maybeEnableAR(projectId) {
|
|
70
|
+
if (!previews_1.previews.artifactregistry) {
|
|
71
|
+
return ensureApiEnabled.check(projectId, "artifactregistry.googleapis.com", "functions", true);
|
|
72
|
+
}
|
|
73
|
+
await ensureApiEnabled.ensure(projectId, "artifactregistry.googleapis.com", "functions");
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
exports.maybeEnableAR = maybeEnableAR;
|
|
77
|
+
async function secretsToServiceAccounts(b) {
|
|
78
|
+
const secretsToSa = {};
|
|
79
|
+
for (const e of backend.allEndpoints(b)) {
|
|
80
|
+
const sa = e.serviceAccountEmail || (await module.exports.defaultServiceAccount(e));
|
|
81
|
+
for (const s of e.secretEnvironmentVariables || []) {
|
|
82
|
+
const serviceAccounts = secretsToSa[s.secret] || new Set();
|
|
83
|
+
serviceAccounts.add(sa);
|
|
84
|
+
secretsToSa[s.secret] = serviceAccounts;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return secretsToSa;
|
|
88
|
+
}
|
|
89
|
+
async function secretAccess(projectId, wantBackend, haveBackend) {
|
|
90
|
+
var _a, _b;
|
|
91
|
+
const ensureAccess = async (secret, serviceAccounts) => {
|
|
92
|
+
(0, utils_1.logLabeledBullet)("functions", `ensuring ${clc.bold(serviceAccounts.join(", "))} access to secret ${clc.bold(secret)}.`);
|
|
93
|
+
await (0, secretManager_1.ensureServiceAgentRole)({ name: secret, projectId }, serviceAccounts, "roles/secretmanager.secretAccessor");
|
|
94
|
+
(0, utils_1.logLabeledSuccess)("functions", `ensured ${clc.bold(serviceAccounts.join(", "))} access to ${clc.bold(secret)}.`);
|
|
95
|
+
};
|
|
96
|
+
const wantSecrets = await secretsToServiceAccounts(wantBackend);
|
|
97
|
+
const haveSecrets = await secretsToServiceAccounts(haveBackend);
|
|
98
|
+
for (const [secret, serviceAccounts] of Object.entries(haveSecrets)) {
|
|
99
|
+
for (const serviceAccount of serviceAccounts) {
|
|
100
|
+
(_a = wantSecrets[secret]) === null || _a === void 0 ? void 0 : _a.delete(serviceAccount);
|
|
101
|
+
}
|
|
102
|
+
if (((_b = wantSecrets[secret]) === null || _b === void 0 ? void 0 : _b.size) === 0) {
|
|
103
|
+
delete wantSecrets[secret];
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
const ensure = [];
|
|
107
|
+
for (const [secret, serviceAccounts] of Object.entries(wantSecrets)) {
|
|
108
|
+
ensure.push(ensureAccess(secret, Array.from(serviceAccounts)));
|
|
109
|
+
}
|
|
110
|
+
await Promise.all(ensure);
|
|
111
|
+
}
|
|
112
|
+
exports.secretAccess = secretAccess;
|
|
@@ -1,50 +1 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ensureCloudBuildEnabled = void 0;
|
|
4
|
-
const cli_color_1 = require("cli-color");
|
|
5
|
-
const track = require("../../track");
|
|
6
|
-
const ensureApiEnabled_1 = require("../../ensureApiEnabled");
|
|
7
|
-
const error_1 = require("../../error");
|
|
8
|
-
const FAQ_URL = "https://firebase.google.com/support/faq#functions-runtime";
|
|
9
|
-
const CLOUD_BUILD_API = "cloudbuild.googleapis.com";
|
|
10
|
-
function nodeBillingError(projectId) {
|
|
11
|
-
track("functions_runtime_notices", "nodejs10_billing_error");
|
|
12
|
-
return new error_1.FirebaseError(`Cloud Functions deployment requires the pay-as-you-go (Blaze) billing plan. To upgrade your project, visit the following URL:
|
|
13
|
-
|
|
14
|
-
https://console.firebase.google.com/project/${projectId}/usage/details
|
|
15
|
-
|
|
16
|
-
For additional information about this requirement, see Firebase FAQs:
|
|
17
|
-
|
|
18
|
-
${FAQ_URL}`, { exit: 1 });
|
|
19
|
-
}
|
|
20
|
-
function nodePermissionError(projectId) {
|
|
21
|
-
track("functions_runtime_notices", "nodejs10_permission_error");
|
|
22
|
-
return new error_1.FirebaseError(`Cloud Functions deployment requires the Cloud Build API to be enabled. The current credentials do not have permission to enable APIs for project ${(0, cli_color_1.bold)(projectId)}.
|
|
23
|
-
|
|
24
|
-
Please ask a project owner to visit the following URL to enable Cloud Build:
|
|
25
|
-
|
|
26
|
-
https://console.cloud.google.com/apis/library/cloudbuild.googleapis.com?project=${projectId}
|
|
27
|
-
|
|
28
|
-
For additional information about this requirement, see Firebase FAQs:
|
|
29
|
-
${FAQ_URL}
|
|
30
|
-
`);
|
|
31
|
-
}
|
|
32
|
-
function isPermissionError(e) {
|
|
33
|
-
var _a, _b, _c;
|
|
34
|
-
return ((_c = (_b = (_a = e.context) === null || _a === void 0 ? void 0 : _a.body) === null || _b === void 0 ? void 0 : _b.error) === null || _c === void 0 ? void 0 : _c.status) === "PERMISSION_DENIED";
|
|
35
|
-
}
|
|
36
|
-
async function ensureCloudBuildEnabled(projectId) {
|
|
37
|
-
try {
|
|
38
|
-
await (0, ensureApiEnabled_1.ensure)(projectId, CLOUD_BUILD_API, "functions");
|
|
39
|
-
}
|
|
40
|
-
catch (e) {
|
|
41
|
-
if ((0, error_1.isBillingError)(e)) {
|
|
42
|
-
throw nodeBillingError(projectId);
|
|
43
|
-
}
|
|
44
|
-
else if (isPermissionError(e)) {
|
|
45
|
-
throw nodePermissionError(projectId);
|
|
46
|
-
}
|
|
47
|
-
throw e;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
exports.ensureCloudBuildEnabled = ensureCloudBuildEnabled;
|