Merge branch 'master' of github.com:backstage/backstage into seant-splunk/awsS3_readTree_processor
This commit is contained in:
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder': patch
|
||||
---
|
||||
|
||||
Allow to pass custom TemplateCard to ScaffolderPage
|
||||
@@ -1,6 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-react': patch
|
||||
---
|
||||
|
||||
Added a `useEntityKinds` hook to load a unique list of entity kinds from the catalog.
|
||||
Fixed a bug in `EntityTypePicker` where the component did not hide when no types were available in returned entities.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend': patch
|
||||
---
|
||||
|
||||
GitHub discovery processor passes over repositories that do not have a default branch
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder-backend': patch
|
||||
---
|
||||
|
||||
GitHub create repository webhook action: `github:webhook` for Backstage plugin Scaffolder has been added.
|
||||
@@ -0,0 +1,23 @@
|
||||
---
|
||||
'@backstage/create-app': patch
|
||||
---
|
||||
|
||||
Updated the search configuration class to use the static `fromConfig`-based constructor for the `DefaultCatalogCollator`.
|
||||
|
||||
To apply this change to an existing app, replace the following line in `search.ts`:
|
||||
|
||||
```diff
|
||||
-collator: new DefaultCatalogCollator({ discovery })
|
||||
+collator: DefaultCatalogCollator.fromConfig(config, { discovery })
|
||||
```
|
||||
|
||||
The `config` parameter was not needed before, so make sure you also add that in the signature of `createPlugin`
|
||||
in `search.ts`:
|
||||
|
||||
```diff
|
||||
export default async function createPlugin({
|
||||
logger,
|
||||
discovery,
|
||||
+ config,
|
||||
}: PluginEnvironment) {
|
||||
```
|
||||
@@ -1,9 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-techdocs': patch
|
||||
'@backstage/plugin-techdocs-backend': patch
|
||||
'@backstage/techdocs-common': patch
|
||||
---
|
||||
|
||||
Adding in-context search to TechDocs Reader component. Using existing search-backend to query for indexed search results scoped into a specific entity's techdocs. Needs TechDocsCollator enabled on the backend to work.
|
||||
|
||||
Adding extra information to indexed tech docs documents for search.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend': patch
|
||||
---
|
||||
|
||||
Adds optional namespacing for users in the GitHub Multi Org Plugin
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-catalog-backend': patch
|
||||
---
|
||||
|
||||
Allow the catalog search collator to filter the entities that it indexes
|
||||
@@ -1,7 +0,0 @@
|
||||
---
|
||||
'@backstage/core-components': patch
|
||||
'@backstage/plugin-catalog-import': patch
|
||||
'@backstage/plugin-shortcuts': patch
|
||||
---
|
||||
|
||||
Bumped `react-hook-form` to `^7.12.2`
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-git-release-manager': patch
|
||||
---
|
||||
|
||||
Wrap each feature in custom feature arrays with an element containing a 'key' to avoid missing-key-warnings
|
||||
@@ -1,10 +0,0 @@
|
||||
---
|
||||
'@backstage/core-components': minor
|
||||
---
|
||||
|
||||
Changed the `titleComponent` prop on `ContentHeader` to accept `ReactNode` instead of a React `ComponentType`. Usages of this prop should be converted from passing a component to passing in the rendered element:
|
||||
|
||||
```diff
|
||||
-<ContentHeader titleComponent={MyComponent}>
|
||||
+<ContentHeader titleComponent={<MyComponent />}>
|
||||
```
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-org': patch
|
||||
---
|
||||
|
||||
Don't open a new tab when clicking on the ownership card.
|
||||
@@ -1,7 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-catalog': patch
|
||||
---
|
||||
|
||||
Update the `AboutCard` to properly support non-standard entity types and rework the defaults for the build-in kinds.
|
||||
|
||||
This change also uses `useElementFilter(...)` instead of `React.children.count(...)` in `AboutField` to properly recognize whether children are available.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-auth-backend': patch
|
||||
---
|
||||
|
||||
typo fix `tenentId` in Azure auth provider docs
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-catalog': patch
|
||||
---
|
||||
|
||||
Added the ability to switch entity kind on the catalog index page. This is a non-breaking change, but if you created a custom `CatalogPage` and wish to use this feature, make the modifications shown on [#6895](https://github.com/backstage/backstage/pull/6895).
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-shortcuts': patch
|
||||
---
|
||||
|
||||
Fix material-ui warning about deprecated `onExit` prop
|
||||
@@ -1,11 +0,0 @@
|
||||
---
|
||||
'@backstage/catalog-model': patch
|
||||
---
|
||||
|
||||
Add an optional `metadata.title` field to all entity kinds.
|
||||
|
||||
This used to be available on only the `Template` kind, and we have decided that the metadata block should be the same for all kinds. A title can be useful especially in large and complex catalogs where users have a tough time navigating or discerning among the entities.
|
||||
|
||||
It also carries some risk. You do not want to end up giving a title that collides with an actual name, which at best leads to confusion and at worst could be a liability. We do not perform any collision detection in the catalog. If you want to disallow this facility you may want to add a small processor that makes sure it's not set.
|
||||
|
||||
At the time of writing this message, only the scaffolder actually makes use of this field for display purposes.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/create-app': patch
|
||||
---
|
||||
|
||||
Minor tweaks to the create-app template to match earlier documented changes
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/backend-common': patch
|
||||
---
|
||||
|
||||
Export type that are needed to implement a new `UrlReader`
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-sonarqube': patch
|
||||
---
|
||||
|
||||
Update README to show a simpler proxy configuration.
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/techdocs-common': patch
|
||||
---
|
||||
|
||||
Force using `posix` path for cloud storage
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-catalog': patch
|
||||
---
|
||||
|
||||
Use a `Link` for the edit button on the `AboutCard` instead of doing `window.open(...)`
|
||||
@@ -1,5 +0,0 @@
|
||||
---
|
||||
'@backstage/plugin-org': patch
|
||||
---
|
||||
|
||||
Fix OwnershipCard links to support namespaced owners
|
||||
@@ -133,6 +133,9 @@ kafka:
|
||||
brokers:
|
||||
- localhost:9092
|
||||
|
||||
allure:
|
||||
baseUrl: http://localhost:5050/allure-docker-service
|
||||
|
||||
integrations:
|
||||
github:
|
||||
- host: github.com
|
||||
|
||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 17 KiB |
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 22 KiB |
@@ -547,8 +547,8 @@ The current set of well-known and common values for this field is:
|
||||
|
||||
### `spec.owner` [required]
|
||||
|
||||
An [entity reference](#string-references) to the owner of the component, e.g.
|
||||
`artist-relations-team`. This field is required.
|
||||
An [entity reference](references.md#string-references) to the owner of the
|
||||
component, e.g. `artist-relations-team`. This field is required.
|
||||
|
||||
In Backstage, the owner of a component is the singular entity (commonly a team)
|
||||
that bears ultimate responsibility for the component, and has the authority and
|
||||
@@ -566,8 +566,8 @@ component, but there will always be one ultimate owner.
|
||||
|
||||
### `spec.system` [optional]
|
||||
|
||||
An [entity reference](#string-references) to the system that the component
|
||||
belongs to, e.g. `artist-engagement-portal`. This field is optional.
|
||||
An [entity reference](references.md#string-references) to the system that the
|
||||
component belongs to, e.g. `artist-engagement-portal`. This field is optional.
|
||||
|
||||
| [`kind`](#apiversion-and-kind-required) | Default [`namespace`](#namespace-optional) | Generated [relation](well-known-relations.md) type |
|
||||
| --------------------------------------- | ------------------------------------------ | ----------------------------------------------------------------------------- |
|
||||
@@ -575,8 +575,8 @@ belongs to, e.g. `artist-engagement-portal`. This field is optional.
|
||||
|
||||
### `spec.subcomponentOf` [optional]
|
||||
|
||||
An [entity reference](#string-references) to another component of which the
|
||||
component is a part, e.g. `spotify-ios-app`. This field is optional.
|
||||
An [entity reference](references.md#string-references) to another component of
|
||||
which the component is a part, e.g. `spotify-ios-app`. This field is optional.
|
||||
|
||||
| [`kind`](#apiversion-and-kind-required) | Default [`namespace`](#namespace-optional) | Generated [relation](well-known-relations.md) type |
|
||||
| ---------------------------------------- | ------------------------------------------ | ----------------------------------------------------------------------------- |
|
||||
@@ -584,8 +584,8 @@ component is a part, e.g. `spotify-ios-app`. This field is optional.
|
||||
|
||||
### `spec.providesApis` [optional]
|
||||
|
||||
An array of [entity references](#string-references) to the APIs that are
|
||||
provided by the component, e.g. `artist-api`. This field is optional.
|
||||
An array of [entity references](references.md#string-references) to the APIs
|
||||
that are provided by the component, e.g. `artist-api`. This field is optional.
|
||||
|
||||
| [`kind`](#apiversion-and-kind-required) | Default [`namespace`](#namespace-optional) | Generated [relation](well-known-relations.md) type |
|
||||
| --------------------------------------- | ------------------------------------------ | --------------------------------------------------------------------------------------------------- |
|
||||
@@ -593,8 +593,8 @@ provided by the component, e.g. `artist-api`. This field is optional.
|
||||
|
||||
### `spec.consumesApis` [optional]
|
||||
|
||||
An array of [entity references](#string-references) to the APIs that are
|
||||
consumed by the component, e.g. `artist-api`. This field is optional.
|
||||
An array of [entity references](references.md#string-references) to the APIs
|
||||
that are consumed by the component, e.g. `artist-api`. This field is optional.
|
||||
|
||||
| [`kind`](#apiversion-and-kind-required) | Default [`namespace`](#namespace-optional) | Generated [relation](well-known-relations.md) type |
|
||||
| --------------------------------------- | ------------------------------------------ | --------------------------------------------------------------------------------------------------- |
|
||||
@@ -602,9 +602,9 @@ consumed by the component, e.g. `artist-api`. This field is optional.
|
||||
|
||||
### `spec.dependsOn` [optional]
|
||||
|
||||
An array of [entity references](#string-references) to the components and
|
||||
resources that the component depends on, e.g. `artists-db`. This field is
|
||||
optional.
|
||||
An array of [entity references](references.md#string-references) to the
|
||||
components and resources that the component depends on, e.g. `artists-db`. This
|
||||
field is optional.
|
||||
|
||||
| [`kind`](#apiversion-and-kind-required) | Default [`namespace`](#namespace-optional) | Generated [relation](well-known-relations.md) type |
|
||||
| --------------------------------------- | ------------------------------------------ | --------------------------------------------------------------------------------------------- |
|
||||
@@ -732,8 +732,8 @@ You can find out more about the `steps` key
|
||||
|
||||
### `spec.owner` [optional]
|
||||
|
||||
An [entity reference](#string-references) to the owner of the template, e.g.
|
||||
`artist-relations-team`. This field is required.
|
||||
An [entity reference](references.md#string-references) to the owner of the
|
||||
template, e.g. `artist-relations-team`. This field is required.
|
||||
|
||||
In Backstage, the owner of a Template is the singular entity (commonly a team)
|
||||
that bears ultimate responsibility for the Template, and has the authority and
|
||||
@@ -842,8 +842,8 @@ The current set of well-known and common values for this field is:
|
||||
|
||||
### `spec.owner` [required]
|
||||
|
||||
An [entity reference](#string-references) to the owner of the component, e.g.
|
||||
`artist-relations-team`. This field is required.
|
||||
An [entity reference](references.md#string-references) to the owner of the
|
||||
component, e.g. `artist-relations-team`. This field is required.
|
||||
|
||||
In Backstage, the owner of an API is the singular entity (commonly a team) that
|
||||
bears ultimate responsibility for the API, and has the authority and capability
|
||||
@@ -861,8 +861,8 @@ one ultimate owner.
|
||||
|
||||
### `spec.system` [optional]
|
||||
|
||||
An [entity reference](#string-references) to the system that the API belongs to,
|
||||
e.g. `artist-engagement-portal`. This field is optional.
|
||||
An [entity reference](references.md#string-references) to the system that the
|
||||
API belongs to, e.g. `artist-engagement-portal`. This field is optional.
|
||||
|
||||
| [`kind`](#apiversion-and-kind-required) | Default [`namespace`](#namespace-optional) | Generated [relation](well-known-relations.md) type |
|
||||
| --------------------------------------- | ------------------------------------------ | ----------------------------------------------------------------------------- |
|
||||
@@ -1069,8 +1069,8 @@ Exactly equal to `backstage.io/v1alpha1` and `Resource`, respectively.
|
||||
|
||||
### `spec.owner` [required]
|
||||
|
||||
An [entity reference](#string-references) to the owner of the resource, e.g.
|
||||
`artist-relations-team`. This field is required.
|
||||
An [entity reference](references.md#string-references) to the owner of the
|
||||
resource, e.g. `artist-relations-team`. This field is required.
|
||||
|
||||
In Backstage, the owner of a resource is the singular entity (commonly a team)
|
||||
that bears ultimate responsibility for the resource, and has the authority and
|
||||
@@ -1101,8 +1101,8 @@ Some common values for this field could be:
|
||||
|
||||
### `spec.system` [optional]
|
||||
|
||||
An [entity reference](#string-references) to the system that the resource
|
||||
belongs to, e.g. `artist-engagement-portal`. This field is optional.
|
||||
An [entity reference](references.md#string-references) to the system that the
|
||||
resource belongs to, e.g. `artist-engagement-portal`. This field is optional.
|
||||
|
||||
| [`kind`](#apiversion-and-kind-required) | Default [`namespace`](#namespace-optional) | Generated [relation](well-known-relations.md) type |
|
||||
| --------------------------------------- | ------------------------------------------ | ----------------------------------------------------------------------------- |
|
||||
@@ -1110,9 +1110,9 @@ belongs to, e.g. `artist-engagement-portal`. This field is optional.
|
||||
|
||||
### `spec.dependsOn` [optional]
|
||||
|
||||
An array of [entity references](#string-references) to the components and
|
||||
resources that the resource depends on, e.g. `artist-lookup`. This field is
|
||||
optional.
|
||||
An array of [entity references](references.md#string-references) to the
|
||||
components and resources that the resource depends on, e.g. `artist-lookup`.
|
||||
This field is optional.
|
||||
|
||||
| [`kind`](#apiversion-and-kind-required) | Default [`namespace`](#namespace-optional) | Generated [relation](well-known-relations.md) type |
|
||||
| --------------------------------------- | ------------------------------------------ | --------------------------------------------------------------------------------------------- |
|
||||
@@ -1156,8 +1156,8 @@ Exactly equal to `backstage.io/v1alpha1` and `System`, respectively.
|
||||
|
||||
### `spec.owner` [required]
|
||||
|
||||
An [entity reference](#string-references) to the owner of the system, e.g.
|
||||
`artist-relations-team`. This field is required.
|
||||
An [entity reference](references.md#string-references) to the owner of the
|
||||
system, e.g. `artist-relations-team`. This field is required.
|
||||
|
||||
In Backstage, the owner of a system is the singular entity (commonly a team)
|
||||
that bears ultimate responsibility for the system, and has the authority and
|
||||
@@ -1175,8 +1175,8 @@ but there will always be one ultimate owner.
|
||||
|
||||
### `spec.domain` [optional]
|
||||
|
||||
An [entity reference](#string-references) to the domain that the system belongs
|
||||
to, e.g. `artists`. This field is optional.
|
||||
An [entity reference](references.md#string-references) to the domain that the
|
||||
system belongs to, e.g. `artists`. This field is optional.
|
||||
|
||||
| [`kind`](#apiversion-and-kind-required) | Default [`namespace`](#namespace-optional) | Generated [relation](well-known-relations.md) type |
|
||||
| --------------------------------------- | ------------------------------------------ | ----------------------------------------------------------------------------- |
|
||||
@@ -1215,8 +1215,8 @@ Exactly equal to `backstage.io/v1alpha1` and `Domain`, respectively.
|
||||
|
||||
### `spec.owner` [required]
|
||||
|
||||
An [entity reference](#string-references) to the owner of the domain, e.g.
|
||||
`artist-relations-team`. This field is required.
|
||||
An [entity reference](references.md#string-references) to the owner of the
|
||||
domain, e.g. `artist-relations-team`. This field is required.
|
||||
|
||||
In Backstage, the owner of a domain is the singular entity (commonly a team)
|
||||
that bears ultimate responsibility for the domain, and has the authority and
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
---
|
||||
id: life-of-an-entity
|
||||
title: The Life of an Entity
|
||||
sidebar_label: The Life of an Entity
|
||||
# prettier-ignore
|
||||
description: The life cycle of entities, from being introduced into the catalog, through processing, to being removed again
|
||||
---
|
||||
|
||||
This document gives a high level overview of the catalog backend, and the
|
||||
technical processes involved in making entities flow through it. It is mainly
|
||||
aimed at developers who want to understand the internals while installing or
|
||||
extending the catalog. However, it can be informative for other personas too.
|
||||
|
||||
## Key Concepts
|
||||
|
||||
The catalog forms a hub of sorts, where entities are ingested from various
|
||||
authoritative sources and held in a database, subject to automated processing,
|
||||
and then presented through an API for quick and easy access by Backstage and
|
||||
others. The most common source is [YAML files](descriptor-format.md) on a
|
||||
standard format, living in version control systems near the source code of
|
||||
systems that they describe. Those files are registered with the catalog and
|
||||
maintained by the respective owners. The catalog makes sure to keep itself up to
|
||||
date with changes to those files.
|
||||
|
||||
The main extension points where developers can customize the catalog are:
|
||||
|
||||
- _Entity providers_, that feed initial raw entity data into the catalog,
|
||||
- _Policies_, that establish baseline rules about the shape of entities,
|
||||
- _Processors_, that validate, analyze, and mutate the raw entity data into its
|
||||
final form.
|
||||
|
||||
The high level processes involved are:
|
||||
|
||||
- _Ingestion_, where entity providers fetch raw entity data from external
|
||||
sources and seed it into the database,
|
||||
- _Processing_, where the policies and processors continually treat the ingested
|
||||
data and may emit both other raw entities (that are also subject to
|
||||
processing), errors, relations to other entities, etc.,
|
||||
- _Stitching_, where all of the data emitted by various processors are assembled
|
||||
together into the final output entity.
|
||||
|
||||
An entity is not visible to the outside world (through the catalog API), until
|
||||
it has passed through the last process and landed among the final entities.
|
||||
|
||||

|
||||
|
||||
The details of these processes are described below.
|
||||
|
||||
## Ingestion
|
||||
|
||||
Each catalog deployment has a number of entity providers installed. They are
|
||||
responsible for fetching data from external authoritative sources in any way
|
||||
that they see fit, to translate those into entity objects, and to notify the
|
||||
database when those entities are added or removed. These are the _unprocessed
|
||||
entities_ that will be subject to later processing (see below), and they form
|
||||
the very basis of existence for entities. If there were no entity providers, no
|
||||
entities would ever enter the system.
|
||||
|
||||
The database always keeps track of the set of entities that belong to each
|
||||
provider; no two providers can try to output the same entity. And when a
|
||||
provider signals the removal of an entity, then that leads to an _eager
|
||||
deletion_: the entity and all auxiliary data that it has led to in the database
|
||||
is immediately purged.
|
||||
|
||||

|
||||
|
||||
There are two providers installed by default: the one that deals with user
|
||||
registered locations (e.g. URLs to YAML files), and the one that deals with
|
||||
static locations in the app-config. You can add more third party providers by
|
||||
passing them to the catalog builder in your backend initialization code, and you
|
||||
can easily write your own.
|
||||
|
||||
An entity provider is a class that implements the `EntityProvider` interface. It
|
||||
has three main parts:
|
||||
|
||||
- The identity: Each provider instance has a unique, stable identifier that the
|
||||
database can use to keep track of the originator of each unprocessed entity.
|
||||
- The connection: During backend startup, each provider is attached to the
|
||||
catalog runtime.
|
||||
- The stream of events: During its lifetime, the provider can issue change
|
||||
events to the runtime at any point in time, to modify its set of unprocessed
|
||||
entities.
|
||||
|
||||
It is entirely up to the provider to choose how and when it produces these
|
||||
change events. For example, the app-config provider only fires off an update at
|
||||
startup and then lies dormant. The location database provider does an initial
|
||||
update at startup, and then small delta updates every time a location database
|
||||
change is detected. The LDAP provider is driven externally by a timer loop that
|
||||
occasionally triggers a full update. Some future provider may be entirely event
|
||||
driven, feeding off an event bus or web hook. There is no magic coordination
|
||||
among providers; if they need to arrange synchronization or locking among
|
||||
themselves for example to avoid duplicate work across multiple catalog service
|
||||
machines, they need to handle that out-of-band.
|
||||
|
||||
The entities that are emitted get some coarse validation applied to them, to
|
||||
ensure that they at least adhere to the most basic schema rules about how an
|
||||
entity should be shaped. For example, they need to have a `kind`, a
|
||||
`metadata.name`, and optionally a `metadata.namespace`, among others. Apart from
|
||||
that, the ingestion stage considers its work done, and stores the unprocessed
|
||||
entities to be picked up at a later time by the processing system. This means
|
||||
that the more precise validation rules that you put in place on entities are
|
||||
_not_ yet applied at this stage.
|
||||
|
||||
## Processing
|
||||
|
||||
Every unprocessed entity comes with a timestamp, which tells at what time that
|
||||
the processing loop should next try to process it. When the entity first
|
||||
appears, this timestamp is set to "now" - asking for it to be picked up as soon
|
||||
as possible.
|
||||
|
||||
Each catalog deployment has a number of processors installed. They are
|
||||
responsible for receiving unprocessed entities that the catalog decided are due
|
||||
for processing, and then running that data through a number of processing
|
||||
stages. mutating the entity and emitting auxiliary data about it. When all of
|
||||
that is done, the catalog takes all of that information and stores it as the
|
||||
processed entity, and errors and relations to other entities separately. Then,
|
||||
the catalog checks to see what entities are touched by that output, and triggers
|
||||
the final assembly of those (see Stitching below).
|
||||
|
||||
There are several stages involved in the processing.
|
||||
|
||||
> TODO: More info here
|
||||
|
||||
## Stitching
|
||||
|
||||
The stitching is currently a fixed process, that cannot be modified or extended.
|
||||
This means that any modifications you want to make on the final result, has to
|
||||
happen during ingestion or processing.
|
||||
|
||||
> TODO: More info here
|
||||
@@ -148,12 +148,18 @@ const AppRoutes = () => {
|
||||
> an external storage provider and external CI/CD is used). If you use the
|
||||
> "basic" or "out-of-the-box" setup, you can stop here! No action needed.
|
||||
|
||||
The beta version of TechDocs (v0.x.y) made a breaking change to the way TechDocs
|
||||
content was accessed and stored, allowing pages to be accessed with
|
||||
case-insensitive entity triplet paths (e.g. `/docs/namespace/kind/name` whereas
|
||||
in prior versions, they could only be accessed at `/docs/namespace/Kind/name`).
|
||||
In order to enable this change, documentation has to be stored in an external
|
||||
storage provider using an object key whose entity triplet is lower-cased.
|
||||
For the purposes of this guide, TechDocs Beta version is defined as:
|
||||
|
||||
- **TechDocs Plugin**: At least `v0.11.0`
|
||||
- **TechDocs Backend Plugin**: At least `v0.10.0`
|
||||
- **TechDocs CLI**: At least `v0.7.0`
|
||||
|
||||
The beta version of TechDocs made a breaking change to the way TechDocs content
|
||||
was accessed and stored, allowing pages to be accessed with case-insensitive
|
||||
entity triplet paths (e.g. `/docs/namespace/kind/name` whereas in prior
|
||||
versions, they could only be accessed at `/docs/namespace/Kind/name`). In order
|
||||
to enable this change, documentation has to be stored in an external storage
|
||||
provider using an object key whose entity triplet is lower-cased.
|
||||
|
||||
New installations of TechDocs since the beta version will work fine with no
|
||||
action, but for those who were running TechDocs prior to this version, a
|
||||
|
||||
@@ -128,7 +128,7 @@ const themeOptions = createThemeOptions({
|
||||
tool: genPageTheme(['#123456','#123456'], shapes.round),
|
||||
service: genPageTheme(['#123456','#123456'], shapes.wave),
|
||||
website: genPageTheme(['#123456','#123456'], shapes.wave),
|
||||
library: genPageTheme(['#123456','#123456'] shapes.wave),
|
||||
library: genPageTheme(['#123456','#123456'], shapes.wave),
|
||||
other: genPageTheme(['#123456','#123456'], shapes.wave),
|
||||
app: genPageTheme(['#123456','#123456'], shapes.wave),
|
||||
apis: genPageTheme(['#123456','#123456'], shapes.wave),
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
title: Allure Reports
|
||||
author: Deepak Bhardwaj
|
||||
authorUrl: https://github.com/deepak-bhardwaj-ps
|
||||
category: Reporting
|
||||
description: View Allure reports for your components in Backstage.
|
||||
documentation: https://github.com/backstage/backstage/tree/master/plugins/allure
|
||||
iconUrl: https://avatars.githubusercontent.com/u/5879127
|
||||
npmPackageName: '@backstage/plugin-allure'
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
title: Home
|
||||
author: Spotify
|
||||
authorUrl: https://github.com/spotify
|
||||
category: Discovery
|
||||
description: This plugin provides a composable home page, and ability to create home page components
|
||||
documentation: https://github.com/backstage/backstage/blob/master/plugins/home/README.md
|
||||
iconUrl: img/home.png
|
||||
npmPackageName: '@backstage/plugin-home'
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
@@ -33,6 +33,7 @@ nav:
|
||||
- Core Features:
|
||||
- Software Catalog:
|
||||
- Overview: 'features/software-catalog/index.md'
|
||||
- The Life of an Entity: 'features/software-catalog/life-of-an-entity.md'
|
||||
- Catalog Configuration: 'features/software-catalog/configuration.md'
|
||||
- System Model: 'features/software-catalog/system-model.md'
|
||||
- YAML File Format: 'features/software-catalog/descriptor-format.md'
|
||||
|
||||
@@ -1,5 +1,47 @@
|
||||
# example-app
|
||||
|
||||
## 0.2.44
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/plugin-scaffolder@0.11.2
|
||||
- @backstage/cli@0.7.10
|
||||
- @backstage/plugin-catalog-react@0.4.5
|
||||
- @backstage/plugin-techdocs@0.11.1
|
||||
- @backstage/core-components@0.4.0
|
||||
- @backstage/plugin-catalog-import@0.5.20
|
||||
- @backstage/plugin-shortcuts@0.1.7
|
||||
- @backstage/plugin-org@0.3.21
|
||||
- @backstage/plugin-catalog@0.6.14
|
||||
- @backstage/search-common@0.2.0
|
||||
- @backstage/plugin-search@0.4.9
|
||||
- @backstage/catalog-model@0.9.1
|
||||
- @backstage/core-app-api@0.1.11
|
||||
- @backstage/integration-react@0.1.8
|
||||
- @backstage/plugin-api-docs@0.6.7
|
||||
- @backstage/plugin-badges@0.2.8
|
||||
- @backstage/plugin-circleci@0.2.22
|
||||
- @backstage/plugin-cloudbuild@0.2.22
|
||||
- @backstage/plugin-code-coverage@0.1.10
|
||||
- @backstage/plugin-cost-insights@0.11.5
|
||||
- @backstage/plugin-explore@0.3.15
|
||||
- @backstage/plugin-gcp-projects@0.3.3
|
||||
- @backstage/plugin-github-actions@0.4.17
|
||||
- @backstage/plugin-graphiql@0.2.15
|
||||
- @backstage/plugin-home@0.2.1
|
||||
- @backstage/plugin-jenkins@0.5.5
|
||||
- @backstage/plugin-kafka@0.2.14
|
||||
- @backstage/plugin-kubernetes@0.4.12
|
||||
- @backstage/plugin-lighthouse@0.2.24
|
||||
- @backstage/plugin-newrelic@0.3.3
|
||||
- @backstage/plugin-pagerduty@0.3.12
|
||||
- @backstage/plugin-rollbar@0.3.13
|
||||
- @backstage/plugin-sentry@0.3.20
|
||||
- @backstage/plugin-tech-radar@0.4.6
|
||||
- @backstage/plugin-todo@0.1.8
|
||||
- @backstage/plugin-user-settings@0.3.4
|
||||
|
||||
## 0.2.43
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -34,7 +34,7 @@ describe('SearchPage', () => {
|
||||
cy.visit('/search-next', {
|
||||
onBeforeLoad(win) {
|
||||
cy.stub(win, 'fetch')
|
||||
.withArgs(`${API_ENDPOINT}?term=&pageCursor=`)
|
||||
.withArgs(`${API_ENDPOINT}?term=`)
|
||||
.resolves({
|
||||
ok: true,
|
||||
json: () => ({ results }),
|
||||
@@ -56,7 +56,7 @@ describe('SearchPage', () => {
|
||||
onBeforeLoad(win) {
|
||||
cy.stub(win, 'fetch')
|
||||
.withArgs(
|
||||
`${API_ENDPOINT}?term=&filters%5Bkind%5D=Component&filters%5Blifecycle%5D%5B0%5D=experimental&pageCursor=`,
|
||||
`${API_ENDPOINT}?term=&filters%5Bkind%5D=Component&filters%5Blifecycle%5D%5B0%5D=experimental`,
|
||||
)
|
||||
.resolves({
|
||||
ok: true,
|
||||
@@ -102,7 +102,7 @@ describe('SearchPage', () => {
|
||||
cy.visit('/search-next?query=backstage', {
|
||||
onBeforeLoad(win) {
|
||||
cy.stub(win, 'fetch')
|
||||
.withArgs(`${API_ENDPOINT}?term=backstage&pageCursor=`)
|
||||
.withArgs(`${API_ENDPOINT}?term=backstage`)
|
||||
.resolves({
|
||||
ok: true,
|
||||
json: () => ({ results: [] }),
|
||||
|
||||
+37
-37
@@ -1,46 +1,46 @@
|
||||
{
|
||||
"name": "example-app",
|
||||
"version": "0.2.43",
|
||||
"version": "0.2.44",
|
||||
"private": true,
|
||||
"bundled": true,
|
||||
"dependencies": {
|
||||
"@backstage/catalog-model": "^0.9.0",
|
||||
"@backstage/cli": "^0.7.9",
|
||||
"@backstage/core-app-api": "^0.1.10",
|
||||
"@backstage/core-components": "^0.3.3",
|
||||
"@backstage/catalog-model": "^0.9.1",
|
||||
"@backstage/cli": "^0.7.10",
|
||||
"@backstage/core-app-api": "^0.1.11",
|
||||
"@backstage/core-components": "^0.4.0",
|
||||
"@backstage/core-plugin-api": "^0.1.6",
|
||||
"@backstage/integration-react": "^0.1.7",
|
||||
"@backstage/plugin-api-docs": "^0.6.6",
|
||||
"@backstage/plugin-badges": "^0.2.7",
|
||||
"@backstage/plugin-catalog": "^0.6.13",
|
||||
"@backstage/plugin-catalog-import": "^0.5.19",
|
||||
"@backstage/plugin-catalog-react": "^0.4.4",
|
||||
"@backstage/plugin-circleci": "^0.2.21",
|
||||
"@backstage/plugin-cloudbuild": "^0.2.21",
|
||||
"@backstage/plugin-code-coverage": "^0.1.9",
|
||||
"@backstage/plugin-cost-insights": "^0.11.4",
|
||||
"@backstage/plugin-explore": "^0.3.14",
|
||||
"@backstage/plugin-gcp-projects": "^0.3.2",
|
||||
"@backstage/plugin-github-actions": "^0.4.16",
|
||||
"@backstage/plugin-graphiql": "^0.2.14",
|
||||
"@backstage/plugin-home": "^0.2.0",
|
||||
"@backstage/plugin-jenkins": "^0.5.4",
|
||||
"@backstage/plugin-kafka": "^0.2.13",
|
||||
"@backstage/plugin-kubernetes": "^0.4.11",
|
||||
"@backstage/plugin-lighthouse": "^0.2.23",
|
||||
"@backstage/plugin-newrelic": "^0.3.2",
|
||||
"@backstage/plugin-org": "^0.3.20",
|
||||
"@backstage/plugin-pagerduty": "0.3.11",
|
||||
"@backstage/plugin-rollbar": "^0.3.12",
|
||||
"@backstage/plugin-scaffolder": "^0.11.1",
|
||||
"@backstage/plugin-search": "^0.4.8",
|
||||
"@backstage/plugin-sentry": "^0.3.19",
|
||||
"@backstage/plugin-shortcuts": "^0.1.6",
|
||||
"@backstage/plugin-tech-radar": "^0.4.5",
|
||||
"@backstage/plugin-techdocs": "^0.11.0",
|
||||
"@backstage/plugin-todo": "^0.1.7",
|
||||
"@backstage/plugin-user-settings": "^0.3.3",
|
||||
"@backstage/search-common": "^0.1.3",
|
||||
"@backstage/integration-react": "^0.1.8",
|
||||
"@backstage/plugin-api-docs": "^0.6.7",
|
||||
"@backstage/plugin-badges": "^0.2.8",
|
||||
"@backstage/plugin-catalog": "^0.6.14",
|
||||
"@backstage/plugin-catalog-import": "^0.5.20",
|
||||
"@backstage/plugin-catalog-react": "^0.4.5",
|
||||
"@backstage/plugin-circleci": "^0.2.22",
|
||||
"@backstage/plugin-cloudbuild": "^0.2.22",
|
||||
"@backstage/plugin-code-coverage": "^0.1.10",
|
||||
"@backstage/plugin-cost-insights": "^0.11.5",
|
||||
"@backstage/plugin-explore": "^0.3.15",
|
||||
"@backstage/plugin-gcp-projects": "^0.3.3",
|
||||
"@backstage/plugin-github-actions": "^0.4.17",
|
||||
"@backstage/plugin-graphiql": "^0.2.15",
|
||||
"@backstage/plugin-home": "^0.2.1",
|
||||
"@backstage/plugin-jenkins": "^0.5.5",
|
||||
"@backstage/plugin-kafka": "^0.2.14",
|
||||
"@backstage/plugin-kubernetes": "^0.4.12",
|
||||
"@backstage/plugin-lighthouse": "^0.2.24",
|
||||
"@backstage/plugin-newrelic": "^0.3.3",
|
||||
"@backstage/plugin-org": "^0.3.21",
|
||||
"@backstage/plugin-pagerduty": "0.3.12",
|
||||
"@backstage/plugin-rollbar": "^0.3.13",
|
||||
"@backstage/plugin-scaffolder": "^0.11.2",
|
||||
"@backstage/plugin-search": "^0.4.9",
|
||||
"@backstage/plugin-sentry": "^0.3.20",
|
||||
"@backstage/plugin-shortcuts": "^0.1.7",
|
||||
"@backstage/plugin-tech-radar": "^0.4.6",
|
||||
"@backstage/plugin-techdocs": "^0.11.1",
|
||||
"@backstage/plugin-todo": "^0.1.8",
|
||||
"@backstage/plugin-user-settings": "^0.3.4",
|
||||
"@backstage/search-common": "^0.2.0",
|
||||
"@backstage/theme": "^0.2.10",
|
||||
"@material-ui/core": "^4.12.2",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
|
||||
@@ -14,20 +14,19 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { makeStyles, Theme, Grid, List, Paper } from '@material-ui/core';
|
||||
import Pagination from '@material-ui/lab/Pagination';
|
||||
import { Content, Header, Lifecycle, Page } from '@backstage/core-components';
|
||||
import { CatalogResultListItem } from '@backstage/plugin-catalog';
|
||||
import {
|
||||
DefaultResultListItem,
|
||||
SearchBar,
|
||||
SearchFilter,
|
||||
SearchResult,
|
||||
SearchResultPager,
|
||||
SearchType,
|
||||
DefaultResultListItem,
|
||||
} from '@backstage/plugin-search';
|
||||
import { Content, Header, Lifecycle, Page } from '@backstage/core-components';
|
||||
import { DocsResultListItem } from '@backstage/plugin-techdocs';
|
||||
import { SearchResultSet } from '@backstage/search-common';
|
||||
import { Grid, List, makeStyles, Paper, Theme } from '@material-ui/core';
|
||||
import React from 'react';
|
||||
|
||||
const useStyles = makeStyles((theme: Theme) => ({
|
||||
bar: {
|
||||
@@ -43,57 +42,6 @@ const useStyles = makeStyles((theme: Theme) => ({
|
||||
},
|
||||
}));
|
||||
|
||||
// TODO: Move this into the search plugin once pagination is natively supported.
|
||||
// See: https://github.com/backstage/backstage/issues/6062
|
||||
const SearchResultList = ({ results }: SearchResultSet) => {
|
||||
const pageSize = 10;
|
||||
const [page, setPage] = useState(1);
|
||||
const changePage = (_: any, pageIndex: number) => {
|
||||
setPage(pageIndex);
|
||||
};
|
||||
const pageAmount = Math.ceil((results.length || 0) / pageSize);
|
||||
return (
|
||||
<>
|
||||
<List>
|
||||
{results
|
||||
.slice(pageSize * (page - 1), pageSize * page)
|
||||
.map(({ type, document }) => {
|
||||
switch (type) {
|
||||
case 'software-catalog':
|
||||
return (
|
||||
<CatalogResultListItem
|
||||
key={document.location}
|
||||
result={document}
|
||||
/>
|
||||
);
|
||||
case 'techdocs':
|
||||
return (
|
||||
<DocsResultListItem
|
||||
key={document.location}
|
||||
result={document}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<DefaultResultListItem
|
||||
key={document.location}
|
||||
result={document}
|
||||
/>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</List>
|
||||
<Pagination
|
||||
count={pageAmount}
|
||||
page={page}
|
||||
onChange={changePage}
|
||||
showFirstButton
|
||||
showLastButton
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const SearchPage = () => {
|
||||
const classes = useStyles();
|
||||
return (
|
||||
@@ -127,8 +75,37 @@ const SearchPage = () => {
|
||||
</Grid>
|
||||
<Grid item xs={9}>
|
||||
<SearchResult>
|
||||
{({ results }) => <SearchResultList results={results} />}
|
||||
{({ results }) => (
|
||||
<List>
|
||||
{results.map(({ type, document }) => {
|
||||
switch (type) {
|
||||
case 'software-catalog':
|
||||
return (
|
||||
<CatalogResultListItem
|
||||
key={document.location}
|
||||
result={document}
|
||||
/>
|
||||
);
|
||||
case 'techdocs':
|
||||
return (
|
||||
<DocsResultListItem
|
||||
key={document.location}
|
||||
result={document}
|
||||
/>
|
||||
);
|
||||
default:
|
||||
return (
|
||||
<DefaultResultListItem
|
||||
key={document.location}
|
||||
result={document}
|
||||
/>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</List>
|
||||
)}
|
||||
</SearchResult>
|
||||
<SearchResultPager />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Content>
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# @backstage/backend-common
|
||||
|
||||
## 0.9.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 714a2a918: Export type that are needed to implement a new `UrlReader`
|
||||
- Updated dependencies
|
||||
- @backstage/integration@0.6.3
|
||||
|
||||
## 0.9.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/backend-common",
|
||||
"description": "Common functionality library for Backstage backends",
|
||||
"version": "0.9.0",
|
||||
"version": "0.9.1",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"private": false,
|
||||
@@ -33,7 +33,7 @@
|
||||
"@backstage/config": "^0.1.8",
|
||||
"@backstage/config-loader": "^0.6.7",
|
||||
"@backstage/errors": "^0.1.1",
|
||||
"@backstage/integration": "^0.6.2",
|
||||
"@backstage/integration": "^0.6.3",
|
||||
"@google-cloud/storage": "^5.8.0",
|
||||
"@octokit/rest": "^18.5.3",
|
||||
"@types/cors": "^2.8.6",
|
||||
@@ -77,7 +77,7 @@
|
||||
}
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.7.9",
|
||||
"@backstage/cli": "^0.7.10",
|
||||
"@backstage/test-utils": "^0.1.17",
|
||||
"@types/archiver": "^5.1.0",
|
||||
"@types/compression": "^1.7.0",
|
||||
|
||||
@@ -1,5 +1,23 @@
|
||||
# example-backend
|
||||
|
||||
## 0.2.44
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/plugin-catalog-backend@0.13.6
|
||||
- @backstage/plugin-scaffolder-backend@0.15.3
|
||||
- @backstage/plugin-techdocs-backend@0.10.1
|
||||
- @backstage/plugin-auth-backend@0.3.24
|
||||
- @backstage/integration@0.6.3
|
||||
- @backstage/plugin-search-backend@0.2.6
|
||||
- @backstage/plugin-search-backend-module-elasticsearch@0.0.3
|
||||
- @backstage/plugin-search-backend-module-pg@0.2.0
|
||||
- @backstage/plugin-search-backend-node@0.4.2
|
||||
- @backstage/catalog-model@0.9.1
|
||||
- @backstage/backend-common@0.9.1
|
||||
- example-app@0.2.44
|
||||
|
||||
## 0.2.43
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "example-backend",
|
||||
"version": "0.2.43",
|
||||
"version": "0.2.44",
|
||||
"main": "dist/index.cjs.js",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
@@ -27,15 +27,15 @@
|
||||
"migrate:create": "knex migrate:make -x ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/backend-common": "^0.9.0",
|
||||
"@backstage/backend-common": "^0.9.1",
|
||||
"@backstage/catalog-client": "^0.3.17",
|
||||
"@backstage/catalog-model": "^0.9.0",
|
||||
"@backstage/catalog-model": "^0.9.1",
|
||||
"@backstage/config": "^0.1.8",
|
||||
"@backstage/integration": "^0.6.2",
|
||||
"@backstage/integration": "^0.6.3",
|
||||
"@backstage/plugin-app-backend": "^0.3.16",
|
||||
"@backstage/plugin-auth-backend": "^0.3.23",
|
||||
"@backstage/plugin-auth-backend": "^0.3.24",
|
||||
"@backstage/plugin-badges-backend": "^0.1.9",
|
||||
"@backstage/plugin-catalog-backend": "^0.13.5",
|
||||
"@backstage/plugin-catalog-backend": "^0.13.6",
|
||||
"@backstage/plugin-code-coverage-backend": "^0.1.10",
|
||||
"@backstage/plugin-graphql-backend": "^0.1.9",
|
||||
"@backstage/plugin-jenkins-backend": "^0.1.4",
|
||||
@@ -43,19 +43,19 @@
|
||||
"@backstage/plugin-kafka-backend": "^0.2.9",
|
||||
"@backstage/plugin-proxy-backend": "^0.2.12",
|
||||
"@backstage/plugin-rollbar-backend": "^0.1.14",
|
||||
"@backstage/plugin-scaffolder-backend": "^0.15.2",
|
||||
"@backstage/plugin-scaffolder-backend": "^0.15.3",
|
||||
"@backstage/plugin-scaffolder-backend-module-rails": "^0.1.5",
|
||||
"@backstage/plugin-search-backend": "^0.2.5",
|
||||
"@backstage/plugin-search-backend-node": "^0.4.0",
|
||||
"@backstage/plugin-search-backend-module-elasticsearch": "^0.0.2",
|
||||
"@backstage/plugin-search-backend-module-pg": "^0.1.3",
|
||||
"@backstage/plugin-techdocs-backend": "^0.10.0",
|
||||
"@backstage/plugin-search-backend": "^0.2.6",
|
||||
"@backstage/plugin-search-backend-node": "^0.4.2",
|
||||
"@backstage/plugin-search-backend-module-elasticsearch": "^0.0.3",
|
||||
"@backstage/plugin-search-backend-module-pg": "^0.2.0",
|
||||
"@backstage/plugin-techdocs-backend": "^0.10.1",
|
||||
"@backstage/plugin-todo-backend": "^0.1.11",
|
||||
"@gitbeaker/node": "^30.2.0",
|
||||
"@octokit/rest": "^18.5.3",
|
||||
"azure-devops-node-api": "^10.2.2",
|
||||
"azure-devops-node-api": "^11.0.1",
|
||||
"dockerode": "^3.2.1",
|
||||
"example-app": "^0.2.43",
|
||||
"example-app": "^0.2.44",
|
||||
"express": "^4.17.1",
|
||||
"express-promise-router": "^4.1.0",
|
||||
"express-prom-bundle": "^6.3.6",
|
||||
@@ -67,7 +67,7 @@
|
||||
"winston": "^3.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.7.8",
|
||||
"@backstage/cli": "^0.7.10",
|
||||
"@types/dockerode": "^3.2.1",
|
||||
"@types/express": "^4.17.6",
|
||||
"@types/express-serve-static-core": "^4.17.5"
|
||||
|
||||
@@ -68,7 +68,7 @@ export default async function createPlugin({
|
||||
// particular collator gathers entities from the software catalog.
|
||||
indexBuilder.addCollator({
|
||||
defaultRefreshIntervalSeconds: 600,
|
||||
collator: new DefaultCatalogCollator({ discovery }),
|
||||
collator: DefaultCatalogCollator.fromConfig(config, { discovery }),
|
||||
});
|
||||
|
||||
indexBuilder.addCollator({
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
# @backstage/catalog-model
|
||||
|
||||
## 0.9.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 13dc3735c: Add an optional `metadata.title` field to all entity kinds.
|
||||
|
||||
This used to be available on only the `Template` kind, and we have decided that the metadata block should be the same for all kinds. A title can be useful especially in large and complex catalogs where users have a tough time navigating or discerning among the entities.
|
||||
|
||||
It also carries some risk. You do not want to end up giving a title that collides with an actual name, which at best leads to confusion and at worst could be a liability. We do not perform any collision detection in the catalog. If you want to disallow this facility you may want to add a small processor that makes sure it's not set.
|
||||
|
||||
At the time of writing this message, only the scaffolder actually makes use of this field for display purposes.
|
||||
|
||||
## 0.9.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@backstage/catalog-model",
|
||||
"version": "0.9.0",
|
||||
"version": "0.9.1",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
@@ -40,7 +40,7 @@
|
||||
"yup": "^0.29.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.7.2",
|
||||
"@backstage/cli": "^0.7.10",
|
||||
"@types/express": "^4.17.6",
|
||||
"@types/jest": "^26.0.7",
|
||||
"@types/lodash": "^4.14.151",
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @backstage/cli
|
||||
|
||||
## 0.7.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 5e803edb8: Added support for importing font files. Imports in CSS via `url()` are supported for the final frontend bundle, but not for packages that are built for publishing. Module imports of fonts files from TypeScript are supported everywhere.
|
||||
- b5118ff76: Updated dependencies
|
||||
|
||||
## 0.7.9
|
||||
|
||||
### Patch Changes
|
||||
|
||||
+20
@@ -68,6 +68,26 @@ declare module '*.svg' {
|
||||
export default src;
|
||||
}
|
||||
|
||||
declare module '*.eot' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
declare module '*.woff' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
declare module '*.woff2' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
declare module '*.ttf' {
|
||||
const src: string;
|
||||
export default src;
|
||||
}
|
||||
|
||||
declare module '*.css' {
|
||||
const classes: { readonly [key: string]: string };
|
||||
export default classes;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/cli",
|
||||
"description": "CLI for developing Backstage plugins and apps",
|
||||
"version": "0.7.9",
|
||||
"version": "0.7.10",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
@@ -51,7 +51,7 @@
|
||||
"@svgr/rollup": "5.5.x",
|
||||
"@svgr/webpack": "5.5.x",
|
||||
"@types/webpack-env": "^1.15.2",
|
||||
"@typescript-eslint/eslint-plugin": "^v4.28.3",
|
||||
"@typescript-eslint/eslint-plugin": "^v4.30.0",
|
||||
"@typescript-eslint/parser": "^v4.28.3",
|
||||
"@yarnpkg/lockfile": "^1.1.0",
|
||||
"babel-plugin-dynamic-import-node": "^2.3.3",
|
||||
@@ -120,12 +120,12 @@
|
||||
"yn": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/backend-common": "^0.9.0",
|
||||
"@backstage/backend-common": "^0.9.1",
|
||||
"@backstage/config": "^0.1.8",
|
||||
"@backstage/core-components": "^0.3.3",
|
||||
"@backstage/core-components": "^0.4.0",
|
||||
"@backstage/core-plugin-api": "^0.1.6",
|
||||
"@backstage/core-app-api": "^0.1.10",
|
||||
"@backstage/dev-utils": "^0.2.7",
|
||||
"@backstage/core-app-api": "^0.1.11",
|
||||
"@backstage/dev-utils": "^0.2.8",
|
||||
"@backstage/test-utils": "^0.1.17",
|
||||
"@backstage/theme": "^0.2.10",
|
||||
"@types/diff": "^5.0.0",
|
||||
|
||||
@@ -80,7 +80,17 @@ export const makeConfigs = async (
|
||||
postcss(),
|
||||
forwardFileImports({
|
||||
exclude: /\.icon\.svg$/,
|
||||
include: [/\.svg$/, /\.png$/, /\.gif$/, /\.jpg$/, /\.jpeg$/],
|
||||
include: [
|
||||
/\.svg$/,
|
||||
/\.png$/,
|
||||
/\.gif$/,
|
||||
/\.jpg$/,
|
||||
/\.jpeg$/,
|
||||
/\.eot$/,
|
||||
/\.woff$/,
|
||||
/\.woff2$/,
|
||||
/\.ttf$/,
|
||||
],
|
||||
}),
|
||||
json(),
|
||||
yaml(),
|
||||
|
||||
@@ -89,6 +89,17 @@ export const transforms = (options: TransformOptions): Transforms => {
|
||||
name: 'static/[name].[hash:8].[ext]',
|
||||
},
|
||||
},
|
||||
{
|
||||
test: /\.(eot|woff|woff2|ttf)$/,
|
||||
use: [
|
||||
{
|
||||
loader: require.resolve('file-loader'),
|
||||
options: {
|
||||
name: 'static/[name].[hash:8].[ext]',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
test: /\.ya?ml$/,
|
||||
use: require.resolve('yml-loader'),
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# @backstage/codemods
|
||||
|
||||
## 0.1.12
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/core-components@0.4.0
|
||||
- @backstage/core-app-api@0.1.11
|
||||
|
||||
## 0.1.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/codemods",
|
||||
"description": "A collection of codemods for Backstage projects",
|
||||
"version": "0.1.11",
|
||||
"version": "0.1.12",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# @backstage/core-app-api
|
||||
|
||||
## 0.1.11
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/core-components@0.4.0
|
||||
|
||||
## 0.1.10
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/core-app-api",
|
||||
"description": "Core app API used by Backstage apps",
|
||||
"version": "0.1.10",
|
||||
"version": "0.1.11",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
@@ -29,7 +29,7 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/core-components": "^0.3.3",
|
||||
"@backstage/core-components": "^0.4.0",
|
||||
"@backstage/config": "^0.1.8",
|
||||
"@backstage/core-plugin-api": "^0.1.6",
|
||||
"@backstage/theme": "^0.2.10",
|
||||
@@ -44,7 +44,7 @@
|
||||
"zen-observable": "^0.8.15"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.7.9",
|
||||
"@backstage/cli": "^0.7.10",
|
||||
"@backstage/test-utils": "^0.1.17",
|
||||
"@backstage/test-utils-core": "^0.1.2",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
|
||||
@@ -1,5 +1,20 @@
|
||||
# @backstage/core-components
|
||||
|
||||
## 0.4.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 3ed78fca3: Changed the `titleComponent` prop on `ContentHeader` to accept `ReactNode` instead of a React `ComponentType`. Usages of this prop should be converted from passing a component to passing in the rendered element:
|
||||
|
||||
```diff
|
||||
-<ContentHeader titleComponent={MyComponent}>
|
||||
+<ContentHeader titleComponent={<MyComponent />}>
|
||||
```
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- e0a6aea82: Bumped `react-hook-form` to `^7.12.2`
|
||||
|
||||
## 0.3.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/core-components",
|
||||
"description": "Core components used by Backstage plugins and apps",
|
||||
"version": "0.3.3",
|
||||
"version": "0.4.0",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
@@ -70,8 +70,8 @@
|
||||
"zen-observable": "^0.8.15"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/core-app-api": "^0.1.10",
|
||||
"@backstage/cli": "^0.7.9",
|
||||
"@backstage/core-app-api": "^0.1.11",
|
||||
"@backstage/cli": "^0.7.10",
|
||||
"@backstage/test-utils": "^0.1.17",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
"@testing-library/react": "^11.2.5",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @backstage/create-app
|
||||
|
||||
## 0.3.39
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 25924638b: Minor tweaks to the create-app template to match earlier documented changes
|
||||
|
||||
## 0.3.38
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/create-app",
|
||||
"description": "Create app package for Backstage",
|
||||
"version": "0.3.38",
|
||||
"version": "0.3.39",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
||||
@@ -10,6 +10,7 @@ import { DefaultCatalogCollator } from '@backstage/plugin-catalog-backend';
|
||||
export default async function createPlugin({
|
||||
logger,
|
||||
discovery,
|
||||
config,
|
||||
}: PluginEnvironment) {
|
||||
// Initialize a connection to a search engine.
|
||||
const searchEngine = new LunrSearchEngine({ logger });
|
||||
@@ -19,7 +20,7 @@ export default async function createPlugin({
|
||||
// particular collator gathers entities from the software catalog.
|
||||
indexBuilder.addCollator({
|
||||
defaultRefreshIntervalSeconds: 600,
|
||||
collator: new DefaultCatalogCollator({ discovery }),
|
||||
collator: DefaultCatalogCollator.fromConfig(config, { discovery }),
|
||||
});
|
||||
|
||||
// The scheduler controls when documents are gathered from collators and sent
|
||||
|
||||
@@ -1,5 +1,16 @@
|
||||
# @backstage/dev-utils
|
||||
|
||||
## 0.2.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/plugin-catalog-react@0.4.5
|
||||
- @backstage/core-components@0.4.0
|
||||
- @backstage/catalog-model@0.9.1
|
||||
- @backstage/core-app-api@0.1.11
|
||||
- @backstage/integration-react@0.1.8
|
||||
|
||||
## 0.2.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/dev-utils",
|
||||
"description": "Utilities for developing Backstage plugins.",
|
||||
"version": "0.2.7",
|
||||
"version": "0.2.8",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
@@ -29,12 +29,12 @@
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/core-app-api": "^0.1.9",
|
||||
"@backstage/core-components": "^0.3.2",
|
||||
"@backstage/core-app-api": "^0.1.11",
|
||||
"@backstage/core-components": "^0.4.0",
|
||||
"@backstage/core-plugin-api": "^0.1.6",
|
||||
"@backstage/catalog-model": "^0.9.0",
|
||||
"@backstage/integration-react": "^0.1.7",
|
||||
"@backstage/plugin-catalog-react": "^0.4.3",
|
||||
"@backstage/catalog-model": "^0.9.1",
|
||||
"@backstage/integration-react": "^0.1.8",
|
||||
"@backstage/plugin-catalog-react": "^0.4.5",
|
||||
"@backstage/test-utils": "^0.1.17",
|
||||
"@backstage/theme": "^0.2.10",
|
||||
"@material-ui/core": "^4.12.2",
|
||||
@@ -50,7 +50,7 @@
|
||||
"react-router-dom": "6.0.0-beta.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.7.9",
|
||||
"@backstage/cli": "^0.7.10",
|
||||
"@types/jest": "^26.0.7",
|
||||
"@types/node": "^14.14.32"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
# @backstage/integration-react
|
||||
|
||||
## 0.1.8
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/integration@0.6.3
|
||||
- @backstage/core-components@0.4.0
|
||||
|
||||
## 0.1.7
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@backstage/integration-react",
|
||||
"version": "0.1.7",
|
||||
"version": "0.1.8",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
@@ -21,9 +21,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/config": "^0.1.6",
|
||||
"@backstage/core-components": "^0.3.1",
|
||||
"@backstage/core-components": "^0.4.0",
|
||||
"@backstage/core-plugin-api": "^0.1.6",
|
||||
"@backstage/integration": "^0.6.0",
|
||||
"@backstage/integration": "^0.6.3",
|
||||
"@backstage/theme": "^0.2.9",
|
||||
"@material-ui/core": "^4.12.2",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
@@ -33,8 +33,8 @@
|
||||
"react-use": "^17.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.7.8",
|
||||
"@backstage/dev-utils": "^0.2.6",
|
||||
"@backstage/cli": "^0.7.10",
|
||||
"@backstage/dev-utils": "^0.2.8",
|
||||
"@backstage/test-utils": "^0.1.17",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
"@testing-library/react": "^11.2.5",
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
# @backstage/integration
|
||||
|
||||
## 0.6.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 5dca42b17: Update to properly handle Azure DevOps Server download URL
|
||||
|
||||
## 0.6.2
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@backstage/integration",
|
||||
"version": "0.6.2",
|
||||
"version": "0.6.3",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
@@ -37,7 +37,7 @@
|
||||
"luxon": "^2.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.7.9",
|
||||
"@backstage/cli": "^0.7.10",
|
||||
"@backstage/config-loader": "^0.6.7",
|
||||
"@backstage/test-utils": "^0.1.17",
|
||||
"@types/jest": "^26.0.7",
|
||||
|
||||
@@ -97,5 +97,20 @@ describe('azure core', () => {
|
||||
);
|
||||
expect(new URL(result).searchParams.get('scopePath')).toEqual('docs');
|
||||
});
|
||||
|
||||
it.each([
|
||||
{
|
||||
url: 'https://dev.azure.com/org-name/project-name/_git/repo-name',
|
||||
result:
|
||||
'https://dev.azure.com/org-name/project-name/_apis/git/repositories/repo-name/items?recursionLevel=full&download=true&api-version=6.0',
|
||||
},
|
||||
{
|
||||
url: 'https://api.com/org-name/project-name/_git/repo-name',
|
||||
result:
|
||||
'https://api.com/org-name/project-name/_apis/git/repositories/repo-name/items?recursionLevel=full&download=true&api-version=6.0',
|
||||
},
|
||||
])('should handle happy path %#', async ({ url, result }) => {
|
||||
expect(getAzureDownloadUrl(url)).toBe(result);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -100,7 +100,17 @@ export function getAzureDownloadUrl(url: string): string {
|
||||
? `&scopePath=${encodeURIComponent(filepath)}`
|
||||
: '';
|
||||
|
||||
return `${protocol}://${resource}/${organization}/${project}/_apis/git/repositories/${repoName}/items?recursionLevel=full&download=true&api-version=6.0${scopePath}`;
|
||||
if (resource === 'dev.azure.com') {
|
||||
return `${protocol}://${resource}/${organization}/${project}/_apis/git/repositories/${repoName}/items?recursionLevel=full&download=true&api-version=6.0${scopePath}`;
|
||||
}
|
||||
|
||||
// For Azure DevOps Server `parseGitUrl` returns the same values
|
||||
// for `organization` and `project` like this: `organization/project/_git`
|
||||
// so we drop `project` and then strip `/_git` from `organization`
|
||||
return `${protocol}://${resource}/${organization.replace(
|
||||
'/_git',
|
||||
'',
|
||||
)}/_apis/git/repositories/${repoName}/items?recursionLevel=full&download=true&api-version=6.0${scopePath}`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
# @backstage/search-common
|
||||
|
||||
## 0.2.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- a13f21cdc: Implement optional `pageCursor` based paging in search.
|
||||
|
||||
To use paging in your app, add a `<SearchResultPager />` to your
|
||||
`SearchPage.tsx`.
|
||||
|
||||
## 0.1.3
|
||||
|
||||
### Patch Changes
|
||||
|
||||
@@ -53,7 +53,7 @@ export interface SearchQuery {
|
||||
// (undocumented)
|
||||
filters?: JsonObject;
|
||||
// (undocumented)
|
||||
pageCursor: string;
|
||||
pageCursor?: string;
|
||||
// (undocumented)
|
||||
term: string;
|
||||
// (undocumented)
|
||||
@@ -74,6 +74,10 @@ export interface SearchResult {
|
||||
//
|
||||
// @public (undocumented)
|
||||
export interface SearchResultSet {
|
||||
// (undocumented)
|
||||
nextPageCursor?: string;
|
||||
// (undocumented)
|
||||
previousPageCursor?: string;
|
||||
// (undocumented)
|
||||
results: SearchResult[];
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/search-common",
|
||||
"description": "Common functionalities for Search, to be shared between various search-enabled plugins",
|
||||
"version": "0.1.3",
|
||||
"version": "0.2.0",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"private": false,
|
||||
@@ -39,7 +39,7 @@
|
||||
"@backstage/config": "^0.1.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.7.7"
|
||||
"@backstage/cli": "^0.7.10"
|
||||
},
|
||||
"jest": {
|
||||
"roots": [
|
||||
|
||||
@@ -19,7 +19,7 @@ export interface SearchQuery {
|
||||
term: string;
|
||||
filters?: JsonObject;
|
||||
types?: string[];
|
||||
pageCursor: string;
|
||||
pageCursor?: string;
|
||||
}
|
||||
|
||||
export interface SearchResult {
|
||||
@@ -29,6 +29,8 @@ export interface SearchResult {
|
||||
|
||||
export interface SearchResultSet {
|
||||
results: SearchResult[];
|
||||
nextPageCursor?: string;
|
||||
previousPageCursor?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,42 @@
|
||||
# @backstage/techdocs-common
|
||||
|
||||
## 0.10.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
- 8b0f6f860: Set the correct `edit_uri` or `repo_url` for documentation pages that are hosted on GitHub and GitLab.
|
||||
|
||||
The constructor of the `TechDocsGenerator` changed.
|
||||
Prefer the use of `TechdocsGenerator.fromConfig(…)` instead:
|
||||
|
||||
```diff
|
||||
- const techdocsGenerator = new TechdocsGenerator({
|
||||
+ const techdocsGenerator = TechdocsGenerator.fromConfig(config, {
|
||||
logger,
|
||||
containerRunner,
|
||||
- config,
|
||||
});
|
||||
```
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- 30ed662a3: Adding in-context search to TechDocs Reader component. Using existing search-backend to query for indexed search results scoped into a specific entity's techdocs. Needs TechDocsCollator enabled on the backend to work.
|
||||
|
||||
Adding extra information to indexed tech docs documents for search.
|
||||
|
||||
- 3624616e7: "Local" (out-of-the-box) publisher explicitly follows lower-case entity triplet
|
||||
logic.
|
||||
- 67ba7e088: Only write the updated `mkdocs.yml` file if the content was updated.
|
||||
|
||||
This keeps local files unchanged if the `dir` annotation is used in combination with the `file` location.
|
||||
|
||||
- 8eab6be6a: Force using `posix` path for cloud storage
|
||||
- Updated dependencies
|
||||
- @backstage/integration@0.6.3
|
||||
- @backstage/search-common@0.2.0
|
||||
- @backstage/catalog-model@0.9.1
|
||||
- @backstage/backend-common@0.9.1
|
||||
|
||||
## 0.9.0
|
||||
|
||||
### Minor Changes
|
||||
|
||||
@@ -249,10 +249,12 @@ export class TechdocsGenerator implements GeneratorBase {
|
||||
logger,
|
||||
containerRunner,
|
||||
config,
|
||||
scmIntegrations,
|
||||
}: {
|
||||
logger: Logger_2;
|
||||
containerRunner: ContainerRunner;
|
||||
config: Config;
|
||||
scmIntegrations: ScmIntegrationRegistry;
|
||||
});
|
||||
// (undocumented)
|
||||
static fromConfig(
|
||||
@@ -264,7 +266,7 @@ export class TechdocsGenerator implements GeneratorBase {
|
||||
containerRunner: ContainerRunner;
|
||||
logger: Logger_2;
|
||||
},
|
||||
): Promise<TechdocsGenerator>;
|
||||
): TechdocsGenerator;
|
||||
// (undocumented)
|
||||
run({
|
||||
inputDir,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "@backstage/techdocs-common",
|
||||
"description": "Common functionalities for TechDocs, to be shared between techdocs-backend plugin and techdocs-cli",
|
||||
"version": "0.9.0",
|
||||
"version": "0.10.0",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"private": false,
|
||||
@@ -38,18 +38,19 @@
|
||||
"dependencies": {
|
||||
"@azure/identity": "^1.5.0",
|
||||
"@azure/storage-blob": "^12.5.0",
|
||||
"@backstage/backend-common": "^0.9.0",
|
||||
"@backstage/catalog-model": "^0.9.0",
|
||||
"@backstage/backend-common": "^0.9.1",
|
||||
"@backstage/catalog-model": "^0.9.1",
|
||||
"@backstage/config": "^0.1.8",
|
||||
"@backstage/errors": "^0.1.1",
|
||||
"@backstage/search-common": "^0.1.3",
|
||||
"@backstage/integration": "^0.6.2",
|
||||
"@backstage/search-common": "^0.2.0",
|
||||
"@backstage/integration": "^0.6.3",
|
||||
"@google-cloud/storage": "^5.6.0",
|
||||
"@trendyol-js/openstack-swift-sdk": "^0.0.4",
|
||||
"@types/express": "^4.17.6",
|
||||
"aws-sdk": "^2.840.0",
|
||||
"express": "^4.17.1",
|
||||
"fs-extra": "9.1.0",
|
||||
"git-url-parse": "~11.4.4",
|
||||
"js-yaml": "^4.0.0",
|
||||
"json5": "^2.1.3",
|
||||
"mime-types": "^2.1.27",
|
||||
@@ -59,7 +60,7 @@
|
||||
"winston": "^3.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.7.8",
|
||||
"@backstage/cli": "^0.7.10",
|
||||
"@types/fs-extra": "^9.0.5",
|
||||
"@types/js-yaml": "^4.0.0",
|
||||
"@types/mime-types": "^2.1.0",
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
site_name: Test site name
|
||||
site_description: Test site description
|
||||
# This is a comment that is removed after editing
|
||||
@@ -0,0 +1,4 @@
|
||||
site_name: Test site name
|
||||
site_description: Test site description
|
||||
|
||||
edit_uri: https://github.com/backstage/backstage/edit/main/docs
|
||||
@@ -44,10 +44,9 @@ describe('generators', () => {
|
||||
|
||||
it('should return correct registered generator', async () => {
|
||||
const generators = new Generators();
|
||||
const techdocs = new TechdocsGenerator({
|
||||
const techdocs = TechdocsGenerator.fromConfig(new ConfigReader({}), {
|
||||
logger,
|
||||
containerRunner,
|
||||
config: new ConfigReader({}),
|
||||
});
|
||||
|
||||
generators.register('techdocs', techdocs);
|
||||
|
||||
@@ -38,10 +38,9 @@ export class Generators implements GeneratorBuilder {
|
||||
): Promise<GeneratorBuilder> {
|
||||
const generators = new Generators();
|
||||
|
||||
const techdocsGenerator = new TechdocsGenerator({
|
||||
const techdocsGenerator = TechdocsGenerator.fromConfig(config, {
|
||||
logger,
|
||||
containerRunner,
|
||||
config,
|
||||
});
|
||||
generators.register('techdocs', techdocsGenerator);
|
||||
|
||||
|
||||
@@ -13,19 +13,20 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { getVoidLogger } from '@backstage/backend-common';
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import { ScmIntegrations } from '@backstage/integration';
|
||||
import fs from 'fs-extra';
|
||||
import mockFs from 'mock-fs';
|
||||
import os from 'os';
|
||||
import path, { resolve as resolvePath } from 'path';
|
||||
import { ParsedLocationAnnotation } from '../../helpers';
|
||||
import { RemoteProtocol } from '../prepare/types';
|
||||
import {
|
||||
addBuildTimestampMetadata,
|
||||
getGeneratorKey,
|
||||
getMkdocsYml,
|
||||
getRepoUrlFromLocationAnnotation,
|
||||
isValidRepoUrlForMkdocs,
|
||||
patchMkdocsYmlPreBuild,
|
||||
storeEtagMetadata,
|
||||
validateMkdocsYaml,
|
||||
@@ -48,6 +49,9 @@ const mkdocsYmlWithExtensions = fs.readFileSync(
|
||||
const mkdocsYmlWithRepoUrl = fs.readFileSync(
|
||||
resolvePath(__filename, '../__fixtures__/mkdocs_with_repo_url.yml'),
|
||||
);
|
||||
const mkdocsYmlWithEditUri = fs.readFileSync(
|
||||
resolvePath(__filename, '../__fixtures__/mkdocs_with_edit_uri.yml'),
|
||||
);
|
||||
const mkdocsYmlWithValidDocDir = fs.readFileSync(
|
||||
resolvePath(__filename, '../__fixtures__/mkdocs_valid_doc_dir.yml'),
|
||||
);
|
||||
@@ -57,9 +61,14 @@ const mkdocsYmlWithInvalidDocDir = fs.readFileSync(
|
||||
const mkdocsYmlWithInvalidDocDir2 = fs.readFileSync(
|
||||
resolvePath(__filename, '../__fixtures__/mkdocs_invalid_doc_dir2.yml'),
|
||||
);
|
||||
const mkdocsYmlWithComments = fs.readFileSync(
|
||||
resolvePath(__filename, '../__fixtures__/mkdocs_with_comments.yml'),
|
||||
);
|
||||
const mockLogger = getVoidLogger();
|
||||
const rootDir = os.platform() === 'win32' ? 'C:\\rootDir' : '/rootDir';
|
||||
|
||||
const scmIntegrations = ScmIntegrations.fromConfig(new ConfigReader({}));
|
||||
|
||||
describe('helpers', () => {
|
||||
describe('getGeneratorKey', () => {
|
||||
it('should return techdocs as the only generator key', () => {
|
||||
@@ -68,120 +77,85 @@ describe('helpers', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('isValidRepoUrlForMkdocs', () => {
|
||||
it('should return true for valid repo_url values for mkdocs', () => {
|
||||
const validRepoUrls = [
|
||||
'https://github.com/org/repo',
|
||||
'https://github.com/backstage/backstage/',
|
||||
'https://github.com/org123/repo1-2-3/',
|
||||
'http://github.com/insecureOrg/insecureRepo',
|
||||
'https://gitlab.com/org/repo',
|
||||
'https://gitlab.com/backstage/backstage/',
|
||||
'https://gitlab.com/org123/repo1-2-3/',
|
||||
'http://gitlab.com/insecureOrg/insecureRepo',
|
||||
];
|
||||
|
||||
const validRemoteProtocols = ['github', 'gitlab'];
|
||||
|
||||
validRepoUrls.forEach(url => {
|
||||
validRemoteProtocols.forEach(targetType => {
|
||||
expect(
|
||||
isValidRepoUrlForMkdocs(url, targetType as RemoteProtocol),
|
||||
).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should return false for invalid repo_urls values for mkdocs', () => {
|
||||
const invalidRepoUrls = [
|
||||
'git@github.com:org/repo',
|
||||
'https://github.com/backstage/backstage/tree/master/plugins/techdocs-backend',
|
||||
];
|
||||
|
||||
invalidRepoUrls.forEach(url => {
|
||||
expect(isValidRepoUrlForMkdocs(url, 'github')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return false for unsupported remote protocols', () => {
|
||||
const validRepoUrl = 'https://github.com/backstage/backstage';
|
||||
|
||||
const unsupportedRemoteProtocols = ['dir', 'file', 'url'];
|
||||
|
||||
unsupportedRemoteProtocols.forEach(targetType => {
|
||||
expect(
|
||||
isValidRepoUrlForMkdocs(validRepoUrl, targetType as RemoteProtocol),
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getRepoUrlFromLocationAnnotation', () => {
|
||||
it('should return undefined for unsupported location type', () => {
|
||||
const parsedLocationAnnotation1: ParsedLocationAnnotation = {
|
||||
it.each`
|
||||
url | repo_url | edit_uri
|
||||
${'https://github.com/backstage/backstage'} | ${'https://github.com/backstage/backstage'} | ${undefined}
|
||||
${'https://github.com/backstage/backstage/tree/main/examples/techdocs/'} | ${undefined} | ${'https://github.com/backstage/backstage/edit/main/examples/techdocs/docs'}
|
||||
${'https://github.com/backstage/backstage/tree/main/'} | ${undefined} | ${'https://github.com/backstage/backstage/edit/main/docs'}
|
||||
${'https://gitlab.com/backstage/backstage'} | ${'https://gitlab.com/backstage/backstage'} | ${undefined}
|
||||
${'https://gitlab.com/backstage/backstage/-/blob/main/examples/techdocs/'} | ${undefined} | ${'https://gitlab.com/backstage/backstage/-/edit/main/examples/techdocs/docs'}
|
||||
${'https://gitlab.com/backstage/backstage/-/blob/main/'} | ${undefined} | ${'https://gitlab.com/backstage/backstage/-/edit/main/docs'}
|
||||
`('should convert $url', ({ url: target, repo_url, edit_uri }) => {
|
||||
const parsedLocationAnnotation: ParsedLocationAnnotation = {
|
||||
type: 'url',
|
||||
target,
|
||||
};
|
||||
|
||||
expect(
|
||||
getRepoUrlFromLocationAnnotation(
|
||||
parsedLocationAnnotation,
|
||||
scmIntegrations,
|
||||
),
|
||||
).toEqual({ repo_url, edit_uri });
|
||||
});
|
||||
|
||||
it.each`
|
||||
url | edit_uri
|
||||
${'https://github.com/backstage/backstage/tree/main/examples/techdocs/'} | ${'https://github.com/backstage/backstage/edit/main/examples/techdocs/custom/folder'}
|
||||
${'https://github.com/backstage/backstage/tree/main/'} | ${'https://github.com/backstage/backstage/edit/main/custom/folder'}
|
||||
${'https://gitlab.com/backstage/backstage/-/blob/main/examples/techdocs/'} | ${'https://gitlab.com/backstage/backstage/-/edit/main/examples/techdocs/custom/folder'}
|
||||
${'https://gitlab.com/backstage/backstage/-/blob/main/'} | ${'https://gitlab.com/backstage/backstage/-/edit/main/custom/folder'}
|
||||
`(
|
||||
'should convert $url with custom docsFolder',
|
||||
({ url: target, edit_uri }) => {
|
||||
const parsedLocationAnnotation: ParsedLocationAnnotation = {
|
||||
type: 'url',
|
||||
target,
|
||||
};
|
||||
|
||||
expect(
|
||||
getRepoUrlFromLocationAnnotation(
|
||||
parsedLocationAnnotation,
|
||||
scmIntegrations,
|
||||
'./custom/folder',
|
||||
),
|
||||
).toEqual({ edit_uri });
|
||||
},
|
||||
);
|
||||
|
||||
it.each`
|
||||
url
|
||||
${'https://bitbucket.org/backstage/backstage/src/master/examples/techdocs/'}
|
||||
${'https://bitbucket.org/backstage/backstage/src/master/'}
|
||||
${'https://dev.azure.com/organization/project/_git/repository?path=%2Fexamples%2Ftechdocs'}
|
||||
${'https://dev.azure.com/organization/project/_git/repository?path=%2F'}
|
||||
`('should ignore $url', ({ url: target }) => {
|
||||
const parsedLocationAnnotation: ParsedLocationAnnotation = {
|
||||
type: 'url',
|
||||
target,
|
||||
};
|
||||
|
||||
expect(
|
||||
getRepoUrlFromLocationAnnotation(
|
||||
parsedLocationAnnotation,
|
||||
scmIntegrations,
|
||||
),
|
||||
).toEqual({});
|
||||
});
|
||||
|
||||
it('should ignore unsupported location type', () => {
|
||||
const parsedLocationAnnotation: ParsedLocationAnnotation = {
|
||||
type: 'dir',
|
||||
target: '/home/user/workspace/docs-repository',
|
||||
};
|
||||
|
||||
const parsedLocationAnnotation2: ParsedLocationAnnotation = {
|
||||
type: 'file' as RemoteProtocol,
|
||||
target: '/home/user/workspace/docs-repository/catalog-info.yaml',
|
||||
};
|
||||
|
||||
const parsedLocationAnnotation3: ParsedLocationAnnotation = {
|
||||
type: 'url',
|
||||
target: 'https://my-website.com/storage/this/docs/repository',
|
||||
};
|
||||
|
||||
expect(getRepoUrlFromLocationAnnotation(parsedLocationAnnotation1)).toBe(
|
||||
undefined,
|
||||
);
|
||||
expect(getRepoUrlFromLocationAnnotation(parsedLocationAnnotation2)).toBe(
|
||||
undefined,
|
||||
);
|
||||
|
||||
expect(getRepoUrlFromLocationAnnotation(parsedLocationAnnotation3)).toBe(
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
|
||||
it('should return correct target url for supported hosts', () => {
|
||||
const parsedLocationAnnotation1: ParsedLocationAnnotation = {
|
||||
type: 'github' as RemoteProtocol,
|
||||
target: 'https://github.com/backstage/backstage.git',
|
||||
};
|
||||
|
||||
expect(getRepoUrlFromLocationAnnotation(parsedLocationAnnotation1)).toBe(
|
||||
'https://github.com/backstage/backstage',
|
||||
);
|
||||
|
||||
const parsedLocationAnnotation2: ParsedLocationAnnotation = {
|
||||
type: 'github' as RemoteProtocol,
|
||||
target: 'https://github.com/org/repo',
|
||||
};
|
||||
|
||||
expect(getRepoUrlFromLocationAnnotation(parsedLocationAnnotation2)).toBe(
|
||||
'https://github.com/org/repo',
|
||||
);
|
||||
|
||||
const parsedLocationAnnotation3: ParsedLocationAnnotation = {
|
||||
type: 'gitlab' as RemoteProtocol,
|
||||
target: 'https://gitlab.com/org/repo',
|
||||
};
|
||||
|
||||
expect(getRepoUrlFromLocationAnnotation(parsedLocationAnnotation3)).toBe(
|
||||
'https://gitlab.com/org/repo',
|
||||
);
|
||||
|
||||
const parsedLocationAnnotation4: ParsedLocationAnnotation = {
|
||||
type: 'github' as RemoteProtocol,
|
||||
target:
|
||||
'github.com/backstage/backstage/blob/master/plugins/techdocs-backend/examples/documented-component',
|
||||
};
|
||||
|
||||
expect(getRepoUrlFromLocationAnnotation(parsedLocationAnnotation4)).toBe(
|
||||
'github.com/backstage/backstage/blob/master/plugins/techdocs-backend/examples/documented-component',
|
||||
);
|
||||
expect(
|
||||
getRepoUrlFromLocationAnnotation(
|
||||
parsedLocationAnnotation,
|
||||
scmIntegrations,
|
||||
),
|
||||
).toEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -190,7 +164,9 @@ describe('helpers', () => {
|
||||
mockFs({
|
||||
'/mkdocs.yml': mkdocsYml,
|
||||
'/mkdocs_with_repo_url.yml': mkdocsYmlWithRepoUrl,
|
||||
'/mkdocs_with_edit_uri.yml': mkdocsYmlWithEditUri,
|
||||
'/mkdocs_with_extensions.yml': mkdocsYmlWithExtensions,
|
||||
'/mkdocs_with_comments.yml': mkdocsYmlWithComments,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -198,9 +174,9 @@ describe('helpers', () => {
|
||||
mockFs.restore();
|
||||
});
|
||||
|
||||
it('should add repo_url to mkdocs.yml', async () => {
|
||||
it('should add edit_uri to mkdocs.yml', async () => {
|
||||
const parsedLocationAnnotation: ParsedLocationAnnotation = {
|
||||
type: 'github' as RemoteProtocol,
|
||||
type: 'url',
|
||||
target: 'https://github.com/backstage/backstage',
|
||||
};
|
||||
|
||||
@@ -208,6 +184,7 @@ describe('helpers', () => {
|
||||
'/mkdocs.yml',
|
||||
mockLogger,
|
||||
parsedLocationAnnotation,
|
||||
scmIntegrations,
|
||||
);
|
||||
|
||||
const updatedMkdocsYml = await fs.readFile('/mkdocs.yml');
|
||||
@@ -219,7 +196,7 @@ describe('helpers', () => {
|
||||
|
||||
it('should add repo_url to mkdocs.yml that contains custom yaml tags', async () => {
|
||||
const parsedLocationAnnotation: ParsedLocationAnnotation = {
|
||||
type: 'github' as RemoteProtocol,
|
||||
type: 'url',
|
||||
target: 'https://github.com/backstage/backstage',
|
||||
};
|
||||
|
||||
@@ -227,6 +204,7 @@ describe('helpers', () => {
|
||||
'/mkdocs_with_extensions.yml',
|
||||
mockLogger,
|
||||
parsedLocationAnnotation,
|
||||
scmIntegrations,
|
||||
);
|
||||
|
||||
const updatedMkdocsYml = await fs.readFile('/mkdocs_with_extensions.yml');
|
||||
@@ -241,7 +219,7 @@ describe('helpers', () => {
|
||||
|
||||
it('should not override existing repo_url in mkdocs.yml', async () => {
|
||||
const parsedLocationAnnotation: ParsedLocationAnnotation = {
|
||||
type: 'github' as RemoteProtocol,
|
||||
type: 'url',
|
||||
target: 'https://github.com/neworg/newrepo',
|
||||
};
|
||||
|
||||
@@ -249,6 +227,7 @@ describe('helpers', () => {
|
||||
'/mkdocs_with_repo_url.yml',
|
||||
mockLogger,
|
||||
parsedLocationAnnotation,
|
||||
scmIntegrations,
|
||||
);
|
||||
|
||||
const updatedMkdocsYml = await fs.readFile('/mkdocs_with_repo_url.yml');
|
||||
@@ -260,6 +239,51 @@ describe('helpers', () => {
|
||||
'repo_url: https://github.com/neworg/newrepo',
|
||||
);
|
||||
});
|
||||
|
||||
it('should not override existing edit_uri in mkdocs.yml', async () => {
|
||||
const parsedLocationAnnotation: ParsedLocationAnnotation = {
|
||||
type: 'url',
|
||||
target: 'https://github.com/neworg/newrepo',
|
||||
};
|
||||
|
||||
await patchMkdocsYmlPreBuild(
|
||||
'/mkdocs_with_edit_uri.yml',
|
||||
mockLogger,
|
||||
parsedLocationAnnotation,
|
||||
scmIntegrations,
|
||||
);
|
||||
|
||||
const updatedMkdocsYml = await fs.readFile('/mkdocs_with_edit_uri.yml');
|
||||
|
||||
expect(updatedMkdocsYml.toString()).toContain(
|
||||
'edit_uri: https://github.com/backstage/backstage/edit/main/docs',
|
||||
);
|
||||
expect(updatedMkdocsYml.toString()).not.toContain(
|
||||
'https://github.com/neworg/newrepo',
|
||||
);
|
||||
});
|
||||
|
||||
it('should not update mkdocs.yml if nothing should be changed', async () => {
|
||||
const parsedLocationAnnotation: ParsedLocationAnnotation = {
|
||||
type: 'dir',
|
||||
target: '/unsupported/path',
|
||||
};
|
||||
|
||||
await patchMkdocsYmlPreBuild(
|
||||
'/mkdocs_with_comments.yml',
|
||||
mockLogger,
|
||||
parsedLocationAnnotation,
|
||||
scmIntegrations,
|
||||
);
|
||||
|
||||
const updatedMkdocsYml = await fs.readFile('/mkdocs_with_comments.yml');
|
||||
|
||||
expect(updatedMkdocsYml.toString()).toContain(
|
||||
'# This is a comment that is removed after editing',
|
||||
);
|
||||
expect(updatedMkdocsYml.toString()).not.toContain('edit_uri');
|
||||
expect(updatedMkdocsYml.toString()).not.toContain('repo_url');
|
||||
});
|
||||
});
|
||||
|
||||
describe('addBuildTimestampMetadata', () => {
|
||||
|
||||
@@ -14,10 +14,12 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { isChildPath } from '@backstage/backend-common';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
import { ScmIntegrationRegistry } from '@backstage/integration';
|
||||
import { spawn } from 'child_process';
|
||||
import fs from 'fs-extra';
|
||||
import gitUrlParse from 'git-url-parse';
|
||||
import yaml, { DEFAULT_SCHEMA, Type } from 'js-yaml';
|
||||
import path, { resolve as resolvePath } from 'path';
|
||||
import { PassThrough, Writable } from 'stream';
|
||||
@@ -80,63 +82,42 @@ export const runCommand = async ({
|
||||
};
|
||||
|
||||
/**
|
||||
* Return true if mkdocs can compile docs with provided repo_url
|
||||
*
|
||||
* Valid repo_url examples in mkdocs.yml
|
||||
* - https://github.com/backstage/backstage
|
||||
* - https://gitlab.com/org/repo/
|
||||
* - http://github.com/backstage/backstage
|
||||
* - A http(s) protocol URL to the root of the repository
|
||||
*
|
||||
* Invalid repo_url examples in mkdocs.yml
|
||||
* - https://github.com/backstage/backstage/blob/master/plugins/techdocs-backend/examples/documented-component
|
||||
* - (anything that is not valid as described above)
|
||||
*
|
||||
* @param {string} repoUrl URL supposed to be used as repo_url in mkdocs.yml
|
||||
* @param {string} locationType Type of source code host - github, gitlab, dir, url, etc.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export const isValidRepoUrlForMkdocs = (
|
||||
repoUrl: string,
|
||||
locationType: string,
|
||||
): boolean => {
|
||||
// Trim trailing slash
|
||||
const cleanRepoUrl = repoUrl.replace(/\/$/, '');
|
||||
|
||||
if (locationType === 'github' || locationType === 'gitlab') {
|
||||
// A valid repoUrl to the root of the repository will be split into 5 strings if split using the / delimiter.
|
||||
// We do not want URLs which have more than that number of forward slashes since they will signify a non-root location
|
||||
// Note: This is not the best possible implementation but will work most of the times.. Feel free to improve or
|
||||
// highlight edge cases.
|
||||
return cleanRepoUrl.split('/').length === 5;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return a valid URL of the repository used in backstage.io/techdocs-ref annotation.
|
||||
* Return undefined if the `target` is not valid in context of repo_url in mkdocs.yml
|
||||
* Alter URL so that it is a valid repo_url config in mkdocs.yml
|
||||
* Return the source url for MkDocs based on the backstage.io/techdocs-ref annotation.
|
||||
* Depending on the type of target, it can either return a repo_url, an edit_uri, both, or none.
|
||||
*
|
||||
* @param {ParsedLocationAnnotation} parsedLocationAnnotation Object with location url and type
|
||||
* @returns {string | undefined}
|
||||
* @param {ScmIntegrationRegistry} scmIntegrations the scmIntegration to do url transformations
|
||||
* @param {string} docsFolder the configured docs folder in the mkdocs.yml (defaults to 'docs')
|
||||
* @returns the settings for the mkdocs.yml
|
||||
*/
|
||||
export const getRepoUrlFromLocationAnnotation = (
|
||||
parsedLocationAnnotation: ParsedLocationAnnotation,
|
||||
): string | undefined => {
|
||||
scmIntegrations: ScmIntegrationRegistry,
|
||||
docsFolder: string = 'docs',
|
||||
): { repo_url?: string; edit_uri?: string } => {
|
||||
const { type: locationType, target } = parsedLocationAnnotation;
|
||||
|
||||
// Add more options from the RemoteProtocol type of parsedLocationAnnotation.type here
|
||||
// when TechDocs supports more hosts and if mkdocs can generated an Edit URL for them.
|
||||
const supportedHosts = ['github', 'gitlab'];
|
||||
if (locationType === 'url') {
|
||||
const integration = scmIntegrations.byUrl(target);
|
||||
|
||||
if (supportedHosts.includes(locationType)) {
|
||||
// Trim .git or .git/ from the end of repository url
|
||||
return target.replace(/.git\/*$/, '');
|
||||
// We only support it for github and gitlab for now as the edit_uri
|
||||
// is not properly supported for others yet.
|
||||
if (integration && ['github', 'gitlab'].includes(integration.type)) {
|
||||
// handle the case where a user manually writes url:https://github.com/backstage/backstage i.e. without /blob/...
|
||||
const { filepathtype } = gitUrlParse(target);
|
||||
if (filepathtype === '') {
|
||||
return { repo_url: target };
|
||||
}
|
||||
|
||||
const sourceFolder = integration.resolveUrl({
|
||||
url: `./${docsFolder}`,
|
||||
base: target,
|
||||
});
|
||||
return { edit_uri: integration.resolveEditUrl(sourceFolder) };
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
return {};
|
||||
};
|
||||
|
||||
class UnknownTag {
|
||||
@@ -217,7 +198,7 @@ export const validateMkdocsYaml = async (
|
||||
* Update the mkdocs.yml file before TechDocs generator uses it to generate docs site.
|
||||
*
|
||||
* List of tasks:
|
||||
* - Add repo_url if it does not exists
|
||||
* - Add repo_url or edit_uri if it does not exists
|
||||
* If mkdocs.yml has a repo_url, the generated docs site gets an Edit button on the pages by default.
|
||||
* If repo_url is missing in mkdocs.yml, we will use techdocs annotation of the entity to possibly get
|
||||
* the repository URL.
|
||||
@@ -228,12 +209,18 @@ export const validateMkdocsYaml = async (
|
||||
* @param {string} mkdocsYmlPath Absolute path to mkdocs.yml or equivalent of a docs site
|
||||
* @param {Logger} logger
|
||||
* @param {ParsedLocationAnnotation} parsedLocationAnnotation Object with location url and type
|
||||
* @param {ScmIntegrationRegistry} scmIntegrations the scmIntegration to do url transformations
|
||||
*/
|
||||
export const patchMkdocsYmlPreBuild = async (
|
||||
mkdocsYmlPath: string,
|
||||
logger: Logger,
|
||||
parsedLocationAnnotation: ParsedLocationAnnotation,
|
||||
scmIntegrations: ScmIntegrationRegistry,
|
||||
) => {
|
||||
// We only want to override the mkdocs.yml if it has actually changed. This is relevant if
|
||||
// used with a 'dir' location on the file system as this would permanently update the file.
|
||||
let didEdit = false;
|
||||
|
||||
let mkdocsYmlFileString;
|
||||
try {
|
||||
mkdocsYmlFileString = await fs.readFile(mkdocsYmlPath, 'utf8');
|
||||
@@ -260,25 +247,37 @@ export const patchMkdocsYmlPreBuild = async (
|
||||
return;
|
||||
}
|
||||
|
||||
// Add repo_url to mkdocs.yml if it is missing. This will enable the Page edit button generated by MkDocs.
|
||||
if (!('repo_url' in mkdocsYml)) {
|
||||
const repoUrl = getRepoUrlFromLocationAnnotation(parsedLocationAnnotation);
|
||||
if (repoUrl !== undefined) {
|
||||
// mkdocs.yml will not build with invalid repo_url. So, make sure it is valid.
|
||||
// TODO: this is no longer working/meaningful because annotation type is
|
||||
// now only ever "url" or "dir." Should be re-implemented!
|
||||
if (isValidRepoUrlForMkdocs(repoUrl, parsedLocationAnnotation.type)) {
|
||||
mkdocsYml.repo_url = repoUrl;
|
||||
}
|
||||
// Add edit_uri and/or repo_url to mkdocs.yml if it is missing.
|
||||
// This will enable the Page edit button generated by MkDocs.
|
||||
// If the either has been set, keep the original value
|
||||
if (!('repo_url' in mkdocsYml) && !('edit_uri' in mkdocsYml)) {
|
||||
const result = getRepoUrlFromLocationAnnotation(
|
||||
parsedLocationAnnotation,
|
||||
scmIntegrations,
|
||||
mkdocsYml.docs_dir,
|
||||
);
|
||||
|
||||
if (result.repo_url || result.edit_uri) {
|
||||
mkdocsYml.repo_url = result.repo_url;
|
||||
mkdocsYml.edit_uri = result.edit_uri;
|
||||
didEdit = true;
|
||||
|
||||
logger.info(
|
||||
`Set ${JSON.stringify(
|
||||
result,
|
||||
)}. You can disable this feature by manually setting 'repo_url' or 'edit_uri' according to the MkDocs documentation at https://www.mkdocs.org/user-guide/configuration/#repo_url`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await fs.writeFile(
|
||||
mkdocsYmlPath,
|
||||
yaml.dump(mkdocsYml, { schema: MKDOCS_SCHEMA }),
|
||||
'utf8',
|
||||
);
|
||||
if (didEdit) {
|
||||
await fs.writeFile(
|
||||
mkdocsYmlPath,
|
||||
yaml.dump(mkdocsYml, { schema: MKDOCS_SCHEMA }),
|
||||
'utf8',
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
logger.warn(
|
||||
`Could not write to ${mkdocsYmlPath} after updating it before running the generator. ${error.message}`,
|
||||
|
||||
@@ -18,6 +18,10 @@ import { ContainerRunner } from '@backstage/backend-common';
|
||||
import { Config } from '@backstage/config';
|
||||
import path from 'path';
|
||||
import { Logger } from 'winston';
|
||||
import {
|
||||
ScmIntegrationRegistry,
|
||||
ScmIntegrations,
|
||||
} from '@backstage/integration';
|
||||
import {
|
||||
addBuildTimestampMetadata,
|
||||
getMkdocsYml,
|
||||
@@ -39,29 +43,39 @@ export class TechdocsGenerator implements GeneratorBase {
|
||||
private readonly logger: Logger;
|
||||
private readonly containerRunner: ContainerRunner;
|
||||
private readonly options: GeneratorConfig;
|
||||
private readonly scmIntegrations: ScmIntegrationRegistry;
|
||||
|
||||
static async fromConfig(
|
||||
static fromConfig(
|
||||
config: Config,
|
||||
{
|
||||
containerRunner,
|
||||
logger,
|
||||
}: { containerRunner: ContainerRunner; logger: Logger },
|
||||
) {
|
||||
return new TechdocsGenerator({ logger, containerRunner, config });
|
||||
const scmIntegrations = ScmIntegrations.fromConfig(config);
|
||||
return new TechdocsGenerator({
|
||||
logger,
|
||||
containerRunner,
|
||||
config,
|
||||
scmIntegrations,
|
||||
});
|
||||
}
|
||||
|
||||
constructor({
|
||||
logger,
|
||||
containerRunner,
|
||||
config,
|
||||
scmIntegrations,
|
||||
}: {
|
||||
logger: Logger;
|
||||
containerRunner: ContainerRunner;
|
||||
config: Config;
|
||||
scmIntegrations: ScmIntegrationRegistry;
|
||||
}) {
|
||||
this.logger = logger;
|
||||
this.options = readGeneratorConfig(config, logger);
|
||||
this.containerRunner = containerRunner;
|
||||
this.scmIntegrations = scmIntegrations;
|
||||
}
|
||||
|
||||
public async run({
|
||||
@@ -74,16 +88,19 @@ export class TechdocsGenerator implements GeneratorBase {
|
||||
}: GeneratorRunOptions): Promise<void> {
|
||||
// Do some updates to mkdocs.yml before generating docs e.g. adding repo_url
|
||||
const { path: mkdocsYmlPath, content } = await getMkdocsYml(inputDir);
|
||||
|
||||
// validate the docs_dir first
|
||||
await validateMkdocsYaml(inputDir, content);
|
||||
|
||||
if (parsedLocationAnnotation) {
|
||||
await patchMkdocsYmlPreBuild(
|
||||
mkdocsYmlPath,
|
||||
childLogger,
|
||||
parsedLocationAnnotation,
|
||||
this.scmIntegrations,
|
||||
);
|
||||
}
|
||||
|
||||
await validateMkdocsYaml(inputDir, content);
|
||||
|
||||
// Directories to bind on container
|
||||
const mountDirs = {
|
||||
[inputDir]: '/input',
|
||||
|
||||
@@ -25,10 +25,10 @@ import mockFs from 'mock-fs';
|
||||
import * as os from 'os';
|
||||
import { LocalPublish } from './local';
|
||||
|
||||
const createMockEntity = (annotations = {}) => {
|
||||
const createMockEntity = (annotations = {}, lowerCase = false) => {
|
||||
return {
|
||||
apiVersion: 'version',
|
||||
kind: 'TestKind',
|
||||
kind: lowerCase ? 'testkind' : 'TestKind',
|
||||
metadata: {
|
||||
name: 'test-component-name',
|
||||
annotations: {
|
||||
@@ -65,11 +65,42 @@ describe('local publisher', () => {
|
||||
|
||||
const publisher = new LocalPublish(mockConfig, logger, testDiscovery);
|
||||
const mockEntity = createMockEntity();
|
||||
const lowerMockEntity = createMockEntity(undefined, true);
|
||||
|
||||
await publisher.publish({ entity: mockEntity, directory: tmpDir });
|
||||
|
||||
expect(await publisher.hasDocsBeenGenerated(mockEntity)).toBe(true);
|
||||
|
||||
// Lower/upper should be treated the same.
|
||||
expect(await publisher.hasDocsBeenGenerated(lowerMockEntity)).toBe(true);
|
||||
|
||||
mockFs.restore();
|
||||
});
|
||||
|
||||
it('should respect legacy casing', async () => {
|
||||
mockFs({
|
||||
[tmpDir]: {
|
||||
'index.html': '',
|
||||
},
|
||||
});
|
||||
|
||||
const mockConfig = new ConfigReader({
|
||||
techdocs: {
|
||||
legacyUseCaseSensitiveTripletPaths: true,
|
||||
},
|
||||
});
|
||||
|
||||
const publisher = new LocalPublish(mockConfig, logger, testDiscovery);
|
||||
const mockEntity = createMockEntity();
|
||||
const lowerMockEntity = createMockEntity(undefined, true);
|
||||
|
||||
await publisher.publish({ entity: mockEntity, directory: tmpDir });
|
||||
|
||||
expect(await publisher.hasDocsBeenGenerated(mockEntity)).toBe(true);
|
||||
|
||||
// Lower/upper should be treated differently.
|
||||
expect(await publisher.hasDocsBeenGenerated(lowerMockEntity)).toBe(false);
|
||||
|
||||
mockFs.restore();
|
||||
});
|
||||
|
||||
@@ -86,6 +117,13 @@ describe('local publisher', () => {
|
||||
[resolvedDir]: {
|
||||
'unsafe.html': '<html></html>',
|
||||
'unsafe.svg': '<svg></svg>',
|
||||
default: {
|
||||
testkind: {
|
||||
testname: {
|
||||
'index.html': 'found it',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
@@ -107,5 +145,38 @@ describe('local publisher', () => {
|
||||
'content-type': 'text/plain; charset=utf-8',
|
||||
});
|
||||
});
|
||||
|
||||
it('should redirect case-sensitive triplet path to lower-case', async () => {
|
||||
const response = await request(app)
|
||||
.get('/default/TestKind/TestName/index.html')
|
||||
.expect('Location', '/default/testkind/testname/index.html');
|
||||
expect(response.status).toBe(301);
|
||||
});
|
||||
|
||||
it('should resolve lower-case triplet path content eventually', async () => {
|
||||
const response = await request(app)
|
||||
.get('/default/TestKind/TestName/index.html')
|
||||
.redirects(1);
|
||||
expect(response.text).toEqual('found it');
|
||||
});
|
||||
|
||||
it('should not redirect when legacy case setting is used', async () => {
|
||||
const legacyConfig = new ConfigReader({
|
||||
techdocs: {
|
||||
legacyUseCaseSensitiveTripletPaths: true,
|
||||
},
|
||||
});
|
||||
const legacyPublisher = new LocalPublish(
|
||||
legacyConfig,
|
||||
logger,
|
||||
testDiscovery,
|
||||
);
|
||||
app = express().use(legacyPublisher.docsRouter());
|
||||
|
||||
const response = await request(app).get(
|
||||
'/default/TestKind/TestName/index.html',
|
||||
);
|
||||
expect(response.status).toBe(404);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -58,6 +58,8 @@ try {
|
||||
* called "static" at the root of techdocs-backend plugin.
|
||||
*/
|
||||
export class LocalPublish implements PublisherBase {
|
||||
private legacyPathCasing: boolean;
|
||||
|
||||
// TODO: Use a static fromConfig method to create a LocalPublish instance, similar to aws/gcs publishers.
|
||||
// Move the logic of setting staticDocsDir based on config over to fromConfig,
|
||||
// and set the value as a class parameter.
|
||||
@@ -70,6 +72,10 @@ export class LocalPublish implements PublisherBase {
|
||||
this.config = config;
|
||||
this.logger = logger;
|
||||
this.discovery = discovery;
|
||||
this.legacyPathCasing =
|
||||
config.getOptionalBoolean(
|
||||
'techdocs.legacyUseCaseSensitiveTripletPaths',
|
||||
) || false;
|
||||
}
|
||||
|
||||
async getReadiness(): Promise<ReadinessResponse> {
|
||||
@@ -81,8 +87,7 @@ export class LocalPublish implements PublisherBase {
|
||||
publish({ entity, directory }: PublishRequest): Promise<PublishResponse> {
|
||||
const entityNamespace = entity.metadata.namespace ?? 'default';
|
||||
|
||||
const publishDir = path.join(
|
||||
staticDocsDir,
|
||||
const publishDir = this.staticEntityPathJoin(
|
||||
entityNamespace,
|
||||
entity.kind,
|
||||
entity.metadata.name,
|
||||
@@ -119,8 +124,7 @@ export class LocalPublish implements PublisherBase {
|
||||
async fetchTechDocsMetadata(
|
||||
entityName: EntityName,
|
||||
): Promise<TechDocsMetadata> {
|
||||
const metadataPath = path.join(
|
||||
staticDocsDir,
|
||||
const metadataPath = this.staticEntityPathJoin(
|
||||
entityName.namespace,
|
||||
entityName.kind,
|
||||
entityName.name,
|
||||
@@ -138,23 +142,61 @@ export class LocalPublish implements PublisherBase {
|
||||
}
|
||||
|
||||
docsRouter(): express.Handler {
|
||||
return express.static(staticDocsDir, {
|
||||
// Handle content-type header the same as all other publishers.
|
||||
setHeaders: (res, filePath) => {
|
||||
const fileExtension = path.extname(filePath);
|
||||
const headers = getHeadersForFileExtension(fileExtension);
|
||||
for (const [header, value] of Object.entries(headers)) {
|
||||
res.setHeader(header, value);
|
||||
}
|
||||
},
|
||||
const router = express.Router();
|
||||
|
||||
// Redirect middleware ensuring that requests to case-sensitive entity
|
||||
// triplet paths are always sent to lower-case versions.
|
||||
router.use((req, res, next) => {
|
||||
// If legacy path casing is on, let the request immediately continue.
|
||||
if (this.legacyPathCasing) {
|
||||
return next();
|
||||
}
|
||||
|
||||
// Generate a lower-case entity triplet path.
|
||||
const [_, namespace, kind, name, ...rest] = req.path.split('/');
|
||||
|
||||
// Ignore non-triplet objects.
|
||||
if (!namespace || !kind || !name) {
|
||||
return next();
|
||||
}
|
||||
|
||||
const newPath = [
|
||||
_,
|
||||
namespace.toLowerCase(),
|
||||
kind.toLowerCase(),
|
||||
name.toLowerCase(),
|
||||
...rest,
|
||||
].join('/');
|
||||
|
||||
// If there was no change, then let express.static() handle the request.
|
||||
if (newPath === req.path) {
|
||||
return next();
|
||||
}
|
||||
|
||||
// Otherwise, redirect to the new path.
|
||||
return res.redirect(req.baseUrl + newPath, 301);
|
||||
});
|
||||
|
||||
router.use(
|
||||
express.static(staticDocsDir, {
|
||||
// Handle content-type header the same as all other publishers.
|
||||
setHeaders: (res, filePath) => {
|
||||
const fileExtension = path.extname(filePath);
|
||||
const headers = getHeadersForFileExtension(fileExtension);
|
||||
for (const [header, value] of Object.entries(headers)) {
|
||||
res.setHeader(header, value);
|
||||
}
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
return router;
|
||||
}
|
||||
|
||||
async hasDocsBeenGenerated(entity: Entity): Promise<boolean> {
|
||||
const namespace = entity.metadata.namespace ?? 'default';
|
||||
|
||||
const indexHtmlPath = path.join(
|
||||
staticDocsDir,
|
||||
const indexHtmlPath = this.staticEntityPathJoin(
|
||||
namespace,
|
||||
entity.kind,
|
||||
entity.metadata.name,
|
||||
@@ -210,4 +252,22 @@ export class LocalPublish implements PublisherBase {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility wrapper around path.join(), used to control legacy case logic.
|
||||
*/
|
||||
protected staticEntityPathJoin(...allParts: string[]): string {
|
||||
if (this.legacyPathCasing) {
|
||||
const [namespace, kind, name, ...parts] = allParts;
|
||||
return path.join(staticDocsDir, namespace, kind, name, ...parts);
|
||||
}
|
||||
const [namespace, kind, name, ...parts] = allParts;
|
||||
return path.join(
|
||||
staticDocsDir,
|
||||
namespace.toLowerCase(),
|
||||
kind.toLowerCase(),
|
||||
name.toLowerCase(),
|
||||
...parts,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
extends: [require.resolve('@backstage/cli/config/eslint')],
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
# @backstage/plugin-allure
|
||||
|
||||
## 0.1.1
|
||||
|
||||
### Patch Changes
|
||||
|
||||
- Updated dependencies
|
||||
- @backstage/plugin-catalog-react@0.4.5
|
||||
- @backstage/core-components@0.4.0
|
||||
- @backstage/catalog-model@0.9.1
|
||||
@@ -0,0 +1,47 @@
|
||||
# [Allure](https://docs.qameta.io/allure/)
|
||||
|
||||
Welcome to the Backstage Allure plugin. This plugin add an entity service page to display Allure test reports related to the service.
|
||||
|
||||
## Install
|
||||
|
||||
Run the below command from the `app` package directory.
|
||||
|
||||
```shell
|
||||
yarn add @backstage/plugin-allure
|
||||
```
|
||||
|
||||
alternatively, you can execute below command from the root directory of your Backstage app.
|
||||
|
||||
```shell
|
||||
yarn workspace app add @backstage/plugin-allure
|
||||
```
|
||||
|
||||
## Configure
|
||||
|
||||
### Configure Allure service
|
||||
|
||||
Add below configuration in the `app-config.yaml`.
|
||||
|
||||
```yaml
|
||||
allure:
|
||||
baseUrl: <ALLURE_SERVICE_BASE_URL> # Example: https://allure.my-company.net or when running allure locally, http://localhost:5050/allure-docker-service
|
||||
```
|
||||
|
||||
### Setup entity service page
|
||||
|
||||
Add `EntityAllureReportContent` in the `EntityPage.tsx` like below:
|
||||
|
||||
```diff
|
||||
+ import { EntityAllureReportContent } from '@backstage/plugin-allure';
|
||||
|
||||
...
|
||||
|
||||
const serviceEntityPage = (
|
||||
<EntityLayoutWrapper>
|
||||
...
|
||||
+ <EntityLayout.Route path="/allure" title="Allure Report">
|
||||
+ <EntityAllureReportContent />
|
||||
+ </EntityLayout.Route>
|
||||
</EntityLayoutWrapper>
|
||||
);
|
||||
```
|
||||
@@ -0,0 +1,27 @@
|
||||
## API Report File for "@backstage/plugin-allure"
|
||||
|
||||
> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/).
|
||||
|
||||
```ts
|
||||
/// <reference types="react" />
|
||||
|
||||
import { BackstagePlugin } from '@backstage/core-plugin-api';
|
||||
import { RouteRef } from '@backstage/core-plugin-api';
|
||||
|
||||
// Warning: (ae-missing-release-tag) "allurePlugin" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export const allurePlugin: BackstagePlugin<
|
||||
{
|
||||
root: RouteRef<undefined>;
|
||||
},
|
||||
{}
|
||||
>;
|
||||
|
||||
// Warning: (ae-missing-release-tag) "EntityAllureReportContent" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal)
|
||||
//
|
||||
// @public (undocumented)
|
||||
export const EntityAllureReportContent: () => JSX.Element;
|
||||
|
||||
// (No @packageDocumentation comment for this package)
|
||||
```
|
||||
@@ -0,0 +1,11 @@
|
||||
apiVersion: backstage.io/v1alpha1
|
||||
kind: Component
|
||||
metadata:
|
||||
name: allure-example-service
|
||||
description: Allure Example Service
|
||||
annotations:
|
||||
qameta.io/allure-project: sample
|
||||
spec:
|
||||
type: service
|
||||
lifecycle: experimental
|
||||
owner: team-a
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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 React from 'react';
|
||||
import { createDevApp } from '@backstage/dev-utils';
|
||||
import { EntityAllureReportContent } from '../src/plugin';
|
||||
import { Content, Header, Page } from '@backstage/core-components';
|
||||
import { EntityProvider } from '@backstage/plugin-catalog-react';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
|
||||
import exampleEntity from './example-entity.yaml';
|
||||
import { allureApiRef } from '../src/api';
|
||||
|
||||
createDevApp()
|
||||
.registerApi({
|
||||
api: allureApiRef,
|
||||
deps: {},
|
||||
factory: () =>
|
||||
({
|
||||
async getReportUrl(projectId: string) {
|
||||
return Promise.resolve(
|
||||
// Follow the instructions from https://github.com/fescobar/allure-docker-service-ui
|
||||
// to setup Allure service locally
|
||||
`http://localhost:5050/allure-docker-service/projects/${projectId}/reports/latest/index.html`,
|
||||
);
|
||||
},
|
||||
} as unknown as typeof allureApiRef.T),
|
||||
})
|
||||
.addPage({
|
||||
element: (
|
||||
<Page themeId="home">
|
||||
<Header title="Allure Report" />
|
||||
<Content>
|
||||
<EntityProvider entity={exampleEntity as any as Entity}>
|
||||
<EntityAllureReportContent />
|
||||
</EntityProvider>
|
||||
</Content>
|
||||
</Page>
|
||||
),
|
||||
title: 'Allure Report',
|
||||
path: '/allure',
|
||||
})
|
||||
.render();
|
||||
@@ -0,0 +1,69 @@
|
||||
{
|
||||
"name": "@backstage/plugin-allure",
|
||||
"version": "0.1.1",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
"private": false,
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"main": "dist/index.esm.js",
|
||||
"types": "dist/index.d.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "backstage-cli plugin:build",
|
||||
"start": "backstage-cli plugin:serve",
|
||||
"lint": "backstage-cli lint",
|
||||
"test": "backstage-cli test",
|
||||
"diff": "backstage-cli plugin:diff",
|
||||
"prepack": "backstage-cli prepack",
|
||||
"postpack": "backstage-cli postpack",
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/catalog-model": "^0.9.1",
|
||||
"@backstage/core-components": "^0.4.0",
|
||||
"@backstage/core-plugin-api": "^0.1.6",
|
||||
"@backstage/plugin-catalog-react": "^0.4.5",
|
||||
"@backstage/theme": "^0.2.10",
|
||||
"@material-ui/core": "^4.12.2",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@material-ui/lab": "4.0.0-alpha.45",
|
||||
"react": "^16.13.1",
|
||||
"react-dom": "^16.13.1",
|
||||
"react-router-dom": "6.0.0-beta.0",
|
||||
"react-use": "^17.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.7.10",
|
||||
"@backstage/core-app-api": "^0.1.11",
|
||||
"@backstage/dev-utils": "^0.2.8",
|
||||
"@backstage/test-utils": "^0.1.17",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
"@testing-library/react": "^11.2.5",
|
||||
"@testing-library/user-event": "^13.1.8",
|
||||
"@types/jest": "^26.0.7",
|
||||
"@types/node": "^14.14.32",
|
||||
"cross-fetch": "^3.0.6",
|
||||
"msw": "^0.29.0"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"configSchema": {
|
||||
"$schema": "https://backstage.io/schema/config-v1",
|
||||
"title": "@backstage/allure",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"allure": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"baseUrl": {
|
||||
"type": "string",
|
||||
"visibility": "frontend"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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 { createApiRef } from '@backstage/core-plugin-api';
|
||||
|
||||
export type AllureApi = {
|
||||
getReportUrl(projectId: string): Promise<string>;
|
||||
};
|
||||
|
||||
export const allureApiRef = createApiRef<AllureApi>({
|
||||
id: 'allure-api',
|
||||
});
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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 { ConfigApi } from '@backstage/core-plugin-api';
|
||||
import { AllureApi } from './AllureApi';
|
||||
|
||||
export class AllureApiClient implements AllureApi {
|
||||
readonly configApi: ConfigApi;
|
||||
|
||||
constructor(options: { configApi: ConfigApi }) {
|
||||
this.configApi = options.configApi;
|
||||
}
|
||||
|
||||
async getReportUrl(projectId: string): Promise<string> {
|
||||
const baseUrl = this.configApi.getString('allure.baseUrl');
|
||||
return `${baseUrl}/projects/${projectId}/reports/latest/index.html`;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
export { allureApiRef } from './AllureApi';
|
||||
export { AllureApiClient } from './AllureApiClient';
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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 React from 'react';
|
||||
import { AllureReportComponent } from './AllureReportComponent';
|
||||
import { ThemeProvider } from '@material-ui/core';
|
||||
import { lightTheme } from '@backstage/theme';
|
||||
import { rest } from 'msw';
|
||||
import { setupServer } from 'msw/node';
|
||||
import { msw, renderInTestApp } from '@backstage/test-utils';
|
||||
|
||||
describe('ExampleComponent', () => {
|
||||
const server = setupServer();
|
||||
// Enable sane handlers for network requests
|
||||
msw.setupDefaultHandlers(server);
|
||||
|
||||
// setup mock response
|
||||
beforeEach(() => {
|
||||
server.use(
|
||||
rest.get('/*', (_, res, ctx) => res(ctx.status(200), ctx.json({}))),
|
||||
);
|
||||
});
|
||||
|
||||
it('should render', async () => {
|
||||
const rendered = await renderInTestApp(
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<AllureReportComponent />
|
||||
</ThemeProvider>,
|
||||
);
|
||||
expect(rendered.getByText('Missing Annotation')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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 React from 'react';
|
||||
import { useApi } from '@backstage/core-plugin-api';
|
||||
import { allureApiRef } from '../../api';
|
||||
import { useEntity } from '@backstage/plugin-catalog-react';
|
||||
import {
|
||||
ALLURE_PROJECT_ID_ANNOTATION,
|
||||
isAllureReportAvailable,
|
||||
getAllureProjectId,
|
||||
} from '../annotationHelpers';
|
||||
import {
|
||||
MissingAnnotationEmptyState,
|
||||
Progress,
|
||||
} from '@backstage/core-components';
|
||||
import { useAsync } from 'react-use';
|
||||
import { Entity } from '@backstage/catalog-model';
|
||||
|
||||
const AllureReport = (props: { entity: Entity }) => {
|
||||
const allureApi = useApi(allureApiRef);
|
||||
|
||||
const allureProjectId = getAllureProjectId(props.entity);
|
||||
const { value, loading } = useAsync(async () => {
|
||||
const url = await allureApi.getReportUrl(allureProjectId);
|
||||
return url;
|
||||
});
|
||||
|
||||
if (loading) {
|
||||
return <Progress />;
|
||||
}
|
||||
return (
|
||||
<iframe
|
||||
style={{
|
||||
display: 'table',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
}}
|
||||
title="Allure Report"
|
||||
src={value}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export const AllureReportComponent = () => {
|
||||
const { entity } = useEntity();
|
||||
const isReportAvailable = entity && isAllureReportAvailable(entity);
|
||||
|
||||
if (isReportAvailable) return <AllureReport entity={entity} />;
|
||||
return (
|
||||
<MissingAnnotationEmptyState annotation={ALLURE_PROJECT_ID_ANNOTATION} />
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
export { AllureReportComponent } from './AllureReportComponent';
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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 { Entity } from '@backstage/catalog-model';
|
||||
|
||||
export const ALLURE_PROJECT_ID_ANNOTATION = 'qameta.io/allure-project';
|
||||
|
||||
export const isAllureReportAvailable = (entity: Entity) =>
|
||||
Boolean(entity.metadata.annotations?.[ALLURE_PROJECT_ID_ANNOTATION]);
|
||||
|
||||
export const getAllureProjectId = (entity: Entity) => {
|
||||
return entity?.metadata.annotations?.[ALLURE_PROJECT_ID_ANNOTATION] ?? '';
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
export { allurePlugin, EntityAllureReportContent } from './plugin';
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* 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 { allurePlugin } from './plugin';
|
||||
|
||||
describe('allure', () => {
|
||||
it('should export plugin', () => {
|
||||
expect(allurePlugin).toBeDefined();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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 {
|
||||
configApiRef,
|
||||
createApiFactory,
|
||||
createPlugin,
|
||||
createRoutableExtension,
|
||||
createRouteRef,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import { AllureApiClient, allureApiRef } from './api';
|
||||
|
||||
export const allureRouteRef = createRouteRef({
|
||||
title: 'allure-report',
|
||||
});
|
||||
|
||||
export const allurePlugin = createPlugin({
|
||||
id: 'allure',
|
||||
apis: [
|
||||
createApiFactory({
|
||||
api: allureApiRef,
|
||||
deps: {
|
||||
configApi: configApiRef,
|
||||
},
|
||||
factory: ({ configApi }) => new AllureApiClient({ configApi }),
|
||||
}),
|
||||
],
|
||||
routes: {
|
||||
root: allureRouteRef,
|
||||
},
|
||||
});
|
||||
|
||||
export const EntityAllureReportContent = allurePlugin.provide(
|
||||
createRoutableExtension({
|
||||
component: () =>
|
||||
import('./components/AllureReportComponent').then(
|
||||
m => m.AllureReportComponent,
|
||||
),
|
||||
mountPoint: allureRouteRef,
|
||||
}),
|
||||
);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user