firebase-tools 13.6.0 → 13.6.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.
Files changed (49) hide show
  1. package/lib/apphosting/config.js +31 -0
  2. package/lib/apphosting/githubConnections.js +261 -0
  3. package/lib/{init/features/apphosting → apphosting}/index.js +21 -17
  4. package/lib/{init/features/apphosting → apphosting}/repo.js +9 -9
  5. package/lib/apphosting/secrets/dialogs.js +169 -0
  6. package/lib/apphosting/secrets/index.js +98 -0
  7. package/lib/commands/apphosting-backends-create.js +4 -2
  8. package/lib/commands/apphosting-backends-delete.js +1 -1
  9. package/lib/commands/apphosting-secrets-describe.js +29 -0
  10. package/lib/commands/apphosting-secrets-grantaccess.js +45 -0
  11. package/lib/commands/apphosting-secrets-set.js +102 -0
  12. package/lib/commands/functions-secrets-access.js +2 -2
  13. package/lib/commands/functions-secrets-describe.js +14 -0
  14. package/lib/commands/functions-secrets-destroy.js +2 -2
  15. package/lib/commands/functions-secrets-get.js +3 -17
  16. package/lib/commands/functions-secrets-prune.js +2 -1
  17. package/lib/commands/functions-secrets-set.js +2 -2
  18. package/lib/commands/index.js +5 -0
  19. package/lib/deploy/functions/checkIam.js +3 -6
  20. package/lib/deploy/functions/containerCleaner.js +1 -11
  21. package/lib/deploy/functions/params.js +2 -2
  22. package/lib/deploy/functions/prepare.js +12 -3
  23. package/lib/deploy/functions/release/fabricator.js +5 -5
  24. package/lib/deploy/functions/runtimes/index.js +6 -43
  25. package/lib/deploy/functions/runtimes/node/index.js +3 -2
  26. package/lib/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.js +15 -34
  27. package/lib/deploy/functions/runtimes/python/index.js +11 -7
  28. package/lib/deploy/functions/runtimes/supported.js +135 -0
  29. package/lib/emulator/controller.js +8 -1
  30. package/lib/emulator/functionsEmulator.js +2 -2
  31. package/lib/emulator/hub.js +5 -0
  32. package/lib/extensions/emulator/specHelper.js +4 -3
  33. package/lib/functional.js +2 -2
  34. package/lib/functions/secrets.js +40 -22
  35. package/lib/gcp/apphosting.js +15 -2
  36. package/lib/gcp/cloudbuild.js +7 -3
  37. package/lib/gcp/cloudfunctions.js +5 -5
  38. package/lib/gcp/cloudfunctionsv2.js +3 -3
  39. package/lib/gcp/cloudscheduler.js +2 -2
  40. package/lib/gcp/computeEngine.js +7 -0
  41. package/lib/gcp/devConnect.js +24 -11
  42. package/lib/gcp/iam.js +9 -1
  43. package/lib/gcp/secretManager.js +53 -13
  44. package/lib/gcp/serviceusage.js +21 -5
  45. package/lib/init/features/functions/python.js +4 -3
  46. package/lib/init/features/index.js +1 -1
  47. package/package.json +1 -1
  48. package/schema/firebase-config.json +12 -2
  49. /package/lib/{init/features/apphosting → apphosting}/constants.js +0 -0
@@ -4,17 +4,19 @@ exports.command = void 0;
4
4
  const command_1 = require("../command");
5
5
  const projectUtils_1 = require("../projectUtils");
6
6
  const requireInteractive_1 = require("../requireInteractive");
7
- const apphosting_1 = require("../init/features/apphosting");
7
+ const apphosting_1 = require("../apphosting");
8
8
  const apphosting_2 = require("../gcp/apphosting");
9
9
  exports.command = new command_1.Command("apphosting:backends:create")
10
10
  .description("create a backend in a Firebase project")
11
11
  .option("-l, --location <location>", "specify the region of the backend", "")
12
12
  .option("-s, --service-account <serviceAccount>", "specify the service account used to run the server", "")
13
+ .option("-w, --with-dev-connect", "use the Developer Connect flow insetad of Cloud Build Repositories (testing)", false)
13
14
  .before(apphosting_2.ensureApiEnabled)
14
15
  .before(requireInteractive_1.default)
15
16
  .action(async (options) => {
16
17
  const projectId = (0, projectUtils_1.needProjectId)(options);
17
18
  const location = options.location;
18
19
  const serviceAccount = options.serviceAccount;
19
- await (0, apphosting_1.doSetup)(projectId, location, serviceAccount);
20
+ const withDevConnect = options.withDevConnect;
21
+ await (0, apphosting_1.doSetup)(projectId, location, serviceAccount, withDevConnect);
20
22
  });
@@ -5,7 +5,7 @@ const command_1 = require("../command");
5
5
  const projectUtils_1 = require("../projectUtils");
6
6
  const error_1 = require("../error");
7
7
  const prompt_1 = require("../prompt");
8
- const constants_1 = require("../init/features/apphosting/constants");
8
+ const constants_1 = require("../apphosting/constants");
9
9
  const utils = require("../utils");
10
10
  const apphosting = require("../gcp/apphosting");
11
11
  const apphosting_backends_list_1 = require("./apphosting-backends-list");
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.command = void 0;
4
+ const command_1 = require("../command");
5
+ const projectUtils_1 = require("../projectUtils");
6
+ const logger_1 = require("../logger");
7
+ const requireAuth_1 = require("../requireAuth");
8
+ const secretManager_1 = require("../gcp/secretManager");
9
+ const secretManager = require("../gcp/secretManager");
10
+ const requirePermissions_1 = require("../requirePermissions");
11
+ const Table = require("cli-table");
12
+ exports.command = new command_1.Command("apphosting:secrets:describe <secretName>")
13
+ .description("Get metadata for secret and its versions.")
14
+ .before(requireAuth_1.requireAuth)
15
+ .before(secretManager.ensureApi)
16
+ .before(requirePermissions_1.requirePermissions, ["secretmanager.secrets.get"])
17
+ .action(async (secretName, options) => {
18
+ const projectId = (0, projectUtils_1.needProjectId)(options);
19
+ const versions = await (0, secretManager_1.listSecretVersions)(projectId, secretName);
20
+ const table = new Table({
21
+ head: ["Name", "Version", "Status", "Create Time"],
22
+ style: { head: ["yellow"] },
23
+ });
24
+ for (const version of versions) {
25
+ table.push([secretName, version.versionId, version.state, version.createTime]);
26
+ }
27
+ logger_1.logger.info(table.toString());
28
+ return { secrets: versions };
29
+ });
@@ -0,0 +1,45 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.command = void 0;
4
+ const command_1 = require("../command");
5
+ const projectUtils_1 = require("../projectUtils");
6
+ const error_1 = require("../error");
7
+ const requireAuth_1 = require("../requireAuth");
8
+ const secretManager = require("../gcp/secretManager");
9
+ const requirePermissions_1 = require("../requirePermissions");
10
+ const apphosting = require("../gcp/apphosting");
11
+ const secrets = require("../apphosting/secrets");
12
+ exports.command = new command_1.Command("apphosting:secrets:grantaccess <secretName>")
13
+ .description("grant service accounts permissions to the provided secret")
14
+ .option("-l, --location <location>", "app backend location")
15
+ .option("-b, --backend <backend>", "app backend name")
16
+ .before(requireAuth_1.requireAuth)
17
+ .before(secretManager.ensureApi)
18
+ .before(apphosting.ensureApiEnabled)
19
+ .before(requirePermissions_1.requirePermissions, [
20
+ "secretmanager.secrets.create",
21
+ "secretmanager.secrets.get",
22
+ "secretmanager.secrets.update",
23
+ "secretmanager.versions.add",
24
+ "secretmanager.secrets.getIamPolicy",
25
+ "secretmanager.secrets.setIamPolicy",
26
+ ])
27
+ .action(async (secretName, options) => {
28
+ const projectId = (0, projectUtils_1.needProjectId)(options);
29
+ const projectNumber = await (0, projectUtils_1.needProjectNumber)(options);
30
+ if (!options.location) {
31
+ throw new error_1.FirebaseError("Missing required flag --location. See firebase apphosting:secrets:grantaccess --help for more info");
32
+ }
33
+ const location = options.location;
34
+ if (!options.backend) {
35
+ throw new error_1.FirebaseError("Missing required flag --backend. See firebase apphosting:secrets:grantaccess --help for more info");
36
+ }
37
+ const exists = await secretManager.secretExists(projectId, secretName);
38
+ if (!exists) {
39
+ throw new error_1.FirebaseError(`Cannot find secret ${secretName}`);
40
+ }
41
+ const backendId = options.backend;
42
+ const backend = await apphosting.getBackend(projectId, location, backendId);
43
+ const accounts = secrets.toMulti(secrets.serviceAccountsForBackend(projectNumber, backend));
44
+ await secrets.grantSecretAccess(projectId, secretName, accounts);
45
+ });
@@ -0,0 +1,102 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.command = void 0;
4
+ const tty = require("tty");
5
+ const clc = require("colorette");
6
+ const path_1 = require("path");
7
+ const command_1 = require("../command");
8
+ const projectUtils_1 = require("../projectUtils");
9
+ const requireAuth_1 = require("../requireAuth");
10
+ const fs = require("fs");
11
+ const gcsm = require("../gcp/secretManager");
12
+ const apphosting = require("../gcp/apphosting");
13
+ const requirePermissions_1 = require("../requirePermissions");
14
+ const prompt_1 = require("../prompt");
15
+ const secrets = require("../apphosting/secrets");
16
+ const dialogs = require("../apphosting/secrets/dialogs");
17
+ const config = require("../apphosting/config");
18
+ const utils_1 = require("../utils");
19
+ exports.command = new command_1.Command("apphosting:secrets:set <secretName>")
20
+ .description("grant service accounts permissions to the provided secret")
21
+ .option("-l, --location <location>", "optional location to retrict secret replication")
22
+ .withForce("Automatically create a secret, grant permissions, and add to YAML.")
23
+ .before(requireAuth_1.requireAuth)
24
+ .before(gcsm.ensureApi)
25
+ .before(apphosting.ensureApiEnabled)
26
+ .before(requirePermissions_1.requirePermissions, [
27
+ "secretmanager.secrets.create",
28
+ "secretmanager.secrets.get",
29
+ "secretmanager.secrets.update",
30
+ "secretmanager.versions.add",
31
+ "secretmanager.secrets.getIamPolicy",
32
+ "secretmanager.secrets.setIamPolicy",
33
+ ])
34
+ .option("--data-file <dataFile>", 'File path from which to read secret data. Set to "-" to read the secret data from stdin.')
35
+ .action(async (secretName, options) => {
36
+ var _a;
37
+ const howToAccess = `You can access the contents of the secret's latest value with ${clc.bold(`firebase apphosting:secrets:access ${secretName}`)}`;
38
+ const projectId = (0, projectUtils_1.needProjectId)(options);
39
+ const projectNumber = await (0, projectUtils_1.needProjectNumber)(options);
40
+ const created = secrets.upsertSecret(projectId, secretName, options.location);
41
+ if (created === null) {
42
+ return;
43
+ }
44
+ let secretValue;
45
+ if ((!options.dataFile || options.dataFile === "-") && tty.isatty(0)) {
46
+ secretValue = await (0, prompt_1.promptOnce)({
47
+ type: "password",
48
+ message: `Enter a value for ${secretName}`,
49
+ });
50
+ }
51
+ else {
52
+ let dataFile = 0;
53
+ if (options.dataFile && options.dataFile !== "-") {
54
+ dataFile = options.dataFile;
55
+ }
56
+ secretValue = fs.readFileSync(dataFile, "utf-8");
57
+ }
58
+ if (!created) {
59
+ const version = await gcsm.addVersion(projectId, secretName, secretValue);
60
+ (0, utils_1.logSuccess)(`Created new secret version ${gcsm.toSecretVersionResourceName(version)}`);
61
+ (0, utils_1.logSuccess)(howToAccess);
62
+ return;
63
+ }
64
+ (0, utils_1.logSuccess)(`Created new secret projects/${projectId}/secrets/${secretName}`);
65
+ (0, utils_1.logSuccess)(howToAccess);
66
+ const accounts = await dialogs.selectBackendServiceAccounts(projectNumber, projectId, options);
67
+ if (!accounts.buildServiceAccounts.length && !accounts.runServiceAccounts.length) {
68
+ (0, utils_1.logWarning)(`To use this secret your backend, you must grant access. You can do so in the future with ${clc.bold("firebase apphosting:secrets:grantAccess")}`);
69
+ }
70
+ else {
71
+ await secrets.grantSecretAccess(projectId, secretName, accounts);
72
+ }
73
+ let path = config.yamlPath(process.cwd());
74
+ let yaml = {};
75
+ if (path) {
76
+ yaml = config.load(path);
77
+ if ((_a = yaml.env) === null || _a === void 0 ? void 0 : _a.find((env) => env.variable === secretName)) {
78
+ return;
79
+ }
80
+ }
81
+ const addToYaml = await (0, prompt_1.confirm)({
82
+ message: "Would you like to add this secret to apphosting.yaml?",
83
+ default: true,
84
+ });
85
+ if (!addToYaml) {
86
+ return;
87
+ }
88
+ if (!path) {
89
+ path = await (0, prompt_1.promptOnce)({
90
+ message: "It looks like you don't have an apphosting.yaml yet. Where would you like to store it?",
91
+ default: process.cwd(),
92
+ });
93
+ path = (0, path_1.join)(path, "apphosting.yaml");
94
+ }
95
+ const envName = await dialogs.envVarForSecret(secretName);
96
+ yaml.env = yaml.env || [];
97
+ yaml.env.push({
98
+ variable: envName,
99
+ secret: secretName,
100
+ });
101
+ config.store(path, yaml);
102
+ });
@@ -6,11 +6,11 @@ const logger_1 = require("../logger");
6
6
  const projectUtils_1 = require("../projectUtils");
7
7
  const secretManager_1 = require("../gcp/secretManager");
8
8
  const requireAuth_1 = require("../requireAuth");
9
- const secrets = require("../functions/secrets");
9
+ const secretManager = require("../gcp/secretManager");
10
10
  exports.command = new command_1.Command("functions:secrets:access <KEY>[@version]")
11
11
  .description("Access secret value given secret and its version. Defaults to accessing the latest version.")
12
12
  .before(requireAuth_1.requireAuth)
13
- .before(secrets.ensureApi)
13
+ .before(secretManager.ensureApi)
14
14
  .action(async (key, options) => {
15
15
  const projectId = (0, projectUtils_1.needProjectId)(options);
16
16
  let [name, version] = key.split("@");
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.command = void 0;
4
+ const requireAuth_1 = require("../requireAuth");
5
+ const command_1 = require("../command");
6
+ const requirePermissions_1 = require("../requirePermissions");
7
+ const secretManager = require("../gcp/secretManager");
8
+ const secrets = require("../functions/secrets");
9
+ exports.command = new command_1.Command("functions:secrets:describe <KEY>")
10
+ .description("Get metadata for secret and its versions. Alias for functions:secrets:get to align with gcloud")
11
+ .before(requireAuth_1.requireAuth)
12
+ .before(secretManager.ensureApi)
13
+ .before(requirePermissions_1.requirePermissions, ["secretmanager.secrets.get"])
14
+ .action(secrets.describeSecret);
@@ -13,7 +13,7 @@ exports.command = new command_1.Command("functions:secrets:destroy <KEY>[@versio
13
13
  .description("Destroy a secret. Defaults to destroying the latest version.")
14
14
  .withForce("Destroys a secret without confirmation.")
15
15
  .before(requireAuth_1.requireAuth)
16
- .before(secrets.ensureApi)
16
+ .before(secretManager_1.ensureApi)
17
17
  .action(async (key, options) => {
18
18
  const projectId = (0, projectUtils_1.needProjectId)(options);
19
19
  const projectNumber = await (0, projectUtils_1.needProjectNumber)(options);
@@ -54,7 +54,7 @@ exports.command = new command_1.Command("functions:secrets:destroy <KEY>[@versio
54
54
  await (0, secretManager_1.destroySecretVersion)(projectId, name, version);
55
55
  (0, utils_1.logBullet)(`Destroyed secret version ${name}@${sv.versionId}`);
56
56
  const secret = await (0, secretManager_1.getSecret)(projectId, name);
57
- if (secrets.isFirebaseManaged(secret)) {
57
+ if ((0, secretManager_1.isFunctionsManaged)(secret)) {
58
58
  const versions = await (0, secretManager_1.listSecretVersions)(projectId, name);
59
59
  if (versions.filter((v) => v.state === "ENABLED").length === 0) {
60
60
  (0, utils_1.logBullet)(`No active secret versions left. Destroying secret ${name}`);
@@ -1,28 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.command = void 0;
4
- const Table = require("cli-table");
5
4
  const requireAuth_1 = require("../requireAuth");
6
5
  const command_1 = require("../command");
7
- const logger_1 = require("../logger");
8
- const projectUtils_1 = require("../projectUtils");
9
- const secretManager_1 = require("../gcp/secretManager");
10
6
  const requirePermissions_1 = require("../requirePermissions");
7
+ const secretManager = require("../gcp/secretManager");
11
8
  const secrets = require("../functions/secrets");
12
9
  exports.command = new command_1.Command("functions:secrets:get <KEY>")
13
10
  .description("Get metadata for secret and its versions")
14
11
  .before(requireAuth_1.requireAuth)
15
- .before(secrets.ensureApi)
12
+ .before(secretManager.ensureApi)
16
13
  .before(requirePermissions_1.requirePermissions, ["secretmanager.secrets.get"])
17
- .action(async (key, options) => {
18
- const projectId = (0, projectUtils_1.needProjectId)(options);
19
- const versions = await (0, secretManager_1.listSecretVersions)(projectId, key);
20
- const table = new Table({
21
- head: ["Version", "State"],
22
- style: { head: ["yellow"] },
23
- });
24
- for (const version of versions) {
25
- table.push([version.versionId, version.state]);
26
- }
27
- logger_1.logger.info(table.toString());
28
- });
14
+ .action(secrets.describeSecret);
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.command = void 0;
4
4
  const backend = require("../deploy/functions/backend");
5
5
  const secrets = require("../functions/secrets");
6
+ const secretManager = require("../gcp/secretManager");
6
7
  const command_1 = require("../command");
7
8
  const projectUtils_1 = require("../projectUtils");
8
9
  const requirePermissions_1 = require("../requirePermissions");
@@ -15,7 +16,7 @@ exports.command = new command_1.Command("functions:secrets:prune")
15
16
  .withForce("Destroys unused secrets without prompt")
16
17
  .description("Destroys unused secrets")
17
18
  .before(requireAuth_1.requireAuth)
18
- .before(secrets.ensureApi)
19
+ .before(secretManager.ensureApi)
19
20
  .before(requirePermissions_1.requirePermissions, [
20
21
  "cloudfunctions.functions.list",
21
22
  "secretmanager.secrets.list",
@@ -20,7 +20,7 @@ exports.command = new command_1.Command("functions:secrets:set <KEY>")
20
20
  .description("Create or update a secret for use in Cloud Functions for Firebase.")
21
21
  .withForce("Automatically updates functions to use the new secret.")
22
22
  .before(requireAuth_1.requireAuth)
23
- .before(secrets.ensureApi)
23
+ .before(secretManager_1.ensureApi)
24
24
  .before(requirePermissions_1.requirePermissions, [
25
25
  "secretmanager.secrets.create",
26
26
  "secretmanager.secrets.get",
@@ -50,7 +50,7 @@ exports.command = new command_1.Command("functions:secrets:set <KEY>")
50
50
  }
51
51
  const secretVersion = await (0, secretManager_1.addVersion)(projectId, key, secretValue);
52
52
  (0, utils_1.logSuccess)(`Created a new secret version ${(0, secretManager_1.toSecretVersionResourceName)(secretVersion)}`);
53
- if (!secrets.isFirebaseManaged(secret)) {
53
+ if (!(0, secretManager_1.isFunctionsManaged)(secret)) {
54
54
  (0, utils_1.logBullet)("Please deploy your functions for the change to take effect by running:\n\t" +
55
55
  clc.bold("firebase deploy --only functions"));
56
56
  return;
@@ -129,6 +129,7 @@ function load(client) {
129
129
  client.functions.secrets.access = loadCommand("functions-secrets-access");
130
130
  client.functions.secrets.destroy = loadCommand("functions-secrets-destroy");
131
131
  client.functions.secrets.get = loadCommand("functions-secrets-get");
132
+ client.functions.secrets.describe = loadCommand("functions-secrets-describe");
132
133
  client.functions.secrets.prune = loadCommand("functions-secrets-prune");
133
134
  client.functions.secrets.set = loadCommand("functions-secrets-set");
134
135
  client.help = loadCommand("help");
@@ -164,6 +165,10 @@ function load(client) {
164
165
  client.apphosting.builds = {};
165
166
  client.apphosting.builds.get = loadCommand("apphosting-builds-get");
166
167
  client.apphosting.builds.create = loadCommand("apphosting-builds-create");
168
+ client.apphosting.secrets = {};
169
+ client.apphosting.secrets.set = loadCommand("apphosting-secrets-set");
170
+ client.apphosting.secrets.grantaccess = loadCommand("apphosting-secrets-grantaccess");
171
+ client.apphosting.secrets.describe = loadCommand("apphosting-secrets-describe");
167
172
  client.apphosting.rollouts = {};
168
173
  client.apphosting.rollouts.create = loadCommand("apphosting-rollouts-create");
169
174
  client.apphosting.rollouts.list = loadCommand("apphosting-rollouts-list");
@@ -1,12 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ensureServiceAgentRoles = exports.mergeBindings = exports.obtainDefaultComputeServiceAgentBindings = exports.obtainPubSubServiceAgentBindings = exports.getDefaultComputeServiceAgent = exports.checkHttpIam = exports.checkServiceAccountIam = exports.EVENTARC_EVENT_RECEIVER_ROLE = exports.RUN_INVOKER_ROLE = exports.SERVICE_ACCOUNT_TOKEN_CREATOR_ROLE = void 0;
3
+ exports.ensureServiceAgentRoles = exports.mergeBindings = exports.obtainDefaultComputeServiceAgentBindings = exports.obtainPubSubServiceAgentBindings = exports.checkHttpIam = exports.checkServiceAccountIam = exports.EVENTARC_EVENT_RECEIVER_ROLE = exports.RUN_INVOKER_ROLE = exports.SERVICE_ACCOUNT_TOKEN_CREATOR_ROLE = void 0;
4
4
  const colorette_1 = require("colorette");
5
5
  const logger_1 = require("../../logger");
6
6
  const functionsDeployHelper_1 = require("./functionsDeployHelper");
7
7
  const error_1 = require("../../error");
8
8
  const functional_1 = require("../../functional");
9
9
  const iam = require("../../gcp/iam");
10
+ const gce = require("../../gcp/computeEngine");
10
11
  const backend = require("./backend");
11
12
  const track_1 = require("../../track");
12
13
  const utils = require("../../utils");
@@ -73,10 +74,6 @@ exports.checkHttpIam = checkHttpIam;
73
74
  function getPubsubServiceAgent(projectNumber) {
74
75
  return `service-${projectNumber}@gcp-sa-pubsub.iam.gserviceaccount.com`;
75
76
  }
76
- function getDefaultComputeServiceAgent(projectNumber) {
77
- return `${projectNumber}-compute@developer.gserviceaccount.com`;
78
- }
79
- exports.getDefaultComputeServiceAgent = getDefaultComputeServiceAgent;
80
77
  function reduceEventsToServices(services, endpoint) {
81
78
  const service = (0, services_1.serviceForEndpoint)(endpoint);
82
79
  if (service.requiredProjectBindings && !services.find((s) => s.name === service.name)) {
@@ -93,7 +90,7 @@ function obtainPubSubServiceAgentBindings(projectNumber) {
93
90
  }
94
91
  exports.obtainPubSubServiceAgentBindings = obtainPubSubServiceAgentBindings;
95
92
  function obtainDefaultComputeServiceAgentBindings(projectNumber) {
96
- const defaultComputeServiceAgent = `serviceAccount:${getDefaultComputeServiceAgent(projectNumber)}`;
93
+ const defaultComputeServiceAgent = `serviceAccount:${gce.getDefaultServiceAccount(projectNumber)}`;
97
94
  const runInvokerBinding = {
98
95
  role: exports.RUN_INVOKER_ROLE,
99
96
  members: [defaultComputeServiceAgent],
@@ -49,7 +49,7 @@ async function cleanupBuildImages(haveFunctions, deletedFunctions, cleaners = {}
49
49
  }));
50
50
  cleanup.push(...deletedFunctions.map(async (func) => {
51
51
  try {
52
- await Promise.all([arCleaner.cleanupFunction(func), arCleaner.cleanupFunctionCache(func)]);
52
+ await arCleaner.cleanupFunction(func);
53
53
  }
54
54
  catch (err) {
55
55
  const path = `${func.project}/${func.region}/gcf-artifacts`;
@@ -106,13 +106,6 @@ class ArtifactRegistryCleaner {
106
106
  }
107
107
  await poller.pollOperation(Object.assign(Object.assign({}, ArtifactRegistryCleaner.POLLER_OPTIONS), { pollerName: `cleanup-${func.region}-${func.id}`, operationResourceName: op.name }));
108
108
  }
109
- async cleanupFunctionCache(func) {
110
- const op = await artifactregistry.deletePackage(`${ArtifactRegistryCleaner.packagePath(func)}%2Fcache`);
111
- if (op.done) {
112
- return;
113
- }
114
- await poller.pollOperation(Object.assign(Object.assign({}, ArtifactRegistryCleaner.POLLER_OPTIONS), { pollerName: `cleanup-cache-${func.region}-${func.id}`, operationResourceName: op.name }));
115
- }
116
109
  }
117
110
  exports.ArtifactRegistryCleaner = ArtifactRegistryCleaner;
118
111
  ArtifactRegistryCleaner.POLLER_OPTIONS = {
@@ -124,9 +117,6 @@ class NoopArtifactRegistryCleaner extends ArtifactRegistryCleaner {
124
117
  cleanupFunction() {
125
118
  return Promise.resolve();
126
119
  }
127
- cleanupFunctionCache() {
128
- return Promise.resolve();
129
- }
130
120
  }
131
121
  exports.NoopArtifactRegistryCleaner = NoopArtifactRegistryCleaner;
132
122
  class ContainerRegistryCleaner {
@@ -8,7 +8,7 @@ const functional_1 = require("../../functional");
8
8
  const secretManager = require("../../gcp/secretManager");
9
9
  const storage_1 = require("../../gcp/storage");
10
10
  const cel_1 = require("./cel");
11
- const secrets_1 = require("../../functions/secrets");
11
+ const secretManager_1 = require("../../gcp/secretManager");
12
12
  function dependenciesCEL(expr) {
13
13
  const deps = [];
14
14
  const paramCapture = /{{ params\.(\w+) }}/g;
@@ -222,7 +222,7 @@ async function handleSecret(secretParam, projectId) {
222
222
  type: "password",
223
223
  message: `This secret will be stored in Cloud Secret Manager (https://cloud.google.com/secret-manager/pricing) as ${secretParam.name}. Enter a value for ${secretParam.label || secretParam.name}:`,
224
224
  });
225
- await secretManager.createSecret(projectId, secretParam.name, (0, secrets_1.labels)());
225
+ await secretManager.createSecret(projectId, secretParam.name, (0, secretManager_1.labels)());
226
226
  await secretManager.addVersion(projectId, secretParam.name, secretValue);
227
227
  return secretValue;
228
228
  }
@@ -8,6 +8,7 @@ const ensureApiEnabled = require("../../ensureApiEnabled");
8
8
  const functionsConfig = require("../../functionsConfig");
9
9
  const functionsEnv = require("../../functions/env");
10
10
  const runtimes = require("./runtimes");
11
+ const supported = require("./runtimes/supported");
11
12
  const validate = require("./validate");
12
13
  const ensure = require("./ensure");
13
14
  const api_1 = require("../../api");
@@ -284,12 +285,20 @@ async function loadCodebases(config, options, firebaseConfig, runtimeConfig, fil
284
285
  projectId,
285
286
  sourceDir,
286
287
  projectDir: options.config.projectDir,
287
- runtime: codebaseConfig.runtime || "",
288
288
  };
289
+ const firebaseJsonRuntime = codebaseConfig.runtime;
290
+ if (firebaseJsonRuntime && !supported.isRuntime(firebaseJsonRuntime)) {
291
+ throw new error_1.FirebaseError(`Functions codebase ${codebase} has invalid runtime ` +
292
+ `${firebaseJsonRuntime} specified in firebase.json. Valid values are: ` +
293
+ Object.keys(supported.RUNTIMES)
294
+ .map((s) => `- ${s}`)
295
+ .join("\n"));
296
+ }
289
297
  const runtimeDelegate = await runtimes.getRuntimeDelegate(delegateContext);
290
- logger_1.logger.debug(`Validating ${runtimeDelegate.name} source`);
298
+ logger_1.logger.debug(`Validating ${runtimeDelegate.language} source`);
299
+ supported.guardVersionSupport(runtimeDelegate.runtime);
291
300
  await runtimeDelegate.validate();
292
- logger_1.logger.debug(`Building ${runtimeDelegate.name} source`);
301
+ logger_1.logger.debug(`Building ${runtimeDelegate.language} source`);
293
302
  await runtimeDelegate.build();
294
303
  const firebaseEnvs = functionsEnv.loadFirebaseEnvs(firebaseConfig, projectId);
295
304
  (0, utils_1.logLabeledBullet)("functions", `Loading and analyzing source code for codebase ${codebase} to determine what to deploy`);
@@ -7,7 +7,7 @@ const error_1 = require("../../../error");
7
7
  const sourceTokenScraper_1 = require("./sourceTokenScraper");
8
8
  const timer_1 = require("./timer");
9
9
  const functional_1 = require("../../../functional");
10
- const runtimes_1 = require("../runtimes");
10
+ const supported_1 = require("../runtimes/supported");
11
11
  const api_1 = require("../../../api");
12
12
  const logger_1 = require("../../../logger");
13
13
  const backend = require("../backend");
@@ -25,7 +25,7 @@ const scheduler = require("../../../gcp/cloudscheduler");
25
25
  const utils = require("../../../utils");
26
26
  const services = require("../services");
27
27
  const v1_1 = require("../../../functions/events/v1");
28
- const checkIam_1 = require("../checkIam");
28
+ const gce = require("../../../gcp/computeEngine");
29
29
  const functionsDeployHelper_1 = require("../functionsDeployHelper");
30
30
  const gcfV1PollerOptions = {
31
31
  apiOrigin: (0, api_1.functionsOrigin)(),
@@ -332,7 +332,7 @@ class Fabricator {
332
332
  else if (backend.isScheduleTriggered(endpoint)) {
333
333
  const invoker = endpoint.serviceAccount
334
334
  ? [endpoint.serviceAccount]
335
- : [(0, checkIam_1.getDefaultComputeServiceAgent)(this.projectNumber)];
335
+ : [gce.getDefaultServiceAccount(this.projectNumber)];
336
336
  await this.executor
337
337
  .run(() => run.setInvokerCreate(endpoint.project, serviceName, invoker))
338
338
  .catch(rethrowAs(endpoint, "set invoker"));
@@ -415,7 +415,7 @@ class Fabricator {
415
415
  else if (backend.isScheduleTriggered(endpoint)) {
416
416
  invoker = endpoint.serviceAccount
417
417
  ? [endpoint.serviceAccount]
418
- : [(0, checkIam_1.getDefaultComputeServiceAgent)(this.projectNumber)];
418
+ : [gce.getDefaultServiceAccount(this.projectNumber)];
419
419
  }
420
420
  if (invoker) {
421
421
  await this.executor
@@ -562,7 +562,7 @@ class Fabricator {
562
562
  .catch(rethrowAs(endpoint, "unregister blocking trigger"));
563
563
  }
564
564
  logOpStart(op, endpoint) {
565
- const runtime = (0, runtimes_1.getHumanFriendlyRuntimeName)(endpoint.runtime);
565
+ const runtime = supported_1.RUNTIMES[endpoint.runtime].friendly;
566
566
  const platform = (0, functionsDeployHelper_1.getHumanFriendlyPlatformName)(endpoint.platform);
567
567
  const label = helper.getFunctionLabel(endpoint);
568
568
  utils.logLabeledBullet("functions", `${op} ${runtime} (${platform}) function ${clc.bold(label)}...`);
@@ -1,61 +1,24 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getRuntimeDelegate = exports.getHumanFriendlyRuntimeName = exports.isValidRuntime = exports.isDeprecatedRuntime = void 0;
3
+ exports.getRuntimeDelegate = void 0;
4
4
  const node = require("./node");
5
5
  const python = require("./python");
6
6
  const validate = require("../validate");
7
7
  const error_1 = require("../../../error");
8
- const RUNTIMES = [
9
- "nodejs10",
10
- "nodejs12",
11
- "nodejs14",
12
- "nodejs16",
13
- "nodejs18",
14
- "nodejs20",
15
- "python310",
16
- "python311",
17
- "python312",
18
- ];
19
- const EXPERIMENTAL_RUNTIMES = [];
20
- const DEPRECATED_RUNTIMES = ["nodejs6", "nodejs8"];
21
- function isDeprecatedRuntime(runtime) {
22
- return DEPRECATED_RUNTIMES.includes(runtime);
23
- }
24
- exports.isDeprecatedRuntime = isDeprecatedRuntime;
25
- function isValidRuntime(runtime) {
26
- return RUNTIMES.includes(runtime) || EXPERIMENTAL_RUNTIMES.includes(runtime);
27
- }
28
- exports.isValidRuntime = isValidRuntime;
29
- const MESSAGE_FRIENDLY_RUNTIMES = {
30
- nodejs6: "Node.js 6 (Deprecated)",
31
- nodejs8: "Node.js 8 (Deprecated)",
32
- nodejs10: "Node.js 10",
33
- nodejs12: "Node.js 12",
34
- nodejs14: "Node.js 14",
35
- nodejs16: "Node.js 16",
36
- nodejs18: "Node.js 18",
37
- nodejs20: "Node.js 20",
38
- python310: "Python 3.10",
39
- python311: "Python 3.11",
40
- python312: "Python 3.12",
41
- };
42
- function getHumanFriendlyRuntimeName(runtime) {
43
- return MESSAGE_FRIENDLY_RUNTIMES[runtime] || runtime;
44
- }
45
- exports.getHumanFriendlyRuntimeName = getHumanFriendlyRuntimeName;
8
+ const supported = require("./supported");
46
9
  const factories = [node.tryCreateDelegate, python.tryCreateDelegate];
47
10
  async function getRuntimeDelegate(context) {
48
11
  const { projectDir, sourceDir, runtime } = context;
49
- validate.functionsDirectoryExists(sourceDir, projectDir);
50
- if (runtime && !isValidRuntime(runtime)) {
51
- throw new error_1.FirebaseError(`Cannot deploy function with runtime ${runtime}`);
12
+ if (runtime && !supported.isRuntime(runtime)) {
13
+ throw new error_1.FirebaseError(`firebase.json specifies invalid runtime ${runtime} for directory ${sourceDir}`);
52
14
  }
15
+ validate.functionsDirectoryExists(sourceDir, projectDir);
53
16
  for (const factory of factories) {
54
17
  const delegate = await factory(context);
55
18
  if (delegate) {
56
19
  return delegate;
57
20
  }
58
21
  }
59
- throw new error_1.FirebaseError(`Could not detect language for functions at ${sourceDir}`);
22
+ throw new error_1.FirebaseError(`Could not detect runtime for functions at ${sourceDir}`);
60
23
  }
61
24
  exports.getRuntimeDelegate = getRuntimeDelegate;
@@ -13,6 +13,7 @@ const parseRuntimeAndValidateSDK_1 = require("./parseRuntimeAndValidateSDK");
13
13
  const logger_1 = require("../../../../logger");
14
14
  const utils_1 = require("../../../../utils");
15
15
  const discovery = require("../discovery");
16
+ const supported = require("../supported");
16
17
  const validate = require("./validate");
17
18
  const versioning = require("./versioning");
18
19
  const parseTriggers = require("./parseTriggers");
@@ -25,7 +26,7 @@ async function tryCreateDelegate(context) {
25
26
  return undefined;
26
27
  }
27
28
  const runtime = (0, parseRuntimeAndValidateSDK_1.getRuntimeChoice)(context.sourceDir, context.runtime);
28
- if (!runtime.startsWith("nodejs")) {
29
+ if (!supported.runtimeIsLanguage(runtime, "nodejs")) {
29
30
  logger_1.logger.debug("Customer has a package.json but did not get a nodejs runtime. This should not happen");
30
31
  throw new error_1.FirebaseError(`Unexpected runtime ${runtime}`);
31
32
  }
@@ -38,7 +39,7 @@ class Delegate {
38
39
  this.projectDir = projectDir;
39
40
  this.sourceDir = sourceDir;
40
41
  this.runtime = runtime;
41
- this.name = "nodejs";
42
+ this.language = "nodejs";
42
43
  this._sdkVersion = undefined;
43
44
  this._bin = "";
44
45
  }