firebase-tools 13.6.0 → 13.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/lib/api.js +1 -1
  2. package/lib/apphosting/config.js +31 -0
  3. package/lib/apphosting/githubConnections.js +261 -0
  4. package/lib/{init/features/apphosting → apphosting}/index.js +21 -17
  5. package/lib/{init/features/apphosting → apphosting}/repo.js +9 -9
  6. package/lib/apphosting/secrets/dialogs.js +169 -0
  7. package/lib/apphosting/secrets/index.js +98 -0
  8. package/lib/commands/apphosting-backends-create.js +4 -2
  9. package/lib/commands/apphosting-backends-delete.js +1 -1
  10. package/lib/commands/apphosting-secrets-access.js +24 -0
  11. package/lib/commands/apphosting-secrets-describe.js +29 -0
  12. package/lib/commands/apphosting-secrets-grantaccess.js +45 -0
  13. package/lib/commands/apphosting-secrets-set.js +105 -0
  14. package/lib/commands/functions-secrets-access.js +2 -2
  15. package/lib/commands/functions-secrets-describe.js +14 -0
  16. package/lib/commands/functions-secrets-destroy.js +2 -2
  17. package/lib/commands/functions-secrets-get.js +3 -17
  18. package/lib/commands/functions-secrets-prune.js +2 -1
  19. package/lib/commands/functions-secrets-set.js +2 -2
  20. package/lib/commands/index.js +6 -0
  21. package/lib/deploy/functions/checkIam.js +3 -6
  22. package/lib/deploy/functions/containerCleaner.js +1 -11
  23. package/lib/deploy/functions/params.js +2 -2
  24. package/lib/deploy/functions/prepare.js +12 -3
  25. package/lib/deploy/functions/prompts.js +39 -7
  26. package/lib/deploy/functions/release/fabricator.js +5 -5
  27. package/lib/deploy/functions/release/index.js +17 -2
  28. package/lib/deploy/functions/release/planner.js +11 -3
  29. package/lib/deploy/functions/runtimes/index.js +6 -43
  30. package/lib/deploy/functions/runtimes/node/index.js +3 -2
  31. package/lib/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.js +15 -34
  32. package/lib/deploy/functions/runtimes/python/index.js +11 -7
  33. package/lib/deploy/functions/runtimes/supported.js +135 -0
  34. package/lib/deploy/functions/services/index.js +4 -0
  35. package/lib/emulator/controller.js +8 -1
  36. package/lib/emulator/functionsEmulator.js +2 -2
  37. package/lib/emulator/hub.js +5 -0
  38. package/lib/experiments.js +12 -0
  39. package/lib/extensions/emulator/specHelper.js +4 -3
  40. package/lib/frameworks/next/constants.js +2 -1
  41. package/lib/frameworks/next/index.js +22 -12
  42. package/lib/frameworks/next/utils.js +32 -3
  43. package/lib/functional.js +2 -2
  44. package/lib/functions/events/v2.js +7 -1
  45. package/lib/functions/secrets.js +40 -22
  46. package/lib/gcp/apphosting.js +15 -2
  47. package/lib/gcp/cloudbuild.js +7 -3
  48. package/lib/gcp/cloudfunctions.js +5 -5
  49. package/lib/gcp/cloudfunctionsv2.js +3 -3
  50. package/lib/gcp/cloudscheduler.js +2 -2
  51. package/lib/gcp/computeEngine.js +7 -0
  52. package/lib/gcp/devConnect.js +24 -11
  53. package/lib/gcp/iam.js +9 -1
  54. package/lib/gcp/secretManager.js +53 -13
  55. package/lib/gcp/serviceusage.js +21 -5
  56. package/lib/init/features/functions/python.js +4 -3
  57. package/lib/init/features/index.js +1 -1
  58. package/lib/utils.js +6 -6
  59. package/package.json +1 -1
  60. package/schema/firebase-config.json +12 -2
  61. /package/lib/{init/features/apphosting → apphosting}/constants.js +0 -0
@@ -4,6 +4,7 @@ exports.getRuntime = exports.DEFAULT_RUNTIME = exports.getFunctionProperties = e
4
4
  const yaml = require("js-yaml");
5
5
  const path = require("path");
6
6
  const fs = require("fs-extra");
7
+ const supported = require("../../deploy/functions/runtimes/supported");
7
8
  const error_1 = require("../../error");
8
9
  const extensionsHelper_1 = require("../extensionsHelper");
9
10
  const utils_1 = require("../utils");
@@ -77,7 +78,7 @@ function getFunctionProperties(resources) {
77
78
  return resources.map((r) => r.properties);
78
79
  }
79
80
  exports.getFunctionProperties = getFunctionProperties;
80
- exports.DEFAULT_RUNTIME = "nodejs14";
81
+ exports.DEFAULT_RUNTIME = supported.latest("nodejs");
81
82
  function getRuntime(resources) {
82
83
  if (resources.length === 0) {
83
84
  return exports.DEFAULT_RUNTIME;
@@ -88,7 +89,7 @@ function getRuntime(resources) {
88
89
  if (!runtime) {
89
90
  return exports.DEFAULT_RUNTIME;
90
91
  }
91
- if (!/^(nodejs)?([0-9]+)/.test(runtime)) {
92
+ if (!supported.runtimeIsLanguage(runtime, "nodejs")) {
92
93
  invalidRuntimes.push(runtime);
93
94
  return exports.DEFAULT_RUNTIME;
94
95
  }
@@ -97,6 +98,6 @@ function getRuntime(resources) {
97
98
  if (invalidRuntimes.length) {
98
99
  throw new error_1.FirebaseError(`The following runtimes are not supported by the Emulator Suite: ${invalidRuntimes.join(", ")}. \n Only Node runtimes are supported.`);
99
100
  }
100
- return runtimes.sort()[runtimes.length - 1];
101
+ return supported.latest("nodejs", runtimes);
101
102
  }
102
103
  exports.getRuntime = getRuntime;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.WEBPACK_LAYERS = exports.ESBUILD_VERSION = exports.SERVER_REFERENCE_MANIFEST = exports.APP_PATHS_MANIFEST = exports.ROUTES_MANIFEST = exports.PRERENDER_MANIFEST = exports.PAGES_MANIFEST = exports.MIDDLEWARE_MANIFEST = exports.IMAGES_MANIFEST = exports.EXPORT_MARKER = exports.APP_PATH_ROUTES_MANIFEST = void 0;
3
+ exports.WEBPACK_LAYERS = exports.ESBUILD_VERSION = exports.CONFIG_FILES = exports.SERVER_REFERENCE_MANIFEST = exports.APP_PATHS_MANIFEST = exports.ROUTES_MANIFEST = exports.PRERENDER_MANIFEST = exports.PAGES_MANIFEST = exports.MIDDLEWARE_MANIFEST = exports.IMAGES_MANIFEST = exports.EXPORT_MARKER = exports.APP_PATH_ROUTES_MANIFEST = void 0;
4
4
  exports.APP_PATH_ROUTES_MANIFEST = "app-path-routes-manifest.json";
5
5
  exports.EXPORT_MARKER = "export-marker.json";
6
6
  exports.IMAGES_MANIFEST = "images-manifest.json";
@@ -10,6 +10,7 @@ exports.PRERENDER_MANIFEST = "prerender-manifest.json";
10
10
  exports.ROUTES_MANIFEST = "routes-manifest.json";
11
11
  exports.APP_PATHS_MANIFEST = "app-paths-manifest.json";
12
12
  exports.SERVER_REFERENCE_MANIFEST = "server-reference-manifest.json";
13
+ exports.CONFIG_FILES = ["next.config.js", "next.config.mjs"];
13
14
  exports.ESBUILD_VERSION = "0.19.2";
14
15
  const WEBPACK_LAYERS_NAMES = {
15
16
  shared: "shared",
@@ -7,7 +7,6 @@ const promises_1 = require("fs/promises");
7
7
  const path_1 = require("path");
8
8
  const fs_extra_1 = require("fs-extra");
9
9
  const url_1 = require("url");
10
- const fs_1 = require("fs");
11
10
  const semver_1 = require("semver");
12
11
  const clc = require("colorette");
13
12
  const stream_chain_1 = require("stream-chain");
@@ -40,7 +39,7 @@ async function discover(dir) {
40
39
  if (!(await (0, fs_extra_1.pathExists)((0, path_1.join)(dir, "package.json"))))
41
40
  return;
42
41
  const version = (0, utils_2.getNextVersion)(dir);
43
- if (!(await (0, fs_extra_1.pathExists)("next.config.js")) && !version)
42
+ if (!(await (0, utils_2.whichNextConfigFile)(dir)) && !version)
44
43
  return;
45
44
  return { mayWantBackend: true, publicDirectory: (0, path_1.join)(dir, PUBLIC_DIR), version };
46
45
  }
@@ -336,7 +335,8 @@ exports.ɵcodegenPublicDirectory = ɵcodegenPublicDirectory;
336
335
  async function ɵcodegenFunctionsDirectory(sourceDir, destDir, target, context) {
337
336
  const { distDir } = await getConfig(sourceDir);
338
337
  const packageJson = await (0, utils_1.readJSON)((0, path_1.join)(sourceDir, "package.json"));
339
- if ((0, fs_1.existsSync)((0, path_1.join)(sourceDir, "next.config.js"))) {
338
+ const configFile = await (0, utils_2.whichNextConfigFile)(sourceDir);
339
+ if (configFile) {
340
340
  try {
341
341
  const productionDeps = await new Promise((resolve) => {
342
342
  const dependencies = [];
@@ -361,8 +361,14 @@ async function ɵcodegenFunctionsDirectory(sourceDir, destDir, target, context)
361
361
  });
362
362
  const esbuildArgs = productionDeps
363
363
  .map((it) => `--external:${it}`)
364
- .concat("--bundle", "--platform=node", `--target=node${constants_1.NODE_VERSION}`, `--outdir=${destDir}`, "--log-level=error");
365
- const bundle = (0, cross_spawn_1.sync)("npx", ["--yes", `esbuild@${constants_2.ESBUILD_VERSION}`, "next.config.js", ...esbuildArgs], {
364
+ .concat("--bundle", "--platform=node", `--target=node${constants_1.NODE_VERSION}`, "--log-level=error");
365
+ if (configFile === "next.config.mjs") {
366
+ esbuildArgs.push(...[`--outfile=${(0, path_1.join)(destDir, configFile)}`, "--format=esm"]);
367
+ }
368
+ else {
369
+ esbuildArgs.push(`--outfile=${(0, path_1.join)(destDir, configFile)}`);
370
+ }
371
+ const bundle = (0, cross_spawn_1.sync)("npx", ["--yes", `esbuild@${constants_2.ESBUILD_VERSION}`, configFile, ...esbuildArgs], {
366
372
  cwd: sourceDir,
367
373
  timeout: BUNDLE_NEXT_CONFIG_TIMEOUT,
368
374
  });
@@ -371,9 +377,9 @@ async function ɵcodegenFunctionsDirectory(sourceDir, destDir, target, context)
371
377
  }
372
378
  }
373
379
  catch (e) {
374
- console.warn("Unable to bundle next.config.js for use in Cloud Functions, proceeding with deploy but problems may be enountered.");
380
+ console.warn(`Unable to bundle ${configFile} for use in Cloud Functions, proceeding with deploy but problems may be encountered.`);
375
381
  console.error(e.message || e);
376
- (0, fs_extra_1.copy)((0, path_1.join)(sourceDir, "next.config.js"), (0, path_1.join)(destDir, "next.config.js"));
382
+ await (0, fs_extra_1.copy)((0, path_1.join)(sourceDir, configFile), (0, path_1.join)(destDir, configFile));
377
383
  }
378
384
  }
379
385
  if (await (0, fs_extra_1.pathExists)((0, path_1.join)(sourceDir, "public"))) {
@@ -390,8 +396,11 @@ async function ɵcodegenFunctionsDirectory(sourceDir, destDir, target, context)
390
396
  dotEnv["VERCEL_URL"] = deploymentDomain;
391
397
  }
392
398
  }
393
- await (0, fs_extra_1.mkdirp)((0, path_1.join)(destDir, distDir));
394
- await (0, fs_extra_1.copy)((0, path_1.join)(sourceDir, distDir), (0, path_1.join)(destDir, distDir));
399
+ const [productionDistDirfiles] = await Promise.all([
400
+ (0, utils_2.getProductionDistDirFiles)(sourceDir, distDir),
401
+ (0, fs_extra_1.mkdirp)((0, path_1.join)(destDir, distDir)),
402
+ ]);
403
+ await Promise.all(productionDistDirfiles.map((file) => (0, fs_extra_1.copy)(file, file.replace(sourceDir, destDir), { recursive: true })));
395
404
  return { packageJson, frameworksEntry: "next.js", dotEnv };
396
405
  }
397
406
  exports.ɵcodegenFunctionsDirectory = ɵcodegenFunctionsDirectory;
@@ -422,7 +431,8 @@ async function getConfig(dir) {
422
431
  var _a;
423
432
  var _b;
424
433
  let config = {};
425
- if ((0, fs_1.existsSync)((0, path_1.join)(dir, "next.config.js"))) {
434
+ const configFile = await (0, utils_2.whichNextConfigFile)(dir);
435
+ if (configFile) {
426
436
  const version = (0, utils_2.getNextVersion)(dir);
427
437
  if (!version)
428
438
  throw new Error("Unable to find the next dep, try NPM installing?");
@@ -435,10 +445,10 @@ async function getConfig(dir) {
435
445
  }
436
446
  else {
437
447
  try {
438
- config = await (_a = (0, url_1.pathToFileURL)((0, path_1.join)(dir, "next.config.js")).toString(), Promise.resolve().then(() => require(_a)));
448
+ config = await (_a = (0, url_1.pathToFileURL)((0, path_1.join)(dir, configFile)).toString(), Promise.resolve().then(() => require(_a)));
439
449
  }
440
450
  catch (e) {
441
- throw new Error("Unable to load next.config.js.");
451
+ throw new Error(`Unable to load ${configFile}.`);
442
452
  }
443
453
  }
444
454
  }
@@ -1,15 +1,17 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getRoutesWithServerAction = exports.hasStaticAppNotFoundComponent = exports.getNextVersion = exports.getBuildId = exports.getHeadersFromMetaFiles = exports.getNonStaticServerComponents = exports.getNonStaticRoutes = exports.getMiddlewareMatcherRegexes = exports.allDependencyNames = exports.isUsingAppDirectory = exports.isUsingNextImageInAppDirectory = exports.isUsingImageOptimization = exports.isUsingMiddleware = exports.hasUnoptimizedImage = exports.usesNextImage = exports.usesAppDirRouter = exports.getNextjsRewritesToUse = exports.isHeaderSupportedByHosting = exports.isRedirectSupportedByHosting = exports.isRewriteSupportedByHosting = exports.cleanI18n = exports.cleanCustomRouteI18n = exports.cleanEscapedChars = exports.I18N_SOURCE = void 0;
3
+ exports.whichNextConfigFile = exports.getProductionDistDirFiles = exports.getRoutesWithServerAction = exports.hasStaticAppNotFoundComponent = exports.getNextVersion = exports.getBuildId = exports.getHeadersFromMetaFiles = exports.getNonStaticServerComponents = exports.getNonStaticRoutes = exports.getMiddlewareMatcherRegexes = exports.allDependencyNames = exports.isUsingAppDirectory = exports.isUsingNextImageInAppDirectory = exports.isUsingImageOptimization = exports.isUsingMiddleware = exports.hasUnoptimizedImage = exports.usesNextImage = exports.usesAppDirRouter = exports.getNextjsRewritesToUse = exports.isHeaderSupportedByHosting = exports.isRedirectSupportedByHosting = exports.isRewriteSupportedByHosting = exports.cleanI18n = exports.cleanCustomRouteI18n = exports.cleanEscapedChars = exports.I18N_SOURCE = void 0;
4
4
  const fs_1 = require("fs");
5
5
  const fs_extra_1 = require("fs-extra");
6
6
  const path_1 = require("path");
7
7
  const promises_1 = require("fs/promises");
8
8
  const glob_1 = require("glob");
9
+ const glob = require("glob");
9
10
  const semver_1 = require("semver");
10
11
  const utils_1 = require("../utils");
11
12
  const constants_1 = require("./constants");
12
13
  const fsutils_1 = require("../../fsutils");
14
+ const utils_2 = require("../../utils");
13
15
  exports.I18N_SOURCE = /\/:nextInternalLocale(\([^\)]+\))?/;
14
16
  function cleanEscapedChars(path) {
15
17
  return path.replace(/\\([(){}:+?*])/g, (a, b) => b);
@@ -101,10 +103,15 @@ async function isUsingImageOptimization(projectDir, distDir) {
101
103
  }
102
104
  exports.isUsingImageOptimization = isUsingImageOptimization;
103
105
  async function isUsingNextImageInAppDirectory(projectDir, nextDir) {
106
+ const nextImagePath = ["node_modules", "next", "dist", "client", "image"];
107
+ const nextImageString = utils_2.IS_WINDOWS
108
+ ?
109
+ nextImagePath.join(path_1.sep + path_1.sep)
110
+ : (0, path_1.join)(...nextImagePath);
104
111
  const files = (0, glob_1.sync)((0, path_1.join)(projectDir, nextDir, "server", "**", "*client-reference-manifest.js"));
105
112
  for (const filepath of files) {
106
- const fileContents = await (0, promises_1.readFile)(filepath);
107
- if (fileContents.includes("node_modules/next/dist/client/image")) {
113
+ const fileContents = await (0, promises_1.readFile)(filepath, "utf-8");
114
+ if (fileContents.includes(nextImageString)) {
108
115
  return true;
109
116
  }
110
117
  }
@@ -218,3 +225,25 @@ function getRoutesWithServerAction(serverReferenceManifest, appPathRoutesManifes
218
225
  return Array.from(routesWithServerAction);
219
226
  }
220
227
  exports.getRoutesWithServerAction = getRoutesWithServerAction;
228
+ async function getProductionDistDirFiles(sourceDir, distDir) {
229
+ const productionDistDirFiles = await new Promise((resolve, reject) => glob("**", {
230
+ ignore: [(0, path_1.join)("cache", "webpack", "*-development", "**"), (0, path_1.join)("cache", "eslint", "**")],
231
+ cwd: (0, path_1.join)(sourceDir, distDir),
232
+ nodir: true,
233
+ absolute: true,
234
+ }, (err, matches) => {
235
+ if (err)
236
+ reject(err);
237
+ resolve(matches);
238
+ }));
239
+ return productionDistDirFiles;
240
+ }
241
+ exports.getProductionDistDirFiles = getProductionDistDirFiles;
242
+ async function whichNextConfigFile(dir) {
243
+ for (const file of constants_1.CONFIG_FILES) {
244
+ if (await (0, fs_extra_1.pathExists)((0, path_1.join)(dir, file)))
245
+ return file;
246
+ }
247
+ return null;
248
+ }
249
+ exports.whichNextConfigFile = whichNextConfigFile;
package/lib/functional.js CHANGED
@@ -52,8 +52,8 @@ const zipIn = (other) => (elem, ndx) => {
52
52
  return [elem, other[ndx]];
53
53
  };
54
54
  exports.zipIn = zipIn;
55
- function assertExhaustive(val) {
56
- throw new Error(`Never has a value (${val}).`);
55
+ function assertExhaustive(val, message) {
56
+ throw new Error(message || `Never has a value (${val}).`);
57
57
  }
58
58
  exports.assertExhaustive = assertExhaustive;
59
59
  function partition(arr, predicate) {
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.FIRESTORE_EVENTS = exports.TEST_LAB_EVENT = exports.REMOTE_CONFIG_EVENT = exports.DATABASE_EVENTS = exports.FIREBASE_ALERTS_PUBLISH_EVENT = exports.STORAGE_EVENTS = exports.PUBSUB_PUBLISH_EVENT = void 0;
3
+ exports.FIRESTORE_EVENT_WITH_AUTH_CONTEXT_REGEX = exports.FIRESTORE_EVENT_REGEX = exports.FIRESTORE_EVENTS = exports.TEST_LAB_EVENT = exports.REMOTE_CONFIG_EVENT = exports.DATABASE_EVENTS = exports.FIREBASE_ALERTS_PUBLISH_EVENT = exports.STORAGE_EVENTS = exports.PUBSUB_PUBLISH_EVENT = void 0;
4
4
  exports.PUBSUB_PUBLISH_EVENT = "google.cloud.pubsub.topic.v1.messagePublished";
5
5
  exports.STORAGE_EVENTS = [
6
6
  "google.cloud.storage.object.v1.finalized",
@@ -22,4 +22,10 @@ exports.FIRESTORE_EVENTS = [
22
22
  "google.cloud.firestore.document.v1.created",
23
23
  "google.cloud.firestore.document.v1.updated",
24
24
  "google.cloud.firestore.document.v1.deleted",
25
+ "google.cloud.firestore.document.v1.written.withAuthContext",
26
+ "google.cloud.firestore.document.v1.created.withAuthContext",
27
+ "google.cloud.firestore.document.v1.updated.withAuthContext",
28
+ "google.cloud.firestore.document.v1.deleted.withAuthContext",
25
29
  ];
30
+ exports.FIRESTORE_EVENT_REGEX = /^google\.cloud\.firestore\.document\.v1\.[^\.]*$/;
31
+ exports.FIRESTORE_EVENT_WITH_AUTH_CONTEXT_REGEX = /^google\.cloud\.firestore\.document\.v1\..*\.withAuthContext$/;
@@ -1,11 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.updateEndpointSecret = exports.pruneAndDestroySecrets = exports.pruneSecrets = exports.versionInUse = exports.inUse = exports.getSecretVersions = exports.of = exports.ensureSecret = exports.ensureValidKey = exports.ensureApi = exports.labels = exports.isFirebaseManaged = void 0;
3
+ exports.describeSecret = exports.updateEndpointSecret = exports.pruneAndDestroySecrets = exports.pruneSecrets = exports.versionInUse = exports.inUse = exports.getSecretVersions = exports.of = exports.ensureSecret = exports.ensureValidKey = void 0;
4
4
  const utils = require("../utils");
5
5
  const poller = require("../operation-poller");
6
6
  const gcfV1 = require("../gcp/cloudfunctions");
7
7
  const gcfV2 = require("../gcp/cloudfunctionsv2");
8
- const ensureApiEnabled = require("../ensureApiEnabled");
9
8
  const api_1 = require("../api");
10
9
  const secretManager_1 = require("../gcp/secretManager");
11
10
  const error_1 = require("../error");
@@ -14,8 +13,10 @@ const prompt_1 = require("../prompt");
14
13
  const env_1 = require("./env");
15
14
  const logger_1 = require("../logger");
16
15
  const functional_1 = require("../functional");
16
+ const secretManager_2 = require("../gcp/secretManager");
17
+ const secretManager_3 = require("../gcp/secretManager");
17
18
  const projectUtils_1 = require("../projectUtils");
18
- const FIREBASE_MANAGED = "firebase-managed";
19
+ const Table = require("cli-table");
19
20
  const gcfV1PollerOptions = {
20
21
  apiOrigin: (0, api_1.functionsOrigin)(),
21
22
  apiVersion: "v1",
@@ -28,25 +29,12 @@ const gcfV2PollerOptions = {
28
29
  masterTimeout: 25 * 60 * 1000,
29
30
  maxBackoff: 10000,
30
31
  };
31
- function isFirebaseManaged(secret) {
32
- return Object.keys(secret.labels || []).includes(FIREBASE_MANAGED);
33
- }
34
- exports.isFirebaseManaged = isFirebaseManaged;
35
- function labels() {
36
- return { [FIREBASE_MANAGED]: "true" };
37
- }
38
- exports.labels = labels;
39
32
  function toUpperSnakeCase(key) {
40
33
  return key
41
34
  .replace(/[.-]/g, "_")
42
35
  .replace(/([a-z])([A-Z])/g, "$1_$2")
43
36
  .toUpperCase();
44
37
  }
45
- function ensureApi(options) {
46
- const projectId = (0, projectUtils_1.needProjectId)(options);
47
- return ensureApiEnabled.ensure(projectId, (0, api_1.secretManagerOrigin)(), "secretmanager", true);
48
- }
49
- exports.ensureApi = ensureApi;
50
38
  async function ensureValidKey(key, options) {
51
39
  const transformedKey = toUpperSnakeCase(key);
52
40
  if (transformedKey !== key) {
@@ -76,18 +64,34 @@ exports.ensureValidKey = ensureValidKey;
76
64
  async function ensureSecret(projectId, name, options) {
77
65
  try {
78
66
  const secret = await (0, secretManager_1.getSecret)(projectId, name);
79
- if (!isFirebaseManaged(secret)) {
67
+ if ((0, secretManager_1.isAppHostingManaged)(secret)) {
68
+ (0, utils_1.logWarning)("Your secret is managed by Firebase App Hosting. Continuing will disable automatic deletion of old versions.");
69
+ const stopTracking = await (0, prompt_1.promptOnce)({
70
+ name: "doNotTrack",
71
+ type: "confirm",
72
+ default: false,
73
+ message: "Do you wish to continue?",
74
+ }, options);
75
+ if (stopTracking) {
76
+ delete secret.labels[secretManager_2.FIREBASE_MANAGED];
77
+ await (0, secretManager_1.patchSecret)(secret.projectId, secret.name, secret.labels);
78
+ }
79
+ else {
80
+ throw new Error("A secret cannot be managed by both Firebase App Hosting and Cloud Functions for Firebase");
81
+ }
82
+ }
83
+ else if (!(0, secretManager_2.isFunctionsManaged)(secret)) {
80
84
  if (!options.force) {
81
- (0, utils_1.logWarning)("Your secret is not managed by Firebase. " +
85
+ (0, utils_1.logWarning)("Your secret is not managed by Cloud Functions for Firebase. " +
82
86
  "Firebase managed secrets are automatically pruned to reduce your monthly cost for using Secret Manager. ");
83
87
  const confirm = await (0, prompt_1.promptOnce)({
84
88
  name: "updateLabels",
85
89
  type: "confirm",
86
90
  default: true,
87
- message: `Would you like to have your secret ${secret.name} managed by Firebase?`,
91
+ message: `Would you like to have your secret ${secret.name} managed by Cloud Functions for Firebase?`,
88
92
  }, options);
89
93
  if (confirm) {
90
- return (0, secretManager_1.patchSecret)(projectId, secret.name, Object.assign(Object.assign({}, secret.labels), labels()));
94
+ return (0, secretManager_1.patchSecret)(projectId, secret.name, Object.assign(Object.assign({}, secret.labels), (0, secretManager_3.labels)()));
91
95
  }
92
96
  }
93
97
  }
@@ -98,7 +102,7 @@ async function ensureSecret(projectId, name, options) {
98
102
  throw err;
99
103
  }
100
104
  }
101
- return await (0, secretManager_1.createSecret)(projectId, name, labels());
105
+ return await (0, secretManager_1.createSecret)(projectId, name, (0, secretManager_3.labels)());
102
106
  }
103
107
  exports.ensureSecret = ensureSecret;
104
108
  function of(endpoints) {
@@ -139,7 +143,7 @@ async function pruneSecrets(projectInfo, endpoints) {
139
143
  const { projectId, projectNumber } = projectInfo;
140
144
  const pruneKey = (name, version) => `${name}@${version}`;
141
145
  const prunedSecrets = new Set();
142
- const haveSecrets = await (0, secretManager_1.listSecrets)(projectId, `labels.${FIREBASE_MANAGED}=true`);
146
+ const haveSecrets = await (0, secretManager_1.listSecrets)(projectId, `labels.${secretManager_2.FIREBASE_MANAGED}=true`);
143
147
  for (const secret of haveSecrets) {
144
148
  const versions = await (0, secretManager_1.listSecretVersions)(projectId, secret.name, `NOT state: DESTROYED`);
145
149
  for (const version of versions) {
@@ -237,3 +241,17 @@ async function updateEndpointSecret(projectInfo, secretVersion, endpoint) {
237
241
  }
238
242
  }
239
243
  exports.updateEndpointSecret = updateEndpointSecret;
244
+ async function describeSecret(key, options) {
245
+ const projectId = (0, projectUtils_1.needProjectId)(options);
246
+ const versions = await (0, secretManager_1.listSecretVersions)(projectId, key);
247
+ const table = new Table({
248
+ head: ["Version", "State"],
249
+ style: { head: ["yellow"] },
250
+ });
251
+ for (const version of versions) {
252
+ table.push([version.versionId, version.state]);
253
+ }
254
+ logger_1.logger.info(table.toString());
255
+ return { secrets: versions };
256
+ }
257
+ exports.describeSecret = describeSecret;
@@ -31,9 +31,22 @@ async function getBackend(projectId, location, backendId) {
31
31
  }
32
32
  exports.getBackend = getBackend;
33
33
  async function listBackends(projectId, location) {
34
+ var _a;
34
35
  const name = `projects/${projectId}/locations/${location}/backends`;
35
- const res = await exports.client.get(name);
36
- return res.body;
36
+ let pageToken;
37
+ const res = {
38
+ backends: [],
39
+ unreachable: [],
40
+ };
41
+ do {
42
+ const queryParams = pageToken ? { pageToken } : {};
43
+ const int = await exports.client.get(name, { queryParams });
44
+ res.backends.push(...(int.body.backends || []));
45
+ (_a = res.unreachable) === null || _a === void 0 ? void 0 : _a.push(...(int.body.unreachable || []));
46
+ pageToken = int.body.nextPageToken;
47
+ } while (pageToken);
48
+ res.unreachable = [...new Set(res.unreachable)];
49
+ return res;
37
50
  }
38
51
  exports.listBackends = listBackends;
39
52
  async function deleteBackend(projectId, location, backendId) {
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.serviceAgentEmail = exports.deleteRepository = exports.getRepository = exports.createRepository = exports.fetchLinkableRepositories = exports.deleteConnection = exports.listConnections = exports.getConnection = exports.createConnection = void 0;
3
+ exports.getDefaultServiceAgent = exports.getDefaultServiceAccount = exports.deleteRepository = exports.getRepository = exports.createRepository = exports.fetchLinkableRepositories = exports.deleteConnection = exports.listConnections = exports.getConnection = exports.createConnection = void 0;
4
4
  const apiv2_1 = require("../apiv2");
5
5
  const api_1 = require("../api");
6
6
  const PAGE_SIZE_MAX = 100;
@@ -74,7 +74,11 @@ async function deleteRepository(projectId, location, connectionId, repositoryId)
74
74
  return res.body;
75
75
  }
76
76
  exports.deleteRepository = deleteRepository;
77
- function serviceAgentEmail(projectNumber) {
77
+ function getDefaultServiceAccount(projectNumber) {
78
+ return `${projectNumber}@cloudbuild.gserviceaccount.com`;
79
+ }
80
+ exports.getDefaultServiceAccount = getDefaultServiceAccount;
81
+ function getDefaultServiceAgent(projectNumber) {
78
82
  return `service-${projectNumber}@gcp-sa-cloudbuild.iam.gserviceaccount.com`;
79
83
  }
80
- exports.serviceAgentEmail = serviceAgentEmail;
84
+ exports.getDefaultServiceAgent = getDefaultServiceAgent;
@@ -7,7 +7,7 @@ const logger_1 = require("../logger");
7
7
  const backend = require("../deploy/functions/backend");
8
8
  const utils = require("../utils");
9
9
  const proto = require("./proto");
10
- const runtimes = require("../deploy/functions/runtimes");
10
+ const supported = require("../deploy/functions/runtimes/supported");
11
11
  const projectConfig = require("../functions/projectConfig");
12
12
  const apiv2_1 = require("../apiv2");
13
13
  const api_1 = require("../api");
@@ -241,8 +241,8 @@ function endpointFromFunction(gcfFunction) {
241
241
  uri = gcfFunction.httpsTrigger.url;
242
242
  securityLevel = gcfFunction.httpsTrigger.securityLevel;
243
243
  }
244
- if (!runtimes.isValidRuntime(gcfFunction.runtime)) {
245
- logger_1.logger.debug("GCFv1 function has a deprecated runtime:", JSON.stringify(gcfFunction, null, 2));
244
+ if (!supported.isRuntime(gcfFunction.runtime)) {
245
+ logger_1.logger.debug("GCF 1st gen function has unsupported runtime:", JSON.stringify(gcfFunction, null, 2));
246
246
  }
247
247
  const endpoint = Object.assign(Object.assign({ platform: "gcfv1", id,
248
248
  project,
@@ -273,9 +273,9 @@ function functionFromEndpoint(endpoint, sourceUploadUrl) {
273
273
  if (endpoint.platform !== "gcfv1") {
274
274
  throw new error_1.FirebaseError("Trying to create a v1 CloudFunction with v2 API. This should never happen");
275
275
  }
276
- if (!runtimes.isValidRuntime(endpoint.runtime)) {
276
+ if (!supported.isRuntime(endpoint.runtime)) {
277
277
  throw new error_1.FirebaseError("Failed internal assertion. Trying to deploy a new function with a deprecated runtime." +
278
- " This should never happen");
278
+ " This should never happen", { exit: 1 });
279
279
  }
280
280
  const gcfFunction = {
281
281
  name: backend.functionName(endpoint),
@@ -7,7 +7,7 @@ const api_1 = require("../api");
7
7
  const logger_1 = require("../logger");
8
8
  const v2_1 = require("../functions/events/v2");
9
9
  const backend = require("../deploy/functions/backend");
10
- const runtimes = require("../deploy/functions/runtimes");
10
+ const supported = require("../deploy/functions/runtimes/supported");
11
11
  const proto = require("./proto");
12
12
  const utils = require("../utils");
13
13
  const projectConfig = require("../functions/projectConfig");
@@ -175,7 +175,7 @@ function functionFromEndpoint(endpoint) {
175
175
  if (endpoint.platform !== "gcfv2") {
176
176
  throw new error_1.FirebaseError("Trying to create a v2 CloudFunction with v1 API. This should never happen");
177
177
  }
178
- if (!runtimes.isValidRuntime(endpoint.runtime)) {
178
+ if (!supported.isRuntime(endpoint.runtime)) {
179
179
  throw new error_1.FirebaseError("Failed internal assertion. Trying to deploy a new function with a deprecated runtime." +
180
180
  " This should never happen");
181
181
  }
@@ -337,7 +337,7 @@ function endpointFromFunction(gcfFunction) {
337
337
  else {
338
338
  trigger = { httpsTrigger: {} };
339
339
  }
340
- if (!runtimes.isValidRuntime(gcfFunction.buildConfig.runtime)) {
340
+ if (!supported.isRuntime(gcfFunction.buildConfig.runtime)) {
341
341
  logger_1.logger.debug("GCFv2 function has a deprecated runtime:", JSON.stringify(gcfFunction, null, 2));
342
342
  }
343
343
  const endpoint = Object.assign(Object.assign({ platform: "gcfv2", id,
@@ -8,7 +8,7 @@ const api_1 = require("../api");
8
8
  const apiv2_1 = require("../apiv2");
9
9
  const backend = require("../deploy/functions/backend");
10
10
  const proto = require("./proto");
11
- const checkIam_1 = require("../deploy/functions/checkIam");
11
+ const gce = require("../gcp/computeEngine");
12
12
  const functional_1 = require("../functional");
13
13
  const VERSION = "v1";
14
14
  const DEFAULT_TIME_ZONE_V1 = "America/Los_Angeles";
@@ -130,7 +130,7 @@ function jobFromEndpoint(endpoint, location, projectNumber) {
130
130
  uri: endpoint.uri,
131
131
  httpMethod: "POST",
132
132
  oidcToken: {
133
- serviceAccountEmail: (_a = endpoint.serviceAccount) !== null && _a !== void 0 ? _a : (0, checkIam_1.getDefaultComputeServiceAgent)(projectNumber),
133
+ serviceAccountEmail: (_a = endpoint.serviceAccount) !== null && _a !== void 0 ? _a : gce.getDefaultServiceAccount(projectNumber),
134
134
  },
135
135
  };
136
136
  }
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getDefaultServiceAccount = void 0;
4
+ function getDefaultServiceAccount(projectNumber) {
5
+ return `${projectNumber}-compute@developer.gserviceaccount.com`;
6
+ }
7
+ exports.getDefaultServiceAccount = getDefaultServiceAccount;
@@ -1,24 +1,32 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.serviceAgentEmail = exports.getGitRepositoryLink = exports.createGitRepositoryLink = exports.listAllLinkableGitRepositories = exports.listAllConnections = exports.getConnection = exports.createConnection = exports.client = void 0;
3
+ exports.generateP4SA = exports.serviceAgentEmail = exports.getGitRepositoryLink = exports.createGitRepositoryLink = exports.listAllLinkableGitRepositories = exports.listAllConnections = exports.getConnection = exports.deleteConnection = exports.createConnection = exports.client = void 0;
4
4
  const apiv2_1 = require("../apiv2");
5
5
  const api_1 = require("../api");
6
+ const serviceusage_1 = require("./serviceusage");
6
7
  const PAGE_SIZE_MAX = 1000;
8
+ const LOCATION_OVERRIDE = process.env.FIREBASE_DEVELOPERCONNECT_LOCATION_OVERRIDE;
7
9
  exports.client = new apiv2_1.Client({
8
10
  urlPrefix: (0, api_1.developerConnectOrigin)(),
9
11
  auth: true,
10
12
  apiVersion: "v1",
11
13
  });
12
- async function createConnection(projectId, location, connectionId, githubConfig) {
14
+ async function createConnection(projectId, location, connectionId, githubConfig = {}) {
13
15
  const config = Object.assign(Object.assign({}, githubConfig), { githubApp: "FIREBASE" });
14
- const res = await exports.client.post(`projects/${projectId}/locations/${location}/connections`, {
16
+ const res = await exports.client.post(`projects/${projectId}/locations/${LOCATION_OVERRIDE !== null && LOCATION_OVERRIDE !== void 0 ? LOCATION_OVERRIDE : location}/connections`, {
15
17
  githubConfig: config,
16
18
  }, { queryParams: { connectionId } });
17
19
  return res.body;
18
20
  }
19
21
  exports.createConnection = createConnection;
22
+ async function deleteConnection(projectId, location, connectionId) {
23
+ const name = `projects/${projectId}/locations/${LOCATION_OVERRIDE !== null && LOCATION_OVERRIDE !== void 0 ? LOCATION_OVERRIDE : location}/connections/${connectionId}`;
24
+ const res = await exports.client.delete(name, { queryParams: { force: "true" } });
25
+ return res.body;
26
+ }
27
+ exports.deleteConnection = deleteConnection;
20
28
  async function getConnection(projectId, location, connectionId) {
21
- const name = `projects/${projectId}/locations/${location}/connections/${connectionId}`;
29
+ const name = `projects/${projectId}/locations/${LOCATION_OVERRIDE !== null && LOCATION_OVERRIDE !== void 0 ? LOCATION_OVERRIDE : location}/connections/${connectionId}`;
22
30
  const res = await exports.client.get(name);
23
31
  return res.body;
24
32
  }
@@ -26,7 +34,7 @@ exports.getConnection = getConnection;
26
34
  async function listAllConnections(projectId, location) {
27
35
  const conns = [];
28
36
  const getNextPage = async (pageToken = "") => {
29
- const res = await exports.client.get(`/projects/${projectId}/locations/${location}/connections`, {
37
+ const res = await exports.client.get(`/projects/${projectId}/locations/${LOCATION_OVERRIDE !== null && LOCATION_OVERRIDE !== void 0 ? LOCATION_OVERRIDE : location}/connections`, {
30
38
  queryParams: {
31
39
  pageSize: PAGE_SIZE_MAX,
32
40
  pageToken,
@@ -44,17 +52,17 @@ async function listAllConnections(projectId, location) {
44
52
  }
45
53
  exports.listAllConnections = listAllConnections;
46
54
  async function listAllLinkableGitRepositories(projectId, location, connectionId) {
47
- const name = `projects/${projectId}/locations/${location}/connections/${connectionId}:fetchLinkableRepositories`;
55
+ const name = `projects/${projectId}/locations/${LOCATION_OVERRIDE !== null && LOCATION_OVERRIDE !== void 0 ? LOCATION_OVERRIDE : location}/connections/${connectionId}:fetchLinkableGitRepositories`;
48
56
  const repos = [];
49
57
  const getNextPage = async (pageToken = "") => {
50
58
  const res = await exports.client.get(name, {
51
59
  queryParams: {
52
- PAGE_SIZE_MAX,
60
+ pageSize: PAGE_SIZE_MAX,
53
61
  pageToken,
54
62
  },
55
63
  });
56
- if (Array.isArray(res.body.repositories)) {
57
- repos.push(...res.body.repositories);
64
+ if (Array.isArray(res.body.linkableGitRepositories)) {
65
+ repos.push(...res.body.linkableGitRepositories);
58
66
  }
59
67
  if (res.body.nextPageToken) {
60
68
  await getNextPage(res.body.nextPageToken);
@@ -65,12 +73,12 @@ async function listAllLinkableGitRepositories(projectId, location, connectionId)
65
73
  }
66
74
  exports.listAllLinkableGitRepositories = listAllLinkableGitRepositories;
67
75
  async function createGitRepositoryLink(projectId, location, connectionId, gitRepositoryLinkId, cloneUri) {
68
- const res = await exports.client.post(`projects/${projectId}/locations/${location}/connections/${connectionId}/gitRepositoryLinks`, { cloneUri }, { queryParams: { gitRepositoryLinkId } });
76
+ const res = await exports.client.post(`projects/${projectId}/locations/${LOCATION_OVERRIDE !== null && LOCATION_OVERRIDE !== void 0 ? LOCATION_OVERRIDE : location}/connections/${connectionId}/gitRepositoryLinks`, { cloneUri }, { queryParams: { gitRepositoryLinkId } });
69
77
  return res.body;
70
78
  }
71
79
  exports.createGitRepositoryLink = createGitRepositoryLink;
72
80
  async function getGitRepositoryLink(projectId, location, connectionId, gitRepositoryLinkId) {
73
- const name = `projects/${projectId}/locations/${location}/connections/${connectionId}/gitRepositoryLinks/${gitRepositoryLinkId}`;
81
+ const name = `projects/${projectId}/locations/${LOCATION_OVERRIDE !== null && LOCATION_OVERRIDE !== void 0 ? LOCATION_OVERRIDE : location}/connections/${connectionId}/gitRepositoryLinks/${gitRepositoryLinkId}`;
74
82
  const res = await exports.client.get(name);
75
83
  return res.body;
76
84
  }
@@ -79,3 +87,8 @@ function serviceAgentEmail(projectNumber) {
79
87
  return `service-${projectNumber}@${(0, api_1.developerConnectP4SAOrigin)()}`;
80
88
  }
81
89
  exports.serviceAgentEmail = serviceAgentEmail;
90
+ async function generateP4SA(projectNumber) {
91
+ const devConnectOrigin = (0, api_1.developerConnectOrigin)();
92
+ await (0, serviceusage_1.generateServiceIdentityAndPoll)(projectNumber, new URL(devConnectOrigin).hostname, "apphosting");
93
+ }
94
+ exports.generateP4SA = generateP4SA;
package/lib/gcp/iam.js CHANGED
@@ -1,10 +1,18 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.testIamPermissions = exports.testResourceIamPermissions = exports.getRole = exports.listServiceAccountKeys = exports.deleteServiceAccount = exports.createServiceAccountKey = exports.getServiceAccount = exports.createServiceAccount = void 0;
3
+ exports.testIamPermissions = exports.testResourceIamPermissions = exports.getRole = exports.listServiceAccountKeys = exports.deleteServiceAccount = exports.createServiceAccountKey = exports.getServiceAccount = exports.createServiceAccount = exports.getDefaultComputeEngineServiceAgent = exports.getDefaultCloudBuildServiceAgent = void 0;
4
4
  const api_1 = require("../api");
5
5
  const logger_1 = require("../logger");
6
6
  const apiv2_1 = require("../apiv2");
7
7
  const apiClient = new apiv2_1.Client({ urlPrefix: (0, api_1.iamOrigin)(), apiVersion: "v1" });
8
+ function getDefaultCloudBuildServiceAgent(projectNumber) {
9
+ return `${projectNumber}@cloudbuild.gserviceaccount.com`;
10
+ }
11
+ exports.getDefaultCloudBuildServiceAgent = getDefaultCloudBuildServiceAgent;
12
+ function getDefaultComputeEngineServiceAgent(projectNumber) {
13
+ return `${projectNumber}-compute@developer.gserviceaccount.com`;
14
+ }
15
+ exports.getDefaultComputeEngineServiceAgent = getDefaultComputeEngineServiceAgent;
8
16
  async function createServiceAccount(projectId, accountId, description, displayName) {
9
17
  const response = await apiClient.post(`/projects/${projectId}/serviceAccounts`, {
10
18
  accountId,