Skip to content

fix(backend-dynamic-feature-service): fall back to main from alpha#34535

Merged
freben merged 1 commit into
backstage:masterfrom
gashcrumb:backend-entrypoint-evaluation
Jun 16, 2026
Merged

fix(backend-dynamic-feature-service): fall back to main from alpha#34535
freben merged 1 commit into
backstage:masterfrom
gashcrumb:backend-entrypoint-evaluation

Conversation

@gashcrumb

Copy link
Copy Markdown
Contributor

Hey, I just made a Pull Request!

Dynamic backend plugins with an alpha package.json failed to load when the alpha export only exposed supplementary APIs, even if the main export was valid. The loader now tries the alpha entry first and falls back to the main export when no valid plugin installer is found.

Without this fix, attempting to load plugins such as catalog-backend using the backend dynamic feature service will currently fail with an error similar to following message:

dynamic backend plugin '@backstage/plugin-catalog-backend-dynamic' could not be loaded from 'file:///opt/app-root/src/dynamic-plugins-root/backstage-plugin-catalog-backend/alpha': the module should either export a 'BackendFeature' or 'BackendFeatureFactory' as default export, or export a 'const dynamicPluginInstaller: BackendDynamicPluginInstaller' field as dynamic loading entrypoint.

Assisted-By: Cursor Desktop

✔️ Checklist

  • A changeset describing the change and affected packages. (more info)
  • Added or updated documentation
  • Tests for new functionality and regression tests for bug fixes
  • Screenshots attached (for UI changes)
  • All your commits have a Signed-off-by line in the message. (more info)

Copilot AI review requested due to automatic review settings June 10, 2026 13:33
@gashcrumb gashcrumb requested a review from a team as a code owner June 10, 2026 13:33
@backstage-goalie

Copy link
Copy Markdown
Contributor

Changed Packages

Package Name Package Path Changeset Bump Current Version
@backstage/backend-dynamic-feature-service packages/backend-dynamic-feature-service patch v0.8.3-next.0

@backstage-goalie backstage-goalie Bot added size:medium Medium-sized pull requests receive no change in priority for reviews. waiting-for:review The PR needs a review and will be visible in the review queue unless already assigned an owner. labels Jun 10, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Fixes dynamic backend plugin loading so that when an alpha package.json exists but doesn’t expose a dynamic plugin entrypoint, the loader falls back to the main package export instead of marking the plugin as failed.

Changes:

  • Refactors plugin loading to try multiple entrypoints (alpha first, then main) and resolves installers via a helper.
  • Adds unit + integration coverage for the alpha-fallback behavior.
  • Adds a fixture dynamic plugin that has an alpha export with only supplementary APIs plus a changeset entry.

Reviewed changes

Copilot reviewed 6 out of 8 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
packages/backend-dynamic-feature-service/src/manager/plugin-manager.ts Implements “try alpha then main” entrypoint loading and factors installer resolution into helpers.
packages/backend-dynamic-feature-service/src/manager/plugin-manager.test.ts Adds a unit test ensuring fallback to main when alpha doesn’t provide an entrypoint.
packages/backend-dynamic-feature-service/src/features/features.test.ts Adds an integration test validating fallback behavior and associated logs/scanned package metadata.
packages/backend-dynamic-feature-service/src/features/fixtures/dynamic-plugins-root-for-alpha-fallback/test-backend-alpha-fallback-dynamic/package.json Adds fixture plugin package manifest with main + alpha export mappings.
packages/backend-dynamic-feature-service/src/features/fixtures/dynamic-plugins-root-for-alpha-fallback/test-backend-alpha-fallback-dynamic/dist/index.cjs.js Fixture main entry exporting a backend plugin.
packages/backend-dynamic-feature-service/src/features/fixtures/dynamic-plugins-root-for-alpha-fallback/test-backend-alpha-fallback-dynamic/dist/alpha.cjs.js Fixture alpha entry exporting only supplementary APIs (no installer).
packages/backend-dynamic-feature-service/src/features/fixtures/dynamic-plugins-root-for-alpha-fallback/test-backend-alpha-fallback-dynamic/alpha/package.json Fixture alpha package.json pointing at the alpha bundle.
.changeset/fix-dynamic-plugin-alpha-fallback.md Documents the patch fix and expected loader behavior.

Comment thread packages/backend-dynamic-feature-service/src/manager/plugin-manager.ts Outdated
Comment thread packages/backend-dynamic-feature-service/src/features/features.test.ts Outdated
Dynamic backend plugins with an alpha package.json failed to load when the
alpha export only exposed supplementary APIs, even if the main export was
valid. The loader now tries the alpha entry first and falls back to the main
export when no valid plugin installer is found.

Signed-off-by: Stan Lewis <gashcrumb@gmail.com>
Assisted-By: Cursor Desktop

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 6 out of 8 changed files in this pull request and generated no new comments.

@schultzp2020 schultzp2020 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛶

@backstage-goalie backstage-goalie Bot added the reviewer-approved The pull request has been approved by a member of the reviewers group and has much higher priority. label Jun 10, 2026
@freben freben self-assigned this Jun 10, 2026
gashcrumb added a commit to gashcrumb/rhdh that referenced this pull request Jun 11, 2026
This change adds a temporary patch to update the backend dynamic feature
service so that it will also load backend plugins that have an alpha
export but do not export the plugin object in that alpha export.  The
patch is similar to the fix applied in this PR:

backstage/backstage#34535

Assisted-By: Cursor Desktop
gashcrumb added a commit to gashcrumb/rhdh that referenced this pull request Jun 11, 2026
This change adds a temporary patch to update the backend dynamic feature
service so that it will also load backend plugins that have an alpha
export but do not export the plugin object in that alpha export.  The
patch is similar to the fix applied in this PR:

backstage/backstage#34535

Assisted-By: Cursor Desktop
gashcrumb added a commit to gashcrumb/rhdh that referenced this pull request Jun 12, 2026
This change adds a temporary patch to update the backend dynamic feature
service so that it will also load backend plugins that have an alpha
export but do not export the plugin object in that alpha export.  The
patch is similar to the fix applied in this PR:

backstage/backstage#34535

Assisted-By: Cursor Desktop
gashcrumb added a commit to gashcrumb/rhdh that referenced this pull request Jun 12, 2026
This change adds a temporary patch to update the backend dynamic feature
service so that it will also load backend plugins that have an alpha
export but do not export the plugin object in that alpha export.  The
patch is similar to the fix applied in this PR:

backstage/backstage#34535

Assisted-By: Cursor Desktop
gashcrumb added a commit to gashcrumb/rhdh that referenced this pull request Jun 12, 2026
This change adds a temporary patch to update the backend dynamic feature
service so that it will also load backend plugins that have an alpha
export but do not export the plugin object in that alpha export.  The
patch is similar to the fix applied in this PR:

backstage/backstage#34535

Assisted-By: Cursor Desktop

@freben freben left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, since this is a valuable fix let's merge this with some comments for potential follow-up cleanup. I think it's correct at runtime, which is what's most important.

},
];

if (entryPoints.length === 0) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can actually never happen now

if (dynamicPlugin.installer) {
const entryPoints: Array<{ location: URL; manifest: string }> = [
...(plugin.alphaManifest?.main
? [

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could have looked clearer with an if and a push, instead of the ternary+spread

return dynamicPlugin;
}

return dynamicPlugin;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this can technically happen now either, right? The code above seems to always do either continue or throw.

Comment on lines +369 to +372
): Promise<{
installer?: BackendDynamicPlugin['installer'];
error?: Error;
}> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could have been a discriminated union if we wanted some extra clarity

}

if (!loadResult.error && index < entryPoints.length - 1) {
continue;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps a matter of taste, but continue is a bit of a minor smell that can often be rewritten more readably. I think in this case it shadows the fact that the final return can't happen for example.

The overall structure could probably have been

let outcome = load(first);

if (condition && second) {
  outcome = load(second);
}

if (outcome is error) ...

return

@backstage-goalie backstage-goalie Bot added waiting-for:merge The PR has been approved and is awaiting merge. and removed waiting-for:review The PR needs a review and will be visible in the review queue unless already assigned an owner. labels Jun 16, 2026
@freben freben merged commit 69e949d into backstage:master Jun 16, 2026
27 checks passed
@github-actions

Copy link
Copy Markdown
Contributor

Thank you for contributing to Backstage! The changes in this pull request will be part of the 1.52.0 release, scheduled for Tue, 16 Jun 2026.

@gashcrumb

Copy link
Copy Markdown
Contributor Author

Alright, since this is a valuable fix let's merge this with some comments for potential follow-up cleanup. I think it's correct at runtime, which is what's most important.

@freben Sounds good! I'll prepare an update that incorporates your feedback, thanks!

gashcrumb added a commit to gashcrumb/rhdh that referenced this pull request Jun 16, 2026
This change adds a temporary patch to update the backend dynamic feature
service so that it will also load backend plugins that have an alpha
export but do not export the plugin object in that alpha export.  The
patch is similar to the fix applied in this PR:

backstage/backstage#34535

Assisted-By: Cursor Desktop
gashcrumb added a commit to gashcrumb/backstage that referenced this pull request Jun 16, 2026
…loading

Follow-up cleanup after backstage#34535. Use a discriminated union for load
results, try alpha before falling back to main, and remove the loop
with unreachable branches.

Assisted-By: Cursor Desktop
Signed-off-by: Stan Lewis <gashcrumb@gmail.com>
@gashcrumb

Copy link
Copy Markdown
Contributor Author

Have created this PR to address the feedback, think it definitely reads a lot better. Thanks!

gashcrumb added a commit to gashcrumb/backstage that referenced this pull request Jun 16, 2026
…loading

Follow-up cleanup after backstage#34535. Use a discriminated union for load
results, try alpha before falling back to main, and remove the loop
with unreachable branches.

Assisted-By: Cursor Desktop
Signed-off-by: Stan Lewis <gashcrumb@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

reviewer-approved The pull request has been approved by a member of the reviewers group and has much higher priority. size:medium Medium-sized pull requests receive no change in priority for reviews. waiting-for:merge The PR has been approved and is awaiting merge.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants