firebase-tools
Version:
Command-Line Interface for Firebase
156 lines (155 loc) • 6.56 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.shouldUploadBeSkipped = exports.deploy = exports.uploadSourceV2 = exports.createReadStream = void 0;
const tmp_1 = require("tmp");
const clc = require("colorette");
const fs = require("fs");
const checkIam_1 = require("./checkIam");
const utils_1 = require("../../utils");
const projectConfig_1 = require("../../functions/projectConfig");
const gcs = require("../../gcp/storage");
const gcf = require("../../gcp/cloudfunctions");
const gcfv2 = require("../../gcp/cloudfunctionsv2");
const backend = require("./backend");
const experiments = require("../../experiments");
const backend_1 = require("./backend");
const extensions_1 = require("../extensions");
const getProjectNumber_1 = require("../../getProjectNumber");
(0, tmp_1.setGracefulCleanup)();
async function uploadSourceV1(projectId, source, wantBackend) {
const v1Endpoints = backend.allEndpoints(wantBackend).filter((e) => e.platform === "gcfv1");
if (v1Endpoints.length === 0) {
return;
}
const region = v1Endpoints[0].region;
const uploadUrl = await gcf.generateUploadUrl(projectId, region);
const uploadOpts = {
file: source.functionsSourceV1,
stream: fs.createReadStream(source.functionsSourceV1),
};
if (process.env.GOOGLE_CLOUD_QUOTA_PROJECT) {
(0, utils_1.logLabeledWarning)("functions", "GOOGLE_CLOUD_QUOTA_PROJECT is not usable when uploading source for Cloud Functions.");
}
await gcs.upload(uploadOpts, uploadUrl, {
"x-goog-content-length-range": "0,104857600",
}, true);
return uploadUrl;
}
function createReadStream(filePath) {
return fs.createReadStream(filePath);
}
exports.createReadStream = createReadStream;
async function uploadSourceV2(projectId, projectNumber, source, wantBackend) {
const v2Endpoints = backend
.allEndpoints(wantBackend)
.filter((e) => e.platform === "gcfv2" || e.platform === "run");
if (v2Endpoints.length === 0) {
return;
}
const region = v2Endpoints[0].region;
const uploadOpts = {
file: source.functionsSourceV2,
stream: exports.createReadStream(source.functionsSourceV2),
};
if (!experiments.isEnabled("runfunctions")) {
if (process.env.GOOGLE_CLOUD_QUOTA_PROJECT) {
(0, utils_1.logLabeledWarning)("functions", "GOOGLE_CLOUD_QUOTA_PROJECT is not usable when uploading source for Cloud Functions.");
}
const res = await gcfv2.generateUploadUrl(projectId, region);
await gcs.upload(uploadOpts, res.uploadUrl, undefined, true);
return res.storageSource;
}
const baseName = `firebase-functions-src-${projectNumber}`;
const bucketName = await gcs.upsertBucket({
product: "functions",
projectId,
createMessage: `Creating Cloud Storage bucket in ${region} to store Functions source code uploads at ${baseName}...`,
req: {
baseName,
location: region,
purposeLabel: `functions-source-${region.toLowerCase()}`,
lifecycle: {
rule: [
{
action: { type: "Delete" },
condition: { age: 1 },
},
],
},
},
});
const objectPath = `${source.functionsSourceV2Hash}.zip`;
await gcs.upload(uploadOpts, `${bucketName}/${objectPath}`, undefined, true);
return {
bucket: bucketName,
object: objectPath,
};
}
exports.uploadSourceV2 = uploadSourceV2;
async function uploadCodebase(context, projectNumber, codebase, wantBackend) {
var _a, _b, _c, _d;
const source = (_a = context.sources) === null || _a === void 0 ? void 0 : _a[codebase];
if (!source || (!source.functionsSourceV1 && !source.functionsSourceV2)) {
return;
}
const uploads = [];
try {
uploads.push(uploadSourceV1(context.projectId, source, wantBackend));
uploads.push(uploadSourceV2(context.projectId, projectNumber, source, wantBackend));
const [sourceUrl, storage] = await Promise.all(uploads);
if (sourceUrl) {
source.sourceUrl = sourceUrl;
}
if (storage) {
source.storage = storage;
}
const cfg = (0, projectConfig_1.configForCodebase)(context.config, codebase);
const label = (_d = (_b = cfg.source) !== null && _b !== void 0 ? _b : (_c = cfg.remoteSource) === null || _c === void 0 ? void 0 : _c.dir) !== null && _d !== void 0 ? _d : "remote";
if (uploads.length) {
(0, utils_1.logLabeledSuccess)("functions", `${clc.bold(label)} source uploaded successfully`);
}
}
catch (err) {
(0, utils_1.logWarning)(clc.yellow("functions:") + " Upload Error: " + err.message);
throw err;
}
}
async function deploy(context, options, payload) {
if (payload.extensions && context.extensions) {
await (0, extensions_1.deploy)(context.extensions, options, payload.extensions);
}
if (payload.functions && context.config) {
await (0, checkIam_1.checkHttpIam)(context, options, payload);
const uploads = [];
for (const [codebase, { wantBackend, haveBackend }] of Object.entries(payload.functions)) {
if (shouldUploadBeSkipped(context, wantBackend, haveBackend)) {
continue;
}
const projectNumber = options.projectNumber || (await (0, getProjectNumber_1.getProjectNumber)(context.projectId));
uploads.push(uploadCodebase(context, projectNumber, codebase, wantBackend));
}
await Promise.all(uploads);
}
}
exports.deploy = deploy;
function shouldUploadBeSkipped(context, wantBackend, haveBackend) {
if (context.filters && context.filters.length > 0) {
return false;
}
const wantEndpoints = backend.allEndpoints(wantBackend);
const haveEndpoints = backend.allEndpoints(haveBackend);
if (wantEndpoints.length !== haveEndpoints.length) {
return false;
}
return wantEndpoints.every((wantEndpoint) => {
const haveEndpoint = (0, backend_1.findEndpoint)(haveBackend, (endpoint) => endpoint.id === wantEndpoint.id);
if (!haveEndpoint) {
return false;
}
return (haveEndpoint.hash &&
wantEndpoint.hash &&
haveEndpoint.hash === wantEndpoint.hash &&
haveEndpoint.state === "ACTIVE");
});
}
exports.shouldUploadBeSkipped = shouldUploadBeSkipped;