firebase-tools 11.25.3 → 11.27.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/lib/commands/ext-dev-publish.js +28 -10
- package/lib/commands/ext-install.js +1 -2
- package/lib/deploy/functions/backend.js +1 -5
- package/lib/deploy/functions/release/fabricator.js +16 -6
- package/lib/emulator/functionsEmulator.js +18 -11
- package/lib/emulator/storage/apis/gcloud.js +1 -1
- package/lib/extensions/extensionsApi.js +8 -5
- package/lib/extensions/extensionsHelper.js +230 -32
- package/lib/extensions/localHelper.js +4 -4
- package/lib/extensions/warnings.js +3 -16
- package/lib/frameworks/angular/index.js +6 -3
- package/lib/frameworks/astro/index.js +76 -0
- package/lib/frameworks/astro/utils.js +22 -0
- package/lib/frameworks/index.js +25 -7
- package/lib/frameworks/nuxt/index.js +34 -14
- package/lib/frameworks/nuxt/utils.js +11 -1
- package/lib/frameworks/nuxt2/index.js +3 -9
- package/lib/frameworks/sveltekit/index.js +54 -0
- package/lib/frameworks/sveltekit/interfaces.js +2 -0
- package/lib/frameworks/utils.js +2 -2
- package/lib/frameworks/vite/index.js +12 -5
- package/lib/gcp/cloudfunctions.js +3 -0
- package/lib/gcp/cloudfunctionsv2.js +39 -26
- package/lib/track.js +19 -13
- package/npm-shrinkwrap.json +14 -14
- package/package.json +1 -1
|
@@ -17,7 +17,10 @@ marked_1.marked.setOptions({
|
|
|
17
17
|
});
|
|
18
18
|
exports.command = new command_1.Command("ext:dev:publish <extensionRef>")
|
|
19
19
|
.description(`publish a new version of an extension`)
|
|
20
|
-
.option(`-s, --stage <stage>`, `release stage (supports "
|
|
20
|
+
.option(`-s, --stage <stage>`, `release stage (supports "alpha", "beta", "rc", and "stable")`)
|
|
21
|
+
.option(`--repo <repo>`, `Public Git repo URI (only required for first version from repo, cannot be changed)`)
|
|
22
|
+
.option(`--ref <ref>`, `commit hash, branch, or tag to build from the repo (defaults to HEAD)`)
|
|
23
|
+
.option(`--root <root>`, `root directory that contains this Extension (defaults to previous version's root or root of repo if none set)`)
|
|
21
24
|
.withForce()
|
|
22
25
|
.help("if you have not previously published a version of this extension, this will " +
|
|
23
26
|
"create the extension. If you have previously published a version of this extension, this version must " +
|
|
@@ -32,15 +35,30 @@ exports.command = new command_1.Command("ext:dev:publish <extensionRef>")
|
|
|
32
35
|
if (!publisherId || !extensionId) {
|
|
33
36
|
throw new error_1.FirebaseError(`Error parsing publisher ID and extension ID from extension reference '${clc.bold(extensionRef)}'. Please use the format '${clc.bold("<publisherId>/<extensionId>")}'.`);
|
|
34
37
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
38
|
+
let res;
|
|
39
|
+
if (options.repo || options.root || options.ref) {
|
|
40
|
+
res = await (0, extensionsHelper_1.publishExtensionVersionFromRemoteRepo)({
|
|
41
|
+
publisherId,
|
|
42
|
+
extensionId,
|
|
43
|
+
repoUri: options.repo,
|
|
44
|
+
sourceRef: options.ref,
|
|
45
|
+
extensionRoot: options.root,
|
|
46
|
+
nonInteractive: options.nonInteractive,
|
|
47
|
+
force: options.force,
|
|
48
|
+
stage: options.stage,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
const extensionYamlDirectory = (0, localHelper_1.findExtensionYaml)(process.cwd());
|
|
53
|
+
res = await (0, extensionsHelper_1.publishExtensionVersionFromLocalSource)({
|
|
54
|
+
publisherId,
|
|
55
|
+
extensionId,
|
|
56
|
+
rootDirectory: extensionYamlDirectory,
|
|
57
|
+
nonInteractive: options.nonInteractive,
|
|
58
|
+
force: options.force,
|
|
59
|
+
stage: (_a = options.stage) !== null && _a !== void 0 ? _a : "stable",
|
|
60
|
+
});
|
|
61
|
+
}
|
|
44
62
|
if (res) {
|
|
45
63
|
utils.logLabeledBullet(extensionsHelper_1.logPrefix, (0, marked_1.marked)(`[Install Link](${(0, publishHelpers_1.consoleInstallLink)(res.ref)})`));
|
|
46
64
|
}
|
|
@@ -115,9 +115,8 @@ exports.command = new command_1.Command("ext:install [extensionName]")
|
|
|
115
115
|
});
|
|
116
116
|
async function infoExtensionVersion(args) {
|
|
117
117
|
const ref = refs.parse(args.extensionName);
|
|
118
|
-
const extension = await extensionsApi.getExtension(refs.toExtensionRef(ref));
|
|
119
118
|
await (0, displayExtensionInfo_1.displayExtInfo)(args.extensionName, ref.publisherId, args.extensionVersion.spec, true);
|
|
120
|
-
await (0, warnings_1.displayWarningPrompts)(ref.publisherId,
|
|
119
|
+
await (0, warnings_1.displayWarningPrompts)(ref.publisherId, args.extensionVersion);
|
|
121
120
|
}
|
|
122
121
|
async function installToManifest(options) {
|
|
123
122
|
var _a, _b;
|
|
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.compareFunctions = exports.missingEndpoint = exports.hasEndpoint = exports.regionalEndpoints = exports.matchingBackend = exports.findEndpoint = exports.someEndpoint = exports.allEndpoints = exports.checkAvailability = exports.existingBackend = exports.scheduleIdForFunction = exports.functionName = exports.isEmptyBackend = exports.merge = exports.of = exports.empty = exports.isBlockingTriggered = exports.isTaskQueueTriggered = exports.isScheduleTriggered = exports.isEventTriggered = exports.isCallableTriggered = exports.isHttpsTriggered = exports.AllFunctionsPlatforms = exports.secretVersionName = exports.SCHEDULED_FUNCTION_LABEL = exports.MIN_CPU_FOR_CONCURRENCY = exports.DEFAULT_MEMORY = exports.DEFAULT_CONCURRENCY = exports.memoryToGen2Cpu = exports.memoryToGen1Cpu = exports.memoryOptionDisplayName = exports.isValidMemoryOption = exports.AllIngressSettings = exports.AllVpcEgressSettings = exports.endpointTriggerType = void 0;
|
|
4
4
|
const gcf = require("../../gcp/cloudfunctions");
|
|
5
5
|
const gcfV2 = require("../../gcp/cloudfunctionsv2");
|
|
6
|
-
const run = require("../../gcp/run");
|
|
7
6
|
const utils = require("../../utils");
|
|
8
7
|
const error_1 = require("../../error");
|
|
9
8
|
const functional_1 = require("../../functional");
|
|
@@ -195,11 +194,8 @@ async function loadExistingBackend(ctx) {
|
|
|
195
194
|
let gcfV2Results;
|
|
196
195
|
try {
|
|
197
196
|
gcfV2Results = await gcfV2.listAllFunctions(ctx.projectId);
|
|
198
|
-
const
|
|
199
|
-
for (const [apiFunction, runService] of (0, functional_1.zip)(gcfV2Results.functions, runResults)) {
|
|
197
|
+
for (const apiFunction of gcfV2Results.functions) {
|
|
200
198
|
const endpoint = gcfV2.endpointFromFunction(apiFunction);
|
|
201
|
-
endpoint.concurrency = runService.spec.template.spec.containerConcurrency || 1;
|
|
202
|
-
endpoint.cpu = +runService.spec.template.spec.containers[0].resources.limits.cpu;
|
|
203
199
|
ctx.existingBackend.endpoints[endpoint.region] =
|
|
204
200
|
ctx.existingBackend.endpoints[endpoint.region] || {};
|
|
205
201
|
ctx.existingBackend.endpoints[endpoint.region][endpoint.id] = endpoint;
|
|
@@ -219,7 +219,7 @@ class Fabricator {
|
|
|
219
219
|
}
|
|
220
220
|
}
|
|
221
221
|
async createV2Function(endpoint) {
|
|
222
|
-
var _a, _b, _c;
|
|
222
|
+
var _a, _b, _c, _d, _e;
|
|
223
223
|
const storage = (_a = this.sources[endpoint.codebase]) === null || _a === void 0 ? void 0 : _a.storage;
|
|
224
224
|
if (!storage) {
|
|
225
225
|
logger_1.logger.debug("Precondition failed. Cannot create a GCFv2 function without storage");
|
|
@@ -274,8 +274,13 @@ class Fabricator {
|
|
|
274
274
|
return await poller.pollOperation(Object.assign(Object.assign({}, gcfV2PollerOptions), { pollerName: `create-${endpoint.codebase}-${endpoint.region}-${endpoint.id}`, operationResourceName: op.name }));
|
|
275
275
|
})
|
|
276
276
|
.catch(rethrowAs(endpoint, "create"));
|
|
277
|
-
endpoint.uri = resultFunction.serviceConfig.uri;
|
|
278
|
-
const serviceName = resultFunction.serviceConfig.service;
|
|
277
|
+
endpoint.uri = (_d = resultFunction.serviceConfig) === null || _d === void 0 ? void 0 : _d.uri;
|
|
278
|
+
const serviceName = (_e = resultFunction.serviceConfig) === null || _e === void 0 ? void 0 : _e.service;
|
|
279
|
+
if (!serviceName) {
|
|
280
|
+
logger_1.logger.debug("Result function unexpectedly didn't have a service name.");
|
|
281
|
+
utils.logLabeledWarning("functions", "Updated function is not associated with a service. This deployment is in an unexpected state - please re-deploy your functions.");
|
|
282
|
+
return;
|
|
283
|
+
}
|
|
279
284
|
if (backend.isHttpsTriggered(endpoint)) {
|
|
280
285
|
const invoker = endpoint.httpsTrigger.invoker || ["public"];
|
|
281
286
|
if (!invoker.includes("private")) {
|
|
@@ -346,7 +351,7 @@ class Fabricator {
|
|
|
346
351
|
}
|
|
347
352
|
}
|
|
348
353
|
async updateV2Function(endpoint) {
|
|
349
|
-
var _a, _b;
|
|
354
|
+
var _a, _b, _c, _d;
|
|
350
355
|
const storage = (_a = this.sources[endpoint.codebase]) === null || _a === void 0 ? void 0 : _a.storage;
|
|
351
356
|
if (!storage) {
|
|
352
357
|
logger_1.logger.debug("Precondition failed. Cannot update a GCFv2 function without storage");
|
|
@@ -362,8 +367,13 @@ class Fabricator {
|
|
|
362
367
|
return await poller.pollOperation(Object.assign(Object.assign({}, gcfV2PollerOptions), { pollerName: `update-${endpoint.codebase}-${endpoint.region}-${endpoint.id}`, operationResourceName: op.name }));
|
|
363
368
|
})
|
|
364
369
|
.catch(rethrowAs(endpoint, "update"));
|
|
365
|
-
endpoint.uri = resultFunction.serviceConfig.uri;
|
|
366
|
-
const serviceName = resultFunction.serviceConfig.service;
|
|
370
|
+
endpoint.uri = (_c = resultFunction.serviceConfig) === null || _c === void 0 ? void 0 : _c.uri;
|
|
371
|
+
const serviceName = (_d = resultFunction.serviceConfig) === null || _d === void 0 ? void 0 : _d.service;
|
|
372
|
+
if (!serviceName) {
|
|
373
|
+
logger_1.logger.debug("Result function unexpectedly didn't have a service name.");
|
|
374
|
+
utils.logLabeledWarning("functions", "Updated function is not associated with a service. This deployment is in an unexpected state - please re-deploy your functions.");
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
367
377
|
let invoker;
|
|
368
378
|
if (backend.isHttpsTriggered(endpoint)) {
|
|
369
379
|
invoker = endpoint.httpsTrigger.invoker === null ? ["public"] : endpoint.httpsTrigger.invoker;
|
|
@@ -82,15 +82,15 @@ class FunctionsEmulator {
|
|
|
82
82
|
this.logger = emulatorLogger_1.EmulatorLogger.forEmulator(types_1.Emulators.FUNCTIONS);
|
|
83
83
|
this.multicastTriggers = {};
|
|
84
84
|
this.blockingFunctionsConfig = {};
|
|
85
|
+
this.debugMode = false;
|
|
85
86
|
emulatorLogger_1.EmulatorLogger.verbosity = this.args.quiet ? emulatorLogger_1.Verbosity.QUIET : emulatorLogger_1.Verbosity.DEBUG;
|
|
86
87
|
if (this.args.debugPort) {
|
|
87
88
|
this.args.disabledRuntimeFeatures = this.args.disabledRuntimeFeatures || {};
|
|
88
89
|
this.args.disabledRuntimeFeatures.timeout = true;
|
|
90
|
+
this.debugMode = true;
|
|
89
91
|
}
|
|
90
92
|
this.adminSdkConfig = Object.assign(Object.assign({}, this.args.adminSdkConfig), { projectId: this.args.projectId });
|
|
91
|
-
const mode = this.
|
|
92
|
-
? types_1.FunctionsExecutionMode.SEQUENTIAL
|
|
93
|
-
: types_1.FunctionsExecutionMode.AUTO;
|
|
93
|
+
const mode = this.debugMode ? types_1.FunctionsExecutionMode.SEQUENTIAL : types_1.FunctionsExecutionMode.AUTO;
|
|
94
94
|
this.workerPools = {};
|
|
95
95
|
for (const backend of this.args.emulatableBackends) {
|
|
96
96
|
const pool = new functionsRuntimeWorker_1.RuntimeWorkerPool(mode);
|
|
@@ -204,6 +204,12 @@ class FunctionsEmulator {
|
|
|
204
204
|
}
|
|
205
205
|
}
|
|
206
206
|
const worker = pool.getIdleWorker(trigger.id);
|
|
207
|
+
if (this.debugMode) {
|
|
208
|
+
await worker.sendDebugMsg({
|
|
209
|
+
functionTarget: trigger.entryPoint,
|
|
210
|
+
functionSignature: (0, functionsEmulatorShared_1.getSignatureType)(trigger),
|
|
211
|
+
});
|
|
212
|
+
}
|
|
207
213
|
const reqBody = JSON.stringify(body);
|
|
208
214
|
const headers = {
|
|
209
215
|
"Content-Type": "application/json",
|
|
@@ -406,7 +412,7 @@ class FunctionsEmulator {
|
|
|
406
412
|
this.logger.logLabeled("SUCCESS", `functions[${definition.id}]`, msg);
|
|
407
413
|
}
|
|
408
414
|
}
|
|
409
|
-
if (this.
|
|
415
|
+
if (this.debugMode) {
|
|
410
416
|
if (!((_a = emulatableBackend.runtime) === null || _a === void 0 ? void 0 : _a.startsWith("node"))) {
|
|
411
417
|
this.logger.log("WARN", "--inspect-functions only supported for Node.js runtimes.");
|
|
412
418
|
}
|
|
@@ -722,7 +728,7 @@ class FunctionsEmulator {
|
|
|
722
728
|
emulatorInfos = emulatorInfos.concat(Object.values(this.args.remoteEmulators));
|
|
723
729
|
}
|
|
724
730
|
(0, env_1.setEnvVarsForEmulators)(envs, emulatorInfos);
|
|
725
|
-
if (this.
|
|
731
|
+
if (this.debugMode) {
|
|
726
732
|
envs["FUNCTION_DEBUG_MODE"] = "true";
|
|
727
733
|
}
|
|
728
734
|
return envs;
|
|
@@ -789,7 +795,7 @@ class FunctionsEmulator {
|
|
|
789
795
|
}
|
|
790
796
|
async startNode(backend, envs) {
|
|
791
797
|
const args = [path.join(__dirname, "functionsEmulatorRuntime")];
|
|
792
|
-
if (this.
|
|
798
|
+
if (this.debugMode) {
|
|
793
799
|
if (process.env.FIREPIT_VERSION) {
|
|
794
800
|
this.logger.log("WARN", `To enable function inspection, please run "npm i node@${semver.coerce(backend.runtime || "18.0.0")} --save-dev" in your functions directory`);
|
|
795
801
|
}
|
|
@@ -824,7 +830,7 @@ class FunctionsEmulator {
|
|
|
824
830
|
}
|
|
825
831
|
async startPython(backend, envs) {
|
|
826
832
|
const args = ["functions-framework"];
|
|
827
|
-
if (this.
|
|
833
|
+
if (this.debugMode) {
|
|
828
834
|
this.logger.log("WARN", "--inspect-functions not supported for Python functions. Ignored.");
|
|
829
835
|
}
|
|
830
836
|
const port = await portfinder.getPortPromise({
|
|
@@ -961,12 +967,13 @@ class FunctionsEmulator {
|
|
|
961
967
|
return;
|
|
962
968
|
}
|
|
963
969
|
}
|
|
964
|
-
|
|
965
|
-
|
|
970
|
+
let debugBundle;
|
|
971
|
+
if (this.debugMode) {
|
|
972
|
+
debugBundle = {
|
|
966
973
|
functionTarget: trigger.entryPoint,
|
|
967
974
|
functionSignature: (0, functionsEmulatorShared_1.getSignatureType)(trigger),
|
|
968
|
-
}
|
|
969
|
-
|
|
975
|
+
};
|
|
976
|
+
}
|
|
970
977
|
await pool.submitRequest(trigger.id, {
|
|
971
978
|
method,
|
|
972
979
|
path,
|
|
@@ -135,7 +135,7 @@ function createCloudEndpoints(emulator) {
|
|
|
135
135
|
items: (_a = listResponse.items) === null || _a === void 0 ? void 0 : _a.map((item) => new metadata_1.CloudStorageObjectMetadata(item)),
|
|
136
136
|
});
|
|
137
137
|
});
|
|
138
|
-
gcloudStorageAPI.delete("/b/:bucketId/o/:objectId", async (req, res) => {
|
|
138
|
+
gcloudStorageAPI.delete(["/b/:bucketId/o/:objectId", "/storage/v1/b/:bucketId/o/:objectId"], async (req, res) => {
|
|
139
139
|
try {
|
|
140
140
|
await adminStorageLayer.deleteObject({
|
|
141
141
|
bucketId: req.params.bucketId,
|
|
@@ -391,15 +391,18 @@ async function undeprecateExtensionVersion(extensionRef) {
|
|
|
391
391
|
}
|
|
392
392
|
}
|
|
393
393
|
exports.undeprecateExtensionVersion = undeprecateExtensionVersion;
|
|
394
|
-
async function publishExtensionVersion(
|
|
395
|
-
|
|
394
|
+
async function publishExtensionVersion(args) {
|
|
395
|
+
var _a, _b, _c, _d;
|
|
396
|
+
const ref = refs.parse(args.extensionVersionRef);
|
|
396
397
|
if (!ref.version) {
|
|
397
|
-
throw new error_1.FirebaseError(`ExtensionVersion ref "${extensionVersionRef}" must supply a version.`);
|
|
398
|
+
throw new error_1.FirebaseError(`ExtensionVersion ref "${args.extensionVersionRef}" must supply a version.`);
|
|
398
399
|
}
|
|
399
400
|
const publishRes = await apiClient.post(`/${refs.toExtensionName(ref)}/versions:publish`, {
|
|
400
401
|
versionId: ref.version,
|
|
401
|
-
packageUri,
|
|
402
|
-
extensionRoot: extensionRoot !== null &&
|
|
402
|
+
packageUri: (_a = args.packageUri) !== null && _a !== void 0 ? _a : "",
|
|
403
|
+
extensionRoot: (_b = args.extensionRoot) !== null && _b !== void 0 ? _b : "/",
|
|
404
|
+
repoUri: (_c = args.repoUri) !== null && _c !== void 0 ? _c : "",
|
|
405
|
+
sourceRef: (_d = args.sourceRef) !== null && _d !== void 0 ? _d : "",
|
|
403
406
|
});
|
|
404
407
|
const pollRes = await operationPoller.pollOperation({
|
|
405
408
|
apiOrigin: api_1.extensionsOrigin,
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.canonicalizeRefInput = exports.diagnoseAndFixProject = exports.confirm = exports.getSourceOrigin = exports.isLocalOrURLPath = exports.isLocalPath = exports.isUrlPath = exports.instanceIdExists = exports.promptForRepeatInstance = exports.promptForOfficialExtension = exports.displayReleaseNotes = exports.getPublisherProjectFromName = exports.createSourceFromLocation = exports.publishExtensionVersionFromLocalSource = exports.incrementPrereleaseVersion = exports.ensureExtensionsApiEnabled = exports.promptForValidInstanceId = exports.validateSpec = exports.validateCommandLineParams = exports.populateDefaultParams = exports.substituteParams = exports.getFirebaseProjectParams = exports.getDBInstanceFromURL = exports.AUTOPOULATED_PARAM_PLACEHOLDERS = exports.EXTENSIONS_BUCKET_NAME = exports.URL_REGEX = exports.logPrefix = exports.SourceOrigin = exports.SpecParamType = void 0;
|
|
3
|
+
exports.canonicalizeRefInput = exports.diagnoseAndFixProject = exports.confirm = exports.getSourceOrigin = exports.isLocalOrURLPath = exports.isLocalPath = exports.isUrlPath = exports.instanceIdExists = exports.promptForRepeatInstance = exports.promptForOfficialExtension = exports.displayReleaseNotes = exports.getPublisherProjectFromName = exports.createSourceFromLocation = exports.publishExtensionVersionFromLocalSource = exports.publishExtensionVersionFromRemoteRepo = exports.incrementPrereleaseVersion = exports.ensureExtensionsApiEnabled = 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");
|
|
7
|
+
const tmp = require("tmp");
|
|
8
|
+
const fs = require("fs-extra");
|
|
9
|
+
const unzipper = require("unzipper");
|
|
10
|
+
const node_fetch_1 = require("node-fetch");
|
|
11
|
+
const path = require("path");
|
|
7
12
|
const marked_1 = require("marked");
|
|
8
13
|
const TerminalRenderer = require("marked-terminal");
|
|
9
14
|
marked_1.marked.setOptions({
|
|
@@ -66,6 +71,11 @@ exports.AUTOPOULATED_PARAM_PLACEHOLDERS = {
|
|
|
66
71
|
DATABASE_INSTANCE: "project-id-default-rtdb",
|
|
67
72
|
DATABASE_URL: "https://project-id-default-rtdb.firebaseio.com",
|
|
68
73
|
};
|
|
74
|
+
exports.resourceTypeToNiceName = {
|
|
75
|
+
"firebaseextensions.v1beta.function": "Cloud Function",
|
|
76
|
+
};
|
|
77
|
+
const repoRegex = new RegExp(`^https:\/\/github\.com\/[^\/]+\/[^\/]+$`);
|
|
78
|
+
const stageOptions = ["alpha", "beta", "rc", "stable"];
|
|
69
79
|
function getDBInstanceFromURL(databaseUrl = "") {
|
|
70
80
|
const instanceRegex = new RegExp("(?:https://)(.*)(?:.firebaseio.com)");
|
|
71
81
|
const matches = instanceRegex.exec(databaseUrl);
|
|
@@ -263,6 +273,24 @@ async function promptForValidInstanceId(instanceId) {
|
|
|
263
273
|
return newInstanceId;
|
|
264
274
|
}
|
|
265
275
|
exports.promptForValidInstanceId = promptForValidInstanceId;
|
|
276
|
+
async function promptForValidRepoURI() {
|
|
277
|
+
let repoIsValid = false;
|
|
278
|
+
let extensionRoot = "";
|
|
279
|
+
while (!repoIsValid) {
|
|
280
|
+
extensionRoot = await (0, prompt_1.promptOnce)({
|
|
281
|
+
type: "input",
|
|
282
|
+
message: "Enter the GitHub repo URI where this Extension's source code is located:",
|
|
283
|
+
});
|
|
284
|
+
if (!repoRegex.test(extensionRoot)) {
|
|
285
|
+
logger_1.logger.info("Repo URI must follow this format: https://github.com/<user>/<repo>");
|
|
286
|
+
}
|
|
287
|
+
else {
|
|
288
|
+
repoIsValid = true;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
return extensionRoot;
|
|
292
|
+
}
|
|
293
|
+
exports.promptForValidRepoURI = promptForValidRepoURI;
|
|
266
294
|
async function ensureExtensionsApiEnabled(options) {
|
|
267
295
|
const projectId = (0, projectUtils_1.getProjectId)(options);
|
|
268
296
|
if (!projectId) {
|
|
@@ -281,7 +309,6 @@ async function archiveAndUploadSource(extPath, bucketName) {
|
|
|
281
309
|
}
|
|
282
310
|
async function incrementPrereleaseVersion(ref, extensionVersion, stage) {
|
|
283
311
|
var _a;
|
|
284
|
-
const stageOptions = ["stable", "alpha", "beta", "rc"];
|
|
285
312
|
if (!stageOptions.includes(stage)) {
|
|
286
313
|
throw new error_1.FirebaseError(`--stage flag only supports the following values: ${stageOptions}`);
|
|
287
314
|
}
|
|
@@ -306,7 +333,8 @@ async function incrementPrereleaseVersion(ref, extensionVersion, stage) {
|
|
|
306
333
|
return extensionVersion;
|
|
307
334
|
}
|
|
308
335
|
exports.incrementPrereleaseVersion = incrementPrereleaseVersion;
|
|
309
|
-
async function
|
|
336
|
+
async function validateExtensionSpec(args) {
|
|
337
|
+
const extensionRef = `${args.publisherId}/${args.extensionId}`;
|
|
310
338
|
const extensionSpec = await (0, localHelper_1.getLocalExtensionSpec)(args.rootDirectory);
|
|
311
339
|
if (extensionSpec.name !== args.extensionId) {
|
|
312
340
|
throw new error_1.FirebaseError(`Extension ID '${clc.bold(args.extensionId)}' does not match the name in extension.yaml '${clc.bold(extensionSpec.name)}'.`);
|
|
@@ -314,13 +342,7 @@ async function publishExtensionVersionFromLocalSource(args) {
|
|
|
314
342
|
const subbedSpec = JSON.parse(JSON.stringify(extensionSpec));
|
|
315
343
|
subbedSpec.params = substituteParams(extensionSpec.params || [], exports.AUTOPOULATED_PARAM_PLACEHOLDERS);
|
|
316
344
|
validateSpec(subbedSpec);
|
|
317
|
-
extensionSpec.version = await incrementPrereleaseVersion(
|
|
318
|
-
let extension;
|
|
319
|
-
try {
|
|
320
|
-
extension = await (0, extensionsApi_1.getExtension)(`${args.publisherId}/${args.extensionId}`);
|
|
321
|
-
}
|
|
322
|
-
catch (err) {
|
|
323
|
-
}
|
|
345
|
+
extensionSpec.version = await incrementPrereleaseVersion(extensionRef, extensionSpec.version, args.stage);
|
|
324
346
|
let notes;
|
|
325
347
|
try {
|
|
326
348
|
const changes = (0, change_log_1.getLocalChangelog)(args.rootDirectory);
|
|
@@ -331,30 +353,197 @@ async function publishExtensionVersionFromLocalSource(args) {
|
|
|
331
353
|
"Please create one and add an entry for this version. " +
|
|
332
354
|
(0, marked_1.marked)("See https://firebase.google.com/docs/extensions/alpha/create-user-docs#writing-changelog for more details."));
|
|
333
355
|
}
|
|
334
|
-
if (!notes && !semver.prerelease(extensionSpec.version) &&
|
|
356
|
+
if (!notes && !semver.prerelease(extensionSpec.version) && args.latestVersion) {
|
|
335
357
|
throw new error_1.FirebaseError(`No entry for version ${extensionSpec.version} found in CHANGELOG.md. ` +
|
|
336
358
|
"Please add one so users know what has changed in this version. " +
|
|
337
359
|
(0, marked_1.marked)("See https://firebase.google.com/docs/extensions/alpha/create-user-docs#writing-changelog for more details."));
|
|
338
360
|
}
|
|
339
|
-
|
|
340
|
-
|
|
361
|
+
if (args.latestVersion) {
|
|
362
|
+
if (semver.lt(extensionSpec.version, args.latestVersion)) {
|
|
363
|
+
throw new error_1.FirebaseError(`The version you are trying to publish (${clc.bold(extensionSpec.version)}) is lower than the current version (${clc.bold(args.latestVersion)}) for the extension '${clc.bold(extensionRef)}'. Please make sure this version is greater than the current version (${clc.bold(args.latestVersion)}) inside of extension.yaml.\n`, { exit: 104 });
|
|
364
|
+
}
|
|
365
|
+
else if (semver.eq(extensionSpec.version, args.latestVersion)) {
|
|
366
|
+
throw new error_1.FirebaseError(`The version you are trying to publish (${clc.bold(extensionSpec.version)}) already exists for Extension '${clc.bold(extensionRef)}'. Please increment the version inside of extension.yaml.\n`, { exit: 103 });
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
return { extensionSpec, notes };
|
|
370
|
+
}
|
|
371
|
+
async function publishExtensionVersionFromRemoteRepo(args) {
|
|
372
|
+
const extensionRef = `${args.publisherId}/${args.extensionId}`;
|
|
373
|
+
let extension;
|
|
374
|
+
try {
|
|
375
|
+
extension = await (0, extensionsApi_1.getExtension)(extensionRef);
|
|
376
|
+
}
|
|
377
|
+
catch (err) {
|
|
378
|
+
}
|
|
379
|
+
if (args.repoUri && !repoRegex.test(args.repoUri)) {
|
|
380
|
+
throw new error_1.FirebaseError("Repo URI must follow this format: https://github.com/<user>/<repo>");
|
|
381
|
+
}
|
|
382
|
+
let repoUri = args.repoUri || (extension === null || extension === void 0 ? void 0 : extension.repoUri);
|
|
383
|
+
if (!repoUri) {
|
|
384
|
+
if (!args.nonInteractive) {
|
|
385
|
+
repoUri = await promptForValidRepoURI();
|
|
386
|
+
}
|
|
387
|
+
else {
|
|
388
|
+
throw new error_1.FirebaseError("Repo URI is required but not currently set.");
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
if ((extension === null || extension === void 0 ? void 0 : extension.repoUri) && extension.repoUri !== repoUri) {
|
|
392
|
+
throw new error_1.FirebaseError(`Repo URI '${clc.bold(args.repoUri)}' does not match repo URI '${clc.bold(extension.repoUri)}' already associated with Extension ${clc.bold(extensionRef)}. Repo URI cannot be changed.`);
|
|
393
|
+
}
|
|
394
|
+
if (!(extension === null || extension === void 0 ? void 0 : extension.repoUri)) {
|
|
395
|
+
logger_1.logger.info(`\n${clc.red("Warning:")} You are about to associate repo URI ${clc.bold(repoUri)} with Extension ${clc.bold(extensionRef)}. This cannot be changed. All future verifiable versions must be published from this repo. ` +
|
|
396
|
+
`You can continue publishing unverifiable versions from local source.`);
|
|
397
|
+
const confirmed = await confirm({
|
|
398
|
+
nonInteractive: args.nonInteractive,
|
|
399
|
+
force: args.force,
|
|
400
|
+
default: false,
|
|
401
|
+
});
|
|
402
|
+
if (!confirmed) {
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
else {
|
|
407
|
+
logger_1.logger.info(`Extension ${clc.bold(extensionRef)} is published from ${clc.bold(extension === null || extension === void 0 ? void 0 : extension.repoUri)}.`);
|
|
408
|
+
}
|
|
409
|
+
let extensionRoot = args.extensionRoot;
|
|
410
|
+
let defaultRoot = "/";
|
|
411
|
+
if (!extensionRoot) {
|
|
412
|
+
if (extension) {
|
|
413
|
+
try {
|
|
414
|
+
const extensionVersionRef = `${extensionRef}@${extension.latestVersion}`;
|
|
415
|
+
const extensionVersion = await (0, extensionsApi_1.getExtensionVersion)(extensionVersionRef);
|
|
416
|
+
if (extensionVersion.extensionRoot) {
|
|
417
|
+
defaultRoot = extensionVersion.extensionRoot;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
catch (err) {
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
extensionRoot = defaultRoot;
|
|
424
|
+
if (!args.nonInteractive) {
|
|
425
|
+
extensionRoot = await (0, prompt_1.promptOnce)({
|
|
426
|
+
type: "input",
|
|
427
|
+
message: "Enter this Extension's root directory in the repo (defaults to previous root if set):",
|
|
428
|
+
default: defaultRoot,
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
let sourceRef = args.sourceRef;
|
|
433
|
+
const defaultSourceRef = "HEAD";
|
|
434
|
+
if (!sourceRef) {
|
|
435
|
+
if (!args.nonInteractive) {
|
|
436
|
+
sourceRef = await (0, prompt_1.promptOnce)({
|
|
437
|
+
type: "input",
|
|
438
|
+
message: "Enter the commit hash, branch, or tag name to build from in the repo:",
|
|
439
|
+
default: defaultSourceRef,
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
else {
|
|
443
|
+
sourceRef = defaultSourceRef;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
let stage = args.stage;
|
|
447
|
+
const defaultStage = "rc";
|
|
448
|
+
if (!stage) {
|
|
449
|
+
if (!args.nonInteractive) {
|
|
450
|
+
stage = await (0, prompt_1.promptOnce)({
|
|
451
|
+
type: "list",
|
|
452
|
+
message: "Choose the release stage (pre-release annotations will be auto-incremented):",
|
|
453
|
+
choices: stageOptions,
|
|
454
|
+
default: defaultStage,
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
else {
|
|
458
|
+
stage = defaultStage;
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
logger_1.logger.info("Downloading and validating source code...");
|
|
462
|
+
const archiveUri = `${repoUri}/archive/${sourceRef}.zip`;
|
|
463
|
+
const tempDirectory = tmp.dirSync({ unsafeCleanup: true });
|
|
464
|
+
try {
|
|
465
|
+
const response = await (0, node_fetch_1.default)(archiveUri);
|
|
466
|
+
if (response.ok) {
|
|
467
|
+
await response.body.pipe(unzipper.Extract({ path: tempDirectory.name })).promise();
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
catch (err) {
|
|
471
|
+
throw new error_1.FirebaseError(`Failed to fetch Extension archive ${archiveUri}. Please check the repo URI and source ref. ${err}`);
|
|
472
|
+
}
|
|
473
|
+
const archiveName = fs.readdirSync(tempDirectory.name)[0];
|
|
474
|
+
const rootDirectory = path.join(tempDirectory.name, archiveName, extensionRoot);
|
|
475
|
+
try {
|
|
476
|
+
(0, localHelper_1.readFile)(path.resolve(rootDirectory, localHelper_1.EXTENSIONS_SPEC_FILE));
|
|
477
|
+
}
|
|
478
|
+
catch (err) {
|
|
479
|
+
throw new error_1.FirebaseError(`Failed to find ${clc.bold(localHelper_1.EXTENSIONS_SPEC_FILE)} in directory ${clc.bold(extensionRoot)}. Please verify the root and try again.`);
|
|
480
|
+
}
|
|
481
|
+
const { extensionSpec, notes } = await validateExtensionSpec({
|
|
482
|
+
publisherId: args.publisherId,
|
|
483
|
+
extensionId: args.extensionId,
|
|
484
|
+
rootDirectory: rootDirectory,
|
|
485
|
+
latestVersion: extension === null || extension === void 0 ? void 0 : extension.latestVersion,
|
|
486
|
+
stage: stage,
|
|
487
|
+
});
|
|
488
|
+
const sourceUri = path.join(repoUri, "tree", sourceRef, extensionRoot);
|
|
489
|
+
displayReleaseNotes(extensionRef, extensionSpec.version, notes, sourceUri);
|
|
490
|
+
const confirmed = await confirm({
|
|
341
491
|
nonInteractive: args.nonInteractive,
|
|
342
492
|
force: args.force,
|
|
343
493
|
default: false,
|
|
344
|
-
})
|
|
494
|
+
});
|
|
495
|
+
if (!confirmed) {
|
|
345
496
|
return;
|
|
346
497
|
}
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
498
|
+
const extensionVersionRef = `${extensionRef}@${extensionSpec.version}`;
|
|
499
|
+
const publishSpinner = ora(`Publishing ${clc.bold(extensionVersionRef)}`);
|
|
500
|
+
let res;
|
|
501
|
+
try {
|
|
502
|
+
publishSpinner.start();
|
|
503
|
+
res = await (0, extensionsApi_1.publishExtensionVersion)({
|
|
504
|
+
extensionVersionRef,
|
|
505
|
+
packageUri: "",
|
|
506
|
+
extensionRoot,
|
|
507
|
+
repoUri,
|
|
508
|
+
sourceRef: args.sourceRef,
|
|
509
|
+
});
|
|
510
|
+
publishSpinner.succeed(` Successfully published ${clc.bold(extensionRef)}`);
|
|
351
511
|
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
512
|
+
catch (err) {
|
|
513
|
+
publishSpinner.fail();
|
|
514
|
+
if (err.status === 404) {
|
|
515
|
+
throw getMissingPublisherError(args.publisherId);
|
|
516
|
+
}
|
|
517
|
+
throw err;
|
|
518
|
+
}
|
|
519
|
+
return res;
|
|
520
|
+
}
|
|
521
|
+
exports.publishExtensionVersionFromRemoteRepo = publishExtensionVersionFromRemoteRepo;
|
|
522
|
+
async function publishExtensionVersionFromLocalSource(args) {
|
|
523
|
+
let extension;
|
|
524
|
+
try {
|
|
525
|
+
extension = await (0, extensionsApi_1.getExtension)(`${args.publisherId}/${args.extensionId}`);
|
|
356
526
|
}
|
|
357
|
-
|
|
527
|
+
catch (err) {
|
|
528
|
+
}
|
|
529
|
+
const { extensionSpec, notes } = await validateExtensionSpec({
|
|
530
|
+
publisherId: args.publisherId,
|
|
531
|
+
extensionId: args.extensionId,
|
|
532
|
+
rootDirectory: args.rootDirectory,
|
|
533
|
+
latestVersion: extension === null || extension === void 0 ? void 0 : extension.latestVersion,
|
|
534
|
+
stage: args.stage,
|
|
535
|
+
});
|
|
536
|
+
const extensionRef = `${args.publisherId}/${args.extensionId}`;
|
|
537
|
+
displayReleaseNotes(extensionRef, extensionSpec.version, notes);
|
|
538
|
+
const confirmed = await confirm({
|
|
539
|
+
nonInteractive: args.nonInteractive,
|
|
540
|
+
force: args.force,
|
|
541
|
+
default: false,
|
|
542
|
+
});
|
|
543
|
+
if (!confirmed) {
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
const extensionVersionRef = `${extensionRef}@${extensionSpec.version}`;
|
|
358
547
|
let packageUri;
|
|
359
548
|
let objectPath = "";
|
|
360
549
|
const uploadSpinner = ora(" Archiving and uploading extension source code");
|
|
@@ -370,17 +559,17 @@ async function publishExtensionVersionFromLocalSource(args) {
|
|
|
370
559
|
original: err,
|
|
371
560
|
});
|
|
372
561
|
}
|
|
373
|
-
const publishSpinner = ora(`Publishing ${clc.bold(
|
|
562
|
+
const publishSpinner = ora(`Publishing ${clc.bold(extensionVersionRef)}`);
|
|
374
563
|
let res;
|
|
375
564
|
try {
|
|
376
565
|
publishSpinner.start();
|
|
377
|
-
res = await (0, extensionsApi_1.publishExtensionVersion)(
|
|
378
|
-
publishSpinner.succeed(` Successfully published ${clc.bold(
|
|
566
|
+
res = await (0, extensionsApi_1.publishExtensionVersion)({ extensionVersionRef, packageUri });
|
|
567
|
+
publishSpinner.succeed(` Successfully published ${clc.bold(extensionVersionRef)}`);
|
|
379
568
|
}
|
|
380
569
|
catch (err) {
|
|
381
570
|
publishSpinner.fail();
|
|
382
571
|
if (err.status === 404) {
|
|
383
|
-
throw
|
|
572
|
+
throw getMissingPublisherError(args.publisherId);
|
|
384
573
|
}
|
|
385
574
|
throw err;
|
|
386
575
|
}
|
|
@@ -388,6 +577,9 @@ async function publishExtensionVersionFromLocalSource(args) {
|
|
|
388
577
|
return res;
|
|
389
578
|
}
|
|
390
579
|
exports.publishExtensionVersionFromLocalSource = publishExtensionVersionFromLocalSource;
|
|
580
|
+
function getMissingPublisherError(publisherId) {
|
|
581
|
+
return new error_1.FirebaseError((0, marked_1.marked)(`Couldn't find publisher ID '${clc.bold(publisherId)}'. Please ensure that you have registered this ID. To register as a publisher, you can check out the [Firebase documentation](https://firebase.google.com/docs/extensions/alpha/share#register_as_an_extensions_publisher) for step-by-step instructions.`));
|
|
582
|
+
}
|
|
391
583
|
async function createSourceFromLocation(projectId, sourceUri) {
|
|
392
584
|
const extensionRoot = "/";
|
|
393
585
|
let packageUri;
|
|
@@ -431,12 +623,18 @@ function getPublisherProjectFromName(publisherName) {
|
|
|
431
623
|
throw new error_1.FirebaseError(`Could not find publisher with name '${publisherName}'.`);
|
|
432
624
|
}
|
|
433
625
|
exports.getPublisherProjectFromName = getPublisherProjectFromName;
|
|
434
|
-
function displayReleaseNotes(
|
|
626
|
+
function displayReleaseNotes(extensionRef, versionId, releaseNotes, sourceUri) {
|
|
627
|
+
const source = sourceUri || "local source";
|
|
435
628
|
const releaseNotesMessage = releaseNotes
|
|
436
|
-
?
|
|
437
|
-
: "
|
|
438
|
-
const
|
|
439
|
-
"
|
|
629
|
+
? `${clc.bold("Release notes:")}\n${(0, marked_1.marked)(releaseNotes)}`
|
|
630
|
+
: "";
|
|
631
|
+
const metadataMessage = `${clc.bold("Extension:")} ${extensionRef}\n` +
|
|
632
|
+
`${clc.bold("Version:")} ${clc.bold(clc.green(versionId))}\n` +
|
|
633
|
+
`${clc.bold("Source:")} ${source}\n`;
|
|
634
|
+
const message = `\nYou are about to publish a new version to Firebase's registry of Extensions.\n\n` +
|
|
635
|
+
metadataMessage +
|
|
636
|
+
releaseNotesMessage +
|
|
637
|
+
`\nOnce an Extension version is published, it cannot be changed. If you wish to make changes after publishing, you will need to publish a new version.\n`;
|
|
440
638
|
logger_1.logger.info(message);
|
|
441
639
|
}
|
|
442
640
|
exports.displayReleaseNotes = displayReleaseNotes;
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.isLocalExtension = exports.readFile = exports.findExtensionYaml = exports.getLocalExtensionSpec = void 0;
|
|
3
|
+
exports.isLocalExtension = exports.readFile = exports.findExtensionYaml = exports.getLocalExtensionSpec = exports.EXTENSIONS_SPEC_FILE = void 0;
|
|
4
4
|
const fs = require("fs-extra");
|
|
5
5
|
const path = require("path");
|
|
6
6
|
const yaml = require("js-yaml");
|
|
7
7
|
const fsutils_1 = require("../fsutils");
|
|
8
8
|
const error_1 = require("../error");
|
|
9
9
|
const logger_1 = require("../logger");
|
|
10
|
-
|
|
10
|
+
exports.EXTENSIONS_SPEC_FILE = "extension.yaml";
|
|
11
11
|
const EXTENSIONS_PREINSTALL_FILE = "PREINSTALL.md";
|
|
12
12
|
async function getLocalExtensionSpec(directory) {
|
|
13
|
-
const spec = await parseYAML(readFile(path.resolve(directory, EXTENSIONS_SPEC_FILE)));
|
|
13
|
+
const spec = await parseYAML(readFile(path.resolve(directory, exports.EXTENSIONS_SPEC_FILE)));
|
|
14
14
|
try {
|
|
15
15
|
const preinstall = readFile(path.resolve(directory, EXTENSIONS_PREINSTALL_FILE));
|
|
16
16
|
spec.preinstallContent = preinstall;
|
|
@@ -22,7 +22,7 @@ async function getLocalExtensionSpec(directory) {
|
|
|
22
22
|
}
|
|
23
23
|
exports.getLocalExtensionSpec = getLocalExtensionSpec;
|
|
24
24
|
function findExtensionYaml(directory) {
|
|
25
|
-
while (!(0, fsutils_1.fileExistsSync)(path.resolve(directory, EXTENSIONS_SPEC_FILE))) {
|
|
25
|
+
while (!(0, fsutils_1.fileExistsSync)(path.resolve(directory, exports.EXTENSIONS_SPEC_FILE))) {
|
|
26
26
|
const parentDir = path.dirname(directory);
|
|
27
27
|
if (parentDir === directory) {
|
|
28
28
|
throw new error_1.FirebaseError("Couldn't find an extension.yaml file. Check that you are in the root directory of your extension.");
|