firebase-tools 9.16.6 → 9.20.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/api.js +1 -0
- package/lib/apiv2.js +1 -1
- 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 +8 -6
- 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-init.js +18 -9
- package/lib/commands/ext-dev-publish.js +11 -4
- package/lib/commands/ext-dev-unpublish.js +2 -1
- package/lib/commands/ext-install.js +115 -48
- package/lib/commands/ext-uninstall.js +6 -0
- package/lib/commands/ext-update.js +67 -43
- package/lib/commands/functions-config-export.js +115 -0
- package/lib/commands/functions-delete.js +44 -35
- 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 +47 -12
- package/lib/deploy/functions/containerCleaner.js +5 -1
- package/lib/deploy/functions/deploy.js +7 -5
- package/lib/deploy/functions/prepare.js +9 -7
- package/lib/deploy/functions/prompts.js +3 -21
- package/lib/deploy/functions/runtimes/discovery/v1alpha1.js +2 -1
- package/lib/deploy/functions/runtimes/index.js +2 -1
- package/lib/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.js +4 -3
- package/lib/deploy/functions/runtimes/node/parseTriggers.js +14 -9
- package/lib/deploy/functions/triggerRegionHelper.js +32 -0
- package/lib/downloadUtils.js +37 -0
- package/lib/emulator/auth/apiSpec.js +1758 -404
- package/lib/emulator/auth/handlers.js +6 -5
- package/lib/emulator/auth/operations.js +429 -40
- package/lib/emulator/auth/server.js +18 -11
- package/lib/emulator/auth/state.js +186 -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 +102 -17
- 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/extensions/askUserForConsent.js +16 -13
- package/lib/extensions/askUserForParam.js +72 -3
- package/lib/extensions/billingMigrationHelper.js +1 -11
- package/lib/extensions/changelog.js +93 -0
- package/lib/extensions/displayExtensionInfo.js +38 -38
- package/lib/extensions/emulator/optionsHelper.js +3 -3
- package/lib/extensions/emulator/triggerHelper.js +2 -32
- package/lib/extensions/extensionsApi.js +69 -95
- package/lib/extensions/extensionsHelper.js +75 -50
- package/lib/extensions/paramHelper.js +79 -36
- package/lib/extensions/refs.js +59 -0
- package/lib/extensions/resolveSource.js +2 -20
- package/lib/extensions/secretsUtils.js +58 -0
- package/lib/extensions/updateHelper.js +39 -105
- 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/listFunctions.js +10 -0
- package/lib/functions/runtimeConfigExport.js +137 -0
- package/lib/gcp/cloudfunctions.js +84 -9
- package/lib/gcp/cloudfunctionsv2.js +99 -7
- package/lib/gcp/cloudlogging.js +27 -21
- 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/package.json +5 -4
- package/schema/firebase-config.json +2 -1
- package/templates/extensions/CHANGELOG.md +7 -0
- package/templates/init/hosting/index.html +10 -10
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.inferUpdateSource = 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");
|
|
15
|
+
const changelog = require("./changelog");
|
|
15
16
|
function invalidSourceErrMsgTemplate(instanceId, source) {
|
|
16
17
|
return `Unable to update from the source \`${clc.bold(source)}\`. To update this instance, you can either:\n
|
|
17
18
|
- Run \`${clc.bold("firebase ext:update " + instanceId)}\` to update from the published source.\n
|
|
@@ -19,26 +20,12 @@ function invalidSourceErrMsgTemplate(instanceId, source) {
|
|
|
19
20
|
}
|
|
20
21
|
async function getExistingSourceOrigin(projectId, instanceId, extensionName, existingSource) {
|
|
21
22
|
const instance = await extensionsApi.getInstance(projectId, instanceId);
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
let existingSourceOrigin;
|
|
26
|
-
try {
|
|
27
|
-
const registryEntry = await resolveSource.resolveRegistryEntry(extensionName);
|
|
28
|
-
if (resolveSource.isOfficialSource(registryEntry, existingSource)) {
|
|
29
|
-
existingSourceOrigin = extensionsHelper_1.SourceOrigin.OFFICIAL_EXTENSION;
|
|
30
|
-
}
|
|
31
|
-
else {
|
|
32
|
-
existingSourceOrigin = extensionsHelper_1.SourceOrigin.LOCAL;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
catch (_a) {
|
|
36
|
-
existingSourceOrigin = extensionsHelper_1.SourceOrigin.LOCAL;
|
|
37
|
-
}
|
|
38
|
-
return existingSourceOrigin;
|
|
23
|
+
return instance && instance.config.extensionRef
|
|
24
|
+
? extensionsHelper_1.SourceOrigin.PUBLISHED_EXTENSION
|
|
25
|
+
: extensionsHelper_1.SourceOrigin.LOCAL;
|
|
39
26
|
}
|
|
40
27
|
exports.getExistingSourceOrigin = getExistingSourceOrigin;
|
|
41
|
-
|
|
28
|
+
function showUpdateVersionInfo(instanceId, from, to, source) {
|
|
42
29
|
if (source) {
|
|
43
30
|
source = clc.bold(source);
|
|
44
31
|
}
|
|
@@ -47,19 +34,13 @@ async function showUpdateVersionInfo(instanceId, from, to, source) {
|
|
|
47
34
|
}
|
|
48
35
|
utils.logLabeledBullet(extensionsHelper_1.logPrefix, `Updating ${clc.bold(instanceId)} from version ${clc.bold(from)} to ${source} (${clc.bold(to)})`);
|
|
49
36
|
if (semver.lt(to, from)) {
|
|
50
|
-
utils.
|
|
51
|
-
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.");
|
|
52
38
|
}
|
|
53
39
|
return;
|
|
54
40
|
}
|
|
55
|
-
|
|
41
|
+
function warningUpdateToOtherSource(sourceOrigin) {
|
|
56
42
|
let targetText;
|
|
57
|
-
if ([
|
|
58
|
-
extensionsHelper_1.SourceOrigin.PUBLISHED_EXTENSION,
|
|
59
|
-
extensionsHelper_1.SourceOrigin.PUBLISHED_EXTENSION_VERSION,
|
|
60
|
-
extensionsHelper_1.SourceOrigin.OFFICIAL_EXTENSION,
|
|
61
|
-
extensionsHelper_1.SourceOrigin.OFFICIAL_EXTENSION_VERSION,
|
|
62
|
-
].includes(sourceOrigin)) {
|
|
43
|
+
if ([extensionsHelper_1.SourceOrigin.PUBLISHED_EXTENSION, extensionsHelper_1.SourceOrigin.PUBLISHED_EXTENSION_VERSION].includes(sourceOrigin)) {
|
|
63
44
|
targetText = "published extension";
|
|
64
45
|
}
|
|
65
46
|
else if (sourceOrigin === extensionsHelper_1.SourceOrigin.LOCAL) {
|
|
@@ -70,31 +51,14 @@ async function warningUpdateToOtherSource(sourceOrigin) {
|
|
|
70
51
|
}
|
|
71
52
|
const warning = `All the instance's resources and logic will be overwritten to use the source code and files from the ${targetText}.\n`;
|
|
72
53
|
logger_1.logger.info(marked(warning));
|
|
73
|
-
const continueUpdate = await prompt_1.promptOnce({
|
|
74
|
-
type: "confirm",
|
|
75
|
-
message: "Do you wish to continue with this update?",
|
|
76
|
-
default: false,
|
|
77
|
-
});
|
|
78
|
-
if (!continueUpdate) {
|
|
79
|
-
throw new error_1.FirebaseError(`Update cancelled.`, { exit: 2 });
|
|
80
|
-
}
|
|
81
54
|
}
|
|
82
55
|
exports.warningUpdateToOtherSource = warningUpdateToOtherSource;
|
|
83
|
-
async function displayChanges(
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
displayExtensionInfo_1.
|
|
87
|
-
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);
|
|
88
60
|
}
|
|
89
61
|
exports.displayChanges = displayChanges;
|
|
90
|
-
async function retryUpdate() {
|
|
91
|
-
return prompt_1.promptOnce({
|
|
92
|
-
type: "confirm",
|
|
93
|
-
message: "Are you sure you wish to continue with updating anyways?",
|
|
94
|
-
default: false,
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
exports.retryUpdate = retryUpdate;
|
|
98
62
|
async function update(updateOptions) {
|
|
99
63
|
const { projectId, instanceId, source, extRef, params } = updateOptions;
|
|
100
64
|
if (extRef) {
|
|
@@ -106,7 +70,7 @@ async function update(updateOptions) {
|
|
|
106
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.`);
|
|
107
71
|
}
|
|
108
72
|
exports.update = update;
|
|
109
|
-
async function updateFromLocalSource(projectId, instanceId, localSource, existingSpec
|
|
73
|
+
async function updateFromLocalSource(projectId, instanceId, localSource, existingSpec) {
|
|
110
74
|
displayExtensionInfo_1.displayExtInfo(instanceId, "", existingSpec, false);
|
|
111
75
|
let source;
|
|
112
76
|
try {
|
|
@@ -116,12 +80,12 @@ async function updateFromLocalSource(projectId, instanceId, localSource, existin
|
|
|
116
80
|
throw new error_1.FirebaseError(invalidSourceErrMsgTemplate(instanceId, localSource));
|
|
117
81
|
}
|
|
118
82
|
utils.logLabeledBullet(extensionsHelper_1.logPrefix, `${clc.bold("You are updating this extension instance to a local source.")}`);
|
|
119
|
-
|
|
120
|
-
|
|
83
|
+
showUpdateVersionInfo(instanceId, existingSpec.version, source.spec.version, localSource);
|
|
84
|
+
warningUpdateToOtherSource(extensionsHelper_1.SourceOrigin.LOCAL);
|
|
121
85
|
return source.name;
|
|
122
86
|
}
|
|
123
87
|
exports.updateFromLocalSource = updateFromLocalSource;
|
|
124
|
-
async function updateFromUrlSource(projectId, instanceId, urlSource, existingSpec
|
|
88
|
+
async function updateFromUrlSource(projectId, instanceId, urlSource, existingSpec) {
|
|
125
89
|
displayExtensionInfo_1.displayExtInfo(instanceId, "", existingSpec, false);
|
|
126
90
|
let source;
|
|
127
91
|
try {
|
|
@@ -131,17 +95,18 @@ async function updateFromUrlSource(projectId, instanceId, urlSource, existingSpe
|
|
|
131
95
|
throw new error_1.FirebaseError(invalidSourceErrMsgTemplate(instanceId, urlSource));
|
|
132
96
|
}
|
|
133
97
|
utils.logLabeledBullet(extensionsHelper_1.logPrefix, `${clc.bold("You are updating this extension instance to a URL source.")}`);
|
|
134
|
-
|
|
135
|
-
|
|
98
|
+
showUpdateVersionInfo(instanceId, existingSpec.version, source.spec.version, urlSource);
|
|
99
|
+
warningUpdateToOtherSource(extensionsHelper_1.SourceOrigin.URL);
|
|
136
100
|
return source.name;
|
|
137
101
|
}
|
|
138
102
|
exports.updateFromUrlSource = updateFromUrlSource;
|
|
139
|
-
async function updateToVersionFromPublisherSource(projectId, instanceId, extVersionRef, existingSpec
|
|
103
|
+
async function updateToVersionFromPublisherSource(projectId, instanceId, extVersionRef, existingSpec) {
|
|
140
104
|
let source;
|
|
141
|
-
const
|
|
142
|
-
const version =
|
|
143
|
-
|
|
144
|
-
|
|
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);
|
|
109
|
+
const extension = await extensionsApi.getExtension(extensionRef);
|
|
145
110
|
try {
|
|
146
111
|
source = await extensionsApi.getExtensionVersion(extVersionRef);
|
|
147
112
|
}
|
|
@@ -149,66 +114,35 @@ async function updateToVersionFromPublisherSource(projectId, instanceId, extVers
|
|
|
149
114
|
throw new error_1.FirebaseError(`Could not find source '${clc.bold(extVersionRef)}' because (${clc.bold(version)}) is not a published version. To update, use the latest version of this extension (${clc.bold(extension.latestVersion)}).`);
|
|
150
115
|
}
|
|
151
116
|
let registryEntry;
|
|
152
|
-
let sourceOrigin;
|
|
153
117
|
try {
|
|
154
118
|
registryEntry = await resolveSource.resolveRegistryEntry(existingSpec.name);
|
|
155
|
-
sourceOrigin =
|
|
156
|
-
registryEntry.publisher === refObj.publisherId
|
|
157
|
-
? extensionsHelper_1.SourceOrigin.OFFICIAL_EXTENSION
|
|
158
|
-
: extensionsHelper_1.SourceOrigin.PUBLISHED_EXTENSION;
|
|
159
119
|
}
|
|
160
120
|
catch (err) {
|
|
161
|
-
|
|
121
|
+
logger_1.logger.debug(`Unable to fetch registry.json entry for ${existingSpec.name}`);
|
|
162
122
|
}
|
|
163
|
-
utils.logLabeledBullet(extensionsHelper_1.logPrefix, `${clc.bold(`You are updating this extension instance to a(n) ${sourceOrigin}.`)}`);
|
|
164
123
|
if (registryEntry) {
|
|
165
124
|
const minVer = resolveSource.getMinRequiredVersion(registryEntry);
|
|
166
125
|
if (minVer && semver.gt(minVer, source.spec.version)) {
|
|
167
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.`);
|
|
168
127
|
}
|
|
169
128
|
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
129
|
+
showUpdateVersionInfo(instanceId, existingSpec.version, source.spec.version, extVersionRef);
|
|
130
|
+
warningUpdateToOtherSource(extensionsHelper_1.SourceOrigin.PUBLISHED_EXTENSION);
|
|
131
|
+
const releaseNotes = await changelog.getReleaseNotesForUpdate({
|
|
132
|
+
extensionRef,
|
|
133
|
+
fromVersion: existingSpec.version,
|
|
134
|
+
toVersion: source.spec.version,
|
|
135
|
+
});
|
|
136
|
+
if (Object.keys(releaseNotes).length) {
|
|
137
|
+
changelog.displayReleaseNotes(releaseNotes, existingSpec.version);
|
|
174
138
|
}
|
|
175
139
|
return source.name;
|
|
176
140
|
}
|
|
177
141
|
exports.updateToVersionFromPublisherSource = updateToVersionFromPublisherSource;
|
|
178
|
-
async function updateFromPublisherSource(projectId, instanceId, extRef, existingSpec
|
|
179
|
-
return updateToVersionFromPublisherSource(projectId, instanceId, `${extRef}@latest`, existingSpec
|
|
142
|
+
async function updateFromPublisherSource(projectId, instanceId, extRef, existingSpec) {
|
|
143
|
+
return updateToVersionFromPublisherSource(projectId, instanceId, `${extRef}@latest`, existingSpec);
|
|
180
144
|
}
|
|
181
145
|
exports.updateFromPublisherSource = updateFromPublisherSource;
|
|
182
|
-
async function updateToVersionFromRegistryFile(projectId, instanceId, existingSpec, existingSource, version) {
|
|
183
|
-
if (version !== "latest" && !semver.valid(version)) {
|
|
184
|
-
throw new error_1.FirebaseError(`cannot update to invalid version ${version}`);
|
|
185
|
-
}
|
|
186
|
-
let registryEntry;
|
|
187
|
-
try {
|
|
188
|
-
registryEntry = await resolveSource.resolveRegistryEntry(existingSpec.name);
|
|
189
|
-
}
|
|
190
|
-
catch (err) {
|
|
191
|
-
throw new error_1.FirebaseError(`Cannot find the latest version of this extension. To update this instance to a local source or URL source, run "firebase ext:update ${instanceId} <localSourceOrURL>".`);
|
|
192
|
-
}
|
|
193
|
-
displayExtensionInfo_1.displayExtInfo(instanceId, registryEntry.publisher, existingSpec, true);
|
|
194
|
-
utils.logLabeledBullet(extensionsHelper_1.logPrefix, clc.bold("You are updating this extension instance to an official source."));
|
|
195
|
-
const minVer = resolveSource.getMinRequiredVersion(registryEntry);
|
|
196
|
-
if (minVer) {
|
|
197
|
-
if (version !== "latest" && semver.gt(minVer, version)) {
|
|
198
|
-
throw new error_1.FirebaseError(`The version you are trying to upgrade to (${clc.bold(version)}) is less than the minimum version required (${clc.bold(minVer)}) to use this extension.`);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
const targetVersion = resolveSource.getTargetVersion(registryEntry, version);
|
|
202
|
-
await showUpdateVersionInfo(instanceId, existingSpec.version, targetVersion);
|
|
203
|
-
await warningUpdateToOtherSource(extensionsHelper_1.SourceOrigin.OFFICIAL_EXTENSION);
|
|
204
|
-
await resolveSource.promptForUpdateWarnings(registryEntry, existingSpec.version, targetVersion);
|
|
205
|
-
return resolveSource.resolveSourceUrl(registryEntry, existingSpec.name, targetVersion);
|
|
206
|
-
}
|
|
207
|
-
exports.updateToVersionFromRegistryFile = updateToVersionFromRegistryFile;
|
|
208
|
-
async function updateFromRegistryFile(projectId, instanceId, existingSpec, existingSource) {
|
|
209
|
-
return updateToVersionFromRegistryFile(projectId, instanceId, existingSpec, existingSource, "latest");
|
|
210
|
-
}
|
|
211
|
-
exports.updateFromRegistryFile = updateFromRegistryFile;
|
|
212
146
|
function inferUpdateSource(updateSource, existingRef) {
|
|
213
147
|
if (!updateSource) {
|
|
214
148
|
return `${existingRef}@latest`;
|
|
@@ -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;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.listFunctions = void 0;
|
|
4
|
+
const backend = require("../deploy/functions/backend");
|
|
5
|
+
async function listFunctions(context) {
|
|
6
|
+
const functionSpecs = (await backend.existingBackend(context, true)).cloudFunctions;
|
|
7
|
+
functionSpecs.sort(backend.compareFunctions);
|
|
8
|
+
return { functions: functionSpecs };
|
|
9
|
+
}
|
|
10
|
+
exports.listFunctions = listFunctions;
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.generateDotenvFilename = exports.toDotenvFormat = exports.hydrateEnvs = exports.configToEnv = exports.convertKey = exports.hydrateConfigs = exports.getProjectInfos = void 0;
|
|
4
|
+
const clc = require("cli-color");
|
|
5
|
+
const env = require("./env");
|
|
6
|
+
const functionsConfig = require("../functionsConfig");
|
|
7
|
+
const error_1 = require("../error");
|
|
8
|
+
const logger_1 = require("../logger");
|
|
9
|
+
const projectUtils_1 = require("../projectUtils");
|
|
10
|
+
const rc_1 = require("../rc");
|
|
11
|
+
const utils_1 = require("../utils");
|
|
12
|
+
const functional_1 = require("../functional");
|
|
13
|
+
function getProjectInfos(options) {
|
|
14
|
+
const result = {};
|
|
15
|
+
const rc = rc_1.loadRC(options);
|
|
16
|
+
if (rc.projects) {
|
|
17
|
+
for (const [alias, projectId] of Object.entries(rc.projects)) {
|
|
18
|
+
if (Object.keys(result).includes(projectId)) {
|
|
19
|
+
utils_1.logWarning(`Multiple aliases found for ${clc.bold(projectId)}. ` +
|
|
20
|
+
`Preferring alias (${clc.bold(result[projectId])}) over (${clc.bold(alias)}).`);
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
result[projectId] = alias;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const projectId = projectUtils_1.getProjectId(options);
|
|
27
|
+
if (projectId && !Object.keys(result).includes(projectId)) {
|
|
28
|
+
result[projectId] = projectId;
|
|
29
|
+
}
|
|
30
|
+
return Object.entries(result).map(([k, v]) => {
|
|
31
|
+
const result = { projectId: k };
|
|
32
|
+
if (k !== v) {
|
|
33
|
+
result.alias = v;
|
|
34
|
+
}
|
|
35
|
+
return result;
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
exports.getProjectInfos = getProjectInfos;
|
|
39
|
+
async function hydrateConfigs(pInfos) {
|
|
40
|
+
const hydrate = pInfos.map((info) => {
|
|
41
|
+
return functionsConfig
|
|
42
|
+
.materializeAll(info.projectId)
|
|
43
|
+
.then((config) => {
|
|
44
|
+
info.config = config;
|
|
45
|
+
return;
|
|
46
|
+
})
|
|
47
|
+
.catch((err) => {
|
|
48
|
+
logger_1.logger.debug(`Failed to fetch runtime config for project ${info.projectId}: ${err.message}`);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
await Promise.all(hydrate);
|
|
52
|
+
}
|
|
53
|
+
exports.hydrateConfigs = hydrateConfigs;
|
|
54
|
+
function convertKey(configKey, prefix) {
|
|
55
|
+
const baseKey = configKey
|
|
56
|
+
.toUpperCase()
|
|
57
|
+
.replace(/\./g, "_")
|
|
58
|
+
.replace(/-/g, "_");
|
|
59
|
+
let envKey = baseKey;
|
|
60
|
+
try {
|
|
61
|
+
env.validateKey(envKey);
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
if (err instanceof env.KeyValidationError) {
|
|
65
|
+
envKey = prefix + envKey;
|
|
66
|
+
env.validateKey(envKey);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return envKey;
|
|
70
|
+
}
|
|
71
|
+
exports.convertKey = convertKey;
|
|
72
|
+
function configToEnv(configs, prefix) {
|
|
73
|
+
const success = [];
|
|
74
|
+
const errors = [];
|
|
75
|
+
for (const [configKey, value] of functional_1.flatten(configs)) {
|
|
76
|
+
try {
|
|
77
|
+
const envKey = convertKey(configKey, prefix);
|
|
78
|
+
success.push({ origKey: configKey, newKey: envKey, value: value });
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
if (err instanceof env.KeyValidationError) {
|
|
82
|
+
errors.push({
|
|
83
|
+
origKey: configKey,
|
|
84
|
+
newKey: err.key,
|
|
85
|
+
err: err.message,
|
|
86
|
+
value: value,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
throw new error_1.FirebaseError("Unexpected error while converting config", {
|
|
91
|
+
exit: 2,
|
|
92
|
+
original: err,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return { success, errors };
|
|
98
|
+
}
|
|
99
|
+
exports.configToEnv = configToEnv;
|
|
100
|
+
function hydrateEnvs(pInfos, prefix) {
|
|
101
|
+
let errMsg = "";
|
|
102
|
+
for (const pInfo of pInfos) {
|
|
103
|
+
const { success, errors } = configToEnv(pInfo.config, prefix);
|
|
104
|
+
if (errors.length > 0) {
|
|
105
|
+
const msg = `${pInfo.projectId} ` +
|
|
106
|
+
`${pInfo.alias ? "(" + pInfo.alias + ")" : ""}:\n` +
|
|
107
|
+
errors.map((err) => `\t${err.origKey} => ${clc.bold(err.newKey)} (${err.err})`).join("\n") +
|
|
108
|
+
"\n";
|
|
109
|
+
errMsg += msg;
|
|
110
|
+
}
|
|
111
|
+
else {
|
|
112
|
+
pInfo.envs = success;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return errMsg;
|
|
116
|
+
}
|
|
117
|
+
exports.hydrateEnvs = hydrateEnvs;
|
|
118
|
+
function escape(s) {
|
|
119
|
+
const result = s
|
|
120
|
+
.replace("\n", "\\n")
|
|
121
|
+
.replace("\r", "\\r")
|
|
122
|
+
.replace("\t", "\\t")
|
|
123
|
+
.replace("\v", "\\v");
|
|
124
|
+
return result.replace(/(['"])/g, "\\$1");
|
|
125
|
+
}
|
|
126
|
+
function toDotenvFormat(envs, header = "") {
|
|
127
|
+
const lines = envs.map(({ newKey, value }) => `${newKey}="${escape(value)}"`);
|
|
128
|
+
const maxLineLen = Math.max(...lines.map((l) => l.length));
|
|
129
|
+
return (`${header}\n` +
|
|
130
|
+
lines.map((line, idx) => `${line.padEnd(maxLineLen)} # from ${envs[idx].origKey}`).join("\n"));
|
|
131
|
+
}
|
|
132
|
+
exports.toDotenvFormat = toDotenvFormat;
|
|
133
|
+
function generateDotenvFilename(pInfo) {
|
|
134
|
+
var _a;
|
|
135
|
+
return `.env.${(_a = pInfo.alias) !== null && _a !== void 0 ? _a : pInfo.projectId}`;
|
|
136
|
+
}
|
|
137
|
+
exports.generateDotenvFilename = generateDotenvFilename;
|