diff --git a/src/apphosting/backend.spec.ts b/src/apphosting/backend.spec.ts index 24892b8815c..1f9e7eb0bc4 100644 --- a/src/apphosting/backend.spec.ts +++ b/src/apphosting/backend.spec.ts @@ -18,7 +18,6 @@ import { } from "./backend"; import * as deploymentTool from "../deploymentTool"; import { FirebaseError } from "../error"; -import * as experiments from "../experiments"; describe("apphosting setup functions", () => { const projectId = "projectId"; @@ -67,7 +66,6 @@ describe("apphosting setup functions", () => { testResourceIamPermissionsStub = sinon .stub(iam, "testResourceIamPermissions") .throws("Unexpected testResourceIamPermissions call"); - sinon.stub(experiments, "isEnabled").returns(false).withArgs("abiu").returns(true); }); afterEach(() => { diff --git a/src/apphosting/backend.ts b/src/apphosting/backend.ts index 74097355659..85f828dea93 100644 --- a/src/apphosting/backend.ts +++ b/src/apphosting/backend.ts @@ -28,7 +28,7 @@ import * as ora from "ora"; import fetch from "node-fetch"; import { orchestrateRollout } from "./rollout"; import * as fuzzy from "fuzzy"; -import { isEnabled } from "../experiments"; + import { resolveRuntime } from "./prompts"; const DEFAULT_COMPUTE_SERVICE_ACCOUNT_NAME = "firebase-app-hosting-compute"; @@ -387,10 +387,7 @@ export async function createBackend( appId: webAppId, }; - // this is to be extra careful that we do not set the ABIU fields if the experiment is disabled - if (isEnabled("abiu")) { - backendReqBody.runtime = { value: runtime ?? "" }; - } + backendReqBody.runtime = { value: runtime ?? "" }; async function createBackendAndPoll(): Promise { const op = await apphosting.createBackend(projectId, location, backendReqBody, backendId); diff --git a/src/apphosting/prompts.spec.ts b/src/apphosting/prompts.spec.ts index 6d523e3b1c6..0409dc02e8a 100644 --- a/src/apphosting/prompts.spec.ts +++ b/src/apphosting/prompts.spec.ts @@ -1,7 +1,6 @@ import * as sinon from "sinon"; import { expect } from "chai"; import * as prompts from "./prompts"; -import * as experiments from "../experiments"; import * as apphosting from "../gcp/apphosting"; import * as promptImport from "../prompt"; @@ -9,12 +8,10 @@ describe("prompts", () => { const projectId = "projectId"; const location = "us-central1"; - let isEnabledStub: sinon.SinonStub; let listSupportedRuntimesStub: sinon.SinonStub; let selectStub: sinon.SinonStub; beforeEach(() => { - isEnabledStub = sinon.stub(experiments, "isEnabled"); listSupportedRuntimesStub = sinon.stub(apphosting, "listSupportedRuntimes"); selectStub = sinon.stub(promptImport, "select"); }); @@ -29,20 +26,12 @@ describe("prompts", () => { expect(result).to.equal("nodejs22"); }); - it("should return undefined if abiu experiment is disabled", async () => { - isEnabledStub.withArgs("abiu").returns(false); - const result = await prompts.resolveRuntime(projectId, location, false); - expect(result).to.be.undefined; - }); - it("should return DEFAULT_RUNTIME in non-interactive mode", async () => { - isEnabledStub.withArgs("abiu").returns(true); const result = await prompts.resolveRuntime(projectId, location, true); expect(result).to.equal(prompts.DEFAULT_RUNTIME); }); it("should call promptRuntime and return selected runtime in interactive mode", async () => { - isEnabledStub.withArgs("abiu").returns(true); listSupportedRuntimesStub.resolves([ { runtimeId: "nodejs22", automaticBaseImageUpdatesSupported: true }, ]); diff --git a/src/apphosting/prompts.ts b/src/apphosting/prompts.ts index e59ea5fdfa6..c9ecf1d0729 100644 --- a/src/apphosting/prompts.ts +++ b/src/apphosting/prompts.ts @@ -1,7 +1,6 @@ import { select, Choice } from "../prompt"; import { logWarning, logBullet } from "../utils"; import * as apphosting from "../gcp/apphosting"; -import { isEnabled } from "../experiments"; export const DEFAULT_RUNTIME = "nodejs"; @@ -60,9 +59,6 @@ export async function resolveRuntime( if (runtimeOption !== undefined) { return runtimeOption; } - if (!isEnabled("abiu")) { - return undefined; - } if (nonInteractive) { return DEFAULT_RUNTIME; } diff --git a/src/commands/apphosting-backends-create.spec.ts b/src/commands/apphosting-backends-create.spec.ts index 9f1ef6d061e..33bbec40595 100644 --- a/src/commands/apphosting-backends-create.spec.ts +++ b/src/commands/apphosting-backends-create.spec.ts @@ -3,19 +3,15 @@ import { expect } from "chai"; import { Command } from "../command"; import { command as apphostingBackendsCreate } from "./apphosting-backends-create"; import * as backend from "../apphosting/backend"; -import * as experiments from "../experiments"; -import { FirebaseError } from "../error"; describe("apphosting:backends:create", () => { const PROJECT_ID = "test-project"; let command: Command; - let isEnabledStub: sinon.SinonStub; let doSetupStub: sinon.SinonStub; beforeEach(() => { command = apphostingBackendsCreate; (command as unknown as { befores: unknown[] }).befores = []; // Bypass pre-action hooks for unit testing action - isEnabledStub = sinon.stub(experiments, "isEnabled"); doSetupStub = sinon.stub(backend, "doSetup").resolves(); }); @@ -23,23 +19,7 @@ describe("apphosting:backends:create", () => { sinon.restore(); }); - it("should fail if runtime flag is used without experiment enabled", async () => { - isEnabledStub.returns(false); - const options = { - project: PROJECT_ID, - runtime: "nodejs22", - backend: "test-backend", - primaryRegion: "us-central1", - }; - - await expect(command.runner()(options)).to.be.rejectedWith( - FirebaseError, - /The --runtime flag is only available when the 'abiu' experiment is enabled/, - ); - }); - - it("should default runtime to undefined when experiment is on and no flag provided", async () => { - isEnabledStub.returns(true); + it("should default runtime to undefined when no flag provided", async () => { const options = { project: PROJECT_ID, nonInteractive: true, @@ -63,7 +43,6 @@ describe("apphosting:backends:create", () => { }); it("should pass explicit empty runtime", async () => { - isEnabledStub.returns(true); const options = { project: PROJECT_ID, nonInteractive: true, @@ -88,7 +67,6 @@ describe("apphosting:backends:create", () => { }); it("should pass explicit runtime", async () => { - isEnabledStub.returns(true); const options = { project: PROJECT_ID, nonInteractive: true, @@ -113,7 +91,6 @@ describe("apphosting:backends:create", () => { }); it("should default runtime if flag is present without value (boolean true)", async () => { - isEnabledStub.returns(true); const options = { project: PROJECT_ID, nonInteractive: true, diff --git a/src/commands/apphosting-backends-create.ts b/src/commands/apphosting-backends-create.ts index 5d3389c4caf..8babdca11c2 100644 --- a/src/commands/apphosting-backends-create.ts +++ b/src/commands/apphosting-backends-create.ts @@ -5,7 +5,7 @@ import { needProjectId } from "../projectUtils"; import { requireAuth } from "../requireAuth"; import { doSetup } from "../apphosting/backend"; import { ensureApiEnabled } from "../gcp/apphosting"; -import * as experiments from "../experiments"; + import { APPHOSTING_TOS_ID } from "../gcp/firedata"; import { requireTosAcceptance } from "../requireTosAcceptance"; @@ -29,13 +29,10 @@ export const command = new Command("apphosting:backends:create") "specify the primary region for the backend. Required with --non-interactive.", ) .option("--root-dir ", "specify the root directory for the backend."); -const abiuEnabled = experiments.isEnabled("abiu"); -if (abiuEnabled) { - command.option( - "--runtime [runtime]", - "specify the runtime for the backend (e.g., nodejs, nodejs22)", - ); -} +command.option( + "--runtime [runtime]", + "specify the runtime for the backend (e.g., nodejs, nodejs22)", +); command .before(requireAuth) @@ -47,16 +44,9 @@ command throw new FirebaseError(`--non-interactive option requires --backend and --primary-region`); } - const abiuAllowed = experiments.isEnabled("abiu"); - if (!abiuAllowed && options.runtime) { - throw new FirebaseError( - "The --runtime flag is only available when the 'abiu' experiment is enabled. To enable it, run 'firebase experiments:enable abiu'.", - ); - } - // When ABIU is allowed but the user doesn't provide a runtime string, we let doSetup handle it. + // When the user doesn't provide a runtime string, we let doSetup handle it. // We strictly check for string type to avoid boolean true (flag present without value) causing issues. - const runtime = - abiuAllowed && typeof options.runtime === "string" ? options.runtime : undefined; + const runtime = typeof options.runtime === "string" ? options.runtime : undefined; return doSetup( projectId, diff --git a/src/commands/apphosting-backends-list.spec.ts b/src/commands/apphosting-backends-list.spec.ts index 3a585e4ca29..df013a87da0 100644 --- a/src/commands/apphosting-backends-list.spec.ts +++ b/src/commands/apphosting-backends-list.spec.ts @@ -2,16 +2,13 @@ import * as sinon from "sinon"; import { expect } from "chai"; import { logger } from "../logger"; import { printBackendsTable } from "./apphosting-backends-list"; -import * as experiments from "../experiments"; import * as apphosting from "../gcp/apphosting"; describe("apphosting:backends:list printBackendsTable", () => { let loggerStub: sinon.SinonStub; - let isEnabledStub: sinon.SinonStub; beforeEach(() => { loggerStub = sinon.stub(logger, "info"); - isEnabledStub = sinon.stub(experiments, "isEnabled"); }); afterEach(() => { @@ -19,7 +16,6 @@ describe("apphosting:backends:list printBackendsTable", () => { }); it("should display Disabled if automaticBaseImageUpdatesDisabled is true", () => { - isEnabledStub.withArgs("abiu").returns(true); const mockBackend: Partial = { name: "projects/test-project/locations/us-central1/backends/test-backend", uri: "https://test-backend.app", @@ -35,7 +31,6 @@ describe("apphosting:backends:list printBackendsTable", () => { }); it("should display Enabled if automaticBaseImageUpdatesDisabled is false", () => { - isEnabledStub.withArgs("abiu").returns(true); const mockBackend: Partial = { name: "projects/test-project/locations/us-central1/backends/test-backend", uri: "https://test-backend.app", @@ -51,7 +46,6 @@ describe("apphosting:backends:list printBackendsTable", () => { }); it("should fallback to Disabled if automaticBaseImageUpdatesDisabled is missing and runtime is legacy", () => { - isEnabledStub.withArgs("abiu").returns(true); const mockBackend: Partial = { name: "projects/test-project/locations/us-central1/backends/test-backend", uri: "https://test-backend.app", @@ -66,7 +60,6 @@ describe("apphosting:backends:list printBackendsTable", () => { }); it("should fallback to Enabled if automaticBaseImageUpdatesDisabled is missing and runtime is new", () => { - isEnabledStub.withArgs("abiu").returns(true); const mockBackend: Partial = { name: "projects/test-project/locations/us-central1/backends/test-backend", uri: "https://test-backend.app", diff --git a/src/commands/apphosting-backends-list.ts b/src/commands/apphosting-backends-list.ts index 65108863170..a4cc1a3063c 100644 --- a/src/commands/apphosting-backends-list.ts +++ b/src/commands/apphosting-backends-list.ts @@ -6,7 +6,7 @@ import { needProjectId } from "../projectUtils"; import { requireAuth } from "../requireAuth"; import { Options } from "../options"; import * as apphosting from "../gcp/apphosting"; -import { isEnabled } from "../experiments"; + import * as Table from "cli-table3"; export const command = new Command("apphosting:backends:list") @@ -35,12 +35,7 @@ export const command = new Command("apphosting:backends:list") * Prints a table given a list of backends */ export function printBackendsTable(backends: apphosting.Backend[]): void { - const abiuEnabled = isEnabled("abiu"); - const head = ["Backend", "Repository", "URL", "Primary Region"]; - if (abiuEnabled) { - head.push("ABIU"); - head.push("Runtime"); - } + const head = ["Backend", "Repository", "URL", "Primary Region", "ABIU", "Runtime"]; head.push("Updated Date"); const table = new Table({ @@ -57,18 +52,16 @@ export function printBackendsTable(backends: apphosting.Backend[]): void { backend.uri.startsWith("https:") ? backend.uri : "https://" + backend.uri, location, ]; - if (abiuEnabled) { - let abiuStatus = "N/A"; - const runtimeValue = backend.runtime?.value ?? ""; - // We know these runtimes do not support ABIU - if (runtimeValue === "" || runtimeValue === "nodejs") { - abiuStatus = "Disabled"; - } else { - abiuStatus = backend.automaticBaseImageUpdatesDisabled ? "Disabled" : "Enabled"; - } - row.push(abiuStatus); - row.push(backend.runtime?.value ?? "N/A"); + let abiuStatus = "N/A"; + const runtimeValue = backend.runtime?.value ?? ""; + // We know these runtimes do not support ABIU + if (runtimeValue === "" || runtimeValue === "nodejs") { + abiuStatus = "Disabled"; + } else { + abiuStatus = backend.automaticBaseImageUpdatesDisabled ? "Disabled" : "Enabled"; } + row.push(abiuStatus); + row.push(backend.runtime?.value ?? "N/A"); row.push(datetimeString(new Date(backend.updateTime))); table.push(row); } diff --git a/src/experiments.ts b/src/experiments.ts index 5daa8e826b3..47f42bd33ea 100644 --- a/src/experiments.ts +++ b/src/experiments.ts @@ -149,13 +149,6 @@ export const ALL_EXPERIMENTS = experiments({ public: false, }, - abiu: { - shortDescription: - "Enable Automatic Base Image Updates (ABIU) and runtime selection for App Hosting", - default: true, - public: true, - }, - // TODO(joehanley): Delete this once weve scrubbed all references to experiment from docs. dataconnect: { shortDescription: "Deprecated. Previosuly, enabled SQL Connect related features.",