firebase-tools 9.22.0 → 9.23.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.
- package/CHANGELOG.md +1 -3
- package/lib/deploy/extensions/params.js +39 -0
- package/lib/deploy/extensions/planner.js +11 -12
- package/lib/deploy/extensions/prepare.js +9 -1
- package/lib/deploy/functions/containerCleaner.js +41 -40
- package/lib/deploy/functions/prepare.js +1 -0
- package/lib/deploy/index.js +9 -1
- package/lib/gcp/cloudfunctions.js +2 -9
- package/lib/previews.js +1 -1
- package/lib/projectUtils.js +10 -1
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1 @@
|
|
|
1
|
-
-
|
|
2
|
-
- Fixes issue where `init` would crash with multiple Hosting items selected (#3742).
|
|
3
|
-
- Adds a command (`crashlytics:symbols:upload`) to upload native symbol files, used in Android NDK crash symbolication.
|
|
1
|
+
- `firebase deploy --only extensions` now supports project specifc .env files. When deploying to multiple projects, param values that are different between projects can be put in `extensions/${extensionInstanceId}.env.${projectIdOrAlias}` and common param values can be put in `extensions/${extensionInstanceId}.env`.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.readParams = void 0;
|
|
4
|
+
const path = require("path");
|
|
5
|
+
const logger_1 = require("../../logger");
|
|
6
|
+
const paramHelper_1 = require("../../extensions/paramHelper");
|
|
7
|
+
const error_1 = require("../../error");
|
|
8
|
+
const ENV_DIRECTORY = "extensions";
|
|
9
|
+
function readParams(args) {
|
|
10
|
+
const filesToCheck = [
|
|
11
|
+
`${args.instanceId}.env`,
|
|
12
|
+
...args.aliases.map((alias) => `${args.instanceId}.env.${alias}`),
|
|
13
|
+
`${args.instanceId}.env.${args.projectNumber}`,
|
|
14
|
+
`${args.instanceId}.env.${args.projectId}`,
|
|
15
|
+
];
|
|
16
|
+
let noFilesFound = true;
|
|
17
|
+
const combinedParams = {};
|
|
18
|
+
for (const fileToCheck of filesToCheck) {
|
|
19
|
+
try {
|
|
20
|
+
const params = readParamsFile(args.projectDir, fileToCheck);
|
|
21
|
+
logger_1.logger.debug(`Successfully read params from ${fileToCheck}`);
|
|
22
|
+
noFilesFound = false;
|
|
23
|
+
Object.assign(combinedParams, params);
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
logger_1.logger.debug(`${err}`);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
if (noFilesFound) {
|
|
30
|
+
throw new error_1.FirebaseError(`No params file found for ${args.instanceId}`);
|
|
31
|
+
}
|
|
32
|
+
return combinedParams;
|
|
33
|
+
}
|
|
34
|
+
exports.readParams = readParams;
|
|
35
|
+
function readParamsFile(projectDir, fileName) {
|
|
36
|
+
const paramPath = path.join(projectDir, ENV_DIRECTORY, fileName);
|
|
37
|
+
const params = paramHelper_1.readEnvFile(paramPath);
|
|
38
|
+
return params;
|
|
39
|
+
}
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.resolveVersion = exports.want = exports.have = exports.getExtension = exports.getExtensionVersion = void 0;
|
|
4
|
-
const path = require("path");
|
|
5
4
|
const semver = require("semver");
|
|
6
5
|
const error_1 = require("../../error");
|
|
7
6
|
const extensionsApi = require("../../extensions/extensionsApi");
|
|
8
7
|
const extensionsHelper_1 = require("../../extensions/extensionsHelper");
|
|
9
8
|
const refs = require("../../extensions/refs");
|
|
10
|
-
const
|
|
9
|
+
const params_1 = require("./params");
|
|
11
10
|
const logger_1 = require("../../logger");
|
|
12
11
|
async function getExtensionVersion(i) {
|
|
13
12
|
if (!i.extensionVersion) {
|
|
@@ -29,7 +28,6 @@ async function getExtension(i) {
|
|
|
29
28
|
return i.extension;
|
|
30
29
|
}
|
|
31
30
|
exports.getExtension = getExtension;
|
|
32
|
-
const ENV_DIRECTORY = "extensions";
|
|
33
31
|
async function have(projectId) {
|
|
34
32
|
const instances = await extensionsApi.listInstances(projectId);
|
|
35
33
|
return instances.map((i) => {
|
|
@@ -46,16 +44,22 @@ async function have(projectId) {
|
|
|
46
44
|
});
|
|
47
45
|
}
|
|
48
46
|
exports.have = have;
|
|
49
|
-
async function want(
|
|
47
|
+
async function want(args) {
|
|
50
48
|
const instanceSpecs = [];
|
|
51
49
|
const errors = [];
|
|
52
|
-
for (const e of Object.entries(extensions)) {
|
|
50
|
+
for (const e of Object.entries(args.extensions)) {
|
|
53
51
|
try {
|
|
54
52
|
const instanceId = e[0];
|
|
55
53
|
const ref = refs.parse(e[1]);
|
|
56
54
|
ref.version = await resolveVersion(ref);
|
|
57
|
-
const params = readParams(
|
|
58
|
-
|
|
55
|
+
const params = params_1.readParams({
|
|
56
|
+
projectDir: args.projectDir,
|
|
57
|
+
instanceId,
|
|
58
|
+
projectId: args.projectId,
|
|
59
|
+
projectNumber: args.projectNumber,
|
|
60
|
+
aliases: args.aliases,
|
|
61
|
+
});
|
|
62
|
+
const autoPopulatedParams = await extensionsHelper_1.getFirebaseProjectParams(args.projectId);
|
|
59
63
|
const subbedParams = extensionsHelper_1.substituteParams(params, autoPopulatedParams);
|
|
60
64
|
instanceSpecs.push({
|
|
61
65
|
instanceId,
|
|
@@ -88,8 +92,3 @@ async function resolveVersion(ref) {
|
|
|
88
92
|
return maxSatisfying;
|
|
89
93
|
}
|
|
90
94
|
exports.resolveVersion = resolveVersion;
|
|
91
|
-
function readParams(projectDir, instanceId) {
|
|
92
|
-
const paramPath = path.join(projectDir, ENV_DIRECTORY, `${instanceId}.env`);
|
|
93
|
-
const params = paramHelper_1.readEnvFile(paramPath);
|
|
94
|
-
return params;
|
|
95
|
-
}
|
|
@@ -16,10 +16,18 @@ const warnings_1 = require("../../extensions/warnings");
|
|
|
16
16
|
async function prepare(context, options, payload) {
|
|
17
17
|
var _a;
|
|
18
18
|
const projectId = projectUtils_1.needProjectId(options);
|
|
19
|
+
const projectNumber = await projectUtils_1.needProjectNumber(options);
|
|
20
|
+
const aliases = projectUtils_1.getAliases(options, projectId);
|
|
19
21
|
await extensionsHelper_1.ensureExtensionsApiEnabled(options);
|
|
20
22
|
await requirePermissions_1.requirePermissions(options, ["firebaseextensions.instances.list"]);
|
|
21
23
|
context.have = await planner.have(projectId);
|
|
22
|
-
context.want = await planner.want(
|
|
24
|
+
context.want = await planner.want({
|
|
25
|
+
projectId,
|
|
26
|
+
projectNumber,
|
|
27
|
+
aliases,
|
|
28
|
+
projectDir: options.config.projectDir,
|
|
29
|
+
extensions: options.config.get("extensions"),
|
|
30
|
+
});
|
|
23
31
|
const usingSecrets = await Promise.all((_a = context.have) === null || _a === void 0 ? void 0 : _a.map(secrets_1.checkSpecForSecrets));
|
|
24
32
|
if (usingSecrets.some((i) => i)) {
|
|
25
33
|
await secretsUtils_1.ensureSecretManagerApiEnabled(options);
|
|
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.DockerHelper = exports.deleteGcfArtifacts = exports.listGcfPaths = exports.ContainerRegistryCleaner = exports.ArtifactRegistryCleaner = exports.cleanupBuildImages = exports.SUBDOMAIN_MAPPING = void 0;
|
|
4
4
|
const clc = require("cli-color");
|
|
5
5
|
const error_1 = require("../../error");
|
|
6
|
-
const previews_1 = require("../../previews");
|
|
7
6
|
const api_1 = require("../../api");
|
|
8
7
|
const logger_1 = require("../../logger");
|
|
9
8
|
const artifactregistry = require("../../gcp/artifactregistry");
|
|
@@ -62,44 +61,37 @@ async function retry(func) {
|
|
|
62
61
|
async function cleanupBuildImages(haveFunctions, deletedFunctions, cleaners = {}) {
|
|
63
62
|
utils.logBullet(clc.bold.cyan("functions: ") + "cleaning up build files...");
|
|
64
63
|
const failedDomains = new Set();
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
}
|
|
97
|
-
catch (err) {
|
|
98
|
-
const path = `${func.project}/${exports.SUBDOMAIN_MAPPING[func.region]}/gcf`;
|
|
99
|
-
failedDomains.add(`https://console.cloud.google.com/gcr/images/${path}`);
|
|
100
|
-
}
|
|
101
|
-
}));
|
|
102
|
-
}
|
|
64
|
+
const cleanup = [];
|
|
65
|
+
const arCleaner = cleaners.ar || new ArtifactRegistryCleaner();
|
|
66
|
+
cleanup.push(...haveFunctions.map(async (func) => {
|
|
67
|
+
try {
|
|
68
|
+
await arCleaner.cleanupFunction(func);
|
|
69
|
+
}
|
|
70
|
+
catch (err) {
|
|
71
|
+
const path = `${func.project}/${func.region}/gcf-artifacts`;
|
|
72
|
+
failedDomains.add(`https://console.cloud.google.com/artifacts/docker/${path}`);
|
|
73
|
+
}
|
|
74
|
+
}));
|
|
75
|
+
cleanup.push(...deletedFunctions.map(async (func) => {
|
|
76
|
+
try {
|
|
77
|
+
await Promise.all([arCleaner.cleanupFunction(func), arCleaner.cleanupFunctionCache(func)]);
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
const path = `${func.project}/${func.region}/gcf-artifacts`;
|
|
81
|
+
failedDomains.add(`https://console.cloud.google.com/artifacts/docker/${path}`);
|
|
82
|
+
}
|
|
83
|
+
}));
|
|
84
|
+
const gcrCleaner = cleaners.gcr || new ContainerRegistryCleaner();
|
|
85
|
+
cleanup.push(...[...haveFunctions, ...deletedFunctions].map(async (func) => {
|
|
86
|
+
try {
|
|
87
|
+
await gcrCleaner.cleanupFunction(func);
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
const path = `${func.project}/${exports.SUBDOMAIN_MAPPING[func.region]}/gcf`;
|
|
91
|
+
failedDomains.add(`https://console.cloud.google.com/gcr/images/${path}`);
|
|
92
|
+
}
|
|
93
|
+
}));
|
|
94
|
+
await Promise.all(cleanup);
|
|
103
95
|
if (failedDomains.size) {
|
|
104
96
|
let message = "Unhandled error cleaning up build images. This could result in a small monthly bill if not corrected. ";
|
|
105
97
|
message +=
|
|
@@ -119,7 +111,16 @@ class ArtifactRegistryCleaner {
|
|
|
119
111
|
return `projects/${func.project}/locations/${func.region}/repositories/gcf-artifacts/packages/${func.id}`;
|
|
120
112
|
}
|
|
121
113
|
async cleanupFunction(func) {
|
|
122
|
-
|
|
114
|
+
let op;
|
|
115
|
+
try {
|
|
116
|
+
op = await artifactregistry.deletePackage(ArtifactRegistryCleaner.packagePath(func));
|
|
117
|
+
}
|
|
118
|
+
catch (err) {
|
|
119
|
+
if (err.status === 404) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
throw err;
|
|
123
|
+
}
|
|
123
124
|
if (op.done) {
|
|
124
125
|
return;
|
|
125
126
|
}
|
|
@@ -39,6 +39,7 @@ async function prepare(context, options, payload) {
|
|
|
39
39
|
ensureApiEnabled.ensure(projectId, "cloudfunctions.googleapis.com", "functions"),
|
|
40
40
|
ensureApiEnabled.check(projectId, "runtimeconfig.googleapis.com", "runtimeconfig", true),
|
|
41
41
|
ensureCloudBuildEnabled_1.ensureCloudBuildEnabled(projectId),
|
|
42
|
+
ensureApiEnabled.ensure(projectId, "artifactregistry.googleapis.com", "functions"),
|
|
42
43
|
]);
|
|
43
44
|
context.runtimeConfigEnabled = checkAPIsEnabled[1];
|
|
44
45
|
const firebaseConfig = await functionsConfig.getFirebaseConfig(options);
|
package/lib/deploy/index.js
CHANGED
|
@@ -38,6 +38,7 @@ var deploy = function (targetNames, options, customContext = {}) {
|
|
|
38
38
|
var deploys = [];
|
|
39
39
|
var releases = [];
|
|
40
40
|
var postdeploys = [];
|
|
41
|
+
var startTime = Date.now();
|
|
41
42
|
for (var i = 0; i < targetNames.length; i++) {
|
|
42
43
|
var targetName = targetNames[i];
|
|
43
44
|
var target = TARGETS[targetName];
|
|
@@ -75,8 +76,15 @@ var deploy = function (targetNames, options, customContext = {}) {
|
|
|
75
76
|
})
|
|
76
77
|
.then(function () {
|
|
77
78
|
if (_.has(options, "config.notes.databaseRules")) {
|
|
78
|
-
track("Rules Deploy", options.config.notes.databaseRules);
|
|
79
|
+
return track("Rules Deploy", options.config.notes.databaseRules);
|
|
79
80
|
}
|
|
81
|
+
return;
|
|
82
|
+
})
|
|
83
|
+
.then(function () {
|
|
84
|
+
const duration = Date.now() - startTime;
|
|
85
|
+
return track("Product Deploy", [...targetNames].sort().join(","), duration);
|
|
86
|
+
})
|
|
87
|
+
.then(function () {
|
|
80
88
|
logger.info();
|
|
81
89
|
utils.logSuccess(clc.underline.bold("Deploy complete!"));
|
|
82
90
|
logger.info();
|
|
@@ -4,7 +4,6 @@ exports.functionFromEndpoint = exports.endpointFromFunction = exports.listAllFun
|
|
|
4
4
|
const clc = require("cli-color");
|
|
5
5
|
const error_1 = require("../error");
|
|
6
6
|
const logger_1 = require("../logger");
|
|
7
|
-
const previews_1 = require("../previews");
|
|
8
7
|
const api = require("../api");
|
|
9
8
|
const backend = require("../deploy/functions/backend");
|
|
10
9
|
const utils = require("../utils");
|
|
@@ -51,11 +50,8 @@ async function createFunction(cloudFunction) {
|
|
|
51
50
|
const apiPath = cloudFunction.name.substring(0, cloudFunction.name.lastIndexOf("/"));
|
|
52
51
|
const endpoint = `/${exports.API_VERSION}/${apiPath}`;
|
|
53
52
|
try {
|
|
54
|
-
const headers = previews_1.previews.artifactregistry
|
|
55
|
-
? { "X-Firebase-Artifact-Registry": "optin" }
|
|
56
|
-
: undefined;
|
|
57
53
|
const res = await api.request("POST", endpoint, {
|
|
58
|
-
headers,
|
|
54
|
+
headers: { "X-Firebase-Artifact-Registry": "optin" },
|
|
59
55
|
auth: true,
|
|
60
56
|
data: cloudFunction,
|
|
61
57
|
origin: api.functionsOrigin,
|
|
@@ -150,11 +146,8 @@ async function updateFunction(cloudFunction) {
|
|
|
150
146
|
const endpoint = `/${exports.API_VERSION}/${cloudFunction.name}`;
|
|
151
147
|
const fieldMasks = proto.fieldMasks(cloudFunction, "labels", "environmentVariables");
|
|
152
148
|
try {
|
|
153
|
-
const headers = previews_1.previews.artifactregistry
|
|
154
|
-
? { "X-Firebase-Artifact-Registry": "optin" }
|
|
155
|
-
: undefined;
|
|
156
149
|
const res = await api.request("PATCH", endpoint, {
|
|
157
|
-
headers,
|
|
150
|
+
headers: { "X-Firebase-Artifact-Registry": "optin" },
|
|
158
151
|
qs: {
|
|
159
152
|
updateMask: fieldMasks.join(","),
|
|
160
153
|
},
|
package/lib/previews.js
CHANGED
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.previews = void 0;
|
|
4
4
|
const lodash_1 = require("lodash");
|
|
5
5
|
const configstore_1 = require("./configstore");
|
|
6
|
-
exports.previews = Object.assign({ rtdbrules: false, ext: false, extdev: false, rtdbmanagement: false, functionsv2: false, golang: false, deletegcfartifacts: false, dotenv: false
|
|
6
|
+
exports.previews = Object.assign({ rtdbrules: false, ext: false, extdev: false, rtdbmanagement: false, functionsv2: false, golang: false, deletegcfartifacts: false, dotenv: false }, configstore_1.configstore.get("previews"));
|
|
7
7
|
if (process.env.FIREBASE_CLI_PREVIEWS) {
|
|
8
8
|
process.env.FIREBASE_CLI_PREVIEWS.split(",").forEach((feature) => {
|
|
9
9
|
if (lodash_1.has(exports.previews, feature)) {
|
package/lib/projectUtils.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.needProjectNumber = exports.needProjectId = exports.getProjectId = void 0;
|
|
3
|
+
exports.getAliases = exports.needProjectNumber = exports.needProjectId = exports.getProjectId = void 0;
|
|
4
4
|
const projects_1 = require("./management/projects");
|
|
5
5
|
const clc = require("cli-color");
|
|
6
6
|
const marked = require("marked");
|
|
@@ -48,3 +48,12 @@ async function needProjectNumber(options) {
|
|
|
48
48
|
return options.projectNumber;
|
|
49
49
|
}
|
|
50
50
|
exports.needProjectNumber = needProjectNumber;
|
|
51
|
+
function getAliases(options, projectId) {
|
|
52
|
+
if (options.rc.hasProjects) {
|
|
53
|
+
return Object.entries(options.rc.projects)
|
|
54
|
+
.filter((entry) => entry[1] === projectId)
|
|
55
|
+
.map((entry) => entry[0]);
|
|
56
|
+
}
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
exports.getAliases = getAliases;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "firebase-tools",
|
|
3
|
-
"version": "9.
|
|
3
|
+
"version": "9.23.0",
|
|
4
4
|
"description": "Command-Line Interface for Firebase",
|
|
5
5
|
"main": "./lib/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -94,7 +94,7 @@
|
|
|
94
94
|
"chokidar": "^3.0.2",
|
|
95
95
|
"cjson": "^0.3.1",
|
|
96
96
|
"cli-color": "^1.2.0",
|
|
97
|
-
"cli-table": "^0.3.
|
|
97
|
+
"cli-table": "^0.3.8",
|
|
98
98
|
"commander": "^4.0.1",
|
|
99
99
|
"configstore": "^5.0.1",
|
|
100
100
|
"cors": "^2.8.5",
|
|
@@ -138,7 +138,7 @@
|
|
|
138
138
|
"universal-analytics": "^0.4.16",
|
|
139
139
|
"unzipper": "^0.10.10",
|
|
140
140
|
"update-notifier": "^5.1.0",
|
|
141
|
-
"uuid": "^3.
|
|
141
|
+
"uuid": "^8.3.2",
|
|
142
142
|
"winston": "^3.0.0",
|
|
143
143
|
"winston-transport": "^4.4.0",
|
|
144
144
|
"ws": "^7.2.3"
|
|
@@ -182,7 +182,7 @@
|
|
|
182
182
|
"@types/tmp": "^0.1.0",
|
|
183
183
|
"@types/triple-beam": "^1.3.0",
|
|
184
184
|
"@types/unzipper": "^0.10.0",
|
|
185
|
-
"@types/uuid": "^3.
|
|
185
|
+
"@types/uuid": "^8.3.1",
|
|
186
186
|
"@types/winston": "^2.4.4",
|
|
187
187
|
"@types/ws": "^7.2.3",
|
|
188
188
|
"@typescript-eslint/eslint-plugin": "^4.12.0",
|