Compare commits

...

88 Commits

Author SHA1 Message Date
backstage-goalie[bot] c2e852b3a3 Merge pull request #34434 from backstage/renovate/pg-8.x-lockfile
Deploy Microsite / stable (push) Failing after 10m52s
Deploy Microsite / next (push) Failing after 1m31s
Deploy Microsite / deploy-microsite-and-storybook (push) Has been skipped
Deploy Packages / build (22.x) (push) Failing after 4m12s
Deploy Packages / build (24.x) (push) Failing after 1m43s
Scorecard supply-chain security / Scorecard analysis (push) Failing after 10m45s
Sync BUI Docs / sync-docs-ui (push) Failing after 1m18s
Sync code formatting / Autofix Markdown files using Prettier (push) Failing after 4m29s
Sync Version Packages PR / Create Changeset PR (push) Failing after 20s
Chromatic / Chromatic (22.x, ubuntu-latest) (push) Failing after 5m52s
Verify CodeQL / Analyze (javascript) (push) Failing after 10m46s
E2E Linux / E2E Linux 22.x (push) Failing after 1m31s
E2E Linux / E2E Linux 24.x (push) Failing after 1m47s
E2E Techdocs / Techdocs (22.x) (push) Failing after 3m4s
E2E Techdocs / Techdocs (24.x) (push) Failing after 1m36s
Verify FOSSA / analyze (push) Failing after 44s
Deploy Packages / release (22.x) (push) Has been skipped
E2E Windows / E2E Windows 22.x (push) Has been cancelled
E2E Windows / E2E Windows 24.x (push) Has been cancelled
Verify Master Branch on Windows / build (22.x) (push) Has been cancelled
Verify Master Branch on Windows / build (24.x) (push) Has been cancelled
Update dependency pg to v8.21.0
2026-06-01 16:46:19 +00:00
renovate[bot] 372eb5ab6b Update dependency pg to v8.21.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-06-01 15:23:52 +00:00
Johan Persson 1f709a386b fix(ui): preserve Header breadcrumb typography
Increase the specificity of Header breadcrumb styles so that Link defaults do not override contextual typography when stylesheets are loaded in a different order.

Add a patch changeset for @backstage/ui.

Signed-off-by: Johan Persson <johanopersson@gmail.com>
2026-06-01 11:32:46 +02:00
Fredrik Adelöw 0d8d90b351 Merge pull request #34368 from backstage/renovate/rollup-4.x-lockfile
Update dependency rollup to v4.60.4
2026-05-29 20:54:08 +02:00
Fredrik Adelöw b55138e4ba Merge pull request #34444 from backstage/freben/fix-ci-timing-flakes
fix: increase timing thresholds in CI-flaky database tests
2026-05-29 17:36:39 +02:00
Fredrik Adelöw 496982782b fix: fix CI-flaky database test timing
DatabaseEventBusStore: 500ms → 2000ms for the performance threshold
when cleaning 100k rows — this is intentionally a perf test so a real
time assertion is correct, it just needs more headroom for CI.

DatabaseKeyStore: replace fixed 500ms sleep with waitForExpect polling
so the test adapts to however long the async deletion actually takes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@gmail.com>
2026-05-29 16:56:21 +02:00
Charles de Dreuille c2f9d8c25d Merge pull request #34400 from backstage/feat/bui-token-redesign
feat(ui): introduce semantic color token families and deprecate legacy tokens
2026-05-29 15:40:20 +01:00
Charles de Dreuille 78f6fd3b5b Small fixes
Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
2026-05-29 16:07:56 +02:00
Charles de Dreuille a4f2c56110 Bring back correct neutral tokens
Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
2026-05-29 15:59:39 +02:00
Charles de Dreuille 64c8108166 Move to minor
Signed-off-by: Charles de Dreuille <charles.dedreuille@gmail.com>
2026-05-29 13:35:11 +02:00
Fredrik Adelöw 8f7c02a525 Merge pull request #34425 from backstage/freben/fix-msgraph-group-member-filter
fix(catalog-backend-module-msgraph): filter disabled group members client-side
2026-05-29 10:41:55 +02:00
Fredrik Adelöw 39e0c4f155 Introduce MINIMUM_USER_SELECT with id and accountEnabled
Replace ensureSelectContains with ensureMinimumSelect that adds all
fields our code requires (id for photo fetching and Map keys,
accountEnabled for disabled user filtering). Separates the minimum
viable set from the default projection list.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@gmail.com>
2026-05-29 10:11:42 +02:00
backstage-goalie[bot] 854c88cde4 Merge pull request #34433 from backstage/renovate/node-gyp-12.x-lockfile
chore(deps): update dependency node-gyp to v12.3.0
2026-05-28 20:22:36 +00:00
renovate[bot] 89f08c5304 chore(deps): update dependency node-gyp to v12.3.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-28 19:40:08 +00:00
backstage-goalie[bot] 19c94c49e8 Merge pull request #34432 from backstage/renovate/mysql2-3.x-lockfile
chore(deps): update dependency mysql2 to v3.22.3
2026-05-28 19:30:01 +00:00
renovate[bot] 813e7d9ef6 chore(deps): update dependency mysql2 to v3.22.3
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-28 18:42:16 +00:00
backstage-goalie[bot] d0e64a1603 Merge pull request #34429 from backstage/renovate/isomorphic-git-1.x-lockfile
chore(deps): update dependency isomorphic-git to v1.38.2
2026-05-28 18:33:22 +00:00
backstage-goalie[bot] b2117003c4 Merge pull request #34428 from backstage/renovate/ipaddr.js-2.x-lockfile
chore(deps): update dependency ipaddr.js to v2.4.0
2026-05-28 18:33:17 +00:00
renovate[bot] 11f2765e63 chore(deps): update dependency isomorphic-git to v1.38.2
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-28 17:48:23 +00:00
renovate[bot] afe3160dfa chore(deps): update dependency ipaddr.js to v2.4.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-28 17:47:53 +00:00
backstage-goalie[bot] 6ef44643ee Merge pull request #34391 from backstage/renovate/globals-17.x-lockfile
chore(deps): update dependency globals to v17.6.0
2026-05-28 17:36:36 +00:00
backstage-goalie[bot] ddd22005e9 Merge pull request #34390 from backstage/renovate/express-rate-limit-8.x-lockfile
chore(deps): update dependency express-rate-limit to v8.5.2
2026-05-28 17:36:31 +00:00
Fredrik Adelöw 37b29c240b Merge pull request #34427 from halkeye/patch-2 2026-05-28 18:38:40 +02:00
Gavin Mogan 47fc640b49 fix(catalog-backend-module-backstage-openapi): Remove extra quote in example config
Signed-off-by: Gavin Mogan <github@gavinmogan.com>
2026-05-28 09:02:22 -07:00
Fredrik Adelöw 4857351bf3 Merge pull request #34415 from stijnbrouwers/bugfix/gitlab-archive-fetch 2026-05-28 17:52:36 +02:00
Fredrik Adelöw 1fc84c8df8 Always request accountEnabled in $select
The Microsoft Graph API does NOT include accountEnabled in the
default user projection — it requires an explicit $select. Without
it, the client-side filterDisabledUsers check cannot work because
the field is undefined in the response.

Add a DEFAULT_USER_SELECT constant with all default Graph API user
fields plus accountEnabled. ensureSelectContains now falls back to
this list when no custom userSelect is configured.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@gmail.com>
2026-05-28 17:24:37 +02:00
Fredrik Adelöw bbb9ca105f Fix inconsistent accountEnabled wording across docs and changeset
Consistently use 'accountEnabled === false' everywhere to describe
which users are filtered out.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@gmail.com>
2026-05-28 17:02:02 +02:00
Fredrik Adelöw 44f017668b Fix remaining stale docs and add mutual exclusivity tests
- Fix two remaining config.d.ts entries that still referenced the
  automatic accountEnabled base filter
- Add tests verifying that userFilter + userGroupMemberFilter and
  userFilter + userGroupMemberSearch are rejected

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@gmail.com>
2026-05-28 16:53:20 +02:00
renovate[bot] 593d011eea chore(deps): update dependency globals to v17.6.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-28 14:49:14 +00:00
renovate[bot] ce6a326c5c chore(deps): update dependency express-rate-limit to v8.5.2
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-28 14:48:45 +00:00
renovate[bot] 9dfb8948ea chore(deps): update dependency rollup to v4.60.4
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-28 14:45:49 +00:00
Fredrik Adelöw c630594e7a Merge pull request #34423 from backstage/freben/enable-corepack-ci
ci: enable corepack in setup-node steps
2026-05-28 16:27:26 +02:00
Fredrik Adelöw 42a907dfe0 Add test coverage for both user paths with and without userSelect
Cover the disabled user filtering and select augmentation for both
the /users and group members paths, with and without userSelect
configured.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@gmail.com>
2026-05-28 15:55:33 +02:00
Fredrik Adelöw 72152935ef Use !== false for safer disabled user filtering
The === true check would silently drop all users if accountEnabled
is not in the API response (e.g. if the default projection omits
it). The !== false check is safer: it only filters users that are
explicitly disabled, and passes through users with unset
accountEnabled (same as pre-v1.51.0 behavior).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@gmail.com>
2026-05-28 15:53:25 +02:00
Fredrik Adelöw 5b07b4ef04 Simplify ensureSelectContains to accept undefined, inline calls
Accept undefined input (returns undefined, preserving default
projection). Inline the calls at both use sites per review feedback.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@gmail.com>
2026-05-28 15:37:20 +02:00
Fredrik Adelöw ce2e763606 Address review: use === true, update docs, add tests
- Use accountEnabled === true instead of !== false (stricter,
  excludes users with unset accountEnabled)
- Update doc comments to mention client-side filtering and remove
  accountEnabled from the filter example
- Update changeset to mention userGroupMemberSearch exclusivity
- Add accountEnabled: true to test mocks
- Add regression tests for disabled user filtering

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@gmail.com>
2026-05-28 15:33:08 +02:00
Fredrik Adelöw 8930d77157 fix(catalog-backend-module-msgraph): filter disabled users client-side
Revert the server-side accountEnabled base filter from #34165 which
broke the group members endpoint. Filter disabled users client-side
in both the /users and group members paths. Restore the mutual
exclusivity check between userFilter and userGroupMemberFilter.

Fixes #34422

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@gmail.com>
2026-05-28 15:24:48 +02:00
Fredrik Adelöw 3346845548 Merge pull request #34421 from backstage/freben/mysql-connect-timeout
fix(backend-test-utils): increase MySQL connect and pool timeouts
2026-05-28 10:21:42 +02:00
Fredrik Adelöw ebcc8b7ca9 ci: enable corepack in setup-node steps
The actions/setup-node@v6.4.0 upgrade stopped auto-shimming yarn onto
PATH via corepack. The yarn-plugin test spawns yarn via Node's
child_process.spawn() which does a raw PATH lookup, causing
ENOENT failures. Adding corepack: true ensures the yarn shim is
available.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@gmail.com>
2026-05-28 10:20:37 +02:00
Stijn Brouwers 3692a346a5 chore(gitlabUrlReader): Add comment and patch file
Signed-off-by: Stijn Brouwers <stijn@bdcommit.com>
2026-05-28 09:26:05 +02:00
Stijn Brouwers 05d4126db0 chore(code-review): Use single quote instead of ticks
Signed-off-by: Stijn Brouwers <stijn@bdcommit.com>
2026-05-28 09:13:38 +02:00
Ahmet Oğuzhan Engin f9cee7bc13 docs: add backend.startup configuration documentation (#34137)
* docs: add backend.startup configuration documentation

Add a new 'Startup Configuration' section at the bottom of the
building-backends guide documenting the backend.startup config block.

- Explains onPluginBootFailure per-plugin control
- Explains onPluginModuleBootFailure per-module control
- Shows how to set a global default via backend.startup.default
- Includes a full YAML configuration reference

Closes #34075

Signed-off-by: ahmetoguzhanengin <ahmetoguzhanengin@gmail.com>

* docs: fix backend.startup examples per Copilot review

- Replace 'githubEntityProvider' with 'github' (the actual moduleId)
  as declared in createBackendModule({ moduleId: '...' })
- Replace 'abort | continue' YAML values with 'abort # or continue'
  to avoid invalid literal string interpretation

Signed-off-by: Ahmet Oğuzhan Engin <ahmetoguzhanengin@gmail.com>
Signed-off-by: ahmetoguzhanengin <ahmetoguzhanengin@gmail.com>

---------

Signed-off-by: ahmetoguzhanengin <ahmetoguzhanengin@gmail.com>
Signed-off-by: Ahmet Oğuzhan Engin <ahmetoguzhanengin@gmail.com>
2026-05-27 23:20:32 -04:00
Fredrik Adelöw 2d181c035d Add changeset
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@gmail.com>
2026-05-27 22:37:22 +02:00
Fredrik Adelöw 2a85164e49 Use cheaper auth plugin and skip binlog for test MySQL containers
Switch the dockerized MySQL container to mysql_native_password (single
challenge-response) instead of the default caching_sha2_password
(extra RSA key exchange round-trip on non-TLS connections). Also
disable binary logging since tests don't need replication. Both
reduce per-connection overhead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@gmail.com>
2026-05-27 22:36:57 +02:00
Fredrik Adelöw 6fe88aabb3 fix(backend-test-utils): increase MySQL connect and pool timeouts
The mysql2 driver defaults to a 10-second connect timeout, which is
too short when many test suites share a single MySQL container under
CI load. Increase the connect timeout to 30 seconds and add pool
acquire/create timeouts of 30 seconds to reduce flaky ETIMEDOUT
failures.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@gmail.com>
2026-05-27 22:35:31 +02:00
backstage-goalie[bot] a71ae90198 Merge pull request #34417 from backstage/renovate/graphql-16.x-lockfile
chore(deps): update dependency graphql to v16.14.0
2026-05-27 16:49:38 +00:00
James Brooks b33bb24b5a Add NumberField component to @backstage/ui (#34264)
* Add NumberField component to @backstage/ui

Signed-off-by: James Brooks <jamesbrooks@spotify.com>

* Address review feedback on NumberField

Signed-off-by: James Brooks <jamesbrooks@spotify.com>

* Fix NumberField CSS formatting

Signed-off-by: James Brooks <jamesbrooks@spotify.com>

* Add increment/decrement buttons to NumberField

Signed-off-by: James Brooks <jamesbrooks@spotify.com>

* Fix NumberField looking disabled at min/max bounds

Signed-off-by: James Brooks <jamesbrooks@spotify.com>

---------

Signed-off-by: James Brooks <jamesbrooks@spotify.com>
2026-05-27 17:20:27 +02:00
Fredrik Adelöw 58fb313f22 Merge pull request #34398 from robingileborg/master
Bitbucket username should not be treated as a secret
2026-05-27 16:29:50 +02:00
Fredrik Adelöw 18644ee7a2 Merge pull request #34406 from F-Secure-web/replace-react-use-recommendations-with-react-hookz-web
docs: Replace `react-use` recommendations with `@react-hookz/web`
2026-05-27 16:13:10 +02:00
Fredrik Adelöw 5aa867c86f Merge pull request #34416 from backstage/freben/fix-missing-runtime-deps
fix: restore runtime dependencies incorrectly demoted to devDependencies
2026-05-27 15:30:53 +02:00
renovate[bot] 28e5198e23 chore(deps): update dependency graphql to v16.14.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-27 13:07:38 +00:00
Fredrik Adelöw 150f290178 Merge pull request #31613 from backstage/renovate/azure-sdk-for-js-monorepo
chore(deps): update azure-sdk-for-js monorepo
2026-05-27 14:55:44 +02:00
Fredrik Adelöw de9873e142 chore: add patch entry for #34416
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@gmail.com>
2026-05-27 14:52:26 +02:00
Fredrik Adelöw 378784ebf0 fix: restore runtime dependencies incorrectly demoted to devDependencies
PR #33936 removed duplicate dependency entries, but in two cases moved
deps from dependencies to devDependencies that are still re-exported in
the published API reports:

- @backstage/catalog-client: @backstage/plugin-catalog-common
  (AnalyzeLocationRequest/AnalyzeLocationResponse types)
- @backstage/frontend-plugin-api: @backstage/config (Config type)

These need to be runtime dependencies so consumers can resolve the
types at build time.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@gmail.com>
2026-05-27 14:51:28 +02:00
Stijn Brouwers 34f21c39a5 chore(changeset): Add PR changeset
Signed-off-by: Stijn Brouwers <stijn@bdcommit.com>
2026-05-27 14:32:51 +02:00
Stijn Brouwers 70fc9e0370 bugfix(gitlabUrlReader): Fix issue with repository archive retrieval
Signed-off-by: Stijn Brouwers <stijn@bdcommit.com>
2026-05-27 14:29:41 +02:00
Fredrik Adelöw 55009be8a4 Merge pull request #34413 from backstage/freben/revert-seed-protobufjs-unpin
Revert protobufjs seed lockfile unpin
2026-05-27 13:48:43 +02:00
Fredrik Adelöw 826788803e Revert "try to remove protojs pins"
This reverts commit b15f74b62f.

Signed-off-by: Fredrik Adelöw <freben@gmail.com>
2026-05-27 12:29:38 +02:00
Fredrik Adelöw 73ac7983a6 Merge pull request #34411 from backstage/dependabot/npm_and_yarn/tmp-0.2.6 2026-05-27 07:36:18 +02:00
dependabot[bot] 635eea3ac7 chore(deps): bump tmp from 0.2.5 to 0.2.6
Bumps [tmp](https://github.com/raszi/node-tmp) from 0.2.5 to 0.2.6.
- [Changelog](https://github.com/raszi/node-tmp/blob/master/CHANGELOG.md)
- [Commits](https://github.com/raszi/node-tmp/compare/v0.2.5...v0.2.6)

---
updated-dependencies:
- dependency-name: tmp
  dependency-version: 0.2.6
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-05-27 01:26:30 +00:00
backstage-goalie[bot] 8d2316f276 Merge pull request #34410 from backstage/renovate/nodemailer-8.x-lockfile
chore(deps): update dependency nodemailer to v8.0.8
2026-05-27 01:21:39 +00:00
renovate[bot] 74b16ec203 chore(deps): update dependency nodemailer to v8.0.8
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-27 00:21:54 +00:00
backstage-goalie[bot] d43186ac5a Merge pull request #34388 from backstage/renovate/cleye-2.x-lockfile
chore(deps): update dependency cleye to v2.6.0
2026-05-27 00:14:53 +00:00
backstage-goalie[bot] 2b9bb3c4d2 Merge pull request #34387 from backstage/renovate/better-sqlite3-12.x-lockfile
chore(deps): update dependency better-sqlite3 to v12.10.0
2026-05-27 00:14:48 +00:00
Timo Sand d2c181f539 Update TOC
Signed-off-by: Timo Sand <timo.sand@f-secure.com>
2026-05-27 01:02:06 +03:00
Timo Sand f56177b47e Replace mentions of react-use in docs with @react-hookz/web
Signed-off-by: Timo Sand <timo.sand@f-secure.com>
2026-05-27 01:01:20 +03:00
Patrik Oldsberg ada73f2ba4 Merge pull request #31466 from jescalada/31624-fix-autologout-bug
fix: auto-logout not working when closing tabs and reopening
2026-05-26 18:13:26 +02:00
Fredrik Adelöw 65456c31b6 Merge pull request #34319 from backstage/changeset-release/master
Version Packages (next)
2026-05-26 18:04:57 +02:00
github-actions[bot] 68db890456 Version Packages (next) 2026-05-26 15:26:38 +00:00
Fredrik Adelöw b75158b2c1 chore: add changeset for Azure SDK test fixes
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@gmail.com>
2026-05-26 17:18:31 +02:00
Fredrik Adelöw 72db53e9fb Remove stale credsManager field from conflict resolution
The field was accidentally kept from master during the rebase
conflict resolution but doesn't belong in this PR's version of
the file.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@gmail.com>
2026-05-26 17:18:31 +02:00
Fredrik Adelöw 8dae41216d chore: update API report for AzureBlobStorageUrlReader
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@gmail.com>
2026-05-26 17:18:31 +02:00
Fredrik Adelöw 843f71caf4 fix: adapt tests for Azure SDK upgrade to ESM-style exports
The Azure SDK monorepo upgrade (storage-blob 12.26→12.31, identity
4.5→4.9) adds "type": "module" to package.json, making jest.mock()
unable to intercept imports from production code.

- AzureBlobStorageUrlReader: accept createContainerClient as an
  optional dependency, letting tests pass a mock directly instead
  of trying to mock the @azure/storage-blob module
- AzureUrlReader: provide PAT credentials in all test cases so
  DefaultAzureCredential is never instantiated — the Bearer auth
  flow is already covered by the integration package's own tests
- DefaultAzureDevOpsCredentialsProvider: use expect.any() for
  mock instance comparison instead of new DefaultAzureCredential()

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Fredrik Adelöw <freben@gmail.com>
2026-05-26 17:18:31 +02:00
renovate[bot] a30cf94882 chore(deps): update azure-sdk-for-js monorepo
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-26 17:18:31 +02:00
Robin Gileborg 241d359913 Bitbucket username should not be treated as a secret
Signed-off-by: Robin Gileborg <robin@gileborg.com>
2026-05-26 13:19:08 +02:00
renovate[bot] cd677edc56 chore(deps): update dependency cleye to v2.6.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-25 23:15:59 +00:00
renovate[bot] f2698df4a6 chore(deps): update dependency better-sqlite3 to v12.10.0
Signed-off-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2026-05-25 23:15:12 +00:00
Juan Escalada e8c67ea82c test: add test for deleting store on autologout timeout, remove unused import
Signed-off-by: Juan Escalada <juanescalada175@gmail.com>
2026-05-13 13:18:47 +09:00
Juan Escalada 19b5047c12 fix: refactor disconnectedUsers for readability
Signed-off-by: Juan Escalada <juanescalada175@gmail.com>
2026-05-13 13:11:54 +09:00
Juan Escalada 92c3435d9e fix: state bug in onIdle
Signed-off-by: Juan Escalada <juanescalada175@gmail.com>
2026-05-13 13:11:54 +09:00
Juan Escalada 327cc8b09c fix: remove timeout for deleting lastOnline store
Signed-off-by: Juan Escalada <juanescalada175@gmail.com>
2026-05-13 13:11:54 +09:00
Juan Escalada c3e07a5cb3 fix: prevent unexpected signouts when disabling autologout
Signed-off-by: Juan Escalada <juanescalada175@gmail.com>
2026-04-30 12:04:03 +09:00
Juan Escalada 6b96557718 fix: add isLoggedRef and modify login checking logic
Signed-off-by: Juan Escalada <juanescalada175@gmail.com>
2026-04-30 11:45:48 +09:00
Juan Escalada 19a79d3f80 Update .changeset/pretty-parts-admire.md
Co-authored-by: Andre Wanlin <67169551+awanlin@users.noreply.github.com>
Signed-off-by: Juan Escalada <97265671+jescalada@users.noreply.github.com>
Signed-off-by: Juan Escalada <juanescalada175@gmail.com>
2026-04-30 11:43:09 +09:00
Juan Escalada dbe93a7aee chore: add changeset
Signed-off-by: Juan Escalada <juanescalada175@gmail.com>
2025-10-20 09:29:42 +09:00
Juan Escalada 61e0844bde test: amend existing disconnectedUsers tests and add new one for unresolved isLoggedIn
Signed-off-by: Juan Escalada <juanescalada175@gmail.com>
2025-10-18 21:45:00 +09:00
Juan Escalada 18430adf40 fix: amend AutoLogout logic to work with any provider and correctly logout
Signed-off-by: Juan Escalada <juanescalada175@gmail.com>
2025-10-18 21:44:59 +09:00
Juan Escalada 2259e99a6e fix: auto-logout not working when closing tabs and reopening
Signed-off-by: Juan Escalada <juanescalada175@gmail.com>
2025-10-18 12:31:34 +09:00
419 changed files with 5461 additions and 981 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/create-app': patch
---
Bumped create-app version.
@@ -2,4 +2,4 @@
'@backstage/eslint-plugin': patch
---
Adds a new `@backstage/no-deprecated-bui-tokens` lint rule that warns when a deprecated `@backstage/ui` CSS token is referenced in a JavaScript or TypeScript file. The rule is included in the `recommended` config, so plugin authors using `plugin:@backstage/recommended` will receive warnings automatically when using tokens that have been superseded by the new semantic color families.
Adds a new `@backstage/no-deprecated-bui-tokens` lint rule that warns when a deprecated `@backstage/ui` CSS token is referenced in a JavaScript or TypeScript file (including CSS-in-JS patterns and template literals). The rule is included in the `recommended` config, so plugin authors using `plugin:@backstage/recommended` will receive warnings automatically when using tokens that have been superseded by the new semantic color families. Note that plain CSS and CSS module files are outside ESLint's scope and are not covered by this rule.
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/backend-defaults': patch
'@backstage/integration': patch
---
Adapted Azure-related tests for the Azure SDK upgrade to ESM-style exports. The `AzureBlobStorageUrlReader` now accepts an optional `createContainerClient` dependency for testability without needing to mock the `@azure/storage-blob` module.
+6
View File
@@ -0,0 +1,6 @@
---
'@backstage/catalog-client': patch
'@backstage/frontend-plugin-api': patch
---
Moved dependencies that are re-exported in the public API from `devDependencies` to `dependencies`. These were incorrectly demoted in #33936 because the source code only uses type imports, but the types still appear in the published API surface and need to be resolvable by consumers at build time.
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-backend-module-msgraph': patch
---
Reverted the server-side `accountEnabled eq true` base filter that was added in v1.51.0, which broke the `userGroupMember` path because the group members endpoint doesn't support `$filter` on that property. Disabled users (`accountEnabled === false`) are now filtered client-side in both the `/users` and group members paths. The mutual exclusivity checks between `userFilter` and `userGroupMemberFilter`/`userGroupMemberSearch` have been restored.
@@ -0,0 +1,5 @@
---
'@backstage/integration': patch
---
Changed visibility of Bitbucket username as it is not a secret.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/backend-test-utils': patch
---
Increased MySQL connection and pool timeouts to reduce flaky `connect ETIMEDOUT` failures in CI. The test MySQL container now also uses `mysql_native_password` for cheaper connection handshakes and disables binary logging.
+32 -1
View File
@@ -227,5 +227,36 @@
"@backstage/plugin-user-settings-backend": "0.4.3",
"@backstage/plugin-user-settings-common": "0.1.0"
},
"changesets": []
"changesets": [
"catalog-backend-totalitems-mode",
"catalog-client-totalitems-mode",
"catalog-react-split-count",
"create-app-1779809101",
"dependabot-84d9635",
"dependabot-a525941",
"disable-experimental-public-entrypoint",
"eager-birds-fly",
"fast-stars-smile",
"fix-azure-blob-storage-entity-provider-import",
"fix-azure-blob-storage-integration-class-name",
"fix-azure-blob-storage-url-reader-type",
"fix-entity-list-triple-fetch",
"fix-home-translation-key-typo",
"fix-related-entities-card-property-typo",
"fix-table-filters-class-key-typo",
"fix-test-route-matching",
"legal-results-kneel",
"notifications-backend-openapi-router",
"plenty-worms-happen",
"proxied-signin-base64url-token",
"rate-limit-ipv6-key-generator",
"remove-immediate-stitching",
"remove-protobufjs-pins",
"renovate-dcee473",
"search-extended-statistics",
"split-query-entities-count",
"stitch-claim-transaction",
"stitch-queue-no-overlap",
"two-books-sleep"
]
}
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/core-components': patch
---
Fix autologout not working correctly when closing all tabs
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/backend-defaults': patch
---
Fix gitlabUrlReader issue with retrieving the repository archive tree
+7
View File
@@ -0,0 +1,7 @@
---
'@backstage/ui': patch
---
Added a new `NumberField` component for numeric input with support for min, max, step, and keyboard increment/decrement.
**Affected components:** NumberField
@@ -0,0 +1,7 @@
---
'@backstage/ui': patch
---
Fixed Header breadcrumb typography so it remains consistent when component styles are loaded in different orders.
**Affected components:** Header
+1 -11
View File
@@ -10,15 +10,7 @@ The previous tokens remain in place for backward compatibility but are now depre
**Neutral backgrounds**
The neutral background scale has been renamed and extended. `--bui-bg-app` is replaced by the new `--bui-bg-neutral-1`, and the old overlay-based `--bui-bg-neutral-1..4` shift up by one to become `--bui-bg-neutral-2..5`:
| Deprecated | Replacement |
| ---------------------------------- | -------------------- |
| `--bui-bg-app` | `--bui-bg-neutral-1` |
| `--bui-bg-neutral-1` (old overlay) | `--bui-bg-neutral-2` |
| `--bui-bg-neutral-2` (old overlay) | `--bui-bg-neutral-3` |
| `--bui-bg-neutral-3` (old overlay) | `--bui-bg-neutral-4` |
| `--bui-bg-neutral-4` (old overlay) | `--bui-bg-neutral-5` |
The neutral background tokens keep their existing names (`--bui-bg-app`, `--bui-bg-neutral-1` through `--bui-bg-neutral-4`) but are updated with new solid-color values for both light and dark themes. No token renaming is required. The `-hover`, `-pressed`, and `-disabled` interaction variants of these tokens are deprecated and should be removed.
**Foreground**
@@ -69,5 +61,3 @@ The neutral background scale has been renamed and extended. `--bui-bg-app` is re
| `--bui-bg-info` | `--bui-announcement-bg-subdued` |
| `--bui-fg-info-on-bg` | `--bui-announcement-fg-subdued` |
| `--bui-border-info` | `--bui-announcement-border` |
**Affected components:** Colors
@@ -31,6 +31,7 @@ jobs:
with:
node-version: 22.x
registry-url: https://registry.npmjs.org/
corepack: true
- name: yarn install
uses: backstage/actions/yarn-install@2cd6978b476cbdc39fec48346f8b6ca13199dd6a # v0.7.8
+3
View File
@@ -53,6 +53,7 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/ # Needed for auth
corepack: true
- name: yarn install
uses: backstage/actions/yarn-install@2cd6978b476cbdc39fec48346f8b6ca13199dd6a # v0.7.8
@@ -89,6 +90,7 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/ # Needed for auth
corepack: true
- name: yarn install
uses: backstage/actions/yarn-install@2cd6978b476cbdc39fec48346f8b6ca13199dd6a # v0.7.8
@@ -262,6 +264,7 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/ # Needed for auth
corepack: true
- name: yarn install
uses: backstage/actions/yarn-install@2cd6978b476cbdc39fec48346f8b6ca13199dd6a # v0.7.8
@@ -35,6 +35,7 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/ # Needed for auth
corepack: true
- name: yarn install
uses: backstage/actions/yarn-install@2cd6978b476cbdc39fec48346f8b6ca13199dd6a # v0.7.8
+3
View File
@@ -64,6 +64,7 @@ jobs:
with:
node-version: 22.x
registry-url: https://registry.npmjs.org/ # Needed for auth
corepack: true
- name: yarn install
uses: backstage/actions/yarn-install@2cd6978b476cbdc39fec48346f8b6ca13199dd6a # v0.7.8
@@ -137,6 +138,7 @@ jobs:
with:
node-version: 22.x
registry-url: https://registry.npmjs.org/ # Needed for auth
corepack: true
- name: yarn install
uses: backstage/actions/yarn-install@2cd6978b476cbdc39fec48346f8b6ca13199dd6a # v0.7.8
@@ -228,6 +230,7 @@ jobs:
with:
node-version: 22.x
registry-url: https://registry.npmjs.org/ # Needed for auth
corepack: true
# Stable docs
- name: checkout latest release
+1
View File
@@ -75,6 +75,7 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/ # Needed for auth
corepack: true
- name: yarn install
uses: backstage/actions/yarn-install@2cd6978b476cbdc39fec48346f8b6ca13199dd6a # v0.7.8
with:
@@ -30,6 +30,7 @@ jobs:
with:
node-version: 22.x
registry-url: https://registry.npmjs.org/ # Needed for auth
corepack: true
- name: yarn install
uses: backstage/actions/yarn-install@2cd6978b476cbdc39fec48346f8b6ca13199dd6a # v0.7.8
+1
View File
@@ -20,6 +20,7 @@ jobs:
with:
node-version: 22.x
registry-url: https://registry.npmjs.org/ # Needed for auth
corepack: true
- name: yarn install
uses: backstage/actions/yarn-install@2cd6978b476cbdc39fec48346f8b6ca13199dd6a # v0.7.8
@@ -24,6 +24,7 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/ # Needed for auth
corepack: true
- name: yarn install
uses: backstage/actions/yarn-install@2cd6978b476cbdc39fec48346f8b6ca13199dd6a # v0.7.8
with:
+1
View File
@@ -36,6 +36,7 @@ jobs:
with:
node-version: 22.x
registry-url: https://registry.npmjs.org/ # Needed for auth
corepack: true
- name: Configure Git
run: |
@@ -23,6 +23,7 @@ jobs:
with:
node-version: 22.x
registry-url: https://registry.npmjs.org/ # Needed for auth
corepack: true
- name: yarn install
uses: backstage/actions/yarn-install@2cd6978b476cbdc39fec48346f8b6ca13199dd6a # v0.7.8
@@ -23,6 +23,7 @@ jobs:
with:
node-version: 22.x
registry-url: https://registry.npmjs.org/ # Needed for auth
corepack: true
- name: yarn install
uses: backstage/actions/yarn-install@2cd6978b476cbdc39fec48346f8b6ca13199dd6a # v0.7.8
with:
@@ -34,6 +34,7 @@ jobs:
with:
node-version: 22.x
registry-url: https://registry.npmjs.org/ # Needed for auth
corepack: true
- name: Install Dependencies
run: yarn --immutable
+1
View File
@@ -38,6 +38,7 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/ # Needed for auth
corepack: true
- name: Install dependencies
uses: backstage/actions/yarn-install@2cd6978b476cbdc39fec48346f8b6ca13199dd6a # v0.7.8
+1
View File
@@ -71,6 +71,7 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/ # Needed for auth
corepack: true
- name: yarn install
uses: backstage/actions/yarn-install@2cd6978b476cbdc39fec48346f8b6ca13199dd6a # v0.7.8
with:
+1
View File
@@ -56,6 +56,7 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/ # Needed for auth
corepack: true
- name: setup python
uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
+2
View File
@@ -69,6 +69,7 @@ jobs:
with:
node-version: 22.x
registry-url: https://registry.npmjs.org/ # Needed for auth
corepack: true
- name: yarn install
uses: backstage/actions/yarn-install@2cd6978b476cbdc39fec48346f8b6ca13199dd6a # v0.7.8
@@ -139,6 +140,7 @@ jobs:
with:
node-version: 22.x
registry-url: https://registry.npmjs.org/ # Needed for auth
corepack: true
- name: yarn install
uses: backstage/actions/yarn-install@2cd6978b476cbdc39fec48346f8b6ca13199dd6a # v0.7.8
+1
View File
@@ -40,6 +40,7 @@ jobs:
with:
node-version: ${{ matrix.node-version }}
registry-url: https://registry.npmjs.org/ # Needed for auth
corepack: true
# Windows file operation slowness means there's no point caching this
- name: yarn install
+1
View File
@@ -0,0 +1 @@
Fix 406 response for repository/archive retrieval in gitlabUrlReader
+1
View File
@@ -0,0 +1 @@
Restore runtime dependencies incorrectly demoted to devDependencies
+1
View File
@@ -0,0 +1 @@
Fix msgraph userGroupMember filter error by filtering disabled users client-side
+1 -1
View File
@@ -213,6 +213,7 @@
}
[data-theme-mode='dark'][data-theme-name='spotify'] {
--bui-bg-app: var(--bui-black);
--bui-ring: rgba(255, 255, 255, 0.2);
--bui-accent-bg: #1ed760;
--bui-accent-bg-hover: #3be477;
@@ -221,7 +222,6 @@
--bui-accent-fg-disabled: #62ab7c;
/* Deprecated tokens */
--bui-bg-app: var(--bui-black);
--bui-bg-solid: #1ed760;
--bui-bg-solid-hover: #3be477;
--bui-bg-solid-pressed: #1abc54;
@@ -0,0 +1,51 @@
'use client';
import { NumberField } from '../../../../../packages/ui/src/components/NumberField/NumberField';
import { Flex } from '../../../../../packages/ui/src/components/Flex/Flex';
import { RiTimeLine } from '@remixicon/react';
export const WithLabel = () => {
return (
<NumberField
name="quantity"
placeholder="Enter a number"
label="Label"
style={{ maxWidth: '300px' }}
/>
);
};
export const Sizes = () => {
return (
<Flex
direction="column"
gap="4"
style={{ width: '100%', maxWidth: '300px' }}
>
<NumberField
name="quantity"
placeholder="Enter a number"
size="small"
icon={<RiTimeLine />}
/>
<NumberField
name="quantity"
placeholder="Enter a number"
size="medium"
icon={<RiTimeLine />}
/>
</Flex>
);
};
export const WithDescription = () => {
return (
<NumberField
name="quantity"
placeholder="Enter a number"
label="Label"
description="Description"
style={{ maxWidth: '300px' }}
/>
);
};
@@ -0,0 +1,70 @@
import { PropsTable } from '@/components/PropsTable';
import { Snippet } from '@/components/Snippet';
import { numberFieldPropDefs } from './props-definition';
import {
numberFieldUsageSnippet,
withLabelSnippet,
sizesSnippet,
withDescriptionSnippet,
} from './snippets';
import { WithLabel, Sizes, WithDescription } from './components';
import { PageTitle } from '@/components/PageTitle';
import { Theming } from '@/components/Theming';
import { NumberFieldDefinition } from '../../../utils/definitions';
import { ChangelogComponent } from '@/components/ChangelogComponent';
import { CodeBlock } from '@/components/CodeBlock';
import { ReactAriaLink } from '@/components/ReactAriaLink';
export const reactAriaUrls = {
numberField: 'https://react-aria.adobe.com/NumberField',
};
<PageTitle
title="NumberField"
description="A numeric input with label, description, icon, and validation support."
/>
<Snippet
align="center"
py={4}
preview={<WithLabel />}
code={withLabelSnippet}
/>
## Usage
<CodeBlock code={numberFieldUsageSnippet} />
## API reference
<PropsTable data={numberFieldPropDefs} />
<ReactAriaLink component="NumberField" href={reactAriaUrls.numberField} />
## Examples
### Sizes
<Snippet
align="center"
py={4}
open
preview={<Sizes />}
code={sizesSnippet}
layout="side-by-side"
/>
### With description
<Snippet
align="center"
py={4}
open
preview={<WithDescription />}
code={withDescriptionSnippet}
layout="side-by-side"
/>
<Theming definition={NumberFieldDefinition} />
<ChangelogComponent component="number-field" />
@@ -0,0 +1,100 @@
import {
classNamePropDefs,
stylePropDefs,
type PropDef,
} from '@/utils/propDefs';
import { Chip } from '@/components/Chip';
export const numberFieldPropDefs: Record<string, PropDef> = {
size: {
type: 'enum',
values: ['small', 'medium'],
default: 'small',
responsive: true,
description: (
<>
Visual size of the input. Use <Chip>small</Chip> for dense layouts,{' '}
<Chip>medium</Chip> for prominent fields.
</>
),
},
label: {
type: 'string',
description: 'Visible label displayed above the input.',
},
secondaryLabel: {
type: 'string',
description: (
<>
Secondary text shown next to the label. If not provided and isRequired
is true, displays <Chip>Required</Chip>.
</>
),
},
description: {
type: 'string',
description: 'Help text displayed below the label.',
},
icon: {
type: 'enum',
values: ['ReactNode'],
description: 'Icon rendered before the input.',
},
placeholder: {
type: 'string',
description: 'Text displayed when the input is empty.',
},
name: {
type: 'string',
description: 'Form field name for submission.',
},
minValue: {
type: 'number',
description: 'Minimum allowed value.',
},
maxValue: {
type: 'number',
description: 'Maximum allowed value.',
},
step: {
type: 'number',
description: 'Step increment for arrow key changes.',
},
formatOptions: {
type: 'enum',
values: ['Intl.NumberFormatOptions'],
description: (
<>
Number formatting options. Defaults to{' '}
<Chip>{'useGrouping: false'}</Chip>.
</>
),
},
isRequired: {
type: 'boolean',
description: 'Whether the field is required for form submission.',
},
isDisabled: {
type: 'boolean',
description: 'Whether the input is disabled.',
},
isReadOnly: {
type: 'boolean',
description: 'Whether the input is read-only.',
},
value: {
type: 'number',
description: 'Controlled value of the input.',
},
defaultValue: {
type: 'number',
description: 'Default value for uncontrolled usage.',
},
onChange: {
type: 'enum',
values: ['(value: number) => void'],
description: 'Handler called when the input value changes.',
},
...classNamePropDefs,
...stylePropDefs,
};
@@ -0,0 +1,31 @@
export const numberFieldUsageSnippet = `import { NumberField } from '@backstage/ui';
<NumberField label="Minutes" minValue={0} maxValue={59} step={1} />`;
export const withLabelSnippet = `<NumberField
name="quantity"
placeholder="Enter a number"
label="Label"
/>`;
export const sizesSnippet = `<Flex direction="column" gap="4">
<NumberField
size="small"
name="quantity"
placeholder="Enter a number"
icon={<RiTimeLine />}
/>
<NumberField
size="medium"
name="quantity"
placeholder="Enter a number"
icon={<RiTimeLine />}
/>
</Flex>`;
export const withDescriptionSnippet = `<NumberField
name="quantity"
placeholder="Enter a number"
label="Label"
description="Description"
/>`;
+4
View File
@@ -98,6 +98,10 @@ export const components: Page[] = [
title: 'Menu',
slug: 'menu',
},
{
title: 'NumberField',
slug: 'number-field',
},
{
title: 'PasswordField',
slug: 'password-field',
@@ -153,3 +153,90 @@ Below is an example of a more elaborate setup where we have three different back
In this example we have split out the Catalog and Search plugins into one backend deployment. The proxy routes all traffic for `/api/catalog/` and `/api/search/` to this instance. With this separation we're able to scale and deploy these two plugins independently, and they are also isolated from both a performance and security perspective. Likewise the TechDocs and Scaffolder plugins are split out as well, and then we route the rest of the traffic to our instance that contains the App, Auth, and Proxy plugins.
We also see how each of the plugins have their own logical database, but are often set up to share the actual Database Management System (DBMS) instance. This is of course not a requirement, and you can choose to further divide or consolidate the databases as you see fit.
## Startup Configuration
The `backend.startup` configuration block lets you control how the backend behaves when plugins or plugin modules fail to boot during startup. By default, any plugin or module boot failure is fatal and causes the backend to abort. This configuration lets you make specific plugins or modules optional, or flip the default so that all are optional unless explicitly required.
### Plugin Boot Failure Handling
If a plugin fails to boot, the backend aborts startup by default. You can change this per-plugin using `onPluginBootFailure: continue`:
```yaml
backend:
startup:
plugins:
catalog:
onPluginBootFailure: continue
```
With this configuration, if the `catalog` plugin crashes during startup, the backend will log the error and continue starting up the remaining plugins. This is useful when troubleshooting data-dependent issues, as it allows you to leave a crashing plugin installed while still allowing the rest of the backend to serve requests.
### Plugin Module Boot Failure Handling
You can apply the same control to individual plugin modules using `onPluginModuleBootFailure`:
```yaml
backend:
startup:
plugins:
catalog:
modules:
github: # moduleId as declared in createBackendModule({ moduleId: '...' })
onPluginModuleBootFailure: continue
```
This allows the `github` catalog module to fail without bringing down the `catalog` plugin or the rest of the backend. Note that the key under `modules` is the `moduleId` declared in `createBackendModule`, not the plugin or entity provider name.
### Setting a Global Default
Instead of opting each plugin into `continue` mode individually, you can flip the global default so that all plugins continue on failure, and only specific ones are required to succeed:
```yaml
backend:
startup:
default:
onPluginBootFailure: continue
plugins:
auth:
onPluginBootFailure: abort
```
In this example, all plugins are optional except `auth`, which is explicitly set to `abort` and is therefore required to start successfully.
The same `default` mechanism works for modules via `onPluginModuleBootFailure`:
```yaml
backend:
startup:
default:
onPluginModuleBootFailure: continue
plugins:
catalog:
modules:
github:
onPluginModuleBootFailure: abort
```
### Full Configuration Reference
```yaml
backend:
startup:
# Global defaults applied when not specified per-plugin or per-module
default:
# Defaults to 'abort'. Set to 'continue' to make all plugins optional by default.
onPluginBootFailure: abort # or continue
# Defaults to 'abort'. Set to 'continue' to make all plugin modules optional by default.
onPluginModuleBootFailure: abort # or continue
# Per-plugin and per-module overrides
plugins:
<pluginId>:
# Override the default boot failure behavior for this specific plugin.
onPluginBootFailure: abort # or continue
modules:
<moduleId>:
# Override the default boot failure behavior for this specific plugin module.
onPluginModuleBootFailure: abort # or continue
```
@@ -28,7 +28,7 @@ function useTodos() {
const data = await response.json();
return data.items;
}, [fetch]);
});
}
```
@@ -37,8 +37,8 @@ Here, we're using Backstage's `fetchApi` which wraps the browser `fetch` and aut
1. Injects authentication credentials - you don't need to attach any `Authorization` headers manually.
2. Resolves `plugin://<pluginId>` URL schemes to the real plugin URL for your instance.
The `useAsync` hook from `react-use` runs the async function on mount and
returns `{ value, loading, error }`, which the component uses to show a
The `useAsync` hook from `@react-hookz/web` runs the async function on mount and
returns `[{ status, result, error }, { execute }]`, which the component uses to show a
loading spinner, example todo items if the backend request fails, or the
fetched todo list.
+15 -9
View File
@@ -26,13 +26,15 @@ such as `axios`.
Example:
```ts title="plugins/my-awesome-plugin/src/components/AwesomeUsersTable.tsx"
import useAsync from 'react-use/esm/useAsync';
import { useAsync, useMountEffect } from '@react-hookz/web';
function AwesomeUsersTable() {
const { value, loading, error } = useAsync(async () => {
const [{ status, result, error }, { execute }] = useAsync(async () => {
const response = await fetch('https://api.frobsco.com/v1/list');
return response.json();
}, []);
});
useMountEffect(execute);
...
@@ -94,17 +96,19 @@ import {
discoveryApiRef,
fetchApiRef,
} from '@backstage/core-plugin-api';
import useAsync from 'react-use/esm/useAsync';
import { useAsync, useMountEffect } from '@react-hookz/web';
function FrobsAggregator() {
const fetchApi = useApi(fetchApiRef);
const discoveryApi = useApi(discoveryApiRef);
const { value, loading, error } = useAsync(async () => {
const [{ status, result, error }, { execute }] = useAsync(async () => {
const baseUrl = await discoveryApi.getBaseUrl('proxy');
const response = await fetchApi.fetch(`${baseUrl}/frobs`);
return response.json();
}, [fetchApi, discoveryApi]);
});
useMountEffect(execute);
// ...
}
@@ -171,19 +175,21 @@ import {
discoveryApiRef,
fetchApiRef,
} from '@backstage/core-plugin-api';
import useAsync from 'react-use/esm/useAsync';
import { useAsync, useMountEffect } from '@react-hookz/web';
function FrobsAggregator() {
const fetchApi = useApi(fetchApiRef);
const discoveryApi = useApi(discoveryApiRef);
const { value, loading, error } = useAsync(async () => {
const [{ status, result, error }, { execute }] = useAsync(async () => {
// highlight-next-line
const baseUrl = await discoveryApi.getBaseUrl('frobs-aggregator');
// highlight-next-line
const response = await fetchApi.fetch(`${baseUrl}/summary`);
return response.json();
}, [fetchApi, discoveryApi]);
});
useMountEffect(execute);
// ...
}
File diff suppressed because it is too large Load Diff
@@ -21,11 +21,12 @@ If your plugin requires access to an API, backstage offers
- [Setting up the backstage proxy](#setting-up-the-backstage-proxy)
- [Calling an API using the backstage proxy](#calling-an-api-using-the-backstage-proxy)
- [Option 1: Calling the proxy directly from the frontend plugin](#option-1-calling-the-proxy-directly-from-the-frontend-plugin)
- [Option 2: Defining the API client interface](#defining-the-api-client-interface)
- [Creating the API client](#creating-the-api-client)
- [Bundling your ApiRef with your plugin](#bundling-your-apiref-with-your-plugin)
- [Using the API in your components](#using-the-api-in-your-components)
- [Option 1: Calling the proxy directly from the frontend plugin](#option-1-calling-the-proxy-directly-from-the-frontend-plugin)
- [Option 2: Defining the API client interface](#option-2-defining-the-api-client-interface)
- [Defining the API client interface](#defining-the-api-client-interface)
- [Creating the API client](#creating-the-api-client)
- [Bundling your ApiRef with your plugin](#bundling-your-apiref-with-your-plugin)
- [Using the API in your components](#using-the-api-in-your-components)
## Setting up the backstage proxy
@@ -71,20 +72,22 @@ import {
fetchApiRef,
} from '@backstage/core-plugin-api';
import { Progress, Alert } from '@backstage/core-components';
import useAsync from 'react-use/esm/useAsync';
import { useAsync, useMountEffect } from '@react-hookz/web';
import { myAwesomeApiRef } from '../../api';
export const AwesomeUsersTable = () => {
const fetchApi = useApi(fetchApiRef);
const discoveryApi = useApi(discoveryApiRef);
const { value, loading, error } = useAsync(async () => {
const [{ status, result, error }, { execute }] = useAsync(async () => {
const baseUrl = await discoveryApi.getBaseUrl('proxy');
// As configured previously for the backend proxy
const resp = await fetchApi.fetch(`${baseUrl}/<your-proxy-uri>`);
if (!resp.ok) throw new Error(resp.statusText);
return resp.json();
}, [fetchApi, discoveryApi]);
});
useMountEffect(execute);
// ...
};
@@ -238,16 +241,18 @@ Now you should be able to access your API using the backstage hook
```ts title="plugins/my-awesome-plugin/src/components/AwesomeUsersTable.tsx"
import { useApi } from '@backstage/core-plugin-api';
import { myAwesomeApiRef } from '../../api';
import useAsync from 'react-use/esm/useAsync';
import { useAsync, useMountEffect } from '@react-hookz/web';
export const AwesomeUsersTable = () => {
const apiClient = useApi(myAwesomeApiRef);
const { value, loading, error } = useAsync(async () => {
const [{ status, result, error }, { execute }] = useAsync(async () => {
const users = await apiClient.listUsers();
return users;
}, [apiClient]);
useMountEffect(execute);
// ...
};
```
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "root",
"version": "1.51.0",
"version": "1.52.0-next.0",
"backstage": {
"cli": {
"new": {
+7
View File
@@ -1,5 +1,12 @@
# @backstage/app-defaults
## 1.7.9-next.0
### Patch Changes
- Updated dependencies
- @backstage/core-components@0.18.11-next.0
## 1.7.8
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/app-defaults",
"version": "1.7.8",
"version": "1.7.9-next.0",
"description": "Provides the default wiring of a Backstage App",
"backstage": {
"role": "web-library"
+7
View File
@@ -1,5 +1,12 @@
# app-example-plugin
## 0.0.36-next.0
### Patch Changes
- Updated dependencies
- @backstage/core-components@0.18.11-next.0
## 0.0.35
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "app-example-plugin",
"version": "0.0.35",
"version": "0.0.36-next.0",
"description": "Backstage internal example plugin",
"backstage": {
"role": "frontend-plugin",
+34
View File
@@ -1,5 +1,39 @@
# example-app-legacy
## 0.2.122-next.0
### Patch Changes
- Updated dependencies
- @backstage/plugin-catalog-react@3.0.1-next.0
- @backstage/plugin-catalog@2.0.6-next.0
- @backstage/core-components@0.18.11-next.0
- @backstage/plugin-catalog-graph@0.6.5-next.0
- @backstage/plugin-org@0.7.5-next.0
- @backstage/plugin-scaffolder-react@2.0.1-next.0
- @backstage/plugin-scaffolder@1.38.0-next.0
- @backstage/plugin-search-react@1.11.5-next.0
- @backstage/plugin-search@1.7.5-next.0
- @backstage/plugin-catalog-unprocessed-entities@0.2.32-next.0
- @backstage/plugin-home@0.9.7-next.0
- @backstage/cli@0.36.3-next.0
- @backstage/plugin-catalog-import@0.13.14-next.0
- @backstage/plugin-techdocs@1.17.7-next.0
- @backstage/plugin-api-docs@0.14.2-next.0
- @backstage/plugin-kubernetes@0.12.20-next.0
- @backstage/plugin-kubernetes-cluster@0.0.38-next.0
- @backstage/plugin-techdocs-module-addons-contrib@1.1.37-next.0
- @backstage/plugin-user-settings@0.9.4-next.0
- @backstage/app-defaults@1.7.9-next.0
- @backstage/integration-react@1.2.19-next.0
- @backstage/plugin-auth-react@0.1.28-next.0
- @backstage/plugin-devtools@0.1.40-next.0
- @backstage/plugin-home-react@0.1.39-next.0
- @backstage/plugin-notifications@0.5.18-next.0
- @backstage/plugin-signals@0.0.32-next.0
- @backstage/plugin-techdocs-react@1.3.12-next.0
- @backstage/frontend-app-api@0.16.4-next.0
## 0.2.121
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "example-app-legacy",
"version": "0.2.121",
"version": "0.2.122-next.0",
"backstage": {
"role": "frontend"
},
+38
View File
@@ -1,5 +1,43 @@
# example-app
## 0.0.36-next.0
### Patch Changes
- Updated dependencies
- @backstage/plugin-catalog-react@3.0.1-next.0
- @backstage/plugin-catalog@2.0.6-next.0
- @backstage/core-components@0.18.11-next.0
- @backstage/plugin-catalog-graph@0.6.5-next.0
- @backstage/plugin-org@0.7.5-next.0
- @backstage/plugin-scaffolder-react@2.0.1-next.0
- @backstage/plugin-scaffolder@1.38.0-next.0
- @backstage/plugin-search-react@1.11.5-next.0
- @backstage/plugin-search@1.7.5-next.0
- @backstage/plugin-catalog-unprocessed-entities@0.2.32-next.0
- @backstage/plugin-home@0.9.7-next.0
- @backstage/cli@0.36.3-next.0
- @backstage/plugin-catalog-import@0.13.14-next.0
- @backstage/plugin-techdocs@1.17.7-next.0
- @backstage/core-compat-api@0.5.12-next.0
- @backstage/plugin-api-docs@0.14.2-next.0
- @backstage/plugin-kubernetes@0.12.20-next.0
- @backstage/plugin-kubernetes-cluster@0.0.38-next.0
- @backstage/plugin-techdocs-module-addons-contrib@1.1.37-next.0
- @backstage/plugin-user-settings@0.9.4-next.0
- @backstage/app-defaults@1.7.9-next.0
- @backstage/frontend-defaults@0.5.3-next.0
- @backstage/integration-react@1.2.19-next.0
- @backstage/plugin-app@0.4.7-next.0
- @backstage/plugin-app-visualizer@0.2.5-next.0
- @backstage/plugin-auth-react@0.1.28-next.0
- @backstage/plugin-devtools@0.1.40-next.0
- @backstage/plugin-home-react@0.1.39-next.0
- @backstage/plugin-notifications@0.5.18-next.0
- @backstage/plugin-signals@0.0.32-next.0
- @backstage/plugin-techdocs-react@1.3.12-next.0
- @backstage/frontend-app-api@0.16.4-next.0
## 0.0.35
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "example-app",
"version": "0.0.35",
"version": "0.0.36-next.0",
"backstage": {
"role": "frontend"
},
+7
View File
@@ -1,5 +1,12 @@
# @backstage/backend-app-api
## 1.7.1-next.0
### Patch Changes
- Updated dependencies
- @backstage/backend-plugin-api@1.9.2-next.0
## 1.7.0
### Minor Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/backend-app-api",
"version": "1.7.0",
"version": "1.7.1-next.0",
"description": "Core API used by Backstage backend apps",
"backstage": {
"role": "node-library"
+14
View File
@@ -1,5 +1,19 @@
# @backstage/backend-defaults
## 0.17.2-next.0
### Patch Changes
- a07e6a3: Updated `AzureBlobStorageUrlReader` to reference the correctly-named `AzureBlobStorageIntegration` type from `@backstage/integration`. The previously-used `AzureBlobStorageIntergation` is now an alias for the new type and remains a valid argument to the constructor.
- def82d4: Fixed the built-in rate limiter throwing a validation error and refusing to start when `backend.rateLimit` is enabled. Requests are now keyed using the address normalization helper from `express-rate-limit`, which is required by newer versions of that library and ensures IPv6 clients are grouped by their address block rather than by individual address.
- Updated dependencies
- @backstage/integration@2.0.3-next.0
- @backstage/plugin-auth-node@0.7.2-next.0
- @backstage/backend-app-api@1.7.1-next.0
- @backstage/plugin-permission-node@0.11.1-next.0
- @backstage/backend-plugin-api@1.9.2-next.0
- @backstage/plugin-events-node@0.4.23-next.0
## 0.17.1
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/backend-defaults",
"version": "0.17.1",
"version": "0.17.2-next.0",
"description": "Backend defaults used by Backstage backend apps",
"backstage": {
"role": "node-library"
@@ -12,6 +12,7 @@ import { AzureIntegration } from '@backstage/integration';
import { BitbucketCloudIntegration } from '@backstage/integration';
import { BitbucketServerIntegration } from '@backstage/integration';
import { Config } from '@backstage/config';
import { ContainerClient } from '@azure/storage-blob';
import { GerritIntegration } from '@backstage/integration';
import { GiteaIntegration } from '@backstage/integration';
import { GithubCredentialsProvider } from '@backstage/integration';
@@ -70,6 +71,9 @@ export class AzureBlobStorageUrlReader implements UrlReaderService {
integration: AzureBlobStorageIntegration,
deps: {
treeResponseFactory: ReadTreeResponseFactory;
createContainerClient?: (
containerName: string,
) => Promise<ContainerClient>;
},
);
// (undocumented)
@@ -19,6 +19,7 @@ import {
TestDatabases,
mockServices,
} from '@backstage/backend-test-utils';
import waitForExpect from 'wait-for-expect';
import { DatabaseKeyStore, TABLE } from './DatabaseKeyStore';
const testKey = {
@@ -104,12 +105,13 @@ describe('DatabaseKeyStore', () => {
"Removing expired plugin service keys, 'test-key-2'",
);
// Key deletion happens async, so give it a bit of time to complete
await new Promise(resolve => setTimeout(resolve, 500));
await expect(knex(TABLE).select('id')).resolves.toEqual([
{ id: testKey.kid },
]);
// Key deletion happens async — poll until it completes rather than
// relying on a fixed sleep that can flake in slow CI environments.
await waitForExpect(async () => {
await expect(knex(TABLE).select('id')).resolves.toEqual([
{ id: testKey.kid },
]);
});
});
it('should fail to insert with invalid date', async () => {
@@ -14,7 +14,6 @@
* limitations under the License.
*/
import * as AzureStorage from '@azure/storage-blob';
import { ConfigReader } from '@backstage/config';
import { JsonObject } from '@backstage/types';
import { DefaultReadTreeResponseFactory } from './tree';
@@ -27,36 +26,26 @@ import {
ScmIntegrations,
} from '@backstage/integration';
import { UrlReaderPredicateTuple } from './types';
import { mockServices } from '@backstage/backend-test-utils';
import { Readable } from 'node:stream';
import { ContainerClient } from '@azure/storage-blob';
const treeResponseFactory = DefaultReadTreeResponseFactory.create({
config: new ConfigReader({}),
});
// Mock Azure Blob Storage SDK
const mockBlobDownload = jest.fn();
const mockGetBlobClient = jest.fn(() => ({
download: mockBlobDownload,
}));
const mockListBlobsFlat = jest.fn();
class MockContainerClient {
getBlobClient = mockGetBlobClient;
listBlobsFlat = mockListBlobsFlat;
function createMockContainerClient(): ContainerClient {
return {
getBlobClient: mockGetBlobClient,
listBlobsFlat: mockListBlobsFlat,
} as unknown as ContainerClient;
}
class MockBlobServiceClient {
getContainerClient = jest.fn(() => new MockContainerClient());
}
jest
.spyOn(AzureStorage, 'BlobServiceClient')
.mockReturnValue(new MockBlobServiceClient() as any);
jest
.spyOn(AzureStorage, 'StorageSharedKeyCredential')
.mockReturnValue({} as any);
const treeResponseFactory = DefaultReadTreeResponseFactory.create({
config: new ConfigReader({}),
});
describe('parseUrl', () => {
it('parses Azure Blob Storage URLs correctly', () => {
expect(
@@ -96,35 +85,40 @@ describe('parseUrl', () => {
describe('AzureBlobStorageUrlReader', () => {
const createReader = (config: JsonObject): UrlReaderPredicateTuple[] => {
return AzureBlobStorageUrlReader.factory({
config: new ConfigReader(config),
logger: mockServices.logger.mock(),
treeResponseFactory,
const integrations = ScmIntegrations.fromConfig(new ConfigReader(config));
const credsManager =
DefaultAzureCredentialsManager.fromIntegrations(integrations);
return integrations.azureBlobStorage.list().map(integrationConfig => {
const reader = new AzureBlobStorageUrlReader(
credsManager,
integrationConfig,
{
treeResponseFactory,
createContainerClient: async () => createMockContainerClient(),
},
);
const predicate = (url: URL) =>
url.host.endsWith(
`${integrationConfig.config.accountName}.${integrationConfig.config.host}`,
);
return { reader, predicate };
});
};
it('creates a reader with minimal config', () => {
const entries = createReader({
integrations: {
azureBlobStorage: [
{
accountName: 'test-account',
},
],
azureBlobStorage: [{ accountName: 'test-account' }],
},
});
expect(entries).toHaveLength(1);
});
describe('predicates', () => {
const readers = createReader({
integrations: {
azureBlobStorage: [
{
accountName: 'test-account',
},
],
azureBlobStorage: [{ accountName: 'test-account' }],
},
});
const predicate = readers[0].predicate;
@@ -147,14 +141,10 @@ describe('AzureBlobStorageUrlReader', () => {
const config = new ConfigReader({
integrations: {
azureBlobStorage: [
{
accountName: 'test-account',
accountKey: 'test-key',
},
{ accountName: 'test-account', accountKey: 'test-key' },
],
},
});
const integrations = ScmIntegrations.fromConfig(config);
const credsManager =
DefaultAzureCredentialsManager.fromIntegrations(integrations);
@@ -163,7 +153,6 @@ describe('AzureBlobStorageUrlReader', () => {
integrations.azureBlobStorage.list()[0],
{ treeResponseFactory },
);
expect(reader.toString()).toBe(
'azureBlobStorage{accountName=test-account,authed=true}',
);
@@ -172,14 +161,9 @@ describe('AzureBlobStorageUrlReader', () => {
it('shows authed=false when no account key provided', () => {
const config = new ConfigReader({
integrations: {
azureBlobStorage: [
{
accountName: 'test-account',
},
],
azureBlobStorage: [{ accountName: 'test-account' }],
},
});
const integrations = ScmIntegrations.fromConfig(config);
const credsManager =
DefaultAzureCredentialsManager.fromIntegrations(integrations);
@@ -188,7 +172,6 @@ describe('AzureBlobStorageUrlReader', () => {
integrations.azureBlobStorage.list()[0],
{ treeResponseFactory },
);
expect(reader.toString()).toBe(
'azureBlobStorage{accountName=test-account,authed=false}',
);
@@ -199,10 +182,7 @@ describe('AzureBlobStorageUrlReader', () => {
const [{ reader }] = createReader({
integrations: {
azureBlobStorage: [
{
accountName: 'test-account',
accountKey: 'test-key',
},
{ accountName: 'test-account', accountKey: 'test-key' },
],
},
});
@@ -245,55 +225,38 @@ describe('AzureBlobStorageUrlReader', () => {
const [{ reader }] = createReader({
integrations: {
azureBlobStorage: [
{
accountName: 'test-account',
accountKey: 'test-key',
},
{ accountName: 'test-account', accountKey: 'test-key' },
],
},
});
beforeEach(() => {
jest.clearAllMocks();
const mockBlobs = [
{
name: 'prefix/file1.yaml',
properties: {
lastModified: new Date('2025-01-01T00:00:00Z'),
},
},
{
name: 'prefix/subdir/file2.yaml',
properties: {
lastModified: new Date('2024-01-01T00:00:00Z'),
},
},
];
mockBlobDownload.mockResolvedValue({
readableStreamBody: Readable.from(
Buffer.from('site_name: Test Azure Blob'),
),
});
mockListBlobsFlat.mockReturnValue({
[Symbol.asyncIterator]: async function* generateBlobs() {
for (const blob of mockBlobs) {
yield blob;
}
},
});
});
it('returns contents of blobs in a container', async () => {
mockListBlobsFlat.mockReturnValue(
(async function* mockIterator() {
yield {
name: 'prefix/file1.yaml',
properties: { lastModified: new Date('2025-01-01T00:00:00Z') },
};
yield {
name: 'prefix/file2.yaml',
properties: { lastModified: new Date('2025-01-02T00:00:00Z') },
};
})(),
);
mockBlobDownload.mockResolvedValue({
readableStreamBody: Readable.from(Buffer.from('test content')),
});
const response = await reader.readTree(
'https://test-account.blob.core.windows.net/test-container/prefix',
'https://test-account.blob.core.windows.net/test-container/prefix/',
);
const files = await response.files();
expect(files).toHaveLength(2);
const file1Content = await files[0].content();
expect(file1Content.toString()).toBe('site_name: Test Azure Blob');
});
});
@@ -301,10 +264,7 @@ describe('AzureBlobStorageUrlReader', () => {
const [{ reader }] = createReader({
integrations: {
azureBlobStorage: [
{
accountName: 'test-account',
accountKey: 'test-key',
},
{ accountName: 'test-account', accountKey: 'test-key' },
],
},
});
@@ -313,7 +273,7 @@ describe('AzureBlobStorageUrlReader', () => {
jest.clearAllMocks();
});
it('should return a file when given an exact valid url', async () => {
it('returns a file when given an exact valid url', async () => {
mockBlobDownload.mockResolvedValue({
readableStreamBody: Readable.from(
Buffer.from('site_name: Test Azure Blob'),
@@ -322,26 +282,22 @@ describe('AzureBlobStorageUrlReader', () => {
lastModified: new Date('2025-01-01T00:00:00Z'),
});
const data = await reader.search(
'https://test-account.blob.core.windows.net/test-container/test-file.yaml',
const { files } = await reader.search(
'https://test-account.blob.core.windows.net/test-container/exact-file.yaml',
);
expect(data.etag).toBe('"etag"');
expect(data.files.length).toBe(1);
expect(data.files[0].url).toBe(
'https://test-account.blob.core.windows.net/test-container/test-file.yaml',
);
expect((await data.files[0].content()).toString()).toEqual(
'site_name: Test Azure Blob',
expect(files).toHaveLength(1);
expect(files[0].url).toBe(
'https://test-account.blob.core.windows.net/test-container/exact-file.yaml',
);
});
it('should handle Azure SDK errors from readUrl', async () => {
it('handles Azure SDK errors from readUrl', async () => {
mockBlobDownload.mockRejectedValue(new Error('Blob not found'));
await expect(
reader.search(
'https://test-account.blob.core.windows.net/test-container/missing.yaml',
'https://test-account.blob.core.windows.net/test-container/nonexistent.yaml',
),
).rejects.toThrow('Could not retrieve file from Azure Blob Storage');
});
@@ -349,7 +305,7 @@ describe('AzureBlobStorageUrlReader', () => {
it('throws if given URL with wildcard', async () => {
await expect(
reader.search(
'https://test-account.blob.core.windows.net/test-container/test-*.yaml',
'https://test-account.blob.core.windows.net/test-container/path/to/*.yaml',
),
).rejects.toThrow(
'Glob search pattern not implemented for AzureBlobStorageUrlReader',
@@ -85,12 +85,10 @@ export class AzureBlobStorageUrlReader implements UrlReaderService {
});
};
// private readonly blobServiceClient: BlobServiceClient;
private readonly credsManager: AzureCredentialsManager;
private readonly integration: AzureBlobStorageIntegration;
private readonly deps: {
treeResponseFactory: ReadTreeResponseFactory;
createContainerClient: (containerName: string) => Promise<ContainerClient>;
};
constructor(
@@ -98,18 +96,26 @@ export class AzureBlobStorageUrlReader implements UrlReaderService {
integration: AzureBlobStorageIntegration,
deps: {
treeResponseFactory: ReadTreeResponseFactory;
createContainerClient?: (
containerName: string,
) => Promise<ContainerClient>;
},
) {
this.credsManager = credsManager;
this.integration = integration;
this.deps = deps;
this.deps = {
...deps,
createContainerClient:
deps.createContainerClient ??
this.#defaultCreateContainerClient.bind(this, credsManager),
};
}
private async createContainerClient(
async #defaultCreateContainerClient(
credsManager: AzureCredentialsManager,
containerName: string,
): Promise<ContainerClient> {
const accountName = this.integration.config.accountName; // Use the account name from the integration config
const accountKey = this.integration.config.accountKey; // Get the account key if it exists
const accountName = this.integration.config.accountName;
const accountKey = this.integration.config.accountKey;
if (accountKey && accountName) {
const creds = new StorageSharedKeyCredential(accountName, accountKey);
@@ -119,10 +125,8 @@ export class AzureBlobStorageUrlReader implements UrlReaderService {
);
return blobServiceClient.getContainerClient(containerName);
}
// Use the credentials manager to get the correct credentials
const credential = await this.credsManager.getCredentials(
accountName as string,
);
const credential = await credsManager.getCredentials(accountName as string);
let blobServiceClientUrl: string;
@@ -157,7 +161,7 @@ export class AzureBlobStorageUrlReader implements UrlReaderService {
try {
const { path, container } = parseUrl(url);
const containerClient = await this.createContainerClient(container);
const containerClient = await this.deps.createContainerClient(container);
const blobClient = containerClient.getBlobClient(path);
const getBlobOptions: BlobDownloadOptions = {
@@ -200,7 +204,7 @@ export class AzureBlobStorageUrlReader implements UrlReaderService {
try {
const { path, container } = parseUrl(url);
const containerClient = await this.createContainerClient(container);
const containerClient = await this.deps.createContainerClient(container);
const blobs = containerClient.listBlobsFlat({ prefix: path });
const responses = [];
@@ -120,14 +120,14 @@ describe('AzureUrlReader', () => {
it.each([
{
url: 'https://dev.azure.com/org-name/project-name/_git/repo-name?path=my-template.yaml&version=GBmaster',
config: createConfig(),
config: createConfig('my-pat'),
response: expect.objectContaining({
url: 'https://dev.azure.com/org-name/project-name/_apis/git/repositories/repo-name/items?api-version=6.0&path=my-template.yaml&version=master',
}),
},
{
url: 'https://dev.azure.com/org-name/project-name/_git/repo-name?path=my-template.yaml',
config: createConfig(),
config: createConfig('my-pat'),
response: expect.objectContaining({
url: 'https://dev.azure.com/org-name/project-name/_apis/git/repositories/repo-name/items?api-version=6.0&path=my-template.yaml',
}),
@@ -141,15 +141,6 @@ describe('AzureUrlReader', () => {
}),
}),
},
{
url: 'https://dev.azure.com/a/b/_git/repo-name?path=my-template.yaml',
config: createConfig(undefined),
response: expect.objectContaining({
headers: expect.objectContaining({
authorization: expect.stringMatching(/^Bearer /),
}),
}),
},
])('should handle happy path %#', async ({ url, config, response }) => {
const [{ reader }] = AzureUrlReader.factory({
config,
@@ -166,12 +157,12 @@ describe('AzureUrlReader', () => {
it.each([
{
url: 'https://api.com/a/b/blob/master/path/to/c.yaml',
config: createConfig(),
config: createConfig('my-pat'),
error: 'Azure URL must point to a git repository',
},
{
url: 'com/a/b/blob/master/path/to/c.yaml',
config: createConfig(),
config: createConfig('my-pat'),
error: 'Invalid URL',
},
{
@@ -211,6 +211,10 @@ export class GitlabUrlReader implements UrlReaderService {
)}/repository/archive?${archiveReqParams.toString()}`;
const archiveGitLabResponse = await this.integration.fetch(reqUrl, {
...getGitLabRequestOptions(this.integration.config, token),
// The mode is set to 'same-origin' to overwrite the default 'cors' value.
// The repository/archive endpoint marks mode='cors' as a "hotlink" which will return 406 - Not Acceptable as a response
// More info on this issue can be found @ https://github.com/backstage/backstage/issues/34395
mode: 'same-origin',
// TODO(freben): The signal cast is there because pre-3.x versions of
// node-fetch have a very slightly deviating AbortSignal type signature.
// The difference does not affect us in practice however. The cast can
@@ -1,5 +1,22 @@
# @backstage/backend-dynamic-feature-service
## 0.8.3-next.0
### Patch Changes
- Updated dependencies
- @backstage/plugin-catalog-backend@3.8.0-next.0
- @backstage/backend-defaults@0.17.2-next.0
- @backstage/plugin-auth-node@0.7.2-next.0
- @backstage/plugin-scaffolder-node@0.13.4-next.0
- @backstage/plugin-events-backend@0.6.3-next.0
- @backstage/plugin-permission-node@0.11.1-next.0
- @backstage/plugin-search-backend-node@1.4.5-next.0
- @backstage/backend-plugin-api@1.9.2-next.0
- @backstage/plugin-events-node@0.4.23-next.0
- @backstage/backend-openapi-utils@0.6.10-next.0
- @backstage/plugin-app-node@0.1.46-next.0
## 0.8.2
### Patch Changes
@@ -1,6 +1,6 @@
{
"name": "@backstage/backend-dynamic-feature-service",
"version": "0.8.2",
"version": "0.8.3-next.0",
"description": "Backstage dynamic feature service",
"backstage": {
"role": "node-library"
@@ -1,5 +1,12 @@
# @backstage/backend-openapi-utils
## 0.6.10-next.0
### Patch Changes
- Updated dependencies
- @backstage/backend-plugin-api@1.9.2-next.0
## 0.6.9
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/backend-openapi-utils",
"version": "0.6.9",
"version": "0.6.10-next.0",
"description": "OpenAPI typescript support.",
"backstage": {
"role": "node-library"
+8
View File
@@ -1,5 +1,13 @@
# @backstage/backend-plugin-api
## 1.9.2-next.0
### Patch Changes
- Updated dependencies
- @backstage/plugin-auth-node@0.7.2-next.0
- @backstage/plugin-permission-node@0.11.1-next.0
## 1.9.1
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/backend-plugin-api",
"version": "1.9.1",
"version": "1.9.2-next.0",
"description": "Core API used by Backstage backend plugins",
"backstage": {
"role": "node-library"
+11
View File
@@ -1,5 +1,16 @@
# @backstage/backend-test-utils
## 1.11.4-next.0
### Patch Changes
- Updated dependencies
- @backstage/backend-defaults@0.17.2-next.0
- @backstage/plugin-auth-node@0.7.2-next.0
- @backstage/backend-app-api@1.7.1-next.0
- @backstage/backend-plugin-api@1.9.2-next.0
- @backstage/plugin-events-node@0.4.23-next.0
## 1.11.3
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/backend-test-utils",
"version": "1.11.3",
"version": "1.11.4-next.0",
"description": "Test helpers library for Backstage backends",
"backstage": {
"role": "node-library"
@@ -56,6 +56,10 @@ export async function startMysqlContainer(image: string): Promise<{
.withExposedPorts(3306)
.withEnvironment({ MYSQL_ROOT_PASSWORD: password })
.withTmpFs({ '/var/lib/mysql': 'rw' })
.withCommand([
'--default-authentication-plugin=mysql_native_password',
'--skip-log-bin',
])
.start();
const host = container.getHost();
@@ -176,6 +180,7 @@ export class MysqlEngine implements Engine {
connection: {
...this.#connection,
database: databaseName,
connectTimeout: 30_000,
},
...LARGER_POOL_CONFIG,
});
@@ -129,5 +129,7 @@ export const LARGER_POOL_CONFIG = {
pool: {
min: 0,
max: 50,
acquireTimeoutMillis: 30_000,
createTimeoutMillis: 30_000,
},
};
+42
View File
@@ -1,5 +1,47 @@
# example-backend
## 0.0.51-next.0
### Patch Changes
- Updated dependencies
- @backstage/plugin-catalog-backend@3.8.0-next.0
- @backstage/plugin-search-backend@2.1.3-next.0
- @backstage/plugin-kubernetes-backend@0.21.5-next.0
- @backstage/plugin-signals-backend@0.3.16-next.0
- @backstage/plugin-app-backend@0.5.15-next.0
- @backstage/backend-defaults@0.17.2-next.0
- @backstage/plugin-notifications-backend@0.6.6-next.0
- @backstage/plugin-catalog-backend-module-logs@0.1.23-next.0
- @backstage/plugin-auth-node@0.7.2-next.0
- @backstage/plugin-mcp-actions-backend@0.1.14-next.0
- @backstage/plugin-search-backend-module-catalog@0.3.16-next.0
- @backstage/plugin-search-backend-module-techdocs@0.4.15-next.0
- @backstage/plugin-techdocs-backend@2.2.1-next.0
- @backstage/plugin-catalog-backend-module-openapi@0.2.23-next.0
- @backstage/plugin-scaffolder-backend@4.0.1-next.0
- @backstage/plugin-scaffolder-backend-module-github@0.9.10-next.0
- @backstage/plugin-auth-backend@0.29.1-next.0
- @backstage/plugin-auth-backend-module-github-provider@0.5.4-next.0
- @backstage/plugin-auth-backend-module-openshift-provider@0.1.8-next.0
- @backstage/plugin-devtools-backend@0.5.18-next.0
- @backstage/plugin-events-backend@0.6.3-next.0
- @backstage/plugin-events-backend-module-google-pubsub@0.2.4-next.0
- @backstage/plugin-permission-backend@0.7.13-next.0
- @backstage/plugin-permission-node@0.11.1-next.0
- @backstage/plugin-proxy-backend@0.6.14-next.0
- @backstage/plugin-search-backend-node@1.4.5-next.0
- @backstage/backend-plugin-api@1.9.2-next.0
- @backstage/plugin-auth-backend-module-guest-provider@0.2.20-next.0
- @backstage/plugin-catalog-backend-module-unprocessed@0.6.13-next.0
- @backstage/plugin-permission-backend-module-allow-all-policy@0.2.20-next.0
- @backstage/plugin-catalog-backend-module-ai-model@0.1.1-next.0
- @backstage/plugin-catalog-backend-module-backstage-openapi@0.5.15-next.0
- @backstage/plugin-catalog-backend-module-scaffolder-entity-model@0.2.21-next.0
- @backstage/plugin-scaffolder-backend-module-notifications@0.1.23-next.0
- @backstage/plugin-search-backend-module-elasticsearch@1.8.4-next.0
- @backstage/plugin-search-backend-module-explore@0.3.15-next.0
## 0.0.50
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "example-backend",
"version": "0.0.50",
"version": "0.0.51-next.0",
"backstage": {
"role": "backend"
},
+6
View File
@@ -1,5 +1,11 @@
# @backstage/catalog-client
## 1.16.0-next.0
### Minor Changes
- 8f20cc2: `CatalogApi.queryEntities` now accepts a `totalItems` option (`'include'` or `'exclude'`, default `'include'`) on initial requests. Pass `'exclude'` to skip the `totalItems` count when the caller doesn't need it.
## 1.15.1
### Patch Changes
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/catalog-client",
"version": "1.15.1",
"version": "1.16.0-next.0",
"description": "An isomorphic client for the catalog backend",
"backstage": {
"role": "common-library"
@@ -51,13 +51,13 @@
"@backstage/catalog-model": "workspace:^",
"@backstage/errors": "workspace:^",
"@backstage/filter-predicates": "workspace:^",
"@backstage/plugin-catalog-common": "workspace:^",
"cross-fetch": "^4.0.0",
"lodash": "^4.17.21",
"uri-template": "^2.0.0"
},
"devDependencies": {
"@backstage/cli": "workspace:^",
"@backstage/plugin-catalog-common": "workspace:^",
"@types/lodash": "^4.14.151",
"msw": "^1.0.0"
}
+7
View File
@@ -1,5 +1,12 @@
# @backstage/cli-defaults
## 0.1.3-next.0
### Patch Changes
- Updated dependencies
- @backstage/cli-module-build@0.1.4-next.0
## 0.1.2
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/cli-defaults",
"version": "0.1.2",
"version": "0.1.3-next.0",
"description": "Default set of CLI modules for the Backstage CLI",
"backstage": {
"role": "cli-module"
+7
View File
@@ -1,5 +1,12 @@
# @backstage/cli-module-build
## 0.1.4-next.0
### Patch Changes
- a1971ea: Suppress false-positive `@protobufjs/inquire` "Critical dependency" warning in the bundler. Since `protobufjs` 7.5.9, the dynamic require path in inquire is no longer exercised, but webpack/rspack still flags it during static analysis.
- 8007b58: Updated dependency `embedded-postgres` to `18.3.0-beta.17`.
## 0.1.3
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/cli-module-build",
"version": "0.1.3",
"version": "0.1.4-next.0",
"description": "CLI module for Backstage CLI",
"backstage": {
"role": "cli-module"
+8
View File
@@ -1,5 +1,13 @@
# @backstage/cli
## 0.36.3-next.0
### Patch Changes
- Updated dependencies
- @backstage/cli-module-build@0.1.4-next.0
- @backstage/cli-defaults@0.1.3-next.0
## 0.36.2
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/cli",
"version": "0.36.2",
"version": "0.36.3-next.0",
"description": "CLI for developing Backstage plugins and apps",
"backstage": {
"role": "cli"
+7
View File
@@ -1,5 +1,12 @@
# @backstage/core-compat-api
## 0.5.12-next.0
### Patch Changes
- Updated dependencies
- @backstage/plugin-catalog-react@3.0.1-next.0
## 0.5.11
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/core-compat-api",
"version": "0.5.11",
"version": "0.5.12-next.0",
"backstage": {
"role": "web-library"
},
+8
View File
@@ -1,5 +1,13 @@
# @backstage/core-components
## 0.18.11-next.0
### Patch Changes
- e0889a3: chore(deps): bump `qs` from 6.15.1 to 6.15.2
- a07e6a3: Added the correctly-spelled `'header'` literal to the `TableFiltersClassKey` union type and deprecated the previous typoed `'heder'` literal. The generated CSS class with the old key is preserved for backwards compatibility; switch to `'header'` to avoid future removal.
- 8add9b9: Fixed the proxy-based sign-in page failing to read the session token when the proxy issues a token whose payload is encoded using the URL-safe base64 alphabet. Such tokens are now decoded correctly so sign-in no longer breaks.
## 0.18.10
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/core-components",
"version": "0.18.10",
"version": "0.18.11-next.0",
"description": "Core components used by Backstage plugins and apps",
"backstage": {
"role": "web-library"
@@ -114,6 +114,7 @@ const ConditionalAutoLogout = ({
// Events will be rebound as long as `stopOnMount` is not set.
setPromptOpen(false);
setRemainingTimeCountdown(0);
lastSeenOnlineStore.delete();
identityApi.signOut();
};
@@ -231,18 +232,37 @@ const parseConfig = (
export const AutoLogout = (props: AutoLogoutProps): JSX.Element | null => {
const identityApi = useApi(identityApiRef);
const configApi = useApi(configApiRef);
const [isLogged, setIsLogged] = useState(false);
const [isLogged, setIsLogged] = useState<boolean | null>(null);
const lastSeenOnlineStore: TimestampStore = useMemo(
() => new DefaultTimestampStore(LAST_SEEN_ONLINE_STORAGE_KEY),
[],
);
useEffect(() => {
// if the user is not logged in, the autologout feature won't affect the app even if enabled
async function isLoggedIn(identity: IdentityApi) {
if ((await identity.getCredentials()).token) {
setIsLogged(true);
} else {
let cancelled = false;
async function checkLogin(identity: IdentityApi) {
try {
const creds = await identity.getCredentials();
if (cancelled) return;
if (creds?.token) {
setIsLogged(true);
} else {
setIsLogged(false);
lastSeenOnlineStore.delete();
}
} catch (err) {
if (cancelled) return;
setIsLogged(false);
lastSeenOnlineStore.delete();
}
}
isLoggedIn(identityApi);
}, [identityApi]);
checkLogin(identityApi);
return () => {
cancelled = true;
};
}, [lastSeenOnlineStore, identityApi]);
const {
enabled,
@@ -274,10 +294,6 @@ export const AutoLogout = (props: AutoLogoutProps): JSX.Element | null => {
}
}, [idleTimeoutMinutes, promptBeforeIdleSeconds]);
const lastSeenOnlineStore: TimestampStore = useMemo(
() => new DefaultTimestampStore(LAST_SEEN_ONLINE_STORAGE_KEY),
[],
);
const [promptOpen, setPromptOpen] = useState<boolean>(false);
const [remainingTimeCountdown, setRemainingTimeCountdown] =
@@ -285,7 +301,8 @@ export const AutoLogout = (props: AutoLogoutProps): JSX.Element | null => {
useLogoutDisconnectedUserEffect({
enableEffect: logoutIfDisconnected,
autologoutIsEnabled: enabled && isLogged,
autologoutIsEnabled: enabled,
isLoggedIn: isLogged,
idleTimeoutSeconds: idleTimeoutMinutes * 60,
lastSeenOnlineStore,
identityApi,
@@ -35,10 +35,29 @@ const mockTimestampStore = {
};
describe('useLogoutDisconnectedUserEffect', () => {
it('should not do anything if effect is not enabled', () => {
it('should not do anything if isLoggedIn has not yet resolved', () => {
const props: UseLogoutDisconnectedUserEffectProps = {
enableEffect: true,
autologoutIsEnabled: true,
isLoggedIn: null,
idleTimeoutSeconds: 300,
lastSeenOnlineStore: mockTimestampStore,
identityApi: mockIdentityApi,
};
renderHook(() => useLogoutDisconnectedUserEffect(props));
expect(mockTimestampStore.get).not.toHaveBeenCalled();
expect(mockTimestampStore.delete).not.toHaveBeenCalled();
expect(mockTimestampStore.save).not.toHaveBeenCalled();
expect(mockIdentityApi.signOut).not.toHaveBeenCalled();
});
it('should not do anything if effect is not enabled and isLoggedIn is false', () => {
const props: UseLogoutDisconnectedUserEffectProps = {
enableEffect: false,
autologoutIsEnabled: true,
isLoggedIn: false,
idleTimeoutSeconds: 300,
lastSeenOnlineStore: mockTimestampStore,
identityApi: mockIdentityApi,
@@ -54,6 +73,7 @@ describe('useLogoutDisconnectedUserEffect', () => {
const props: UseLogoutDisconnectedUserEffectProps = {
enableEffect: true,
autologoutIsEnabled: false,
isLoggedIn: true,
idleTimeoutSeconds: 300,
lastSeenOnlineStore: mockTimestampStore,
identityApi: mockIdentityApi,
@@ -64,12 +84,34 @@ describe('useLogoutDisconnectedUserEffect', () => {
expect(mockTimestampStore.delete).toHaveBeenCalled();
});
it('should delete the store and sign out when idle timeout has passed', () => {
const staleStore = {
...mockTimestampStore,
get: jest.fn().mockReturnValue(new Date(Date.now() - 2000)),
};
const props: UseLogoutDisconnectedUserEffectProps = {
enableEffect: true,
autologoutIsEnabled: true,
isLoggedIn: true,
idleTimeoutSeconds: 1,
lastSeenOnlineStore: staleStore,
identityApi: mockIdentityApi,
};
renderHook(() => useLogoutDisconnectedUserEffect(props));
expect(staleStore.delete).toHaveBeenCalled();
expect(mockIdentityApi.signOut).toHaveBeenCalled();
expect(staleStore.save).not.toHaveBeenCalled();
});
it('should call signOut if idle timeout passed', () => {
jest.useFakeTimers();
const props: UseLogoutDisconnectedUserEffectProps = {
enableEffect: true,
autologoutIsEnabled: true,
isLoggedIn: true,
idleTimeoutSeconds: 1,
lastSeenOnlineStore: {
...mockTimestampStore,
@@ -93,6 +135,7 @@ describe('useLogoutDisconnectedUserEffect', () => {
const props: UseLogoutDisconnectedUserEffectProps = {
enableEffect: true,
autologoutIsEnabled: true,
isLoggedIn: true,
idleTimeoutSeconds: 300,
lastSeenOnlineStore: mockTimestampStore,
identityApi: mockIdentityApi,
@@ -24,6 +24,7 @@ export const LAST_SEEN_ONLINE_STORAGE_KEY =
export type UseLogoutDisconnectedUserEffectProps = {
enableEffect: boolean;
autologoutIsEnabled: boolean;
isLoggedIn: boolean | null;
idleTimeoutSeconds: number;
lastSeenOnlineStore: TimestampStore;
identityApi: IdentityApi;
@@ -32,6 +33,7 @@ export type UseLogoutDisconnectedUserEffectProps = {
export const useLogoutDisconnectedUserEffect = ({
enableEffect,
autologoutIsEnabled,
isLoggedIn,
idleTimeoutSeconds,
lastSeenOnlineStore,
identityApi,
@@ -41,30 +43,34 @@ export const useLogoutDisconnectedUserEffect = ({
* Considers disconnected users as inactive users.
* If all Backstage tabs are closed and idleTimeoutMinutes are passed then logout the user anyway.
*/
if (autologoutIsEnabled && enableEffect) {
const lastSeenOnline = lastSeenOnlineStore.get();
if (lastSeenOnline) {
const now = new Date();
const nowSeconds = Math.ceil(now.getTime() / 1000);
const lastSeenOnlineSeconds = Math.ceil(
lastSeenOnline.getTime() / 1000,
);
if (nowSeconds - lastSeenOnlineSeconds > idleTimeoutSeconds) {
identityApi.signOut();
}
}
/**
* save for the first time when app is loaded, so that
* if user logs in and does nothing we still have a
* lastSeenOnline value in store
*/
lastSeenOnlineStore.save(new Date());
} else {
lastSeenOnlineStore.delete();
const shouldCheckDisconnectedUser = autologoutIsEnabled && enableEffect;
// Prevent lastSeen getting deleted before logged state is checked
if (isLoggedIn === null) {
return;
}
if (!shouldCheckDisconnectedUser || !isLoggedIn) {
lastSeenOnlineStore.delete();
return;
}
const lastSeenOnline = lastSeenOnlineStore.get();
if (lastSeenOnline) {
const now = new Date();
const nowSeconds = Math.ceil(now.getTime() / 1000);
const lastSeenOnlineSeconds = Math.ceil(lastSeenOnline.getTime() / 1000);
if (nowSeconds - lastSeenOnlineSeconds > idleTimeoutSeconds) {
lastSeenOnlineStore.delete();
identityApi.signOut();
return;
}
}
lastSeenOnlineStore.save(new Date());
}, [
autologoutIsEnabled,
enableEffect,
isLoggedIn,
identityApi,
idleTimeoutSeconds,
lastSeenOnlineStore,
+6
View File
@@ -1,5 +1,11 @@
# @backstage/create-app
## 0.8.4-next.0
### Patch Changes
- Bumped create-app version.
## 0.8.3
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/create-app",
"version": "0.8.3",
"version": "0.8.4-next.0",
"description": "A CLI that helps you create your own Backstage app",
"backstage": {
"role": "cli"
+43
View File
@@ -37,3 +37,46 @@ fast-xml-builder@*:
version "3.1.0"
resolved "https://registry.yarnpkg.com/knex/-/knex-3.1.0.tgz#b6ddd5b5ad26a6315234a5b09ec38dc4a370bd8c"
integrity sha512-GLoII6hR0c4ti243gMs5/1Rb3B+AjwMOfjYm97pu0FOQa7JH56hgBxYf5WK2525ceSbBY1cjeZ9yk99GPMB6Kw==
// @protobufjs/inquire@1.1.1 dropped the eval-based workaround that hid its
// dynamic require() from bundlers, which causes webpack/rspack to emit a
// "Critical dependency: the request of a dependency is an expression" warning
// that fails the build under CI=true. Pin to 1.1.0 until upstream is fixed.
// See https://github.com/protobufjs/protobuf.js/issues/2057
"@protobufjs/inquire@^1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089"
integrity sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==
// protobufjs@7.5.6 bumped its @protobufjs/inquire dependency to ^1.1.1, which
// would bypass the pin above. Pin protobufjs to 7.5.5 (the last version that
// requests inquire ^1.1.0) for all known workspace queries.
"protobufjs@^7.0.0":
version "7.5.5"
resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.5.5.tgz#b7089ca4410374c75150baf277353ef76db69f96"
integrity sha512-3wY1AxV+VBNW8Yypfd1yQY9pXnqTAN+KwQxL8iYm3/BjKYMNg4i0owhEe26PWDOMaIrzeeF98Lqd5NGz4omiIg==
"protobufjs@^7.2.5":
version "7.5.5"
resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.5.5.tgz#b7089ca4410374c75150baf277353ef76db69f96"
integrity sha512-3wY1AxV+VBNW8Yypfd1yQY9pXnqTAN+KwQxL8iYm3/BjKYMNg4i0owhEe26PWDOMaIrzeeF98Lqd5NGz4omiIg==
"protobufjs@^7.2.6":
version "7.5.5"
resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.5.5.tgz#b7089ca4410374c75150baf277353ef76db69f96"
integrity sha512-3wY1AxV+VBNW8Yypfd1yQY9pXnqTAN+KwQxL8iYm3/BjKYMNg4i0owhEe26PWDOMaIrzeeF98Lqd5NGz4omiIg==
"protobufjs@^7.3.2":
version "7.5.5"
resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.5.5.tgz#b7089ca4410374c75150baf277353ef76db69f96"
integrity sha512-3wY1AxV+VBNW8Yypfd1yQY9pXnqTAN+KwQxL8iYm3/BjKYMNg4i0owhEe26PWDOMaIrzeeF98Lqd5NGz4omiIg==
"protobufjs@^7.4.0":
version "7.5.5"
resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.5.5.tgz#b7089ca4410374c75150baf277353ef76db69f96"
integrity sha512-3wY1AxV+VBNW8Yypfd1yQY9pXnqTAN+KwQxL8iYm3/BjKYMNg4i0owhEe26PWDOMaIrzeeF98Lqd5NGz4omiIg==
"protobufjs@^7.5.3":
version "7.5.5"
resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.5.5.tgz#b7089ca4410374c75150baf277353ef76db69f96"
integrity sha512-3wY1AxV+VBNW8Yypfd1yQY9pXnqTAN+KwQxL8iYm3/BjKYMNg4i0owhEe26PWDOMaIrzeeF98Lqd5NGz4omiIg==
+10
View File
@@ -1,5 +1,15 @@
# @backstage/dev-utils
## 1.1.24-next.0
### Patch Changes
- Updated dependencies
- @backstage/plugin-catalog-react@3.0.1-next.0
- @backstage/core-components@0.18.11-next.0
- @backstage/app-defaults@1.7.9-next.0
- @backstage/integration-react@1.2.19-next.0
## 1.1.23
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/dev-utils",
"version": "1.1.23",
"version": "1.1.24-next.0",
"description": "Utilities for developing Backstage plugins.",
"backstage": {
"role": "web-library"
+7
View File
@@ -1,5 +1,12 @@
# e2e-test
## 0.2.41-next.0
### Patch Changes
- Updated dependencies
- @backstage/create-app@0.8.4-next.0
## 0.2.40
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "e2e-test",
"version": "0.2.40",
"version": "0.2.41-next.0",
"description": "E2E test for verifying Backstage packages",
"backstage": {
"role": "cli"
@@ -40,17 +40,22 @@ Replace deprecated tokens with their equivalents from the new semantic families.
### Neutral backgrounds
The neutral background scale has been renamed and extended. `--bui-bg-app` is replaced by the new `--bui-bg-neutral-1`, and the old overlay-based `--bui-bg-neutral-1..4` shift up by one to become `--bui-bg-neutral-2..5`:
The neutral background tokens (`--bui-bg-app`, `--bui-bg-neutral-1..4`) are now the active semantic tokens with updated solid-color values. The `-hover`, `-pressed`, and `-disabled` interaction variants remain deprecated:
| Deprecated | Replacement |
| ---------------------------------- | -------------------- |
| `--bui-bg-app` | `--bui-bg-neutral-1` |
| `--bui-bg-neutral-1` (old overlay) | `--bui-bg-neutral-2` |
| `--bui-bg-neutral-2` (old overlay) | `--bui-bg-neutral-3` |
| `--bui-bg-neutral-3` (old overlay) | `--bui-bg-neutral-4` |
| `--bui-bg-neutral-4` (old overlay) | `--bui-bg-neutral-5` |
> Note: the old `--bui-bg-neutral-1..4` names are now reused for the new solid-color tokens, so this rule does not warn on them directly. The `-hover`, `-pressed`, and `-disabled` variants of those tokens remain deprecated.
| Deprecated | Replacement |
| ----------------------------- | --------------------- |
| `--bui-bg-neutral-1-hover` | _(remove or restyle)_ |
| `--bui-bg-neutral-1-pressed` | _(remove or restyle)_ |
| `--bui-bg-neutral-1-disabled` | _(remove or restyle)_ |
| `--bui-bg-neutral-2-hover` | _(remove or restyle)_ |
| `--bui-bg-neutral-2-pressed` | _(remove or restyle)_ |
| `--bui-bg-neutral-2-disabled` | _(remove or restyle)_ |
| `--bui-bg-neutral-3-hover` | _(remove or restyle)_ |
| `--bui-bg-neutral-3-pressed` | _(remove or restyle)_ |
| `--bui-bg-neutral-3-disabled` | _(remove or restyle)_ |
| `--bui-bg-neutral-4-hover` | _(remove or restyle)_ |
| `--bui-bg-neutral-4-pressed` | _(remove or restyle)_ |
| `--bui-bg-neutral-4-disabled` | _(remove or restyle)_ |
### Foreground
@@ -22,7 +22,6 @@ const DEPRECATED_TOKENS = [
'--bui-bg-solid-hover',
'--bui-bg-solid-pressed',
'--bui-bg-solid-disabled',
'--bui-bg-app',
'--bui-bg-neutral-1-hover',
'--bui-bg-neutral-1-pressed',
'--bui-bg-neutral-1-disabled',
@@ -46,11 +46,11 @@ ruleTester.run('no-deprecated-bui-tokens', rule, {
{ code: `const s = 'var(--bui-warning-bg)'` },
{ code: `const s = 'var(--bui-announcement-bg)'` },
{ code: `const s = 'var(--bui-gray-1)'` },
{ code: `const s = 'var(--bui-bg-app)'` },
{ code: `const s = 'var(--bui-bg-neutral-1)'` },
{ code: `const s = 'var(--bui-bg-neutral-2)'` },
{ code: `const s = 'var(--bui-bg-neutral-3)'` },
{ code: `const s = 'var(--bui-bg-neutral-4)'` },
{ code: `const s = 'var(--bui-bg-neutral-5)'` },
// Unrelated strings — should not warn
{ code: `const s = 'some-other-string'` },
{ code: `const n = 42` },
@@ -83,10 +83,6 @@ ruleTester.run('no-deprecated-bui-tokens', rule, {
},
],
},
{
code: `const s = 'var(--bui-bg-app)'`,
errors: [{ messageId: 'deprecated', data: { token: '--bui-bg-app' } }],
},
{
code: `const s = 'var(--bui-bg-neutral-2-hover)'`,
errors: [

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