From 6d5d1761c3b2ac155f30e87be6e844e77a91bc24 Mon Sep 17 00:00:00 2001
From: Joanna Wang <annajowang@gmail.com>
Date: Tue, 11 Feb 2025 01:25:33 +0000
Subject: [PATCH 1/2] Implement getBackend to auto-pick backends with duplicate
 names.

---
 src/apphosting/backend.ts | 50 ++++++++++++++++++++++++++++++++++++++-
 src/apphosting/rollout.ts |  5 ++--
 2 files changed, 51 insertions(+), 4 deletions(-)

diff --git a/src/apphosting/backend.ts b/src/apphosting/backend.ts
index 8a5aa579901..285b141eac4 100644
--- a/src/apphosting/backend.ts
+++ b/src/apphosting/backend.ts
@@ -17,7 +17,7 @@ import { Backend, BackendOutputOnlyFields, API_VERSION } from "../gcp/apphosting
 import { addServiceAccountToRoles } from "../gcp/resourceManager";
 import * as iam from "../gcp/iam";
 import { FirebaseError, getErrStatus, getError } from "../error";
-import { promptOnce } from "../prompt";
+import { promptOnce, confirm } from "../prompt";
 import { DEFAULT_LOCATION } from "./constants";
 import { ensure } from "../ensureApiEnabled";
 import * as deploymentTool from "../deploymentTool";
@@ -474,3 +474,51 @@ export async function getBackendForAmbiguousLocation(
   });
   return backendsByLocation.get(location)!;
 }
+
+/**
+ * Fetches a backend from the server. If there are multiple backends with the name, it will fetch the first
+ * in the list and warn the user that there are other backends with the same name that need to be deleted. If
+ * the force option is specified nad multiple backends have the same name, it throws an error.
+ */
+export async function getBackend(
+  projectId: string,
+  backendId: string,
+  force?: boolean,
+): Promise<apphosting.Backend> {
+  // TODO: call apphosting.getBackend() once all duplicate named backends have been deleted.
+  let { unreachable, backends } = await apphosting.listBackends(projectId, "-");
+  backends = backends.filter(
+    (backend) => apphosting.parseBackendName(backend.name).id === backendId,
+  );
+  if (backends.length > 0) {
+    if (backends.length > 1) {
+      logWarning(
+	`You have multiple backends with the same ${backendId} ID. This is no longer supported. ` +
+	  "Please delete and recreate any backends that share an ID with another backend."
+      )
+      if (force) {
+	throw new FirebaseError(
+	  `Force cannot be used when multiple backends share the same ID`,
+	);
+      };
+      const options = {force: force,  nonInteractive: false };
+      const confirmed = await confirm({
+	...options,
+	message: `Using backend ${backends[0].name} continue?`,
+      });
+      if (!confirmed) {
+	throw new FirebaseError("Command aborted.");
+      }
+    }
+    return backends[0];
+  }
+  if (unreachable && unreachable.length !== 0) {
+    logWarning(
+      `Backends with the following primary regions are unreachable: ${unreachable}.\n` +
+	"If your backend is in one of these regions, please try again later."
+    );
+  };
+  throw new FirebaseError(
+    `No backend named ${backendId} found.`,
+  );
+}
diff --git a/src/apphosting/rollout.ts b/src/apphosting/rollout.ts
index 32b2bcb8d4e..0a6dbc78b25 100644
--- a/src/apphosting/rollout.ts
+++ b/src/apphosting/rollout.ts
@@ -13,7 +13,7 @@ import * as poller from "../operation-poller";
 import { logBullet, sleep } from "../utils";
 import { apphostingOrigin, consoleOrigin } from "../api";
 import { DeepOmit } from "../metaprogramming";
-import { getBackendForAmbiguousLocation, getBackendForLocation } from "./backend";
+import { getBackendForAmbiguousLocation, getBackendForLocation, getBackend } from "./backend";
 
 const apphostingPollerOptions: Omit<poller.OperationPollerOptions, "operationResourceName"> = {
   apiOrigin: apphostingOrigin(),
@@ -38,10 +38,9 @@ export async function createRollout(
 ): Promise<void> {
   let backend: apphosting.Backend;
   if (location === "-" || location === "") {
-    backend = await getBackendForAmbiguousLocation(
+    backend = await getBackend(
       projectId,
       backendId,
-      "Please select the location of the backend you'd like to roll out:",
       force,
     );
     location = apphosting.parseBackendName(backend.name).location;

From 06eb2c74a50a41e86fd6e032f85f8b0122c411bb Mon Sep 17 00:00:00 2001
From: Joanna Wang <annajowang@gmail.com>
Date: Tue, 11 Feb 2025 01:49:27 +0000
Subject: [PATCH 2/2] Remove rollout.ts change

---
 src/apphosting/rollout.ts | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/apphosting/rollout.ts b/src/apphosting/rollout.ts
index 0a6dbc78b25..32b2bcb8d4e 100644
--- a/src/apphosting/rollout.ts
+++ b/src/apphosting/rollout.ts
@@ -13,7 +13,7 @@ import * as poller from "../operation-poller";
 import { logBullet, sleep } from "../utils";
 import { apphostingOrigin, consoleOrigin } from "../api";
 import { DeepOmit } from "../metaprogramming";
-import { getBackendForAmbiguousLocation, getBackendForLocation, getBackend } from "./backend";
+import { getBackendForAmbiguousLocation, getBackendForLocation } from "./backend";
 
 const apphostingPollerOptions: Omit<poller.OperationPollerOptions, "operationResourceName"> = {
   apiOrigin: apphostingOrigin(),
@@ -38,9 +38,10 @@ export async function createRollout(
 ): Promise<void> {
   let backend: apphosting.Backend;
   if (location === "-" || location === "") {
-    backend = await getBackend(
+    backend = await getBackendForAmbiguousLocation(
       projectId,
       backendId,
+      "Please select the location of the backend you'd like to roll out:",
       force,
     );
     location = apphosting.parseBackendName(backend.name).location;