diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d7fdea108b..f031671c3f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,3 +3,4 @@ - Added emulator support for SDK defined extensions. - Fixed various trigger handling issues in the Functions emualtor, including an issue where Eventarc functions would not be emulated correctly after a reload. - Added support for generating Dart SDKs for Data Connect connectors. +- Commands now correctly default to 'default' alias when there is more than one alias listed. (#7624) diff --git a/src/command.spec.ts b/src/command.spec.ts index 465750d8180..a1dee5228f7 100644 --- a/src/command.spec.ts +++ b/src/command.spec.ts @@ -1,4 +1,6 @@ import { expect } from "chai"; +import * as sinon from "sinon"; +import * as rc from "./rc"; import * as nock from "nock"; import { Command, validateProjectId } from "./command"; @@ -30,6 +32,18 @@ describe("Command", () => { }); describe("runner", () => { + let rcStub: sinon.SinonStub; + beforeEach(() => { + rcStub = sinon + .stub(rc, "loadRC") + .returns(new rc.RC(undefined, { projects: { default: "default-project" } })); + }); + + afterEach(() => { + rcStub.restore(); + nock.cleanAll(); + }); + it("should work when no arguments are passed and options", async () => { const run = command .action((options) => { @@ -126,6 +140,25 @@ describe("Command", () => { project: "resolved-project", }); }); + + it("should use the 'default' alias if no project is passed", async () => { + const run = command + .action((options) => { + return { + project: options.project, + projectNumber: options.projectNumber, + projectId: options.projectId, + }; + }) + .runner(); + + const result = await run({}); + expect(result).to.deep.eq({ + projectId: "default-project", + projectNumber: undefined, + project: "default-project", + }); + }); }); }); diff --git a/src/command.ts b/src/command.ts index 7d400377367..1886c9faea6 100644 --- a/src/command.ts +++ b/src/command.ts @@ -1,6 +1,6 @@ import * as clc from "colorette"; import { CommanderStatic } from "commander"; -import { first, last, get, size, head, keys, values } from "lodash"; +import { first, last, size, head, keys, values } from "lodash"; import { FirebaseError } from "./error"; import { getInheritedOption, setupLoggers, withTimeout } from "./utils"; @@ -12,6 +12,7 @@ import { trackEmulator, trackGA4 } from "./track"; import { selectAccount, setActiveAccount } from "./auth"; import { getFirebaseProject } from "./management/projects"; import { requireAuth } from "./requireAuth"; +import { Options } from "./options"; // eslint-disable-next-line @typescript-eslint/no-explicit-any type ActionFunction = (...args: any[]) => any; @@ -334,25 +335,33 @@ export class Command { * @param options the command options object. */ // eslint-disable-next-line @typescript-eslint/no-explicit-any - private applyRC(options: any): void { + private applyRC(options: Options): void { const rc = loadRC(options); options.rc = rc; - - options.project = - options.project || (configstore.get("activeProjects") || {})[options.projectRoot]; + const activeProject = options.projectRoot + ? (configstore.get("activeProjects") ?? {})[options.projectRoot] + : undefined; + options.project = options.project ?? activeProject; // support deprecated "firebase" key in firebase.json if (options.config && !options.project) { options.project = options.config.defaults.project; } const aliases = rc.projects; - const rcProject = get(aliases, options.project); + const rcProject = options.project ? aliases[options.project] : undefined; if (rcProject) { + // Look up aliases options.projectAlias = options.project; options.project = rcProject; } else if (!options.project && size(aliases) === 1) { + // If there's only a single alias, use that. + // This seems to be how we originally implemented default project - keeping this behavior to avoid breaking any unusual set ups. options.projectAlias = head(keys(aliases)); options.project = head(values(aliases)); + } else if (!options.project && aliases["default"]) { + // If there's an alias named 'default', default to that. + options.projectAlias = "default"; + options.project = aliases["default"]; } }