Merge remote-tracking branch 'origin/master' into erikengervall/plugin-release-manager-as-a-service

This commit is contained in:
Erik Engervall
2021-04-20 21:58:58 +02:00
23 changed files with 513 additions and 69 deletions
+7
View File
@@ -0,0 +1,7 @@
---
'@backstage/plugin-techdocs': minor
---
Add feedback link icon in Techdocs Reader that directs to GitLab or GitHub repo issue page with pre-filled title and source link.
For link to appear, requires `repo_url` and `edit_uri` to be filled in mkdocs.yml, as per https://www.mkdocs.org/user-guide/configuration. An `edit_uri` will need to be specified for self-hosted GitLab/GitHub instances with a different host name.
To identify issue URL format as GitHub or GitLab, the host name of source in `repo_url` is checked if it contains `gitlab` or `github`. Alternately this is determined by matching to `host` values from `integrations` in app-config.yaml.
+5
View File
@@ -0,0 +1,5 @@
---
'@backstage/cli': patch
---
Added support for Datadog rum events
+2
View File
@@ -13,9 +13,11 @@ Cloudformation
Codecov
Codehilite
Config
Datadog
Debounce
Discoverability
Dockerfile
dockerfiles
Dockerize
Docusaurus
Env
+6
View File
@@ -2,6 +2,12 @@ app:
title: Backstage Example App
baseUrl: http://localhost:3000
googleAnalyticsTrackingId: # UA-000000-0
datadogRum:
clientToken: '123456789'
applicationId: qwerty
# site: # datadoghq.eu default = datadoghq.com
# env: # optional
support:
url: https://github.com/backstage/backstage/issues # Used by common ErrorPage
items: # Used by common SupportButton component
@@ -1,18 +0,0 @@
FROM nginx:mainline
# The purpose of this image is to serve the frontend app content separately.
# By default the Backstage backend uses the app-backend plugin to serve the
# app from the backend itself, but it may be desirable to move the frontend
# content serving to a separate deployment, in which case this image can be used.
# This dockerfile requires the app to be built on the host first, as it
# simply copies in the build output into the image.
RUN apt-get update && apt-get -y install jq && rm -rf /var/lib/apt/lists/*
COPY packages/app/dist /usr/share/nginx/html
COPY docker/default.conf.template /etc/nginx/conf.d/default.conf.template
COPY docker/run.sh /usr/local/bin/run.sh
CMD run.sh
ENV PORT 80
@@ -0,0 +1,59 @@
# The purpose of this image is to serve the frontend app content separately.
# By default the Backstage backend uses the app-backend plugin to serve the
# app from the backend itself, but it may be desirable to move the frontend
# content serving to a separate deployment, in which case this image can be
# used.
# This dockerfile also performs the build first inside docker. This may come
# with a build time impact, but is sometimes desirable. If you want to run the
# build on the host instead, use the file simply named Dockerfile in this folder
# instead.
# USAGE:
#
# - Copy this file and the "docker" folder from this directory to your project
# root
#
# - Update your .dockerignore, make sure that the source folders are not
# excluded, but do exclude node_modules and build artifacts:
#
# .git
# node_modules
# packages/*/dist
# packages/*/node_modules
# plugins/*/dist
# plugins/*/node_modules
#
# - Update the copy of this file to add configuration arguments to the "build"
# command, for example:
#
# RUN yarn workspace app build --config <config1> --config <config2> ...
#
# - In your project root, run:
#
# docker build -t backstage-frontend -f Dockerfile.dockerbuild .
FROM node:14-buster AS build
RUN mkdir /app
COPY . /app
WORKDIR /app
RUN yarn install
RUN yarn workspace app build
FROM nginx:mainline
RUN apt-get update && apt-get -y install jq && rm -rf /var/lib/apt/lists/*
COPY --from=build /app/packages/app/dist /usr/share/nginx/html
COPY docker/default.conf.template /etc/nginx/conf.d/default.conf.template
COPY docker/run.sh /usr/local/bin/run.sh
CMD run.sh
ENV PORT 80
@@ -0,0 +1,41 @@
# The purpose of this image is to serve the frontend app content separately.
# By default the Backstage backend uses the app-backend plugin to serve the
# app from the backend itself, but it may be desirable to move the frontend
# content serving to a separate deployment, in which case this image can be
# used.
# This dockerfile requires the app to be built on the host first, as it
# simply copies in the build output into the image. If you want to also perform
# the build itself inside docker, use Dockerfile.build in this folder instead.
# USAGE:
#
# - Copy this file and the "docker" folder from this directory to your project
# root
#
# - Add the following line to your .dockerignore to make sure that the built
# frontend actually can be transferred into the docker image:
#
# !packages/app/dist
#
# - In your project root, run:
#
# yarn install
# yarn tsc
# yarn build --config <config1> --config <config2> ...
# docker build -t backstage-frontend -f Dockerfile.hostbuild .
FROM nginx:mainline
RUN apt-get update && apt-get -y install jq && rm -rf /var/lib/apt/lists/*
COPY packages/app/dist /usr/share/nginx/html
COPY docker/default.conf.template /etc/nginx/conf.d/default.conf.template
COPY docker/run.sh /usr/local/bin/run.sh
CMD run.sh
ENV PORT 80
@@ -0,0 +1,16 @@
# Frontend with NGINX
This folder contains Docker images that let you run the Backstage frontend as
a separate image, rather than having it served through the `app-backend` plugin
from the backend.
Note that when running the frontend like this, the app configuration becomes
embedded into the actual static JavaScript files at build time. This means that
you will have to supply the list of configuration files as part of the command
line at build.
## Usage
There are two variants: one that builds inside Docker, and one that builds on
the host. See the comments at the top of the individual dockerfiles for usage
instructions.
@@ -1,23 +0,0 @@
FROM node:12-buster AS build
RUN mkdir /app
COPY . /app
WORKDIR /app
RUN yarn install
RUN yarn workspace example-app build
# Contruct backstage-frontend image
FROM nginx:mainline
RUN apt-get update && apt-get -y install jq && rm -rf /var/lib/apt/lists/*
# Copy from build stage
COPY --from=build /app/packages/app/dist /usr/share/nginx/html
COPY docker/default.conf.template /etc/nginx/conf.d/default.conf.template
COPY docker/run.sh /usr/local/bin/run.sh
CMD run.sh
ENV PORT 80
@@ -1,19 +0,0 @@
# Standalone Dockerfile for frontend
This directory contains the resources which will help you build backstage without any requirements
other than docker itself. It uses a multi-stage Dockerfile to build and ship backstage.
## Usage
You can simply run the following command to build backstage.
```
# Make sure you are in the root directory of backstage then run
docker build -t backstage-frontend -f ./contrib/docker/multi-stage-frontend/Dockerfile .
```
After a successful build, You can simply run backstage frontend with the following command.
```
docker run -it --rm -p 3080:80 backstage-frontend
```
+1 -1
View File
@@ -232,7 +232,7 @@ package, which is done as follows:
Once the `app-backend` is removed from the backend, you can use your favorite
static file serving method for serving the frontend. An example of how to set up
an NGINX image is available in the
[contrib folder in the main repo](https://github.com/backstage/backstage/blob/master/contrib/docker/frontend-with-nginx/Dockerfile)
[contrib folder in the main repo](https://github.com/backstage/backstage/blob/master/contrib/docker/frontend-with-nginx)
Note that if you're building a separate docker build of the frontend you
probably need to adjust `.dockerignore` appropriately. Most likely by making
+12
View File
@@ -45,3 +45,15 @@ metadata annotation is used in the build process of TechDocs. But when
annotation should still be present in entity descriptor file (e.g.
`catalog-info.yaml`) for Backstage to know that TechDocs is enabled for the
entity.
#### Is it possible for users to suggest changes or provide feedback on a TechDocs page?
This is supported for TechDocs sites whose source code is hosted in either
GitHub or GitLab. In order to add "edit this page" and "leave feedback" buttons
on a TechDocs page, be sure that you have `repo_url` and `edit_uri` values in
your `mkdocs.yml` files per
[MkDocs instructions](https://www.mkdocs.org/user-guide/configuration).
If the host name of your source code hosting URL does not include `github` or
`gitlab`, an `integrations` entry in your `app-config.yaml` pointed at your
source code provider is also needed (only the `host` key is necessary).
@@ -0,0 +1,30 @@
---
id: installation
title: Datadog RUM Installation
sidebar_label: Installation
# prettier-ignore
description: Adding Datadog RUM to Your App
---
There is a basic [Datadog](https://docs.datadoghq.com/real_user_monitoring/)
integration built into Backstage. You can enable it by adding the following to
your app configuration:
```yaml
app:
datadogRum:
clientToken: '123456789'
applicationId: qwerty
# site: # datadoghq.eu default = datadoghq.com
# env: # optional
```
Replace the clientToken and applicationId with the ones generated for you
Datadog.
optional arguments:
```
site: datadoghq.eu # default equals datadoghq.com
env: dev # allow to specify the environment
```
+5
View File
@@ -13,6 +13,11 @@ Backstage integrator.
See how to install Google Analytics in your app
[here](../integrations/google-analytics/installation.md)
## Datadog RUM Events
See how to install Datadog Events in your app
[here](../integrations/datadog-rum/installation.md)
## Logging
The backend supplies a central [winston](https://github.com/winstonjs/winston)
+37 -2
View File
@@ -49,8 +49,8 @@
</style>
<title><%= app.title %></title>
<% if (app.googleAnalyticsTrackingId && typeof app.googleAnalyticsTrackingId
=== 'string') { %>
<% if (app.googleAnalyticsTrackingId && typeof
app.googleAnalyticsTrackingId==='string' ) { %>
<script
async
src="https://www.googletagmanager.com/gtag/js?id=<%= app.googleAnalyticsTrackingId %>"
@@ -64,8 +64,43 @@
gtag('config', '<%= app.googleAnalyticsTrackingId %>');
</script>
<% } %> <% if (app.datadogRum.clientToken && app.datadogRum.applicationId )
{ %>
<script>
(function (h, o, u, n, d) {
h = h[d] = h[d] || {
q: [],
onReady: function (c) {
h.q.push(c);
},
};
d = o.createElement(u);
d.async = 1;
d.src = n;
n = o.getElementsByTagName(u)[0];
n.parentNode.insertBefore(d, n);
})(
window,
document,
'script',
'https://www.datadoghq-browser-agent.com/datadog-rum.js',
'DD_RUM',
);
DD_RUM.onReady(function () {
DD_RUM.init({
clientToken: '<%= app.datadogRum.clientToken %>',
applicationId: '<%= app.datadogRum.applicationId %>',
site: '<%= app.datadogRum.site %>' || 'datadoghq.com',
service: 'backstage',
env: '<%= app.datadogRum.env %>',
sampleRate: 100,
trackInteractions: true,
});
});
</script>
<% } %>
</head>
<body style="margin: 0">
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
+30
View File
@@ -180,6 +180,36 @@
"UA-000000-0"
]
},
"datadogRum": {
"type": "object",
"description": "Datadog RUM events configuration",
"properties": {
"env": {
"type": "string",
"visibility": "frontend",
"description": "Environment for Datadog RUM events"
},
"clientToken": {
"type": "string",
"visibility": "frontend",
"description": "clientToken for Datadog RUM events"
},
"applicationId": {
"type": "string",
"visibility": "frontend",
"description": "applicationId for Datadog RUM events"
},
"site": {
"type": "string",
"visibility": "frontend",
"description": "site for Datadog RUM events"
}
},
"required": [
"clientToken",
"applicationId"
]
},
"listen": {
"type": "object",
"description": "Listening configuration for local development",
+10
View File
@@ -127,6 +127,16 @@ export async function createConfig(
googleAnalyticsTrackingId: frontendConfig.getOptionalString(
'app.googleAnalyticsTrackingId',
),
datadogRum: {
env: frontendConfig.getOptionalString('app.datadogRum.env'),
clientToken: frontendConfig.getOptionalString(
'app.datadogRum.clientToken',
),
applicationId: frontendConfig.getOptionalString(
'app.datadogRum.applicationId',
),
site: frontendConfig.getOptionalString('app.datadogRum.site'),
},
},
},
}),
+16 -3
View File
@@ -1,12 +1,25 @@
# Catalog Frontend
WORK IN PROGRESS
# Backstage Catalog Frontend
This is the frontend part of the default catalog plugin.
It will implement the core API for handling your catalog of software, and
supply the base views to show and manage them.
## Getting Started
This frontend plugin can be started in a standalone mode from directly in this package
with `yarn start`. However, it will have limited functionality and that process is
most convenient when developing the catalog frontend plugin itself.
To evaluate the catalog and have a greater amount of functionality available, from the main
Backstage root folder, instead do:
```bash
yarn dev
```
This will launch both frontend and backend in the same window, populated with some example entities.
## Links
- [Backend part of the plugin](https://github.com/backstage/backstage/tree/master/plugins/catalog-backend)
@@ -75,7 +75,7 @@
"Quiché": "Xe qij",
"Romani [Sinte]": "Lashi rachi",
"Romanian": "Bunã seara",
"Russian": "Dobry vyecher",
"Russian": "Добрый вечер",
"Scottish Gaelic": "Feasgar mhath",
"Scots": "Guid eenin",
"Sesotho": "Fonane",
@@ -91,7 +91,7 @@
"Telugu": "శుభ సాయంత్రం",
"Thai": "Sawat-dii torn khum",
"Turkish": "İyi akşamlar",
"Ukrainian": "Dobry vechir",
"Ukrainian": "Добрий вечiр",
"Uzbek": "Xayrli kech",
"Welsh": "Noswaith dda",
"Yiddish": "Ah gutn ovnt",
@@ -14,7 +14,7 @@
* limitations under the License.
*/
import { EntityName } from '@backstage/catalog-model';
import { useApi } from '@backstage/core';
import { useApi, configApiRef } from '@backstage/core';
import { BackstageTheme } from '@backstage/theme';
import { useTheme } from '@material-ui/core';
import { Alert } from '@material-ui/lab';
@@ -31,6 +31,7 @@ import transformer, {
rewriteDocLinks,
sanitizeDOM,
simplifyMkdocsFooter,
addGitFeedbackLink,
} from '../transformers';
import { TechDocsNotFound } from './TechDocsNotFound';
import TechDocsProgressBar from './TechDocsProgressBar';
@@ -52,6 +53,7 @@ export const Reader = ({ entityId, onReady }: Props) => {
const [loadedPath, setLoadedPath] = useState('');
const [atInitialLoad, setAtInitialLoad] = useState(true);
const [newerDocsExist, setNewerDocsExist] = useState(false);
const configApi = useApi(configApiRef);
const {
value: isSynced,
@@ -142,6 +144,7 @@ export const Reader = ({ entityId, onReady }: Props) => {
rewriteDocLinks(),
removeMkdocsHeader(),
simplifyMkdocsFooter(),
addGitFeedbackLink(configApi),
injectCss({
css: `
body {
@@ -313,6 +316,7 @@ export const Reader = ({ entityId, onReady }: Props) => {
name,
newerDocsExist,
isSynced,
configApi,
]);
// docLoadError not considered an error state if sync request is still ongoing
@@ -0,0 +1,142 @@
/*
* Copyright 2021 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.
*/
import { createTestShadowDom } from '../../test-utils';
import { addGitFeedbackLink } from './addGitFeedbackLink';
const configApi = {
getConfigArray: function getConfigArray(key: string) {
return key === 'integrations.github'
? [{ data: { host: 'self-hosted-git-hub-provider.com' } }]
: [];
},
};
describe('addGitFeedbackLink', () => {
it('adds a feedback link when a Gitlab source edit link is available', () => {
const shadowDom = createTestShadowDom(
`
<!DOCTYPE html>
<html>
<article class="md-content__inner">
<h1>HeaderText</h1>
<a title="Edit this page" href="https://gitlab.com/reponame/username/docs/TestDoc.md"></>
</article>
</html>
`,
{
preTransformers: [addGitFeedbackLink(configApi)],
postTransformers: [],
},
);
expect(shadowDom.querySelector('#git-feedback-link')).toBeTruthy();
expect(
(shadowDom.querySelector('#git-feedback-link') as HTMLLinkElement)!.href,
).toEqual(
'https://gitlab.com/reponame/username/issues/new?issue[title]=Documentation%20Feedback%3A%20HeaderText&issue[description]=Page%20source%3A%0Ahttps%3A%2F%2Fgitlab.com%2Freponame%2Fusername%2Fdocs%2FTestDoc.md%0A%0AFeedback%3A',
);
});
it('adds a feedback link when a Github source edit link is available', () => {
const shadowDom = createTestShadowDom(
`
<!DOCTYPE html>
<html>
<article class="md-content__inner">
<h1>HeaderText</h1>
<a title="Edit this page" href="https://github.com/reponame/username/docs/TestDoc.md"></>
</article>
</html>
`,
{
preTransformers: [addGitFeedbackLink(configApi)],
postTransformers: [],
},
);
expect(shadowDom.querySelector('#git-feedback-link')).toBeTruthy();
expect(
(shadowDom.querySelector('#git-feedback-link') as HTMLLinkElement)!.href,
).toEqual(
'https://github.com/reponame/username/issues/new?title=Documentation%20Feedback%3A%20HeaderText&body=Page%20source%3A%0Ahttps%3A%2F%2Fgithub.com%2Freponame%2Fusername%2Fdocs%2FTestDoc.md%0A%0AFeedback%3A',
);
});
it('does not add a feedback link when no source edit link is available', () => {
const shadowDom = createTestShadowDom(
`
<!DOCTYPE html>
<html>
<article class="md-content__inner">
<h1>HeaderText<a class="headerlink" href="http://headerlink.com"></a></h1>
</article>
</html>
`,
{
preTransformers: [addGitFeedbackLink(configApi)],
postTransformers: [],
},
);
expect(shadowDom.querySelector('#git-feedback-link')).toBeFalsy();
});
it('does not add a feedback link when a Gitlab or Github source edit link is not available', () => {
const shadowDom = createTestShadowDom(
`
<!DOCTYPE html>
<html>
<article class="md-content__inner">
<h1>HeaderText<a class="headerlink" href="http://headerlink.com"></a></h1>
<a title="Edit this page" href="https://not-a-git-provider.com/reponame/username/docs/TestDoc.md"/>
</article>
</html>
`,
{
preTransformers: [addGitFeedbackLink(configApi)],
postTransformers: [],
},
);
expect(shadowDom.querySelector('#git-feedback-link')).toBeFalsy();
});
it('adds a feedback link when a Gitlab or Github source edit link is not available but hostname matches an integrations host', () => {
const shadowDom = createTestShadowDom(
`
<!DOCTYPE html>
<html>
<article class="md-content__inner">
<h1>HeaderText<a class="headerlink" href="http://headerlink.com"></a></h1>
<a title="Edit this page" href="https://self-hosted-git-hub-provider.com/reponame/username/docs/TestDoc.md"/>
</article>
</html>
`,
{
preTransformers: [addGitFeedbackLink(configApi)],
postTransformers: [],
},
);
expect(shadowDom.querySelector('#git-feedback-link')).toBeTruthy();
expect(
(shadowDom.querySelector('#git-feedback-link') as HTMLLinkElement)!.href,
).toEqual(
'https://self-hosted-git-hub-provider.com/reponame/username/issues/new?title=Documentation%20Feedback%3A%20HeaderText&body=Page%20source%3A%0Ahttps%3A%2F%2Fself-hosted-git-hub-provider.com%2Freponame%2Fusername%2Fdocs%2FTestDoc.md%0A%0AFeedback%3A',
);
});
});
@@ -0,0 +1,86 @@
/*
* Copyright 2021 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.
*/
import type { Transformer } from './index';
import FeedbackOutlinedIcon from '@material-ui/icons/FeedbackOutlined';
import React from 'react';
import ReactDOM from 'react-dom';
// requires repo
export const addGitFeedbackLink = (configApi: any): Transformer => {
return dom => {
// attempting to use selectors that are more likely to be static as MkDocs updates over time
const sourceAnchor = dom.querySelector(
'[title="Edit this page"]',
) as HTMLAnchorElement;
// don't show if edit link not available in raw page
if (!sourceAnchor || !sourceAnchor.href) {
return dom;
}
let gitHost = '';
const sourceURL = new URL(sourceAnchor.href);
const githubHosts = configApi
.getConfigArray('integrations.github')
.map((integration: any) => integration.data.host);
const gitlabHosts = configApi
.getConfigArray('integrations.gitlab')
.map((integration: any) => integration.data.host);
// don't show if can't identify edit link hostname as a gitlab/github hosting
if (
githubHosts.includes(sourceURL.hostname) ||
sourceURL.origin.includes('github')
) {
gitHost = 'github';
} else if (
gitlabHosts.includes(sourceURL.hostname) ||
sourceURL.origin.includes('gitlab')
) {
gitHost = 'gitlab';
} else {
return dom;
}
// topmost h1 only contains title for whole page
const title = (dom.querySelector('article>h1') as HTMLElement).childNodes[0]
.textContent;
const issueTitle = encodeURIComponent(`Documentation Feedback: ${title}`);
const issueDesc = encodeURIComponent(
`Page source:\n${sourceAnchor.href}\n\nFeedback:`,
);
const repoPath = sourceURL.pathname.split('/').slice(0, 3).join('/');
const feedbackLink = sourceAnchor.cloneNode() as HTMLAnchorElement;
switch (gitHost) {
case 'gitlab':
feedbackLink.href = `${sourceURL.origin}${repoPath}/issues/new?issue[title]=${issueTitle}&issue[description]=${issueDesc}`;
break;
case 'github':
feedbackLink.href = `${sourceURL.origin}${repoPath}/issues/new?title=${issueTitle}&body=${issueDesc}`;
break;
default:
return dom;
}
ReactDOM.render(React.createElement(FeedbackOutlinedIcon), feedbackLink);
feedbackLink.style.paddingLeft = '5px';
feedbackLink.title = 'Leave feedback for this page';
feedbackLink.id = 'git-feedback-link';
sourceAnchor?.insertAdjacentElement('beforebegin', feedbackLink);
return dom;
};
};
@@ -15,6 +15,7 @@
*/
export * from './addBaseUrl';
export * from './addGitFeedbackLink';
export * from './rewriteDocLinks';
export * from './addLinkClickListener';
export * from './removeMkdocsHeader';