UNPKG

firebase-tools

Version:
114 lines (113 loc) 5.36 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.determineDeploymentEvent = determineDeploymentEvent; exports.executeLifecycleHooks = executeLifecycleHooks; exports.executeHook = executeHook; const backend = require("../backend"); const error_1 = require("../../../error"); const logger_1 = require("../../../logger"); const utils_1 = require("../../../utils"); const cloudtasks = require("../../../gcp/cloudtasks"); const computeEngine = require("../../../gcp/computeEngine"); const projects_1 = require("../../../management/projects"); const functional_1 = require("../../../functional"); function determineDeploymentEvent(haveBackend) { const hasExistingEndpoints = backend.someEndpoint(haveBackend, () => true); if (!hasExistingEndpoints) { return "afterFirstDeploy"; } return "afterRedeploy"; } async function executeLifecycleHooks(wantBackend, haveBackend, plan, codebase) { const event = determineDeploymentEvent(haveBackend); const hooks = wantBackend.lifecycleHooks || {}; const hook = hooks[event]; if (!hook) { logger_1.logger.debug(`No lifecycle hook configured for event: ${event}`); return false; } if (event === "afterRedeploy" && plan) { const codebasePlans = codebase ? [plan[codebase]].filter(Boolean) : Object.values(plan); const hasResourceModifications = codebasePlans.some((codebasePlan) => Object.values(codebasePlan.regionalChangesets).some((changeset) => changeset.endpointsToCreate.length > 0 || changeset.endpointsToUpdate.length > 0 || changeset.endpointsToDelete.length > 0)); if (!hasResourceModifications) { (0, utils_1.logLabeledBullet)("functions", `No resources modified for codebase: ${codebase ?? "default"}. Skipping afterRedeploy lifecycle hook.`); return false; } } try { await executeHook(event, hook, wantBackend); return true; } catch (err) { const errorMsg = err instanceof Error ? err.message : String(err); (0, utils_1.logLabeledWarning)("functions", `Failed to execute ${event} lifecycle hook: ${errorMsg}`); (0, utils_1.logLabeledBullet)("functions", `You can retry the lifecycle hook in isolation by running: firebase functions:lifecycle:run ${event} ${codebase ?? "default"}`); return false; } } async function executeTaskQueueHook(taskHook, wantBackend) { const targetEndpoint = backend.findEndpoint(wantBackend, (ep) => ep.id === taskHook.function); if (!targetEndpoint) { throw new error_1.FirebaseError(`Target endpoint "${taskHook.function}" not found in backend for lifecycle hook.`); } if (!backend.isTaskQueueTriggered(targetEndpoint)) { throw new error_1.FirebaseError(`Target endpoint "${taskHook.function}" is not a task queue function.`); } const queueName = cloudtasks.queueNameForEndpoint(targetEndpoint); const bodyStr = taskHook.body ? JSON.stringify(taskHook.body) : ""; const body = bodyStr ? Buffer.from(bodyStr).toString("base64") : undefined; const url = targetEndpoint.uri; if (!url) { throw new error_1.FirebaseError(`Target endpoint "${taskHook.function}" does not have a trigger URI.`); } const projectMetadata = await (0, projects_1.getProject)(targetEndpoint.project); const projectNumber = projectMetadata.projectNumber; const sa = targetEndpoint.serviceAccount || (await computeEngine.getDefaultServiceAccount(projectNumber)); const task = { httpRequest: { url, httpMethod: "POST", headers: { "Content-Type": "application/json", }, oidcToken: { serviceAccountEmail: sa, audience: url, }, }, }; if (body) { task.httpRequest.body = body; } await cloudtasks.enqueueTask(queueName, task); (0, utils_1.logLabeledSuccess)("functions", `Successfully queued task for lifecycle hook ${taskHook.function} in queue ${queueName}.`); return targetEndpoint; } function getCloudConsoleLogUrl(endpoint) { const { project, region, id } = endpoint; const serviceName = endpoint.runServiceId || id; const query = `resource.type="cloud_run_revision"\nresource.labels.service_name="${serviceName}"\nresource.labels.location="${region}"`; return `https://console.cloud.google.com/logs/query;query=${encodeURIComponent(query)};project=${project}`; } async function executeHook(event, hook, backendSpec) { let executedEndpoint; if ("task" in hook) { (0, utils_1.logLabeledBullet)("functions", `Executing ${event} lifecycle hook targeting: ${hook.task.function}...`); executedEndpoint = await executeTaskQueueHook(hook.task, backendSpec); } else if ("call" in hook) { throw new error_1.FirebaseError(`Lifecycle hook action type "call" is not supported.`); } else if ("http" in hook) { throw new error_1.FirebaseError(`Lifecycle hook action type "http" is not supported.`); } else { (0, functional_1.assertExhaustive)(hook); } if (executedEndpoint) { (0, utils_1.logLabeledBullet)("functions", `View logs for ${event} at: ${getCloudConsoleLogUrl(executedEndpoint)}`); } return executedEndpoint; }