firebase-tools 9.17.0 → 9.21.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 +3 -7
- package/lib/api.js +1 -0
- package/lib/apiv2.js +5 -3
- package/lib/appdistribution/client.js +84 -72
- package/lib/appdistribution/distribution.js +8 -26
- package/lib/appdistribution/options-parser-util.js +51 -0
- package/lib/command.js +1 -1
- package/lib/commands/appdistribution-distribute.js +74 -91
- package/lib/commands/appdistribution-testers-add.js +18 -0
- package/lib/commands/appdistribution-testers-remove.js +32 -0
- package/lib/commands/crashlytics-symbols-upload.js +146 -0
- package/lib/commands/ext-configure.js +9 -1
- package/lib/commands/ext-dev-extension-delete.js +2 -1
- package/lib/commands/ext-dev-publish.js +11 -4
- package/lib/commands/ext-dev-unpublish.js +12 -4
- package/lib/commands/ext-install.js +115 -48
- package/lib/commands/ext-uninstall.js +6 -0
- package/lib/commands/ext-update.js +61 -18
- package/lib/commands/functions-config-export.js +115 -0
- package/lib/commands/functions-delete.js +45 -25
- package/lib/commands/functions-list.js +54 -0
- package/lib/commands/functions-log.js +5 -22
- package/lib/commands/hosting-channel-deploy.js +6 -4
- package/lib/commands/index.js +12 -0
- package/lib/deploy/functions/backend.js +118 -121
- package/lib/deploy/functions/checkIam.js +8 -8
- package/lib/deploy/functions/containerCleaner.js +5 -1
- package/lib/deploy/functions/deploy.js +11 -15
- package/lib/deploy/functions/functionsDeployHelper.js +3 -68
- package/lib/deploy/functions/prepare.js +67 -33
- package/lib/deploy/functions/pricing.js +17 -17
- package/lib/deploy/functions/prompts.js +24 -41
- package/lib/deploy/functions/release/executor.js +39 -0
- package/lib/deploy/functions/release/fabricator.js +362 -0
- package/lib/deploy/functions/release/index.js +69 -0
- package/lib/deploy/functions/release/planner.js +159 -0
- package/lib/deploy/functions/release/reporter.js +162 -0
- package/lib/deploy/functions/release/sourceTokenScraper.js +28 -0
- package/lib/deploy/functions/release/timer.js +14 -0
- package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +102 -126
- package/lib/deploy/functions/runtimes/node/parseTriggers.js +34 -50
- package/lib/deploy/functions/triggerRegionHelper.js +40 -0
- package/lib/deploy/functions/validate.js +1 -24
- package/lib/downloadUtils.js +37 -0
- package/lib/emulator/auth/apiSpec.js +1788 -403
- package/lib/emulator/auth/handlers.js +6 -5
- package/lib/emulator/auth/operations.js +439 -40
- package/lib/emulator/auth/server.js +32 -11
- package/lib/emulator/auth/state.js +205 -5
- package/lib/emulator/auth/widget_ui.js +2 -2
- package/lib/emulator/download.js +2 -31
- package/lib/emulator/downloadableEmulators.js +7 -7
- package/lib/emulator/emulatorLogger.js +0 -3
- package/lib/emulator/events/types.js +16 -0
- package/lib/emulator/functionsEmulator.js +117 -20
- package/lib/emulator/functionsEmulatorRuntime.js +46 -121
- package/lib/emulator/functionsEmulatorShared.js +51 -7
- package/lib/emulator/functionsEmulatorShell.js +1 -1
- package/lib/emulator/pubsubEmulator.js +61 -40
- package/lib/emulator/storage/cloudFunctions.js +37 -7
- package/lib/extensions/askUserForConsent.js +16 -13
- package/lib/extensions/askUserForParam.js +72 -3
- package/lib/extensions/billingMigrationHelper.js +1 -11
- package/lib/extensions/changelog.js +2 -1
- package/lib/extensions/displayExtensionInfo.js +35 -33
- package/lib/extensions/emulator/optionsHelper.js +3 -3
- package/lib/extensions/emulator/triggerHelper.js +2 -32
- package/lib/extensions/extensionsApi.js +67 -94
- package/lib/extensions/extensionsHelper.js +49 -35
- package/lib/extensions/paramHelper.js +79 -36
- package/lib/extensions/refs.js +59 -0
- package/lib/extensions/secretsUtils.js +58 -0
- package/lib/extensions/updateHelper.js +21 -45
- package/lib/extensions/warnings.js +1 -7
- package/lib/functional.js +64 -0
- package/lib/functions/env.js +26 -13
- package/lib/functions/functionslog.js +40 -0
- package/lib/functions/runtimeConfigExport.js +137 -0
- package/lib/gcp/cloudfunctions.js +46 -38
- package/lib/gcp/cloudfunctionsv2.js +47 -47
- package/lib/gcp/cloudlogging.js +27 -21
- package/lib/gcp/cloudscheduler.js +22 -16
- package/lib/gcp/pubsub.js +1 -9
- package/lib/gcp/secretManager.js +111 -0
- package/lib/gcp/storage.js +16 -0
- package/lib/previews.js +1 -1
- package/lib/requireInteractive.js +12 -0
- package/lib/utils.js +30 -1
- package/package.json +5 -4
- package/lib/deploy/functions/deploymentPlanner.js +0 -113
- package/lib/deploy/functions/deploymentTimer.js +0 -23
- package/lib/deploy/functions/errorHandler.js +0 -75
- package/lib/deploy/functions/release.js +0 -116
- package/lib/deploy/functions/tasks.js +0 -324
- package/lib/functionsDelete.js +0 -60
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.readEnvFile = exports.getParamsFromFile = exports.promptForNewParams = exports.getParamsForUpdate = exports.getParams = exports.getParamsWithCurrentValuesAsDefaults = void 0;
|
|
4
4
|
const _ = require("lodash");
|
|
5
5
|
const path = require("path");
|
|
6
6
|
const clc = require("cli-color");
|
|
@@ -25,70 +25,113 @@ function getParamsWithCurrentValuesAsDefaults(extensionInstance) {
|
|
|
25
25
|
return setNewDefaults(specParams, currentParams);
|
|
26
26
|
}
|
|
27
27
|
exports.getParamsWithCurrentValuesAsDefaults = getParamsWithCurrentValuesAsDefaults;
|
|
28
|
-
async function getParams(
|
|
29
|
-
let
|
|
30
|
-
if (
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
28
|
+
async function getParams(args) {
|
|
29
|
+
let params;
|
|
30
|
+
if (args.nonInteractive && !args.paramsEnvPath) {
|
|
31
|
+
const paramsMessage = args.paramSpecs
|
|
32
|
+
.map((p) => {
|
|
33
|
+
return `\t${p.param}${p.required ? "" : " (Optional)"}`;
|
|
34
|
+
})
|
|
35
|
+
.join("\n");
|
|
36
|
+
throw new error_1.FirebaseError("In non-interactive mode but no `--params` flag found. " +
|
|
37
|
+
"To install this extension in non-interactive mode, set `--params` to a path to an .env file" +
|
|
38
|
+
" containing values for this extension's params:\n" +
|
|
39
|
+
paramsMessage);
|
|
40
|
+
}
|
|
41
|
+
else if (args.paramsEnvPath) {
|
|
42
|
+
params = getParamsFromFile({
|
|
43
|
+
projectId: args.projectId,
|
|
44
|
+
paramSpecs: args.paramSpecs,
|
|
45
|
+
noninteractive: args.nonInteractive,
|
|
46
|
+
paramsEnvPath: args.paramsEnvPath,
|
|
47
|
+
});
|
|
40
48
|
}
|
|
41
49
|
else {
|
|
42
|
-
|
|
50
|
+
const firebaseProjectParams = await extensionsHelper_1.getFirebaseProjectParams(args.projectId);
|
|
51
|
+
params = await askUserForParam.ask(args.projectId, args.instanceId, args.paramSpecs, firebaseProjectParams, !!args.reconfiguring);
|
|
43
52
|
}
|
|
44
|
-
|
|
53
|
+
track("Extension Params", _.isEmpty(params) ? "Not Present" : "Present", _.size(params));
|
|
54
|
+
return params;
|
|
55
|
+
}
|
|
56
|
+
exports.getParams = getParams;
|
|
57
|
+
async function getParamsForUpdate(args) {
|
|
45
58
|
let params;
|
|
46
|
-
if (
|
|
47
|
-
|
|
48
|
-
|
|
59
|
+
if (args.nonInteractive && !args.paramsEnvPath) {
|
|
60
|
+
const paramsMessage = args.newSpec.params
|
|
61
|
+
.map((p) => {
|
|
62
|
+
return `\t${p.param}${p.required ? "" : " (Optional)"}`;
|
|
63
|
+
})
|
|
64
|
+
.join("\n");
|
|
65
|
+
throw new error_1.FirebaseError("In non-interactive mode but no `--params` flag found. " +
|
|
66
|
+
"To update this extension in non-interactive mode, set `--params` to a path to an .env file" +
|
|
67
|
+
" containing values for this extension's params:\n" +
|
|
68
|
+
paramsMessage);
|
|
69
|
+
}
|
|
70
|
+
else if (args.paramsEnvPath) {
|
|
71
|
+
params = getParamsFromFile({
|
|
72
|
+
projectId: args.projectId,
|
|
73
|
+
paramSpecs: args.newSpec.params,
|
|
74
|
+
noninteractive: args.nonInteractive,
|
|
75
|
+
paramsEnvPath: args.paramsEnvPath,
|
|
76
|
+
});
|
|
49
77
|
}
|
|
50
78
|
else {
|
|
51
|
-
params = await
|
|
79
|
+
params = await promptForNewParams({
|
|
80
|
+
spec: args.spec,
|
|
81
|
+
newSpec: args.newSpec,
|
|
82
|
+
currentParams: args.currentParams,
|
|
83
|
+
projectId: args.projectId,
|
|
84
|
+
instanceId: args.instanceId,
|
|
85
|
+
});
|
|
52
86
|
}
|
|
53
87
|
track("Extension Params", _.isEmpty(params) ? "Not Present" : "Present", _.size(params));
|
|
54
88
|
return params;
|
|
55
89
|
}
|
|
56
|
-
exports.
|
|
57
|
-
async function promptForNewParams(
|
|
58
|
-
const firebaseProjectParams = await extensionsHelper_1.getFirebaseProjectParams(projectId);
|
|
90
|
+
exports.getParamsForUpdate = getParamsForUpdate;
|
|
91
|
+
async function promptForNewParams(args) {
|
|
92
|
+
const firebaseProjectParams = await extensionsHelper_1.getFirebaseProjectParams(args.projectId);
|
|
59
93
|
const comparer = (param1, param2) => {
|
|
60
94
|
return param1.type === param2.type && param1.param === param2.param;
|
|
61
95
|
};
|
|
62
|
-
let paramsDiffDeletions = _.differenceWith(spec.params, _.get(newSpec, "params", []), comparer);
|
|
96
|
+
let paramsDiffDeletions = _.differenceWith(args.spec.params, _.get(args.newSpec, "params", []), comparer);
|
|
63
97
|
paramsDiffDeletions = extensionsHelper_1.substituteParams(paramsDiffDeletions, firebaseProjectParams);
|
|
64
|
-
let paramsDiffAdditions = _.differenceWith(newSpec.params, _.get(spec, "params", []), comparer);
|
|
98
|
+
let paramsDiffAdditions = _.differenceWith(args.newSpec.params, _.get(args.spec, "params", []), comparer);
|
|
65
99
|
paramsDiffAdditions = extensionsHelper_1.substituteParams(paramsDiffAdditions, firebaseProjectParams);
|
|
66
100
|
if (paramsDiffDeletions.length) {
|
|
67
101
|
logger_1.logger.info("The following params will no longer be used:");
|
|
68
102
|
paramsDiffDeletions.forEach((param) => {
|
|
69
|
-
logger_1.logger.info(clc.red(`- ${param.param}: ${currentParams[param.param.toUpperCase()]}`));
|
|
70
|
-
delete currentParams[param.param.toUpperCase()];
|
|
103
|
+
logger_1.logger.info(clc.red(`- ${param.param}: ${args.currentParams[param.param.toUpperCase()]}`));
|
|
104
|
+
delete args.currentParams[param.param.toUpperCase()];
|
|
71
105
|
});
|
|
72
106
|
}
|
|
73
107
|
if (paramsDiffAdditions.length) {
|
|
74
108
|
logger_1.logger.info("To update this instance, configure the following new parameters:");
|
|
75
109
|
for (const param of paramsDiffAdditions) {
|
|
76
|
-
const chosenValue = await askUserForParam.askForParam(param);
|
|
77
|
-
currentParams[param.param] = chosenValue;
|
|
110
|
+
const chosenValue = await askUserForParam.askForParam(args.projectId, args.instanceId, param, false);
|
|
111
|
+
args.currentParams[param.param] = chosenValue;
|
|
78
112
|
}
|
|
79
113
|
}
|
|
80
|
-
return currentParams;
|
|
114
|
+
return args.currentParams;
|
|
81
115
|
}
|
|
82
116
|
exports.promptForNewParams = promptForNewParams;
|
|
83
|
-
function
|
|
117
|
+
function getParamsFromFile(args) {
|
|
118
|
+
let envParams;
|
|
84
119
|
try {
|
|
85
|
-
|
|
86
|
-
|
|
120
|
+
envParams = readEnvFile(args.paramsEnvPath);
|
|
121
|
+
track("Extension Env File", "Present");
|
|
87
122
|
}
|
|
88
123
|
catch (err) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
});
|
|
124
|
+
track("Extension Env File", "Invalid");
|
|
125
|
+
throw new error_1.FirebaseError(`Error reading env file: ${err.message}\n`, { original: err });
|
|
92
126
|
}
|
|
127
|
+
const params = extensionsHelper_1.populateDefaultParams(envParams, args.paramSpecs);
|
|
128
|
+
extensionsHelper_1.validateCommandLineParams(params, args.paramSpecs);
|
|
129
|
+
logger_1.logger.info(`Using param values from ${args.paramsEnvPath}`);
|
|
130
|
+
return params;
|
|
131
|
+
}
|
|
132
|
+
exports.getParamsFromFile = getParamsFromFile;
|
|
133
|
+
function readEnvFile(envPath) {
|
|
134
|
+
const buf = fs.readFileSync(path.resolve(envPath), "utf8");
|
|
135
|
+
return dotenv.parse(buf.toString().trim(), { debug: true });
|
|
93
136
|
}
|
|
94
|
-
exports.
|
|
137
|
+
exports.readEnvFile = readEnvFile;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.toExtensionVersionName = exports.toExtensionName = exports.toExtensionVersionRef = exports.toExtensionRef = exports.parse = void 0;
|
|
4
|
+
const semver = require("semver");
|
|
5
|
+
const error_1 = require("../error");
|
|
6
|
+
const refRegex = new RegExp(/^([^/@\n]+)\/{1}([^/@\n]+)(@{1}([^\n]+)|)$/);
|
|
7
|
+
function parse(refOrName) {
|
|
8
|
+
const ret = parseRef(refOrName) || parseName(refOrName);
|
|
9
|
+
if (!ret || !ret.publisherId || !ret.extensionId) {
|
|
10
|
+
throw new error_1.FirebaseError(`Unable to parse ${refOrName} as an extension ref`);
|
|
11
|
+
}
|
|
12
|
+
if (ret.version &&
|
|
13
|
+
!semver.valid(ret.version) &&
|
|
14
|
+
!semver.validRange(ret.version) &&
|
|
15
|
+
ret.version !== "latest") {
|
|
16
|
+
throw new error_1.FirebaseError(`Extension reference ${ret} contains an invalid version ${ret.version}.`);
|
|
17
|
+
}
|
|
18
|
+
return ret;
|
|
19
|
+
}
|
|
20
|
+
exports.parse = parse;
|
|
21
|
+
function parseRef(ref) {
|
|
22
|
+
const parts = refRegex.exec(ref);
|
|
23
|
+
if (parts && (parts.length == 5 || parts.length == 7)) {
|
|
24
|
+
const publisherId = parts[1];
|
|
25
|
+
const extensionId = parts[2];
|
|
26
|
+
const version = parts[4];
|
|
27
|
+
return { publisherId, extensionId, version };
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function parseName(name) {
|
|
31
|
+
const parts = name.split("/");
|
|
32
|
+
return {
|
|
33
|
+
publisherId: parts[1],
|
|
34
|
+
extensionId: parts[3],
|
|
35
|
+
version: parts[5],
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function toExtensionRef(ref) {
|
|
39
|
+
return `${ref.publisherId}/${ref.extensionId}`;
|
|
40
|
+
}
|
|
41
|
+
exports.toExtensionRef = toExtensionRef;
|
|
42
|
+
function toExtensionVersionRef(ref) {
|
|
43
|
+
if (!ref.version) {
|
|
44
|
+
throw new error_1.FirebaseError(`Ref does not have a version`);
|
|
45
|
+
}
|
|
46
|
+
return `${ref.publisherId}/${ref.extensionId}@${ref.version}`;
|
|
47
|
+
}
|
|
48
|
+
exports.toExtensionVersionRef = toExtensionVersionRef;
|
|
49
|
+
function toExtensionName(ref) {
|
|
50
|
+
return `publishers/${ref.publisherId}/extensions/${ref.extensionId}`;
|
|
51
|
+
}
|
|
52
|
+
exports.toExtensionName = toExtensionName;
|
|
53
|
+
function toExtensionVersionName(ref) {
|
|
54
|
+
if (!ref.version) {
|
|
55
|
+
throw new error_1.FirebaseError(`Ref does not have a version`);
|
|
56
|
+
}
|
|
57
|
+
return `publishers/${ref.publisherId}/extensions/${ref.extensionId}/versions/${ref.version}`;
|
|
58
|
+
}
|
|
59
|
+
exports.toExtensionVersionName = toExtensionVersionName;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.prettySecretName = exports.getSecretLabels = exports.getManagedSecrets = exports.grantFirexServiceAgentSecretAdminRole = exports.usesSecrets = exports.ensureSecretManagerApiEnabled = void 0;
|
|
4
|
+
const getProjectNumber_1 = require("../getProjectNumber");
|
|
5
|
+
const utils = require("../utils");
|
|
6
|
+
const ensureApiEnabled_1 = require("../ensureApiEnabled");
|
|
7
|
+
const projectUtils_1 = require("../projectUtils");
|
|
8
|
+
const extensionsApi = require("./extensionsApi");
|
|
9
|
+
const secretManagerApi = require("../gcp/secretManager");
|
|
10
|
+
const logger_1 = require("../logger");
|
|
11
|
+
const SECRET_LABEL = "firebase-extensions-managed";
|
|
12
|
+
async function ensureSecretManagerApiEnabled(options) {
|
|
13
|
+
const projectId = projectUtils_1.needProjectId(options);
|
|
14
|
+
return await ensureApiEnabled_1.ensure(projectId, "secretmanager.googleapis.com", "extensions", options.markdown);
|
|
15
|
+
}
|
|
16
|
+
exports.ensureSecretManagerApiEnabled = ensureSecretManagerApiEnabled;
|
|
17
|
+
function usesSecrets(spec) {
|
|
18
|
+
return spec.params && !!spec.params.find((p) => p.type == extensionsApi.ParamType.SECRET);
|
|
19
|
+
}
|
|
20
|
+
exports.usesSecrets = usesSecrets;
|
|
21
|
+
async function grantFirexServiceAgentSecretAdminRole(secret) {
|
|
22
|
+
const projectNumber = await getProjectNumber_1.getProjectNumber({ projectId: secret.projectId });
|
|
23
|
+
const firexSaProjectId = utils.envOverride("FIREBASE_EXTENSIONS_SA_PROJECT_ID", "gcp-sa-firebasemods");
|
|
24
|
+
const saEmail = `service-${projectNumber}@${firexSaProjectId}.iam.gserviceaccount.com`;
|
|
25
|
+
return secretManagerApi.grantServiceAgentRole(secret, saEmail, "roles/secretmanager.admin");
|
|
26
|
+
}
|
|
27
|
+
exports.grantFirexServiceAgentSecretAdminRole = grantFirexServiceAgentSecretAdminRole;
|
|
28
|
+
async function getManagedSecrets(instance) {
|
|
29
|
+
return (await Promise.all(getActiveSecrets(instance).map(async (secretResourceName) => {
|
|
30
|
+
const secret = secretManagerApi.parseSecretResourceName(secretResourceName);
|
|
31
|
+
const labels = await secretManagerApi.getSecretLabels(secret.projectId, secret.name);
|
|
32
|
+
if (labels && labels[SECRET_LABEL]) {
|
|
33
|
+
return secretResourceName;
|
|
34
|
+
}
|
|
35
|
+
return Promise.resolve("");
|
|
36
|
+
}))).filter((secretId) => !!secretId);
|
|
37
|
+
}
|
|
38
|
+
exports.getManagedSecrets = getManagedSecrets;
|
|
39
|
+
function getActiveSecrets(instance) {
|
|
40
|
+
return instance.config.source.spec.params
|
|
41
|
+
.map((p) => p.type == extensionsApi.ParamType.SECRET && instance.config.params[p.param])
|
|
42
|
+
.filter((pv) => !!pv);
|
|
43
|
+
}
|
|
44
|
+
function getSecretLabels(instanceId) {
|
|
45
|
+
const labels = {};
|
|
46
|
+
labels[SECRET_LABEL] = instanceId;
|
|
47
|
+
return labels;
|
|
48
|
+
}
|
|
49
|
+
exports.getSecretLabels = getSecretLabels;
|
|
50
|
+
function prettySecretName(secretResourceName) {
|
|
51
|
+
const nameTokens = secretResourceName.split("/");
|
|
52
|
+
if (nameTokens.length != 4 && nameTokens.length != 6) {
|
|
53
|
+
logger_1.logger.debug(`unable to parse secret secretResourceName: ${secretResourceName}`);
|
|
54
|
+
return secretResourceName;
|
|
55
|
+
}
|
|
56
|
+
return nameTokens.slice(0, 4).join("/");
|
|
57
|
+
}
|
|
58
|
+
exports.prettySecretName = prettySecretName;
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.inferUpdateSource = exports.updateFromPublisherSource = exports.updateToVersionFromPublisherSource = exports.updateFromUrlSource = exports.updateFromLocalSource = exports.update = exports.displayChanges = exports.warningUpdateToOtherSource = exports.getExistingSourceOrigin = void 0;
|
|
4
4
|
const clc = require("cli-color");
|
|
5
5
|
const semver = require("semver");
|
|
6
|
+
const marked = require("marked");
|
|
6
7
|
const error_1 = require("../error");
|
|
7
8
|
const logger_1 = require("../logger");
|
|
8
9
|
const resolveSource = require("./resolveSource");
|
|
9
10
|
const extensionsApi = require("./extensionsApi");
|
|
10
|
-
const
|
|
11
|
-
const marked = require("marked");
|
|
11
|
+
const refs = require("./refs");
|
|
12
12
|
const extensionsHelper_1 = require("./extensionsHelper");
|
|
13
13
|
const utils = require("../utils");
|
|
14
14
|
const displayExtensionInfo_1 = require("./displayExtensionInfo");
|
|
@@ -25,7 +25,7 @@ async function getExistingSourceOrigin(projectId, instanceId, extensionName, exi
|
|
|
25
25
|
: extensionsHelper_1.SourceOrigin.LOCAL;
|
|
26
26
|
}
|
|
27
27
|
exports.getExistingSourceOrigin = getExistingSourceOrigin;
|
|
28
|
-
|
|
28
|
+
function showUpdateVersionInfo(instanceId, from, to, source) {
|
|
29
29
|
if (source) {
|
|
30
30
|
source = clc.bold(source);
|
|
31
31
|
}
|
|
@@ -34,8 +34,7 @@ async function showUpdateVersionInfo(instanceId, from, to, source) {
|
|
|
34
34
|
}
|
|
35
35
|
utils.logLabeledBullet(extensionsHelper_1.logPrefix, `Updating ${clc.bold(instanceId)} from version ${clc.bold(from)} to ${source} (${clc.bold(to)})`);
|
|
36
36
|
if (semver.lt(to, from)) {
|
|
37
|
-
utils.
|
|
38
|
-
return await displayExtensionInfo_1.getConsent("version", "Do you wish to continue?");
|
|
37
|
+
utils.logLabeledWarning(extensionsHelper_1.logPrefix, "The version you are updating to is less than the current version for this extension. This extension may not be backwards compatible.");
|
|
39
38
|
}
|
|
40
39
|
return;
|
|
41
40
|
}
|
|
@@ -54,21 +53,12 @@ function warningUpdateToOtherSource(sourceOrigin) {
|
|
|
54
53
|
logger_1.logger.info(marked(warning));
|
|
55
54
|
}
|
|
56
55
|
exports.warningUpdateToOtherSource = warningUpdateToOtherSource;
|
|
57
|
-
async function displayChanges(
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
displayExtensionInfo_1.
|
|
61
|
-
await displayExtensionInfo_1.displayUpdateChangesRequiringConfirmation(spec, newSpec);
|
|
56
|
+
async function displayChanges(args) {
|
|
57
|
+
utils.logLabeledBullet("extensions", "This update contains the following changes:");
|
|
58
|
+
displayExtensionInfo_1.displayUpdateChangesNoInput(args.spec, args.newSpec);
|
|
59
|
+
await displayExtensionInfo_1.displayUpdateChangesRequiringConfirmation(args);
|
|
62
60
|
}
|
|
63
61
|
exports.displayChanges = displayChanges;
|
|
64
|
-
async function retryUpdate() {
|
|
65
|
-
return prompt_1.promptOnce({
|
|
66
|
-
type: "confirm",
|
|
67
|
-
message: "Are you sure you wish to continue with updating anyways?",
|
|
68
|
-
default: false,
|
|
69
|
-
});
|
|
70
|
-
}
|
|
71
|
-
exports.retryUpdate = retryUpdate;
|
|
72
62
|
async function update(updateOptions) {
|
|
73
63
|
const { projectId, instanceId, source, extRef, params } = updateOptions;
|
|
74
64
|
if (extRef) {
|
|
@@ -80,7 +70,7 @@ async function update(updateOptions) {
|
|
|
80
70
|
throw new error_1.FirebaseError(`Neither a source nor a version of the extension was supplied for ${instanceId}. Please make sure this is a valid extension and try again.`);
|
|
81
71
|
}
|
|
82
72
|
exports.update = update;
|
|
83
|
-
async function updateFromLocalSource(projectId, instanceId, localSource, existingSpec
|
|
73
|
+
async function updateFromLocalSource(projectId, instanceId, localSource, existingSpec) {
|
|
84
74
|
displayExtensionInfo_1.displayExtInfo(instanceId, "", existingSpec, false);
|
|
85
75
|
let source;
|
|
86
76
|
try {
|
|
@@ -90,13 +80,12 @@ async function updateFromLocalSource(projectId, instanceId, localSource, existin
|
|
|
90
80
|
throw new error_1.FirebaseError(invalidSourceErrMsgTemplate(instanceId, localSource));
|
|
91
81
|
}
|
|
92
82
|
utils.logLabeledBullet(extensionsHelper_1.logPrefix, `${clc.bold("You are updating this extension instance to a local source.")}`);
|
|
93
|
-
|
|
83
|
+
showUpdateVersionInfo(instanceId, existingSpec.version, source.spec.version, localSource);
|
|
94
84
|
warningUpdateToOtherSource(extensionsHelper_1.SourceOrigin.LOCAL);
|
|
95
|
-
await confirmUpdate();
|
|
96
85
|
return source.name;
|
|
97
86
|
}
|
|
98
87
|
exports.updateFromLocalSource = updateFromLocalSource;
|
|
99
|
-
async function updateFromUrlSource(projectId, instanceId, urlSource, existingSpec
|
|
88
|
+
async function updateFromUrlSource(projectId, instanceId, urlSource, existingSpec) {
|
|
100
89
|
displayExtensionInfo_1.displayExtInfo(instanceId, "", existingSpec, false);
|
|
101
90
|
let source;
|
|
102
91
|
try {
|
|
@@ -106,18 +95,17 @@ async function updateFromUrlSource(projectId, instanceId, urlSource, existingSpe
|
|
|
106
95
|
throw new error_1.FirebaseError(invalidSourceErrMsgTemplate(instanceId, urlSource));
|
|
107
96
|
}
|
|
108
97
|
utils.logLabeledBullet(extensionsHelper_1.logPrefix, `${clc.bold("You are updating this extension instance to a URL source.")}`);
|
|
109
|
-
|
|
98
|
+
showUpdateVersionInfo(instanceId, existingSpec.version, source.spec.version, urlSource);
|
|
110
99
|
warningUpdateToOtherSource(extensionsHelper_1.SourceOrigin.URL);
|
|
111
|
-
await confirmUpdate();
|
|
112
100
|
return source.name;
|
|
113
101
|
}
|
|
114
102
|
exports.updateFromUrlSource = updateFromUrlSource;
|
|
115
|
-
async function updateToVersionFromPublisherSource(projectId, instanceId, extVersionRef, existingSpec
|
|
103
|
+
async function updateToVersionFromPublisherSource(projectId, instanceId, extVersionRef, existingSpec) {
|
|
116
104
|
let source;
|
|
117
|
-
const
|
|
118
|
-
const version =
|
|
119
|
-
const extensionRef =
|
|
120
|
-
displayExtensionInfo_1.displayExtInfo(instanceId,
|
|
105
|
+
const ref = refs.parse(extVersionRef);
|
|
106
|
+
const version = ref.version;
|
|
107
|
+
const extensionRef = refs.toExtensionRef(ref);
|
|
108
|
+
displayExtensionInfo_1.displayExtInfo(instanceId, ref.publisherId, existingSpec, true);
|
|
121
109
|
const extension = await extensionsApi.getExtension(extensionRef);
|
|
122
110
|
try {
|
|
123
111
|
source = await extensionsApi.getExtensionVersion(extVersionRef);
|
|
@@ -138,7 +126,7 @@ async function updateToVersionFromPublisherSource(projectId, instanceId, extVers
|
|
|
138
126
|
throw new error_1.FirebaseError(`The version you are trying to update to (${clc.bold(source.spec.version)}) is less than the minimum version required (${clc.bold(minVer)}) to use this extension.`);
|
|
139
127
|
}
|
|
140
128
|
}
|
|
141
|
-
|
|
129
|
+
showUpdateVersionInfo(instanceId, existingSpec.version, source.spec.version, extVersionRef);
|
|
142
130
|
warningUpdateToOtherSource(extensionsHelper_1.SourceOrigin.PUBLISHED_EXTENSION);
|
|
143
131
|
const releaseNotes = await changelog.getReleaseNotesForUpdate({
|
|
144
132
|
extensionRef,
|
|
@@ -148,12 +136,11 @@ async function updateToVersionFromPublisherSource(projectId, instanceId, extVers
|
|
|
148
136
|
if (Object.keys(releaseNotes).length) {
|
|
149
137
|
changelog.displayReleaseNotes(releaseNotes, existingSpec.version);
|
|
150
138
|
}
|
|
151
|
-
await confirmUpdate();
|
|
152
139
|
return source.name;
|
|
153
140
|
}
|
|
154
141
|
exports.updateToVersionFromPublisherSource = updateToVersionFromPublisherSource;
|
|
155
|
-
async function updateFromPublisherSource(projectId, instanceId, extRef, existingSpec
|
|
156
|
-
return updateToVersionFromPublisherSource(projectId, instanceId, `${extRef}@latest`, existingSpec
|
|
142
|
+
async function updateFromPublisherSource(projectId, instanceId, extRef, existingSpec) {
|
|
143
|
+
return updateToVersionFromPublisherSource(projectId, instanceId, `${extRef}@latest`, existingSpec);
|
|
157
144
|
}
|
|
158
145
|
exports.updateFromPublisherSource = updateFromPublisherSource;
|
|
159
146
|
function inferUpdateSource(updateSource, existingRef) {
|
|
@@ -174,14 +161,3 @@ function inferUpdateSource(updateSource, existingRef) {
|
|
|
174
161
|
return updateSource;
|
|
175
162
|
}
|
|
176
163
|
exports.inferUpdateSource = inferUpdateSource;
|
|
177
|
-
async function confirmUpdate() {
|
|
178
|
-
const continueUpdate = await prompt_1.promptOnce({
|
|
179
|
-
type: "confirm",
|
|
180
|
-
message: "Do you wish to continue with this update?",
|
|
181
|
-
default: false,
|
|
182
|
-
});
|
|
183
|
-
if (!continueUpdate) {
|
|
184
|
-
throw new error_1.FirebaseError(`Update cancelled.`);
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
exports.confirmUpdate = confirmUpdate;
|
|
@@ -7,7 +7,6 @@ const extensionsApi_1 = require("./extensionsApi");
|
|
|
7
7
|
const displayExtensionInfo_1 = require("./displayExtensionInfo");
|
|
8
8
|
const extensionsHelper_1 = require("./extensionsHelper");
|
|
9
9
|
const resolveSource_1 = require("./resolveSource");
|
|
10
|
-
const prompt_1 = require("../prompt");
|
|
11
10
|
const utils = require("../utils");
|
|
12
11
|
function displayEAPWarning({ publisherId, sourceDownloadUri, githubLink, }) {
|
|
13
12
|
const publisherNameLink = githubLink ? `[${publisherId}](${githubLink})` : publisherId;
|
|
@@ -32,12 +31,7 @@ async function displayWarningPrompts(publisherId, launchStage, extensionVersion)
|
|
|
32
31
|
displayExperimentalWarning();
|
|
33
32
|
}
|
|
34
33
|
else {
|
|
35
|
-
return
|
|
34
|
+
return;
|
|
36
35
|
}
|
|
37
|
-
return await prompt_1.promptOnce({
|
|
38
|
-
type: "confirm",
|
|
39
|
-
message: "Do you acknowledge the status of this extension?",
|
|
40
|
-
default: true,
|
|
41
|
-
});
|
|
42
36
|
}
|
|
43
37
|
exports.displayWarningPrompts = displayWarningPrompts;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.partition = exports.assertExhaustive = exports.zipIn = exports.zip = exports.reduceFlat = exports.flatten = exports.flattenArray = exports.flattenObject = void 0;
|
|
4
|
+
function* flattenObject(obj) {
|
|
5
|
+
function* helper(path, obj) {
|
|
6
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
7
|
+
if (typeof v !== "object" || v === null) {
|
|
8
|
+
yield [[...path, k].join("."), v];
|
|
9
|
+
}
|
|
10
|
+
else {
|
|
11
|
+
yield* helper([...path, k], v);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
yield* helper([], obj);
|
|
16
|
+
}
|
|
17
|
+
exports.flattenObject = flattenObject;
|
|
18
|
+
function* flattenArray(arr) {
|
|
19
|
+
for (const val of arr) {
|
|
20
|
+
if (Array.isArray(val)) {
|
|
21
|
+
yield* flattenArray(val);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
yield val;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
exports.flattenArray = flattenArray;
|
|
29
|
+
function flatten(objOrArr) {
|
|
30
|
+
if (Array.isArray(objOrArr)) {
|
|
31
|
+
return flattenArray(objOrArr);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
return flattenObject(objOrArr);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
exports.flatten = flatten;
|
|
38
|
+
function reduceFlat(accum, next) {
|
|
39
|
+
return [...(accum || []), ...flatten([next])];
|
|
40
|
+
}
|
|
41
|
+
exports.reduceFlat = reduceFlat;
|
|
42
|
+
function* zip(left, right) {
|
|
43
|
+
if (left.length != right.length) {
|
|
44
|
+
throw new Error("Cannot zip between two lists of differen lengths");
|
|
45
|
+
}
|
|
46
|
+
for (let i = 0; i < left.length; i++) {
|
|
47
|
+
yield [left[i], right[i]];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
exports.zip = zip;
|
|
51
|
+
exports.zipIn = (other) => (elem, ndx) => {
|
|
52
|
+
return [elem, other[ndx]];
|
|
53
|
+
};
|
|
54
|
+
function assertExhaustive(val) {
|
|
55
|
+
throw new Error(`Never has a value (${val}). This should be impossible`);
|
|
56
|
+
}
|
|
57
|
+
exports.assertExhaustive = assertExhaustive;
|
|
58
|
+
function partition(arr, callbackFn) {
|
|
59
|
+
return arr.reduce((acc, elem) => {
|
|
60
|
+
acc[callbackFn(elem) ? 0 : 1].push(elem);
|
|
61
|
+
return acc;
|
|
62
|
+
}, [[], []]);
|
|
63
|
+
}
|
|
64
|
+
exports.partition = partition;
|
package/lib/functions/env.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.loadFirebaseEnvs = exports.loadUserEnvs = exports.hasUserEnvs = exports.validateKey = exports.parse = void 0;
|
|
3
|
+
exports.loadFirebaseEnvs = exports.loadUserEnvs = exports.hasUserEnvs = exports.validateKey = exports.KeyValidationError = exports.parse = void 0;
|
|
4
4
|
const clc = require("cli-color");
|
|
5
5
|
const fs = require("fs");
|
|
6
6
|
const path = require("path");
|
|
@@ -8,6 +8,7 @@ const error_1 = require("../error");
|
|
|
8
8
|
const logger_1 = require("../logger");
|
|
9
9
|
const previews_1 = require("../previews");
|
|
10
10
|
const utils_1 = require("../utils");
|
|
11
|
+
const FUNCTIONS_EMULATOR_DOTENV = ".env.local";
|
|
11
12
|
const RESERVED_KEYS = [
|
|
12
13
|
"FIREBASE_CONFIG",
|
|
13
14
|
"CLOUD_RUNTIME_CONFIG",
|
|
@@ -35,7 +36,7 @@ const LINE_RE = new RegExp("^" +
|
|
|
35
36
|
"(" +
|
|
36
37
|
"\\s*'(?:\\\\'|[^'])*'|" +
|
|
37
38
|
'\\s*"(?:\\\\"|[^"])*"|' +
|
|
38
|
-
"[
|
|
39
|
+
"[^#\\r\\n]+" +
|
|
39
40
|
")?" +
|
|
40
41
|
"\\s*" +
|
|
41
42
|
"(?:#[^\\n]*)?" +
|
|
@@ -71,17 +72,23 @@ function parse(data) {
|
|
|
71
72
|
}
|
|
72
73
|
exports.parse = parse;
|
|
73
74
|
class KeyValidationError extends Error {
|
|
75
|
+
constructor(key, message) {
|
|
76
|
+
super(`Failed to validate key ${key}: ${message}`);
|
|
77
|
+
this.key = key;
|
|
78
|
+
this.message = message;
|
|
79
|
+
}
|
|
74
80
|
}
|
|
81
|
+
exports.KeyValidationError = KeyValidationError;
|
|
75
82
|
function validateKey(key) {
|
|
76
83
|
if (RESERVED_KEYS.includes(key)) {
|
|
77
|
-
throw new KeyValidationError(`Key ${key} is reserved for internal use.`);
|
|
84
|
+
throw new KeyValidationError(key, `Key ${key} is reserved for internal use.`);
|
|
78
85
|
}
|
|
79
86
|
if (!/^[A-Z_][A-Z0-9_]*$/.test(key)) {
|
|
80
|
-
throw new KeyValidationError(`Key ${key} must start with an uppercase ASCII letter or underscore` +
|
|
87
|
+
throw new KeyValidationError(key, `Key ${key} must start with an uppercase ASCII letter or underscore` +
|
|
81
88
|
", and then consist of uppercase ASCII letters, digits, and underscores.");
|
|
82
89
|
}
|
|
83
90
|
if (key.startsWith("X_GOOGLE_") || key.startsWith("FIREBASE_")) {
|
|
84
|
-
throw new KeyValidationError(`Key ${key} starts with a reserved prefix (X_GOOGLE_ or FIREBASE_)`);
|
|
91
|
+
throw new KeyValidationError(key, `Key ${key} starts with a reserved prefix (X_GOOGLE_ or FIREBASE_)`);
|
|
85
92
|
}
|
|
86
93
|
}
|
|
87
94
|
exports.validateKey = validateKey;
|
|
@@ -110,26 +117,32 @@ function parseStrict(data) {
|
|
|
110
117
|
}
|
|
111
118
|
return envs;
|
|
112
119
|
}
|
|
113
|
-
function findEnvfiles(functionsSource, projectId, projectAlias) {
|
|
114
|
-
const files = [".env"
|
|
115
|
-
if (
|
|
116
|
-
files.push(
|
|
120
|
+
function findEnvfiles(functionsSource, projectId, projectAlias, isEmulator) {
|
|
121
|
+
const files = [".env"];
|
|
122
|
+
if (isEmulator) {
|
|
123
|
+
files.push(FUNCTIONS_EMULATOR_DOTENV);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
files.push(`.env.${projectId}`);
|
|
127
|
+
if (projectAlias && projectAlias.length) {
|
|
128
|
+
files.push(`.env.${projectAlias}`);
|
|
129
|
+
}
|
|
117
130
|
}
|
|
118
131
|
return files
|
|
119
132
|
.map((f) => path.join(functionsSource, f))
|
|
120
133
|
.filter(fs.existsSync)
|
|
121
134
|
.map((p) => path.basename(p));
|
|
122
135
|
}
|
|
123
|
-
function hasUserEnvs({ functionsSource, projectId, projectAlias }) {
|
|
124
|
-
return findEnvfiles(functionsSource, projectId, projectAlias).length > 0;
|
|
136
|
+
function hasUserEnvs({ functionsSource, projectId, projectAlias, isEmulator, }) {
|
|
137
|
+
return findEnvfiles(functionsSource, projectId, projectAlias, isEmulator).length > 0;
|
|
125
138
|
}
|
|
126
139
|
exports.hasUserEnvs = hasUserEnvs;
|
|
127
|
-
function loadUserEnvs({ functionsSource, projectId, projectAlias, }) {
|
|
140
|
+
function loadUserEnvs({ functionsSource, projectId, projectAlias, isEmulator, }) {
|
|
128
141
|
var _a;
|
|
129
142
|
if (!previews_1.previews.dotenv) {
|
|
130
143
|
return {};
|
|
131
144
|
}
|
|
132
|
-
const envFiles = findEnvfiles(functionsSource, projectId, projectAlias);
|
|
145
|
+
const envFiles = findEnvfiles(functionsSource, projectId, projectAlias, isEmulator);
|
|
133
146
|
if (envFiles.length == 0) {
|
|
134
147
|
return {};
|
|
135
148
|
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.logEntries = exports.getApiFilter = void 0;
|
|
4
|
+
const logger_1 = require("../logger");
|
|
5
|
+
const previews_1 = require("../previews");
|
|
6
|
+
function getApiFilter(functionList) {
|
|
7
|
+
const baseFilter = previews_1.previews.functionsv2
|
|
8
|
+
? 'resource.type="cloud_function" OR ' +
|
|
9
|
+
'(resource.type="cloud_run_revision" AND ' +
|
|
10
|
+
'labels."goog-managed-by"="cloudfunctions")'
|
|
11
|
+
: 'resource.type="cloud_function"';
|
|
12
|
+
if (functionList) {
|
|
13
|
+
const apiFuncFilters = functionList.split(",").map((fn) => {
|
|
14
|
+
return previews_1.previews.functionsv2
|
|
15
|
+
? `resource.labels.function_name="${fn}" ` + `OR resource.labels.service_name="${fn}"`
|
|
16
|
+
: `resource.labels.function_name="${fn}"`;
|
|
17
|
+
});
|
|
18
|
+
return baseFilter + `\n(${apiFuncFilters.join(" OR ")})`;
|
|
19
|
+
}
|
|
20
|
+
return baseFilter;
|
|
21
|
+
}
|
|
22
|
+
exports.getApiFilter = getApiFilter;
|
|
23
|
+
function logEntries(entries) {
|
|
24
|
+
if (!entries || entries.length === 0) {
|
|
25
|
+
logger_1.logger.info("No log entries found.");
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
for (let i = entries.length - 1; i >= 0; i--) {
|
|
29
|
+
const entry = entries[i];
|
|
30
|
+
const timestamp = entry.timestamp || "---";
|
|
31
|
+
const severity = (entry.severity || "?").substring(0, 1);
|
|
32
|
+
const name = entry.resource.labels.function_name || entry.resource.labels.service_name;
|
|
33
|
+
const message = entry.textPayload ||
|
|
34
|
+
JSON.stringify(entry.jsonPayload) ||
|
|
35
|
+
JSON.stringify(entry.protoPayload) ||
|
|
36
|
+
"";
|
|
37
|
+
logger_1.logger.info(`${timestamp} ${severity} ${name}: ${message}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
exports.logEntries = logEntries;
|