Merge branch 'master' into mobile-sidebar

This commit is contained in:
Philipp Hugenroth
2022-01-05 16:43:17 +01:00
363 changed files with 3839 additions and 2203 deletions
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/test-utils': patch
---
Add new `MockConfigApi` as a more discoverable and leaner method for mocking configuration.
+28
View File
@@ -0,0 +1,28 @@
---
'@backstage/cli': minor
---
ESLint upgraded to version 8 and all it's plugins updated to newest version.
If you use any custom plugins for ESLint please check compatibility.
```diff
- "@typescript-eslint/eslint-plugin": "^v4.33.0",
- "@typescript-eslint/parser": "^v4.28.3",
+ "@typescript-eslint/eslint-plugin": "^5.9.0",
+ "@typescript-eslint/parser": "^5.9.0",
- "eslint": "^7.30.0",
+ "eslint": "^8.6.0",
- "eslint-plugin-import": "^2.20.2",
- "eslint-plugin-jest": "^24.1.0",
- "eslint-plugin-jsx-a11y": "^6.2.1",
+ "eslint-plugin-import": "^2.25.4",
+ "eslint-plugin-jest": "^25.3.4",
+ "eslint-plugin-jsx-a11y": "^6.5.1",
- "eslint-plugin-react": "^7.12.4",
- "eslint-plugin-react-hooks": "^4.0.0",
+ "eslint-plugin-react": "^7.28.0",
+ "eslint-plugin-react-hooks": "^4.3.0",
```
Please consult changelogs from packages if you find any problems.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/plugin-catalog-graphql': patch
---
Bump graphql versions
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/plugin-search': patch
---
Fix missing search context issue with `HomePageSearchBar`
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/plugin-catalog-react': patch
---
Add Override Components for Components in @backstage/plugin-catalog-react
+7
View File
@@ -0,0 +1,7 @@
---
'@backstage/plugin-kubernetes': patch
'@backstage/plugin-pagerduty': patch
'@backstage/plugin-splunk-on-call': patch
---
Clean up emptystate.svg image, removing wrong white artifact from the background
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/cli': patch
---
Update `config/eslint.js` to forbid imports of `@material-ui/icons/` as well.
-7
View File
@@ -1,7 +0,0 @@
---
'@backstage/plugin-kubernetes': patch
'@backstage/plugin-kubernetes-backend': patch
'@backstage/plugin-kubernetes-common': patch
---
implement dashboard link formatter for GKE
-5
View File
@@ -1,5 +0,0 @@
---
'@backstage/plugin-tech-insights': patch
---
remove unnecessary http request when running all checks
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/backend-test-utils': patch
---
Bump `testcontainers` dependency to version `8.1.2`
-6
View File
@@ -1,6 +0,0 @@
---
'@backstage/plugin-api-docs': patch
'@backstage/plugin-catalog': patch
---
Support customizing index page layouts via outlets
-9
View File
@@ -1,9 +0,0 @@
---
'@backstage/backend-tasks': patch
---
Updated README to clarify the following:
- Dashes cannot be used in the `id`, changed to an underscore in the example
- The `timeout` is required, this was also added to the example
- Added a note about tasks not running as expected for local development using persistent database
+2 -2
View File
@@ -5,7 +5,7 @@ updates:
schedule:
interval: daily
time: '04:00'
open-pull-requests-limit: 5
open-pull-requests-limit: 10
labels:
- dependencies
- package-ecosystem: npm
@@ -13,6 +13,6 @@ updates:
schedule:
interval: daily
time: '04:00'
open-pull-requests-limit: 2
open-pull-requests-limit: 5
labels:
- dependencies
+2 -2
View File
@@ -17,7 +17,7 @@ jobs:
for your contributions.
days-before-issue-stale: 60
days-before-issue-close: 7
exempt-issue-labels: 'pinned,security,plugin,help wanted,good first issue,rfc'
exempt-issue-labels: 'pinned,security,plugin,help wanted,good first issue,rfc,will-fix'
stale-issue-label: stale
stale-pr-message: >
This PR has been automatically marked as stale because it has not had
@@ -25,6 +25,6 @@ jobs:
If you are the author and the PR has been closed, feel free to re-open the PR and continue the contribution!
days-before-pr-stale: 7
days-before-pr-close: 5
exempt-pr-labels: reviewer-approved,awaiting-review
exempt-pr-labels: reviewer-approved,awaiting-review,will-fix
stale-pr-label: stale
operations-per-run: 100
-4
View File
@@ -2,10 +2,6 @@
# [Backstage](https://backstage.io)
> 🎄 The maintainers will be taking a break over the holidays from week beginning 20th Dec. The Repository and Discord may be quieter than usual. We will be back next year, rested and restored, on Jan. 3. 🎄
> 🎅 Happy holidays and a Happy New Year to all, and especially those that have made this year special for Backstage! 🎅
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
[![CNCF Status](https://img.shields.io/badge/cncf%20status-sandbox-blue.svg)](https://www.cncf.io/projects)
[![Main CI Build](https://github.com/backstage/backstage/workflows/Main%20Master%20Build/badge.svg)](https://github.com/backstage/backstage/actions?query=workflow%3A%22Main+Master+Build%22)
@@ -129,8 +129,12 @@ function msUntilExpiry(token: string): number {
// Calls the specified url regularly using an auth token to set a token cookie
// to authorize regular HTTP requests when loading techdocs
async function setTokenCookie(url: string, getIdToken: () => Promise<string>) {
const token = await getIdToken();
async function setTokenCookie(url: string, identityApi: IdentityApi) {
const { token } = await identityApi.getCredentials();
if (!token) {
return;
}
await fetch(url, {
mode: 'cors',
credentials: 'include',
@@ -138,11 +142,12 @@ async function setTokenCookie(url: string, getIdToken: () => Promise<string>) {
Authorization: `Bearer ${token}`,
},
});
// Call this function again a few minutes before the token expires
const ms = msUntilExpiry(token) - 4 * 60 * 1000;
setTimeout(
() => {
setTokenCookie(url, getIdToken);
setTokenCookie(url, identityApi);
},
ms > 0 ? ms : 10000,
);
@@ -160,16 +165,13 @@ const app = createApp({
providers={['guest', 'custom', ...providers]}
title="Select a sign-in method"
align="center"
onResult={async result => {
// When logged in, set a token cookie
if (typeof result.getIdToken !== 'undefined') {
setTokenCookie(
await discoveryApi.getBaseUrl('cookie'),
result.getIdToken,
);
}
// Forward results
props.onResult(result);
onSignInSuccess={async (identityApi: IdentityApi) => {
setTokenCookie(
await discoveryApi.getBaseUrl('cookie'),
identityApi,
);
props.onSignInSuccess(identityApi);
}}
/>
);
@@ -4,7 +4,7 @@ ExampleFetchComponent.tsx reference
```tsx
import React from 'react';
import { useAsync } from 'react-use';
import useAsync from 'react-use/lib/useAsync';
import Alert from '@material-ui/lab/Alert';
import { githubAuthApiRef, useApi } from '@backstage/core-plugin-api';
import { Table, TableColumn, Progress } from '@backstage/core-components';
@@ -35,9 +35,11 @@ We follow this structure for plugin packages (where `x` is the plugin name, for
example `catalog` or `techdocs`):
- `x`: Contains the main frontend code of the plugin.
- `x-module-<name>`: Contains optional modules related to the frontend plugin
package.
- `x-backend`: Contains the main backend code of the plugin.
- `x-backend-module-<name>`: Contains optional modules related to the backend
plugin.
plugin package.
- `x-react`: Contains shared widgets, hooks and similar that both the plugin
itself (`x`) and third-party frontend plugins can depend on.
- `x-node`: Contains utilities for backends that both the plugin backend itself
@@ -0,0 +1 @@
<mxfile host="Chrome" modified="2021-12-30T23:10:43.723Z" agent="5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36" etag="XmQE_awyVPeS9B3bWnM_" version="15.5.6" type="device"><diagram id="M4OCM2KiCGRnt6vHj1W_" name="Page-1">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</diagram></mxfile>
Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

@@ -30,12 +30,15 @@ export const CustomCatalogPage = ({
actions,
initiallySelectedFilter = 'owned',
}: CatalogPageProps) => {
const createComponentLink = useRouteRef(
catalogPlugin.externalRoutes.createComponent,
);
return (
<PageWithHeader title={`${orgName} Catalog`} themeId="home">
<EntityListProvider>
<Content>
<ContentHeader titleComponent={<CatalogKindHeader />}>
<CreateButton title="Create Component" to={link} />
<CreateButton title="Create Component" to={createComponentLink()} />
<SupportButton>All your software catalog entities</SupportButton>
</ContentHeader>
<FilteredEntityLayout>
@@ -22,6 +22,13 @@ yarn backstage-cli versions:bump
The reason for bumping all `@backstage` packages at once is to maintain the
dependencies that they have between each other.
If you are using other plugins you can pass in the `--pattern` option to update
more than just the `@backstage/*` dependencies.
```bash
yarn backstage-cli versions:bump --pattern '@{backstage,roadiehq}/*'
```
## Following create-app template changes
The `@backstage/create-app` command creates the initial structure of your
+16 -4
View File
@@ -20,8 +20,7 @@ To use this integration, add configuration to your `app-config.yaml`:
```yaml
integrations:
awsS3:
- host: amazonaws.com
accessKeyId: ${AWS_ACCESS_KEY_ID}
- accessKeyId: ${AWS_ACCESS_KEY_ID}
secretAccessKey: ${AWS_SECRET_ACCESS_KEY}
```
@@ -35,8 +34,21 @@ instruct the AWS S3 reader to assume a role before accessing S3:
```yaml
integrations:
awsS3:
- host: amazonaws.com
accessKeyId: ${AWS_ACCESS_KEY_ID}
- accessKeyId: ${AWS_ACCESS_KEY_ID}
secretAccessKey: ${AWS_SECRET_ACCESS_KEY}
roleArn: 'arn:aws:iam::xxxxxxxxxxxx:role/example-role'
```
Configuration allows specifying custom S3 endpoint, along with
[path-style access](https://docs.aws.amazon.com/AmazonS3/latest/userguide/VirtualHosting.html)
to support integration with providers like
[LocalStack](https://github.com/localstack/localstack):
```yaml
integrations:
awsS3:
- endpoint: 'http://localhost:4566'
s3ForcePathStyle: true
accessKeyId: ${AWS_ACCESS_KEY_ID}
secretAccessKey: ${AWS_SECRET_ACCESS_KEY}
```
+1
View File
@@ -594,6 +594,7 @@ Usage: backstage-cli versions:bump [options]
Options:
-h, --help display help for command
-p, --pattern Override glob for matching packages to upgrade
```
## versions:check
+100 -2
View File
@@ -164,12 +164,110 @@ https://circleci.com.
![CircleCI plugin talking to proxy talking to SaaS Circle CI](../assets/architecture-overview/circle-ci-plugin-architecture.png)
## Package Architecture
Backstage relies heavily on NPM packages, both for distribution of libraries,
and structuring of code within projects. While the way you structure your
Backstage project is up to you, there is a set of established patterns that we
encourage you to follow. These patterns can help set up a sound project
structure as well as provide familiarity between different Backstage projects.
The following diagram shows an overview of the package architecture of
Backstage. It takes the point of view of an individual plugin and all of the
packages that it may contain, indicated by the thicker border and italic text.
Surrounding the plugin are different package groups which are the different
possible interface points of the plugin. Note that not all library package lists
are complete as packages have been omitted for brevity.
![Package architecture](../assets/architecture-overview/package-architecture.png)
<!--
NOTE: The diagram is exported from /docs/assets/architecture-overview/package-architecture.drawio
It can be edited using https://www.diagrams.net/
Export it as a PNG with 10px margin without transparent background
-->
### Overview
The arrows in the diagram above indicate a runtime dependency on the code of the
target package. This strict dependency graph only applies to runtime
`dependencies`, and there may be `devDependencies` that break the rules of this
table for the purpose of testing. While there are some arrows that show a
dependency on a collection of frontend, backend and isomorphic packages, those
still have to abide by important compatibility rules shown in the bottom left.
The `app` and `backend` packages are the entry points of a Backstage project.
The `app` package is the frontend application that brings together a collection
of frontend plugins and customizes them to fit an organization, while the
`backend` package is the backend service that powers the Backstage application.
Worth noting is that there can be more than one instance of each of these
packages within a project. Particularly the `backend` packages can benefit from
being split up into smaller deployment units that each serve their own purpose
with a smaller collection of plugins.
### Plugin Packages
A typical plugin consists of up to five packages, two frontend ones, two
backend, and one isomorphic packages. All packages within the plugin must share
a common prefix, typically of the form `@<scope>/plugin-<plugin-id>`, but
alternatives like `backstage-plugin-<plugin-id>` or
`@scope/backstage-plugin-<plugin-id>` are also valid. Along with this prefix,
each of the packages have their own unique suffix that denotes their role. In
addition to these five plugin packages it's also possible for to a plugin to
have additional frontend and backend modules that can be installed to enable
optional features. For a full list of suffixes and their roles, see the
[Plugin Package Structure ADR](../architecture-decisions/adr011-plugin-package-structure.md).
The `-react`, `-common`, and `-node` plugin packages together form the external
library of a plugin. The plugin library enables other plugins to build on top of
and extend a plugin, and likewise allows the plugin to depend on and extend
other plugins. Because of this, it is preferable that plugin library packages
allow duplicate installations of themselves, as you may end up with a mix of
versions being installed as dependencies of various plugins. It is also
forbidden for plugins to directly import non-library packages from other
plugins, all communication between plugins must be handled through libraries and
the application itself.
### Frontend Packages
The frontend packages are grouped into two main groups. The first one is
"Frontend App Core", which is the set of packages that are only used by the
`app` package itself. These packages help build up the core structure of the app
as well as provide a foundation for the plugin libraries to rely upon.
The second group is the rest of the shared packages, further divided into
"Frontend Plugin Core" and "Frontend Libraries". The core packages that are
considered particularly stable and form the core of the frontend framework.
Their most important role is to form the boundary around each plugin and provide
a set of tools that helps you combine a collection of plugins into a running
application. The rest of the frontend packages are more traditional libraries
that serve as building blocks to create plugins.
### Backend Packages
The backend library packages do not currently share a similar plugin
architecture as the frontend packages. They are instead simply a collection of
building blocks and patterns that help you build backend services. This is
however likely to change in the future.
### Common Packages
The common packages are the packages are effectively depended on by all other
pages. This is a much smaller set of packages but they are also very pervasive.
Because the common packages are isomorphic and must execute both in the frontend
and backend, they are never allowed to depend on any of the frontend of backend
packages.
The Backstage CLI is in a category of its own and is depended on by virtually
all other packages. It's not a library in itself though, and must always be a
development dependency only.
## Databases
As we have seen, both the `lighthouse-audit-service` and `catalog-backend`
require a database to work with.
The Backstage backend and its builtin plugins are based on the
The Backstage backend and its built-in plugins are based on the
[Knex](http://knexjs.org/) library, and set up a separate logical database per
plugin. This gives great isolation and lets them perform migrations and evolve
separate from each other.
@@ -184,7 +282,7 @@ yet.
## Cache
The Backstage backend and its builtin plugins are also able to leverage cache
The Backstage backend and its built-in plugins are also able to leverage cache
stores as a means of improving performance or reliability. Similar to how
databases are supported, plugins receive logically separated cache connections,
which are powered by [Keyv](https://github.com/lukechilds/keyv) under the hood.
+1 -1
View File
@@ -135,7 +135,7 @@ changes, let's start by wiping this component clean.
```tsx
import React from 'react';
import { useAsync } from 'react-use';
import useAsync from 'react-use/lib/useAsync';
import Alert from '@material-ui/lab/Alert';
import { Table, TableColumn, Progress } from '@backstage/core-components';
import { githubAuthApiRef, useApi } from '@backstage/core-plugin-api';
+2 -2
View File
@@ -9,7 +9,7 @@
"start": "yarn workspace example-app start",
"start-backend": "yarn workspace example-backend start",
"build": "lerna run build",
"build:api-reports": "yarn tsc:full && yarn build:api-reports:only",
"build:api-reports": "yarn build:api-reports:only --tsc",
"build:api-reports:only": "ts-node -T -P scripts/tsconfig.json scripts/api-extractor.ts",
"build:api-docs": "yarn build:api-reports --docs",
"tsc": "tsc",
@@ -68,7 +68,7 @@
"@types/webpack": "^5.28.0",
"command-exists": "^1.2.9",
"cross-env": "^7.0.0",
"concurrently": "^6.0.0",
"concurrently": "^7.0.0",
"eslint-plugin-notice": "^0.9.10",
"fs-extra": "9.1.0",
"husky": "^6.0.0",
+44
View File
@@ -1,5 +1,49 @@
# example-app
## 0.2.59
### Patch Changes
- Updated dependencies
- @backstage/cli@0.10.5
- @backstage/plugin-search@0.5.4
- @backstage/plugin-techdocs@0.12.13
- @backstage/core-plugin-api@0.4.1
- @backstage/plugin-catalog-react@0.6.10
- @backstage/plugin-kubernetes@0.5.3
- @backstage/core-app-api@0.3.1
- @backstage/core-components@0.8.3
- @backstage/plugin-apache-airflow@0.1.2
- @backstage/plugin-azure-devops@0.1.9
- @backstage/plugin-badges@0.2.18
- @backstage/plugin-catalog@0.7.7
- @backstage/plugin-catalog-graph@0.2.5
- @backstage/plugin-catalog-import@0.7.8
- @backstage/plugin-circleci@0.2.33
- @backstage/plugin-cloudbuild@0.2.31
- @backstage/plugin-code-coverage@0.1.21
- @backstage/plugin-cost-insights@0.11.16
- @backstage/plugin-explore@0.3.24
- @backstage/plugin-gcp-projects@0.3.12
- @backstage/plugin-github-actions@0.4.30
- @backstage/plugin-graphiql@0.2.26
- @backstage/plugin-home@0.4.9
- @backstage/plugin-jenkins@0.5.16
- @backstage/plugin-kafka@0.2.24
- @backstage/plugin-lighthouse@0.2.33
- @backstage/plugin-newrelic@0.3.12
- @backstage/plugin-newrelic-dashboard@0.1.2
- @backstage/plugin-org@0.3.33
- @backstage/plugin-pagerduty@0.3.21
- @backstage/plugin-rollbar@0.3.22
- @backstage/plugin-scaffolder@0.11.17
- @backstage/plugin-sentry@0.3.32
- @backstage/plugin-shortcuts@0.1.18
- @backstage/plugin-tech-insights@0.1.4
- @backstage/plugin-tech-radar@0.5.1
- @backstage/plugin-user-settings@0.3.15
- @backstage/plugin-api-docs@0.6.21
## 0.2.58
### Patch Changes
+42 -42
View File
@@ -1,60 +1,60 @@
{
"name": "example-app",
"version": "0.2.58",
"version": "0.2.59",
"private": true,
"bundled": true,
"dependencies": {
"@backstage/app-defaults": "^0.1.3",
"@backstage/catalog-model": "^0.9.8",
"@backstage/cli": "^0.10.3",
"@backstage/core-app-api": "^0.3.0",
"@backstage/core-components": "^0.8.2",
"@backstage/core-plugin-api": "^0.4.0",
"@backstage/cli": "^0.10.5",
"@backstage/core-app-api": "^0.3.1",
"@backstage/core-components": "^0.8.3",
"@backstage/core-plugin-api": "^0.4.1",
"@backstage/integration-react": "^0.1.16",
"@backstage/plugin-api-docs": "^0.6.19",
"@backstage/plugin-azure-devops": "^0.1.8",
"@backstage/plugin-apache-airflow": "^0.1.1",
"@backstage/plugin-badges": "^0.2.17",
"@backstage/plugin-catalog": "^0.7.5",
"@backstage/plugin-catalog-graph": "^0.2.4",
"@backstage/plugin-catalog-import": "^0.7.6",
"@backstage/plugin-catalog-react": "^0.6.8",
"@backstage/plugin-circleci": "^0.2.32",
"@backstage/plugin-cloudbuild": "^0.2.30",
"@backstage/plugin-code-coverage": "^0.1.20",
"@backstage/plugin-cost-insights": "^0.11.14",
"@backstage/plugin-explore": "^0.3.23",
"@backstage/plugin-gcp-projects": "^0.3.11",
"@backstage/plugin-github-actions": "^0.4.28",
"@backstage/plugin-graphiql": "^0.2.25",
"@backstage/plugin-home": "^0.4.8",
"@backstage/plugin-jenkins": "^0.5.15",
"@backstage/plugin-kafka": "^0.2.23",
"@backstage/plugin-kubernetes": "^0.5.2",
"@backstage/plugin-lighthouse": "^0.2.32",
"@backstage/plugin-newrelic": "^0.3.11",
"@backstage/plugin-newrelic-dashboard": "^0.1.1",
"@backstage/plugin-org": "^0.3.32",
"@backstage/plugin-pagerduty": "0.3.20",
"@backstage/plugin-rollbar": "^0.3.21",
"@backstage/plugin-scaffolder": "^0.11.15",
"@backstage/plugin-search": "^0.5.2",
"@backstage/plugin-sentry": "^0.3.31",
"@backstage/plugin-shortcuts": "^0.1.16",
"@backstage/plugin-tech-radar": "^0.5.0",
"@backstage/plugin-techdocs": "^0.12.11",
"@backstage/plugin-api-docs": "^0.6.21",
"@backstage/plugin-azure-devops": "^0.1.9",
"@backstage/plugin-apache-airflow": "^0.1.2",
"@backstage/plugin-badges": "^0.2.18",
"@backstage/plugin-catalog": "^0.7.7",
"@backstage/plugin-catalog-graph": "^0.2.5",
"@backstage/plugin-catalog-import": "^0.7.8",
"@backstage/plugin-catalog-react": "^0.6.10",
"@backstage/plugin-circleci": "^0.2.33",
"@backstage/plugin-cloudbuild": "^0.2.31",
"@backstage/plugin-code-coverage": "^0.1.21",
"@backstage/plugin-cost-insights": "^0.11.16",
"@backstage/plugin-explore": "^0.3.24",
"@backstage/plugin-gcp-projects": "^0.3.12",
"@backstage/plugin-github-actions": "^0.4.30",
"@backstage/plugin-graphiql": "^0.2.26",
"@backstage/plugin-home": "^0.4.9",
"@backstage/plugin-jenkins": "^0.5.16",
"@backstage/plugin-kafka": "^0.2.24",
"@backstage/plugin-kubernetes": "^0.5.3",
"@backstage/plugin-lighthouse": "^0.2.33",
"@backstage/plugin-newrelic": "^0.3.12",
"@backstage/plugin-newrelic-dashboard": "^0.1.2",
"@backstage/plugin-org": "^0.3.33",
"@backstage/plugin-pagerduty": "0.3.21",
"@backstage/plugin-rollbar": "^0.3.22",
"@backstage/plugin-scaffolder": "^0.11.17",
"@backstage/plugin-search": "^0.5.4",
"@backstage/plugin-sentry": "^0.3.32",
"@backstage/plugin-shortcuts": "^0.1.18",
"@backstage/plugin-tech-radar": "^0.5.1",
"@backstage/plugin-techdocs": "^0.12.13",
"@backstage/plugin-todo": "^0.1.17",
"@backstage/plugin-user-settings": "^0.3.14",
"@backstage/plugin-user-settings": "^0.3.15",
"@backstage/search-common": "^0.2.0",
"@backstage/plugin-tech-insights": "^0.1.2",
"@backstage/plugin-tech-insights": "^0.1.4",
"@backstage/theme": "^0.2.14",
"@material-ui/core": "^4.12.2",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.57",
"@octokit/rest": "^18.5.3",
"@roadiehq/backstage-plugin-github-insights": "^1.2.2",
"@roadiehq/backstage-plugin-github-pull-requests": "^1.0.13",
"@roadiehq/backstage-plugin-travis-ci": "^1.0.11",
"@roadiehq/backstage-plugin-github-insights": "^1.4.2",
"@roadiehq/backstage-plugin-github-pull-requests": "^1.3.2",
"@roadiehq/backstage-plugin-travis-ci": "^1.3.2",
"history": "^5.0.0",
"prop-types": "^15.7.2",
"react": "^16.13.1",
+11
View File
@@ -1,5 +1,16 @@
# @backstage/backend-common
## 0.10.2
### Patch Changes
- 21ae56168e: Updated the Git class with the following:
- Added `depth` and `noCheckout` options to Git clone, using these you can create a bare clone that includes just the git history
- New `log` function which you can use to view the commit history of a git repo
- eacc582473: Reverted the default CSP configuration to include `'unsafe-eval'` again, which was mistakenly removed in the previous version.
## 0.10.1
### Patch Changes
+8 -7
View File
@@ -234,8 +234,13 @@ export class Git {
remote: string;
url: string;
}): Promise<void>;
// (undocumented)
clone(options: { url: string; dir: string; ref?: string }): Promise<void>;
clone(options: {
url: string;
dir: string;
ref?: string;
depth?: number;
noCheckout?: boolean;
}): Promise<void>;
// (undocumented)
commit(options: {
dir: string;
@@ -249,12 +254,10 @@ export class Git {
email: string;
};
}): Promise<string>;
// (undocumented)
currentBranch(options: {
dir: string;
fullName?: boolean;
}): Promise<string | undefined>;
// (undocumented)
fetch(options: { dir: string; remote?: string }): Promise<void>;
// (undocumented)
static fromAuth: (options: {
@@ -264,7 +267,7 @@ export class Git {
}) => Git;
// (undocumented)
init(options: { dir: string; defaultBranch?: string }): Promise<void>;
// (undocumented)
log(options: { dir: string; ref?: string }): Promise<ReadCommitResult[]>;
merge(options: {
dir: string;
theirs: string;
@@ -280,9 +283,7 @@ export class Git {
}): Promise<MergeResult>;
// (undocumented)
push(options: { dir: string; remote: string }): Promise<PushResult>;
// (undocumented)
readCommit(options: { dir: string; sha: string }): Promise<ReadCommitResult>;
// (undocumented)
resolveRef(options: { dir: string; ref: string }): Promise<string>;
}
+2 -2
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/backend-common",
"description": "Common functionality library for Backstage backends",
"version": "0.10.1",
"version": "0.10.2",
"main": "src/index.ts",
"types": "src/index.ts",
"private": false,
@@ -81,7 +81,7 @@
}
},
"devDependencies": {
"@backstage/cli": "^0.10.4",
"@backstage/cli": "^0.10.5",
"@backstage/test-utils": "^0.2.1",
"@types/archiver": "^5.1.0",
"@types/compression": "^1.7.0",
@@ -320,4 +320,21 @@ describe('Git', () => {
});
});
});
describe('log', () => {
it('should call isomorphic-git with the correct arguments', async () => {
const dir = '/some/mock/dir';
const ref = 'as43bd7';
const git = Git.fromAuth({});
await git.log({ dir, ref });
expect(isomorphic.log).toHaveBeenCalledWith({
fs,
dir,
ref,
});
});
});
});
+24 -7
View File
@@ -76,12 +76,15 @@ export class Git {
return git.commit({ fs, dir, message, author, committer });
}
/** https://isomorphic-git.org/docs/en/clone */
async clone(options: {
url: string;
dir: string;
ref?: string;
depth?: number;
noCheckout?: boolean;
}): Promise<void> {
const { url, dir, ref } = options;
const { url, dir, ref, depth, noCheckout } = options;
this.config.logger?.info(`Cloning repo {dir=${dir},url=${url}}`);
return git.clone({
fs,
@@ -90,7 +93,8 @@ export class Git {
dir,
ref,
singleBranch: true,
depth: 1,
depth: depth ?? 1,
noCheckout,
onProgress: this.onProgressHandler(),
headers: {
'user-agent': 'git/@isomorphic-git',
@@ -99,7 +103,7 @@ export class Git {
});
}
// https://isomorphic-git.org/docs/en/currentBranch
/** https://isomorphic-git.org/docs/en/currentBranch */
async currentBranch(options: {
dir: string;
fullName?: boolean;
@@ -110,7 +114,7 @@ export class Git {
>;
}
// https://isomorphic-git.org/docs/en/fetch
/** https://isomorphic-git.org/docs/en/fetch */
async fetch(options: { dir: string; remote?: string }): Promise<void> {
const { dir, remote = 'origin' } = options;
this.config.logger?.info(
@@ -138,7 +142,7 @@ export class Git {
});
}
// https://isomorphic-git.org/docs/en/merge
/** https://isomorphic-git.org/docs/en/merge */
async merge(options: {
dir: string;
theirs: string;
@@ -180,7 +184,7 @@ export class Git {
});
}
// https://isomorphic-git.org/docs/en/readCommit
/** https://isomorphic-git.org/docs/en/readCommit */
async readCommit(options: {
dir: string;
sha: string;
@@ -189,12 +193,25 @@ export class Git {
return git.readCommit({ fs, dir, oid: sha });
}
// https://isomorphic-git.org/docs/en/resolveRef
/** https://isomorphic-git.org/docs/en/resolveRef */
async resolveRef(options: { dir: string; ref: string }): Promise<string> {
const { dir, ref } = options;
return git.resolveRef({ fs, dir, ref });
}
/** https://isomorphic-git.org/docs/en/log */
async log(options: {
dir: string;
ref?: string;
}): Promise<ReadCommitResult[]> {
const { dir, ref } = options;
return git.log({
fs,
dir,
ref: ref ?? 'HEAD',
});
}
private onAuth = () => ({
username: this.config.username,
password: this.config.password,
@@ -228,6 +228,10 @@ export function applyCspDirectives(
const result: ContentSecurityPolicyOptions['directives'] =
helmet.contentSecurityPolicy.getDefaultDirectives();
// TODO(Rugvip): We currently use non-precompiled AJV for validation in the frontend, which uses eval.
// It should be replaced by any other solution that doesn't require unsafe-eval.
result['script-src'] = ["'self'", "'unsafe-eval'"];
if (directives) {
for (const [key, value] of Object.entries(directives)) {
if (value === false) {
+13
View File
@@ -1,5 +1,18 @@
# @backstage/backend-tasks
## 0.1.2
### Patch Changes
- e188b37024: Updated README to clarify the following:
- Dashes cannot be used in the `id`, changed to an underscore in the example
- The `timeout` is required, this was also added to the example
- Added a note about tasks not running as expected for local development using persistent database
- Updated dependencies
- @backstage/backend-common@0.10.2
## 0.1.1
### Patch Changes
+3 -3
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/backend-tasks",
"description": "Common distributed task management library for Backstage backends",
"version": "0.1.1",
"version": "0.1.2",
"main": "src/index.ts",
"types": "src/index.ts",
"private": false,
@@ -29,7 +29,7 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/backend-common": "^0.10.0",
"@backstage/backend-common": "^0.10.2",
"@backstage/config": "^0.1.11",
"@backstage/errors": "^0.1.4",
"@backstage/types": "^0.1.1",
@@ -44,7 +44,7 @@
},
"devDependencies": {
"@backstage/backend-test-utils": "^0.1.11",
"@backstage/cli": "^0.10.3",
"@backstage/cli": "^0.10.5",
"jest": "^26.0.1",
"wait-for-expect": "^3.0.2"
},
+1 -1
View File
@@ -37,7 +37,7 @@
"mysql2": "^2.2.5",
"pg": "^8.3.0",
"sqlite3": "^5.0.1",
"testcontainers": "^7.23.0",
"testcontainers": "^8.1.2",
"uuid": "^8.0.0"
},
"devDependencies": {
+3 -3
View File
@@ -41,11 +41,11 @@ export default async function createPlugin({
discovery,
factRetrievers: [
createFactRetrieverRegistration(
'* * * * *', // Example cron, every minute
'1 1 1 * *', // Example cron, At 01:01 on day-of-month 1.
entityOwnershipFactRetriever,
),
createFactRetrieverRegistration('* * * * *', entityMetadataFactRetriever),
createFactRetrieverRegistration('* * * * *', techdocsFactRetriever),
createFactRetrieverRegistration('1 1 1 * *', entityMetadataFactRetriever),
createFactRetrieverRegistration('1 1 1 * *', techdocsFactRetriever),
],
factCheckerFactory: new JsonRulesEngineFactCheckerFactory({
checks: [
+8
View File
@@ -1,5 +1,13 @@
# @backstage/cli
## 0.10.5
### Patch Changes
- 37123b4851: Bump `css-loader` from `5.2.6` to `6.5.1`
- 9534391ae4: Add --pattern option to override matching glob patterns for backstage versioning
- 4ce51ab0f1: Internal refactor of the `react-use` imports to use `react-use/lib/*` instead.
## 0.10.4
### Patch Changes
+4
View File
@@ -80,6 +80,10 @@ module.exports = {
name: '@material-ui/icons',
message: "Please import '@material-ui/icons/<Icon>' instead.",
},
{
name: '@material-ui/icons/', // because this is possible too ._.
message: "Please import '@material-ui/icons/<Icon>' instead.",
},
...require('module').builtinModules,
],
// Avoid cross-package imports
+17 -15
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/cli",
"description": "CLI for developing Backstage plugins and apps",
"version": "0.10.4",
"version": "0.10.5",
"private": false,
"publishConfig": {
"access": "public"
@@ -51,26 +51,26 @@
"@svgr/rollup": "5.5.x",
"@svgr/webpack": "5.5.x",
"@types/webpack-env": "^1.15.2",
"@typescript-eslint/eslint-plugin": "^v4.33.0",
"@typescript-eslint/parser": "^v4.28.3",
"@typescript-eslint/eslint-plugin": "^5.9.0",
"@typescript-eslint/parser": "^5.9.0",
"@yarnpkg/lockfile": "^1.1.0",
"bfj": "^7.0.2",
"buffer": "^6.0.3",
"chalk": "^4.0.0",
"chokidar": "^3.3.1",
"commander": "^6.1.0",
"css-loader": "^5.2.6",
"css-loader": "^6.5.1",
"diff": "^5.0.0",
"esbuild": "^0.14.1",
"eslint": "^7.30.0",
"eslint": "^8.6.0",
"eslint-config-prettier": "^8.3.0",
"eslint-formatter-friendly": "^7.0.0",
"eslint-plugin-import": "^2.20.2",
"eslint-plugin-jest": "^24.1.0",
"eslint-plugin-jsx-a11y": "^6.2.1",
"eslint-plugin-import": "^2.25.4",
"eslint-plugin-jest": "^25.3.4",
"eslint-plugin-jsx-a11y": "^6.5.1",
"eslint-plugin-monorepo": "^0.3.2",
"eslint-plugin-react": "^7.12.4",
"eslint-plugin-react-hooks": "^4.0.0",
"eslint-plugin-react": "^7.28.0",
"eslint-plugin-react-hooks": "^4.3.0",
"express": "^4.17.1",
"fork-ts-checker-webpack-plugin": "^4.0.5",
"fs-extra": "9.1.0",
@@ -83,6 +83,7 @@
"json-schema": "^0.4.0",
"jest-transform-yaml": "^0.1.1",
"lodash": "^4.17.21",
"minimatch": "3.0.4",
"mini-css-extract-plugin": "^2.4.2",
"node-libs-browser": "^2.2.1",
"ora": "^5.3.0",
@@ -114,12 +115,12 @@
"yn": "^4.0.0"
},
"devDependencies": {
"@backstage/backend-common": "^0.10.1",
"@backstage/backend-common": "^0.10.2",
"@backstage/config": "^0.1.11",
"@backstage/core-components": "^0.8.2",
"@backstage/core-plugin-api": "^0.4.0",
"@backstage/core-app-api": "^0.3.0",
"@backstage/dev-utils": "^0.2.15",
"@backstage/core-components": "^0.8.3",
"@backstage/core-plugin-api": "^0.4.1",
"@backstage/core-app-api": "^0.3.1",
"@backstage/dev-utils": "^0.2.16",
"@backstage/test-utils": "^0.2.1",
"@backstage/theme": "^0.2.14",
"@types/diff": "^5.0.0",
@@ -127,6 +128,7 @@
"@types/fs-extra": "^9.0.1",
"@types/http-proxy": "^1.17.4",
"@types/inquirer": "^8.1.3",
"@types/minimatch": "^3.0.5",
"@types/mock-fs": "^4.13.0",
"@types/node": "^14.14.32",
"@types/recursive-readdir": "^2.2.0",
+4
View File
@@ -214,6 +214,10 @@ export function registerCommands(program: CommanderStatic) {
program
.command('versions:bump')
.option(
'--pattern <glob>',
'Override glob for matching packages to upgrade',
)
.description('Bump Backstage packages to the latest versions')
.action(lazy(() => import('./versions/bump').then(m => m.default)));
+145 -4
View File
@@ -16,6 +16,7 @@
import fs from 'fs-extra';
import mockFs from 'mock-fs';
import { Command } from 'commander';
import { resolve as resolvePath } from 'path';
import { paths } from '../../lib/paths';
import { mapDependencies } from '../../lib/versioning';
@@ -36,6 +37,8 @@ const REGISTRY_VERSIONS: { [name: string]: string } = {
'@backstage/core': '1.0.6',
'@backstage/core-api': '1.0.7',
'@backstage/theme': '2.0.0',
'@backstage-extra/custom': '1.1.0',
'@backstage-extra/custom-two': '2.0.0',
};
const HEADER = `# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
@@ -83,7 +86,7 @@ describe('bump', () => {
it('should bump backstage dependencies', async () => {
// Make sure all modules involved in package discovery are in the module cache before we mock fs
await mapDependencies(paths.targetDir);
await mapDependencies(paths.targetDir, '@backstage/*');
mockFs({
'/yarn.lock': lockfileMock,
@@ -123,9 +126,10 @@ describe('bump', () => {
jest.spyOn(runObj, 'run').mockResolvedValue(undefined);
const { log: logs } = await withLogCollector(['log'], async () => {
await bump();
await bump({ pattern: null } as unknown as Command);
});
expect(logs.filter(Boolean)).toEqual([
'Using default pattern glob @backstage/*',
'Checking for updates of @backstage/theme',
'Checking for updates of @backstage/core',
'Checking for updates of @backstage/core-api',
@@ -178,9 +182,145 @@ describe('bump', () => {
});
});
it('should bump backstage dependencies and dependencies matching pattern glob', async () => {
// Make sure all modules involved in package discovery are in the module cache before we mock fs
await mapDependencies(paths.targetDir, '@backstage/*');
const customLockfileMock = `${lockfileMock}
"@backstage-extra/custom@^1.1.0":
version "1.1.0"
"@backstage-extra/custom@^1.0.1":
version "1.0.1"
"@backstage-extra/custom-two@^1.0.0":
version "1.0.0"
`;
const customLockfileMockResult = `${HEADER}
"@backstage-extra/custom-two@^1.0.0":
version "1.0.0"
"@backstage-extra/custom@^1.1.0":
version "1.1.0"
"@backstage/core@^1.0.5":
version "1.0.6"
dependencies:
"@backstage/core-api" "^1.0.6"
"@backstage/theme@^1.0.0":
version "1.0.0"
`;
mockFs({
'/yarn.lock': customLockfileMock,
'/lerna.json': JSON.stringify({
packages: ['packages/*'],
}),
'/packages/a/package.json': JSON.stringify({
name: 'a',
dependencies: {
'@backstage/core': '^1.0.5',
'@backstage-extra/custom': '^1.0.1',
'@backstage-extra/custom-two': '^1.0.0',
},
}),
'/packages/b/package.json': JSON.stringify({
name: 'b',
dependencies: {
'@backstage/core': '^1.0.3',
'@backstage/theme': '^1.0.0',
'@backstage-extra/custom': '^1.1.0',
'@backstage-extra/custom-two': '^1.0.0',
},
}),
});
paths.targetDir = '/';
jest
.spyOn(paths, 'resolveTargetRoot')
.mockImplementation((...path) => resolvePath('/', ...path));
jest.spyOn(runObj, 'runPlain').mockImplementation(async (...[, , , name]) =>
JSON.stringify({
type: 'inspect',
data: {
name: name,
'dist-tags': {
latest: REGISTRY_VERSIONS[name],
},
},
}),
);
jest.spyOn(runObj, 'run').mockResolvedValue(undefined);
const { log: logs } = await withLogCollector(['log'], async () => {
await bump({ pattern: '@{backstage,backstage-extra}/*' } as any);
});
expect(logs.filter(Boolean)).toEqual([
'Using custom pattern glob @{backstage,backstage-extra}/*',
'Checking for updates of @backstage/theme',
'Checking for updates of @backstage-extra/custom-two',
'Checking for updates of @backstage-extra/custom',
'Checking for updates of @backstage/core',
'Checking for updates of @backstage/core-api',
'Some packages are outdated, updating',
'unlocking @backstage-extra/custom@^1.0.1 ~> 1.1.0',
'unlocking @backstage/core@^1.0.3 ~> 1.0.6',
'unlocking @backstage/core-api@^1.0.6 ~> 1.0.7',
'unlocking @backstage/core-api@^1.0.3 ~> 1.0.7',
'bumping @backstage-extra/custom-two in a to ^2.0.0',
'bumping @backstage/theme in b to ^2.0.0',
'bumping @backstage-extra/custom-two in b to ^2.0.0',
'Running yarn install to install new versions',
'⚠️ The following packages may have breaking changes:',
' @backstage-extra/custom-two : 1.0.0 ~> 2.0.0',
' @backstage/theme : 1.0.0 ~> 2.0.0',
' https://github.com/backstage/backstage/blob/master/packages/theme/CHANGELOG.md',
'Version bump complete!',
]);
expect(runObj.runPlain).toHaveBeenCalledTimes(6);
expect(runObj.runPlain).toHaveBeenCalledWith(
'yarn',
'info',
'--json',
'@backstage/core',
);
expect(runObj.runPlain).toHaveBeenCalledWith(
'yarn',
'info',
'--json',
'@backstage/theme',
);
expect(runObj.run).toHaveBeenCalledTimes(1);
expect(runObj.run).toHaveBeenCalledWith('yarn', ['install']);
const lockfileContents = await fs.readFile('/yarn.lock', 'utf8');
expect(lockfileContents).toEqual(customLockfileMockResult);
const packageA = await fs.readJson('/packages/a/package.json');
expect(packageA).toEqual({
name: 'a',
dependencies: {
'@backstage-extra/custom': '^1.0.1',
'@backstage-extra/custom-two': '^2.0.0',
'@backstage/core': '^1.0.5', // not bumped since new version is within range
},
});
const packageB = await fs.readJson('/packages/b/package.json');
expect(packageB).toEqual({
name: 'b',
dependencies: {
'@backstage-extra/custom': '^1.1.0',
'@backstage-extra/custom-two': '^2.0.0',
'@backstage/core': '^1.0.3', // not bumped
'@backstage/theme': '^2.0.0', // bumped since newer
},
});
});
it('should ignore not found packages', async () => {
// Make sure all modules involved in package discovery are in the module cache before we mock fs
await mapDependencies(paths.targetDir);
await mapDependencies(paths.targetDir, '@backstage/*');
mockFs({
'/yarn.lock': lockfileMockResult,
'/lerna.json': JSON.stringify({
@@ -209,9 +349,10 @@ describe('bump', () => {
jest.spyOn(runObj, 'run').mockResolvedValue(undefined);
const { log: logs } = await withLogCollector(['log'], async () => {
await bump();
await bump({ pattern: null } as unknown as Command);
});
expect(logs.filter(Boolean)).toEqual([
'Using default pattern glob @backstage/*',
'Checking for updates of @backstage/theme',
'Checking for updates of @backstage/core',
'Package info not found, ignoring package @backstage/theme',
+19 -5
View File
@@ -17,6 +17,8 @@
import fs from 'fs-extra';
import chalk from 'chalk';
import semver from 'semver';
import minimatch from 'minimatch';
import { Command } from 'commander';
import { isError } from '@backstage/errors';
import { resolve as resolvePath } from 'path';
import { run } from '../../lib/run';
@@ -26,7 +28,7 @@ import {
fetchPackageInfo,
Lockfile,
} from '../../lib/versioning';
import { includedFilter, forbiddenDuplicatesFilter } from './lint';
import { forbiddenDuplicatesFilter } from './lint';
import { BACKSTAGE_JSON } from '@backstage/cli-common';
const DEP_TYPES = [
@@ -36,6 +38,8 @@ const DEP_TYPES = [
'optionalDependencies',
];
const DEFAULT_PATTERN_GLOB = '@backstage/*';
type PkgVersionInfo = {
range: string;
target: string;
@@ -43,14 +47,22 @@ type PkgVersionInfo = {
location: string;
};
export default async () => {
export default async (cmd: Command) => {
const lockfilePath = paths.resolveTargetRoot('yarn.lock');
const lockfile = await Lockfile.load(lockfilePath);
let pattern = cmd.pattern;
if (!pattern) {
console.log(`Using default pattern glob ${DEFAULT_PATTERN_GLOB}`);
pattern = DEFAULT_PATTERN_GLOB;
} else {
console.log(`Using custom pattern glob ${pattern}`);
}
const findTargetVersion = createVersionFinder();
// First we discover all Backstage dependencies within our own repo
const dependencyMap = await mapDependencies(paths.targetDir);
const dependencyMap = await mapDependencies(paths.targetDir, pattern);
// Next check with the package registry to see which dependency ranges we need to bump
const versionBumps = new Map<string, PkgVersionInfo[]>();
@@ -88,10 +100,12 @@ export default async () => {
}
});
const filter = (name: string) => minimatch(name, pattern);
// Check for updates of transitive backstage dependencies
await workerThreads(16, lockfile.keys(), async name => {
// Only check @backstage packages and friends, we don't want this to do a full update of all deps
if (!includedFilter(name)) {
if (!filter(name)) {
return;
}
@@ -237,7 +251,7 @@ export default async () => {
// Finally we make sure the new lockfile doesn't have any duplicates
const dedupLockfile = await Lockfile.load(lockfilePath);
const result = dedupLockfile.analyze({
filter: includedFilter,
filter,
});
if (result.newVersions.length > 0) {
@@ -82,7 +82,10 @@ describe('mapDependencies', () => {
}),
});
const dependencyMap = await mapDependencies(paths.targetDir);
const dependencyMap = await mapDependencies(
paths.targetDir,
'@backstage/*',
);
expect(Array.from(dependencyMap)).toEqual([
[
'@backstage/core',
+3 -4
View File
@@ -13,12 +13,10 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import minimatch from 'minimatch';
import { runPlain } from '../../lib/run';
import { NotFoundError } from '../errors';
const PREFIX = '@backstage';
const DEP_TYPES = [
'dependencies',
'devDependencies',
@@ -66,6 +64,7 @@ export async function fetchPackageInfo(
/** Map all dependencies in the repo as dependency => dependents */
export async function mapDependencies(
targetDir: string,
pattern: string,
): Promise<Map<string, PkgVersionInfo[]>> {
const { Project } = require('@lerna/project');
const project = new Project(targetDir);
@@ -78,7 +77,7 @@ export async function mapDependencies(
);
for (const [name, range] of deps) {
if (name.startsWith(PREFIX)) {
if (minimatch(name, pattern)) {
dependencyMap.set(
name,
(dependencyMap.get(name) ?? []).concat({
@@ -2,7 +2,7 @@ import React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import { Table, TableColumn, Progress } from '@backstage/core-components';
import Alert from '@material-ui/lab/Alert';
import { useAsync } from 'react-use';
import useAsync from 'react-use/lib/useAsync';
const useStyles = makeStyles({
avatar: {
+9
View File
@@ -1,5 +1,14 @@
# @backstage/codemods
## 0.1.28
### Patch Changes
- Updated dependencies
- @backstage/core-plugin-api@0.4.1
- @backstage/core-app-api@0.3.1
- @backstage/core-components@0.8.3
## 0.1.27
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/codemods",
"description": "A collection of codemods for Backstage projects",
"version": "0.1.27",
"version": "0.1.28",
"private": false,
"publishConfig": {
"access": "public",
+9
View File
@@ -1,5 +1,14 @@
# @backstage/core-app-api
## 0.3.1
### Patch Changes
- 4ce51ab0f1: Internal refactor of the `react-use` imports to use `react-use/lib/*` instead.
- Updated dependencies
- @backstage/core-plugin-api@0.4.1
- @backstage/core-components@0.8.3
## 0.3.0
### Minor Changes
+4 -4
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/core-app-api",
"description": "Core app API used by Backstage apps",
"version": "0.3.0",
"version": "0.3.1",
"private": false,
"publishConfig": {
"access": "public",
@@ -30,9 +30,9 @@
},
"dependencies": {
"@backstage/app-defaults": "^0.1.3",
"@backstage/core-components": "^0.8.2",
"@backstage/core-components": "^0.8.3",
"@backstage/config": "^0.1.11",
"@backstage/core-plugin-api": "^0.4.0",
"@backstage/core-plugin-api": "^0.4.1",
"@backstage/theme": "^0.2.14",
"@backstage/types": "^0.1.1",
"@backstage/version-bridge": "^0.1.1",
@@ -50,7 +50,7 @@
"react": "^16.13.1 || ^17.0.0"
},
"devDependencies": {
"@backstage/cli": "^0.10.3",
"@backstage/cli": "^0.10.5",
"@backstage/test-utils": "^0.2.0",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^11.2.5",
+1 -1
View File
@@ -24,7 +24,7 @@ import React, {
useState,
} from 'react';
import { Route, Routes } from 'react-router-dom';
import { useAsync } from 'react-use';
import useAsync from 'react-use/lib/useAsync';
import {
ApiProvider,
ApiRegistry,
@@ -16,7 +16,7 @@
import React, { useMemo, useEffect, useState, PropsWithChildren } from 'react';
import { useApi, appThemeApiRef, AppTheme } from '@backstage/core-plugin-api';
import { useObservable } from 'react-use';
import useObservable from 'react-use/lib/useObservable';
// This tries to find the most accurate match, but also falls back to less
// accurate results in order to avoid errors.
+8
View File
@@ -1,5 +1,13 @@
# @backstage/core-components
## 0.8.3
### Patch Changes
- 4ce51ab0f1: Internal refactor of the `react-use` imports to use `react-use/lib/*` instead.
- Updated dependencies
- @backstage/core-plugin-api@0.4.1
## 0.8.2
### Patch Changes
+4 -4
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/core-components",
"description": "Core components used by Backstage plugins and apps",
"version": "0.8.2",
"version": "0.8.3",
"private": false,
"publishConfig": {
"access": "public",
@@ -30,7 +30,7 @@
},
"dependencies": {
"@backstage/config": "^0.1.11",
"@backstage/core-plugin-api": "^0.4.0",
"@backstage/core-plugin-api": "^0.4.1",
"@backstage/errors": "^0.1.5",
"@backstage/theme": "^0.2.14",
"@material-table/core": "^3.1.0",
@@ -72,8 +72,8 @@
"react-dom": "^16.13.1 || ^17.0.0"
},
"devDependencies": {
"@backstage/core-app-api": "^0.3.0",
"@backstage/cli": "^0.10.3",
"@backstage/core-app-api": "^0.3.1",
"@backstage/cli": "^0.10.5",
"@backstage/test-utils": "^0.2.0",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^11.2.5",
@@ -19,7 +19,7 @@ import { act, fireEvent } from '@testing-library/react';
import { renderInTestApp, TestApiProvider } from '@backstage/test-utils';
import { CopyTextButton } from './CopyTextButton';
import { errorApiRef } from '@backstage/core-plugin-api';
import { useCopyToClipboard } from 'react-use';
import useCopyToClipboard from 'react-use/lib/useCopyToClipboard';
jest.mock('popper.js', () => {
const PopperJS = jest.requireActual('popper.js');
@@ -32,14 +32,12 @@ jest.mock('popper.js', () => {
};
});
jest.mock('react-use', () => {
const original = jest.requireActual('react-use');
jest.mock('react-use/lib/useCopyToClipboard', () => {
const original = jest.requireActual('react-use/lib/useCopyToClipboard');
return {
...original,
useCopyToClipboard: jest
.fn()
.mockImplementation(original.useCopyToClipboard),
__esModule: true,
default: jest.fn().mockImplementation(original.default),
};
});
@@ -19,7 +19,7 @@ import IconButton from '@material-ui/core/IconButton';
import Tooltip from '@material-ui/core/Tooltip';
import CopyIcon from '@material-ui/icons/FileCopy';
import React, { MouseEventHandler, useEffect, useState } from 'react';
import { useCopyToClipboard } from 'react-use';
import useCopyToClipboard from 'react-use/lib/useCopyToClipboard';
/**
* Properties for {@link CopyTextButton}
@@ -16,7 +16,7 @@
import React, { ReactNode, useState, useEffect } from 'react';
import { useApi, storageApiRef } from '@backstage/core-plugin-api';
import { useObservable } from 'react-use';
import useObservable from 'react-use/lib/useObservable';
import classNames from 'classnames';
import { makeStyles } from '@material-ui/core/styles';
import { BackstageTheme } from '@backstage/theme';
@@ -15,7 +15,7 @@
*/
import { useMemo, useState } from 'react';
import { useToggle } from 'react-use';
import useToggle from 'react-use/lib/useToggle';
import { AnsiLine } from './AnsiProcessor';
export function applySearchFilter(lines: AnsiLine[], searchText: string) {
@@ -16,7 +16,7 @@
import { errorApiRef, useApi } from '@backstage/core-plugin-api';
import { useEffect, useState } from 'react';
import { useCopyToClipboard } from 'react-use';
import useCopyToClipboard from 'react-use/lib/useCopyToClipboard';
import { AnsiLine } from './AnsiProcessor';
export function useLogViewerSelection(lines: AnsiLine[]) {
@@ -22,7 +22,7 @@ import DialogTitle from '@material-ui/core/DialogTitle';
import List from '@material-ui/core/List';
import Button from '@material-ui/core/Button';
import React, { useMemo, useState } from 'react';
import { useObservable } from 'react-use';
import useObservable from 'react-use/lib/useObservable';
import LoginRequestListItem from './LoginRequestListItem';
import { useApi, oauthRequestApiRef } from '@backstage/core-plugin-api';
@@ -18,7 +18,7 @@ import { makeStyles } from '@material-ui/core/styles';
import Tooltip, { TooltipProps } from '@material-ui/core/Tooltip';
import React, { useState } from 'react';
import TextTruncate, { TextTruncateProps } from 'react-text-truncate';
import { useMountedState } from 'react-use';
import useMountedState from 'react-use/lib/useMountedState';
type Props = {
text: TextTruncateProps['text'];
@@ -21,7 +21,7 @@ import { makeStyles } from '@material-ui/core/styles';
import NavigateBeforeIcon from '@material-ui/icons/NavigateBefore';
import NavigateNextIcon from '@material-ui/icons/NavigateNext';
import { chunkArray } from './utils';
import { useWindowSize } from 'react-use';
import useWindowSize from 'react-use/lib/useWindowSize';
/* Import Components */
@@ -18,7 +18,7 @@ import { isEqual } from 'lodash';
import qs from 'qs';
import { useEffect, useState } from 'react';
import { useSearchParams } from 'react-router-dom';
import { useDebounce } from 'react-use';
import useDebounce from 'react-use/lib/useDebounce';
function stringify(queryParams: any): string {
// Even though these setting don't look nice (e.g. escaped brackets), we should keep
@@ -21,7 +21,7 @@ import { makeStyles } from '@material-ui/core/styles';
import Typography from '@material-ui/core/Typography';
import CloseIcon from '@material-ui/icons/Close';
import React, { useContext, useState } from 'react';
import { useLocalStorage } from 'react-use';
import useLocalStorage from 'react-use/lib/useLocalStorage';
import {
sidebarConfig,
SidebarContext,
@@ -25,7 +25,7 @@ import Button from '@material-ui/core/Button';
import Grid from '@material-ui/core/Grid';
import Typography from '@material-ui/core/Typography';
import React, { useState } from 'react';
import { useMount } from 'react-use';
import useMount from 'react-use/lib/useMount';
import { Progress } from '../../components/Progress';
import { Content } from '../Content/Content';
import { ContentHeader } from '../ContentHeader/ContentHeader';
+6
View File
@@ -1,5 +1,11 @@
# @backstage/core-plugin-api
## 0.4.1
### Patch Changes
- c534ef2242: Deprecated `OldIconComponent`. Existing usage should be replaced with `IconComponent`.
## 0.4.0
### Minor Changes
+1 -1
View File
@@ -623,7 +623,7 @@ export const oktaAuthApiRef: ApiRef<
SessionApi
>;
// @public
// @public @deprecated
export type OldIconComponent = ComponentType<SvgIconProps>;
// @alpha
+3 -3
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/core-plugin-api",
"description": "Core API used by Backstage plugins",
"version": "0.4.0",
"version": "0.4.1",
"private": false,
"publishConfig": {
"access": "public",
@@ -45,8 +45,8 @@
"react": "^16.13.1 || ^17.0.0"
},
"devDependencies": {
"@backstage/cli": "^0.10.3",
"@backstage/core-app-api": "^0.3.0",
"@backstage/cli": "^0.10.5",
"@backstage/core-app-api": "^0.3.1",
"@backstage/test-utils": "^0.2.0",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^11.2.5",
@@ -43,5 +43,6 @@ export type IconComponent = ComponentType<{
* migration, but it is not exported.
*
* @public
* @deprecated This type will be removed, use {@link IconComponent} instead.
*/
export type OldIconComponent = ComponentType<SvgIconProps>;
+2
View File
@@ -1,5 +1,7 @@
# @backstage/create-app
## 0.4.11
## 0.4.10
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/create-app",
"description": "A CLI that helps you create your own Backstage app",
"version": "0.4.10",
"version": "0.4.11",
"private": false,
"publishConfig": {
"access": "public"
+11
View File
@@ -1,5 +1,16 @@
# @backstage/dev-utils
## 0.2.16
### Patch Changes
- 4ce51ab0f1: Internal refactor of the `react-use` imports to use `react-use/lib/*` instead.
- Updated dependencies
- @backstage/core-plugin-api@0.4.1
- @backstage/plugin-catalog-react@0.6.10
- @backstage/core-app-api@0.3.1
- @backstage/core-components@0.8.3
## 0.2.15
### Patch Changes
+6 -6
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/dev-utils",
"description": "Utilities for developing Backstage plugins.",
"version": "0.2.15",
"version": "0.2.16",
"private": false,
"publishConfig": {
"access": "public",
@@ -30,12 +30,12 @@
},
"dependencies": {
"@backstage/app-defaults": "^0.1.3",
"@backstage/core-app-api": "^0.3.0",
"@backstage/core-components": "^0.8.2",
"@backstage/core-plugin-api": "^0.4.0",
"@backstage/core-app-api": "^0.3.1",
"@backstage/core-components": "^0.8.3",
"@backstage/core-plugin-api": "^0.4.1",
"@backstage/catalog-model": "^0.9.7",
"@backstage/integration-react": "^0.1.16",
"@backstage/plugin-catalog-react": "^0.6.8",
"@backstage/plugin-catalog-react": "^0.6.10",
"@backstage/test-utils": "^0.2.0",
"@backstage/theme": "^0.2.14",
"@material-ui/core": "^4.12.2",
@@ -55,7 +55,7 @@
"react-dom": "^16.13.1 || ^17.0.0"
},
"devDependencies": {
"@backstage/cli": "^0.10.3",
"@backstage/cli": "^0.10.5",
"@types/jest": "^26.0.7",
"@types/node": "^14.14.32"
},
@@ -18,7 +18,7 @@ import { appThemeApiRef, useApi } from '@backstage/core-plugin-api';
import { ListItemIcon, ListItemText, Menu, MenuItem } from '@material-ui/core';
import AutoIcon from '@material-ui/icons/BrightnessAuto';
import React, { cloneElement, useCallback, useState } from 'react';
import { useObservable } from 'react-use';
import useObservable from 'react-use/lib/useObservable';
type ThemeIconProps = {
active?: boolean;
+40
View File
@@ -8,10 +8,13 @@ import { AnalyticsEvent } from '@backstage/core-plugin-api';
import { ApiHolder } from '@backstage/core-plugin-api';
import { ApiRef } from '@backstage/core-plugin-api';
import { ComponentType } from 'react';
import { Config } from '@backstage/config';
import { ConfigApi } from '@backstage/core-plugin-api';
import { ErrorApi } from '@backstage/core-plugin-api';
import { ErrorApiError } from '@backstage/core-plugin-api';
import { ErrorApiErrorContext } from '@backstage/core-plugin-api';
import { ExternalRouteRef } from '@backstage/core-plugin-api';
import { JsonObject } from '@backstage/types';
import { JsonValue } from '@backstage/types';
import { Observable } from '@backstage/types';
import { ReactElement } from 'react';
@@ -52,6 +55,43 @@ export class MockAnalyticsApi implements AnalyticsApi {
// @public
export function mockBreakpoint(options: { matches: boolean }): void;
// @public
export class MockConfigApi implements ConfigApi {
constructor(data: JsonObject);
// (undocumented)
get<T = JsonValue>(key?: string): T;
// (undocumented)
getBoolean(key: string): boolean;
// (undocumented)
getConfig(key: string): Config;
// (undocumented)
getConfigArray(key: string): Config[];
// (undocumented)
getNumber(key: string): number;
// (undocumented)
getOptional<T = JsonValue>(key?: string): T | undefined;
// (undocumented)
getOptionalBoolean(key: string): boolean | undefined;
// (undocumented)
getOptionalConfig(key: string): Config | undefined;
// (undocumented)
getOptionalConfigArray(key: string): Config[] | undefined;
// (undocumented)
getOptionalNumber(key: string): number | undefined;
// (undocumented)
getOptionalString(key: string): string | undefined;
// (undocumented)
getOptionalStringArray(key: string): string[] | undefined;
// (undocumented)
getString(key: string): string;
// (undocumented)
getStringArray(key: string): string[];
// (undocumented)
has(key: string): boolean;
// (undocumented)
keys(): string[];
}
// @public
export class MockErrorApi implements ErrorApi {
constructor(options?: MockErrorApiOptions);
+1
View File
@@ -29,6 +29,7 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/config": "^0.1.11",
"@backstage/core-app-api": "^0.3.0",
"@backstage/core-plugin-api": "^0.4.0",
"@backstage/theme": "^0.2.14",
@@ -0,0 +1,42 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { MockConfigApi } from './MockConfigApi';
describe('MockConfigApi', () => {
it('is able to read some basic config', () => {
const mock = new MockConfigApi({
app: {
title: 'Hello',
},
x: 1,
y: false,
z: [{ a: 3 }],
});
expect(mock.getString('app.title')).toEqual('Hello');
expect(mock.getNumber('x')).toEqual(1);
expect(mock.getBoolean('y')).toEqual(false);
expect(mock.getConfigArray('z')[0].getOptionalNumber('a')).toEqual(3);
expect(() => mock.getString('x')).toThrow(
"Invalid type in config for key 'x' in 'mock-config', got number, wanted string",
);
expect(() => mock.getString('missing')).toThrow(
"Missing required config value at 'missing'",
);
});
});
@@ -0,0 +1,111 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Config, ConfigReader } from '@backstage/config';
import { JsonObject, JsonValue } from '@backstage/types';
import { ConfigApi } from '@backstage/core-plugin-api';
/**
* MockConfigApi is a thin wrapper around {@link @backstage/config#ConfigReader}
* that can be used to mock configuration using a plain object.
*
* @public
* @example
* ```tsx
* const mockConfig = new MockConfigApi({
* app: { baseUrl: 'https://example.com' },
* });
*
* const rendered = await renderInTestApp(
* <TestApiProvider apis={[[configApiRef, mockConfig]]}>
* <MyTestedComponent />
* </TestApiProvider>,
* );
* ```
*/
export class MockConfigApi implements ConfigApi {
readonly #config: ConfigReader;
// NOTE: not extending in order to avoid inheriting the static `.fromConfigs`
constructor(data: JsonObject) {
this.#config = new ConfigReader(data);
}
/** {@inheritdoc @backstage/config#Config.has} */
has(key: string): boolean {
return this.#config.has(key);
}
/** {@inheritdoc @backstage/config#Config.keys} */
keys(): string[] {
return this.#config.keys();
}
/** {@inheritdoc @backstage/config#Config.get} */
get<T = JsonValue>(key?: string): T {
return this.#config.get(key);
}
/** {@inheritdoc @backstage/config#Config.getOptional} */
getOptional<T = JsonValue>(key?: string): T | undefined {
return this.#config.getOptional(key);
}
/** {@inheritdoc @backstage/config#Config.getConfig} */
getConfig(key: string): Config {
return this.#config.getConfig(key);
}
/** {@inheritdoc @backstage/config#Config.getOptionalConfig} */
getOptionalConfig(key: string): Config | undefined {
return this.#config.getOptionalConfig(key);
}
/** {@inheritdoc @backstage/config#Config.getConfigArray} */
getConfigArray(key: string): Config[] {
return this.#config.getConfigArray(key);
}
/** {@inheritdoc @backstage/config#Config.getOptionalConfigArray} */
getOptionalConfigArray(key: string): Config[] | undefined {
return this.#config.getOptionalConfigArray(key);
}
/** {@inheritdoc @backstage/config#Config.getNumber} */
getNumber(key: string): number {
return this.#config.getNumber(key);
}
/** {@inheritdoc @backstage/config#Config.getOptionalNumber} */
getOptionalNumber(key: string): number | undefined {
return this.#config.getOptionalNumber(key);
}
/** {@inheritdoc @backstage/config#Config.getBoolean} */
getBoolean(key: string): boolean {
return this.#config.getBoolean(key);
}
/** {@inheritdoc @backstage/config#Config.getOptionalBoolean} */
getOptionalBoolean(key: string): boolean | undefined {
return this.#config.getOptionalBoolean(key);
}
/** {@inheritdoc @backstage/config#Config.getString} */
getString(key: string): string {
return this.#config.getString(key);
}
/** {@inheritdoc @backstage/config#Config.getOptionalString} */
getOptionalString(key: string): string | undefined {
return this.#config.getOptionalString(key);
}
/** {@inheritdoc @backstage/config#Config.getStringArray} */
getStringArray(key: string): string[] {
return this.#config.getStringArray(key);
}
/** {@inheritdoc @backstage/config#Config.getOptionalStringArray} */
getOptionalStringArray(key: string): string[] | undefined {
return this.#config.getOptionalStringArray(key);
}
}
@@ -0,0 +1,17 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { MockConfigApi } from './MockConfigApi';
@@ -15,5 +15,6 @@
*/
export * from './AnalyticsApi';
export * from './ConfigApi';
export * from './ErrorApi';
export * from './StorageApi';
+10
View File
@@ -1,5 +1,15 @@
# @backstage/plugin-allure
## 0.1.10
### Patch Changes
- 4ce51ab0f1: Internal refactor of the `react-use` imports to use `react-use/lib/*` instead.
- Updated dependencies
- @backstage/core-plugin-api@0.4.1
- @backstage/plugin-catalog-react@0.6.10
- @backstage/core-components@0.8.3
## 0.1.9
### Patch Changes
+7 -7
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/plugin-allure",
"description": "A Backstage plugin that integrates with Allure",
"version": "0.1.9",
"version": "0.1.10",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -23,9 +23,9 @@
},
"dependencies": {
"@backstage/catalog-model": "^0.9.7",
"@backstage/core-components": "^0.8.2",
"@backstage/core-plugin-api": "^0.4.0",
"@backstage/plugin-catalog-react": "^0.6.8",
"@backstage/core-components": "^0.8.3",
"@backstage/core-plugin-api": "^0.4.1",
"@backstage/plugin-catalog-react": "^0.6.10",
"@backstage/theme": "^0.2.14",
"@material-ui/core": "^4.12.2",
"@material-ui/icons": "^4.9.1",
@@ -37,9 +37,9 @@
"react": "^16.13.1 || ^17.0.0"
},
"devDependencies": {
"@backstage/cli": "^0.10.4",
"@backstage/core-app-api": "^0.3.0",
"@backstage/dev-utils": "^0.2.15",
"@backstage/cli": "^0.10.5",
"@backstage/core-app-api": "^0.3.1",
"@backstage/dev-utils": "^0.2.16",
"@backstage/test-utils": "^0.2.1",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^11.2.5",
@@ -26,7 +26,7 @@ import {
MissingAnnotationEmptyState,
Progress,
} from '@backstage/core-components';
import { useAsync } from 'react-use';
import useAsync from 'react-use/lib/useAsync';
import { Entity } from '@backstage/catalog-model';
const AllureReport = (props: { entity: Entity }) => {
+5 -5
View File
@@ -22,8 +22,8 @@
},
"dependencies": {
"@backstage/config": "^0.1.5",
"@backstage/core-components": "^0.8.2",
"@backstage/core-plugin-api": "^0.4.0",
"@backstage/core-components": "^0.8.3",
"@backstage/core-plugin-api": "^0.4.1",
"@backstage/theme": "^0.2.14",
"@material-ui/core": "^4.12.2",
"@material-ui/icons": "^4.9.1",
@@ -35,9 +35,9 @@
"react": "^16.13.1 || ^17.0.0"
},
"devDependencies": {
"@backstage/cli": "^0.10.4",
"@backstage/core-app-api": "^0.3.0",
"@backstage/dev-utils": "^0.2.15",
"@backstage/cli": "^0.10.5",
"@backstage/core-app-api": "^0.3.1",
"@backstage/dev-utils": "^0.2.16",
"@backstage/test-utils": "^0.2.1",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^11.2.5",
+9
View File
@@ -1,5 +1,14 @@
# @backstage/plugin-apache-airflow
## 0.1.2
### Patch Changes
- 4ce51ab0f1: Internal refactor of the `react-use` imports to use `react-use/lib/*` instead.
- Updated dependencies
- @backstage/core-plugin-api@0.4.1
- @backstage/core-components@0.8.3
## 0.1.1
### Patch Changes
+6 -6
View File
@@ -1,6 +1,6 @@
{
"name": "@backstage/plugin-apache-airflow",
"version": "0.1.1",
"version": "0.1.2",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -20,8 +20,8 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/core-components": "^0.8.2",
"@backstage/core-plugin-api": "^0.4.0",
"@backstage/core-components": "^0.8.3",
"@backstage/core-plugin-api": "^0.4.1",
"@material-ui/core": "^4.12.2",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "4.0.0-alpha.57",
@@ -33,9 +33,9 @@
"react": "^16.13.1 || ^17.0.0"
},
"devDependencies": {
"@backstage/cli": "^0.10.4",
"@backstage/core-app-api": "^0.3.0",
"@backstage/dev-utils": "^0.2.15",
"@backstage/cli": "^0.10.5",
"@backstage/core-app-api": "^0.3.1",
"@backstage/dev-utils": "^0.2.16",
"@backstage/test-utils": "^0.2.1",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^11.2.5",
@@ -31,7 +31,7 @@ import Typography from '@material-ui/core/Typography';
import OpenInBrowserIcon from '@material-ui/icons/OpenInBrowser';
import Alert from '@material-ui/lab/Alert';
import React from 'react';
import { useAsync } from 'react-use';
import useAsync from 'react-use/lib/useAsync';
import { apacheAirflowApiRef } from '../../api';
import { Dag } from '../../api/types';
import { ScheduleIntervalLabel } from '../ScheduleIntervalLabel';
@@ -22,7 +22,7 @@ import {
import { useApi } from '@backstage/core-plugin-api';
import Alert from '@material-ui/lab/Alert';
import React from 'react';
import { useAsync } from 'react-use';
import useAsync from 'react-use/lib/useAsync';
import { apacheAirflowApiRef } from '../../api';
import { InstanceStatus } from '../../api/types';
@@ -22,7 +22,7 @@ import {
import { useApi } from '@backstage/core-plugin-api';
import Alert from '@material-ui/lab/Alert';
import React from 'react';
import { useAsync } from 'react-use';
import useAsync from 'react-use/lib/useAsync';
import { apacheAirflowApiRef } from '../../api';
import { InstanceVersion } from '../../api/types';
+11
View File
@@ -1,5 +1,16 @@
# @backstage/plugin-api-docs
## 0.6.21
### Patch Changes
- 11b81683a9: Support customizing index page layouts via outlets
- Updated dependencies
- @backstage/core-plugin-api@0.4.1
- @backstage/plugin-catalog-react@0.6.10
- @backstage/core-components@0.8.3
- @backstage/plugin-catalog@0.7.7
## 0.6.20
### Patch Changes
+8 -8
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/plugin-api-docs",
"description": "A Backstage plugin that helps represent API entities in the frontend",
"version": "0.6.20",
"version": "0.6.21",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -32,10 +32,10 @@
"dependencies": {
"@asyncapi/react-component": "^1.0.0-next.25",
"@backstage/catalog-model": "^0.9.7",
"@backstage/core-components": "^0.8.2",
"@backstage/core-plugin-api": "^0.4.0",
"@backstage/plugin-catalog": "^0.7.6",
"@backstage/plugin-catalog-react": "^0.6.9",
"@backstage/core-components": "^0.8.3",
"@backstage/core-plugin-api": "^0.4.1",
"@backstage/plugin-catalog": "^0.7.7",
"@backstage/plugin-catalog-react": "^0.6.10",
"@backstage/theme": "^0.2.14",
"@material-ui/core": "^4.12.2",
"@material-ui/icons": "^4.9.1",
@@ -54,9 +54,9 @@
"react": "^16.13.1 || ^17.0.0"
},
"devDependencies": {
"@backstage/cli": "^0.10.4",
"@backstage/core-app-api": "^0.3.0",
"@backstage/dev-utils": "^0.2.15",
"@backstage/cli": "^0.10.5",
"@backstage/core-app-api": "^0.3.1",
"@backstage/dev-utils": "^0.2.16",
"@backstage/test-utils": "^0.2.1",
"@testing-library/jest-dom": "^5.10.1",
"@testing-library/react": "^11.2.5",
+8
View File
@@ -1,5 +1,13 @@
# @backstage/plugin-auth-backend
## 0.6.1
### Patch Changes
- e0e57817d2: Added Google Cloud Identity-Aware Proxy as an identity provider.
- Updated dependencies
- @backstage/backend-common@0.10.2
## 0.6.0
### Minor Changes
+32 -6
View File
@@ -9,6 +9,7 @@ import { CatalogApi } from '@backstage/catalog-client';
import { Config } from '@backstage/config';
import { Entity } from '@backstage/catalog-model';
import express from 'express';
import { JsonValue } from '@backstage/types';
import { JSONWebKey } from 'jose';
import { Logger as Logger_2 } from 'winston';
import { PluginDatabaseManager } from '@backstage/backend-common';
@@ -59,8 +60,8 @@ export type Auth0ProviderOptions = {
};
// @public
export type AuthHandler<AuthResult> = (
input: AuthResult,
export type AuthHandler<TAuthResult> = (
input: TAuthResult,
) => Promise<AuthHandlerResult>;
// @public
@@ -249,6 +250,11 @@ export const createBitbucketProvider: (
options?: BitbucketProviderOptions | undefined,
) => AuthProviderFactory;
// @public
export function createGcpIapProvider(
options: GcpIapProviderOptions,
): AuthProviderFactory;
// Warning: (ae-missing-release-tag) "createGithubProvider" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
// @public (undocumented)
@@ -335,6 +341,26 @@ export const encodeState: (state: OAuthState) => string;
// @public (undocumented)
export const ensuresXRequestedWith: (req: express.Request) => boolean;
// @public
export type GcpIapProviderOptions = {
authHandler?: AuthHandler<GcpIapResult>;
signIn: {
resolver: SignInResolver<GcpIapResult>;
};
};
// @public
export type GcpIapResult = {
iapToken: GcpIapTokenInfo;
};
// @public
export type GcpIapTokenInfo = {
sub: string;
email: string;
[key: string]: JsonValue;
};
// Warning: (ae-forgotten-export) The symbol "TokenParams" needs to be exported by the entry point index.d.ts
// Warning: (ae-missing-release-tag) "getEntityClaims" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
//
@@ -661,14 +687,14 @@ export type SamlProviderOptions = {
};
// @public
export type SignInInfo<AuthResult> = {
export type SignInInfo<TAuthResult> = {
profile: ProfileInfo;
result: AuthResult;
result: TAuthResult;
};
// @public
export type SignInResolver<AuthResult> = (
info: SignInInfo<AuthResult>,
export type SignInResolver<TAuthResult> = (
info: SignInInfo<TAuthResult>,
context: {
tokenIssuer: TokenIssuer;
catalogIdentityClient: CatalogIdentityClient;
+7 -4
View File
@@ -1,7 +1,7 @@
{
"name": "@backstage/plugin-auth-backend",
"description": "A Backstage backend plugin that handles authentication",
"version": "0.6.0",
"version": "0.6.1",
"main": "src/index.ts",
"types": "src/index.ts",
"license": "Apache-2.0",
@@ -30,12 +30,13 @@
"clean": "backstage-cli clean"
},
"dependencies": {
"@backstage/backend-common": "^0.10.1",
"@backstage/backend-common": "^0.10.2",
"@backstage/catalog-client": "^0.5.3",
"@backstage/catalog-model": "^0.9.8",
"@backstage/config": "^0.1.11",
"@backstage/errors": "^0.1.5",
"@backstage/test-utils": "^0.2.1",
"@backstage/types": "^0.1.1",
"@google-cloud/firestore": "^4.15.1",
"@types/express": "^4.17.6",
"@types/passport": "^1.0.3",
@@ -46,6 +47,7 @@
"express-promise-router": "^4.1.0",
"express-session": "^1.17.1",
"fs-extra": "9.1.0",
"google-auth-library": "^7.6.1",
"helmet": "^4.0.0",
"jose": "^1.27.1",
"jwt-decode": "^3.1.0",
@@ -72,7 +74,7 @@
"yn": "^4.0.0"
},
"devDependencies": {
"@backstage/cli": "^0.10.4",
"@backstage/cli": "^0.10.5",
"@types/body-parser": "^1.19.0",
"@types/cookie-parser": "^1.4.2",
"@types/express-session": "^1.17.2",
@@ -83,7 +85,8 @@
"@types/passport-saml": "^1.1.3",
"@types/passport-strategy": "^0.2.35",
"@types/xml2js": "^0.4.7",
"msw": "^0.35.0"
"msw": "^0.35.0",
"supertest": "^6.1.3"
},
"files": [
"dist",
@@ -13,5 +13,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export { createAwsAlbProvider } from './provider';
export type { AwsAlbProviderOptions } from './provider';
@@ -18,7 +18,7 @@ import express from 'express';
import { JWT } from 'jose';
import {
ALB_ACCESSTOKEN_HEADER,
ALB_ACCESS_TOKEN_HEADER,
ALB_JWT_HEADER,
AwsAlbAuthProvider,
} from './provider';
@@ -80,7 +80,7 @@ describe('AwsAlbAuthProvider', () => {
header: jest.fn(name => {
if (name === ALB_JWT_HEADER) {
return mockJwt;
} else if (name === ALB_ACCESSTOKEN_HEADER) {
} else if (name === ALB_ACCESS_TOKEN_HEADER) {
return mockAccessToken;
}
return undefined;
@@ -88,7 +88,7 @@ describe('AwsAlbAuthProvider', () => {
} as unknown as express.Request;
const mockRequestWithoutJwt = {
header: jest.fn(name => {
if (name === ALB_ACCESSTOKEN_HEADER) {
if (name === ALB_ACCESS_TOKEN_HEADER) {
return mockAccessToken;
}
return undefined;
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
AuthHandler,
AuthProviderFactory,
@@ -35,7 +36,7 @@ import { AuthenticationError } from '@backstage/errors';
import { prepareBackstageIdentityResponse } from '../prepareBackstageIdentityResponse';
export const ALB_JWT_HEADER = 'x-amzn-oidc-data';
export const ALB_ACCESSTOKEN_HEADER = 'x-amzn-oidc-accesstoken';
export const ALB_ACCESS_TOKEN_HEADER = 'x-amzn-oidc-accesstoken';
type Options = {
region: string;
@@ -134,7 +135,7 @@ export class AwsAlbAuthProvider implements AuthProviderRouteHandlers {
private async getResult(req: express.Request): Promise<AwsAlbResult> {
const jwt = req.header(ALB_JWT_HEADER);
const accessToken = req.header(ALB_ACCESSTOKEN_HEADER);
const accessToken = req.header(ALB_ACCESS_TOKEN_HEADER);
if (jwt === undefined) {
throw new AuthenticationError(
@@ -144,7 +145,7 @@ export class AwsAlbAuthProvider implements AuthProviderRouteHandlers {
if (accessToken === undefined) {
throw new AuthenticationError(
`Missing ALB OIDC header: ${ALB_ACCESSTOKEN_HEADER}`,
`Missing ALB OIDC header: ${ALB_ACCESS_TOKEN_HEADER}`,
);
}
@@ -0,0 +1,134 @@
/*
* Copyright 2021 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ConflictError } from '@backstage/errors';
import { OAuth2Client } from 'google-auth-library';
import { createTokenValidator, parseRequestToken } from './helpers';
const validJwt =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImZvbyIsImlzcyI6ImZvbyJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.T2BNS4G-6RoiFnXc8Q8TiwdWzTpNitY8jcsGM3N3-Yo';
beforeEach(() => {
jest.clearAllMocks();
});
describe('helpers', () => {
describe('createTokenValidator', () => {
it('runs the happy path', async () => {
const mockClient = {
getIapPublicKeys: async () => ({ pubkeys: '' }),
verifySignedJwtWithCertsAsync: async () => ({
getPayload: () => ({ sub: 's', email: 'e@mail.com' }),
}),
};
const validator = createTokenValidator(
'a',
mockClient as unknown as OAuth2Client,
);
await expect(validator(validJwt)).resolves.toMatchObject({
sub: 's',
email: 'e@mail.com',
});
});
it('throws if the client throws', async () => {
const mockClient = {
getIapPublicKeys: async () => {
throw new TypeError('bam');
},
};
const validator = createTokenValidator(
'a',
mockClient as unknown as OAuth2Client,
);
await expect(validator(validJwt)).rejects.toThrowError(TypeError);
});
it('rejects empty payload', async () => {
const mockClient = {
getIapPublicKeys: async () => ({ pubkeys: '' }),
verifySignedJwtWithCertsAsync: async () => ({
getPayload: () => undefined,
}),
};
const validator = createTokenValidator(
'a',
mockClient as unknown as OAuth2Client,
);
await expect(validator(validJwt)).rejects.toMatchObject({
name: 'TypeError',
message: 'Token had no payload',
});
});
});
describe('parseRequestToken', () => {
it('runs the happy path', async () => {
await expect(
parseRequestToken(
validJwt,
async () => ({ sub: 's', email: 'e@mail.com' } as any),
),
).resolves.toMatchObject({
iapToken: {
sub: 's',
email: 'e@mail.com',
},
});
});
it('rejects bad tokens', async () => {
await expect(
parseRequestToken(7, undefined as any),
).rejects.toMatchObject({
name: 'AuthenticationError',
message: 'Missing Google IAP header: x-goog-iap-jwt-assertion',
});
await expect(
parseRequestToken(undefined, undefined as any),
).rejects.toMatchObject({
name: 'AuthenticationError',
message: 'Missing Google IAP header: x-goog-iap-jwt-assertion',
});
await expect(
parseRequestToken('', undefined as any),
).rejects.toMatchObject({
name: 'AuthenticationError',
message: 'Missing Google IAP header: x-goog-iap-jwt-assertion',
});
});
it('translates validator errors', async () => {
await expect(
parseRequestToken(validJwt, async () => {
throw new ConflictError('Ouch');
}),
).rejects.toMatchObject({
name: 'AuthenticationError',
message: 'Google IAP token verification failed, ConflictError: Ouch',
});
});
it('rejects bad token payloads', async () => {
await expect(
parseRequestToken(validJwt, async () => ({ sub: 'a' } as any)),
).rejects.toMatchObject({
name: 'AuthenticationError',
message: 'Google IAP token payload is missing sub and/or email claim',
});
});
});
});

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