From 14a90f8ff9824982e1ae7971dd3fe77682a8f1cf Mon Sep 17 00:00:00 2001 From: Brian Li Date: Tue, 22 Oct 2024 14:10:50 -0400 Subject: [PATCH 1/7] lookup backends in other regions --- src/apphosting/index.ts | 17 +++++++++++++++++ src/apphosting/rollout.ts | 14 +++++++++++++- src/commands/apphosting-backends-delete.ts | 20 +++++--------------- src/commands/apphosting-rollouts-create.ts | 2 +- 4 files changed, 36 insertions(+), 17 deletions(-) diff --git a/src/apphosting/index.ts b/src/apphosting/index.ts index ccda072452b..5fb6c597bcf 100644 --- a/src/apphosting/index.ts +++ b/src/apphosting/index.ts @@ -388,6 +388,23 @@ export async function promptLocation( return location; } +/** + * Fetches a backend from the server in the specified region (location). + */ +export async function getBackendForLocation( + projectId: string, + location: string, + backendId: string, +): Promise { + try { + return await apphosting.getBackend(projectId, location, backendId); + } catch (err: any) { + throw new FirebaseError(`No backend named "${backendId}" found in ${location}.`, { + original: err, + }); + } +} + /** * Fetches a backend from the server. If there are multiple backends with that name (ie multi-regional backends), * prompts the user to disambiguate. diff --git a/src/apphosting/rollout.ts b/src/apphosting/rollout.ts index e1f175adc19..e3011338cb6 100644 --- a/src/apphosting/rollout.ts +++ b/src/apphosting/rollout.ts @@ -14,6 +14,7 @@ import { confirm } from "../prompt"; import { logBullet, sleep } from "../utils"; import { apphostingOrigin, consoleOrigin } from "../api"; import { DeepOmit } from "../metaprogramming"; +import { getBackendForAmbiguousLocation, getBackendForLocation } from "."; const apphostingPollerOptions: Omit = { apiOrigin: apphostingOrigin(), @@ -36,7 +37,18 @@ export async function createRollout( commit?: string, force?: boolean, ): Promise { - const backend = await apphosting.getBackend(projectId, location, backendId); + let backend: apphosting.Backend; + if (location === "-" || location === "") { + backend = await getBackendForAmbiguousLocation( + projectId, + backendId, + "Please select the location of the backend you'd like to roll out:", + ); + location = apphosting.parseBackendName(backend.name).location; + } else { + backend = await getBackendForLocation(projectId, location, backendId); + } + if (!backend.codebase.repository) { throw new FirebaseError( `Backend ${backendId} is misconfigured due to missing a connected repository. You can delete and recreate your backend using 'firebase apphosting:backends:delete' and 'firebase apphosting:backends:create'.`, diff --git a/src/commands/apphosting-backends-delete.ts b/src/commands/apphosting-backends-delete.ts index d54e4f6192b..34f57308f75 100644 --- a/src/commands/apphosting-backends-delete.ts +++ b/src/commands/apphosting-backends-delete.ts @@ -6,7 +6,11 @@ import { promptOnce } from "../prompt"; import * as utils from "../utils"; import * as apphosting from "../gcp/apphosting"; import { printBackendsTable } from "./apphosting-backends-list"; -import { deleteBackendAndPoll, getBackendForAmbiguousLocation } from "../apphosting"; +import { + deleteBackendAndPoll, + getBackendForAmbiguousLocation, + getBackendForLocation, +} from "../apphosting"; import * as ora from "ora"; export const command = new Command("apphosting:backends:delete ") @@ -54,17 +58,3 @@ export const command = new Command("apphosting:backends:delete ") throw new FirebaseError(`Failed to delete backend: ${backendId}.`, { original: err }); } }); - -async function getBackendForLocation( - projectId: string, - location: string, - backendId: string, -): Promise { - try { - return await apphosting.getBackend(projectId, location, backendId); - } catch (err: any) { - throw new FirebaseError(`No backend named "${backendId}" found in ${location}.`, { - original: err, - }); - } -} diff --git a/src/commands/apphosting-rollouts-create.ts b/src/commands/apphosting-rollouts-create.ts index 82962568994..de259a9723b 100644 --- a/src/commands/apphosting-rollouts-create.ts +++ b/src/commands/apphosting-rollouts-create.ts @@ -7,7 +7,7 @@ import { createRollout } from "../apphosting/rollout"; export const command = new Command("apphosting:rollouts:create ") .description("create a rollout using a build for an App Hosting backend") - .option("-l, --location ", "specify the region of the backend", "us-central1") + .option("-l, --location ", "specify the region of the backend", "-") .option("-i, --id ", "id of the rollout (defaults to autogenerating a random id)", "") .option( "-gb, --git-branch ", From 3869b158ab0692fc3f12ca446eb6c385f1315fdd Mon Sep 17 00:00:00 2001 From: Brian Li Date: Thu, 24 Oct 2024 13:52:43 -0400 Subject: [PATCH 2/7] fix bug bash bugs --- .../{index.spec.ts => backend.spec.ts} | 2 +- src/apphosting/{index.ts => backend.ts} | 9 ++++- src/apphosting/githubConnections.spec.ts | 17 --------- src/apphosting/githubConnections.ts | 36 ++++++++++-------- src/apphosting/rollout.spec.ts | 37 +++++++++++++++---- src/apphosting/rollout.ts | 16 ++++---- src/commands/apphosting-backends-create.ts | 2 +- src/commands/apphosting-backends-delete.ts | 2 +- src/commands/apphosting-rollouts-create.ts | 7 ++-- .../apphosting-secrets-grantaccess.ts | 2 +- 10 files changed, 72 insertions(+), 58 deletions(-) rename src/apphosting/{index.spec.ts => backend.spec.ts} (99%) rename src/apphosting/{index.ts => backend.ts} (97%) diff --git a/src/apphosting/index.spec.ts b/src/apphosting/backend.spec.ts similarity index 99% rename from src/apphosting/index.spec.ts rename to src/apphosting/backend.spec.ts index b4d0c09d027..ddf5629d5c0 100644 --- a/src/apphosting/index.spec.ts +++ b/src/apphosting/backend.spec.ts @@ -13,7 +13,7 @@ import { setDefaultTrafficPolicy, ensureAppHostingComputeServiceAccount, getBackendForAmbiguousLocation, -} from "./index"; +} from "./backend"; import * as deploymentTool from "../deploymentTool"; import { FirebaseError } from "../error"; diff --git a/src/apphosting/index.ts b/src/apphosting/backend.ts similarity index 97% rename from src/apphosting/index.ts rename to src/apphosting/backend.ts index 5fb6c597bcf..b763dbfa499 100644 --- a/src/apphosting/index.ts +++ b/src/apphosting/backend.ts @@ -407,12 +407,14 @@ export async function getBackendForLocation( /** * Fetches a backend from the server. If there are multiple backends with that name (ie multi-regional backends), - * prompts the user to disambiguate. + * prompts the user to disambiguate. If the force option is specified and multiple backends have the same name, + * it throws an error. */ export async function getBackendForAmbiguousLocation( projectId: string, backendId: string, locationDisambugationPrompt: string, + force?: boolean, ): Promise { let { unreachable, backends } = await apphosting.listBackends(projectId, "-"); if (unreachable && unreachable.length !== 0) { @@ -430,6 +432,11 @@ export async function getBackendForAmbiguousLocation( if (backends.length === 1) { return backends[0]; } + if (force) { + throw new FirebaseError( + `Multiple backends found with ID ${backendId}. Please specify the region of your target backend.`, + ); + } const backendsByLocation = new Map(); backends.forEach((backend) => diff --git a/src/apphosting/githubConnections.spec.ts b/src/apphosting/githubConnections.spec.ts index 8c3adf68023..1ee472d0831 100644 --- a/src/apphosting/githubConnections.spec.ts +++ b/src/apphosting/githubConnections.spec.ts @@ -545,22 +545,5 @@ describe("githubConnections", () => { }; await expect(repo.promptGitHubBranch(testRepoLink)).to.eventually.equal("main"); }); - - it("re-prompts if user enters a branch that does not exist in given repo", async () => { - listAllBranchesStub.returns(new Set(["main", "test1"])); - - promptOnceStub.onFirstCall().returns("not-main"); - promptOnceStub.onSecondCall().returns("test1"); - const testRepoLink = { - name: "test", - cloneUri: "/test", - createTime: "", - updateTime: "", - deleteTime: "", - reconciling: false, - uid: "", - }; - await expect(repo.promptGitHubBranch(testRepoLink)).to.eventually.equal("test1"); - }); }); }); diff --git a/src/apphosting/githubConnections.ts b/src/apphosting/githubConnections.ts index b895ee0132c..78aef3c6d02 100644 --- a/src/apphosting/githubConnections.ts +++ b/src/apphosting/githubConnections.ts @@ -411,22 +411,28 @@ async function promptCloneUri( */ export async function promptGitHubBranch(repoLink: devConnect.GitRepositoryLink): Promise { const branches = await devConnect.listAllBranches(repoLink.name); - while (true) { - const branch = await promptOnce({ - name: "branch", - type: "input", - default: "main", - message: "Pick a branch for continuous deployment", - }); - - if (branches.has(branch)) { - return branch; - } + const branch = await promptOnce({ + type: "autocomplete", + name: "branch", + message: "Pick a branch for continuous deployment", + source: (_: any, input = ""): Promise<(inquirer.DistinctChoice | inquirer.Separator)[]> => { + return new Promise((resolve) => + resolve([ + ...fuzzy.filter(input, Array.from(branches)).map((result) => { + return { + name: result.original, + value: result.original, + }; + }), + ]), + ); + }, + }); - utils.logWarning( - `The branch "${branch}" does not exist on "${extractRepoSlugFromUri(repoLink.cloneUri)}". Please enter a valid branch for this repo.`, - ); - } + utils.logWarning( + `The branch "${branch}" does not exist on "${extractRepoSlugFromUri(repoLink.cloneUri) ?? ""}". Please enter a valid branch for this repo.`, + ); + return branch; } /** diff --git a/src/apphosting/rollout.spec.ts b/src/apphosting/rollout.spec.ts index 0794b3d5dcd..8b943d1bfb5 100644 --- a/src/apphosting/rollout.spec.ts +++ b/src/apphosting/rollout.spec.ts @@ -4,6 +4,7 @@ import { createRollout, orchestrateRollout } from "./rollout"; import * as devConnect from "../gcp/devConnect"; import * as githubConnections from "../apphosting/githubConnections"; import * as apphosting from "../gcp/apphosting"; +import * as backend from "./backend"; import { FirebaseError } from "../error"; import * as poller from "../operation-poller"; import * as utils from "../utils"; @@ -21,7 +22,8 @@ describe("apphosting rollouts", () => { const gitRepoLinkId = `${user}-${repo}`; const buildAndRolloutId = "build-2024-10-01-001"; - let getBackendStub: sinon.SinonStub; + let getBackendForLocationStub: sinon.SinonStub; + let getBackendForAmbiguousLocationStub: sinon.SinonStub; let getRepoDetailsFromBackendStub: sinon.SinonStub; let listAllBranchesStub: sinon.SinonStub; let getGitHubBranchStub: sinon.SinonStub; @@ -34,7 +36,12 @@ describe("apphosting rollouts", () => { let sleepStub: sinon.SinonStub; beforeEach(() => { - getBackendStub = sinon.stub(apphosting, "getBackend").throws("unexpected getBackend call"); + getBackendForLocationStub = sinon + .stub(backend, "getBackendForLocation") + .throws("unexpected getBackendForLocation call"); + getBackendForAmbiguousLocationStub = sinon + .stub(backend, "getBackendForAmbiguousLocation") + .throws("unexpected getBackendForAmbiguousLocation call"); getRepoDetailsFromBackendStub = sinon .stub(devConnect, "getRepoDetailsFromBackend") .throws("unexpected getRepoDetailsFromBackend call"); @@ -142,7 +149,8 @@ describe("apphosting rollouts", () => { describe("createRollout", () => { it("should create a new rollout from user-specified branch", async () => { - getBackendStub.resolves(backend); + getBackendForLocationStub.resolves(backend); + getBackendForAmbiguousLocationStub.resolves(backend); getRepoDetailsFromBackendStub.resolves(repoLinkDetails); listAllBranchesStub.resolves(branches); getGitHubBranchStub.resolves(branchInfo); @@ -160,7 +168,8 @@ describe("apphosting rollouts", () => { }); it("should create a new rollout from user-specified commit", async () => { - getBackendStub.resolves(backend); + getBackendForLocationStub.resolves(backend); + getBackendForAmbiguousLocationStub.resolves(backend); getRepoDetailsFromBackendStub.resolves(repoLinkDetails); getGitHubCommitStub.resolves(commitInfo); getNextRolloutIdStub.resolves(buildAndRolloutId); @@ -177,7 +186,8 @@ describe("apphosting rollouts", () => { }); it("should prompt user for a branch if branch or commit ID is not specified", async () => { - getBackendStub.resolves(backend); + getBackendForLocationStub.resolves(backend); + getBackendForAmbiguousLocationStub.resolves(backend); getRepoDetailsFromBackendStub.resolves(repoLinkDetails); promptGitHubBranchStub.resolves(branchId); getGitHubBranchStub.resolves(branchInfo); @@ -187,7 +197,7 @@ describe("apphosting rollouts", () => { pollOperationStub.onFirstCall().resolves(rollout); pollOperationStub.onSecondCall().resolves(build); - await createRollout(backendId, projectId, location, undefined, undefined, true); + await createRollout(backendId, projectId, location, undefined, undefined, false); expect(promptGitHubBranchStub).to.be.called; expect(createBuildStub).to.be.called; @@ -196,7 +206,8 @@ describe("apphosting rollouts", () => { }); it("should throw an error if GitHub branch is not found", async () => { - getBackendStub.resolves(backend); + getBackendForLocationStub.resolves(backend); + getBackendForAmbiguousLocationStub.resolves(backend); getRepoDetailsFromBackendStub.resolves(repoLinkDetails); listAllBranchesStub.resolves(branches); @@ -206,7 +217,8 @@ describe("apphosting rollouts", () => { }); it("should throw an error if GitHub commit is not found", async () => { - getBackendStub.resolves(backend); + getBackendForLocationStub.resolves(backend); + getBackendForAmbiguousLocationStub.resolves(backend); getRepoDetailsFromBackendStub.resolves(repoLinkDetails); getGitHubCommitStub.rejects(new FirebaseError("error", { status: 422 })); @@ -214,6 +226,15 @@ describe("apphosting rollouts", () => { createRollout(backendId, projectId, location, undefined, commitSha, true), ).to.be.rejectedWith(/Unrecognized git commit/); }); + + it("should throw an error if --force flag is specified but --git-branch and --git-commit are missing", async () => { + getBackendForLocationStub.resolves(backend); + getRepoDetailsFromBackendStub.resolves(repoLinkDetails); + + await expect( + createRollout(backendId, projectId, location, undefined, undefined, true), + ).to.be.rejectedWith(/Failed to create rollout with --force option/); + }); }); describe("orchestrateRollout", () => { diff --git a/src/apphosting/rollout.ts b/src/apphosting/rollout.ts index e3011338cb6..32b2bcb8d4e 100644 --- a/src/apphosting/rollout.ts +++ b/src/apphosting/rollout.ts @@ -10,11 +10,10 @@ import { } from "../apphosting/githubConnections"; import * as poller from "../operation-poller"; -import { confirm } from "../prompt"; import { logBullet, sleep } from "../utils"; import { apphostingOrigin, consoleOrigin } from "../api"; import { DeepOmit } from "../metaprogramming"; -import { getBackendForAmbiguousLocation, getBackendForLocation } from "."; +import { getBackendForAmbiguousLocation, getBackendForLocation } from "./backend"; const apphostingPollerOptions: Omit = { apiOrigin: apphostingOrigin(), @@ -43,6 +42,7 @@ export async function createRollout( projectId, backendId, "Please select the location of the backend you'd like to roll out:", + force, ); location = apphosting.parseBackendName(backend.name).location; } else { @@ -87,6 +87,11 @@ export async function createRollout( throw err; } } else { + if (force) { + throw new FirebaseError( + `Failed to create rollout with --force option because no target branch or commit was specified. Please specify which branch or commit to roll out with the --git-branch or --git-commit flag.`, + ); + } branch = await promptGitHubBranch(repoLink); const branchInfo = await getGitHubBranch(owner, repo, branch, readToken.token); targetCommit = branchInfo.commit; @@ -95,13 +100,6 @@ export async function createRollout( logBullet( `You are about to deploy [${targetCommit.sha.substring(0, 7)}]: ${targetCommit.commit.message}`, ); - const confirmRollout = await confirm({ - force: !!force, - message: "Do you want to continue?", - }); - if (!confirmRollout) { - return; - } logBullet( `You may also track this rollout at:\n\t${consoleOrigin()}/project/${projectId}/apphosting`, ); diff --git a/src/commands/apphosting-backends-create.ts b/src/commands/apphosting-backends-create.ts index d855d76e535..440870fe853 100644 --- a/src/commands/apphosting-backends-create.ts +++ b/src/commands/apphosting-backends-create.ts @@ -2,7 +2,7 @@ import { Command } from "../command"; import { Options } from "../options"; import { needProjectId } from "../projectUtils"; import requireInteractive from "../requireInteractive"; -import { doSetup } from "../apphosting"; +import { doSetup } from "../apphosting/backend"; import { ensureApiEnabled } from "../gcp/apphosting"; import { APPHOSTING_TOS_ID } from "../gcp/firedata"; import { requireTosAcceptance } from "../requireTosAcceptance"; diff --git a/src/commands/apphosting-backends-delete.ts b/src/commands/apphosting-backends-delete.ts index 34f57308f75..c3d50f94a9e 100644 --- a/src/commands/apphosting-backends-delete.ts +++ b/src/commands/apphosting-backends-delete.ts @@ -10,7 +10,7 @@ import { deleteBackendAndPoll, getBackendForAmbiguousLocation, getBackendForLocation, -} from "../apphosting"; +} from "../apphosting/backend"; import * as ora from "ora"; export const command = new Command("apphosting:backends:delete ") diff --git a/src/commands/apphosting-rollouts-create.ts b/src/commands/apphosting-rollouts-create.ts index de259a9723b..5b827aa0f2b 100644 --- a/src/commands/apphosting-rollouts-create.ts +++ b/src/commands/apphosting-rollouts-create.ts @@ -8,12 +8,11 @@ import { createRollout } from "../apphosting/rollout"; export const command = new Command("apphosting:rollouts:create ") .description("create a rollout using a build for an App Hosting backend") .option("-l, --location ", "specify the region of the backend", "-") - .option("-i, --id ", "id of the rollout (defaults to autogenerating a random id)", "") .option( - "-gb, --git-branch ", - "repository branch to deploy (mutually exclusive with -gc)", + "-b, --git-branch ", + "repository branch to deploy (mutually exclusive with -g)", ) - .option("-gc, --git-commit ", "git commit to deploy (mutually exclusive with -gb)") + .option("-g, --git-commit ", "git commit to deploy (mutually exclusive with -b)") .withForce("Skip confirmation before creating rollout") .before(apphosting.ensureApiEnabled) .action(async (backendId: string, options: Options) => { diff --git a/src/commands/apphosting-secrets-grantaccess.ts b/src/commands/apphosting-secrets-grantaccess.ts index a99da896620..8d6c7da4134 100644 --- a/src/commands/apphosting-secrets-grantaccess.ts +++ b/src/commands/apphosting-secrets-grantaccess.ts @@ -7,7 +7,7 @@ import * as secretManager from "../gcp/secretManager"; import { requirePermissions } from "../requirePermissions"; import * as apphosting from "../gcp/apphosting"; import * as secrets from "../apphosting/secrets"; -import { getBackendForAmbiguousLocation } from "../apphosting"; +import { getBackendForAmbiguousLocation } from "../apphosting/backend"; export const command = new Command("apphosting:secrets:grantaccess ") .description("grant service accounts permissions to the provided secret") From 27c161072f296ff96866f3e84bfb60bbb0b87e8d Mon Sep 17 00:00:00 2001 From: Brian Li Date: Thu, 24 Oct 2024 15:39:22 -0400 Subject: [PATCH 3/7] add src/apphosting/index.ts back in --- src/apphosting/index.ts | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/apphosting/index.ts diff --git a/src/apphosting/index.ts b/src/apphosting/index.ts new file mode 100644 index 00000000000..e69de29bb2d From 22341cbf4974f8c52f6dc258761f53bb05ca9336 Mon Sep 17 00:00:00 2001 From: Brian Li Date: Thu, 24 Oct 2024 16:07:28 -0400 Subject: [PATCH 4/7] export doSetup from index --- src/apphosting/index.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/apphosting/index.ts b/src/apphosting/index.ts index e69de29bb2d..0ecc6703ff5 100644 --- a/src/apphosting/index.ts +++ b/src/apphosting/index.ts @@ -0,0 +1,3 @@ +import { doSetup } from "./backend"; + +export { doSetup as setupBackend }; From 59217ca2ccaff2d0c038a1dcaf7b50a9e17ec0d2 Mon Sep 17 00:00:00 2001 From: Brian Li Date: Thu, 24 Oct 2024 16:21:56 -0400 Subject: [PATCH 5/7] fix broken import --- src/apphosting/backend.ts | 30 +++++++++++++++++++++++++ src/commands/apphosting-repos-create.ts | 2 +- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/apphosting/backend.ts b/src/apphosting/backend.ts index b763dbfa499..9cff51eeea3 100644 --- a/src/apphosting/backend.ts +++ b/src/apphosting/backend.ts @@ -192,6 +192,36 @@ export async function doSetup( logSuccess(`Your backend is now deployed at:\n\thttps://${backend.uri}`); } +/** + * Set up a new App Hosting-type Developer Connect GitRepoLink, optionally with a specific connection ID + */ +export async function createGitRepoLink( + projectId: string, + location: string | null, + connectionId?: string, +): Promise { + await Promise.all([ + ensure(projectId, developerConnectOrigin(), "apphosting", true), + ensure(projectId, secretManagerOrigin(), "apphosting", true), + ensure(projectId, iamOrigin(), "apphosting", true), + ]); + + const allowedLocations = (await apphosting.listLocations(projectId)).map((loc) => loc.locationId); + if (location) { + if (!allowedLocations.includes(location)) { + throw new FirebaseError( + `Invalid location ${location}. Valid choices are ${allowedLocations.join(", ")}`, + ); + } + } + + location = + location || + (await promptLocation(projectId, "Select a location for your GitRepoLink's connection:\n")); + + await githubConnections.linkGitHubRepository(projectId, location, connectionId); +} + /** * Ensures the service account is present the user has permissions to use it by * checking the `iam.serviceAccounts.actAs` permission. If the permissions diff --git a/src/commands/apphosting-repos-create.ts b/src/commands/apphosting-repos-create.ts index bb822c7e6d8..e361a35b8bc 100644 --- a/src/commands/apphosting-repos-create.ts +++ b/src/commands/apphosting-repos-create.ts @@ -2,7 +2,7 @@ import { Command } from "../command"; import { Options } from "../options"; import { needProjectId } from "../projectUtils"; import requireInteractive from "../requireInteractive"; -import { createGitRepoLink } from "../apphosting"; +import { createGitRepoLink } from "../apphosting/backend"; import { ensureApiEnabled } from "../gcp/apphosting"; import { APPHOSTING_TOS_ID } from "../gcp/firedata"; import { requireTosAcceptance } from "../requireTosAcceptance"; From de8849a838c535523097565f78f2d26e7123d75e Mon Sep 17 00:00:00 2001 From: Brian Li Date: Thu, 24 Oct 2024 16:24:20 -0400 Subject: [PATCH 6/7] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e69de29bb2d..219486a04c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -0,0 +1 @@ +- Fix rollouts:create to handle backend regionality & other fixes. (#7862) From e65114a7964a970273dc9672377fad78fa6f1567 Mon Sep 17 00:00:00 2001 From: Brian Li Date: Tue, 29 Oct 2024 11:25:12 -0400 Subject: [PATCH 7/7] switch to v1beta from v1alpha --- src/gcp/apphosting.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gcp/apphosting.ts b/src/gcp/apphosting.ts index 871d46f65d4..aa4b5f961bc 100644 --- a/src/gcp/apphosting.ts +++ b/src/gcp/apphosting.ts @@ -7,7 +7,7 @@ import * as deploymentTool from "../deploymentTool"; import { FirebaseError } from "../error"; import { DeepOmit, RecursiveKeyOf, assertImplements } from "../metaprogramming"; -export const API_VERSION = "v1alpha"; +export const API_VERSION = "v1beta"; export const client = new Client({ urlPrefix: apphostingOrigin(),