firebase-tools 11.30.0 → 12.0.1
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/api.js +4 -2
- package/lib/commands/ext-configure.js +2 -1
- package/lib/commands/ext-dev-deprecate.js +24 -20
- package/lib/commands/ext-dev-list.js +12 -11
- package/lib/commands/ext-dev-publish.js +13 -47
- package/lib/commands/ext-dev-register.js +8 -5
- package/lib/commands/ext-dev-undeprecate.js +4 -4
- package/lib/commands/ext-dev-upload.js +88 -0
- package/lib/commands/ext-dev-usage.js +3 -3
- package/lib/commands/ext-install.js +5 -10
- package/lib/commands/ext-uninstall.js +0 -1
- package/lib/commands/ext-update.js +4 -10
- package/lib/commands/index.js +9 -19
- package/lib/deploy/extensions/planner.js +13 -7
- package/lib/deploy/extensions/prepare.js +16 -32
- package/lib/emulator/eventarcEmulatorUtils.js +4 -2
- package/lib/emulator/storage/rules/config.js +17 -7
- package/lib/experiments.js +7 -6
- package/lib/extensions/extensionsApi.js +24 -151
- package/lib/extensions/extensionsHelper.js +282 -145
- package/lib/extensions/manifest.js +1 -8
- package/lib/extensions/publisherApi.js +215 -0
- package/lib/extensions/refs.js +1 -1
- package/lib/extensions/resolveSource.js +1 -18
- package/lib/extensions/tos.js +78 -0
- package/lib/extensions/warnings.js +21 -41
- package/lib/frameworks/angular/index.js +67 -185
- package/lib/frameworks/angular/interfaces.js +2 -0
- package/lib/frameworks/angular/utils.js +274 -0
- package/lib/frameworks/constants.js +5 -2
- package/lib/frameworks/index.js +60 -28
- package/lib/frameworks/next/index.js +86 -40
- package/lib/frameworks/next/utils.js +29 -12
- package/lib/frameworks/utils.js +11 -4
- package/lib/functions/python.js +2 -2
- package/lib/hosting/api.js +32 -1
- package/package.json +2 -2
- package/templates/extensions/POSTINSTALL.md +2 -2
- package/templates/extensions/PREINSTALL.md +1 -1
- package/templates/extensions/extension.yaml +10 -6
- package/templates/extensions/javascript/WELCOME.md +1 -1
- package/templates/extensions/typescript/WELCOME.md +1 -1
- package/templates/extensions/typescript/index.ts +1 -1
- package/templates/init/functions/javascript/index.js +16 -6
- package/templates/init/functions/javascript/package.lint.json +4 -4
- package/templates/init/functions/javascript/package.nolint.json +4 -4
- package/templates/init/functions/typescript/index.ts +16 -6
- package/templates/init/functions/typescript/package.lint.json +4 -4
- package/templates/init/functions/typescript/package.nolint.json +4 -4
- package/lib/commands/ext-dev-emulators-exec.js +0 -27
- package/lib/commands/ext-dev-emulators-start.js +0 -24
- package/lib/commands/ext-dev-extension-delete.js +0 -45
- package/lib/commands/ext-dev-unpublish.js +0 -49
- package/lib/commands/ext-sources-create.js +0 -24
- package/lib/extensions/askUserForConsent.js +0 -33
- package/npm-shrinkwrap.json +0 -12368
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.canonicalizeRefInput = exports.diagnoseAndFixProject = exports.getSourceOrigin = exports.isLocalOrURLPath = exports.isLocalPath = exports.isUrlPath = exports.instanceIdExists = exports.promptForRepeatInstance = exports.promptForOfficialExtension = exports.displayReleaseNotes = exports.getPublisherProjectFromName = exports.createSourceFromLocation = exports.
|
|
3
|
+
exports.canonicalizeRefInput = 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.promptForValidExtensionRoot = 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");
|
|
@@ -27,6 +27,7 @@ const ensureApiEnabled_1 = require("../ensureApiEnabled");
|
|
|
27
27
|
const storage_1 = require("../gcp/storage");
|
|
28
28
|
const projectUtils_1 = require("../projectUtils");
|
|
29
29
|
const extensionsApi_1 = require("./extensionsApi");
|
|
30
|
+
const publisherApi_1 = require("./publisherApi");
|
|
30
31
|
const refs = require("./refs");
|
|
31
32
|
const localHelper_1 = require("./localHelper");
|
|
32
33
|
const prompt_1 = require("../prompt");
|
|
@@ -75,7 +76,7 @@ exports.resourceTypeToNiceName = {
|
|
|
75
76
|
"firebaseextensions.v1beta.function": "Cloud Function",
|
|
76
77
|
};
|
|
77
78
|
const repoRegex = new RegExp(`^https:\/\/github\.com\/[^\/]+\/[^\/]+$`);
|
|
78
|
-
const stageOptions = ["
|
|
79
|
+
const stageOptions = ["rc", "alpha", "beta", "stable"];
|
|
79
80
|
function getDBInstanceFromURL(databaseUrl = "") {
|
|
80
81
|
const instanceRegex = new RegExp("(?:https://)(.*)(?:.firebaseio.com)");
|
|
81
82
|
const matches = instanceRegex.exec(databaseUrl);
|
|
@@ -175,6 +176,15 @@ function validateSpec(spec) {
|
|
|
175
176
|
if (!spec.version) {
|
|
176
177
|
errors.push("extension.yaml is missing required field: version");
|
|
177
178
|
}
|
|
179
|
+
else if (!semver.valid(spec.version)) {
|
|
180
|
+
errors.push(`version ${spec.version} in extension.yaml is not a valid semver`);
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
const version = semver.parse(spec.version);
|
|
184
|
+
if (version.prerelease.length > 0 || version.build.length > 0) {
|
|
185
|
+
errors.push("version field in extension.yaml does not support pre-release annotations; instead, set a pre-release stage using the --stage flag");
|
|
186
|
+
}
|
|
187
|
+
}
|
|
178
188
|
if (!spec.license) {
|
|
179
189
|
errors.push("extension.yaml is missing required field: license");
|
|
180
190
|
}
|
|
@@ -279,7 +289,7 @@ async function promptForValidRepoURI() {
|
|
|
279
289
|
while (!repoIsValid) {
|
|
280
290
|
extensionRoot = await (0, prompt_1.promptOnce)({
|
|
281
291
|
type: "input",
|
|
282
|
-
message: "Enter the GitHub repo URI where this
|
|
292
|
+
message: "Enter the GitHub repo URI where this extension's source code is located:",
|
|
283
293
|
});
|
|
284
294
|
if (!repoRegex.test(extensionRoot)) {
|
|
285
295
|
logger_1.logger.info("Repo URI must follow this format: https://github.com/<user>/<repo>");
|
|
@@ -291,6 +301,60 @@ async function promptForValidRepoURI() {
|
|
|
291
301
|
return extensionRoot;
|
|
292
302
|
}
|
|
293
303
|
exports.promptForValidRepoURI = promptForValidRepoURI;
|
|
304
|
+
async function promptForValidExtensionRoot(defaultRoot) {
|
|
305
|
+
let rootIsValid = false;
|
|
306
|
+
let extensionRoot = "";
|
|
307
|
+
while (!rootIsValid) {
|
|
308
|
+
extensionRoot = await (0, prompt_1.promptOnce)({
|
|
309
|
+
type: "input",
|
|
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;
|
|
316
|
+
}
|
|
317
|
+
exports.promptForValidExtensionRoot = promptForValidExtensionRoot;
|
|
318
|
+
async function promptForReleaseStage(args) {
|
|
319
|
+
let stage = "rc";
|
|
320
|
+
if (!args.nonInteractive) {
|
|
321
|
+
const choices = [
|
|
322
|
+
{ name: `Release candidate (${args.versionByStage.get("rc")})`, value: "rc" },
|
|
323
|
+
{ name: `Alpha (${args.versionByStage.get("alpha")})`, value: "alpha" },
|
|
324
|
+
{ name: `Beta (${args.versionByStage.get("beta")})`, value: "beta" },
|
|
325
|
+
];
|
|
326
|
+
if (args.allowStable) {
|
|
327
|
+
const stableChoice = {
|
|
328
|
+
name: `Stable (${args.versionByStage.get("stable")}${args.autoReview ? ", automatically sent for review" : ""})`,
|
|
329
|
+
value: "stable",
|
|
330
|
+
};
|
|
331
|
+
choices.push(stableChoice);
|
|
332
|
+
}
|
|
333
|
+
stage = await (0, prompt_1.promptOnce)({
|
|
334
|
+
type: "list",
|
|
335
|
+
message: "Choose the release stage:",
|
|
336
|
+
choices: choices,
|
|
337
|
+
default: stage,
|
|
338
|
+
});
|
|
339
|
+
if (stage === "stable" && !args.hasVersions) {
|
|
340
|
+
logger_1.logger.info(`${clc.bold(clc.yellow("Warning:"))} It's highly recommended to first upload a pre-release version before choosing stable.`);
|
|
341
|
+
const confirmed = await (0, prompt_1.confirm)({
|
|
342
|
+
nonInteractive: args.nonInteractive,
|
|
343
|
+
force: args.force,
|
|
344
|
+
default: false,
|
|
345
|
+
});
|
|
346
|
+
if (!confirmed) {
|
|
347
|
+
stage = await (0, prompt_1.promptOnce)({
|
|
348
|
+
type: "list",
|
|
349
|
+
message: "Choose the release stage:",
|
|
350
|
+
choices: choices,
|
|
351
|
+
default: stage,
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
return stage;
|
|
357
|
+
}
|
|
294
358
|
async function ensureExtensionsApiEnabled(options) {
|
|
295
359
|
const projectId = (0, projectUtils_1.getProjectId)(options);
|
|
296
360
|
if (!projectId) {
|
|
@@ -299,6 +363,14 @@ async function ensureExtensionsApiEnabled(options) {
|
|
|
299
363
|
return await (0, ensureApiEnabled_1.ensure)(projectId, "firebaseextensions.googleapis.com", "extensions", options.markdown);
|
|
300
364
|
}
|
|
301
365
|
exports.ensureExtensionsApiEnabled = ensureExtensionsApiEnabled;
|
|
366
|
+
async function ensureExtensionsPublisherApiEnabled(options) {
|
|
367
|
+
const projectId = (0, projectUtils_1.getProjectId)(options);
|
|
368
|
+
if (!projectId) {
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
return await (0, ensureApiEnabled_1.ensure)(projectId, "firebaseextensionspublisher.googleapis.com", "extensions", options.markdown);
|
|
372
|
+
}
|
|
373
|
+
exports.ensureExtensionsPublisherApiEnabled = ensureExtensionsPublisherApiEnabled;
|
|
302
374
|
async function archiveAndUploadSource(extPath, bucketName) {
|
|
303
375
|
const zippedSource = await (0, archiveDirectory_1.archiveDirectory)(extPath, {
|
|
304
376
|
type: "zip",
|
|
@@ -307,75 +379,150 @@ async function archiveAndUploadSource(extPath, bucketName) {
|
|
|
307
379
|
const res = await (0, storage_1.uploadObject)(zippedSource, bucketName);
|
|
308
380
|
return `/${res.bucket}/${res.object}`;
|
|
309
381
|
}
|
|
310
|
-
async function
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
382
|
+
async function getNextVersionByStage(extensionRef, newVersion) {
|
|
383
|
+
let extensionVersions = [];
|
|
384
|
+
try {
|
|
385
|
+
extensionVersions = await (0, publisherApi_1.listExtensionVersions)(extensionRef, `id="${newVersion}"`, true);
|
|
314
386
|
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
387
|
+
catch (err) {
|
|
388
|
+
}
|
|
389
|
+
const versionByStage = new Map(["rc", "alpha", "beta"].map((stage) => [
|
|
390
|
+
stage,
|
|
391
|
+
semver.inc(`${newVersion}-${stage}`, "prerelease", undefined, stage),
|
|
392
|
+
]));
|
|
393
|
+
for (const extensionVersion of extensionVersions) {
|
|
394
|
+
const version = semver.parse(extensionVersion.spec.version);
|
|
395
|
+
if (!version.prerelease.length) {
|
|
396
|
+
continue;
|
|
323
397
|
}
|
|
324
|
-
|
|
398
|
+
const prerelease = semver.prerelease(version)[0];
|
|
399
|
+
const stage = prerelease.split(".")[0];
|
|
400
|
+
if (versionByStage.has(stage) && semver.gte(version, versionByStage.get(stage))) {
|
|
401
|
+
versionByStage.set(stage, semver.inc(version, "prerelease", undefined, stage));
|
|
325
402
|
}
|
|
326
|
-
const latestVersion = (_a = extensionVersions
|
|
327
|
-
.map((version) => semver.parse(version.spec.version))
|
|
328
|
-
.filter((version) => version.prerelease.length > 0 && version.prerelease[0] === stage)
|
|
329
|
-
.sort((v1, v2) => semver.compare(v1, v2))
|
|
330
|
-
.pop()) !== null && _a !== void 0 ? _a : `${version}-${stage}`;
|
|
331
|
-
return semver.inc(latestVersion, "prerelease", undefined, stage);
|
|
332
403
|
}
|
|
333
|
-
|
|
404
|
+
versionByStage.set("stable", newVersion);
|
|
405
|
+
return { versionByStage, hasVersions: extensionVersions.length > 0 };
|
|
334
406
|
}
|
|
335
|
-
exports.
|
|
336
|
-
async function validateExtensionSpec(
|
|
337
|
-
const
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
throw new error_1.FirebaseError(`Extension ID '${clc.bold(args.extensionId)}' does not match the name in extension.yaml '${clc.bold(extensionSpec.name)}'.`);
|
|
407
|
+
exports.getNextVersionByStage = getNextVersionByStage;
|
|
408
|
+
async function validateExtensionSpec(rootDirectory, extensionId) {
|
|
409
|
+
const extensionSpec = await (0, localHelper_1.getLocalExtensionSpec)(rootDirectory);
|
|
410
|
+
if (extensionSpec.name !== extensionId) {
|
|
411
|
+
throw new error_1.FirebaseError(`Extension ID '${clc.bold(extensionId)}' does not match the name in extension.yaml '${clc.bold(extensionSpec.name)}'.`);
|
|
341
412
|
}
|
|
342
413
|
const subbedSpec = JSON.parse(JSON.stringify(extensionSpec));
|
|
343
414
|
subbedSpec.params = substituteParams(extensionSpec.params || [], exports.AUTOPOULATED_PARAM_PLACEHOLDERS);
|
|
344
415
|
validateSpec(subbedSpec);
|
|
345
|
-
|
|
416
|
+
return extensionSpec;
|
|
417
|
+
}
|
|
418
|
+
function validateReleaseNotes(rootDirectory, newVersion, extension) {
|
|
346
419
|
let notes;
|
|
347
420
|
try {
|
|
348
|
-
const changes = (0, change_log_1.getLocalChangelog)(
|
|
349
|
-
notes = changes[
|
|
421
|
+
const changes = (0, change_log_1.getLocalChangelog)(rootDirectory);
|
|
422
|
+
notes = changes[newVersion];
|
|
350
423
|
}
|
|
351
424
|
catch (err) {
|
|
352
425
|
throw new error_1.FirebaseError("No CHANGELOG.md file found. " +
|
|
353
426
|
"Please create one and add an entry for this version. " +
|
|
354
|
-
(0, marked_1.marked)("See https://firebase.google.com/docs/extensions/
|
|
427
|
+
(0, marked_1.marked)("See https://firebase.google.com/docs/extensions/publishers/user-documentation#writing-changelog for more details."));
|
|
355
428
|
}
|
|
356
|
-
if (!notes && !semver.prerelease(
|
|
357
|
-
throw new error_1.FirebaseError(`No entry for version ${
|
|
429
|
+
if (!notes && !semver.prerelease(newVersion) && extension) {
|
|
430
|
+
throw new error_1.FirebaseError(`No entry for version ${newVersion} found in CHANGELOG.md. ` +
|
|
358
431
|
"Please add one so users know what has changed in this version. " +
|
|
359
|
-
(0, marked_1.marked)("See https://firebase.google.com/docs/extensions/
|
|
432
|
+
(0, marked_1.marked)("See https://firebase.google.com/docs/extensions/publishers/user-documentation#writing-changelog for more details."));
|
|
360
433
|
}
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
434
|
+
return notes;
|
|
435
|
+
}
|
|
436
|
+
function validateVersion(extensionRef, newVersion, latestVersion) {
|
|
437
|
+
if (latestVersion) {
|
|
438
|
+
if (semver.lt(newVersion, latestVersion)) {
|
|
439
|
+
throw new error_1.FirebaseError(`The version you are trying to publish (${clc.bold(newVersion)}) is lower than the current version (${clc.bold(latestVersion)}) for the extension '${clc.bold(extensionRef)}'. Make sure this version is greater than the current version (${clc.bold(latestVersion)}) inside of extension.yaml and try again.\n`, { exit: 104 });
|
|
364
440
|
}
|
|
365
|
-
else if (semver.eq(
|
|
366
|
-
throw new error_1.FirebaseError(`The version you are trying to
|
|
441
|
+
else if (semver.eq(newVersion, latestVersion)) {
|
|
442
|
+
throw new error_1.FirebaseError(`The version you are trying to upload (${clc.bold(newVersion)}) already exists for extension '${clc.bold(extensionRef)}'. Increment the version inside of extension.yaml and try again.\n`, { exit: 103 });
|
|
367
443
|
}
|
|
368
444
|
}
|
|
369
|
-
return { extensionSpec, notes };
|
|
370
445
|
}
|
|
371
|
-
|
|
446
|
+
function unpackExtensionState(extension) {
|
|
447
|
+
switch (extension.state) {
|
|
448
|
+
case "PUBLISHED":
|
|
449
|
+
if (extension.latestApprovedVersion) {
|
|
450
|
+
return clc.bold(clc.green("Published"));
|
|
451
|
+
}
|
|
452
|
+
else if (extension.latestVersion) {
|
|
453
|
+
return clc.green("Uploaded");
|
|
454
|
+
}
|
|
455
|
+
else {
|
|
456
|
+
return "Prerelease";
|
|
457
|
+
}
|
|
458
|
+
case "DEPRECATED":
|
|
459
|
+
return clc.red("Deprecated");
|
|
460
|
+
case "SUSPENDED":
|
|
461
|
+
return clc.bold(clc.red("Suspended"));
|
|
462
|
+
default:
|
|
463
|
+
return "-";
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
exports.unpackExtensionState = unpackExtensionState;
|
|
467
|
+
function displayExtensionHeader(extensionRef, extension, extensionRoot) {
|
|
468
|
+
var _a, _b;
|
|
469
|
+
if (extension) {
|
|
470
|
+
let source = "Local source";
|
|
471
|
+
if (extension.repoUri) {
|
|
472
|
+
const uri = new URL(extension.repoUri);
|
|
473
|
+
uri.pathname = path.join(uri.pathname, extensionRoot !== null && extensionRoot !== void 0 ? extensionRoot : "");
|
|
474
|
+
source = `${uri.toString()} (use --repo and --root to modify)`;
|
|
475
|
+
}
|
|
476
|
+
logger_1.logger.info(`\n${clc.bold("Extension:")} ${extension.ref}\n` +
|
|
477
|
+
`${clc.bold("State:")} ${unpackExtensionState(extension)}\n` +
|
|
478
|
+
`${clc.bold("Latest Version:")} ${(_a = extension.latestVersion) !== null && _a !== void 0 ? _a : "-"}\n` +
|
|
479
|
+
`${clc.bold("Version in Extensions Hub:")} ${(_b = extension.latestApprovedVersion) !== null && _b !== void 0 ? _b : "-"}\n` +
|
|
480
|
+
`${clc.bold("Source in GitHub:")} ${source}\n`);
|
|
481
|
+
}
|
|
482
|
+
else {
|
|
483
|
+
logger_1.logger.info(`\n${clc.bold("Extension:")} ${extensionRef}\n` +
|
|
484
|
+
`${clc.bold("State:")} ${clc.bold(clc.blue("New"))}\n`);
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
async function fetchExtensionSource(repoUri, sourceRef, extensionRoot) {
|
|
488
|
+
const sourceUri = repoUri + path.join("/tree", sourceRef, extensionRoot);
|
|
489
|
+
logger_1.logger.info(`Validating source code at ${clc.bold(sourceUri)}...`);
|
|
490
|
+
const archiveUri = `${repoUri}/archive/${sourceRef}.zip`;
|
|
491
|
+
const tempDirectory = tmp.dirSync({ unsafeCleanup: true });
|
|
492
|
+
try {
|
|
493
|
+
const response = await (0, node_fetch_1.default)(archiveUri);
|
|
494
|
+
if (response.ok) {
|
|
495
|
+
await response.body.pipe((0, unzip_1.createUnzipTransform)(tempDirectory.name)).promise();
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
catch (err) {
|
|
499
|
+
throw new error_1.FirebaseError(`Failed to fetch extension archive from ${archiveUri}. Please check the repo URI and source ref. ${err}`);
|
|
500
|
+
}
|
|
501
|
+
const archiveName = fs.readdirSync(tempDirectory.name)[0];
|
|
502
|
+
const rootDirectory = path.join(tempDirectory.name, archiveName, extensionRoot);
|
|
503
|
+
try {
|
|
504
|
+
(0, localHelper_1.readFile)(path.resolve(rootDirectory, localHelper_1.EXTENSIONS_SPEC_FILE));
|
|
505
|
+
}
|
|
506
|
+
catch (err) {
|
|
507
|
+
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.`);
|
|
508
|
+
}
|
|
509
|
+
return rootDirectory;
|
|
510
|
+
}
|
|
511
|
+
async function uploadExtensionVersionFromGitHubSource(args) {
|
|
512
|
+
var _a, _b, _c;
|
|
372
513
|
const extensionRef = `${args.publisherId}/${args.extensionId}`;
|
|
373
514
|
let extension;
|
|
515
|
+
let latestVersion;
|
|
374
516
|
try {
|
|
375
|
-
extension = await (0,
|
|
517
|
+
extension = await (0, publisherApi_1.getExtension)(extensionRef);
|
|
518
|
+
latestVersion = await (0, publisherApi_1.getExtensionVersion)(`${extensionRef}@latest`);
|
|
376
519
|
}
|
|
377
520
|
catch (err) {
|
|
378
521
|
}
|
|
522
|
+
displayExtensionHeader(extensionRef, extension, latestVersion === null || latestVersion === void 0 ? void 0 : latestVersion.extensionRoot);
|
|
523
|
+
if (args.stage && !stageOptions.includes(args.stage)) {
|
|
524
|
+
throw new error_1.FirebaseError(`--stage only supports the following values: ${stageOptions.join(", ")}`);
|
|
525
|
+
}
|
|
379
526
|
if (args.repoUri && !repoRegex.test(args.repoUri)) {
|
|
380
527
|
throw new error_1.FirebaseError("Repo URI must follow this format: https://github.com/<user>/<repo>");
|
|
381
528
|
}
|
|
@@ -388,30 +535,14 @@ async function publishExtensionVersionFromRemoteRepo(args) {
|
|
|
388
535
|
throw new error_1.FirebaseError("Repo URI is required but not currently set.");
|
|
389
536
|
}
|
|
390
537
|
}
|
|
391
|
-
|
|
392
|
-
logger_1.logger.info(`Extension ${clc.bold(extensionRef)} is published from ${clc.bold(extension === null || extension === void 0 ? void 0 : extension.repoUri)}. Use --repo to change this repo.`);
|
|
393
|
-
}
|
|
394
|
-
let extensionRoot = args.extensionRoot;
|
|
395
|
-
let defaultRoot = "/";
|
|
538
|
+
let extensionRoot = args.extensionRoot || (latestVersion === null || latestVersion === void 0 ? void 0 : latestVersion.extensionRoot);
|
|
396
539
|
if (!extensionRoot) {
|
|
397
|
-
|
|
398
|
-
try {
|
|
399
|
-
const extensionVersionRef = `${extensionRef}@${extension.latestVersion}`;
|
|
400
|
-
const extensionVersion = await (0, extensionsApi_1.getExtensionVersion)(extensionVersionRef);
|
|
401
|
-
if (extensionVersion.extensionRoot) {
|
|
402
|
-
defaultRoot = extensionVersion.extensionRoot;
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
catch (err) {
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
extensionRoot = defaultRoot;
|
|
540
|
+
const defaultRoot = "/";
|
|
409
541
|
if (!args.nonInteractive) {
|
|
410
|
-
extensionRoot = await (
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
});
|
|
542
|
+
extensionRoot = await promptForValidExtensionRoot(defaultRoot);
|
|
543
|
+
}
|
|
544
|
+
else {
|
|
545
|
+
extensionRoot = defaultRoot;
|
|
415
546
|
}
|
|
416
547
|
}
|
|
417
548
|
let sourceRef = args.sourceRef;
|
|
@@ -428,50 +559,35 @@ async function publishExtensionVersionFromRemoteRepo(args) {
|
|
|
428
559
|
sourceRef = defaultSourceRef;
|
|
429
560
|
}
|
|
430
561
|
}
|
|
562
|
+
const rootDirectory = await fetchExtensionSource(repoUri, sourceRef, extensionRoot);
|
|
563
|
+
const extensionSpec = await validateExtensionSpec(rootDirectory, args.extensionId);
|
|
564
|
+
validateVersion(extensionRef, extensionSpec.version, extension === null || extension === void 0 ? void 0 : extension.latestVersion);
|
|
565
|
+
const { versionByStage, hasVersions } = await getNextVersionByStage(extensionRef, extensionSpec.version);
|
|
566
|
+
const autoReview = !!(extension === null || extension === void 0 ? void 0 : extension.latestApprovedVersion) ||
|
|
567
|
+
((_a = latestVersion === null || latestVersion === void 0 ? void 0 : latestVersion.listing) === null || _a === void 0 ? void 0 : _a.state) === "PENDING" ||
|
|
568
|
+
((_b = latestVersion === null || latestVersion === void 0 ? void 0 : latestVersion.listing) === null || _b === void 0 ? void 0 : _b.state) === "APPROVED" ||
|
|
569
|
+
((_c = latestVersion === null || latestVersion === void 0 ? void 0 : latestVersion.listing) === null || _c === void 0 ? void 0 : _c.state) === "REJECTED";
|
|
431
570
|
let stage = args.stage;
|
|
432
|
-
const defaultStage = "rc";
|
|
433
571
|
if (!stage) {
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
}
|
|
442
|
-
else {
|
|
443
|
-
stage = defaultStage;
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
logger_1.logger.info("Downloading and validating source code...");
|
|
447
|
-
const archiveUri = `${repoUri}/archive/${sourceRef}.zip`;
|
|
448
|
-
const tempDirectory = tmp.dirSync({ unsafeCleanup: true });
|
|
449
|
-
try {
|
|
450
|
-
const response = await (0, node_fetch_1.default)(archiveUri);
|
|
451
|
-
if (response.ok) {
|
|
452
|
-
await response.body.pipe((0, unzip_1.createUnzipTransform)(tempDirectory.name)).promise();
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
catch (err) {
|
|
456
|
-
throw new error_1.FirebaseError(`Failed to fetch Extension archive ${archiveUri}. Please check the repo URI and source ref. ${err}`);
|
|
457
|
-
}
|
|
458
|
-
const archiveName = fs.readdirSync(tempDirectory.name)[0];
|
|
459
|
-
const rootDirectory = path.join(tempDirectory.name, archiveName, extensionRoot);
|
|
460
|
-
try {
|
|
461
|
-
(0, localHelper_1.readFile)(path.resolve(rootDirectory, localHelper_1.EXTENSIONS_SPEC_FILE));
|
|
462
|
-
}
|
|
463
|
-
catch (err) {
|
|
464
|
-
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.`);
|
|
572
|
+
stage = await promptForReleaseStage({
|
|
573
|
+
versionByStage,
|
|
574
|
+
autoReview,
|
|
575
|
+
allowStable: true,
|
|
576
|
+
hasVersions,
|
|
577
|
+
nonInteractive: args.nonInteractive,
|
|
578
|
+
force: args.force,
|
|
579
|
+
});
|
|
465
580
|
}
|
|
466
|
-
const
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
581
|
+
const newVersion = versionByStage.get(stage);
|
|
582
|
+
const releaseNotes = validateReleaseNotes(rootDirectory, extensionSpec.version, extension);
|
|
583
|
+
const sourceUri = repoUri + path.join("/tree", sourceRef, extensionRoot);
|
|
584
|
+
displayReleaseNotes({
|
|
585
|
+
extensionRef,
|
|
586
|
+
newVersion,
|
|
587
|
+
releaseNotes,
|
|
588
|
+
sourceUri,
|
|
589
|
+
autoReview: stage === "stable" && autoReview,
|
|
472
590
|
});
|
|
473
|
-
const sourceUri = path.join(repoUri, "tree", sourceRef, extensionRoot);
|
|
474
|
-
displayReleaseNotes(extensionRef, extensionSpec.version, notes, sourceUri);
|
|
475
591
|
const confirmed = await (0, prompt_1.confirm)({
|
|
476
592
|
nonInteractive: args.nonInteractive,
|
|
477
593
|
force: args.force,
|
|
@@ -480,22 +596,21 @@ async function publishExtensionVersionFromRemoteRepo(args) {
|
|
|
480
596
|
if (!confirmed) {
|
|
481
597
|
return;
|
|
482
598
|
}
|
|
483
|
-
const extensionVersionRef = `${extensionRef}@${
|
|
484
|
-
const
|
|
599
|
+
const extensionVersionRef = `${extensionRef}@${newVersion}`;
|
|
600
|
+
const uploadSpinner = ora(`Uploading ${clc.bold(extensionVersionRef)}...`);
|
|
485
601
|
let res;
|
|
486
602
|
try {
|
|
487
|
-
|
|
488
|
-
res = await (0,
|
|
603
|
+
uploadSpinner.start();
|
|
604
|
+
res = await (0, publisherApi_1.createExtensionVersionFromGitHubSource)({
|
|
489
605
|
extensionVersionRef,
|
|
490
|
-
packageUri: "",
|
|
491
606
|
extensionRoot,
|
|
492
607
|
repoUri,
|
|
493
|
-
sourceRef:
|
|
608
|
+
sourceRef: sourceRef,
|
|
494
609
|
});
|
|
495
|
-
|
|
610
|
+
uploadSpinner.succeed(`Successfully uploaded ${clc.bold(extensionRef)}`);
|
|
496
611
|
}
|
|
497
612
|
catch (err) {
|
|
498
|
-
|
|
613
|
+
uploadSpinner.fail();
|
|
499
614
|
if (err.status === 404) {
|
|
500
615
|
throw getMissingPublisherError(args.publisherId);
|
|
501
616
|
}
|
|
@@ -503,23 +618,44 @@ async function publishExtensionVersionFromRemoteRepo(args) {
|
|
|
503
618
|
}
|
|
504
619
|
return res;
|
|
505
620
|
}
|
|
506
|
-
exports.
|
|
507
|
-
async function
|
|
621
|
+
exports.uploadExtensionVersionFromGitHubSource = uploadExtensionVersionFromGitHubSource;
|
|
622
|
+
async function uploadExtensionVersionFromLocalSource(args) {
|
|
623
|
+
const extensionRef = `${args.publisherId}/${args.extensionId}`;
|
|
508
624
|
let extension;
|
|
625
|
+
let latestVersion;
|
|
509
626
|
try {
|
|
510
|
-
extension = await (0,
|
|
627
|
+
extension = await (0, publisherApi_1.getExtension)(extensionRef);
|
|
628
|
+
latestVersion = await (0, publisherApi_1.getExtensionVersion)(`${extensionRef}@latest`);
|
|
511
629
|
}
|
|
512
630
|
catch (err) {
|
|
513
631
|
}
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
const
|
|
522
|
-
|
|
632
|
+
displayExtensionHeader(extensionRef, extension, latestVersion === null || latestVersion === void 0 ? void 0 : latestVersion.extensionRoot);
|
|
633
|
+
const localStageOptions = ["rc", "alpha", "beta"];
|
|
634
|
+
if (args.stage && !localStageOptions.includes(args.stage)) {
|
|
635
|
+
throw new error_1.FirebaseError(`--stage only supports the following values when used with --local: ${localStageOptions.join(", ")}`);
|
|
636
|
+
}
|
|
637
|
+
const extensionSpec = await validateExtensionSpec(args.rootDirectory, args.extensionId);
|
|
638
|
+
validateVersion(extensionRef, extensionSpec.version, extension === null || extension === void 0 ? void 0 : extension.latestVersion);
|
|
639
|
+
const { versionByStage } = await getNextVersionByStage(extensionRef, extensionSpec.version);
|
|
640
|
+
let stage = args.stage;
|
|
641
|
+
if (!stage) {
|
|
642
|
+
if (!args.nonInteractive) {
|
|
643
|
+
stage = await promptForReleaseStage({
|
|
644
|
+
versionByStage,
|
|
645
|
+
autoReview: false,
|
|
646
|
+
allowStable: false,
|
|
647
|
+
hasVersions: false,
|
|
648
|
+
nonInteractive: args.nonInteractive,
|
|
649
|
+
force: args.force,
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
else {
|
|
653
|
+
stage = "rc";
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
const newVersion = versionByStage.get(stage);
|
|
657
|
+
const releaseNotes = validateReleaseNotes(args.rootDirectory, extensionSpec.version, extension);
|
|
658
|
+
displayReleaseNotes({ extensionRef, newVersion, releaseNotes, autoReview: false });
|
|
523
659
|
const confirmed = await (0, prompt_1.confirm)({
|
|
524
660
|
nonInteractive: args.nonInteractive,
|
|
525
661
|
force: args.force,
|
|
@@ -528,28 +664,28 @@ async function publishExtensionVersionFromLocalSource(args) {
|
|
|
528
664
|
if (!confirmed) {
|
|
529
665
|
return;
|
|
530
666
|
}
|
|
531
|
-
const extensionVersionRef = `${extensionRef}@${
|
|
667
|
+
const extensionVersionRef = `${extensionRef}@${newVersion}`;
|
|
532
668
|
let packageUri;
|
|
533
669
|
let objectPath = "";
|
|
534
|
-
const uploadSpinner = ora("
|
|
670
|
+
const uploadSpinner = ora("Archiving and uploading extension source code...");
|
|
535
671
|
try {
|
|
536
672
|
uploadSpinner.start();
|
|
537
673
|
objectPath = await archiveAndUploadSource(args.rootDirectory, exports.EXTENSIONS_BUCKET_NAME);
|
|
538
|
-
uploadSpinner.succeed("
|
|
674
|
+
uploadSpinner.succeed("Uploaded extension source code");
|
|
539
675
|
packageUri = api_1.storageOrigin + objectPath + "?alt=media";
|
|
540
676
|
}
|
|
541
677
|
catch (err) {
|
|
542
678
|
uploadSpinner.fail();
|
|
543
|
-
throw new error_1.FirebaseError(`Failed to archive and upload extension source, ${err}`, {
|
|
679
|
+
throw new error_1.FirebaseError(`Failed to archive and upload extension source code, ${err}`, {
|
|
544
680
|
original: err,
|
|
545
681
|
});
|
|
546
682
|
}
|
|
547
|
-
const publishSpinner = ora(`
|
|
683
|
+
const publishSpinner = ora(`Uploading ${clc.bold(extensionVersionRef)}...`);
|
|
548
684
|
let res;
|
|
549
685
|
try {
|
|
550
686
|
publishSpinner.start();
|
|
551
|
-
res = await (0,
|
|
552
|
-
publishSpinner.succeed(`
|
|
687
|
+
res = await (0, publisherApi_1.createExtensionVersionFromLocalSource)({ extensionVersionRef, packageUri });
|
|
688
|
+
publishSpinner.succeed(`Successfully uploaded ${clc.bold(extensionVersionRef)}`);
|
|
553
689
|
}
|
|
554
690
|
catch (err) {
|
|
555
691
|
publishSpinner.fail();
|
|
@@ -561,10 +697,11 @@ async function publishExtensionVersionFromLocalSource(args) {
|
|
|
561
697
|
await deleteUploadedSource(objectPath);
|
|
562
698
|
return res;
|
|
563
699
|
}
|
|
564
|
-
exports.
|
|
700
|
+
exports.uploadExtensionVersionFromLocalSource = uploadExtensionVersionFromLocalSource;
|
|
565
701
|
function getMissingPublisherError(publisherId) {
|
|
566
|
-
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.
|
|
702
|
+
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. For step-by-step instructions on getting started as a publisher, see https://firebase.google.com/docs/extensions/publishers/get-started.`));
|
|
567
703
|
}
|
|
704
|
+
exports.getMissingPublisherError = getMissingPublisherError;
|
|
568
705
|
async function createSourceFromLocation(projectId, sourceUri) {
|
|
569
706
|
const extensionRoot = "/";
|
|
570
707
|
let packageUri;
|
|
@@ -608,18 +745,18 @@ function getPublisherProjectFromName(publisherName) {
|
|
|
608
745
|
throw new error_1.FirebaseError(`Could not find publisher with name '${publisherName}'.`);
|
|
609
746
|
}
|
|
610
747
|
exports.getPublisherProjectFromName = getPublisherProjectFromName;
|
|
611
|
-
function displayReleaseNotes(
|
|
612
|
-
const source = sourceUri || "
|
|
613
|
-
const releaseNotesMessage = releaseNotes
|
|
614
|
-
? `${clc.bold("Release notes:")}\n${(0, marked_1.marked)(releaseNotes)}`
|
|
615
|
-
: "";
|
|
616
|
-
const metadataMessage = `${clc.bold("Extension:")} ${extensionRef}\n` +
|
|
617
|
-
`${clc.bold("Version:")} ${clc.bold(clc.green(
|
|
748
|
+
function displayReleaseNotes(args) {
|
|
749
|
+
const source = args.sourceUri || "Local source";
|
|
750
|
+
const releaseNotesMessage = args.releaseNotes
|
|
751
|
+
? `${clc.bold("Release notes:")}\n${(0, marked_1.marked)(args.releaseNotes)}`
|
|
752
|
+
: "\n";
|
|
753
|
+
const metadataMessage = `${clc.bold("Extension:")} ${args.extensionRef}\n` +
|
|
754
|
+
`${clc.bold("Version:")} ${clc.bold(clc.green(args.newVersion))} ${args.autoReview ? "(automatically sent for review)" : ""}\n` +
|
|
618
755
|
`${clc.bold("Source:")} ${source}\n`;
|
|
619
|
-
const message = `\nYou are about to
|
|
756
|
+
const message = `\nYou are about to upload a new version to Firebase's registry of extensions.\n\n` +
|
|
620
757
|
metadataMessage +
|
|
621
758
|
releaseNotesMessage +
|
|
622
|
-
|
|
759
|
+
`Once an extension version is uploaded, it becomes installable by other users and cannot be changed. If you wish to make changes after uploading, you will need to upload a new version.\n`;
|
|
623
760
|
logger_1.logger.info(message);
|
|
624
761
|
}
|
|
625
762
|
exports.displayReleaseNotes = displayReleaseNotes;
|
|
@@ -727,7 +864,7 @@ async function canonicalizeRefInput(refInput) {
|
|
|
727
864
|
inferredRef = `firebase/${inferredRef}`;
|
|
728
865
|
}
|
|
729
866
|
if (refInput.split("@").length < 2) {
|
|
730
|
-
inferredRef = `${inferredRef}@latest`;
|
|
867
|
+
inferredRef = `${inferredRef}@latest-approved`;
|
|
731
868
|
}
|
|
732
869
|
const ref = refs.parse(inferredRef);
|
|
733
870
|
ref.version = await (0, planner_1.resolveVersion)(ref);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.readInstanceParam = exports.writeExtensionsToFirebaseJson = exports.getInstanceRef = exports.getInstanceTarget = exports.instanceExists = exports.loadConfig = exports.removeFromManifest = exports.writeLocalSecrets = exports.writeEmptyManifest = exports.writeToManifest = exports.ENV_DIRECTORY = void 0;
|
|
4
4
|
const clc = require("colorette");
|
|
5
5
|
const path = require("path");
|
|
6
6
|
const fs = require("fs-extra");
|
|
@@ -203,10 +203,3 @@ function readParamsFile(projectDir, fileName) {
|
|
|
203
203
|
const params = (0, paramHelper_1.readEnvFile)(paramPath);
|
|
204
204
|
return params;
|
|
205
205
|
}
|
|
206
|
-
function showPostDeprecationNotice() {
|
|
207
|
-
utils.logLabeledBullet(extensionsHelper_1.logPrefix, "The behavior of ext:install, ext:update, ext:configure, and ext:uninstall has changed in firebase-tools@11.0.0. " +
|
|
208
|
-
"Instead of deploying extensions directly, " +
|
|
209
|
-
"changes to extension instances will be written to firebase.json and ./extensions/*.env. " +
|
|
210
|
-
`Then ${clc.bold("firebase deploy (--only extensions)")} will deploy the changes to your Firebase project. See https://firebase.google.com/docs/extensions/manifest for more details.`);
|
|
211
|
-
}
|
|
212
|
-
exports.showPostDeprecationNotice = showPostDeprecationNotice;
|