Merge branch 'master' into migrating_k8s_frontend

Signed-off-by: Nurit Izrailov <38442135+nuritizra@users.noreply.github.com>
This commit is contained in:
Nurit Izrailov
2024-07-23 11:44:45 -04:00
committed by GitHub
465 changed files with 8763 additions and 1917 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/cli': patch
---
Use ES2022 in CLI bundler
+7
View File
@@ -0,0 +1,7 @@
---
'@backstage/plugin-catalog-react': patch
'@backstage/plugin-search-react': patch
'@backstage/plugin-home': patch
---
Updated alpha definitions of extension data references.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/core-components': patch
---
Move the `Link` component to the `RoutedTabs` instead of the `HeaderTabs` component
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-scaffolder-react': patch
---
Add ability to customise form fields in the UI by exposing `uiSchema` and `formContext` in `FormProps`
+18
View File
@@ -0,0 +1,18 @@
---
'@backstage/frontend-plugin-api': patch
---
Added a new `IconBundleBlueprint` that lets you create icon bundle extensions that can be installed in an App in order to override or add new app icons.
```tsx
import { IconBundleBlueprint } from '@backstage/frontend-plugin-api';
const exampleIconBundle = IconBundleBlueprint.make({
name: 'example-bundle',
params: {
icons: {
user: MyOwnUserIcon,
},
},
});
```
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/frontend-app-api': patch
---
Support icon overriding with the new `IconBundleBlueprint` API.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/backend-defaults': patch
---
The `createHealthRouter` utility that allows you to create a health check router is now exported via `@backstage/backend-defaults/rootHttpRouter`.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/cli': patch
---
Updated default backend plugin to use `RootConfigService` instead of `Config`. This also removes the dependency on `@backstage/config` as it's no longer used.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-graph': patch
---
Memoize entity graph nodes when applying an `entityFilter` to prevent repeated redraws
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/cli': patch
---
The experimental module federation build now has the ability to force the use of development versions of `react` and `react-dom` by setting the `FORCE_REACT_DEVELOPMENT` flag.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-scaffolder-react': patch
---
support `ajv-errors` for scaffolder validation to allow for customizing the error messages
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-devtools-backend': patch
---
Removed unused code for lockfile analysis.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-auth-backend-module-microsoft-provider': patch
---
Updated the Microsoft authenticator to accurately define required scopes, but to also omit the required and additional scopes when requesting resource scopes.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-search-backend-module-pg': patch
---
Removing `@backstage/backend-app-api` dependency
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/cli': patch
---
Remove usage of deprecated functionality from @backstage/config-loader
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-scaffolder-backend-module-gitlab': patch
---
Added test cases for gitlab:issue:edit examples
+51 -1
View File
@@ -188,5 +188,55 @@
"@backstage/plugin-user-settings-backend": "0.2.21",
"@backstage/plugin-user-settings-common": "0.0.1"
},
"changesets": []
"changesets": [
"blue-pumas-cheer",
"bright-trainers-brake",
"chilly-trains-sleep",
"cool-insects-remember",
"cuddly-zebras-crash",
"dry-squids-tap",
"dull-ghosts-double",
"early-trees-dance",
"eighty-mirrors-flow",
"flat-plums-grow",
"forty-ties-agree",
"forty-ties-disagree",
"friendly-feet-refuse",
"gentle-dryers-smile",
"grumpy-owls-suffer",
"heavy-numbers-love",
"hip-hairs-exist",
"late-games-protect",
"light-pianos-exercise",
"little-bulldogs-guess",
"little-suns-fly",
"mighty-dolls-retire",
"modern-parrots-protect",
"neat-gifts-join",
"new-scissors-try",
"nice-peas-retire",
"nine-cherries-decide",
"nine-glasses-nail",
"ninety-icons-smile",
"pink-gorillas-brake",
"plenty-tools-exist",
"rich-mugs-dress",
"selfish-bees-think",
"six-rats-kick",
"slow-toes-jog",
"small-spoons-shout",
"soft-gorillas-refuse",
"spotty-planets-accept",
"strange-papayas-beg",
"strong-otters-compete",
"stupid-dots-relate",
"swift-kings-sparkle",
"thirty-paws-hope",
"tiny-oranges-pretend",
"tough-lies-repair",
"violet-jokes-wave",
"wicked-bobcats-teach",
"wild-eggs-exist",
"witty-bears-behave"
]
}
+7
View File
@@ -0,0 +1,7 @@
---
'@backstage/config-loader': patch
---
Add boolean `allowMissingDefaultConfig` option to `ConfigSources.default` and
`ConfigSources.defaultForTargets`, which results in omission of a ConfigSource
for the default app-config.yaml configuration file if it's not present.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-scaffolder-backend-module-gitlab': patch
---
Added test cases for gitlab:projectAccessToken:create example
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-home': patch
---
Fixed a bug on the WelcomeTitle component where the welcome message wasn't correct when the language was set to Spanish
+13
View File
@@ -0,0 +1,13 @@
---
'@backstage/cli': minor
---
**BREAKING**: The lockfile (`yarn.lock`) dependency analysis and mutations have been removed from several commands.
The `versions:bump` command will no longer attempt to bump and deduplicate dependencies by modifying the lockfile, it will only update `package.json` files.
The `versions:check` command has been removed, since its only purpose was verification and mutation of the lockfile. We recommend using the `yarn dedupe` command instead, or the `yarn-deduplicate` package if you're using Yarn classic.
The check that was built into the `package start` command has been removed, it will no longer warn about lockfile mismatches.
The packages in the Backstage ecosystem handle package duplications much better now than when these CLI features were first introduced, so the need for these features has diminished. By removing them, we drastically reduce the integration between the Backstage CLI and Yarn, making it much easier to add support for other package managers in the future.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/backend-test-utils': patch
---
The default services for `startTestBackend` and `ServiceFactoryTester` now includes the Root Health Service.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-scaffolder': patch
---
Cleaned up codebase of RepoUrlPicker
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-scaffolder-backend-module-gitlab': patch
---
Added test cases for gitlab:pipeline:trigger examples
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/cli': patch
---
Add support for dynamic plugins via the EXPERIMENTAL_MODULE_FEDERATION environment variable when running `yarn start`.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/core-app-api': patch
---
The request to delete the session cookie when running the app in protected mode is now done with a plain `fetch` rather than `FetchApi`. This fixes a bug where the app would immediately try to sign-in again when removing the cookie during logout.
+19
View File
@@ -0,0 +1,19 @@
---
'@backstage/frontend-plugin-api': patch
---
Extension data references can now be defined in a way that encapsulates the ID string in the type, in addition to the data type itself. The old way of creating extension data references is deprecated and will be removed in a future release.
For example, the following code:
```ts
export const myExtension = createExtensionDataRef<MyType>('my-plugin.my-data');
```
Should be updated to the following:
```ts
export const myExtension = createExtensionDataRef<MyType>().with({
id: 'my-plugin.my-data',
});
```
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-scaffolder': patch
---
Fix issue with `RepoUrlPicker` not refreshing the credentials for a different host
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/cli': patch
---
Fix for `repo build --all` not properly detecting the experimental public entry point.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-auth-react': patch
---
feat: Hide visibility of CookieAuthRedirect
+10
View File
@@ -0,0 +1,10 @@
---
'@backstage/plugin-scaffolder-backend-module-gitlab': patch
---
Add custom action for merge request: **auto**
The **Auto** action selects the committed action between _create_ and _update_.
The **Auto** action fetches files using the **/projects/repository/tree endpoint**.
After fetching, it checks if the file exists locally and in the repository. If it does, it chooses **update**; otherwise, it chooses **create**.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/integration': patch
---
Updated functions for `getHarnessEditContentsUrl`, `getHarnessFileContentsUrl`, `getHarnessArchiveUrl`, `getHarnessLatestCommitUrl` and `parseHarnessUrl` to handle account and org level urls
@@ -7,6 +7,7 @@ ADRs
airbrake
Airbrake
Airbrakes
ajv
Alaria
Alef
allowlisted
@@ -84,7 +85,9 @@ debounce
Debounce
debuggability
declaratively
deduplicate
deduplicated
deduplication
deliverables
denormalized
dependabot
Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

+1 -3
View File
@@ -67,8 +67,6 @@ auth:
clientSecret: ${AZURE_CLIENT_SECRET}
tenantId: ${AZURE_TENANT_ID}
domainHint: ${AZURE_TENANT_ID}
additionalScopes:
- Mail.Send
signIn:
resolvers:
# typically you would pick one of these
@@ -86,7 +84,7 @@ The Microsoft provider is a structure with three mandatory configuration keys:
Leave blank if your app registration is multi tenant.
When specified, this reduces login friction for users with accounts in multiple tenants by automatically filtering away accounts from other tenants.
For more details, see [Home Realm Discovery](https://learn.microsoft.com/en-us/azure/active-directory/manage-apps/home-realm-discovery-policy)
- `additionalScopes` (optional): List of scopes for the App Registration. The default and mandatory value is ['user.read'].
- `additionalScopes` (optional): List of scopes for the App Registration, to be requested in addition to the required ones.
### Resolvers
@@ -26,6 +26,25 @@ parameters:
ui:help: 'Hint: additional description...'
```
#### Custom validation error message
```yaml
parameters:
- title: Fill in some steps
properties:
name:
title: Simple text input
type: string
description: Description about input
maxLength: 8
pattern: '^([a-zA-Z][a-zA-Z0-9]*)(-[a-zA-Z0-9]+)*$'
ui:autofocus: true
ui:help: 'Hint: additional description...'
errorMessage:
properties:
name: '1-8 alphanumeric tokens (first starts with letter) delimited by -'
```
### Multi line text input
```yaml
@@ -533,6 +533,11 @@ catalogFilter:
metadata.annotations.github.com/team-slug: { exists: true }
```
#### Custom validation messages
You may specify custom JSON Schema validation messages as supported by the
[ajv-errors](https://github.com/ajv-validator/ajv-errors) plugin library to [ajv](https://github.com/ajv-validator/ajv).
## `spec.steps` - `Action[]`
The `steps` is an array of the things that you want to happen part of this
@@ -91,7 +91,9 @@ To create a new extension data reference to represent a type of shared extension
```ts
export const reactElementExtensionDataRef =
createExtensionDataRef<React.JSX.Element>('my-plugin.reactElement');
createExtensionDataRef<React.JSX.Element>().with({
id: 'my-plugin.reactElement',
});
```
The `ExtensionDataRef` can then be used to describe an output property of the extension. This will enforce typing on the return value of the extension factory:
@@ -98,9 +98,9 @@ export interface SearchResultItemExtensionData {
}
export const searchResultItemExtensionDataRef =
createExtensionDataRef<SearchResultItemExtensionData>(
'search.search-result-item',
);
createExtensionDataRef<SearchResultItemExtensionData>().with({
id: 'search.search-result-item',
});
```
#### Grouped Extension Data
@@ -109,8 +109,12 @@ This way of defining extension data is similar to the standalone way, but it use
```ts
export const coreExtensionData = {
reactElement: createExtensionDataRef<ReactElement>('core.react-element'),
routePath: createExtensionDataRef<string>('core.route-path'),
reactElement: createExtensionDataRef<ReactElement>().with({
id: 'core.react-element',
}),
routePath: createExtensionDataRef<string>().with({
id: 'core.route-path',
}),
};
```
@@ -125,9 +129,9 @@ export function createGraphiQLEndpointExtension(options) {
// Use a TypeScript namespace to merge the extension data references with the extension creator
export namespace createGraphiQLEndpointExtension {
export const endpointDataRef = createExtensionDataRef</* ... */>(
'graphiql.graphiql-endpoint.endpoint',
);
export const endpointDataRef = createExtensionDataRef</* ... */>().with({
id: 'graphiql.graphiql-endpoint.endpoint',
});
}
```
@@ -45,6 +45,7 @@ This extension is the first extension attached to the extension tree. It is resp
| themes | The app themes list. | [createThemeExtension.themeDataRef](https://backstage.io/docs/reference/frontend-plugin-api.createthemeextension.themedataref) | false | See [default themes](#default-theme-extensions). | [createThemeExtension](https://backstage.io/docs/reference/frontend-plugin-api.createthemeextension) |
| components | The app components list. | [createComponentExtension.componentDataRef](https://backstage.io/docs/reference/frontend-plugin-api.createcomponentextension.componentdataref) | false | See [default components](#default-components-extensions). | [createComponentExtension](https://backstage.io/docs/reference/frontend-plugin-api.createcomponentextension) |
| translations | The app translations list. | [createTranslationExtension.translationDataRef](https://backstage.io/docs/reference/frontend-plugin-api.createtranslationextension.translationdataref) | false | - | [createTranslationExtension](https://backstage.io/docs/reference/frontend-plugin-api.createtranslationextension) |
| icons | The app icons list. | [IconBundleBlueprint.dataRefs.icons](https://backstage.io/docs/reference/frontend-plugin-api.iconbundleblueprint.dataRefs.icons) | true | - | [IconBundleBlueprint](https://backstage.io/docs/reference/frontend-plugin-api.iconbundleblueprint) |
#### Default theme extensions
@@ -314,6 +314,31 @@ const app = createApp({
});
```
### `icons`
Icons are now installed as extensions, using the `IconBundleBlueprint` to make new instances which can be added to the app.
```ts
import { IconBundleBlueprint } from '@backstage/frontend-plugin-api';
const exampleIconBundle = IconBundleBlueprint.make({
name: 'example-bundle',
params: {
icons: {
user: MyOwnUserIcon,
},
},
});
const app = createApp({
features: [
createExtensionOverrides({
extensions: [exampleIconBundle],
}),
],
});
```
### `bindRoutes`
Route bindings can still be done using this option, but you now also have the ability to bind routes using static configuration instead. See the section on [binding routes](../architecture/07-routes.md#binding-external-route-references) for more information.
@@ -38,6 +38,10 @@ Sign-in page extension have a single purpose - to implement a custom sign-in pag
Theme extensions provide custom themes for the app. They are always attached to the app extension and you can have any number of themes extensions installed in an app at once, letting the user choose which theme to use.
### Icons - [Reference](../../reference/frontend-plugin-api.iconbundleblueprint.md)
Icon bundle extensions provide the ability to replace or provide new icons to the app. You can use the above blueprint to make new extension instances which can be installed into the app.
### Translation - [Reference](../../reference/frontend-plugin-api.createtranslationextension.md)
Translation extension provide custom translation messages for the app. They can be used both to override the default english messages to custom ones, as well as provide translations for additional languages.
@@ -63,18 +63,16 @@ When a given dependency version is the _same_ between different packages, the
dependency is hoisted to the main `node_modules` folder in the monorepo root to
be shared between packages. When _different_ versions of the same dependency are
encountered, Yarn creates a `node_modules` folder within a particular package.
This can lead to multiple versions of the same package being installed and used
in the same app.
This can lead to confusing situations with type definitions, or anything with
global state. React [Context](https://reactjs.org/docs/context.html), for
example, depends on global referential equality. This can cause problems in
Backstage with API lookup, or config loading.
All Backstage core packages are implemented in such as way that package
duplication is **not** a problem. For example, duplicate installations of
packages like `@backstage/core-plugin-api`, `@backstage/core-components`,
`@backstage/plugin-catalog-react`, and `@backstage/backend-plugin-api` are all
acceptable.
To help resolve these situations, the Backstage CLI has
[versions:check](https://backstage.io/docs/tooling/cli/03-commands#versionscheck). This
will validate versions of `@backstage` packages in your app to check for
duplicate definitions:
```bash
# Add --fix to attempt automatic resolution in yarn.lock
yarn backstage-cli versions:check
```
While package duplication might be acceptable in many cases, you might want to
deduplicate packages for the purpose of optimizing bundle size and installation
speed. We recommend using deduplication utilities such as `yarn dedupe` to trim
down the number of duplicate packages.
+2
View File
@@ -20,6 +20,8 @@ Run your Backstage app with `yarn dev`. Navigate to `http://localhost:3000`.
If you're not already logged in, you should see a login screen like this,
![Screenshot of the login screen](../assets/getting-started/login-screen.png)
To login, you should choose the "Github" provider and click the "Sign in" button. This will redirect you to a Github OAuth page. Verify that the scopes mentioned on that page match the setup you did in [the authentication tutorial](./config/authentication.md). Once you click "Confirm", you will be brought back to the Backstage interface and signed in!
If you are already logged in, you will be automatically brought to your Backstage instance.
File diff suppressed because it is too large Load Diff
+1 -20
View File
@@ -26,7 +26,6 @@ repo [command] Command that run across an entire
package [command] Lifecycle scripts for individual packages
migrate [command] Migration utilities
versions:bump [options] Bump Backstage packages to the latest versions
versions:check [options] Check Backstage package versioning
clean Delete cache directories [DEPRECATED]
build-workspace <workspace-dir> [packages...] Builds a temporary dist workspace from the provided
packages
@@ -327,8 +326,7 @@ Options:
## versions\:bump
Bump all `@backstage` packages to the latest versions. This checks for updates
in the package registry, and will update entries both in `yarn.lock` and
`package.json` files when necessary.
in the package registry, and will update entries `package.json` files when necessary.
```text
Usage: backstage-cli versions:bump [options]
@@ -339,23 +337,6 @@ Options:
--release <version|next|main> Bump to a specific Backstage release line or version (default: "main")
```
## versions\:check
Validate `@backstage` dependencies within the repo, making sure that there are
no duplicates of packages that might lead to breakages.
By supplying the `--fix` flag the command will attempt to fix any conflict that
can be resolved by editing `yarn.lock`, but will not attempt to search for
remote updates or modify any `package.json` files.
```text
Usage: backstage-cli versions:check [options]
Options:
--fix Fix any auto-fixable versioning problems
-h, --help display help for command
```
## build-workspace
Builds a mirror of the workspace using the packaged production version of each
+44
View File
@@ -100,3 +100,47 @@ With that, Backstage's cli and backend will detect public entry point and serve
5. Finally, as soon as you log in, you will be redirected to the main app home page (inspect the page and see that the protected bundle was served from the app backend after the redirect).
That's it!
## New Frontend System
If your app uses the new frontend system, you can still use the public entry point feature. The `index-public-experimental.tsx` file does end up looking a bit different in this case:
```tsx title="in packages/app/src/index-public-experimental.tsx"
import React from 'react';
import ReactDOM from 'react-dom/client';
import { CookieAuthRedirect } from '@backstage/plugin-auth-react';
import { createApp } from '@backstage/frontend-app-api';
import {
coreExtensionData,
createExtension,
createExtensionOverrides,
createSignInPageExtension,
} from '@backstage/frontend-plugin-api';
const signInPage = createSignInPageExtension({
name: 'guest',
loader: async () => props => <SignInPage {...props} providers={['guest']} />,
});
const authRedirectExtension = createExtension({
namespace: 'app',
name: 'layout',
attachTo: { id: 'app/root', input: 'children' },
output: {
element: coreExtensionData.reactElement,
},
factory: () => ({
element: <CookieAuthRedirect />,
}),
});
const app = createApp({
features: [
createExtensionOverrides({
extensions: [signInPage, authRedirectExtension],
}),
],
});
ReactDOM.createRoot(document.getElementById('root')!).render(app.createRoot());
```
-10
View File
@@ -1,10 +0,0 @@
---
title: DORA Metrics
author: Okay
authorUrl: https://www.okayhq.com
category: Metrics
description: Embed dashboards (like DORA metrics) in your team or service pages from any dev tools (including Github, Gitlab, Jira, Argo, CircleCI, Buildkite, Pagerduty, Rollbar, Sentry, etc).
documentation: https://github.com/OkayHQ/backstage-plugin
iconUrl: /img/okay.png
npmPackageName: '@okayhq/backstage-plugin'
addedDate: '2022-06-16'
Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "root",
"version": "1.29.0",
"version": "1.30.0-next.0",
"private": true,
"repository": {
"type": "git",
+11
View File
@@ -1,5 +1,16 @@
# @backstage/app-defaults
## 1.5.9-next.0
### Patch Changes
- Updated dependencies
- @backstage/core-components@0.14.10-next.0
- @backstage/core-app-api@1.14.1-next.0
- @backstage/core-plugin-api@1.9.3
- @backstage/theme@0.5.6
- @backstage/plugin-permission-react@0.4.24
## 1.5.8
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/app-defaults",
"version": "1.5.8",
"version": "1.5.9-next.0",
"description": "Provides the default wiring of a Backstage App",
"backstage": {
"role": "web-library"
@@ -1,5 +1,13 @@
# app-next-example-plugin
## 0.0.14-next.0
### Patch Changes
- Updated dependencies
- @backstage/frontend-plugin-api@0.6.8-next.0
- @backstage/core-components@0.14.10-next.0
## 0.0.13
### Patch Changes
@@ -1,6 +1,6 @@
{
"name": "app-next-example-plugin",
"version": "0.0.13",
"version": "0.0.14-next.0",
"description": "Backstage internal example plugin",
"backstage": {
"role": "frontend-plugin",
+43
View File
@@ -1,5 +1,48 @@
# example-app-next
## 0.0.14-next.0
### Patch Changes
- Updated dependencies
- @backstage/cli@0.27.0-next.0
- @backstage/frontend-plugin-api@0.6.8-next.0
- @backstage/plugin-catalog-react@1.12.3-next.0
- @backstage/plugin-search-react@1.7.14-next.0
- @backstage/plugin-home@0.7.8-next.0
- @backstage/core-components@0.14.10-next.0
- @backstage/plugin-scaffolder-react@1.11.0-next.0
- @backstage/frontend-app-api@0.7.4-next.0
- @backstage/plugin-catalog-graph@0.4.8-next.0
- @backstage/plugin-scaffolder@1.23.1-next.0
- @backstage/core-app-api@1.14.1-next.0
- @backstage/plugin-auth-react@0.1.5-next.0
- @backstage/app-defaults@1.5.9-next.0
- @backstage/catalog-model@1.5.0
- @backstage/config@1.2.0
- @backstage/core-compat-api@0.2.8-next.0
- @backstage/core-plugin-api@1.9.3
- @backstage/integration-react@1.1.30-next.0
- @backstage/theme@0.5.6
- @backstage/plugin-api-docs@0.11.8-next.0
- @backstage/plugin-app-visualizer@0.1.9-next.0
- @backstage/plugin-catalog@1.21.2-next.0
- @backstage/plugin-catalog-common@1.0.25
- @backstage/plugin-catalog-import@0.12.2-next.0
- @backstage/plugin-catalog-unprocessed-entities@0.2.7-next.0
- @backstage/plugin-kubernetes@0.11.13-next.0
- @backstage/plugin-kubernetes-cluster@0.0.14-next.0
- @backstage/plugin-notifications@0.2.4-next.0
- @backstage/plugin-org@0.6.28-next.0
- @backstage/plugin-permission-react@0.4.24
- @backstage/plugin-search@1.4.15-next.0
- @backstage/plugin-search-common@1.2.13
- @backstage/plugin-signals@0.0.9-next.0
- @backstage/plugin-techdocs@1.10.8-next.0
- @backstage/plugin-techdocs-module-addons-contrib@1.1.13-next.0
- @backstage/plugin-techdocs-react@1.2.7-next.0
- @backstage/plugin-user-settings@0.8.10-next.0
## 0.0.13
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "example-app-next",
"version": "0.0.13",
"version": "0.0.14-next.0",
"private": true,
"repository": {
"type": "git",
+3 -14
View File
@@ -36,19 +36,14 @@ import { convertLegacyApp } from '@backstage/core-compat-api';
import { FlatRoutes } from '@backstage/core-app-api';
import { Route } from 'react-router';
import { CatalogImportPage } from '@backstage/plugin-catalog-import';
import {
createApiFactory,
configApiRef,
SignInPageProps,
} from '@backstage/core-plugin-api';
import { createApiFactory, configApiRef } from '@backstage/core-plugin-api';
import {
ScmAuth,
ScmIntegrationsApi,
scmIntegrationsApiRef,
} from '@backstage/integration-react';
import { createSignInPageExtension } from '@backstage/frontend-plugin-api';
import { SignInPage } from '@backstage/core-components';
import kubernetesPlugin from '@backstage/plugin-kubernetes/alpha';
import { signInPageOverrides } from './overrides/SignInPage';
/*
@@ -91,12 +86,6 @@ const homePageExtension = createExtension({
},
});
const signInPage = createSignInPageExtension({
name: 'guest',
loader: async () => (props: SignInPageProps) =>
<SignInPage {...props} providers={['guest']} />,
});
const scmAuthExtension = createApiExtension({
factory: ScmAuth.createDefaultApiFactory(),
});
@@ -123,13 +112,13 @@ const app = createApp({
homePlugin,
appVisualizerPlugin,
kubernetesPlugin,
signInPageOverrides,
...collectedLegacyPlugins,
createExtensionOverrides({
extensions: [
homePageExtension,
scmAuthExtension,
scmIntegrationApi,
signInPage,
notFoundErrorPage,
],
}),
@@ -0,0 +1,49 @@
/*
* Copyright 2020 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.
*/
import React from 'react';
import ReactDOM from 'react-dom/client';
import { CookieAuthRedirect } from '@backstage/plugin-auth-react';
import { createApp } from '@backstage/frontend-app-api';
import { signInPageOverrides } from './overrides/SignInPage';
import {
coreExtensionData,
createExtension,
createExtensionOverrides,
} from '@backstage/frontend-plugin-api';
const authRedirectExtension = createExtension({
namespace: 'app',
name: 'layout',
attachTo: { id: 'app/root', input: 'children' },
output: {
element: coreExtensionData.reactElement,
},
factory: () => ({
element: <CookieAuthRedirect />,
}),
});
const app = createApp({
features: [
signInPageOverrides,
createExtensionOverrides({
extensions: [authRedirectExtension],
}),
],
});
ReactDOM.createRoot(document.getElementById('root')!).render(app.createRoot());
@@ -0,0 +1,31 @@
/*
* Copyright 2024 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.
*/
import React from 'react';
import { SignInPage } from '@backstage/core-components';
import {
createExtensionOverrides,
createSignInPageExtension,
} from '@backstage/frontend-plugin-api';
const signInPage = createSignInPageExtension({
name: 'guest',
loader: async () => props => <SignInPage {...props} providers={['guest']} />,
});
export const signInPageOverrides = createExtensionOverrides({
extensions: [signInPage],
});
+41
View File
@@ -1,5 +1,46 @@
# example-app
## 0.2.100-next.0
### Patch Changes
- Updated dependencies
- @backstage/cli@0.27.0-next.0
- @backstage/plugin-catalog-react@1.12.3-next.0
- @backstage/plugin-search-react@1.7.14-next.0
- @backstage/plugin-home@0.7.8-next.0
- @backstage/core-components@0.14.10-next.0
- @backstage/plugin-scaffolder-react@1.11.0-next.0
- @backstage/frontend-app-api@0.7.4-next.0
- @backstage/plugin-catalog-graph@0.4.8-next.0
- @backstage/plugin-scaffolder@1.23.1-next.0
- @backstage/core-app-api@1.14.1-next.0
- @backstage/plugin-auth-react@0.1.5-next.0
- @backstage/app-defaults@1.5.9-next.0
- @backstage/catalog-model@1.5.0
- @backstage/config@1.2.0
- @backstage/core-plugin-api@1.9.3
- @backstage/integration-react@1.1.30-next.0
- @backstage/theme@0.5.6
- @backstage/plugin-api-docs@0.11.8-next.0
- @backstage/plugin-catalog@1.21.2-next.0
- @backstage/plugin-catalog-common@1.0.25
- @backstage/plugin-catalog-import@0.12.2-next.0
- @backstage/plugin-catalog-unprocessed-entities@0.2.7-next.0
- @backstage/plugin-devtools@0.1.17-next.0
- @backstage/plugin-kubernetes@0.11.13-next.0
- @backstage/plugin-kubernetes-cluster@0.0.14-next.0
- @backstage/plugin-notifications@0.2.4-next.0
- @backstage/plugin-org@0.6.28-next.0
- @backstage/plugin-permission-react@0.4.24
- @backstage/plugin-search@1.4.15-next.0
- @backstage/plugin-search-common@1.2.13
- @backstage/plugin-signals@0.0.9-next.0
- @backstage/plugin-techdocs@1.10.8-next.0
- @backstage/plugin-techdocs-module-addons-contrib@1.1.13-next.0
- @backstage/plugin-techdocs-react@1.2.7-next.0
- @backstage/plugin-user-settings@0.8.10-next.0
## 0.2.99
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "example-app",
"version": "0.2.99",
"version": "0.2.100-next.0",
"backstage": {
"role": "frontend"
},
+17
View File
@@ -1,5 +1,22 @@
# @backstage/backend-app-api
## 0.8.1-next.0
### Patch Changes
- Updated dependencies
- @backstage/backend-common@0.23.4-next.0
- @backstage/config-loader@1.8.2-next.0
- @backstage/backend-plugin-api@0.7.1-next.0
- @backstage/backend-tasks@0.5.28-next.0
- @backstage/cli-common@0.1.14
- @backstage/cli-node@0.2.7
- @backstage/config@1.2.0
- @backstage/errors@1.2.4
- @backstage/types@1.1.1
- @backstage/plugin-auth-node@0.4.18-next.0
- @backstage/plugin-permission-node@0.8.1-next.0
## 0.8.0
### Minor Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/backend-app-api",
"version": "0.8.0",
"version": "0.8.1-next.0",
"description": "Core API used by Backstage backend apps",
"backstage": {
"role": "node-library"
+28
View File
@@ -1,5 +1,33 @@
# @backstage/backend-common
## 0.23.4-next.0
### Patch Changes
- ba8571e: Setup user agent header for AWS sdk clients, this enables users to better track API calls made from Backstage to AWS APIs through things like CloudTrail.
- 7e13b7a: The remaining exports in the package have now been deprecated:
- `cacheToPluginCacheManager`
- `createLegacyAuthAdapters`
- `LegacyCreateRouter`
- `legacyPlugin`
- `loggerToWinstonLogger`
- `makeLegacyPlugin`
Users of these export should fully [migrate to the new backend system](https://backstage.io/docs/backend-system/building-backends/migrating).
- Updated dependencies
- @backstage/integration@1.14.0-next.0
- @backstage/config-loader@1.8.2-next.0
- @backstage/backend-dev-utils@0.1.4
- @backstage/backend-plugin-api@0.7.1-next.0
- @backstage/cli-common@0.1.14
- @backstage/config@1.2.0
- @backstage/errors@1.2.4
- @backstage/integration-aws-node@0.1.12
- @backstage/types@1.1.1
- @backstage/plugin-auth-node@0.4.18-next.0
## 0.23.3
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/backend-common",
"version": "0.23.3",
"version": "0.23.4-next.0",
"description": "Common functionality library for Backstage backends",
"backstage": {
"role": "node-library"
+23
View File
@@ -1,5 +1,28 @@
# @backstage/backend-defaults
## 0.4.2-next.0
### Patch Changes
- 4e79d19: The `createHealthRouter` utility that allows you to create a health check router is now exported via `@backstage/backend-defaults/rootHttpRouter`.
- 78c1329: Updated `GitlabUrlReader.readUrl` and `GitlabUrlReader.readTree` to accept a user-provided token, supporting both bearer and private tokens.
- 8e967da: Fixed the routing of the new health check service, the health endpoints should now properly be available at `/.backstage/health/v1/readiness` and `/.backstage/health/v1/liveness`.
- Updated dependencies
- @backstage/backend-common@0.23.4-next.0
- @backstage/integration@1.14.0-next.0
- @backstage/config-loader@1.8.2-next.0
- @backstage/backend-app-api@0.8.1-next.0
- @backstage/backend-dev-utils@0.1.4
- @backstage/backend-plugin-api@0.7.1-next.0
- @backstage/cli-common@0.1.14
- @backstage/config@1.2.0
- @backstage/errors@1.2.4
- @backstage/integration-aws-node@0.1.12
- @backstage/types@1.1.1
- @backstage/plugin-auth-node@0.4.18-next.0
- @backstage/plugin-events-node@0.3.9-next.0
- @backstage/plugin-permission-node@0.8.1-next.0
## 0.4.0
### Minor Changes
@@ -3,6 +3,7 @@
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
```ts
/// <reference types="express" />
/// <reference types="node" />
import { Config } from '@backstage/config';
@@ -17,10 +18,17 @@ import { LoggerService } from '@backstage/backend-plugin-api';
import { RequestHandler } from 'express';
import { RequestListener } from 'http';
import { RootConfigService } from '@backstage/backend-plugin-api';
import { RootHealthService } from '@backstage/backend-plugin-api';
import { RootHttpRouterService } from '@backstage/backend-plugin-api';
import { Router } from 'express';
import type { Server } from 'node:http';
import { ServiceFactory } from '@backstage/backend-plugin-api';
// @public (undocumented)
export function createHealthRouter(options: {
health: RootHealthService;
}): Router;
// @public
export function createHttpServer(
listener: RequestListener,
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/backend-defaults",
"version": "0.4.0",
"version": "0.4.2-next.0",
"description": "Backend defaults used by Backstage backend apps",
"backstage": {
"role": "node-library"
@@ -1,5 +1,3 @@
import { RootHealthService } from '@backstage/backend-plugin-api';
/*
* Copyright 2024 The Backstage Authors
*
@@ -16,9 +14,13 @@ import { RootHealthService } from '@backstage/backend-plugin-api';
* limitations under the License.
*/
import { RootHealthService } from '@backstage/backend-plugin-api';
import Router from 'express-promise-router';
import { Request, Response } from 'express';
/**
* @public
*/
export function createHealthRouter(options: { health: RootHealthService }) {
const router = Router();
@@ -18,6 +18,7 @@ export {
DefaultRootHttpRouter,
type DefaultRootHttpRouterOptions,
} from './DefaultRootHttpRouter';
export { createHealthRouter } from './createHealthRouter';
export * from './http';
export {
rootHttpRouterServiceFactory,
@@ -0,0 +1,74 @@
/*
* Copyright 2022 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.
*/
import {
ServiceFactoryTester,
mockServices,
} from '@backstage/backend-test-utils';
import { Express } from 'express';
import request from 'supertest';
import { rootHttpRouterServiceFactory } from './rootHttpRouterServiceFactory';
import { coreServices } from '@backstage/backend-plugin-api';
describe('rootHttpRouterServiceFactory', () => {
it('should make the health endpoints available', async () => {
let app: Express | undefined = undefined;
const tester = ServiceFactoryTester.from(
rootHttpRouterServiceFactory({
configure(options) {
options.applyDefaults();
app = options.app;
},
}),
{
dependencies: [
mockServices.rootConfig.factory({
data: {
app: { baseUrl: 'http://localhost' },
backend: {
baseUrl: 'http://localhost',
listen: { host: '', port: 0 },
},
},
}),
],
},
);
// Trigger creation of the http service, accessing the app instance through the configure callback
await tester.getSubject();
await request(app!)
.get('/.backstage/health/v1/liveness')
.expect(200, { status: 'ok' });
await request(app!).get('/.backstage/health/v1/readiness').expect(503, {
message: 'Backend has not started yet',
status: 'error',
});
const lifecycle = await tester.getService(coreServices.rootLifecycle);
await (lifecycle as any).startup(); // Trigger startup by calling the private startup method
await request(app!).get('/.backstage/health/v1/readiness').expect(200, {
status: 'ok',
});
expect('test').toBe('test');
});
});
@@ -1,5 +1,31 @@
# @backstage/backend-dynamic-feature-service
## 0.2.16-next.0
### Patch Changes
- Updated dependencies
- @backstage/backend-common@0.23.4-next.0
- @backstage/plugin-catalog-backend@1.24.1-next.0
- @backstage/config-loader@1.8.2-next.0
- @backstage/backend-app-api@0.8.1-next.0
- @backstage/backend-plugin-api@0.7.1-next.0
- @backstage/backend-tasks@0.5.28-next.0
- @backstage/cli-common@0.1.14
- @backstage/cli-node@0.2.7
- @backstage/config@1.2.0
- @backstage/errors@1.2.4
- @backstage/types@1.1.1
- @backstage/plugin-app-node@0.1.23-next.0
- @backstage/plugin-auth-node@0.4.18-next.0
- @backstage/plugin-events-backend@0.3.10-next.0
- @backstage/plugin-events-node@0.3.9-next.0
- @backstage/plugin-permission-common@0.8.0
- @backstage/plugin-permission-node@0.8.1-next.0
- @backstage/plugin-scaffolder-node@0.4.9-next.0
- @backstage/plugin-search-backend-node@1.2.28-next.0
- @backstage/plugin-search-common@1.2.13
## 0.2.15
### Patch Changes
@@ -1,7 +1,7 @@
{
"name": "@backstage/backend-dynamic-feature-service",
"description": "Backstage dynamic feature service",
"version": "0.2.15",
"version": "0.2.16-next.0",
"main": "src/index.ts",
"types": "src/index.ts",
"publishConfig": {
+42
View File
@@ -1,5 +1,47 @@
# example-backend-legacy
## 0.2.101-next.0
### Patch Changes
- Updated dependencies
- @backstage/backend-common@0.23.4-next.0
- @backstage/plugin-scaffolder-backend-module-rails@0.4.40-next.0
- @backstage/plugin-scaffolder-backend-module-gitlab@0.4.5-next.0
- @backstage/plugin-catalog-backend@1.24.1-next.0
- @backstage/plugin-catalog-node@1.12.5-next.0
- @backstage/plugin-devtools-backend@0.3.9-next.0
- @backstage/integration@1.14.0-next.0
- @backstage/plugin-search-backend-module-pg@0.5.33-next.0
- example-app@0.2.100-next.0
- @backstage/backend-tasks@0.5.28-next.0
- @backstage/catalog-client@1.6.5
- @backstage/catalog-model@1.5.0
- @backstage/config@1.2.0
- @backstage/plugin-app-backend@0.3.72-next.0
- @backstage/plugin-auth-backend@0.22.10-next.0
- @backstage/plugin-auth-node@0.4.18-next.0
- @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.1.21-next.0
- @backstage/plugin-catalog-backend-module-unprocessed@0.4.10-next.0
- @backstage/plugin-events-backend@0.3.10-next.0
- @backstage/plugin-events-node@0.3.9-next.0
- @backstage/plugin-kubernetes-backend@0.18.4-next.0
- @backstage/plugin-permission-backend@0.5.47-next.0
- @backstage/plugin-permission-common@0.8.0
- @backstage/plugin-permission-node@0.8.1-next.0
- @backstage/plugin-proxy-backend@0.5.4-next.0
- @backstage/plugin-scaffolder-backend@1.23.1-next.0
- @backstage/plugin-scaffolder-backend-module-confluence-to-markdown@0.2.24-next.0
- @backstage/plugin-search-backend@1.5.15-next.0
- @backstage/plugin-search-backend-module-catalog@0.1.29-next.0
- @backstage/plugin-search-backend-module-elasticsearch@1.5.4-next.0
- @backstage/plugin-search-backend-module-explore@0.1.29-next.0
- @backstage/plugin-search-backend-module-techdocs@0.1.28-next.0
- @backstage/plugin-search-backend-node@1.2.28-next.0
- @backstage/plugin-signals-backend@0.1.9-next.0
- @backstage/plugin-signals-node@0.1.9-next.0
- @backstage/plugin-techdocs-backend@1.10.10-next.0
## 0.2.100
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "example-backend-legacy",
"version": "0.2.100",
"version": "0.2.101-next.0",
"backstage": {
"role": "backend"
},
@@ -1,5 +1,13 @@
# @backstage/backend-openapi-utils
## 0.1.16-next.0
### Patch Changes
- Updated dependencies
- @backstage/backend-plugin-api@0.7.1-next.0
- @backstage/errors@1.2.4
## 0.1.15
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/backend-openapi-utils",
"description": "OpenAPI typescript support.",
"version": "0.1.15",
"version": "0.1.16-next.0",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
+12
View File
@@ -1,5 +1,17 @@
# @backstage/backend-plugin-api
## 0.7.1-next.0
### Patch Changes
- Updated dependencies
- @backstage/cli-common@0.1.14
- @backstage/config@1.2.0
- @backstage/errors@1.2.4
- @backstage/types@1.1.1
- @backstage/plugin-auth-node@0.4.18-next.0
- @backstage/plugin-permission-common@0.8.0
## 0.7.0
### Minor Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/backend-plugin-api",
"version": "0.7.0",
"version": "0.7.1-next.0",
"description": "Core API used by Backstage backend plugins",
"backstage": {
"role": "node-library"
+11
View File
@@ -1,5 +1,16 @@
# @backstage/backend-tasks
## 0.5.28-next.0
### Patch Changes
- Updated dependencies
- @backstage/backend-common@0.23.4-next.0
- @backstage/backend-plugin-api@0.7.1-next.0
- @backstage/config@1.2.0
- @backstage/errors@1.2.4
- @backstage/types@1.1.1
## 0.5.27
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/backend-tasks",
"description": "Common distributed task management library for Backstage backends",
"version": "0.5.27",
"version": "0.5.28-next.0",
"main": "src/index.ts",
"types": "src/index.ts",
"publishConfig": {
+15
View File
@@ -1,5 +1,20 @@
# @backstage/backend-test-utils
## 0.4.5-next.0
### Patch Changes
- 4e79d19: The default services for `startTestBackend` and `ServiceFactoryTester` now includes the Root Health Service.
- Updated dependencies
- @backstage/backend-defaults@0.4.2-next.0
- @backstage/backend-app-api@0.8.1-next.0
- @backstage/backend-plugin-api@0.7.1-next.0
- @backstage/config@1.2.0
- @backstage/errors@1.2.4
- @backstage/types@1.1.1
- @backstage/plugin-auth-node@0.4.18-next.0
- @backstage/plugin-events-node@0.3.9-next.0
## 0.4.4
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/backend-test-utils",
"version": "0.4.4",
"version": "0.4.5-next.0",
"description": "Test helpers library for Backstage backends",
"backstage": {
"role": "node-library"
@@ -242,6 +242,15 @@ describe('TestBackend', () => {
expect(res.body).toEqual({ message: 'pong' });
});
it('should expose health check endpoints', async () => {
const { server } = await startTestBackend({ features: [] });
const res = await request(server).get('/.backstage/health/v1/liveness');
expect(res.status).toEqual(200);
expect(res.body).toEqual({ status: 'ok' });
});
it('should provide extension point implementations', async () => {
expect.assertions(3);
@@ -37,6 +37,7 @@ import express from 'express';
// Direct internal import to avoid duplication
// eslint-disable-next-line @backstage/no-forbidden-package-imports
import { InternalBackendFeature } from '@backstage/backend-plugin-api/src/wiring/types';
import { createHealthRouter } from '@backstage/backend-defaults/rootHttpRouter';
/** @public */
export interface TestBackendOptions<TExtensionPoints extends any[]> {
@@ -73,6 +74,7 @@ export const defaultServiceFactories = [
mockServices.lifecycle.factory(),
mockServices.logger.factory(),
mockServices.permissions.factory(),
mockServices.rootHealth.factory(),
mockServices.rootLifecycle.factory(),
mockServices.rootLogger.factory(),
mockServices.scheduler.factory(),
@@ -251,15 +253,18 @@ export async function startTestBackend<TExtensionPoints extends any[]>(
config: coreServices.rootConfig,
lifecycle: coreServices.rootLifecycle,
rootLogger: coreServices.rootLogger,
health: coreServices.rootHealth,
},
async factory({ config, lifecycle, rootLogger }) {
async factory({ config, lifecycle, rootLogger, health }) {
const router = DefaultRootHttpRouter.create();
const logger = rootLogger.child({ service: 'rootHttpRouter' });
const app = express();
const middleware = MiddlewareFactory.create({ config, logger });
const healthRouter = createHealthRouter({ health });
app.use(healthRouter);
app.use(router.handler());
app.use(middleware.notFound());
app.use(middleware.error());
+37
View File
@@ -1,5 +1,42 @@
# example-backend
## 0.0.29-next.0
### Patch Changes
- Updated dependencies
- @backstage/backend-defaults@0.4.2-next.0
- @backstage/plugin-catalog-backend@1.24.1-next.0
- @backstage/plugin-devtools-backend@0.3.9-next.0
- @backstage/backend-plugin-api@0.7.1-next.0
- @backstage/backend-tasks@0.5.28-next.0
- @backstage/catalog-model@1.5.0
- @backstage/plugin-app-backend@0.3.72-next.0
- @backstage/plugin-auth-backend@0.22.10-next.0
- @backstage/plugin-auth-backend-module-github-provider@0.1.20-next.0
- @backstage/plugin-auth-backend-module-guest-provider@0.1.9-next.0
- @backstage/plugin-auth-node@0.4.18-next.0
- @backstage/plugin-catalog-backend-module-backstage-openapi@0.2.6-next.0
- @backstage/plugin-catalog-backend-module-openapi@0.1.41-next.0
- @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.1.21-next.0
- @backstage/plugin-catalog-backend-module-unprocessed@0.4.10-next.0
- @backstage/plugin-kubernetes-backend@0.18.4-next.0
- @backstage/plugin-notifications-backend@0.3.4-next.0
- @backstage/plugin-permission-backend@0.5.47-next.0
- @backstage/plugin-permission-backend-module-allow-all-policy@0.1.20-next.0
- @backstage/plugin-permission-common@0.8.0
- @backstage/plugin-permission-node@0.8.1-next.0
- @backstage/plugin-proxy-backend@0.5.4-next.0
- @backstage/plugin-scaffolder-backend@1.23.1-next.0
- @backstage/plugin-scaffolder-backend-module-github@0.4.1-next.0
- @backstage/plugin-search-backend@1.5.15-next.0
- @backstage/plugin-search-backend-module-catalog@0.1.29-next.0
- @backstage/plugin-search-backend-module-explore@0.1.29-next.0
- @backstage/plugin-search-backend-module-techdocs@0.1.28-next.0
- @backstage/plugin-search-backend-node@1.2.28-next.0
- @backstage/plugin-signals-backend@0.1.9-next.0
- @backstage/plugin-techdocs-backend@1.10.10-next.0
## 0.0.28
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "example-backend",
"version": "0.0.28",
"version": "0.0.29-next.0",
"main": "dist/index.cjs.js",
"types": "src/index.ts",
"license": "Apache-2.0",
+35
View File
@@ -1,5 +1,40 @@
# @backstage/cli
## 0.27.0-next.0
### Minor Changes
- 32a38e1: **BREAKING**: The lockfile (`yarn.lock`) dependency analysis and mutations have been removed from several commands.
The `versions:bump` command will no longer attempt to bump and deduplicate dependencies by modifying the lockfile, it will only update `package.json` files.
The `versions:check` command has been removed, since its only purpose was verification and mutation of the lockfile. We recommend using the `yarn dedupe` command instead, or the `yarn-deduplicate` package if you're using Yarn classic.
The check that was built into the `package start` command has been removed, it will no longer warn about lockfile mismatches.
The packages in the Backstage ecosystem handle package duplications much better now than when these CLI features were first introduced, so the need for these features has diminished. By removing them, we drastically reduce the integration between the Backstage CLI and Yarn, making it much easier to add support for other package managers in the future.
### Patch Changes
- 7eb08a6: Add frontend-dynamic-container role to eslint config factory
- fbc7819: Use ES2022 in CLI bundler
- e53074f: Updated default backend plugin to use `RootConfigService` instead of `Config`. This also removes the dependency on `@backstage/config` as it's no longer used.
- ee2b0e5: The experimental module federation build now has the ability to force the use of development versions of `react` and `react-dom` by setting the `FORCE_REACT_DEVELOPMENT` flag.
- 239dffc: Remove usage of deprecated functionality from @backstage/config-loader
- 0eedec3: Add support for dynamic plugins via the EXPERIMENTAL_MODULE_FEDERATION environment variable when running `yarn start`.
- dc4fb4f: Fix for `repo build --all` not properly detecting the experimental public entry point.
- Updated dependencies
- @backstage/integration@1.14.0-next.0
- @backstage/config-loader@1.8.2-next.0
- @backstage/catalog-model@1.5.0
- @backstage/cli-common@0.1.14
- @backstage/cli-node@0.2.7
- @backstage/config@1.2.0
- @backstage/errors@1.2.4
- @backstage/eslint-plugin@0.1.8
- @backstage/release-manifests@0.0.11
- @backstage/types@1.1.1
## 0.26.11
### Patch Changes
-11
View File
@@ -22,7 +22,6 @@ Commands:
package [command]
migrate [command]
versions:bump [options]
versions:check [options]
versions:migrate [options]
clean
build-workspace [options] <workspace-dir> [packages...]
@@ -607,16 +606,6 @@ Options:
-h, --help
```
### `backstage-cli versions:check`
```
Usage: backstage-cli versions:check [options]
Options:
--fix
-h, --help
```
### `backstage-cli versions:migrate`
```
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/cli",
"version": "0.26.11",
"version": "0.27.0-next.0",
"description": "CLI for developing Backstage plugins and apps",
"backstage": {
"role": "cli"
@@ -16,11 +16,9 @@
import fs from 'fs-extra';
import { resolve as resolvePath } from 'path';
import { buildBundle } from '../../lib/bundler';
import { buildBundle, getModuleFederationOptions } from '../../lib/bundler';
import { getEnvironmentParallelism } from '../../lib/parallel';
import { loadCliConfig } from '../../lib/config';
import chalk from 'chalk';
import { BuildOptions } from '../../lib/bundler/types';
interface BuildAppOptions {
targetDir: string;
@@ -29,29 +27,6 @@ interface BuildAppOptions {
isModuleFederationRemote?: true;
}
function getModuleFederationOptions(
name: string,
isRemote?: boolean,
): BuildOptions['moduleFederation'] {
if (!isRemote && !process.env.EXPERIMENTAL_MODULE_FEDERATION) {
return undefined;
}
console.log(
chalk.yellow(
`⚠️ WARNING: Module federation is experimental and will receive immediate breaking changes in the future.`,
),
);
return {
mode: isRemote ? 'remote' : 'host',
// The default output mode requires the name to be a usable as a code
// symbol, there might be better options here but for now we need to
// sanitize the name.
name: name.replaceAll('@', '').replaceAll('/', '__').replaceAll('-', '_'),
};
}
export async function buildFrontend(options: BuildAppOptions) {
const { targetDir, writeStats, configPaths } = options;
const { name } = await fs.readJson(resolvePath(targetDir, 'package.json'));
+1 -1
View File
@@ -396,7 +396,7 @@ export function registerCommands(program: Command) {
.action(lazy(() => import('./versions/bump').then(m => m.default)));
program
.command('versions:check')
.command('versions:check', { hidden: true })
.option('--fix', 'Fix any auto-fixable versioning problems')
.description('Check Backstage package versioning')
.action(lazy(() => import('./versions/lint').then(m => m.default)));
@@ -15,6 +15,7 @@
*/
import { OptionValues } from 'commander';
import { PackageRole } from '@backstage/cli-node';
import { findRoleFromCommand } from '../../lib/role';
import { startBackend, startBackendPlugin } from './startBackend';
import { startFrontend } from './startFrontend';
@@ -47,6 +48,13 @@ export async function command(opts: OptionValues): Promise<void> {
case 'frontend-plugin':
case 'frontend-plugin-module':
return startFrontend({ entry: 'dev/index', ...options });
case 'frontend-dynamic-container' as PackageRole: // experimental
return startFrontend({
entry: 'src/index',
...options,
skipOpenBrowser: true,
isModuleFederationRemote: true,
});
default:
throw new Error(
`Start command is not supported for package role '${role}'`,
@@ -14,7 +14,9 @@
* limitations under the License.
*/
import { serveBundle } from '../../lib/bundler';
import { readJson } from 'fs-extra';
import { getModuleFederationOptions, serveBundle } from '../../lib/bundler';
import { paths } from '../../lib/paths';
interface StartAppOptions {
verifyVersions?: boolean;
@@ -22,14 +24,23 @@ interface StartAppOptions {
checksEnabled: boolean;
configPaths: string[];
skipOpenBrowser?: boolean;
isModuleFederationRemote?: boolean;
}
export async function startFrontend(options: StartAppOptions) {
const { name } = await readJson(paths.resolveTarget('package.json'));
const waitForExit = await serveBundle({
entry: options.entry,
checksEnabled: options.checksEnabled,
configPaths: options.configPaths,
verifyVersions: options.verifyVersions,
skipOpenBrowser: options.skipOpenBrowser,
moduleFederation: getModuleFederationOptions(
name,
options.isModuleFederationRemote,
),
});
await waitForExit();
+5 -169
View File
@@ -23,7 +23,6 @@ import { YarnInfoInspectData } from '../../lib/versioning/packages';
import { setupServer } from 'msw/node';
import { rest } from 'msw';
import { NotFoundError } from '@backstage/errors';
import { Lockfile } from '../../lib/versioning/Lockfile';
import {
MockDirectory,
createMockDirectory,
@@ -117,17 +116,6 @@ const lockfileMock = `${HEADER}
version "1.0.3"
`;
// This is the lockfile that we produce to unlock versions before we run yarn install
const lockfileMockResult = `${HEADER}
"@backstage/core@^1.0.5":
version "1.0.6"
dependencies:
"@backstage/core-api" "^1.0.6"
"@backstage/theme@^1.0.0":
version "1.0.0"
`;
// Avoid flakes by comparing sorted log lines. File system access is async, which leads to the log line order being indeterministic
const expectLogsToMatch = (
recievedLogs: String[],
@@ -204,11 +192,7 @@ describe('bump', () => {
'Using default pattern glob @backstage/*',
'Checking for updates of @backstage/core',
'Checking for updates of @backstage/theme',
'Checking for updates of @backstage/core-api',
'Some packages are outdated, updating',
'unlocking @backstage/core@^1.0.3 ~> 1.0.6',
'unlocking @backstage/core-api@^1.0.6 ~> 1.0.7',
'unlocking @backstage/core-api@^1.0.3 ~> 1.0.7',
'bumping @backstage/core in a to ^1.0.6',
'bumping @backstage/core in b to ^1.0.6',
'bumping @backstage/theme in b to ^2.0.0',
@@ -220,9 +204,8 @@ describe('bump', () => {
'Version bump complete!',
]);
expect(mockFetchPackageInfo).toHaveBeenCalledTimes(3);
expect(mockFetchPackageInfo).toHaveBeenCalledTimes(2);
expect(mockFetchPackageInfo).toHaveBeenCalledWith('@backstage/core');
expect(mockFetchPackageInfo).toHaveBeenCalledWith('@backstage/core-api');
expect(mockFetchPackageInfo).toHaveBeenCalledWith('@backstage/theme');
expect(runObj.run).toHaveBeenCalledTimes(1);
@@ -232,12 +215,6 @@ describe('bump', () => {
expect.any(Object),
);
const lockfileContents = await fs.readFile(
mockDir.resolve('yarn.lock'),
'utf8',
);
expect(lockfileContents).toBe(lockfileMockResult);
const packageA = await fs.readJson(
mockDir.resolve('packages/a/package.json'),
);
@@ -312,11 +289,7 @@ describe('bump', () => {
'Using default pattern glob @backstage/*',
'Checking for updates of @backstage/core',
'Checking for updates of @backstage/theme',
'Checking for updates of @backstage/core-api',
'Some packages are outdated, updating',
'unlocking @backstage/core@^1.0.3 ~> 1.0.6',
'unlocking @backstage/core-api@^1.0.6 ~> 1.0.7',
'unlocking @backstage/core-api@^1.0.3 ~> 1.0.7',
'bumping @backstage/core in a to ^1.0.6',
'bumping @backstage/core in b to ^1.0.6',
'bumping @backstage/theme in b to ^2.0.0',
@@ -328,9 +301,8 @@ describe('bump', () => {
'Version bump complete!',
]);
expect(mockFetchPackageInfo).toHaveBeenCalledTimes(3);
expect(mockFetchPackageInfo).toHaveBeenCalledTimes(2);
expect(mockFetchPackageInfo).toHaveBeenCalledWith('@backstage/core');
expect(mockFetchPackageInfo).toHaveBeenCalledWith('@backstage/core-api');
expect(mockFetchPackageInfo).toHaveBeenCalledWith('@backstage/theme');
expect(runObj.run).not.toHaveBeenCalledWith(
@@ -339,12 +311,6 @@ describe('bump', () => {
expect.any(Object),
);
const lockfileContents = await fs.readFile(
mockDir.resolve('yarn.lock'),
'utf8',
);
expect(lockfileContents).toBe(lockfileMockResult);
const packageA = await fs.readJson(
mockDir.resolve('packages/a/package.json'),
);
@@ -425,12 +391,7 @@ describe('bump', () => {
'Using default pattern glob @backstage/*',
'Checking for updates of @backstage/core',
'Checking for updates of @backstage/theme',
'Checking for updates of @backstage/theme',
'Checking for updates of @backstage/core-api',
'Some packages are outdated, updating',
'unlocking @backstage/core@^1.0.3 ~> 1.0.6',
'unlocking @backstage/core-api@^1.0.6 ~> 1.0.7',
'unlocking @backstage/core-api@^1.0.3 ~> 1.0.7',
'bumping @backstage/theme in b to ^5.0.0',
'bumping @backstage/core in b to ^1.0.6',
'bumping @backstage/core in a to ^1.0.6',
@@ -443,9 +404,8 @@ describe('bump', () => {
'Version bump complete!',
]);
expect(mockFetchPackageInfo).toHaveBeenCalledTimes(2);
expect(mockFetchPackageInfo).toHaveBeenCalledTimes(1);
expect(mockFetchPackageInfo).toHaveBeenCalledWith('@backstage/core');
expect(mockFetchPackageInfo).not.toHaveBeenCalledWith('@backstage/theme');
expect(runObj.run).toHaveBeenCalledTimes(1);
expect(runObj.run).toHaveBeenCalledWith(
@@ -454,12 +414,6 @@ describe('bump', () => {
expect.any(Object),
);
const lockfileContents = await fs.readFile(
mockDir.resolve('yarn.lock'),
'utf8',
);
expect(lockfileContents).toBe(lockfileMockResult);
const packageA = await fs.readJson(
mockDir.resolve('packages/a/package.json'),
);
@@ -629,12 +583,7 @@ describe('bump', () => {
'Using default pattern glob @backstage/*',
'Checking for updates of @backstage/core',
'Checking for updates of @backstage/theme',
'Checking for updates of @backstage/theme',
'Checking for updates of @backstage/core-api',
'Some packages are outdated, updating',
'unlocking @backstage/core@^1.0.3 ~> 1.0.6',
'unlocking @backstage/core-api@^1.0.6 ~> 1.0.7',
'unlocking @backstage/core-api@^1.0.3 ~> 1.0.7',
'bumping @backstage/theme in b to ^5.0.0',
'bumping @backstage/core in b to ^1.0.6',
'bumping @backstage/core in a to ^1.0.6',
@@ -658,21 +607,6 @@ describe('bump', () => {
"@backstage-extra/custom-two@^1.0.0":
version "1.0.0"
`;
const customLockfileMockResult = `${HEADER}
"@backstage-extra/custom-two@^1.0.0":
version "1.0.0"
"@backstage-extra/custom@^1.1.0":
version "1.1.0"
"@backstage/core@^1.0.5":
version "1.0.6"
dependencies:
"@backstage/core-api" "^1.0.6"
"@backstage/theme@^1.0.0":
version "1.0.0"
`;
mockDir.setContent({
'yarn.lock': customLockfileMock,
@@ -731,12 +665,7 @@ describe('bump', () => {
'Checking for updates of @backstage-extra/custom',
'Checking for updates of @backstage-extra/custom-two',
'Checking for updates of @backstage/theme',
'Checking for updates of @backstage/core-api',
'Some packages are outdated, updating',
'unlocking @backstage/core@^1.0.3 ~> 1.0.6',
'unlocking @backstage-extra/custom@^1.0.1 ~> 1.1.0',
'unlocking @backstage/core-api@^1.0.6 ~> 1.0.7',
'unlocking @backstage/core-api@^1.0.3 ~> 1.0.7',
'bumping @backstage/core in a to ^1.0.6',
'bumping @backstage-extra/custom in a to ^1.1.0',
'bumping @backstage-extra/custom-two in a to ^2.0.0',
@@ -754,7 +683,7 @@ describe('bump', () => {
'Version bump complete!',
]);
expect(mockFetchPackageInfo).toHaveBeenCalledTimes(5);
expect(mockFetchPackageInfo).toHaveBeenCalledTimes(4);
expect(mockFetchPackageInfo).toHaveBeenCalledWith('@backstage/core');
expect(mockFetchPackageInfo).toHaveBeenCalledWith('@backstage/theme');
@@ -765,12 +694,6 @@ describe('bump', () => {
expect.any(Object),
);
const lockfileContents = await fs.readFile(
mockDir.resolve('yarn.lock'),
'utf8',
);
expect(lockfileContents).toEqual(customLockfileMockResult);
const packageA = await fs.readJson(
mockDir.resolve('packages/a/package.json'),
);
@@ -798,7 +721,7 @@ describe('bump', () => {
it('should ignore not found packages', async () => {
mockDir.setContent({
'yarn.lock': lockfileMockResult,
'yarn.lock': lockfileMock,
'package.json': JSON.stringify({
workspaces: {
packages: ['packages/*'],
@@ -848,21 +771,11 @@ describe('bump', () => {
'Checking for updates of @backstage/theme',
'Package info not found, ignoring package @backstage/core',
'Package info not found, ignoring package @backstage/theme',
'Checking for updates of @backstage/core',
'Checking for updates of @backstage/theme',
'Package info not found, ignoring package @backstage/core',
'Package info not found, ignoring package @backstage/theme',
'All Backstage packages are up to date!',
]);
expect(runObj.run).toHaveBeenCalledTimes(0);
const lockfileContents = await fs.readFile(
mockDir.resolve('yarn.lock'),
'utf8',
);
expect(lockfileContents).toBe(lockfileMockResult);
const packageA = await fs.readJson(
mockDir.resolve('packages/a/package.json'),
);
@@ -883,83 +796,6 @@ describe('bump', () => {
},
});
});
// eslint-disable-next-line jest/expect-expect
it('should log duplicates', async () => {
jest.spyOn(Lockfile.prototype, 'analyze').mockReturnValue({
invalidRanges: [],
newVersions: [],
newRanges: [
{
name: '@backstage/backend-app-api',
oldRange: '^1.0.0',
newRange: '^2.0.0',
oldVersion: '1.0.0',
newVersion: '2.0.0',
},
],
});
mockDir.setContent({
'yarn.lock': `${HEADER}
"@backstage/backend-app-api@^1.0.0":
version "1.0.0"
`,
'package.json': JSON.stringify({
workspaces: {
packages: ['packages/*'],
},
}),
packages: {
a: {
'package.json': JSON.stringify({
name: 'a',
dependencies: {
'@backstage/backend-app-api': '^1.0.0',
},
}),
},
},
});
jest.spyOn(runObj, 'run').mockResolvedValue(undefined);
worker.use(
rest.get(
'https://versions.backstage.io/v1/tags/main/manifest.json',
(_, res, ctx) =>
res(
ctx.status(200),
ctx.json({
packages: [
{
name: '@backstage/backend-app-api',
version: '2.0.0',
},
],
}),
),
),
);
const { log: logs } = await withLogCollector(['log', 'warn'], async () => {
await bump({ pattern: null, release: 'main' } as unknown as Command);
});
expectLogsToMatch(logs, [
'Using default pattern glob @backstage/*',
'Checking for updates of @backstage/backend-app-api',
'Checking for updates of @backstage/backend-app-api',
'Some packages are outdated, updating',
'bumping @backstage/backend-app-api in a to ^2.0.0',
'Running yarn install to install new versions',
'Checking for moved packages to the @backstage-community namespace...',
'⚠️ The following packages may have breaking changes:',
' @backstage/backend-app-api : 1.0.0 ~> 2.0.0',
' https://github.com/backstage/backstage/blob/master/packages/backend-app-api/CHANGELOG.md',
'Version bump complete!',
' ⚠️ Warning! ⚠️',
' The below package(s) have incompatible duplicate installations, likely due to a bad dependency in a plugin.',
' You can investigate this by running `yarn why <package-name>`, and report the issue to the plugin maintainers.',
' @backstage/backend-app-api',
]);
});
});
describe('bumpBackstageJsonVersion', () => {
+1 -97
View File
@@ -24,7 +24,6 @@ import fs from 'fs-extra';
import chalk from 'chalk';
import ora from 'ora';
import semver from 'semver';
import { minimatch } from 'minimatch';
import { OptionValues } from 'commander';
import { isError, NotFoundError } from '@backstage/errors';
import { resolve as resolvePath } from 'path';
@@ -36,7 +35,6 @@ import {
Lockfile,
YarnInfoInspectData,
} from '../../lib/versioning';
import { forbiddenDuplicatesFilter } from './lint';
import { BACKSTAGE_JSON } from '@backstage/cli-common';
import { runParallelWorkers } from '../../lib/parallel';
import {
@@ -44,7 +42,6 @@ import {
getManifestByVersion,
ReleaseManifest,
} from '@backstage/release-manifests';
import { PackageGraph } from '@backstage/cli-node';
import { migrateMovedPackages } from './migrate';
function shouldUseGlobalAgent(): boolean {
@@ -125,8 +122,6 @@ export default async (opts: OptionValues) => {
// Next check with the package registry to see which dependency ranges we need to bump
const versionBumps = new Map<string, PkgVersionInfo[]>();
// Track package versions that we want to remove from yarn.lock in order to trigger a bump
const unlocked = Array<{ name: string; range: string; target: string }>();
await runParallelWorkers({
parallelismFactor: 4,
@@ -157,72 +152,13 @@ export default async (opts: OptionValues) => {
},
});
const filter = (name: string) => minimatch(name, pattern);
// Check for updates of transitive backstage dependencies
await runParallelWorkers({
parallelismFactor: 4,
items: lockfile.keys(),
async worker(name) {
// Only check @backstage packages and friends, we don't want this to do a full update of all deps
if (!filter(name)) {
return;
}
let target: string;
try {
target = await findTargetVersion(name);
} catch (error) {
if (isError(error) && error.name === 'NotFoundError') {
console.log(`Package info not found, ignoring package ${name}`);
return;
}
throw error;
}
for (const entry of lockfile.get(name) ?? []) {
// Ignore lockfile entries that don't satisfy the version range, since
// these can't cause the package to be locked to an older version
if (!semver.satisfies(target, entry.range)) {
continue;
}
// Unlock all entries that are within range but on the old version
unlocked.push({ name, range: entry.range, target });
}
},
});
console.log();
// Write all discovered version bumps to package.json in this repo
if (versionBumps.size === 0 && unlocked.length === 0) {
if (versionBumps.size === 0) {
console.log(chalk.green('All Backstage packages are up to date!'));
} else {
console.log(chalk.yellow('Some packages are outdated, updating'));
console.log();
if (unlocked.length > 0) {
const removed = new Set<string>();
for (const { name, range, target } of unlocked) {
// Don't bother removing lockfile entries if they're already on the correct version
const existingEntry = lockfile.get(name)?.find(e => e.range === range);
if (existingEntry?.version === target) {
continue;
}
const key = JSON.stringify({ name, range });
if (!removed.has(key)) {
removed.add(key);
console.log(
`${chalk.magenta('unlocking')} ${name}@${chalk.yellow(
range,
)} ~> ${chalk.yellow(target)}`,
);
lockfile.remove(name, range);
}
}
await lockfile.save(lockfilePath);
}
const breakingUpdates = new Map<string, { from: string; to: string }>();
await runParallelWorkers({
parallelismFactor: 4,
@@ -334,38 +270,6 @@ export default async (opts: OptionValues) => {
}
console.log();
// Finally we make sure the new lockfile doesn't have any duplicates
const dedupLockfile = await Lockfile.load(lockfilePath);
const result = dedupLockfile.analyze({
filter,
localPackages: PackageGraph.fromPackages(
await PackageGraph.listTargetPackages(),
),
});
const forbiddenNewRanges = result.newRanges.filter(({ name }) =>
forbiddenDuplicatesFilter(name),
);
if (forbiddenNewRanges.length > 0) {
console.log(chalk.yellow(' ⚠️ Warning! ⚠️'));
console.log();
console.log(
chalk.yellow(
' The below package(s) have incompatible duplicate installations, likely due to a bad dependency in a plugin.',
),
);
console.log(
chalk.yellow(
' You can investigate this by running `yarn why <package-name>`, and report the issue to the plugin maintainers.',
),
);
console.log();
for (const { name } of forbiddenNewRanges) {
console.log(chalk.yellow(` ${name}`));
}
}
};
export function createStrictVersionFinder(options: {
+3 -111
View File
@@ -14,116 +14,8 @@
* limitations under the License.
*/
import { OptionValues } from 'commander';
import { Lockfile } from '../../lib/versioning';
import { paths } from '../../lib/paths';
import partition from 'lodash/partition';
import { PackageGraph } from '@backstage/cli-node';
// Packages that we try to avoid duplicates for
const INCLUDED = [/^@backstage\//];
export const includedFilter = (name: string) =>
INCLUDED.some(pattern => pattern.test(name));
// Packages that are not allowed to have any duplicates
const FORBID_DUPLICATES = [/^@backstage\/\w+-app-api$/, /^@backstage\/plugin-/];
// There are some packages that ARE explicitly allowed to have duplicates since
// they handle that appropriately. This takes precedence over FORBID_DUPLICATES
// above.
const ALLOW_DUPLICATES = [
/^@backstage\/core-plugin-api$/,
// Duplicates of libraries are OK
// TODO(Rugvip): Check this using package role instead
/^@backstage\/plugin-.*-react$/,
/^@backstage\/plugin-.*-node$/,
/^@backstage\/plugin-.*-common$/,
];
export const forbiddenDuplicatesFilter = (name: string) =>
FORBID_DUPLICATES.some(pattern => pattern.test(name)) &&
!ALLOW_DUPLICATES.some(pattern => pattern.test(name));
export default async (cmd: OptionValues) => {
const fix = Boolean(cmd.fix);
let success = true;
const lockfilePath = paths.resolveTargetRoot('yarn.lock');
const lockfile = await Lockfile.load(lockfilePath);
const result = lockfile.analyze({
filter: includedFilter,
localPackages: PackageGraph.fromPackages(
await PackageGraph.listTargetPackages(),
),
});
logArray(
result.invalidRanges,
"The following packages versions are invalid and can't be analyzed:",
e => ` ${e.name} @ ${e.range}`,
export default async () => {
throw new Error(
'This command has been removed, please consider alternatives such as `yarn dedupe` instead.',
);
if (fix) {
lockfile.replaceVersions(result.newVersions);
await lockfile.save(lockfilePath);
} else {
const [newVersionsForbidden, newVersionsAllowed] = partition(
result.newVersions,
({ name }) => forbiddenDuplicatesFilter(name),
);
if (newVersionsForbidden.length && !fix) {
success = false;
}
logArray(
newVersionsForbidden,
'The following packages must be deduplicated, this can be done automatically with --fix',
e =>
` ${e.name} @ ${e.range} bumped from ${e.oldVersion} to ${e.newVersion}`,
);
logArray(
newVersionsAllowed,
'The following packages can be deduplicated, this can be done automatically with --fix',
e =>
` ${e.name} @ ${e.range} bumped from ${e.oldVersion} to ${e.newVersion}`,
);
}
const [newRangesForbidden, newRangesAllowed] = partition(
result.newRanges,
({ name }) => forbiddenDuplicatesFilter(name),
);
if (newRangesForbidden.length) {
success = false;
}
logArray(
newRangesForbidden,
'The following packages must be deduplicated by updating dependencies in package.json',
e => ` ${e.name} @ ${e.oldRange} should be changed to ${e.newRange}`,
);
logArray(
newRangesAllowed,
'The following packages can be deduplicated by updating dependencies in package.json',
e => ` ${e.name} @ ${e.oldRange} should be changed to ${e.newRange}`,
);
if (!success) {
throw new Error('Failed versioning check');
}
};
function logArray<T>(arr: T[], header: string, each: (item: T) => string) {
if (arr.length === 0) {
return;
}
console.log(header);
console.log();
for (const e of arr) {
console.log(each(e));
}
console.log();
}
+1
View File
@@ -42,6 +42,7 @@ export async function buildBundle(options: BuildOptions) {
const paths = resolveBundlingPaths(options);
const publicPaths = await resolveOptionalBundlingPaths({
targetDir: options.targetDir,
entry: 'src/index-public-experimental',
dist: 'dist/public',
});
+106 -9
View File
@@ -14,8 +14,13 @@
* limitations under the License.
*/
import { BackendBundlingOptions, BundlingOptions } from './types';
import { posix as posixPath, resolve as resolvePath } from 'path';
import {
BackendBundlingOptions,
BundlingOptions,
ModuleFederationOptions,
} from './types';
import { posix as posixPath, resolve as resolvePath, dirname } from 'path';
import chalk from 'chalk';
import webpack, { ProvidePlugin } from 'webpack';
import { BackstagePackage } from '@backstage/cli-node';
@@ -28,12 +33,13 @@ import { ModuleFederationPlugin } from '@module-federation/enhanced/webpack';
import { LinkedPackageResolvePlugin } from './LinkedPackageResolvePlugin';
import ModuleScopePlugin from 'react-dev-utils/ModuleScopePlugin';
import { RunScriptWebpackPlugin } from 'run-script-webpack-plugin';
import ReactRefreshPlugin from '@pmmmwh/react-refresh-webpack-plugin';
import { paths as cliPaths } from '../../lib/paths';
import fs from 'fs-extra';
import { getPackages } from '@manypkg/get-packages';
import { isChildPath } from '@backstage/cli-common';
import nodeExternals from 'webpack-node-externals';
import { optimization } from './optimization';
import { optimization as optimizationConfig } from './optimization';
import pickBy from 'lodash/pickBy';
import { readEntryPoints } from '../entryPoints';
import { runPlain } from '../run';
@@ -44,15 +50,42 @@ import { hasReactDomClient } from './hasReactDomClient';
const BUILD_CACHE_ENV_VAR = 'BACKSTAGE_CLI_EXPERIMENTAL_BUILD_CACHE';
export function resolveBaseUrl(config: Config): URL {
export function resolveBaseUrl(
config: Config,
moduleFederation?: ModuleFederationOptions,
): URL {
const baseUrl = config.getOptionalString('app.baseUrl');
const defaultBaseUrl =
moduleFederation?.mode === 'remote'
? `http://localhost:${process.env.PORT ?? '3000'}`
: 'http://localhost:3000';
try {
return new URL(baseUrl ?? '/', 'http://localhost:3000');
return new URL(baseUrl ?? '/', defaultBaseUrl);
} catch (error) {
throw new Error(`Invalid app.baseUrl, ${error}`);
}
}
export function resolveEndpoint(
config: Config,
moduleFederation?: ModuleFederationOptions,
): {
host: string;
port: number;
} {
const url = resolveBaseUrl(config, moduleFederation);
return {
host: config.getOptionalString('app.listen.host') ?? url.hostname,
port:
config.getOptionalNumber('app.listen.port') ??
Number(url.port) ??
(url.protocol === 'https:' ? 443 : 80),
};
}
async function readBuildInfo() {
const timestamp = Date.now();
@@ -93,7 +126,13 @@ export async function createConfig(
paths: BundlingPaths,
options: BundlingOptions,
): Promise<webpack.Configuration> {
const { checksEnabled, isDev, frontendConfig, publicSubPath = '' } = options;
const {
checksEnabled,
isDev,
frontendConfig,
moduleFederation,
publicSubPath = '',
} = options;
const { plugins, loaders } = transforms(options);
// Any package that is part of the monorepo but outside the monorepo root dir need
@@ -101,12 +140,29 @@ export async function createConfig(
const { packages } = await getPackages(cliPaths.targetDir);
const externalPkgs = packages.filter(p => !isChildPath(paths.root, p.dir));
const validBaseUrl = resolveBaseUrl(frontendConfig);
const validBaseUrl = resolveBaseUrl(frontendConfig, moduleFederation);
let publicPath = validBaseUrl.pathname.replace(/\/$/, '');
if (publicSubPath) {
publicPath = `${publicPath}${publicSubPath}`.replace('//', '/');
}
if (isDev) {
const { host, port } = resolveEndpoint(
options.frontendConfig,
options.moduleFederation,
);
plugins.push(
new ReactRefreshPlugin({
overlay: {
sockProtocol: 'ws',
sockHost: host,
sockPort: port,
},
}),
);
}
if (checksEnabled) {
plugins.push(
new ForkTsCheckerWebpackPlugin({
@@ -232,12 +288,53 @@ export async function createConfig(
require.resolve('react-refresh'),
];
const mode = isDev ? 'development' : 'production';
const optimization = optimizationConfig(options);
if (
mode === 'production' &&
process.env.EXPERIMENTAL_MODULE_FEDERATION &&
process.env.FORCE_REACT_DEVELOPMENT
) {
console.log(
chalk.yellow(
`⚠️ WARNING: Forcing react and react-dom into development mode. This build should not be used in production.`,
),
);
const reactPackageDirs = [
`${dirname(require.resolve('react/package.json'))}/`,
`${dirname(require.resolve('react-dom/package.json'))}/`,
];
// Don't define process.env.NODE_ENV with value matching config.mode. If we
// don't set this to false, webpack will define the value of
// process.env.NODE_ENV for us, and the definition below will be ignored.
optimization.nodeEnv = false;
// Instead, provide a custom definition which always uses "development" if
// the module is part of `react` or `react-dom`, and `config.mode` otherwise.
plugins.push(
new webpack.DefinePlugin({
'process.env.NODE_ENV': webpack.DefinePlugin.runtimeValue(
({ module }) => {
if (reactPackageDirs.some(val => module.resource.startsWith(val))) {
return '"development"';
}
return `"${mode}"`;
},
),
}),
);
}
const withCache = yn(process.env[BUILD_CACHE_ENV_VAR], { default: false });
return {
mode: isDev ? 'development' : 'production',
mode,
profile: false,
optimization: optimization(options),
optimization,
bail: false,
performance: {
hints: false, // we check the gzip size instead
+1
View File
@@ -16,4 +16,5 @@
export { serveBackend } from './backend';
export { buildBundle } from './bundle';
export { getModuleFederationOptions } from './moduleFederation';
export { serveBundle } from './server';

Some files were not shown because too many files have changed in this diff Show More