From f95a6ffd9e6b565a974650e54eb36a4c2f4509eb Mon Sep 17 00:00:00 2001 From: Joe Hanley Date: Mon, 18 Nov 2024 11:14:21 -0800 Subject: [PATCH 1/7] Added alias support and alias appgistribution:groups:* --- CHANGELOG.md | 1 + npm-shrinkwrap.json | 14 +++++++------- package.json | 2 +- src/bin/firebase.ts | 4 ++-- src/command.spec.ts | 1 + src/command.ts | 14 ++++++++++++++ src/commands/appdistribution-group-create.ts | 1 + src/commands/appdistribution-group-delete.ts | 1 + src/commands/appdistribution-group-list.ts | 1 + 9 files changed, 29 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8372252acd5..e7e85d8d576 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,3 +2,4 @@ - Moved firebase-tools-ui server.js logic to fireabse-tools to run it in-memory. (#7897) - Updates `superstatic` to `9.1.0` (#7929). - Added the appdistribution:group:list and appdistribution:testers:list commands. +- Aliased `appdistribution:group:*` commands to `appdistribution:groups:*`. diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 1dfe3a0f2f2..45265a3a346 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -21,7 +21,7 @@ "cjson": "^0.3.1", "cli-table": "0.3.11", "colorette": "^2.0.19", - "commander": "^4.0.1", + "commander": "^5.0.0", "configstore": "^5.0.1", "cors": "^2.8.5", "cross-env": "^5.1.3", @@ -7190,9 +7190,9 @@ } }, "node_modules/commander": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.0.1.tgz", - "integrity": "sha512-IPF4ouhCP+qdlcmCedhxX4xiGBPyigb8v5NeUp+0LyhwLgxMqyp3S0vl7TAPfS/hiP7FC3caI/PB9lTmP8r1NA==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", "engines": { "node": ">= 6" } @@ -26420,9 +26420,9 @@ "dev": true }, "commander": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-4.0.1.tgz", - "integrity": "sha512-IPF4ouhCP+qdlcmCedhxX4xiGBPyigb8v5NeUp+0LyhwLgxMqyp3S0vl7TAPfS/hiP7FC3caI/PB9lTmP8r1NA==" + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==" }, "comment-parser": { "version": "1.4.1", diff --git a/package.json b/package.json index e8844ccf3a1..022c9906e4d 100644 --- a/package.json +++ b/package.json @@ -110,7 +110,7 @@ "cjson": "^0.3.1", "cli-table": "0.3.11", "colorette": "^2.0.19", - "commander": "^4.0.1", + "commander": "^5.1.0", "configstore": "^5.0.1", "cors": "^2.8.5", "cross-env": "^5.1.3", diff --git a/src/bin/firebase.ts b/src/bin/firebase.ts index 205341bb084..631eb54ee73 100755 --- a/src/bin/firebase.ts +++ b/src/bin/firebase.ts @@ -18,7 +18,7 @@ const updateNotifier = updateNotifierPkg({ pkg }); import { marked } from "marked"; marked.use(markedTerminal() as any); -import { Command } from "commander"; +import { CommanderStatic } from "commander"; import { join } from "node:path"; import { SPLAT } from "triple-beam"; import { stripVTControlCharacters } from "node:util"; @@ -34,7 +34,7 @@ import * as utils from "../utils"; import * as winston from "winston"; let args = process.argv.slice(2); -let cmd: Command; +let cmd: CommanderStatic; function findAvailableLogFile(): string { const candidates = ["firebase-debug.log"]; diff --git a/src/command.spec.ts b/src/command.spec.ts index 5cb2054c64e..8ba2d7b8d08 100644 --- a/src/command.spec.ts +++ b/src/command.spec.ts @@ -24,6 +24,7 @@ describe("Command", () => { }, ["foo", "bar"], ); + command.alias("example2"); command.help("here's how!"); command.action(() => { // do nothing diff --git a/src/command.ts b/src/command.ts index 918d9072401..68b045e5f3e 100644 --- a/src/command.ts +++ b/src/command.ts @@ -37,6 +37,7 @@ export class Command { private descriptionText = ""; // eslint-disable-next-line @typescript-eslint/no-explicit-any private options: any[][] = []; + private aliases: string[] = []; private actionFn: ActionFunction = (): void => { // noop by default, unless overwritten by `.action(fn)`. }; @@ -62,6 +63,16 @@ export class Command { return this; } + /** + * Sets an alias for a command. + * @param aliases an alternativre name for the command. Users will be able to call the command via this name. + * @return the command, for chaining. + */ + alias(alias: string): Command { + this.aliases.push(alias); + return this; + } + /** * Sets any options for the command. * @@ -138,6 +149,9 @@ export class Command { if (this.descriptionText) { cmd.description(this.descriptionText); } + if (this.aliases) { + cmd.aliases(this.aliases); + } this.options.forEach((args) => { const flags = args.shift(); cmd.option(flags, ...args); diff --git a/src/commands/appdistribution-group-create.ts b/src/commands/appdistribution-group-create.ts index 493d32af836..63e5d33cfda 100644 --- a/src/commands/appdistribution-group-create.ts +++ b/src/commands/appdistribution-group-create.ts @@ -6,6 +6,7 @@ import { getProjectName } from "../appdistribution/options-parser-util"; export const command = new Command("appdistribution:group:create [alias]") .description("create group in project") + .alias("appdistribution:groups:create") .before(requireAuth) .action(async (displayName: string, alias?: string, options?: any) => { const projectName = await getProjectName(options); diff --git a/src/commands/appdistribution-group-delete.ts b/src/commands/appdistribution-group-delete.ts index 16651d711f8..4b2acca479c 100644 --- a/src/commands/appdistribution-group-delete.ts +++ b/src/commands/appdistribution-group-delete.ts @@ -7,6 +7,7 @@ import { getProjectName } from "../appdistribution/options-parser-util"; export const command = new Command("appdistribution:group:delete ") .description("delete group from a project") + .alias("appdistribution:groups:delete") .before(requireAuth) .action(async (alias: string, options: any) => { const projectName = await getProjectName(options); diff --git a/src/commands/appdistribution-group-list.ts b/src/commands/appdistribution-group-list.ts index 864dd3e1248..5eb63542559 100644 --- a/src/commands/appdistribution-group-list.ts +++ b/src/commands/appdistribution-group-list.ts @@ -13,6 +13,7 @@ const Table = require("cli-table"); export const command = new Command("appdistribution:group:list") .description("list groups in project") + .alias("appdistribution:groups:list") .before(requireAuth) .action(async (options?: Options): Promise => { const projectName = await getProjectName(options); From 34fd26e68a40e611e1a0a14369eff7c86dff13e6 Mon Sep 17 00:00:00 2001 From: Joe Hanley Date: Mon, 18 Nov 2024 11:45:37 -0800 Subject: [PATCH 2/7] fix shrinkwrap --- npm-shrinkwrap.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/npm-shrinkwrap.json b/npm-shrinkwrap.json index 45265a3a346..69eb36fa021 100644 --- a/npm-shrinkwrap.json +++ b/npm-shrinkwrap.json @@ -21,7 +21,7 @@ "cjson": "^0.3.1", "cli-table": "0.3.11", "colorette": "^2.0.19", - "commander": "^5.0.0", + "commander": "^5.1.0", "configstore": "^5.0.1", "cors": "^2.8.5", "cross-env": "^5.1.3", From 1b05206b82cf4ece25bd8e50ab917d13acd9685b Mon Sep 17 00:00:00 2001 From: Joe Hanley Date: Mon, 18 Nov 2024 11:48:03 -0800 Subject: [PATCH 3/7] Make groups command accessible as a node module --- src/commands/index.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/commands/index.ts b/src/commands/index.ts index 3829ce3cd87..30c36114df4 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -29,6 +29,10 @@ export function load(client: any): any { client.appdistribution.group.list = loadCommand("appdistribution-group-list"); client.appdistribution.group.create = loadCommand("appdistribution-group-create"); client.appdistribution.group.delete = loadCommand("appdistribution-group-delete"); + client.appdistribution.groups = {}; + client.appdistribution.groups.list = loadCommand("appdistribution-group-list"); + client.appdistribution.groups.create = loadCommand("appdistribution-group-create"); + client.appdistribution.groups.delete = loadCommand("appdistribution-group-delete"); client.apps = {}; client.apps.create = loadCommand("apps-create"); client.apps.list = loadCommand("apps-list"); From af98a636fc1f8792a9d616941d55cc57b21e21f8 Mon Sep 17 00:00:00 2001 From: Joe Hanley Date: Mon, 18 Nov 2024 12:43:17 -0800 Subject: [PATCH 4/7] remove repeated entry so help test is not repetitive --- src/commands/index.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/commands/index.ts b/src/commands/index.ts index 30c36114df4..3829ce3cd87 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -29,10 +29,6 @@ export function load(client: any): any { client.appdistribution.group.list = loadCommand("appdistribution-group-list"); client.appdistribution.group.create = loadCommand("appdistribution-group-create"); client.appdistribution.group.delete = loadCommand("appdistribution-group-delete"); - client.appdistribution.groups = {}; - client.appdistribution.groups.list = loadCommand("appdistribution-group-list"); - client.appdistribution.groups.create = loadCommand("appdistribution-group-create"); - client.appdistribution.groups.delete = loadCommand("appdistribution-group-delete"); client.apps = {}; client.apps.create = loadCommand("apps-create"); client.apps.list = loadCommand("apps-list"); From 8596b3b2aab2b9bf27eb3bb022c7ee0e6eef43b2 Mon Sep 17 00:00:00 2001 From: Joe Hanley Date: Mon, 18 Nov 2024 12:46:01 -0800 Subject: [PATCH 5/7] Swap primary and alias --- src/commands/appdistribution-group-create.ts | 4 ++-- src/commands/appdistribution-group-delete.ts | 4 ++-- src/commands/appdistribution-group-list.ts | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/commands/appdistribution-group-create.ts b/src/commands/appdistribution-group-create.ts index 63e5d33cfda..a12c809dd3c 100644 --- a/src/commands/appdistribution-group-create.ts +++ b/src/commands/appdistribution-group-create.ts @@ -4,9 +4,9 @@ import { requireAuth } from "../requireAuth"; import { AppDistributionClient } from "../appdistribution/client"; import { getProjectName } from "../appdistribution/options-parser-util"; -export const command = new Command("appdistribution:group:create [alias]") +export const command = new Command("appdistribution:groups:create [alias]") .description("create group in project") - .alias("appdistribution:groups:create") + .alias("appdistribution:group:create") .before(requireAuth) .action(async (displayName: string, alias?: string, options?: any) => { const projectName = await getProjectName(options); diff --git a/src/commands/appdistribution-group-delete.ts b/src/commands/appdistribution-group-delete.ts index 4b2acca479c..5ec20d4afd5 100644 --- a/src/commands/appdistribution-group-delete.ts +++ b/src/commands/appdistribution-group-delete.ts @@ -5,9 +5,9 @@ import { FirebaseError } from "../error"; import { AppDistributionClient } from "../appdistribution/client"; import { getProjectName } from "../appdistribution/options-parser-util"; -export const command = new Command("appdistribution:group:delete ") +export const command = new Command("appdistribution:groups:delete ") .description("delete group from a project") - .alias("appdistribution:groups:delete") + .alias("appdistribution:group:delete") .before(requireAuth) .action(async (alias: string, options: any) => { const projectName = await getProjectName(options); diff --git a/src/commands/appdistribution-group-list.ts b/src/commands/appdistribution-group-list.ts index 5eb63542559..3637cff986d 100644 --- a/src/commands/appdistribution-group-list.ts +++ b/src/commands/appdistribution-group-list.ts @@ -11,9 +11,9 @@ import * as utils from "../utils"; const Table = require("cli-table"); -export const command = new Command("appdistribution:group:list") +export const command = new Command("appdistribution:groups:list") .description("list groups in project") - .alias("appdistribution:groups:list") + .alias("appdistribution:group:list") .before(requireAuth) .action(async (options?: Options): Promise => { const projectName = await getProjectName(options); From 22fe63c62f096ee8972eb7f17333bd364c954887 Mon Sep 17 00:00:00 2001 From: Joe Hanley Date: Mon, 18 Nov 2024 13:43:26 -0800 Subject: [PATCH 6/7] rename files, better approach for module --- src/commands/appdistribution-group-create.ts | 18 ------ src/commands/appdistribution-group-delete.ts | 22 ------- src/commands/appdistribution-group-list.ts | 62 -------------------- src/commands/index.ts | 7 ++- 4 files changed, 4 insertions(+), 105 deletions(-) delete mode 100644 src/commands/appdistribution-group-create.ts delete mode 100644 src/commands/appdistribution-group-delete.ts delete mode 100644 src/commands/appdistribution-group-list.ts diff --git a/src/commands/appdistribution-group-create.ts b/src/commands/appdistribution-group-create.ts deleted file mode 100644 index a12c809dd3c..00000000000 --- a/src/commands/appdistribution-group-create.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { Command } from "../command"; -import * as utils from "../utils"; -import { requireAuth } from "../requireAuth"; -import { AppDistributionClient } from "../appdistribution/client"; -import { getProjectName } from "../appdistribution/options-parser-util"; - -export const command = new Command("appdistribution:groups:create [alias]") - .description("create group in project") - .alias("appdistribution:group:create") - .before(requireAuth) - .action(async (displayName: string, alias?: string, options?: any) => { - const projectName = await getProjectName(options); - const appDistroClient = new AppDistributionClient(); - utils.logBullet(`Creating group in project`); - const group = await appDistroClient.createGroup(projectName, displayName, alias); - alias = group.name.split("/").pop(); - utils.logSuccess(`Group '${group.displayName}' (alias: ${alias}) created successfully`); - }); diff --git a/src/commands/appdistribution-group-delete.ts b/src/commands/appdistribution-group-delete.ts deleted file mode 100644 index 5ec20d4afd5..00000000000 --- a/src/commands/appdistribution-group-delete.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { Command } from "../command"; -import * as utils from "../utils"; -import { requireAuth } from "../requireAuth"; -import { FirebaseError } from "../error"; -import { AppDistributionClient } from "../appdistribution/client"; -import { getProjectName } from "../appdistribution/options-parser-util"; - -export const command = new Command("appdistribution:groups:delete ") - .description("delete group from a project") - .alias("appdistribution:group:delete") - .before(requireAuth) - .action(async (alias: string, options: any) => { - const projectName = await getProjectName(options); - const appDistroClient = new AppDistributionClient(); - try { - utils.logBullet(`Deleting group from project`); - await appDistroClient.deleteGroup(`${projectName}/groups/${alias}`); - } catch (err: any) { - throw new FirebaseError(`Failed to delete group ${err}`); - } - utils.logSuccess(`Group ${alias} has successfully been deleted`); - }); diff --git a/src/commands/appdistribution-group-list.ts b/src/commands/appdistribution-group-list.ts deleted file mode 100644 index 3637cff986d..00000000000 --- a/src/commands/appdistribution-group-list.ts +++ /dev/null @@ -1,62 +0,0 @@ -import * as ora from "ora"; -import { AppDistributionClient } from "../appdistribution/client"; -import { getProjectName } from "../appdistribution/options-parser-util"; -import { Group, ListGroupsResponse } from "../appdistribution/types"; -import { Command } from "../command"; -import { FirebaseError } from "../error"; -import { logger } from "../logger"; -import { Options } from "../options"; -import { requireAuth } from "../requireAuth"; -import * as utils from "../utils"; - -const Table = require("cli-table"); - -export const command = new Command("appdistribution:groups:list") - .description("list groups in project") - .alias("appdistribution:group:list") - .before(requireAuth) - .action(async (options?: Options): Promise => { - const projectName = await getProjectName(options); - const appDistroClient = new AppDistributionClient(); - let groupsResponse: ListGroupsResponse; - const spinner = ora("Preparing the list of your App Distribution Groups").start(); - try { - groupsResponse = await appDistroClient.listGroups(projectName); - } catch (err: any) { - spinner.fail(); - throw new FirebaseError("Failed to list groups.", { - exit: 1, - original: err, - }); - } - spinner.succeed(); - const groups = groupsResponse.groups ?? []; - printGroupsTable(groups); - utils.logSuccess(`Groups listed successfully`); - return groupsResponse; - }); - -/** - * Prints a table given a list of groups - */ -function printGroupsTable(groups: Group[]): void { - const tableHead = ["Group", "Display Name", "Tester Count", "Release Count", "Invite Link Count"]; - - const table = new Table({ - head: tableHead, - style: { head: ["green"] }, - }); - - for (const group of groups) { - const name = group.name.split("/").pop(); - table.push([ - name, - group.displayName, - group.testerCount || 0, - group.releaseCount || 0, - group.inviteLinkCount || 0, - ]); - } - - logger.info(table.toString()); -} diff --git a/src/commands/index.ts b/src/commands/index.ts index 3829ce3cd87..7ce27a3313e 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -26,9 +26,10 @@ export function load(client: any): any { client.appdistribution.testers.add = loadCommand("appdistribution-testers-add"); client.appdistribution.testers.delete = loadCommand("appdistribution-testers-remove"); client.appdistribution.group = {}; - client.appdistribution.group.list = loadCommand("appdistribution-group-list"); - client.appdistribution.group.create = loadCommand("appdistribution-group-create"); - client.appdistribution.group.delete = loadCommand("appdistribution-group-delete"); + client.appdistribution.group.list = loadCommand("appdistribution-groups-list"); + client.appdistribution.group.create = loadCommand("appdistribution-groups-create"); + client.appdistribution.group.delete = loadCommand("appdistribution-groups-delete"); + client.appdistribution.groups = client.appdistribution.group; client.apps = {}; client.apps.create = loadCommand("apps-create"); client.apps.list = loadCommand("apps-list"); From ffc3573357b31a90b88c9d84ae4f278495da3bcc Mon Sep 17 00:00:00 2001 From: Joe Hanley Date: Tue, 19 Nov 2024 09:44:01 -0800 Subject: [PATCH 7/7] Actually track the renamed files --- src/commands/appdistribution-groups-create.ts | 18 ++++++ src/commands/appdistribution-groups-delete.ts | 22 +++++++ src/commands/appdistribution-groups-list.ts | 62 +++++++++++++++++++ 3 files changed, 102 insertions(+) create mode 100644 src/commands/appdistribution-groups-create.ts create mode 100644 src/commands/appdistribution-groups-delete.ts create mode 100644 src/commands/appdistribution-groups-list.ts diff --git a/src/commands/appdistribution-groups-create.ts b/src/commands/appdistribution-groups-create.ts new file mode 100644 index 00000000000..a12c809dd3c --- /dev/null +++ b/src/commands/appdistribution-groups-create.ts @@ -0,0 +1,18 @@ +import { Command } from "../command"; +import * as utils from "../utils"; +import { requireAuth } from "../requireAuth"; +import { AppDistributionClient } from "../appdistribution/client"; +import { getProjectName } from "../appdistribution/options-parser-util"; + +export const command = new Command("appdistribution:groups:create [alias]") + .description("create group in project") + .alias("appdistribution:group:create") + .before(requireAuth) + .action(async (displayName: string, alias?: string, options?: any) => { + const projectName = await getProjectName(options); + const appDistroClient = new AppDistributionClient(); + utils.logBullet(`Creating group in project`); + const group = await appDistroClient.createGroup(projectName, displayName, alias); + alias = group.name.split("/").pop(); + utils.logSuccess(`Group '${group.displayName}' (alias: ${alias}) created successfully`); + }); diff --git a/src/commands/appdistribution-groups-delete.ts b/src/commands/appdistribution-groups-delete.ts new file mode 100644 index 00000000000..5ec20d4afd5 --- /dev/null +++ b/src/commands/appdistribution-groups-delete.ts @@ -0,0 +1,22 @@ +import { Command } from "../command"; +import * as utils from "../utils"; +import { requireAuth } from "../requireAuth"; +import { FirebaseError } from "../error"; +import { AppDistributionClient } from "../appdistribution/client"; +import { getProjectName } from "../appdistribution/options-parser-util"; + +export const command = new Command("appdistribution:groups:delete ") + .description("delete group from a project") + .alias("appdistribution:group:delete") + .before(requireAuth) + .action(async (alias: string, options: any) => { + const projectName = await getProjectName(options); + const appDistroClient = new AppDistributionClient(); + try { + utils.logBullet(`Deleting group from project`); + await appDistroClient.deleteGroup(`${projectName}/groups/${alias}`); + } catch (err: any) { + throw new FirebaseError(`Failed to delete group ${err}`); + } + utils.logSuccess(`Group ${alias} has successfully been deleted`); + }); diff --git a/src/commands/appdistribution-groups-list.ts b/src/commands/appdistribution-groups-list.ts new file mode 100644 index 00000000000..3637cff986d --- /dev/null +++ b/src/commands/appdistribution-groups-list.ts @@ -0,0 +1,62 @@ +import * as ora from "ora"; +import { AppDistributionClient } from "../appdistribution/client"; +import { getProjectName } from "../appdistribution/options-parser-util"; +import { Group, ListGroupsResponse } from "../appdistribution/types"; +import { Command } from "../command"; +import { FirebaseError } from "../error"; +import { logger } from "../logger"; +import { Options } from "../options"; +import { requireAuth } from "../requireAuth"; +import * as utils from "../utils"; + +const Table = require("cli-table"); + +export const command = new Command("appdistribution:groups:list") + .description("list groups in project") + .alias("appdistribution:group:list") + .before(requireAuth) + .action(async (options?: Options): Promise => { + const projectName = await getProjectName(options); + const appDistroClient = new AppDistributionClient(); + let groupsResponse: ListGroupsResponse; + const spinner = ora("Preparing the list of your App Distribution Groups").start(); + try { + groupsResponse = await appDistroClient.listGroups(projectName); + } catch (err: any) { + spinner.fail(); + throw new FirebaseError("Failed to list groups.", { + exit: 1, + original: err, + }); + } + spinner.succeed(); + const groups = groupsResponse.groups ?? []; + printGroupsTable(groups); + utils.logSuccess(`Groups listed successfully`); + return groupsResponse; + }); + +/** + * Prints a table given a list of groups + */ +function printGroupsTable(groups: Group[]): void { + const tableHead = ["Group", "Display Name", "Tester Count", "Release Count", "Invite Link Count"]; + + const table = new Table({ + head: tableHead, + style: { head: ["green"] }, + }); + + for (const group of groups) { + const name = group.name.split("/").pop(); + table.push([ + name, + group.displayName, + group.testerCount || 0, + group.releaseCount || 0, + group.inviteLinkCount || 0, + ]); + } + + logger.info(table.toString()); +}