diff --git a/.changeset/angry-crabs-relate.md b/.changeset/angry-crabs-relate.md new file mode 100644 index 0000000000..678093c1ac --- /dev/null +++ b/.changeset/angry-crabs-relate.md @@ -0,0 +1,9 @@ +--- +'@backstage/plugin-catalog-backend': minor +--- + +Add ability to dry run adding a new location to the catalog API. + +The location is now added in a transaction and afterwards rolled back. +This allows users to dry run this operation to see if there entity has issues. +This is probably done by automated tools in the CI/CD pipeline. diff --git a/.changeset/cost-insights-kind-trees-occur.md b/.changeset/cost-insights-kind-trees-occur.md new file mode 100644 index 0000000000..600cc3b3b4 --- /dev/null +++ b/.changeset/cost-insights-kind-trees-occur.md @@ -0,0 +1,5 @@ +--- +'@backstage/plugin-cost-insights': patch +--- + +migrate type utilities out of type definition files diff --git a/.changeset/fresh-maps-complain.md b/.changeset/fresh-maps-complain.md new file mode 100644 index 0000000000..ef2021df37 --- /dev/null +++ b/.changeset/fresh-maps-complain.md @@ -0,0 +1,6 @@ +--- +'@backstage/plugin-catalog': minor +'@backstage/plugin-register-component': patch +--- + +Locations registered through the catalog client now default to the 'url' type. The type selection dropdown in the register-component form has been removed. diff --git a/.changeset/nervous-drinks-notice.md b/.changeset/nervous-drinks-notice.md new file mode 100644 index 0000000000..7c6af21f64 --- /dev/null +++ b/.changeset/nervous-drinks-notice.md @@ -0,0 +1,8 @@ +--- +'@backstage/plugin-catalog': minor +'@backstage/plugin-register-component': minor +--- + +Add a validate button to the register-component page + +This allows the user to validate his location before adding it. diff --git a/.changeset/ninety-ads-dance.md b/.changeset/ninety-ads-dance.md new file mode 100644 index 0000000000..a03298fd7f --- /dev/null +++ b/.changeset/ninety-ads-dance.md @@ -0,0 +1,5 @@ +--- +'@backstage/catalog-model': patch +--- + +Fix documentation and validation message for tags diff --git a/.changeset/popular-ghosts-remain.md b/.changeset/popular-ghosts-remain.md new file mode 100644 index 0000000000..4a170c7618 --- /dev/null +++ b/.changeset/popular-ghosts-remain.md @@ -0,0 +1,5 @@ +--- +'@backstage/core': patch +--- + +Proper render boolean values on StructuredMetadataTable component diff --git a/.changeset/swift-emus-mate.md b/.changeset/swift-emus-mate.md new file mode 100644 index 0000000000..1903742115 --- /dev/null +++ b/.changeset/swift-emus-mate.md @@ -0,0 +1,5 @@ +--- +'@backstage/core-api': minor +--- + +Refactored the FeatureFlagsApi to make it easier to re-implement. Existing usage of particularly getUserFlags can be replaced with isActive() or save(). diff --git a/.github/styles/vocab.txt b/.github/styles/vocab.txt index 0cbd0ffa05..1f9e083b51 100644 --- a/.github/styles/vocab.txt +++ b/.github/styles/vocab.txt @@ -91,6 +91,7 @@ learnings lerna Lerna magiclink +Maintainership mailto Malus md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d00f304b58..75c6a13cd2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -67,7 +67,7 @@ jobs: continue-on-error: true - name: verify doc links - run: node docs/verify-links.js + run: node scripts/verify-links.js - name: prettier run: yarn prettier:check diff --git a/GOVERNANCE.md b/GOVERNANCE.md new file mode 100644 index 0000000000..19bbb3bcb9 --- /dev/null +++ b/GOVERNANCE.md @@ -0,0 +1,52 @@ +# Backstage Governance + +This document defines project governance for the project. + +## Maintainers + +Backstage Maintainers have write access to the Backstage GitHub repository https://github.com/backstage/backstage. The current maintainers can be found in [MAINTAINERS](MAINTAINERS.md). + +This privilege is granted with some expectation of responsibility: maintainers are people who care about the Backstage project and want to help it grow and improve. A maintainer is not just someone who can make changes, but someone who has demonstrated his or her ability to collaborate with the team, get the most knowledgeable people to review code, contribute high-quality code, and follow through to fix issues (in code or tests). + +A maintainer is a contributor to the Backstage project's success and a citizen helping the project succeed. + +## Becoming a Maintainer + +To become a maintainer you need to demonstrate the following: + +- commitment to the project + - participate in discussions, contributions, code reviews for 3 months or more, + - perform code reviews for 10 non-trivial pull requests, + - contribute 10 non-trivial pull requests and have them merged into master, +- ability to write good code, +- ability to collaborate with the team, +- understanding of how the team works (policies, processes for testing and code review, etc), +- understanding of the project's code base and coding style. + +## Changes in Maintainership + +A new maintainer must be proposed by an existing maintainer by opening an issue (with title `Maintainer Nomination`) to the Backstage GitHub repository (https://github.com/backstage/backstage) containing the following information: + +- nominee's first and last name, +- nominee's email address and GitHub user name, +- an explanation of why the nominee should be a maintainer, +- a list of links to non-trivial pull requests (top 10) authored by the nominee. + +Maintainers can be removed by a 2/3 majority vote. + +## Approving PRs + +PRs may be merged after receiving at least one approval from a maintainer. + +## GitHub Project Administration + +Maintainers will be added to the collaborators list of the Backstage repository with "Write" access. + +## Changes in Governance + +All changes in Governance require a 2/3 majority vote. + +## Other Changes + +Unless specified above, all other changes to the project require a 2/3 majority vote. +Additionally, any maintainer may request that any change require a 2/3 majority vote. diff --git a/MAINTAINERS.md b/MAINTAINERS.md new file mode 100644 index 0000000000..33437692c9 --- /dev/null +++ b/MAINTAINERS.md @@ -0,0 +1,21 @@ +# Maintainers + +- See [CONTRIBUTING.md](CONTRIBUTING.md) for general contribution guidelines. + +## Current Maintainers 🏓 + +- Stefan Ålund - Spotify (GitHub: @stefanalund, Discord: @stalund) +- Patrik Oldsberg - Spotify (GitHub: @Rugvip, Discord: @Rugvip) +- Fredrik Adelöw - Spotify (GitHub: @freben, Discord: @freben) +- Ben Lambert - Spotify (GitHub: @benjdlambert, Discord: @blam) + +## Plugin maintainers 🧩 + +Teams and individuals that maintain a plugin (or another non-core module of the code) can get write access to that part of the repo using CODEOWNERS. + +## Hall of Fame 👏 + +People that have made significant contributions to the project and earned write access: + +- Andrew Thauer - Wealthsimple (GitHub: @andrewthauer) +- Oliver Sand - SDA SE (GitHub: @Fox32) diff --git a/OWNERS.md b/OWNERS.md deleted file mode 100644 index 1e7fe5b774..0000000000 --- a/OWNERS.md +++ /dev/null @@ -1,19 +0,0 @@ -# Owners - -- See [CONTRIBUTING.md](CONTRIBUTING.md) for general contribution guidelines. - -## Maintainers 🏓 - -- Patrik Oldsberg (@Rugvip, Spotify) -- Fredrik Adelöw (@freben, Spotify) -- Raghunandan Balachandran (@soapraj, Spotify) -- Ben Lambert (@benjdlambert, Spotify) -- Marcus Eide (@marcuseide, Spotify) -- Niklas Ek (@nikek, Spotify) -- Stefan Ålund (@stefanalund, Spotify) -- Kat Zhou (@katz95, Spotify) - -## Hall of Fame 👏 - -- Andrew Thauer (@andrewthauer, Wealthsimple) -- Oliver Sand (@Fox32, SDA-SE) diff --git a/docs/features/software-catalog/descriptor-format.md b/docs/features/software-catalog/descriptor-format.md index 62ac9da8d8..a330ef196a 100644 --- a/docs/features/software-catalog/descriptor-format.md +++ b/docs/features/software-catalog/descriptor-format.md @@ -256,8 +256,8 @@ component, like `java` or `go`. This field is optional, and currently has no special semantics. -Each tag must be sequences of `[a-zA-Z0-9]` separated by `-`, at most 63 -characters in total. +Each tag must be sequences of `[a-z0-9]` separated by `-`, at most 63 characters +in total. ## Kind: Component @@ -418,7 +418,7 @@ of the `metadata.name` field. ### `metadata.tags` [optional] A list of strings that can be associated with the template, e.g. -`['Recommended', 'React']`. +`['recommended', 'react']`. This list will also be used in the frontend to display to the user so you can potentially search and group templates by these tags. diff --git a/docs/features/software-catalog/installation.md b/docs/features/software-catalog/installation.md index 26990033aa..1e037461b4 100644 --- a/docs/features/software-catalog/installation.md +++ b/docs/features/software-catalog/installation.md @@ -122,6 +122,7 @@ export default async function createPlugin({ entitiesCatalog, locationsCatalog, locationReader, + db, logger, ); diff --git a/docs/features/software-catalog/well-known-annotations.md b/docs/features/software-catalog/well-known-annotations.md index 40a4ca3ea1..47b1d5885d 100644 --- a/docs/features/software-catalog/well-known-annotations.md +++ b/docs/features/software-catalog/well-known-annotations.md @@ -173,6 +173,22 @@ The value of these annotations are the corresponding attributes that were found when ingestion the entity from LDAP. Not all of them may be present, depending on what attributes that the server presented at ingestion time. +### sonarqube.org/project-key + +```yaml +# Example: +metadata: + annotations: + sonarqube.org/project-key: pump-station +``` + +The value of this annotation is the project key of a +[SonarQube](https://sonarqube.org) or [SonarCloud](https://sonarcloud.io) +project within your organization. + +Specifying this annotation may enable SonarQube related features in Backstage +for that entity. + ## Deprecated Annotations The following annotations are deprecated, and only listed here to aid in diff --git a/docs/overview/adopting.md b/docs/overview/adopting.md index 422adfb288..0d404d5cb4 100644 --- a/docs/overview/adopting.md +++ b/docs/overview/adopting.md @@ -42,7 +42,7 @@ infra/platform teams and end-users: ![pop](../assets/pop.png) While anyone at your company can contribute to the platform, the vast majority -of work will be done by teams that also has internal engineers as their +of work will be done by teams that also have internal engineers as their customers. The central team should treat these _contributing teams_ as customers of the platform as well. @@ -51,9 +51,9 @@ customers. This is done primarily by building [plugins](../plugins/index.md). Contributing teams should themselves treat their plugins as, or part of, the products they maintain. -> Case study: Inside Spotify we have a team that owns our CI platform. They -> don't only maintain the pipelines and build servers, but also expose their -> product in Backstage through a plugin. Since they also +> Case study: Inside Spotify we have a team that owns our CI platform. They not +> only maintain the pipelines and build servers, but also expose their product +> in Backstage through a plugin. Since they also > [maintain their own API](../plugins/call-existing-api.md), they can improve > their product by iterating on API and UI in lockstep. Because the plugin > follows our [platform design guidelines](../dls/design.md) their customers get @@ -123,14 +123,14 @@ successful impact on your software development process: engineer is someone that is able to contribute to different domains of engineering. Teams with T-shaped people have fewer bottlenecks and can therefore deliver more consistently. Backstage makes it easier to be T-shaped - since tools and infrastructure is consistent between domains, and information + since tools and infrastructure are consistent between domains, and information is available centrally. - **eNPS** Surveys asking about how productive people feel, how easy it is to find information and overall satisfaction with internal tools. - **Fragmentation** _(Experimental)_ Backstage - [Software Templates](../features/software-templates/index.md) helps drive + [Software Templates](../features/software-templates/index.md) help drive standardization in your software ecosystem. By measuring the variance in technology between different software components it is possible to get a sense of the overall fragmentation in your ecosystem. Examples could include: diff --git a/docs/overview/architecture-overview.md b/docs/overview/architecture-overview.md index ee2d3c6097..6f3c6b1a71 100644 --- a/docs/overview/architecture-overview.md +++ b/docs/overview/architecture-overview.md @@ -39,7 +39,7 @@ this. ![The architecture of a basic Backstage application](../assets/architecture-overview/backstage-typical-architecture.png) -## The UI +## User Interface The UI is a thin, client-side wrapper around a set of plugins. It provides some core UI components and libraries for shared activities such as config diff --git a/docs/reference/createPlugin-feature-flags.md b/docs/reference/createPlugin-feature-flags.md index ebb8a6b503..bcea80e26b 100644 --- a/docs/reference/createPlugin-feature-flags.md +++ b/docs/reference/createPlugin-feature-flags.md @@ -8,12 +8,6 @@ The `featureFlags` object passed to the `register` function makes it possible for plugins to register Feature Flags in Backstage for users to opt into. You can use this to split out logic in your code for manual A/B testing, etc. -```typescript -export type FeatureFlagsHooks = { - register(name: FeatureFlagName): void; -}; -``` - Here's a code sample: ```typescript @@ -21,8 +15,7 @@ import { createPlugin } from '@backstage/core'; export default createPlugin({ id: 'welcome', - register({ router, featureFlags }) { - // router.registerRoute('/', Component); + register({ featureFlags }) { featureFlags.register('enable-example-feature'); }, }); @@ -30,41 +23,22 @@ export default createPlugin({ ## Using with useApi -To use it, you'll first need to register the `FeatureFlags` API via -`ApiRegistry` in your `apis.ts` in your App: - -```tsx -import { - ApiHolder, - ApiRegistry, - featureFlagsApiRef, - FeatureFlags, -} from '@backstage/core'; - -const builder = ApiRegistry.builder(); -builder.add(featureFlagsApiRef, new FeatureFlags()); - -export default builder.build() as ApiHolder; -``` - -Then, later, you can directly use it via `useApi`: +To inspect the state of a feature flag inside your plugin, you can use the +`FeatureFlagsApi`, accessed via the `featureFlagsApiRef`. For example: ```tsx import React, { FC } from 'react'; import { Button } from '@material-ui/core'; -import { FeatureFlagState, featureFlagsApiRef, useApi } from '@backstage/core'; +import { featureFlagsApiRef, useApi } from '@backstage/core'; -const ExampleButton: FC<{}> = () => { - const flags = useApi(featureFlagsApiRef).getFlags(); - - const handleClick = () => { - flags.set('enable-example-feature', FeatureFlagState.On); - }; +const ExamplePage = () => { + const featureFlags = useApi(featureFlagsApiRef); return ( - +
+ + { featureFlags.isActive('enable-example-feature') && } +
); }; ``` diff --git a/docs/verify-links.js b/docs/verify-links.js deleted file mode 100755 index 9bf8a67ad8..0000000000 --- a/docs/verify-links.js +++ /dev/null @@ -1,131 +0,0 @@ -#!/usr/bin/env node -/* - * Copyright 2020 Spotify AB - * - * 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. - */ - -const { resolve: resolvePath, dirname } = require('path'); -const fs = require('fs-extra'); -const recursive = require('recursive-readdir'); - -const projectRoot = resolvePath(__dirname, '..'); - -async function verifyUrl(basePath, url) { - // Avoid having absolute URL links within docs/, so that links work on the site - if ( - url.match( - /https:\/\/github.com\/spotify\/backstage\/(tree|blob)\/master\/docs\//, - ) && - basePath.match(/^(?:docs|microsite)\//) - ) { - return { url, basePath, problem: 'absolute' }; - } - - url = url.replace(/#.*$/, ''); - url = url.replace( - /https:\/\/github.com\/spotify\/backstage\/(tree|blob)\/master/, - '', - ); - if (!url) { - return; - } - - // Only verify existence of local files for now, so skip anything with a schema - if (url.match(/[a-z]+:/)) { - return; - } - - let path = ''; - - if (url.startsWith('/')) { - if (url.startsWith('/docs/') && basePath.match(/^(?:docs|microsite)\//)) { - return { url, basePath, problem: 'not-relative' }; - } - - const staticPath = resolvePath(projectRoot, 'microsite/static', `.${url}`); - if (await fs.pathExists(staticPath)) { - return; - } - - path = resolvePath(projectRoot, `.${url}`); - } else { - path = resolvePath(dirname(resolvePath(projectRoot, basePath)), url); - } - - const exists = await fs.pathExists(path); - if (!exists) { - return { url, basePath, problem: 'missing' }; - } - - return; -} - -async function verifyFile(filePath) { - const content = await fs.readFile(filePath, 'utf8'); - const mdLinks = content.match(/\[.+?\]\(.+?\)/g) || []; - const badUrls = []; - - for (const mdLink of mdLinks) { - const url = mdLink.match(/\[.+\]\((.+)\)/)[1].trim(); - const badUrl = await verifyUrl(filePath, url); - if (badUrl) { - badUrls.push(badUrl); - } - } - - return badUrls; -} - -async function main() { - process.chdir(projectRoot); - - const files = await recursive('.', [ - 'node_modules', - 'dist', - 'bin', - 'microsite', - ]); - const mdFiles = files.filter(f => f.endsWith('.md')); - const badUrls = []; - - for (const mdFile of mdFiles) { - const badFileUrls = await verifyFile(mdFile); - badUrls.push(...badFileUrls); - } - - if (badUrls.length) { - console.log(`Found ${badUrls.length} bad links within repo`); - for (const { url, basePath, problem } of badUrls) { - if (problem === 'missing') { - console.error( - `Unable to reach ${url} from root or microsite/static/, linked from ${basePath}`, - ); - } else if (problem === 'not-relative') { - console.error('Links to /docs/ must be relative'); - console.error(` From: ${basePath}`); - console.error(` To: ${url}`); - } else if (problem === 'absolute') { - console.error(`Link to docs/ should be replaced by a relative URL`); - console.error(` From: ${basePath}`); - console.error(` To: ${url}`); - } - } - process.exit(1); - } -} - -main().catch(error => { - console.error(error.stack); - process.exit(1); -}); diff --git a/microsite/blog/2020-05-22-phase-2-service-catalog.md b/microsite/blog/2020-05-22-phase-2-service-catalog.md index 682965d2dd..5c988c03aa 100644 --- a/microsite/blog/2020-05-22-phase-2-service-catalog.md +++ b/microsite/blog/2020-05-22-phase-2-service-catalog.md @@ -27,7 +27,7 @@ Quote from [Platform Nuts & Bolts: Extendable Data Models](https://www.kislayver Entities, or what we refer to as “components” in Backstage, represent all software, including services, websites, libraries, data pipelines, and so forth. The focus of Phase 2 will be on adding an entity model in Backstage that makes it easy for engineers to create and manage the software components they own. -With the ability to create a plethora of components in Backstage, how does one keep track of all the software in the ecosystem? Therein lies the highlight feature of Phase 2: the [Service Catalog](https://github.com/spotify/backstage/milestone/4). The service catalog — or software catalog — is a centralized system that keeps track of ownership and metadata about all software in your ecosystem. The catalog is built around the concept of [metadata yaml files](/docs/architecture-decisions/adr002-default-catalog-file-format.md) stored together with the code, which are then harvested and visualized in Backstage. +With the ability to create a plethora of components in Backstage, how does one keep track of all the software in the ecosystem? Therein lies the highlight feature of Phase 2: the [Service Catalog](https://github.com/spotify/backstage/milestone/4). The service catalog — or software catalog — is a centralized system that keeps track of ownership and metadata about all software in your ecosystem. The catalog is built around the concept of [metadata yaml files](/docs/architecture-decisions/adrs-adr002) stored together with the code, which are then harvested and visualized in Backstage. ![img](assets/20-05-20/Service_Catalog_MVP.png) diff --git a/microsite/blog/2020-06-22-backstage-service-catalog-alpha.md b/microsite/blog/2020-06-22-backstage-service-catalog-alpha.md index 0928b8769b..167cc753f8 100644 --- a/microsite/blog/2020-06-22-backstage-service-catalog-alpha.md +++ b/microsite/blog/2020-06-22-backstage-service-catalog-alpha.md @@ -24,7 +24,7 @@ With these insights we decided to re-focus our efforts towards the most requeste ## What is the service catalog? -The Backstage Service Catalog — actually, a software catalog, since it includes more than just services — is a centralized system that keeps track of ownership and metadata for all the software in your ecosystem (services, websites, libraries, data pipelines, etc). The catalog is built around the concept of [metadata yaml files](/docs/architecture-decisions/adr002-default-catalog-file-format.md#format) stored together with the code, which are then harvested and visualized in Backstage. +The Backstage Service Catalog — actually, a software catalog, since it includes more than just services — is a centralized system that keeps track of ownership and metadata for all the software in your ecosystem (services, websites, libraries, data pipelines, etc). The catalog is built around the concept of [metadata yaml files](/docs/architecture-decisions/adrs-adr002) stored together with the code, which are then harvested and visualized in Backstage. This was our pitch for the virtues of a service catalog when we first [announced](https://backstage.io/blog/2020/05/22/phase-2-service-catalog) it as part of Phase 2: diff --git a/microsite/blog/2020-07-01-how-to-enable-authentication-in-backstage-using-passport.md b/microsite/blog/2020-07-01-how-to-enable-authentication-in-backstage-using-passport.md index ccdd8b34c6..b2a944bf26 100644 --- a/microsite/blog/2020-07-01-how-to-enable-authentication-in-backstage-using-passport.md +++ b/microsite/blog/2020-07-01-how-to-enable-authentication-in-backstage-using-passport.md @@ -39,7 +39,7 @@ Getting started is really straightforward, and can be broadly broken down into f 4. Add the provider to the backend. 5. Add a frontend Auth Utility API. -For full details, take a look at our [“Adding authentication providers” documentation](/docs/auth/add-auth-provider.md) and at the [excellent documentation](http://www.passportjs.org/docs/) provided by Passport. +For full details, take a look at our [“Adding authentication providers” documentation](/docs/auth/add-auth-provider) and at the [excellent documentation](http://www.passportjs.org/docs/) provided by Passport. ## Interested in contributing to the next steps for authentication? diff --git a/microsite/core/Footer.js b/microsite/core/Footer.js index 1bebf2f399..d41b15b74c 100644 --- a/microsite/core/Footer.js +++ b/microsite/core/Footer.js @@ -17,19 +17,6 @@ const React = require('react'); class Footer extends React.Component { - docUrl(doc, language) { - const baseUrl = this.props.config.baseUrl; - const docsUrl = this.props.config.docsUrl; - const docsPart = `${docsUrl ? `${docsUrl}/` : ''}`; - const langPart = `${language ? `${language}/` : ''}`; - return `${baseUrl}${docsPart}${langPart}${doc}`; - } - - pageUrl(doc, language) { - const baseUrl = this.props.config.baseUrl; - return baseUrl + (language ? `${language}/` : '') + doc; - } - render() { return (