export async function prepare()

in src/deploy/functions/prepare.ts [52:189]


export async function prepare(
  context: args.Context,
  options: Options,
  payload: args.Payload
): Promise<void> {
  const projectId = needProjectId(options);

  const sourceDirName = options.config.get("functions.source") as string;
  if (!sourceDirName) {
    throw new FirebaseError(
      `No functions code detected at default location (./functions), and no functions.source defined in firebase.json`
    );
  }
  const sourceDir = options.config.path(sourceDirName);

  const delegateContext: DelegateContext = {
    projectId,
    sourceDir,
    projectDir: options.config.projectDir,
    runtime: (options.config.get("functions.runtime") as string) || "",
  };
  const runtimeDelegate = await runtimes.getRuntimeDelegate(delegateContext);
  logger.debug(`Validating ${runtimeDelegate.name} source`);
  await runtimeDelegate.validate();
  logger.debug(`Building ${runtimeDelegate.name} source`);
  await runtimeDelegate.build();

  // Check that all necessary APIs are enabled.
  const checkAPIsEnabled = await Promise.all([
    ensureApiEnabled.ensure(projectId, "cloudfunctions.googleapis.com", "functions"),
    ensureApiEnabled.check(
      projectId,
      "runtimeconfig.googleapis.com",
      "runtimeconfig",
      /* silent=*/ true
    ),
    ensureCloudBuildEnabled(projectId),
    maybeEnableAR(projectId),
  ]);
  context.runtimeConfigEnabled = checkAPIsEnabled[1];
  context.artifactRegistryEnabled = checkAPIsEnabled[3];

  // Get the Firebase Config, and set it on each function in the deployment.
  const firebaseConfig = await functionsConfig.getFirebaseConfig(options);
  context.firebaseConfig = firebaseConfig;
  const runtimeConfig = await getFunctionsConfig(context);

  const firebaseEnvs = functionsEnv.loadFirebaseEnvs(firebaseConfig, projectId);
  const userEnvOpt = {
    functionsSource: sourceDir,
    projectId: projectId,
    projectAlias: options.projectAlias,
  };
  const userEnvs = functionsEnv.loadUserEnvs(userEnvOpt);
  const usedDotenv = hasDotenv(userEnvOpt);
  const tag = hasUserConfig(runtimeConfig)
    ? usedDotenv
      ? "mixed"
      : "runtime_config"
    : usedDotenv
    ? "dotenv"
    : "none";
  await track("functions_codebase_deploy_env_method", tag);

  logger.debug(`Analyzing ${runtimeDelegate.name} backend spec`);
  const wantBackend = await runtimeDelegate.discoverSpec(runtimeConfig, firebaseEnvs);
  wantBackend.environmentVariables = { ...userEnvs, ...firebaseEnvs };
  payload.functions = { backend: wantBackend };

  // Note: Some of these are premium APIs that require billing to be enabled.
  // We'd eventually have to add special error handling for billing APIs, but
  // enableCloudBuild is called above and has this special casing already.
  if (backend.someEndpoint(wantBackend, (e) => e.platform === "gcfv2")) {
    const V2_APIS = [
      "artifactregistry.googleapis.com",
      "run.googleapis.com",
      "eventarc.googleapis.com",
      "pubsub.googleapis.com",
      "storage.googleapis.com",
    ];
    const enablements = V2_APIS.map((api) => {
      return ensureApiEnabled.ensure(context.projectId, api, "functions");
    });
    await Promise.all(enablements);
  }

  if (backend.someEndpoint(wantBackend, () => true)) {
    logBullet(
      clc.cyan.bold("functions:") +
        " preparing " +
        clc.bold(sourceDirName) +
        " directory for uploading..."
    );
  }
  if (backend.someEndpoint(wantBackend, (e) => e.platform === "gcfv1")) {
    context.functionsSourceV1 = await prepareFunctionsUpload(runtimeConfig, options);
  }
  if (backend.someEndpoint(wantBackend, (e) => e.platform === "gcfv2")) {
    context.functionsSourceV2 = await prepareFunctionsUpload(
      /* runtimeConfig= */ undefined,
      options
    );
  }

  // Setup environment variables on each function.
  for (const endpoint of backend.allEndpoints(wantBackend)) {
    endpoint.environmentVariables = wantBackend.environmentVariables;
  }

  // Enable required APIs. This may come implicitly from triggers (e.g. scheduled triggers
  // require cloudscheudler and, in v1, require pub/sub), or can eventually come from
  // explicit dependencies.
  await Promise.all(
    Object.values(wantBackend.requiredAPIs).map((api) => {
      return ensureApiEnabled.ensure(projectId, api, "functions", /* silent=*/ false);
    })
  );

  // Validate the function code that is being deployed.
  validate.endpointsAreValid(wantBackend);

  // Check what --only filters have been passed in.
  context.filters = getFilterGroups(options);

  const matchingBackend = backend.matchingBackend(wantBackend, (endpoint) => {
    return functionMatchesAnyGroup(endpoint, context.filters);
  });

  const haveBackend = await backend.existingBackend(context);
  await ensureServiceAgentRoles(projectId, wantBackend, haveBackend);
  inferDetailsFromExisting(wantBackend, haveBackend, usedDotenv);
  await ensureTriggerRegions(wantBackend);

  // Display a warning and prompt if any functions in the release have failurePolicies.
  await promptForFailurePolicies(options, matchingBackend, haveBackend);
  await promptForMinInstances(options, matchingBackend, haveBackend);
  await backend.checkAvailability(context, wantBackend);
}