diff --git a/src/commands/functions-config-clone.ts b/src/commands/functions-config-clone.ts index 12b0d2c129d..d5fda76983b 100644 --- a/src/commands/functions-config-clone.ts +++ b/src/commands/functions-config-clone.ts @@ -8,13 +8,13 @@ import { requirePermissions } from "../requirePermissions"; import * as functionsConfig from "../functionsConfig"; import { functionsConfigClone } from "../functionsConfigClone"; import * as utils from "../utils"; -import { logFunctionsConfigDeprecationWarning } from "../functions/deprecationWarnings"; export const command = new Command("functions:config:clone") .description("clone environment config from another project") .option("--from ", "the project from which to clone configuration") .option("--only ", "a comma-separated list of keys to clone") .option("--except ", "a comma-separated list of keys to not clone") + .before(functionsConfig.ensureLegacyRuntimeConfigCommandsEnabled) .before(requirePermissions, [ "runtimeconfig.configs.list", "runtimeconfig.configs.create", @@ -57,5 +57,5 @@ export const command = new Command("functions:config:clone") "firebase deploy --only functions", )}\n`, ); - logFunctionsConfigDeprecationWarning(); + functionsConfig.logFunctionsConfigDeprecationWarning(); }); diff --git a/src/commands/functions-config-get.ts b/src/commands/functions-config-get.ts index 3827b80d2cf..1b944686a4e 100644 --- a/src/commands/functions-config-get.ts +++ b/src/commands/functions-config-get.ts @@ -6,7 +6,6 @@ import { logger } from "../logger"; import { needProjectId } from "../projectUtils"; import { requirePermissions } from "../requirePermissions"; import * as functionsConfig from "../functionsConfig"; -import { logFunctionsConfigDeprecationWarning } from "../functions/deprecationWarnings"; async function materialize(projectId: string, path?: string): Promise { if (path === undefined) { @@ -32,6 +31,6 @@ export const command = new Command("functions:config:get [path]") .action(async (path, options) => { const result = await materialize(needProjectId(options), path); logger.info(JSON.stringify(result, null, 2)); - logFunctionsConfigDeprecationWarning(); + functionsConfig.logFunctionsConfigDeprecationWarning(); return result; }); diff --git a/src/commands/functions-config-set.ts b/src/commands/functions-config-set.ts index 6ab9aa1f67a..2059c45c8ae 100644 --- a/src/commands/functions-config-set.ts +++ b/src/commands/functions-config-set.ts @@ -7,10 +7,10 @@ import { needProjectId } from "../projectUtils"; import { requirePermissions } from "../requirePermissions"; import * as functionsConfig from "../functionsConfig"; import * as utils from "../utils"; -import { logFunctionsConfigDeprecationWarning } from "../functions/deprecationWarnings"; export const command = new Command("functions:config:set [values...]") .description("set environment config with key=value syntax") + .before(functionsConfig.ensureLegacyRuntimeConfigCommandsEnabled) .before(requirePermissions, [ "runtimeconfig.configs.list", "runtimeconfig.configs.create", @@ -50,5 +50,5 @@ export const command = new Command("functions:config:set [values...]") "firebase deploy --only functions", )}\n`, ); - logFunctionsConfigDeprecationWarning(); + functionsConfig.logFunctionsConfigDeprecationWarning(); }); diff --git a/src/commands/functions-config-unset.ts b/src/commands/functions-config-unset.ts index 4b326876236..efcf83a7530 100644 --- a/src/commands/functions-config-unset.ts +++ b/src/commands/functions-config-unset.ts @@ -8,10 +8,10 @@ import * as functionsConfig from "../functionsConfig"; import * as runtimeconfig from "../gcp/runtimeconfig"; import * as utils from "../utils"; import { FirebaseError } from "../error"; -import { logFunctionsConfigDeprecationWarning } from "../functions/deprecationWarnings"; export const command = new Command("functions:config:unset [keys...]") .description("unset environment config at the specified path(s)") + .before(functionsConfig.ensureLegacyRuntimeConfigCommandsEnabled) .before(requirePermissions, [ "runtimeconfig.configs.list", "runtimeconfig.configs.create", @@ -45,5 +45,5 @@ export const command = new Command("functions:config:unset [keys...]") "firebase deploy --only functions", )}\n`, ); - logFunctionsConfigDeprecationWarning(); + functionsConfig.logFunctionsConfigDeprecationWarning(); }); diff --git a/src/commands/internaltesting-functions-discover.ts b/src/commands/internaltesting-functions-discover.ts index 981b3411596..8cb672585a6 100644 --- a/src/commands/internaltesting-functions-discover.ts +++ b/src/commands/internaltesting-functions-discover.ts @@ -24,7 +24,7 @@ export const command = new Command("internaltesting:functions:discover") } let runtimeConfig: Record = { firebase: firebaseConfig }; - const allowFunctionsConfig = experiments.isEnabled("dangerouslyAllowFunctionsConfig"); + const allowFunctionsConfig = experiments.isEnabled("legacyRuntimeConfigCommands"); if (allowFunctionsConfig) { try { diff --git a/src/deploy/functions/prepare.ts b/src/deploy/functions/prepare.ts index e947945379f..eb70fd13ea0 100644 --- a/src/deploy/functions/prepare.ts +++ b/src/deploy/functions/prepare.ts @@ -94,7 +94,7 @@ export async function prepare( // ===Phase 1. Load codebases from source with optional runtime config. let runtimeConfig: Record = { firebase: firebaseConfig }; - const allowFunctionsConfig = experiments.isEnabled("dangerouslyAllowFunctionsConfig"); + const allowFunctionsConfig = experiments.isEnabled("legacyRuntimeConfigCommands"); // Load runtime config if experiment allows it and API is enabled if (allowFunctionsConfig && checkAPIsEnabled[1]) { diff --git a/src/deploy/functions/prepareFunctionsUpload.ts b/src/deploy/functions/prepareFunctionsUpload.ts index 6f2ba2cdd3d..64407702c1e 100644 --- a/src/deploy/functions/prepareFunctionsUpload.ts +++ b/src/deploy/functions/prepareFunctionsUpload.ts @@ -13,7 +13,6 @@ import * as functionsConfig from "../../functionsConfig"; import * as utils from "../../utils"; import * as fsAsync from "../../fsAsync"; import * as projectConfig from "../../functions/projectConfig"; -import { logFunctionsConfigDeprecationWarning } from "../../functions/deprecationWarnings"; const CONFIG_DEST_FILE = ".runtimeconfig.json"; @@ -104,7 +103,7 @@ async function packageSource( // Only warn about deprecated runtime config if there are user-defined values // (i.e., keys other than the default 'firebase' key) if (Object.keys(runtimeConfig).some((k) => k !== "firebase")) { - logFunctionsConfigDeprecationWarning(); + functionsConfig.logFunctionsConfigDeprecationWarning(); } } await pipeAsync(archive, fileStream); diff --git a/src/experiments.ts b/src/experiments.ts index 8c16408cb06..3e7ef0f7aee 100644 --- a/src/experiments.ts +++ b/src/experiments.ts @@ -39,7 +39,7 @@ export const ALL_EXPERIMENTS = experiments({ "of deploys. This has been made an experiment due to backend bugs that are " + "temporarily causing failures in some regions with this optimization enabled", public: true, - default: true, + default: false, }, deletegcfartifacts: { shortDescription: `Add the ${bold( @@ -55,13 +55,13 @@ export const ALL_EXPERIMENTS = experiments({ `Registry. The ${bold("functions:deletegcfartifacts")} command ` + "will delete all Docker images created by Google Cloud Functions irrespective " + "of how that image was created.", - public: true, + public: false, }, - dangerouslyAllowFunctionsConfig: { - shortDescription: "Allows the use of deprecated functions.config() API", + legacyRuntimeConfigCommands: { + shortDescription: "Expose legacy functions.config() CLI commands", fullDescription: - "The functions.config() API is deprecated and will be removed on December 31, 2025. " + - "This experiment allows continued use of the API during the migration period.", + "The Cloud Runtime Config API is deprecated. Enable this experiment to continue using the " + + "`functions:config:*` commands while you migrate to the Firebase Functions params APIs.", default: true, public: true, }, diff --git a/src/functions/deprecationWarnings.ts b/src/functions/deprecationWarnings.ts deleted file mode 100644 index 68c3c0b38d4..00000000000 --- a/src/functions/deprecationWarnings.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { logWarningToStderr } from "../utils"; - -const FUNCTIONS_CONFIG_DEPRECATION_MESSAGE = `DEPRECATION NOTICE: Action required to deploy after March 2026 - - functions.config() API is deprecated. - Cloud Runtime Configuration API, the Google Cloud service used to store function configuration data, will be shut down in March 2026. As a result, you must migrate away from using functions.config() to continue deploying your functions after March 2026. - - What this means for you: - - - The Firebase CLI commands for managing this configuration (functions:config:set, get, unset, clone, and export) are deprecated. These commands will no longer work after March 2026. - - firebase deploy command will fail for functions that use the legacy functions.config() API after March 2026. - - Existing deployments will continue to work with their current configuration. - - See your migration options at: https://firebase.google.com/docs/functions/config-env#migrate-to-dotenv`; - -/** - * Logs a deprecation warning for functions.config() usage - */ -export function logFunctionsConfigDeprecationWarning(): void { - logWarningToStderr(FUNCTIONS_CONFIG_DEPRECATION_MESSAGE); -} diff --git a/src/functionsConfig.ts b/src/functionsConfig.ts index eda054cf627..be5d698614c 100644 --- a/src/functionsConfig.ts +++ b/src/functionsConfig.ts @@ -8,11 +8,52 @@ import { FirebaseError } from "./error"; import { needProjectId } from "./projectUtils"; import * as runtimeconfig from "./gcp/runtimeconfig"; import * as args from "./deploy/functions/args"; +import * as experiments from "./experiments"; +import { logWarningToStderr } from "./utils"; export const RESERVED_NAMESPACES = ["firebase"]; const apiClient = new Client({ urlPrefix: firebaseApiOrigin() }); +const LEGACY_RUNTIME_CONFIG_EXPERIMENT = "legacyRuntimeConfigCommands"; + +const FUNCTIONS_CONFIG_DEPRECATION_MESSAGE = `DEPRECATION NOTICE: Action required before March 2026 + +The functions.config() API and the Cloud Runtime Config service are deprecated. Deploys that rely on functions.config() will fail once Runtime Config shuts down in March 2026. + +The legacy functions:config:* CLI commands are deprecated and will be removed before March 2026. + +Learn how to migrate from functions.config() to the params package: + +https://firebase.google.com/docs/functions/config-env#migrate-config + +To convert existing functions.config() values to params, try the interactive migration command: + + firebase functions:config:export +`; + +const LEGACY_GUIDANCE_MESSAGE = `${FUNCTIONS_CONFIG_DEPRECATION_MESSAGE} + +To run this legacy command temporarily, run the following command and try again: + + firebase experiments:enable ${LEGACY_RUNTIME_CONFIG_EXPERIMENT} +`; + +export function getFunctionsConfigDeprecationMessage(): string { + return FUNCTIONS_CONFIG_DEPRECATION_MESSAGE; +} + +export function logFunctionsConfigDeprecationWarning(): void { + logWarningToStderr(FUNCTIONS_CONFIG_DEPRECATION_MESSAGE); +} + +export function ensureLegacyRuntimeConfigCommandsEnabled(): void { + if (experiments.isEnabled(LEGACY_RUNTIME_CONFIG_EXPERIMENT)) { + return; + } + throw new FirebaseError(LEGACY_GUIDANCE_MESSAGE, { exit: 1 }); +} + interface Id { config: string; variable: string;