firebase-tools 12.4.1 → 12.4.3
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/lib/commands/ext-configure.js +0 -1
- package/lib/commands/ext-install.js +56 -56
- package/lib/commands/ext-update.js +0 -1
- package/lib/commands/index.js +0 -2
- package/lib/commands/internaltesting-frameworks-compose.js +4 -2
- package/lib/commands/open.js +2 -2
- package/lib/deploy/extensions/planner.js +8 -6
- package/lib/deploy/functions/ensure.js +0 -3
- package/lib/deploy/functions/prepare.js +12 -17
- package/lib/deploy/functions/release/index.js +1 -1
- package/lib/deploy/functions/release/reporter.js +41 -24
- package/lib/deploy/functions/runtimes/node/parseRuntimeAndValidateSDK.js +0 -3
- package/lib/deploy/functions/runtimes/node/versioning.js +0 -2
- package/lib/deploy/lifecycleHooks.js +25 -6
- package/lib/emulator/downloadableEmulators.js +3 -3
- package/lib/extensions/displayExtensionInfo.js +104 -42
- package/lib/extensions/emulator/triggerHelper.js +1 -1
- package/lib/extensions/extensionsApi.js +1 -1
- package/lib/extensions/extensionsHelper.js +19 -30
- package/lib/extensions/publisherApi.js +1 -2
- package/lib/extensions/updateHelper.js +2 -2
- package/lib/frameworks/compose/discover/filesystem.js +1 -1
- package/lib/frameworks/compose/discover/index.js +27 -18
- package/lib/frameworks/compose/discover/runtime/node.js +149 -0
- package/lib/frameworks/compose/driver/docker.js +23 -13
- package/lib/frameworks/compose/driver/local.js +13 -4
- package/lib/frameworks/compose/index.js +9 -8
- package/lib/frameworks/next/index.js +13 -4
- package/lib/{api → gcp}/frameworks.js +7 -5
- package/lib/init/features/frameworks/index.js +31 -1
- package/lib/init/features/{composer → frameworks}/repo.js +1 -4
- package/package.json +1 -1
- package/schema/firebase-config.json +4 -0
- package/lib/commands/internaltesting-frameworks-init.js +0 -14
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.retrieveRoleInfo = exports.
|
|
3
|
+
exports.retrieveRoleInfo = exports.displayResources = exports.displayEvents = exports.displayExternalServices = exports.displayExtensionVersionInfo = void 0;
|
|
4
4
|
const clc = require("colorette");
|
|
5
5
|
const marked_1 = require("marked");
|
|
6
|
+
const semver = require("semver");
|
|
6
7
|
const TerminalRenderer = require("marked-terminal");
|
|
7
|
-
const
|
|
8
|
-
const
|
|
8
|
+
const path = require("path");
|
|
9
|
+
const refs = require("../extensions/refs");
|
|
9
10
|
const logger_1 = require("../logger");
|
|
10
|
-
const error_1 = require("../error");
|
|
11
11
|
const types_1 = require("./types");
|
|
12
12
|
const iam = require("../gcp/iam");
|
|
13
13
|
const secretsUtils_1 = require("./secretsUtils");
|
|
@@ -16,23 +16,59 @@ marked_1.marked.setOptions({
|
|
|
16
16
|
});
|
|
17
17
|
const TASKS_ROLE = "cloudtasks.enqueuer";
|
|
18
18
|
const TASKS_API = "cloudtasks.googleapis.com";
|
|
19
|
-
async function
|
|
19
|
+
async function displayExtensionVersionInfo(args) {
|
|
20
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
21
|
+
const { spec, extensionVersion, latestApprovedVersion, latestVersion } = args;
|
|
20
22
|
const lines = [];
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
23
|
+
const extensionRef = extensionVersion
|
|
24
|
+
? refs.toExtensionRef(refs.parse(extensionVersion === null || extensionVersion === void 0 ? void 0 : extensionVersion.ref))
|
|
25
|
+
: "";
|
|
26
|
+
lines.push(`${clc.bold("Extension:")} ${(_a = spec.displayName) !== null && _a !== void 0 ? _a : "Unnamed extension"} ${extensionRef ? `(${extensionRef})` : ""}`);
|
|
25
27
|
if (spec.description) {
|
|
26
|
-
lines.push(
|
|
28
|
+
lines.push(`${clc.bold("Description:")} ${spec.description}`);
|
|
29
|
+
}
|
|
30
|
+
let versionNote = "";
|
|
31
|
+
const latestRelevantVersion = latestApprovedVersion || latestVersion;
|
|
32
|
+
if (latestRelevantVersion && semver.eq(spec.version, latestRelevantVersion)) {
|
|
33
|
+
versionNote = `- ${clc.green("Latest")}`;
|
|
34
|
+
}
|
|
35
|
+
if ((extensionVersion === null || extensionVersion === void 0 ? void 0 : extensionVersion.state) === "DEPRECATED") {
|
|
36
|
+
versionNote = `- ${clc.red("Deprecated")}`;
|
|
27
37
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
38
|
+
lines.push(`${clc.bold("Version:")} ${spec.version} ${versionNote}`);
|
|
39
|
+
if (extensionVersion) {
|
|
40
|
+
let reviewStatus;
|
|
41
|
+
switch ((_b = extensionVersion.listing) === null || _b === void 0 ? void 0 : _b.state) {
|
|
42
|
+
case "APPROVED":
|
|
43
|
+
reviewStatus = clc.bold(clc.green("Accepted"));
|
|
44
|
+
break;
|
|
45
|
+
case "REJECTED":
|
|
46
|
+
reviewStatus = clc.bold(clc.red("Rejected"));
|
|
47
|
+
break;
|
|
48
|
+
default:
|
|
49
|
+
reviewStatus = clc.bold(clc.yellow("Unreviewed"));
|
|
50
|
+
}
|
|
51
|
+
lines.push(`${clc.bold("Review status:")} ${reviewStatus}`);
|
|
52
|
+
if (latestApprovedVersion) {
|
|
53
|
+
lines.push(`${clc.bold("View in Extensions Hub:")} https://extensions.dev/extensions/${extensionRef}`);
|
|
54
|
+
}
|
|
55
|
+
if (extensionVersion.buildSourceUri) {
|
|
56
|
+
const buildSourceUri = new URL(extensionVersion.buildSourceUri);
|
|
57
|
+
buildSourceUri.pathname = path.join(buildSourceUri.pathname, (_c = extensionVersion.extensionRoot) !== null && _c !== void 0 ? _c : "");
|
|
58
|
+
lines.push(`${clc.bold("Source in GitHub:")} ${buildSourceUri}`);
|
|
31
59
|
}
|
|
32
|
-
|
|
33
|
-
lines.push(
|
|
60
|
+
else {
|
|
61
|
+
lines.push(`${clc.bold("Source download URI:")} ${(_d = extensionVersion.sourceDownloadUri) !== null && _d !== void 0 ? _d : "-"}`);
|
|
34
62
|
}
|
|
35
63
|
}
|
|
64
|
+
lines.push(`${clc.bold("License:")} ${(_e = spec.license) !== null && _e !== void 0 ? _e : "-"}`);
|
|
65
|
+
lines.push(displayResources(spec));
|
|
66
|
+
if ((_f = spec.events) === null || _f === void 0 ? void 0 : _f.length) {
|
|
67
|
+
lines.push(displayEvents(spec));
|
|
68
|
+
}
|
|
69
|
+
if ((_g = spec.externalServices) === null || _g === void 0 ? void 0 : _g.length) {
|
|
70
|
+
lines.push(displayExternalServices(spec));
|
|
71
|
+
}
|
|
36
72
|
const apis = impliedApis(spec);
|
|
37
73
|
if (apis.length) {
|
|
38
74
|
lines.push(displayApis(apis));
|
|
@@ -41,44 +77,70 @@ async function displayExtInfo(extensionName, publisher, spec, published = false)
|
|
|
41
77
|
if (roles.length) {
|
|
42
78
|
lines.push(await displayRoles(roles));
|
|
43
79
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
80
|
+
logger_1.logger.info(`\n${lines.join("\n")}`);
|
|
81
|
+
return lines;
|
|
82
|
+
}
|
|
83
|
+
exports.displayExtensionVersionInfo = displayExtensionVersionInfo;
|
|
84
|
+
function displayExternalServices(spec) {
|
|
85
|
+
var _a, _b;
|
|
86
|
+
const lines = (_b = (_a = spec.externalServices) === null || _a === void 0 ? void 0 : _a.map((service) => {
|
|
87
|
+
return ` - ${clc.cyan(`${service.name} (${service.pricingUri})`)}`;
|
|
88
|
+
})) !== null && _b !== void 0 ? _b : [];
|
|
89
|
+
return clc.bold("External services used:\n") + lines.join("\n");
|
|
90
|
+
}
|
|
91
|
+
exports.displayExternalServices = displayExternalServices;
|
|
92
|
+
function displayEvents(spec) {
|
|
93
|
+
var _a, _b;
|
|
94
|
+
const lines = (_b = (_a = spec.events) === null || _a === void 0 ? void 0 : _a.map((event) => {
|
|
95
|
+
return ` - ${clc.magenta(event.type)}${event.description ? `: ${event.description}` : ""}`;
|
|
96
|
+
})) !== null && _b !== void 0 ? _b : [];
|
|
97
|
+
return clc.bold("Events emitted:\n") + lines.join("\n");
|
|
59
98
|
}
|
|
60
|
-
exports.
|
|
61
|
-
function
|
|
62
|
-
|
|
63
|
-
|
|
99
|
+
exports.displayEvents = displayEvents;
|
|
100
|
+
function displayResources(spec) {
|
|
101
|
+
var _a;
|
|
102
|
+
const lines = spec.resources.map((resource) => {
|
|
103
|
+
let type = resource.type;
|
|
104
|
+
switch (resource.type) {
|
|
105
|
+
case "firebaseextensions.v1beta.function":
|
|
106
|
+
type = "Cloud Function (1st gen)";
|
|
107
|
+
break;
|
|
108
|
+
case "firebaseextensions.v1beta.v2function":
|
|
109
|
+
type = "Cloud Function (2nd gen)";
|
|
110
|
+
break;
|
|
111
|
+
default:
|
|
112
|
+
}
|
|
113
|
+
return ` - ${clc.blue(`${resource.name} (${type})`)}${resource.description ? `: ${resource.description}` : ""}`;
|
|
114
|
+
});
|
|
115
|
+
lines.push(...new Set((_a = spec.lifecycleEvents) === null || _a === void 0 ? void 0 : _a.map((event) => {
|
|
116
|
+
return ` - ${clc.blue(`${event.taskQueueTriggerFunction} (Cloud Task queue)`)}`;
|
|
117
|
+
})));
|
|
118
|
+
lines.push(...spec.params
|
|
119
|
+
.filter((param) => {
|
|
120
|
+
return param.type === "SECRET";
|
|
121
|
+
})
|
|
122
|
+
.map((param) => {
|
|
123
|
+
return ` - ${clc.blue(`${param.param} (Cloud Secret Manager secret)`)}`;
|
|
124
|
+
}));
|
|
125
|
+
return clc.bold("Resources created:\n") + (lines.length ? lines.join("\n") : " - None");
|
|
64
126
|
}
|
|
65
|
-
exports.
|
|
127
|
+
exports.displayResources = displayResources;
|
|
66
128
|
async function retrieveRoleInfo(role) {
|
|
67
129
|
const res = await iam.getRole(role);
|
|
68
|
-
return ` ${res.title}
|
|
130
|
+
return ` - ${clc.yellow(res.title)}${res.description ? `: ${res.description}` : ""}`;
|
|
69
131
|
}
|
|
70
132
|
exports.retrieveRoleInfo = retrieveRoleInfo;
|
|
71
133
|
async function displayRoles(roles) {
|
|
72
134
|
const lines = await Promise.all(roles.map((role) => {
|
|
73
135
|
return retrieveRoleInfo(role.role);
|
|
74
136
|
}));
|
|
75
|
-
return clc.bold("
|
|
137
|
+
return clc.bold("Roles granted:\n") + lines.join("\n");
|
|
76
138
|
}
|
|
77
139
|
function displayApis(apis) {
|
|
78
140
|
const lines = apis.map((api) => {
|
|
79
|
-
return ` ${api.apiName}
|
|
141
|
+
return ` - ${clc.cyan(api.apiName)}: ${api.reason}`;
|
|
80
142
|
});
|
|
81
|
-
return "
|
|
143
|
+
return clc.bold("APIs used:\n") + lines.join("\n");
|
|
82
144
|
}
|
|
83
145
|
function usesTasks(spec) {
|
|
84
146
|
return spec.resources.some((r) => { var _a; return r.type === types_1.FUNCTIONS_RESOURCE_TYPE && ((_a = r.properties) === null || _a === void 0 ? void 0 : _a.taskQueueTrigger) !== undefined; });
|
|
@@ -89,13 +151,13 @@ function impliedRoles(spec) {
|
|
|
89
151
|
if ((0, secretsUtils_1.usesSecrets)(spec) && !((_a = spec.roles) === null || _a === void 0 ? void 0 : _a.some((r) => r.role === secretsUtils_1.SECRET_ROLE))) {
|
|
90
152
|
roles.push({
|
|
91
153
|
role: secretsUtils_1.SECRET_ROLE,
|
|
92
|
-
reason: "Allows the extension to read secret values from Cloud Secret Manager",
|
|
154
|
+
reason: "Allows the extension to read secret values from Cloud Secret Manager.",
|
|
93
155
|
});
|
|
94
156
|
}
|
|
95
157
|
if (usesTasks(spec) && !((_b = spec.roles) === null || _b === void 0 ? void 0 : _b.some((r) => r.role === TASKS_ROLE))) {
|
|
96
158
|
roles.push({
|
|
97
159
|
role: TASKS_ROLE,
|
|
98
|
-
reason: "Allows the extension to enqueue Cloud Tasks",
|
|
160
|
+
reason: "Allows the extension to enqueue Cloud Tasks.",
|
|
99
161
|
});
|
|
100
162
|
}
|
|
101
163
|
return roles.concat((_c = spec.roles) !== null && _c !== void 0 ? _c : []);
|
|
@@ -106,7 +168,7 @@ function impliedApis(spec) {
|
|
|
106
168
|
if (usesTasks(spec) && !((_a = spec.apis) === null || _a === void 0 ? void 0 : _a.some((a) => a.apiName === TASKS_API))) {
|
|
107
169
|
apis.push({
|
|
108
170
|
apiName: TASKS_API,
|
|
109
|
-
reason: "Allows the extension to enqueue Cloud Tasks",
|
|
171
|
+
reason: "Allows the extension to enqueue Cloud Tasks.",
|
|
110
172
|
});
|
|
111
173
|
}
|
|
112
174
|
return apis.concat((_b = spec.apis) !== null && _b !== void 0 ? _b : []);
|
|
@@ -37,7 +37,7 @@ function functionResourceToEmulatedTriggerDefintion(resource, systemParams = {})
|
|
|
37
37
|
proto.convertIfPresent(etd, properties, "timeoutSeconds", "timeout", proto.secondsFromDuration);
|
|
38
38
|
proto.convertIfPresent(etd, properties, "regions", "location", (str) => [str]);
|
|
39
39
|
proto.copyIfPresent(etd, properties, "availableMemoryMb");
|
|
40
|
-
if (properties.httpsTrigger) {
|
|
40
|
+
if (properties.httpsTrigger !== undefined) {
|
|
41
41
|
etd.httpsTrigger = properties.httpsTrigger;
|
|
42
42
|
}
|
|
43
43
|
if (properties.eventTrigger) {
|
|
@@ -31,7 +31,7 @@ async function createInstanceHelper(projectId, instanceId, config, validateOnly
|
|
|
31
31
|
apiOrigin: api_1.extensionsOrigin,
|
|
32
32
|
apiVersion: EXTENSIONS_API_VERSION,
|
|
33
33
|
operationResourceName: createRes.body.name,
|
|
34
|
-
masterTimeout:
|
|
34
|
+
masterTimeout: 3600000,
|
|
35
35
|
});
|
|
36
36
|
return pollRes;
|
|
37
37
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.diagnoseAndFixProject = exports.getSourceOrigin = exports.isLocalOrURLPath = exports.isLocalPath = exports.isUrlPath = exports.instanceIdExists = exports.promptForRepeatInstance = exports.promptForOfficialExtension = exports.displayReleaseNotes = exports.getPublisherProjectFromName = exports.createSourceFromLocation = exports.getMissingPublisherError = exports.uploadExtensionVersionFromLocalSource = exports.uploadExtensionVersionFromGitHubSource = exports.unpackExtensionState = exports.getNextVersionByStage = exports.ensureExtensionsPublisherApiEnabled = exports.ensureExtensionsApiEnabled = exports.promptForExtensionRoot = exports.promptForValidRepoURI = exports.promptForValidInstanceId = exports.validateSpec = exports.validateCommandLineParams = exports.populateDefaultParams = exports.substituteParams = exports.getFirebaseProjectParams = exports.getDBInstanceFromURL = exports.resourceTypeToNiceName = exports.AUTOPOULATED_PARAM_PLACEHOLDERS = exports.EXTENSIONS_BUCKET_NAME = exports.URL_REGEX = exports.logPrefix = exports.SourceOrigin = exports.SpecParamType = void 0;
|
|
4
4
|
const clc = require("colorette");
|
|
5
5
|
const ora = require("ora");
|
|
6
6
|
const semver = require("semver");
|
|
@@ -36,7 +36,6 @@ const utils_2 = require("../utils");
|
|
|
36
36
|
const change_log_1 = require("./change-log");
|
|
37
37
|
const getProjectNumber_1 = require("../getProjectNumber");
|
|
38
38
|
const constants_1 = require("../emulator/constants");
|
|
39
|
-
const planner_1 = require("../deploy/extensions/planner");
|
|
40
39
|
var SpecParamType;
|
|
41
40
|
(function (SpecParamType) {
|
|
42
41
|
SpecParamType["SELECT"] = "select";
|
|
@@ -301,20 +300,14 @@ async function promptForValidRepoURI() {
|
|
|
301
300
|
return extensionRoot;
|
|
302
301
|
}
|
|
303
302
|
exports.promptForValidRepoURI = promptForValidRepoURI;
|
|
304
|
-
async function
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
message: "Enter this extension's root directory in the repo (defaults to previous root if set):",
|
|
311
|
-
default: defaultRoot,
|
|
312
|
-
});
|
|
313
|
-
rootIsValid = true;
|
|
314
|
-
}
|
|
315
|
-
return extensionRoot;
|
|
303
|
+
async function promptForExtensionRoot(defaultRoot) {
|
|
304
|
+
return await (0, prompt_1.promptOnce)({
|
|
305
|
+
type: "input",
|
|
306
|
+
message: "Enter this extension's root directory in the repo (defaults to previous root if set):",
|
|
307
|
+
default: defaultRoot,
|
|
308
|
+
});
|
|
316
309
|
}
|
|
317
|
-
exports.
|
|
310
|
+
exports.promptForExtensionRoot = promptForExtensionRoot;
|
|
318
311
|
async function promptForReleaseStage(args) {
|
|
319
312
|
let stage = "rc";
|
|
320
313
|
if (!args.nonInteractive) {
|
|
@@ -489,6 +482,7 @@ async function fetchExtensionSource(repoUri, sourceRef, extensionRoot) {
|
|
|
489
482
|
logger_1.logger.info(`Validating source code at ${clc.bold(sourceUri)}...`);
|
|
490
483
|
const archiveUri = `${repoUri}/archive/${sourceRef}.zip`;
|
|
491
484
|
const tempDirectory = tmp.dirSync({ unsafeCleanup: true });
|
|
485
|
+
const archiveErrorMessage = `Failed to extract archive from ${clc.bold(archiveUri)}. Please check that the repo is public and that the source ref is valid.`;
|
|
492
486
|
try {
|
|
493
487
|
const response = await (0, node_fetch_1.default)(archiveUri);
|
|
494
488
|
if (response.ok) {
|
|
@@ -496,9 +490,12 @@ async function fetchExtensionSource(repoUri, sourceRef, extensionRoot) {
|
|
|
496
490
|
}
|
|
497
491
|
}
|
|
498
492
|
catch (err) {
|
|
499
|
-
throw new error_1.FirebaseError(
|
|
493
|
+
throw new error_1.FirebaseError(archiveErrorMessage);
|
|
500
494
|
}
|
|
501
495
|
const archiveName = fs.readdirSync(tempDirectory.name)[0];
|
|
496
|
+
if (!archiveName) {
|
|
497
|
+
throw new error_1.FirebaseError(archiveErrorMessage);
|
|
498
|
+
}
|
|
502
499
|
const rootDirectory = path.join(tempDirectory.name, archiveName, extensionRoot);
|
|
503
500
|
try {
|
|
504
501
|
(0, localHelper_1.readFile)(path.resolve(rootDirectory, localHelper_1.EXTENSIONS_SPEC_FILE));
|
|
@@ -539,12 +536,17 @@ async function uploadExtensionVersionFromGitHubSource(args) {
|
|
|
539
536
|
if (!extensionRoot) {
|
|
540
537
|
const defaultRoot = "/";
|
|
541
538
|
if (!args.nonInteractive) {
|
|
542
|
-
extensionRoot = await
|
|
539
|
+
extensionRoot = await promptForExtensionRoot(defaultRoot);
|
|
543
540
|
}
|
|
544
541
|
else {
|
|
545
542
|
extensionRoot = defaultRoot;
|
|
546
543
|
}
|
|
547
544
|
}
|
|
545
|
+
const normalizedRoot = path
|
|
546
|
+
.normalize(extensionRoot)
|
|
547
|
+
.replaceAll(/^\/|\/$/g, "")
|
|
548
|
+
.replaceAll(/^(\.\.\/)*/g, "");
|
|
549
|
+
extensionRoot = normalizedRoot || "/";
|
|
548
550
|
let sourceRef = args.sourceRef;
|
|
549
551
|
const defaultSourceRef = "HEAD";
|
|
550
552
|
if (!sourceRef) {
|
|
@@ -858,16 +860,3 @@ async function diagnoseAndFixProject(options) {
|
|
|
858
860
|
}
|
|
859
861
|
}
|
|
860
862
|
exports.diagnoseAndFixProject = diagnoseAndFixProject;
|
|
861
|
-
async function canonicalizeRefInput(refInput) {
|
|
862
|
-
let inferredRef = refInput;
|
|
863
|
-
if (refInput.split("/").length < 2) {
|
|
864
|
-
inferredRef = `firebase/${inferredRef}`;
|
|
865
|
-
}
|
|
866
|
-
if (refInput.split("@").length < 2) {
|
|
867
|
-
inferredRef = `${inferredRef}@latest-approved`;
|
|
868
|
-
}
|
|
869
|
-
const ref = refs.parse(inferredRef);
|
|
870
|
-
ref.version = await (0, planner_1.resolveVersion)(ref);
|
|
871
|
-
return refs.toExtensionVersionRef(ref);
|
|
872
|
-
}
|
|
873
|
-
exports.canonicalizeRefInput = canonicalizeRefInput;
|
|
@@ -106,14 +106,13 @@ async function createExtensionVersionFromLocalSource(args) {
|
|
|
106
106
|
}
|
|
107
107
|
exports.createExtensionVersionFromLocalSource = createExtensionVersionFromLocalSource;
|
|
108
108
|
async function createExtensionVersionFromGitHubSource(args) {
|
|
109
|
-
var _a;
|
|
110
109
|
const ref = refs.parse(args.extensionVersionRef);
|
|
111
110
|
if (!ref.version) {
|
|
112
111
|
throw new error_1.FirebaseError(`Extension version ref "${args.extensionVersionRef}" must supply a version.`);
|
|
113
112
|
}
|
|
114
113
|
const uploadRes = await extensionsPublisherApiClient.post(`/${refs.toExtensionName(ref)}/versions:createFromSource`, {
|
|
115
114
|
versionId: ref.version,
|
|
116
|
-
extensionRoot:
|
|
115
|
+
extensionRoot: args.extensionRoot || "/",
|
|
117
116
|
githubRepositorySource: {
|
|
118
117
|
uri: args.repoUri,
|
|
119
118
|
sourceRef: args.sourceRef,
|
|
@@ -78,7 +78,7 @@ async function update(updateOptions) {
|
|
|
78
78
|
}
|
|
79
79
|
exports.update = update;
|
|
80
80
|
async function updateFromLocalSource(projectId, instanceId, localSource, existingSpec) {
|
|
81
|
-
await (0, displayExtensionInfo_1.
|
|
81
|
+
await (0, displayExtensionInfo_1.displayExtensionVersionInfo)({ spec: existingSpec });
|
|
82
82
|
let source;
|
|
83
83
|
try {
|
|
84
84
|
source = await (0, extensionsHelper_1.createSourceFromLocation)(projectId, localSource);
|
|
@@ -93,7 +93,7 @@ async function updateFromLocalSource(projectId, instanceId, localSource, existin
|
|
|
93
93
|
}
|
|
94
94
|
exports.updateFromLocalSource = updateFromLocalSource;
|
|
95
95
|
async function updateFromUrlSource(projectId, instanceId, urlSource, existingSpec) {
|
|
96
|
-
await (0, displayExtensionInfo_1.
|
|
96
|
+
await (0, displayExtensionInfo_1.displayExtensionVersionInfo)({ spec: existingSpec });
|
|
97
97
|
let source;
|
|
98
98
|
try {
|
|
99
99
|
source = await (0, extensionsHelper_1.createSourceFromLocation)(projectId, urlSource);
|
|
@@ -4,7 +4,7 @@ exports.readOrNull = exports.LocalFileSystem = void 0;
|
|
|
4
4
|
const fs_extra_1 = require("fs-extra");
|
|
5
5
|
const path = require("path");
|
|
6
6
|
const error_1 = require("../../../error");
|
|
7
|
-
const logger_1 = require("
|
|
7
|
+
const logger_1 = require("../../../logger");
|
|
8
8
|
class LocalFileSystem {
|
|
9
9
|
constructor(cwd) {
|
|
10
10
|
this.cwd = cwd;
|
|
@@ -1,23 +1,32 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.discover = void 0;
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
4
|
+
const node_1 = require("./runtime/node");
|
|
5
|
+
const error_1 = require("../../../error");
|
|
6
|
+
const supportedRuntimes = [new node_1.NodejsRuntime()];
|
|
7
|
+
async function discover(fs, allFrameworkSpecs) {
|
|
8
|
+
try {
|
|
9
|
+
let discoveredRuntime = undefined;
|
|
10
|
+
for (const runtime of supportedRuntimes) {
|
|
11
|
+
if (await runtime.match(fs)) {
|
|
12
|
+
if (!discoveredRuntime) {
|
|
13
|
+
discoveredRuntime = runtime;
|
|
14
|
+
}
|
|
15
|
+
else {
|
|
16
|
+
throw new error_1.FirebaseError(`Conflit occurred as multiple runtimes ${discoveredRuntime.getRuntimeName()}, ${runtime.getRuntimeName()} are discovered in the application.`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
if (!discoveredRuntime) {
|
|
21
|
+
throw new error_1.FirebaseError(`Unable to determine the specific runtime for the application. The supported runtime options include ${supportedRuntimes
|
|
22
|
+
.map((x) => x.getRuntimeName())
|
|
23
|
+
.join(" , ")}.`);
|
|
24
|
+
}
|
|
25
|
+
const runtimeSpec = await discoveredRuntime.analyseCodebase(fs, allFrameworkSpecs);
|
|
26
|
+
return runtimeSpec;
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
throw new error_1.FirebaseError(`Failed to identify required specifications to execute the application: ${error}`);
|
|
30
|
+
}
|
|
22
31
|
}
|
|
23
32
|
exports.discover = discover;
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NodejsRuntime = void 0;
|
|
4
|
+
const filesystem_1 = require("../filesystem");
|
|
5
|
+
const frameworkMatcher_1 = require("../frameworkMatcher");
|
|
6
|
+
const error_1 = require("../../../../error");
|
|
7
|
+
const logger_1 = require("../../../../logger");
|
|
8
|
+
const utils_1 = require("../../../utils");
|
|
9
|
+
const supportedNodeVersions = ["18"];
|
|
10
|
+
const NODE_RUNTIME_ID = "nodejs";
|
|
11
|
+
const PACKAGE_JSON = "package.json";
|
|
12
|
+
const YARN_LOCK = "yarn.lock";
|
|
13
|
+
class NodejsRuntime {
|
|
14
|
+
constructor() {
|
|
15
|
+
this.runtimeRequiredFiles = [PACKAGE_JSON];
|
|
16
|
+
}
|
|
17
|
+
async match(fs) {
|
|
18
|
+
const areAllFilesPresent = await Promise.all(this.runtimeRequiredFiles.map((file) => fs.exists(file)));
|
|
19
|
+
return areAllFilesPresent.every((present) => present);
|
|
20
|
+
}
|
|
21
|
+
getRuntimeName() {
|
|
22
|
+
return NODE_RUNTIME_ID;
|
|
23
|
+
}
|
|
24
|
+
getNodeImage(engine) {
|
|
25
|
+
if (!engine || !engine.node) {
|
|
26
|
+
return "us-docker.pkg.dev/firestack-build/test/run";
|
|
27
|
+
}
|
|
28
|
+
const versionNumber = engine.node;
|
|
29
|
+
if (!supportedNodeVersions.includes(versionNumber)) {
|
|
30
|
+
throw new error_1.FirebaseError(`This integration expects Node version ${(0, utils_1.conjoinOptions)(supportedNodeVersions, "or")}. You're running version ${versionNumber}, which is not compatible.`);
|
|
31
|
+
}
|
|
32
|
+
return "us-docker.pkg.dev/firestack-build/test/run";
|
|
33
|
+
}
|
|
34
|
+
async getPackageManager(fs) {
|
|
35
|
+
try {
|
|
36
|
+
if (await fs.exists(YARN_LOCK)) {
|
|
37
|
+
return "yarn";
|
|
38
|
+
}
|
|
39
|
+
return "npm";
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
logger_1.logger.error("Failed to check files to identify package manager");
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
getDependencies(packageJSON) {
|
|
47
|
+
return Object.assign(Object.assign({}, packageJSON.dependencies), packageJSON.devDependencies);
|
|
48
|
+
}
|
|
49
|
+
packageManagerInstallCommand(packageManager) {
|
|
50
|
+
const packages = [];
|
|
51
|
+
if (packageManager === "yarn") {
|
|
52
|
+
packages.push("yarn");
|
|
53
|
+
}
|
|
54
|
+
if (!packages.length) {
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
return `npm install --global ${packages.join(" ")}`;
|
|
58
|
+
}
|
|
59
|
+
installCommand(fs, packageManager) {
|
|
60
|
+
let installCmd = "npm install";
|
|
61
|
+
if (packageManager === "yarn") {
|
|
62
|
+
installCmd = "yarn install";
|
|
63
|
+
}
|
|
64
|
+
return installCmd;
|
|
65
|
+
}
|
|
66
|
+
async detectedCommands(packageManager, scripts, matchedFramework, fs) {
|
|
67
|
+
return {
|
|
68
|
+
build: this.getBuildCommand(packageManager, scripts, matchedFramework),
|
|
69
|
+
dev: this.getDevCommand(packageManager, scripts, matchedFramework),
|
|
70
|
+
run: await this.getRunCommand(packageManager, scripts, matchedFramework, fs),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
executeScript(packageManager, scriptName) {
|
|
74
|
+
return `${packageManager} run ${scriptName}`;
|
|
75
|
+
}
|
|
76
|
+
executeFrameworkCommand(packageManager, command) {
|
|
77
|
+
if (packageManager === "npm" || packageManager === "yarn") {
|
|
78
|
+
command.cmd = "npx " + command.cmd;
|
|
79
|
+
}
|
|
80
|
+
return command;
|
|
81
|
+
}
|
|
82
|
+
getBuildCommand(packageManager, scripts, matchedFramework) {
|
|
83
|
+
var _a;
|
|
84
|
+
let buildCommand = { cmd: "" };
|
|
85
|
+
if (scripts === null || scripts === void 0 ? void 0 : scripts.build) {
|
|
86
|
+
buildCommand.cmd = this.executeScript(packageManager, "build");
|
|
87
|
+
}
|
|
88
|
+
else if (matchedFramework && ((_a = matchedFramework.commands) === null || _a === void 0 ? void 0 : _a.build)) {
|
|
89
|
+
buildCommand = matchedFramework.commands.build;
|
|
90
|
+
buildCommand = this.executeFrameworkCommand(packageManager, buildCommand);
|
|
91
|
+
}
|
|
92
|
+
return buildCommand.cmd === "" ? undefined : buildCommand;
|
|
93
|
+
}
|
|
94
|
+
getDevCommand(packageManager, scripts, matchedFramework) {
|
|
95
|
+
var _a;
|
|
96
|
+
let devCommand = { cmd: "", env: { NODE_ENV: "dev" } };
|
|
97
|
+
if (scripts === null || scripts === void 0 ? void 0 : scripts.dev) {
|
|
98
|
+
devCommand.cmd = this.executeScript(packageManager, "dev");
|
|
99
|
+
}
|
|
100
|
+
else if (matchedFramework && ((_a = matchedFramework.commands) === null || _a === void 0 ? void 0 : _a.dev)) {
|
|
101
|
+
devCommand = matchedFramework.commands.dev;
|
|
102
|
+
devCommand = this.executeFrameworkCommand(packageManager, devCommand);
|
|
103
|
+
}
|
|
104
|
+
return devCommand.cmd === "" ? undefined : devCommand;
|
|
105
|
+
}
|
|
106
|
+
async getRunCommand(packageManager, scripts, matchedFramework, fs) {
|
|
107
|
+
var _a;
|
|
108
|
+
let runCommand = { cmd: "", env: { NODE_ENV: "production" } };
|
|
109
|
+
if (scripts === null || scripts === void 0 ? void 0 : scripts.start) {
|
|
110
|
+
runCommand.cmd = this.executeScript(packageManager, "start");
|
|
111
|
+
}
|
|
112
|
+
else if (matchedFramework && ((_a = matchedFramework.commands) === null || _a === void 0 ? void 0 : _a.run)) {
|
|
113
|
+
runCommand = matchedFramework.commands.run;
|
|
114
|
+
runCommand = this.executeFrameworkCommand(packageManager, runCommand);
|
|
115
|
+
}
|
|
116
|
+
else if (scripts === null || scripts === void 0 ? void 0 : scripts.main) {
|
|
117
|
+
runCommand.cmd = `node ${scripts.main}`;
|
|
118
|
+
}
|
|
119
|
+
else if (await fs.exists("index.js")) {
|
|
120
|
+
runCommand.cmd = `node index.js`;
|
|
121
|
+
}
|
|
122
|
+
return runCommand.cmd === "" ? undefined : runCommand;
|
|
123
|
+
}
|
|
124
|
+
async analyseCodebase(fs, allFrameworkSpecs) {
|
|
125
|
+
try {
|
|
126
|
+
const packageJSONRaw = await (0, filesystem_1.readOrNull)(fs, PACKAGE_JSON);
|
|
127
|
+
let packageJSON = {};
|
|
128
|
+
if (packageJSONRaw) {
|
|
129
|
+
packageJSON = JSON.parse(packageJSONRaw);
|
|
130
|
+
}
|
|
131
|
+
const packageManager = await this.getPackageManager(fs);
|
|
132
|
+
const nodeImage = this.getNodeImage(packageJSON.engines);
|
|
133
|
+
const dependencies = this.getDependencies(packageJSON);
|
|
134
|
+
const matchedFramework = await (0, frameworkMatcher_1.frameworkMatcher)(NODE_RUNTIME_ID, fs, allFrameworkSpecs, dependencies);
|
|
135
|
+
const runtimeSpec = {
|
|
136
|
+
id: NODE_RUNTIME_ID,
|
|
137
|
+
baseImage: nodeImage,
|
|
138
|
+
packageManagerInstallCommand: this.packageManagerInstallCommand(packageManager),
|
|
139
|
+
installCommand: this.installCommand(fs, packageManager),
|
|
140
|
+
detectedCommands: await this.detectedCommands(packageManager, packageJSON.scripts, matchedFramework, fs),
|
|
141
|
+
};
|
|
142
|
+
return runtimeSpec;
|
|
143
|
+
}
|
|
144
|
+
catch (error) {
|
|
145
|
+
throw new error_1.FirebaseError(`Failed to parse engine: ${error}`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
exports.NodejsRuntime = NodejsRuntime;
|
|
@@ -89,7 +89,9 @@ class DockerDriver {
|
|
|
89
89
|
this.dockerfileBuilder.from(spec.baseImage, "base").user("firebase");
|
|
90
90
|
}
|
|
91
91
|
execDockerPush(args) {
|
|
92
|
-
console.
|
|
92
|
+
console.debug(JSON.stringify({ message: `executing docker build: ${args.join(" ")}` }));
|
|
93
|
+
console.info(JSON.stringify({ foo: "bar", message: `executing docker build: ${args.join(" ")}` }));
|
|
94
|
+
console.error(JSON.stringify({ message: `executing docker build: ${args.join(" ")}` }));
|
|
93
95
|
return spawn.sync("docker", ["push", ...args], {
|
|
94
96
|
stdio: ["pipe", "inherit", "inherit"],
|
|
95
97
|
});
|
|
@@ -126,20 +128,28 @@ class DockerDriver {
|
|
|
126
128
|
return JSON.parse(fs.readFileSync("./.firebase/.output/bundle.json", "utf8"));
|
|
127
129
|
}
|
|
128
130
|
install() {
|
|
129
|
-
this.
|
|
130
|
-
.
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
131
|
+
if (this.spec.installCommand) {
|
|
132
|
+
this.dockerfileBuilder
|
|
133
|
+
.fromLastStage(DOCKER_STAGE_INSTALL)
|
|
134
|
+
.workdir("/home/firebase/app")
|
|
135
|
+
.envs(this.spec.environmentVariables || {})
|
|
136
|
+
.copyForFirebase("package.json", ".");
|
|
137
|
+
if (this.spec.packageManagerInstallCommand) {
|
|
138
|
+
this.dockerfileBuilder.run(this.spec.packageManagerInstallCommand);
|
|
139
|
+
}
|
|
140
|
+
this.dockerfileBuilder.run(this.spec.installCommand);
|
|
141
|
+
this.buildStage(DOCKER_STAGE_INSTALL, ".");
|
|
142
|
+
}
|
|
136
143
|
}
|
|
137
144
|
build() {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
.
|
|
141
|
-
|
|
142
|
-
|
|
145
|
+
var _a;
|
|
146
|
+
if ((_a = this.spec.detectedCommands) === null || _a === void 0 ? void 0 : _a.build) {
|
|
147
|
+
this.dockerfileBuilder
|
|
148
|
+
.fromLastStage(DOCKER_STAGE_BUILD)
|
|
149
|
+
.copyForFirebase(".", ".")
|
|
150
|
+
.run(this.spec.detectedCommands.build.cmd);
|
|
151
|
+
this.buildStage(DOCKER_STAGE_BUILD, ".");
|
|
152
|
+
}
|
|
143
153
|
}
|
|
144
154
|
export(bundle) {
|
|
145
155
|
var _a;
|