From 5766fc71c62ef39bd469d81e3f03c66dad615625 Mon Sep 17 00:00:00 2001 From: Juan Pablo Garcia Ripa Date: Wed, 9 Jul 2025 21:33:23 +0200 Subject: [PATCH 01/14] validate plugin and module ids Signed-off-by: Juan Pablo Garcia Ripa --- .changeset/itchy-sites-cheer.md | 8 ++++ .../architecture/08-naming-patterns.md | 16 +++---- .../src/wiring/BackendInitializer.test.ts | 47 +++++++++++++++++++ .../src/wiring/createBackendModule.ts | 7 +++ .../src/wiring/createBackendPlugin.ts | 7 +++ packages/config/report.api.md | 3 ++ packages/config/src/constants.ts | 27 +++++++++++ packages/config/src/index.ts | 1 + packages/config/src/reader.ts | 3 +- 9 files changed, 109 insertions(+), 10 deletions(-) create mode 100644 .changeset/itchy-sites-cheer.md create mode 100644 packages/config/src/constants.ts diff --git a/.changeset/itchy-sites-cheer.md b/.changeset/itchy-sites-cheer.md new file mode 100644 index 0000000000..54f5f2fadf --- /dev/null +++ b/.changeset/itchy-sites-cheer.md @@ -0,0 +1,8 @@ +--- +'@backstage/backend-plugin-api': minor +'@backstage/backend-app-api': minor +'@backstage/config-loader': patch +'@backstage/config': patch +--- + +The backend will now throw an error if a plugin or a module doesn't have a valid ID diff --git a/docs/backend-system/architecture/08-naming-patterns.md b/docs/backend-system/architecture/08-naming-patterns.md index 585a9fe87c..990cc62b70 100644 --- a/docs/backend-system/architecture/08-naming-patterns.md +++ b/docs/backend-system/architecture/08-naming-patterns.md @@ -11,10 +11,10 @@ As a rule, all names should be camel case, with the exceptions of plugin and mod ### Plugins -| Description | Pattern | Examples | -| ----------- | ----------------- | ------------------------------------- | -| export | `Plugin` | `catalogPlugin`, `userSettingsPlugin` | -| ID | `''` | `'catalog'`, `'user-settings'` | +| Description | Pattern | Examples | Notes | +| ----------- | ----------------- | ------------------------------------- | --------------------------------------------------------------------- | +| export | `Plugin` | `catalogPlugin`, `userSettingsPlugin` | | +| ID | `''` | `'catalog'`, `'user-settings'` | letters, digits, dashes, and underscores only, starting with a letter | Example: @@ -27,10 +27,10 @@ export const userSettingsPlugin = createBackendPlugin({ ### Modules -| Description | Pattern | Examples | -| ----------- | ---------------------------- | ----------------------------------- | -| export | `Module` | `catalogModuleGithubEntityProvider` | -| ID | `''` | `'github-entity-provider'` | +| Description | Pattern | Examples | Notes | +| ----------- | ---------------------------- | ----------------------------------- | --------------------------------------------------------------------- | +| export | `Module` | `catalogModuleGithubEntityProvider` | | +| ID | `''` | `'github-entity-provider'` | letters, digits, dashes, and underscores only, starting with a letter | Example: diff --git a/packages/backend-app-api/src/wiring/BackendInitializer.test.ts b/packages/backend-app-api/src/wiring/BackendInitializer.test.ts index 1c0bf5a0ef..a64743233d 100644 --- a/packages/backend-app-api/src/wiring/BackendInitializer.test.ts +++ b/packages/backend-app-api/src/wiring/BackendInitializer.test.ts @@ -1011,6 +1011,53 @@ describe('BackendInitializer', () => { "Service or extension point dependencies of module 'test-mod' for plugin 'test' are missing for the following ref(s): serviceRef{a}", ); }); + it('should reject plugins with invalid pluginId', async () => { + const init = new BackendInitializer(baseFactories); + init.add( + createBackendPlugin({ + pluginId: 'test:invalid&id', + register(reg) { + reg.registerInit({ + deps: {}, + async init() {}, + }); + }, + }), + ); + await expect(init.start()).rejects.toThrow( + "Invalid pluginId 'test:invalid&id', must match the pattern /^[a-z][a-z0-9]*(?:[-_][a-z][a-z0-9]*)*$/i (letters, digits, dashes, and underscores only, starting with a letter)", + ); + }); + + it('should reject modules with invalid moduleId', async () => { + const init = new BackendInitializer(baseFactories); + init.add( + createBackendPlugin({ + pluginId: 'test', + register(reg) { + reg.registerInit({ + deps: {}, + async init() {}, + }); + }, + }), + ); + init.add( + createBackendModule({ + pluginId: 'test', + moduleId: 'invalid:module&id', + register(reg) { + reg.registerInit({ + deps: {}, + async init() {}, + }); + }, + }), + ); + await expect(init.start()).rejects.toThrow( + "Invalid moduleId 'invalid:module&id' for plugin 'test', must match the pattern /^[a-z][a-z0-9]*(?:[-_][a-z][a-z0-9]*)*$/i (letters, digits, dashes, and underscores only, starting with a letter)", + ); + }); it('should properly load double-default CJS modules', async () => { expect.assertions(3); diff --git a/packages/backend-plugin-api/src/wiring/createBackendModule.ts b/packages/backend-plugin-api/src/wiring/createBackendModule.ts index e2357a65d0..9d064d3b0d 100644 --- a/packages/backend-plugin-api/src/wiring/createBackendModule.ts +++ b/packages/backend-plugin-api/src/wiring/createBackendModule.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { CONFIG_KEY_PART_PATTERN } from '@backstage/config'; import { BackendFeature } from '../types'; import { BackendModuleRegistrationPoints, @@ -55,6 +56,12 @@ export function createBackendModule( options: CreateBackendModuleOptions, ): BackendFeature { function getRegistrations() { + if (!CONFIG_KEY_PART_PATTERN.test(options.moduleId)) { + throw new Error( + `Invalid moduleId '${options.moduleId}' for plugin '${options.pluginId}', must match the pattern ${CONFIG_KEY_PART_PATTERN} (letters, digits, dashes, and underscores only, starting with a letter)`, + ); + } + const extensionPoints: InternalBackendPluginRegistration['extensionPoints'] = []; let init: InternalBackendModuleRegistration['init'] | undefined = undefined; diff --git a/packages/backend-plugin-api/src/wiring/createBackendPlugin.ts b/packages/backend-plugin-api/src/wiring/createBackendPlugin.ts index 185500c495..e4d32197ff 100644 --- a/packages/backend-plugin-api/src/wiring/createBackendPlugin.ts +++ b/packages/backend-plugin-api/src/wiring/createBackendPlugin.ts @@ -14,6 +14,7 @@ * limitations under the License. */ +import { CONFIG_KEY_PART_PATTERN } from '@backstage/config'; import { BackendFeature } from '../types'; import { BackendPluginRegistrationPoints, @@ -49,6 +50,12 @@ export function createBackendPlugin( options: CreateBackendPluginOptions, ): BackendFeature { function getRegistrations() { + if (!CONFIG_KEY_PART_PATTERN.test(options.pluginId)) { + throw new Error( + `Invalid pluginId '${options.pluginId}', must match the pattern ${CONFIG_KEY_PART_PATTERN} (letters, digits, dashes, and underscores only, starting with a letter)`, + ); + } + const extensionPoints: InternalBackendPluginRegistration['extensionPoints'] = []; let init: InternalBackendPluginRegistration['init'] | undefined = undefined; diff --git a/packages/config/report.api.md b/packages/config/report.api.md index 94c4652b44..2963960973 100644 --- a/packages/config/report.api.md +++ b/packages/config/report.api.md @@ -43,6 +43,9 @@ export type Config = { getOptionalStringArray(key: string): string[] | undefined; }; +// @public +export const CONFIG_KEY_PART_PATTERN: RegExp; + // @public export class ConfigReader implements Config { constructor( diff --git a/packages/config/src/constants.ts b/packages/config/src/constants.ts new file mode 100644 index 0000000000..acd5c50961 --- /dev/null +++ b/packages/config/src/constants.ts @@ -0,0 +1,27 @@ +/* + * Copyright 2025 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * The pattern that config keys must match. + * + * @remarks + * keys must only contain the letters `a` through `z` and digits, in groups separated by + * dashes or underscores. Additionally, the very first character of each such group + * must be a letter, not a digit + * + * @public + */ +export const CONFIG_KEY_PART_PATTERN = /^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*$/i; diff --git a/packages/config/src/index.ts b/packages/config/src/index.ts index c6254e97ba..2f3ade0189 100644 --- a/packages/config/src/index.ts +++ b/packages/config/src/index.ts @@ -29,3 +29,4 @@ export type { export { readDurationFromConfig } from './readDurationFromConfig'; export { ConfigReader } from './reader'; export type { AppConfig, Config } from './types'; +export { CONFIG_KEY_PART_PATTERN } from './constants'; diff --git a/packages/config/src/reader.ts b/packages/config/src/reader.ts index 8186ee8faa..18f010daac 100644 --- a/packages/config/src/reader.ts +++ b/packages/config/src/reader.ts @@ -17,8 +17,7 @@ import { JsonObject, JsonValue } from '@backstage/types'; import { AppConfig, Config } from './types'; -// Update the same pattern in config-loader package if this is changed -const CONFIG_KEY_PART_PATTERN = /^[a-z][a-z0-9]*(?:[-_:][a-z0-9]+)*$/i; +import { CONFIG_KEY_PART_PATTERN } from './constants'; function isObject(value: JsonValue | undefined): value is JsonObject { return typeof value === 'object' && value !== null && !Array.isArray(value); From 051b3af31d6ff6b061bc42232c32ee21384aee78 Mon Sep 17 00:00:00 2001 From: Juan Pablo Garcia Ripa Date: Wed, 9 Jul 2025 21:55:44 +0200 Subject: [PATCH 02/14] fix test and update invalid ids Signed-off-by: Juan Pablo Garcia Ripa --- .../backend-app-api/src/wiring/BackendInitializer.test.ts | 5 +++-- .../backend-dynamic-feature-service/src/schemas/frontend.ts | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/backend-app-api/src/wiring/BackendInitializer.test.ts b/packages/backend-app-api/src/wiring/BackendInitializer.test.ts index a64743233d..7be989a0ea 100644 --- a/packages/backend-app-api/src/wiring/BackendInitializer.test.ts +++ b/packages/backend-app-api/src/wiring/BackendInitializer.test.ts @@ -27,6 +27,7 @@ import { import { BackendInitializer } from './BackendInitializer'; import { mockServices } from '@backstage/backend-test-utils'; import { BackendStartupError } from './BackendStartupError'; +import { CONFIG_KEY_PART_PATTERN } from '@backstage/config'; const baseFactories = [ mockServices.rootLifecycle.factory(), @@ -1025,7 +1026,7 @@ describe('BackendInitializer', () => { }), ); await expect(init.start()).rejects.toThrow( - "Invalid pluginId 'test:invalid&id', must match the pattern /^[a-z][a-z0-9]*(?:[-_][a-z][a-z0-9]*)*$/i (letters, digits, dashes, and underscores only, starting with a letter)", + `Invalid pluginId 'test:invalid&id', must match the pattern ${CONFIG_KEY_PART_PATTERN} (letters, digits, dashes, and underscores only, starting with a letter)`, ); }); @@ -1055,7 +1056,7 @@ describe('BackendInitializer', () => { }), ); await expect(init.start()).rejects.toThrow( - "Invalid moduleId 'invalid:module&id' for plugin 'test', must match the pattern /^[a-z][a-z0-9]*(?:[-_][a-z][a-z0-9]*)*$/i (letters, digits, dashes, and underscores only, starting with a letter)", + `Invalid moduleId 'invalid:module&id' for plugin 'test', must match the pattern ${CONFIG_KEY_PART_PATTERN} (letters, digits, dashes, and underscores only, starting with a letter)`, ); }); diff --git a/packages/backend-dynamic-feature-service/src/schemas/frontend.ts b/packages/backend-dynamic-feature-service/src/schemas/frontend.ts index d101487318..bb83898f8c 100644 --- a/packages/backend-dynamic-feature-service/src/schemas/frontend.ts +++ b/packages/backend-dynamic-feature-service/src/schemas/frontend.ts @@ -31,7 +31,7 @@ import { */ export const dynamicPluginsFrontendSchemas = createBackendModule({ pluginId: 'app', - moduleId: 'core.dynamicplugins.frontendSchemas', + moduleId: 'core-dynamicplugins-frontendSchemas', register(reg) { reg.registerInit({ deps: { From f4470cd3a3b663ffdcfe484c67e47825dbac0961 Mon Sep 17 00:00:00 2001 From: Juan Pablo Garcia Ripa Date: Sun, 7 Sep 2025 22:50:30 +0200 Subject: [PATCH 03/14] make a warning for invalid plugin and module Ids Signed-off-by: Juan Pablo Garcia Ripa --- .../architecture/08-naming-patterns.md | 16 +++++------ .../src/wiring/constants.ts | 28 +++++++++++++++++++ .../src/wiring/createBackendModule.ts | 11 ++++++-- .../src/wiring/createBackendPlugin.ts | 11 ++++++-- 4 files changed, 52 insertions(+), 14 deletions(-) create mode 100644 packages/backend-plugin-api/src/wiring/constants.ts diff --git a/docs/backend-system/architecture/08-naming-patterns.md b/docs/backend-system/architecture/08-naming-patterns.md index 990cc62b70..b06d52c287 100644 --- a/docs/backend-system/architecture/08-naming-patterns.md +++ b/docs/backend-system/architecture/08-naming-patterns.md @@ -11,10 +11,10 @@ As a rule, all names should be camel case, with the exceptions of plugin and mod ### Plugins -| Description | Pattern | Examples | Notes | -| ----------- | ----------------- | ------------------------------------- | --------------------------------------------------------------------- | -| export | `Plugin` | `catalogPlugin`, `userSettingsPlugin` | | -| ID | `''` | `'catalog'`, `'user-settings'` | letters, digits, dashes, and underscores only, starting with a letter | +| Description | Pattern | Examples | Notes | +| ----------- | ----------------- | ------------------------------------- | -------------------------------------------------- | +| export | `Plugin` | `catalogPlugin`, `userSettingsPlugin` | | +| ID | `''` | `'catalog'`, `'user-settings'` | letters, digits,and dashes, starting with a letter | Example: @@ -27,10 +27,10 @@ export const userSettingsPlugin = createBackendPlugin({ ### Modules -| Description | Pattern | Examples | Notes | -| ----------- | ---------------------------- | ----------------------------------- | --------------------------------------------------------------------- | -| export | `Module` | `catalogModuleGithubEntityProvider` | | -| ID | `''` | `'github-entity-provider'` | letters, digits, dashes, and underscores only, starting with a letter | +| Description | Pattern | Examples | Notes | +| ----------- | ---------------------------- | ----------------------------------- | --------------------------------------------------- | +| export | `Module` | `catalogModuleGithubEntityProvider` | | +| ID | `''` | `'github-entity-provider'` | letters, digits, and dashes, starting with a letter | Example: diff --git a/packages/backend-plugin-api/src/wiring/constants.ts b/packages/backend-plugin-api/src/wiring/constants.ts new file mode 100644 index 0000000000..b5463d10da --- /dev/null +++ b/packages/backend-plugin-api/src/wiring/constants.ts @@ -0,0 +1,28 @@ +/* + * Copyright 2025 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * The pattern that IDs must match. + * + * @remarks + * ids must only contain the letters `a` through `z` and digits, in groups separated by + * dashes. Additionally, the very first character of each such group + * must be a letter, not a digit + * + * @public + */ +export const ID_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/i; +export const ID_PATTERN_OLD = /^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*$/i; diff --git a/packages/backend-plugin-api/src/wiring/createBackendModule.ts b/packages/backend-plugin-api/src/wiring/createBackendModule.ts index 9d064d3b0d..f65e11acae 100644 --- a/packages/backend-plugin-api/src/wiring/createBackendModule.ts +++ b/packages/backend-plugin-api/src/wiring/createBackendModule.ts @@ -14,8 +14,8 @@ * limitations under the License. */ -import { CONFIG_KEY_PART_PATTERN } from '@backstage/config'; import { BackendFeature } from '../types'; +import { ID_PATTERN, ID_PATTERN_OLD } from './constants'; import { BackendModuleRegistrationPoints, InternalBackendModuleRegistration, @@ -56,9 +56,14 @@ export function createBackendModule( options: CreateBackendModuleOptions, ): BackendFeature { function getRegistrations() { - if (!CONFIG_KEY_PART_PATTERN.test(options.moduleId)) { + if (!ID_PATTERN.test(options.moduleId)) { + console.warn( + `WARNING: The moduleId '${options.moduleId}' for plugin '${options.pluginId}', will be invalid soon please must match the pattern ${ID_PATTERN} (letters, digits, and dashes only, starting with a letter)`, + ); + } + if (!ID_PATTERN_OLD.test(options.moduleId)) { throw new Error( - `Invalid moduleId '${options.moduleId}' for plugin '${options.pluginId}', must match the pattern ${CONFIG_KEY_PART_PATTERN} (letters, digits, dashes, and underscores only, starting with a letter)`, + `Invalid moduleId '${options.moduleId}' for plugin '${options.pluginId}', must match the pattern ${ID_PATTERN} (letters, digits, and dashes only, starting with a letter)`, ); } diff --git a/packages/backend-plugin-api/src/wiring/createBackendPlugin.ts b/packages/backend-plugin-api/src/wiring/createBackendPlugin.ts index e4d32197ff..d2aa633620 100644 --- a/packages/backend-plugin-api/src/wiring/createBackendPlugin.ts +++ b/packages/backend-plugin-api/src/wiring/createBackendPlugin.ts @@ -14,13 +14,13 @@ * limitations under the License. */ -import { CONFIG_KEY_PART_PATTERN } from '@backstage/config'; import { BackendFeature } from '../types'; import { BackendPluginRegistrationPoints, InternalBackendPluginRegistration, InternalBackendRegistrations, } from './types'; +import { ID_PATTERN, ID_PATTERN_OLD } from './constants'; /** * The configuration options passed to {@link createBackendPlugin}. @@ -50,9 +50,14 @@ export function createBackendPlugin( options: CreateBackendPluginOptions, ): BackendFeature { function getRegistrations() { - if (!CONFIG_KEY_PART_PATTERN.test(options.pluginId)) { + if (!ID_PATTERN.test(options.pluginId)) { + console.warn( + `WARNING: The pluginId '${options.pluginId}' will be invalid soon please must match the pattern ${ID_PATTERN} (letters, digits, and dashes only, starting with a letter)`, + ); + } + if (!ID_PATTERN_OLD.test(options.pluginId)) { throw new Error( - `Invalid pluginId '${options.pluginId}', must match the pattern ${CONFIG_KEY_PART_PATTERN} (letters, digits, dashes, and underscores only, starting with a letter)`, + `Invalid pluginId '${options.pluginId}', must match the pattern ${ID_PATTERN} (letters, digits, and dashes only, starting with a letter)`, ); } From 9a16824fdbb32f6c1c90234e291487fca7b9c79f Mon Sep 17 00:00:00 2001 From: Juan Pablo Garcia Ripa Date: Thu, 25 Sep 2025 20:34:04 +0200 Subject: [PATCH 04/14] fix: error messages on tests Signed-off-by: Juan Pablo Garcia Ripa --- .../backend-app-api/src/wiring/BackendInitializer.test.ts | 7 ++++--- packages/config-loader/src/sources/EnvConfigSource.ts | 5 +---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/packages/backend-app-api/src/wiring/BackendInitializer.test.ts b/packages/backend-app-api/src/wiring/BackendInitializer.test.ts index 7be989a0ea..79f0f36540 100644 --- a/packages/backend-app-api/src/wiring/BackendInitializer.test.ts +++ b/packages/backend-app-api/src/wiring/BackendInitializer.test.ts @@ -27,7 +27,8 @@ import { import { BackendInitializer } from './BackendInitializer'; import { mockServices } from '@backstage/backend-test-utils'; import { BackendStartupError } from './BackendStartupError'; -import { CONFIG_KEY_PART_PATTERN } from '@backstage/config'; + +const ID_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/i; const baseFactories = [ mockServices.rootLifecycle.factory(), @@ -1026,7 +1027,7 @@ describe('BackendInitializer', () => { }), ); await expect(init.start()).rejects.toThrow( - `Invalid pluginId 'test:invalid&id', must match the pattern ${CONFIG_KEY_PART_PATTERN} (letters, digits, dashes, and underscores only, starting with a letter)`, + `Invalid pluginId 'test:invalid&id', must match the pattern ${ID_PATTERN} (letters, digits, and dashes only, starting with a letter)`, ); }); @@ -1056,7 +1057,7 @@ describe('BackendInitializer', () => { }), ); await expect(init.start()).rejects.toThrow( - `Invalid moduleId 'invalid:module&id' for plugin 'test', must match the pattern ${CONFIG_KEY_PART_PATTERN} (letters, digits, dashes, and underscores only, starting with a letter)`, + `Invalid moduleId 'invalid:module&id' for plugin 'test', must match the pattern ${ID_PATTERN} (letters, digits, and dashes only, starting with a letter)`, ); }); diff --git a/packages/config-loader/src/sources/EnvConfigSource.ts b/packages/config-loader/src/sources/EnvConfigSource.ts index 988110b370..ffb615754f 100644 --- a/packages/config-loader/src/sources/EnvConfigSource.ts +++ b/packages/config-loader/src/sources/EnvConfigSource.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { AppConfig } from '@backstage/config'; +import { AppConfig, CONFIG_KEY_PART_PATTERN } from '@backstage/config'; import { assertError } from '@backstage/errors'; import { JsonObject } from '@backstage/types'; import { AsyncConfigSourceGenerator, ConfigSource } from './types'; @@ -84,9 +84,6 @@ export class EnvConfigSource implements ConfigSource { const ENV_PREFIX = 'APP_CONFIG_'; -// Update the same pattern in config package if this is changed -const CONFIG_KEY_PART_PATTERN = /^[a-z][a-z0-9]*(?:[-_:][a-z0-9]+)*$/i; - /** * Read runtime configuration from the environment. * From 7d15a665c20e4693e0f3ed238a3853f9a57cbb05 Mon Sep 17 00:00:00 2001 From: Juan Pablo Garcia Ripa Date: Tue, 21 Oct 2025 17:18:30 +0200 Subject: [PATCH 05/14] fix: bring back the colon to the regex Signed-off-by: Juan Pablo Garcia Ripa --- packages/config/src/constants.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/config/src/constants.ts b/packages/config/src/constants.ts index acd5c50961..35a1d3652b 100644 --- a/packages/config/src/constants.ts +++ b/packages/config/src/constants.ts @@ -19,9 +19,9 @@ * * @remarks * keys must only contain the letters `a` through `z` and digits, in groups separated by - * dashes or underscores. Additionally, the very first character of each such group + * dashes or underscores and colon. Additionally, the very first character of each such group * must be a letter, not a digit * * @public */ -export const CONFIG_KEY_PART_PATTERN = /^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*$/i; +export const CONFIG_KEY_PART_PATTERN = /^[a-z][a-z0-9]*(?:[-_:][a-z0-9]+)*$/i; From 26880203adcc47d8d9a97249b83c811d7d962c2f Mon Sep 17 00:00:00 2001 From: Juan Pablo Garcia Ripa Date: Fri, 21 Nov 2025 13:17:24 +0100 Subject: [PATCH 06/14] Apply suggestion from @freben MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Fredrik Adelöw Signed-off-by: Juan Pablo Garcia Ripa --- packages/backend-plugin-api/src/wiring/createBackendPlugin.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend-plugin-api/src/wiring/createBackendPlugin.ts b/packages/backend-plugin-api/src/wiring/createBackendPlugin.ts index d2aa633620..0116f885d1 100644 --- a/packages/backend-plugin-api/src/wiring/createBackendPlugin.ts +++ b/packages/backend-plugin-api/src/wiring/createBackendPlugin.ts @@ -52,7 +52,7 @@ export function createBackendPlugin( function getRegistrations() { if (!ID_PATTERN.test(options.pluginId)) { console.warn( - `WARNING: The pluginId '${options.pluginId}' will be invalid soon please must match the pattern ${ID_PATTERN} (letters, digits, and dashes only, starting with a letter)`, + `WARNING: The pluginId '${options.pluginId}' will be invalid soon, please change it to match the pattern ${ID_PATTERN} (letters, digits, and dashes only, starting with a letter)`, ); } if (!ID_PATTERN_OLD.test(options.pluginId)) { From 6f8636f3de5270569c93f1602e0ac757fc5b4dc7 Mon Sep 17 00:00:00 2001 From: Juan Pablo Garcia Ripa Date: Thu, 27 Nov 2025 23:53:05 +0100 Subject: [PATCH 07/14] fix: remove unnecessary changes, and clarify the pattern comments Signed-off-by: Juan Pablo Garcia Ripa --- packages/backend-plugin-api/src/wiring/constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/backend-plugin-api/src/wiring/constants.ts b/packages/backend-plugin-api/src/wiring/constants.ts index b5463d10da..8527802b99 100644 --- a/packages/backend-plugin-api/src/wiring/constants.ts +++ b/packages/backend-plugin-api/src/wiring/constants.ts @@ -19,7 +19,7 @@ * * @remarks * ids must only contain the letters `a` through `z` and digits, in groups separated by - * dashes. Additionally, the very first character of each such group + * dashes. Additionally, the very first character of the first group * must be a letter, not a digit * * @public From 5d49075e1f27a05979a3ba109a684d1a8c19c548 Mon Sep 17 00:00:00 2001 From: Juan Pablo Garcia Ripa Date: Sun, 25 Jan 2026 13:55:42 +0100 Subject: [PATCH 08/14] revert config packages changes Signed-off-by: Juan Pablo Garcia Ripa --- .../src/sources/EnvConfigSource.ts | 5 +++- packages/config/report.api.md | 3 --- packages/config/src/constants.ts | 27 ------------------- packages/config/src/index.ts | 1 - packages/config/src/reader.ts | 3 ++- 5 files changed, 6 insertions(+), 33 deletions(-) delete mode 100644 packages/config/src/constants.ts diff --git a/packages/config-loader/src/sources/EnvConfigSource.ts b/packages/config-loader/src/sources/EnvConfigSource.ts index ffb615754f..988110b370 100644 --- a/packages/config-loader/src/sources/EnvConfigSource.ts +++ b/packages/config-loader/src/sources/EnvConfigSource.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -import { AppConfig, CONFIG_KEY_PART_PATTERN } from '@backstage/config'; +import { AppConfig } from '@backstage/config'; import { assertError } from '@backstage/errors'; import { JsonObject } from '@backstage/types'; import { AsyncConfigSourceGenerator, ConfigSource } from './types'; @@ -84,6 +84,9 @@ export class EnvConfigSource implements ConfigSource { const ENV_PREFIX = 'APP_CONFIG_'; +// Update the same pattern in config package if this is changed +const CONFIG_KEY_PART_PATTERN = /^[a-z][a-z0-9]*(?:[-_:][a-z0-9]+)*$/i; + /** * Read runtime configuration from the environment. * diff --git a/packages/config/report.api.md b/packages/config/report.api.md index 2963960973..94c4652b44 100644 --- a/packages/config/report.api.md +++ b/packages/config/report.api.md @@ -43,9 +43,6 @@ export type Config = { getOptionalStringArray(key: string): string[] | undefined; }; -// @public -export const CONFIG_KEY_PART_PATTERN: RegExp; - // @public export class ConfigReader implements Config { constructor( diff --git a/packages/config/src/constants.ts b/packages/config/src/constants.ts deleted file mode 100644 index 35a1d3652b..0000000000 --- a/packages/config/src/constants.ts +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2025 The Backstage Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/** - * The pattern that config keys must match. - * - * @remarks - * keys must only contain the letters `a` through `z` and digits, in groups separated by - * dashes or underscores and colon. Additionally, the very first character of each such group - * must be a letter, not a digit - * - * @public - */ -export const CONFIG_KEY_PART_PATTERN = /^[a-z][a-z0-9]*(?:[-_:][a-z0-9]+)*$/i; diff --git a/packages/config/src/index.ts b/packages/config/src/index.ts index 2f3ade0189..c6254e97ba 100644 --- a/packages/config/src/index.ts +++ b/packages/config/src/index.ts @@ -29,4 +29,3 @@ export type { export { readDurationFromConfig } from './readDurationFromConfig'; export { ConfigReader } from './reader'; export type { AppConfig, Config } from './types'; -export { CONFIG_KEY_PART_PATTERN } from './constants'; diff --git a/packages/config/src/reader.ts b/packages/config/src/reader.ts index 18f010daac..8186ee8faa 100644 --- a/packages/config/src/reader.ts +++ b/packages/config/src/reader.ts @@ -17,7 +17,8 @@ import { JsonObject, JsonValue } from '@backstage/types'; import { AppConfig, Config } from './types'; -import { CONFIG_KEY_PART_PATTERN } from './constants'; +// Update the same pattern in config-loader package if this is changed +const CONFIG_KEY_PART_PATTERN = /^[a-z][a-z0-9]*(?:[-_:][a-z0-9]+)*$/i; function isObject(value: JsonValue | undefined): value is JsonObject { return typeof value === 'object' && value !== null && !Array.isArray(value); From bb9b471bd342e12790bf1aa3756b579484ff5e91 Mon Sep 17 00:00:00 2001 From: Juan Pablo Garcia Ripa Date: Sun, 25 Jan 2026 16:55:44 +0100 Subject: [PATCH 09/14] add plugin id format warning on frontend framework Signed-off-by: Juan Pablo Garcia Ripa --- .changeset/odd-lemons-occur.md | 5 ++++ .changeset/salty-frogs-cry.md | 5 ++++ ...y-sites-cheer.md => slow-numbers-study.md} | 3 -- .../architecture/08-naming-patterns.md | 8 ++--- .../src/wiring/constants.ts | 3 ++ .../src/wiring/createBackendModule.ts | 22 +++++++------- .../src/wiring/createBackendPlugin.ts | 22 +++++++------- .../src/wiring/constants.ts | 30 +++++++++++++++++++ .../src/wiring/createFrontendPlugin.ts | 8 +++++ 9 files changed, 77 insertions(+), 29 deletions(-) create mode 100644 .changeset/odd-lemons-occur.md create mode 100644 .changeset/salty-frogs-cry.md rename .changeset/{itchy-sites-cheer.md => slow-numbers-study.md} (57%) create mode 100644 packages/frontend-plugin-api/src/wiring/constants.ts diff --git a/.changeset/odd-lemons-occur.md b/.changeset/odd-lemons-occur.md new file mode 100644 index 0000000000..8f97ad0bd8 --- /dev/null +++ b/.changeset/odd-lemons-occur.md @@ -0,0 +1,5 @@ +--- +'@backstage/frontend-plugin-api': minor +--- + +new deprecation warninf will be throw by the framework pluginId should have letters, digits,and dashes, starting with a letter diff --git a/.changeset/salty-frogs-cry.md b/.changeset/salty-frogs-cry.md new file mode 100644 index 0000000000..b0f3a66ffa --- /dev/null +++ b/.changeset/salty-frogs-cry.md @@ -0,0 +1,5 @@ +--- +'@backstage/backend-app-api': patch +--- + +add test for new pluginId and moduleId format validations diff --git a/.changeset/itchy-sites-cheer.md b/.changeset/slow-numbers-study.md similarity index 57% rename from .changeset/itchy-sites-cheer.md rename to .changeset/slow-numbers-study.md index 54f5f2fadf..e53009bffa 100644 --- a/.changeset/itchy-sites-cheer.md +++ b/.changeset/slow-numbers-study.md @@ -1,8 +1,5 @@ --- '@backstage/backend-plugin-api': minor -'@backstage/backend-app-api': minor -'@backstage/config-loader': patch -'@backstage/config': patch --- The backend will now throw an error if a plugin or a module doesn't have a valid ID diff --git a/docs/backend-system/architecture/08-naming-patterns.md b/docs/backend-system/architecture/08-naming-patterns.md index b06d52c287..d073f3382c 100644 --- a/docs/backend-system/architecture/08-naming-patterns.md +++ b/docs/backend-system/architecture/08-naming-patterns.md @@ -11,10 +11,10 @@ As a rule, all names should be camel case, with the exceptions of plugin and mod ### Plugins -| Description | Pattern | Examples | Notes | -| ----------- | ----------------- | ------------------------------------- | -------------------------------------------------- | -| export | `Plugin` | `catalogPlugin`, `userSettingsPlugin` | | -| ID | `''` | `'catalog'`, `'user-settings'` | letters, digits,and dashes, starting with a letter | +| Description | Pattern | Examples | Notes | +| ----------- | ----------------- | ------------------------------------- | --------------------------------------------------- | +| export | `Plugin` | `catalogPlugin`, `userSettingsPlugin` | | +| ID | `''` | `'catalog'`, `'user-settings'` | letters, digits, and dashes, starting with a letter | Example: diff --git a/packages/backend-plugin-api/src/wiring/constants.ts b/packages/backend-plugin-api/src/wiring/constants.ts index 8527802b99..48f8e5a5c7 100644 --- a/packages/backend-plugin-api/src/wiring/constants.ts +++ b/packages/backend-plugin-api/src/wiring/constants.ts @@ -14,6 +14,9 @@ * limitations under the License. */ +// NOTE: changing any of these constants need to be reflected in +// @backstage/frontend-plugin-api/src/wiring/constants.ts as well + /** * The pattern that IDs must match. * diff --git a/packages/backend-plugin-api/src/wiring/createBackendModule.ts b/packages/backend-plugin-api/src/wiring/createBackendModule.ts index f65e11acae..6dcc88eb3f 100644 --- a/packages/backend-plugin-api/src/wiring/createBackendModule.ts +++ b/packages/backend-plugin-api/src/wiring/createBackendModule.ts @@ -55,18 +55,18 @@ export interface CreateBackendModuleOptions { export function createBackendModule( options: CreateBackendModuleOptions, ): BackendFeature { - function getRegistrations() { - if (!ID_PATTERN.test(options.moduleId)) { - console.warn( - `WARNING: The moduleId '${options.moduleId}' for plugin '${options.pluginId}', will be invalid soon please must match the pattern ${ID_PATTERN} (letters, digits, and dashes only, starting with a letter)`, - ); - } - if (!ID_PATTERN_OLD.test(options.moduleId)) { - throw new Error( - `Invalid moduleId '${options.moduleId}' for plugin '${options.pluginId}', must match the pattern ${ID_PATTERN} (letters, digits, and dashes only, starting with a letter)`, - ); - } + if (!ID_PATTERN.test(options.moduleId)) { + console.warn( + `WARNING: The moduleId '${options.moduleId}' for plugin '${options.pluginId}', will be invalid soon, please change it to match the pattern ${ID_PATTERN} (letters, digits, and dashes only, starting with a letter)`, + ); + } + if (!ID_PATTERN_OLD.test(options.moduleId)) { + throw new Error( + `Invalid moduleId '${options.moduleId}' for plugin '${options.pluginId}', must match the pattern ${ID_PATTERN} (letters, digits, and dashes only, starting with a letter)`, + ); + } + function getRegistrations() { const extensionPoints: InternalBackendPluginRegistration['extensionPoints'] = []; let init: InternalBackendModuleRegistration['init'] | undefined = undefined; diff --git a/packages/backend-plugin-api/src/wiring/createBackendPlugin.ts b/packages/backend-plugin-api/src/wiring/createBackendPlugin.ts index 0116f885d1..1a3d3d4a68 100644 --- a/packages/backend-plugin-api/src/wiring/createBackendPlugin.ts +++ b/packages/backend-plugin-api/src/wiring/createBackendPlugin.ts @@ -49,18 +49,18 @@ export interface CreateBackendPluginOptions { export function createBackendPlugin( options: CreateBackendPluginOptions, ): BackendFeature { - function getRegistrations() { - if (!ID_PATTERN.test(options.pluginId)) { - console.warn( - `WARNING: The pluginId '${options.pluginId}' will be invalid soon, please change it to match the pattern ${ID_PATTERN} (letters, digits, and dashes only, starting with a letter)`, - ); - } - if (!ID_PATTERN_OLD.test(options.pluginId)) { - throw new Error( - `Invalid pluginId '${options.pluginId}', must match the pattern ${ID_PATTERN} (letters, digits, and dashes only, starting with a letter)`, - ); - } + if (!ID_PATTERN.test(options.pluginId)) { + console.warn( + `WARNING: The pluginId '${options.pluginId}' will be invalid soon, please change it to match the pattern ${ID_PATTERN} (letters, digits, and dashes only, starting with a letter)`, + ); + } + if (!ID_PATTERN_OLD.test(options.pluginId)) { + throw new Error( + `Invalid pluginId '${options.pluginId}', must match the pattern ${ID_PATTERN} (letters, digits, and dashes only, starting with a letter)`, + ); + } + function getRegistrations() { const extensionPoints: InternalBackendPluginRegistration['extensionPoints'] = []; let init: InternalBackendPluginRegistration['init'] | undefined = undefined; diff --git a/packages/frontend-plugin-api/src/wiring/constants.ts b/packages/frontend-plugin-api/src/wiring/constants.ts new file mode 100644 index 0000000000..5300cfc27f --- /dev/null +++ b/packages/frontend-plugin-api/src/wiring/constants.ts @@ -0,0 +1,30 @@ +/* + * Copyright 2025 The Backstage Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// NOTE: changing any of these constants need to be reflected in +// @backstage/backend-plugin-api/src/wiring/constants.ts as well + +/** + * The pattern that IDs must match. + * + * @remarks + * ids must only contain the letters `a` through `z` and digits, in groups separated by + * dashes. Additionally, the very first character of the first group + * must be a letter, not a digit + * + * @public + */ +export const ID_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/i; diff --git a/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.ts b/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.ts index 68b5826baa..ebd4142db3 100644 --- a/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.ts +++ b/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.ts @@ -30,6 +30,7 @@ import { FeatureFlagConfig } from './types'; import { MakeSortedExtensionsMap } from './MakeSortedExtensionsMap'; import { JsonObject } from '@backstage/types'; import { RouteRef, SubRouteRef, ExternalRouteRef } from '../routing'; +import { ID_PATTERN } from './constants'; /** * Information about the plugin. @@ -199,6 +200,13 @@ export function createFrontendPlugin< > { const pluginId = options.pluginId; + if (!ID_PATTERN.test(pluginId)) { + // eslint-disable-next-line no-console + console.warn( + `WARNING: The pluginId '${pluginId}' will be invalid soon, please change it to match the pattern ${ID_PATTERN} (letters, digits, and dashes only, starting with a letter)`, + ); + } + const extensions = new Array>(); const extensionDefinitionsById = new Map< string, From bd7d15dd7ee86715d12546a28a1655cb1480670b Mon Sep 17 00:00:00 2001 From: Juan Pablo Garcia Ripa Date: Mon, 26 Jan 2026 00:04:49 +0100 Subject: [PATCH 10/14] add some test for invalid ids Signed-off-by: Juan Pablo Garcia Ripa --- .../src/wiring/BackendInitializer.test.ts | 49 ------------------- .../src/wiring/createBackendModule.test.ts | 17 +++++++ .../src/wiring/createBackendPlugin.test.ts | 16 ++++++ .../src/wiring/createFrontendPlugin.test.ts | 11 +++++ 4 files changed, 44 insertions(+), 49 deletions(-) diff --git a/packages/backend-app-api/src/wiring/BackendInitializer.test.ts b/packages/backend-app-api/src/wiring/BackendInitializer.test.ts index 79f0f36540..1c0bf5a0ef 100644 --- a/packages/backend-app-api/src/wiring/BackendInitializer.test.ts +++ b/packages/backend-app-api/src/wiring/BackendInitializer.test.ts @@ -28,8 +28,6 @@ import { BackendInitializer } from './BackendInitializer'; import { mockServices } from '@backstage/backend-test-utils'; import { BackendStartupError } from './BackendStartupError'; -const ID_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/i; - const baseFactories = [ mockServices.rootLifecycle.factory(), mockServices.lifecycle.factory(), @@ -1013,53 +1011,6 @@ describe('BackendInitializer', () => { "Service or extension point dependencies of module 'test-mod' for plugin 'test' are missing for the following ref(s): serviceRef{a}", ); }); - it('should reject plugins with invalid pluginId', async () => { - const init = new BackendInitializer(baseFactories); - init.add( - createBackendPlugin({ - pluginId: 'test:invalid&id', - register(reg) { - reg.registerInit({ - deps: {}, - async init() {}, - }); - }, - }), - ); - await expect(init.start()).rejects.toThrow( - `Invalid pluginId 'test:invalid&id', must match the pattern ${ID_PATTERN} (letters, digits, and dashes only, starting with a letter)`, - ); - }); - - it('should reject modules with invalid moduleId', async () => { - const init = new BackendInitializer(baseFactories); - init.add( - createBackendPlugin({ - pluginId: 'test', - register(reg) { - reg.registerInit({ - deps: {}, - async init() {}, - }); - }, - }), - ); - init.add( - createBackendModule({ - pluginId: 'test', - moduleId: 'invalid:module&id', - register(reg) { - reg.registerInit({ - deps: {}, - async init() {}, - }); - }, - }), - ); - await expect(init.start()).rejects.toThrow( - `Invalid moduleId 'invalid:module&id' for plugin 'test', must match the pattern ${ID_PATTERN} (letters, digits, and dashes only, starting with a letter)`, - ); - }); it('should properly load double-default CJS modules', async () => { expect.assertions(3); diff --git a/packages/backend-plugin-api/src/wiring/createBackendModule.test.ts b/packages/backend-plugin-api/src/wiring/createBackendModule.test.ts index 4d927c86f6..20b3599148 100644 --- a/packages/backend-plugin-api/src/wiring/createBackendModule.test.ts +++ b/packages/backend-plugin-api/src/wiring/createBackendModule.test.ts @@ -15,6 +15,7 @@ */ import { createServiceRef } from '../services'; +import { ID_PATTERN } from './constants'; import { createBackendModule } from './createBackendModule'; import { createExtensionPoint } from './createExtensionPoint'; import { InternalBackendRegistrations } from './types'; @@ -77,4 +78,20 @@ describe('createBackendModule', () => { expect(plugin.$$type).toEqual('@backstage/BackendFeature'); }); + it('should reject modules with invalid moduleId', async () => { + expect(() => + createBackendModule({ + pluginId: 'test', + moduleId: 'invalid:module&id', + register(reg) { + reg.registerInit({ + deps: {}, + async init() {}, + }); + }, + }), + ).toThrow( + `Invalid moduleId 'invalid:module&id' for plugin 'test', must match the pattern ${ID_PATTERN} (letters, digits, and dashes only, starting with a letter)`, + ); + }); }); diff --git a/packages/backend-plugin-api/src/wiring/createBackendPlugin.test.ts b/packages/backend-plugin-api/src/wiring/createBackendPlugin.test.ts index 5acd8e46d7..4759bd2754 100644 --- a/packages/backend-plugin-api/src/wiring/createBackendPlugin.test.ts +++ b/packages/backend-plugin-api/src/wiring/createBackendPlugin.test.ts @@ -15,6 +15,7 @@ */ import { createServiceRef } from '../services'; +import { ID_PATTERN } from './constants'; import { createBackendPlugin } from './createBackendPlugin'; import { createExtensionPoint } from './createExtensionPoint'; import { InternalBackendRegistrations } from './types'; @@ -90,4 +91,19 @@ describe('createBackendPlugin', () => { expect(plugin.$$type).toEqual('@backstage/BackendFeature'); }); + it('should reject plugins with invalid pluginId', async () => { + expect(() => + createBackendPlugin({ + pluginId: 'test:invalid&id', + register(reg) { + reg.registerInit({ + deps: {}, + async init() {}, + }); + }, + }), + ).toThrow( + `Invalid pluginId 'test:invalid&id', must match the pattern ${ID_PATTERN} (letters, digits, and dashes only, starting with a letter)`, + ); + }); }); diff --git a/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.test.ts b/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.test.ts index a3226721a1..1e1f7e1a84 100644 --- a/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.test.ts +++ b/packages/frontend-plugin-api/src/wiring/createFrontendPlugin.test.ts @@ -142,6 +142,17 @@ describe('createFrontendPlugin', () => { expect(String(plugin)).toBe('Plugin{id=test}'); }); + it('should warn about invalid plugin IDs', () => { + const consoleWarn = jest + .spyOn(console, 'warn') + .mockImplementation(() => {}); + createFrontendPlugin({ pluginId: 'invalid&id' }); + expect(consoleWarn).toHaveBeenCalledWith( + expect.stringContaining("The pluginId 'invalid&id' will be invalid soon"), + ); + consoleWarn.mockRestore(); + }); + it('should create a plugin with extension instances', async () => { const plugin = createFrontendPlugin({ pluginId: 'test', From 71aa49196bd6c6822b1b3db3cb786ad7bc1f08cb Mon Sep 17 00:00:00 2001 From: Juan Pablo Garcia Ripa Date: Mon, 26 Jan 2026 00:11:33 +0100 Subject: [PATCH 11/14] spelling fixes Signed-off-by: Juan Pablo Garcia Ripa --- .changeset/odd-lemons-occur.md | 2 +- .changeset/salty-frogs-cry.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.changeset/odd-lemons-occur.md b/.changeset/odd-lemons-occur.md index 8f97ad0bd8..fbbdc760f6 100644 --- a/.changeset/odd-lemons-occur.md +++ b/.changeset/odd-lemons-occur.md @@ -2,4 +2,4 @@ '@backstage/frontend-plugin-api': minor --- -new deprecation warninf will be throw by the framework pluginId should have letters, digits,and dashes, starting with a letter +new deprecation warning will be throw by the framework `pluginId` should have letters, digits,and dashes, starting with a letter diff --git a/.changeset/salty-frogs-cry.md b/.changeset/salty-frogs-cry.md index b0f3a66ffa..bd3c187ff5 100644 --- a/.changeset/salty-frogs-cry.md +++ b/.changeset/salty-frogs-cry.md @@ -2,4 +2,4 @@ '@backstage/backend-app-api': patch --- -add test for new pluginId and moduleId format validations +Add test for new `pluginId` and `moduleId` format validations From 8ee1f4b6dd39086f14cd9ce793c39a891901c630 Mon Sep 17 00:00:00 2001 From: Juan Pablo Garcia Ripa Date: Mon, 26 Jan 2026 00:13:54 +0100 Subject: [PATCH 12/14] changes from @backstage/backend-app-api removed Signed-off-by: Juan Pablo Garcia Ripa --- .changeset/salty-frogs-cry.md | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 .changeset/salty-frogs-cry.md diff --git a/.changeset/salty-frogs-cry.md b/.changeset/salty-frogs-cry.md deleted file mode 100644 index bd3c187ff5..0000000000 --- a/.changeset/salty-frogs-cry.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@backstage/backend-app-api': patch ---- - -Add test for new `pluginId` and `moduleId` format validations From fcaea9208d336288fa8baee89bdeabcf4daae522 Mon Sep 17 00:00:00 2001 From: Juan Pablo Garcia Ripa Date: Mon, 26 Jan 2026 09:53:13 +0100 Subject: [PATCH 13/14] Update changes message Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Juan Pablo Garcia Ripa --- .changeset/odd-lemons-occur.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.changeset/odd-lemons-occur.md b/.changeset/odd-lemons-occur.md index fbbdc760f6..2b536bf012 100644 --- a/.changeset/odd-lemons-occur.md +++ b/.changeset/odd-lemons-occur.md @@ -2,4 +2,4 @@ '@backstage/frontend-plugin-api': minor --- -new deprecation warning will be throw by the framework `pluginId` should have letters, digits,and dashes, starting with a letter +new deprecation warning will be thrown by the framework `pluginId` should have letters, digits, and dashes, starting with a letter From 5d71d7e0b45993a29a123a62d8d2c71182d329ef Mon Sep 17 00:00:00 2001 From: Patrik Oldsberg Date: Mon, 2 Feb 2026 19:41:24 +0100 Subject: [PATCH 14/14] Apply suggestions from code review Signed-off-by: Patrik Oldsberg --- .changeset/odd-lemons-occur.md | 2 +- .changeset/slow-numbers-study.md | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.changeset/odd-lemons-occur.md b/.changeset/odd-lemons-occur.md index 2b536bf012..695ed3c80c 100644 --- a/.changeset/odd-lemons-occur.md +++ b/.changeset/odd-lemons-occur.md @@ -2,4 +2,4 @@ '@backstage/frontend-plugin-api': minor --- -new deprecation warning will be thrown by the framework `pluginId` should have letters, digits, and dashes, starting with a letter +Plugin IDs that do not match the standard format are deprecated (letters, digits, and dashes only, starting with a letter). Plugin IDs that do no match this format will be rejected in a future release. diff --git a/.changeset/slow-numbers-study.md b/.changeset/slow-numbers-study.md index e53009bffa..fe7203e169 100644 --- a/.changeset/slow-numbers-study.md +++ b/.changeset/slow-numbers-study.md @@ -2,4 +2,6 @@ '@backstage/backend-plugin-api': minor --- -The backend will now throw an error if a plugin or a module doesn't have a valid ID +Plugin IDs that do not match the standard format are deprecated (letters, digits, and dashes only, starting with a letter). Plugin IDs that do no match this format will be rejected in a future release. + +In addition, plugin IDs that don't match the legacy pattern that also allows underscores, with be rejected.