diff --git a/.github/workflows/techdocs.yml b/.github/workflows/techdocs.yml index 505f63c879..491b5693de 100644 --- a/.github/workflows/techdocs.yml +++ b/.github/workflows/techdocs.yml @@ -4,6 +4,7 @@ on: pull_request: paths: - '.github/workflows/techdocs.yml' + - 'packages/techdocs-container/**' - 'packages/techdocs-cli/**' - 'plugins/techdocs/**' - 'plugins/techdocs-backend/**' @@ -18,23 +19,18 @@ jobs: python-version: [3.7] env: - TECHDOCS_CORE_PATH: ./plugins/techdocs/mkdocs/container/techdocs-core + TECHDOCS_CORE_PATH: ./packages/techdocs-container/techdocs-core name: Python ${{ matrix.node-version }} on ${{ matrix.os }} steps: - uses: actions/checkout@v2 # Build Docker Image - - name: Build and push Docker images + - name: Build Docker image uses: docker/build-push-action@v1.1.0 with: - path: plugins/techdocs/mkdocs/container - registry: docker.pkg.github.com - repository: ${{ github.repository }}/mkdocs - username: ${{ github.actor }} - password: ${{ github.token }} - tag_with_ref: true - push: true + path: packages/techdocs-container + push: false # Lint Python code for techdocs-core package - name: prepare python environment diff --git a/app-config.yaml b/app-config.yaml index b0b7666f24..cd8494613a 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -19,3 +19,6 @@ proxy: organization: name: Spotify + +techdocs: + storageUrl: https://techdocs-mock-sites.storage.googleapis.com diff --git a/package.json b/package.json index 8b5a7a24c8..cc45d541d4 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "root", "private": true, "engines": { - "node": ">=12.0.0" + "node": "12" }, "scripts": { "start": "yarn workspace example-app start", diff --git a/packages/app/src/apis.ts b/packages/app/src/apis.ts index 3d8a381635..011c549619 100644 --- a/packages/app/src/apis.ts +++ b/packages/app/src/apis.ts @@ -58,6 +58,10 @@ import { import { scaffolderApiRef, ScaffolderApi } from '@backstage/plugin-scaffolder'; import { rollbarApiRef, RollbarClient } from '@backstage/plugin-rollbar'; +import { + GithubActionsClient, + githubActionsApiRef, +} from '@backstage/plugin-github-actions'; export const apis = (config: ConfigApi) => { // eslint-disable-next-line no-console @@ -78,6 +82,7 @@ export const apis = (config: ConfigApi) => { circleCIApiRef, new CircleCIApi(`${backendUrl}/proxy/circleci/api`), ); + builder.add(githubActionsApiRef, new GithubActionsClient()); builder.add(featureFlagsApiRef, new FeatureFlags()); builder.add(lighthouseApiRef, new LighthouseRestApi('http://localhost:3003')); diff --git a/packages/backend/package.json b/packages/backend/package.json index 0018a7e7b6..07c2be8bf7 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -6,7 +6,7 @@ "private": true, "license": "Apache-2.0", "engines": { - "node": ">=12" + "node": "12" }, "scripts": { "build": "backstage-cli backend:build", diff --git a/packages/cli/templates/default-app/package.json.hbs b/packages/cli/templates/default-app/package.json.hbs index c70e00e4d0..1414ef341a 100644 --- a/packages/cli/templates/default-app/package.json.hbs +++ b/packages/cli/templates/default-app/package.json.hbs @@ -3,7 +3,7 @@ "version": "1.0.0", "private": true, "engines": { - "node": ">=12.0.0" + "node": "12" }, "scripts": { "start": "yarn workspace app start", diff --git a/packages/techdocs-cli/README.md b/packages/techdocs-cli/README.md index 8d86363c12..960574db37 100644 --- a/packages/techdocs-cli/README.md +++ b/packages/techdocs-cli/README.md @@ -4,46 +4,42 @@ Check out the [TechDocs README](https://github.com/spotify/backstage/blob/master **WIP: This cli is a work in progress. It is not ready for use yet. Follow our progress on [the Backstage Discord](https://discord.gg/MUpMjP2) under #docs-like-code or on [our GitHub Milestone](https://github.com/spotify/backstage/milestone/15).** -## Getting Started - -You'll need Docker installed and running to use this. You will also need to build the container located at `plugins/techdocs/mkdocs/container` under the tag `mkdocs:local-dev`, as you can see in the commands from below: +## Commands ```bash -docker build plugins/techdocs/mkdocs/container -t mkdocs:local-dev +# Serve localhost:3000 (and localhost:8000) +yarn serve + +# Serve localhost:8000 containing your Mkdocs documentation. +yarn serve:mkdocs +``` + +## Getting Started + +You'll need Docker installed and running to use this. You will also need to build the container located at `/packages/techdocs-container` under the tag `mkdocs:local-dev`, as you can see in the commands from below: + +```bash +docker build packages/techdocs-container -t mkdocs:local-dev ``` From that point, you can invoke the CLI from any project with a docs folder. Try out our example! ```bash -cd plugins/techdocs/mkdocs/mock-docs +cd packages/techdocs-container/mock-docs npx @techdocs/cli serve ``` ## Local Development -You'll need Docker installed and running to use this. You will also need to build the container located at `plugins/techdocs/mkdocs/container` under the tag `mkdocs:local-dev`, as you can see in the commands from below: +You'll need Docker installed and running to use this. You will also need to build the container located at `packages/techdocs-container` under the tag `mkdocs:local-dev` (for now until we deploy the container to a centralized Docker registry), as you can see in the commands from below: ```bash -docker build plugins/techdocs/mkdocs/container -t mkdocs:local-dev +docker build packages/techdocs-container -t mkdocs:local-dev ``` -Once that is built, you'll need to manually create an `alias` for running the CLI locally: - ```bash -cd packages/techdocs-cli -echo "$(pwd)/bin/techdocs" - -# Copy the value from above and add it in [HERE] below -# For more convenience, add it to your ~/.zshrc or ~/.bash_profile -# otherwise you'll lose it when you open a new Terminal -alias techdocs="[HERE]" -``` - -From that point, you can invoke `techdocs` from any project with a docs folder. Try out our example! - -```bash -cd plugins/techdocs/mkdocs/mock-docs -techdocs serve +cd packages/techdocs-container/mock-docs +npx techdocs serve ``` You should have a `localhost:3000` serving TechDocs in Backstage, as well as `localhost:8000` serving Mkdocs (which won't open up and be exposed to the user). diff --git a/packages/techdocs-cli/bin/build b/packages/techdocs-cli/bin/build index c0a1cacdfc..a86954ace9 100755 --- a/packages/techdocs-cli/bin/build +++ b/packages/techdocs-cli/bin/build @@ -22,7 +22,7 @@ TECHDOCS_PREVIEW_DEST=$ROOT_DIR/packages/techdocs-cli/dist/techdocs-preview-bund backstage-cli build --outputs cjs # Create export of the TechDocs plugin -yarn workspace @backstage/plugin-techdocs export +APP_CONFIG_techdocs_storageUrl='"http://localhost:3000/api"' yarn workspace @backstage/plugin-techdocs export # Copy over export to techdocs-cli dist/ folder cp -r $TECHDOCS_PREVIEW_SOURCE $TECHDOCS_PREVIEW_DEST diff --git a/packages/techdocs-cli/package.json b/packages/techdocs-cli/package.json index c8985367b0..2e06c4a71e 100644 --- a/packages/techdocs-cli/package.json +++ b/packages/techdocs-cli/package.json @@ -45,10 +45,11 @@ }, "dependencies": { "@backstage/cli": "^0.1.1-alpha.13", - "@backstage/plugin-techdocs": "^0.1.1-alpha.13", "chalk": "^4.1.0", "commander": "^5.1.0", "fs-extra": "^9.0.1", + "http-proxy": "^1.18.1", + "react-dev-utils": "^10.2.1", "serve-handler": "^6.1.3" } } diff --git a/packages/techdocs-cli/src/index.ts b/packages/techdocs-cli/src/index.ts index f9ab1c45de..5b4f947a73 100644 --- a/packages/techdocs-cli/src/index.ts +++ b/packages/techdocs-cli/src/index.ts @@ -13,17 +13,28 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import { spawn, ChildProcess } from 'child_process'; import program from 'commander'; import { version } from './lib/version'; // import chalk from 'chalk'; -import { spawn } from 'child_process'; import path from 'path'; -// import HTTPServer from './lib/httpServer'; +import HTTPServer from './lib/httpServer'; +import openBrowser from 'react-dev-utils/openBrowser'; -const run = (workingDirectory: string, name: string, args: string[] = []) => { - const child = spawn(name, args, { +const run = ( + workingDirectory: string, + name: string, + args: string[] = [], +): ChildProcess => { + const [stdin, stdout, stderr] = [ + 'inherit' as const, + 'pipe' as const, + 'inherit' as const, + ]; + + const childProcess = spawn(name, args, { cwd: workingDirectory, - stdio: ['inherit', 'inherit', 'inherit'], + stdio: [stdin, stdout, stderr], shell: true, env: { ...process.env, @@ -31,56 +42,87 @@ const run = (workingDirectory: string, name: string, args: string[] = []) => { }, }); - child.once('error', error => { + childProcess.once('error', error => { console.error(error); + childProcess.kill(); }); - child.once('exit', code => { - console.log('exited!', code); + + childProcess.once('exit', () => { + process.exit(0); + }); + + return childProcess; +}; + +const runMkdocsServer = (options?: { + devAddr: string; +}): Promise => { + const devAddr = options?.devAddr ?? '0.0.0.0:8000'; + + return new Promise(resolve => { + const childProcess = run(process.env.PWD!, 'docker', [ + 'run', + '-it', + '-w', + '/content', + '-v', + '$(pwd):/content', + '-p', + '8000:8000', + 'mkdocs:local-dev', + 'serve', + '-a', + devAddr, + ]); + + childProcess.stdout?.on('data', rawData => { + const data = rawData.toString().split('\n')[0]; + console.log('[mkdocs] ', data); + + if (data.includes(`Serving on http://${devAddr}`)) { + resolve(childProcess); + } + }); }); }; const main = (argv: string[]) => { program.name('techdocs-cli').version(version); + program + .command('serve:mkdocs') + .description('Serve a documentation project locally') + .action(() => { + runMkdocsServer().then(() => { + openBrowser('http://localhost:8000'); + }); + }); + program .command('serve') .description('Serve a documentation project locally') .action(() => { - // const techdocsPreviewBundlePath = path.join( - // __dirname, - // '..', - // 'dist', - // 'techdocs-preview-bundle', - // ); + // Mkdocs server + const mkdocsServer = runMkdocsServer(); - // new HTTPServer(techdocsPreviewBundlePath, 3000).serve(); - - run(process.env.PWD!, 'docker', [ - 'run', - '-it', - '-w', - '/content', - '-v', - '$(pwd):/content', - '-p', - '8000:8000', - 'mkdocs:local-dev', - 'serve', - '-a', - '0.0.0.0:8000', - ]); - - const pluginPath = path.join( - require.resolve('@backstage/plugin-techdocs'), - '..', + // Local Backstage Preview + const techdocsPreviewBundlePath = path.join( + __dirname, '..', + 'dist', + 'techdocs-preview-bundle', ); - run( - pluginPath, - path.join(require.resolve('@backstage/cli'), '../../bin/backstage-cli'), - ['plugin:serve'], - ); + const httpServer = new HTTPServer(techdocsPreviewBundlePath, 3000) + .serve() + .catch(err => { + console.error(err); + mkdocsServer.then(childProcess => childProcess.kill()); + }); + + Promise.all([mkdocsServer, httpServer]).then(() => { + openBrowser('http://localhost:3000/docs/local-dev/'); + }); }); program.parse(argv); diff --git a/packages/techdocs-cli/src/lib/httpServer.ts b/packages/techdocs-cli/src/lib/httpServer.ts index 6ca02fc7e9..6e6578840e 100644 --- a/packages/techdocs-cli/src/lib/httpServer.ts +++ b/packages/techdocs-cli/src/lib/httpServer.ts @@ -16,20 +16,64 @@ import serveHandler from 'serve-handler'; import http from 'http'; +import httpProxy from 'http-proxy'; export default class HTTPServer { - constructor(public dir: string, public port: number) {} + proxyEndpoint: string; - serve() { - const server = http.createServer((request, response) => { - return serveHandler(request, response, { - public: this.dir, - trailingSlash: true, - }); + constructor(public dir: string, public port: number) { + this.proxyEndpoint = '/api/'; + } + + private createProxy() { + const proxy = httpProxy.createProxyServer({ + target: 'http://localhost:8000', }); - server.listen(this.port, () => { - console.log('Running at http://localhost:3000'); + return (request: http.IncomingMessage): [httpProxy, string] => { + const [, ...pathChunks] = + request.url?.substring(this.proxyEndpoint.length).split('/') ?? []; + const forwardPath = pathChunks.join('/'); + + return [proxy, forwardPath]; + }; + } + + public async serve(): Promise { + return new Promise((resolve, reject) => { + const proxyHandler = this.createProxy(); + + const server = http.createServer( + (request: http.IncomingMessage, response: http.ServerResponse) => { + if (request.url?.startsWith(this.proxyEndpoint)) { + const [proxy, forwardPath] = proxyHandler(request); + + proxy.on('error', (error: Error) => { + reject(error); + }); + + request.url = forwardPath; + return proxy.web(request, response); + } + + return serveHandler(request, response, { + public: this.dir, + trailingSlash: true, + rewrites: [{ source: '**', destination: 'index.html' }], + }); + }, + ); + + server.listen(this.port, () => { + console.log( + '[techdocs-preview-bundle] Running local version of Backstage at http://localhost:3000', + ); + resolve(server); + }); + + server.on('error', (error: Error) => { + reject(error); + }); }); } } diff --git a/plugins/techdocs/mkdocs/container/Dockerfile b/packages/techdocs-container/Dockerfile similarity index 100% rename from plugins/techdocs/mkdocs/container/Dockerfile rename to packages/techdocs-container/Dockerfile diff --git a/packages/techdocs-container/README.md b/packages/techdocs-container/README.md new file mode 100644 index 0000000000..01cbcc1992 --- /dev/null +++ b/packages/techdocs-container/README.md @@ -0,0 +1,23 @@ +# techdocs-container + +This is the Docker container that powers the creation of static documentation sites that are supported by [TechDocs](https://github.com/spotify/backstage/blob/master/plugins/techdocs). + +**WIP: This is a work in progress. It is not ready for use yet. Follow our progress on [the Backstage Discord](https://discord.gg/MUpMjP2) under #docs-like-code or on [our GitHub Milestone](https://github.com/spotify/backstage/milestone/15).** + +## Getting Started + +Using the TechDocs CLI, we can invoke the latest version of `techdocs-container` via Docker Hub: + +```bash +npx @techdocs/cli serve:container +``` + +## Local Development + +```bash +docker build ./container -t techdocs-container + +docker run -w /content -v $(pwd)/mock-docs:/content -p 8000:8000 -it techdocs-container serve -a 0.0.0.0:8000 +``` + +Then open up `http://localhost:8000` on your local machine. diff --git a/plugins/techdocs/mkdocs/mock-docs/.gitignore b/packages/techdocs-container/mock-docs/.gitignore similarity index 100% rename from plugins/techdocs/mkdocs/mock-docs/.gitignore rename to packages/techdocs-container/mock-docs/.gitignore diff --git a/plugins/techdocs/mkdocs/mock-docs/docs/index.md b/packages/techdocs-container/mock-docs/docs/index.md similarity index 100% rename from plugins/techdocs/mkdocs/mock-docs/docs/index.md rename to packages/techdocs-container/mock-docs/docs/index.md diff --git a/plugins/techdocs/mkdocs/mock-docs/mkdocs.yml b/packages/techdocs-container/mock-docs/mkdocs.yml similarity index 100% rename from plugins/techdocs/mkdocs/mock-docs/mkdocs.yml rename to packages/techdocs-container/mock-docs/mkdocs.yml diff --git a/plugins/techdocs/mkdocs/mock-docs/sub-docs/docs/index.md b/packages/techdocs-container/mock-docs/sub-docs/docs/index.md similarity index 100% rename from plugins/techdocs/mkdocs/mock-docs/sub-docs/docs/index.md rename to packages/techdocs-container/mock-docs/sub-docs/docs/index.md diff --git a/plugins/techdocs/mkdocs/mock-docs/sub-docs/mkdocs.yml b/packages/techdocs-container/mock-docs/sub-docs/mkdocs.yml similarity index 100% rename from plugins/techdocs/mkdocs/mock-docs/sub-docs/mkdocs.yml rename to packages/techdocs-container/mock-docs/sub-docs/mkdocs.yml diff --git a/plugins/techdocs/mkdocs/container/techdocs-core/.gitignore b/packages/techdocs-container/techdocs-core/.gitignore similarity index 100% rename from plugins/techdocs/mkdocs/container/techdocs-core/.gitignore rename to packages/techdocs-container/techdocs-core/.gitignore diff --git a/plugins/techdocs/mkdocs/container/techdocs-core/README.md b/packages/techdocs-container/techdocs-core/README.md similarity index 91% rename from plugins/techdocs/mkdocs/container/techdocs-core/README.md rename to packages/techdocs-container/techdocs-core/README.md index 143acf3c3b..12b3acb1e2 100644 --- a/plugins/techdocs/mkdocs/container/techdocs-core/README.md +++ b/packages/techdocs-container/techdocs-core/README.md @@ -33,7 +33,7 @@ You'll then have the `techdocs-core` package available to use in Mkdocs and `pip In the parent `Dockerfile` we add this folder to the build and install the package locally in the container. In the future, we'll probably move away from this approach and have it download directly from a Python registry (and this folder will publish to one). -See the `README.md` located in the `mkdocs/` folder for more details on how to build and run the Docker container. +See the `README.md` located in the `techdocs-container/` folder for more details on how to build and run the Docker container. ## Linting diff --git a/plugins/techdocs/mkdocs/container/techdocs-core/requirements.txt b/packages/techdocs-container/techdocs-core/requirements.txt similarity index 100% rename from plugins/techdocs/mkdocs/container/techdocs-core/requirements.txt rename to packages/techdocs-container/techdocs-core/requirements.txt diff --git a/plugins/techdocs/mkdocs/container/techdocs-core/setup.py b/packages/techdocs-container/techdocs-core/setup.py similarity index 100% rename from plugins/techdocs/mkdocs/container/techdocs-core/setup.py rename to packages/techdocs-container/techdocs-core/setup.py diff --git a/plugins/techdocs/mkdocs/container/techdocs-core/src/__init__.py b/packages/techdocs-container/techdocs-core/src/__init__.py similarity index 100% rename from plugins/techdocs/mkdocs/container/techdocs-core/src/__init__.py rename to packages/techdocs-container/techdocs-core/src/__init__.py diff --git a/plugins/techdocs/mkdocs/container/techdocs-core/src/core.py b/packages/techdocs-container/techdocs-core/src/core.py similarity index 100% rename from plugins/techdocs/mkdocs/container/techdocs-core/src/core.py rename to packages/techdocs-container/techdocs-core/src/core.py diff --git a/plugins/github-actions/package.json b/plugins/github-actions/package.json index 456bd826c0..8ac474169f 100644 --- a/plugins/github-actions/package.json +++ b/plugins/github-actions/package.json @@ -21,7 +21,9 @@ "clean": "backstage-cli clean" }, "dependencies": { + "@backstage/catalog-model": "^0.1.1-alpha.13", "@backstage/core": "^0.1.1-alpha.13", + "@backstage/core-api": "^0.1.1-alpha.13", "@backstage/theme": "^0.1.1-alpha.13", "@material-ui/core": "^4.9.1", "@material-ui/icons": "^4.9.1", diff --git a/plugins/github-actions/src/apis/builds/types.ts b/plugins/github-actions/src/api/GithubActionsApi.ts similarity index 53% rename from plugins/github-actions/src/apis/builds/types.ts rename to plugins/github-actions/src/api/GithubActionsApi.ts index 134a663d02..dd75f34bf7 100644 --- a/plugins/github-actions/src/apis/builds/types.ts +++ b/plugins/github-actions/src/api/GithubActionsApi.ts @@ -14,25 +14,23 @@ * limitations under the License. */ -export enum BuildStatus { - Null, - Success, - Failure, - Pending, - Running, -} +import { createApiRef } from '@backstage/core'; +import { Build, BuildDetails } from './types'; -export type Build = { - commitId: string; - message: string; - branch: string; - status: BuildStatus; - uri: string; -}; +export const githubActionsApiRef = createApiRef({ + id: 'plugin.githubactions.service', + description: 'Used by the Github Actions plugin to make requests', +}); -export type BuildDetails = { - build: Build; - author: string; - logUrl: string; - overviewUrl: string; +export type GithubActionsApi = { + listBuilds: ({ + owner, + repo, + token, + }: { + owner: string; + repo: string; + token: string; + }) => Promise; + getBuild: (buildUri: string, token: Promise) => Promise; }; diff --git a/plugins/github-actions/src/api/GithubActionsClient.ts b/plugins/github-actions/src/api/GithubActionsClient.ts new file mode 100644 index 0000000000..58d7280c1f --- /dev/null +++ b/plugins/github-actions/src/api/GithubActionsClient.ts @@ -0,0 +1,131 @@ +/* + * 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. + */ + +import { GithubActionsApi } from './GithubActionsApi'; +import { Build, BuildDetails, BuildStatus, WorkflowRun } from './types'; + +const statusToBuildStatus: { [status: string]: BuildStatus } = { + success: BuildStatus.Success, + failure: BuildStatus.Failure, + pending: BuildStatus.Pending, + running: BuildStatus.Running, + in_progress: BuildStatus.Running, + completed: BuildStatus.Success, +}; + +const conclusionToStatus = (conslusion: string): BuildStatus => + statusToBuildStatus[conslusion] ?? BuildStatus.Null; + +export class GithubActionsClient implements GithubActionsApi { + async listBuilds({ + owner, + repo, + token, + }: { + owner: string; + repo: string; + token: string; + }): Promise { + const url = `https://api.github.com/repos/${owner}/${repo}/actions/runs`; + + const response = await fetch(url, { + headers: new Headers({ + Authorization: `Bearer ${token}`, + }), + }); + + if (!response.ok) { + return [ + { + commitId: 'Error', + message: 'Response status is not OK', + branch: 'Error', + status: BuildStatus.Failure, + uri: 'Error', + }, + ]; + } + + const data = await response.json(); + + const newData: WorkflowRun[] = data.workflow_runs; + + const endData: Build[] = []; + + newData.forEach((element, index) => { + const transData: Build = { + commitId: '', + message: '', + branch: '', + status: BuildStatus.Null, + uri: '', + }; + transData.commitId = String(element.head_commit.id); + transData.branch = element.head_branch; + transData.status = conclusionToStatus(element.conclusion); + transData.message = element.head_commit.message; + transData.uri = element.url; + endData[index] = transData; + }); + + return endData; + } + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + async getBuild( + buildUri: string, + token: Promise, + ): Promise { + const response = await fetch(buildUri, { + headers: new Headers({ + Authorization: `Bearer ${await token}`, + }), + }); + const buildBlank: Build = { + commitId: '', + message: '', + branch: '', + status: BuildStatus.Null, + uri: '', + }; + + const dataBlank: BuildDetails = { + build: buildBlank, + author: '', + logUrl: '', + overviewUrl: '', + }; + + if (!response.ok) { + return dataBlank; + } + + const data = await response.json(); + + const newData: WorkflowRun = data; + + dataBlank.author = newData.head_commit.author.name; + dataBlank.build.branch = newData.head_branch; + dataBlank.build.commitId = newData.head_commit.id; + dataBlank.build.message = newData.head_commit.message; + dataBlank.build.status = conclusionToStatus(newData.status); + dataBlank.build.uri = newData.url; + dataBlank.logUrl = newData.logs_url; + dataBlank.overviewUrl = newData.html_url; + + return dataBlank; + } +} diff --git a/plugins/github-actions/src/apis/builds/index.ts b/plugins/github-actions/src/api/index.ts similarity index 89% rename from plugins/github-actions/src/apis/builds/index.ts rename to plugins/github-actions/src/api/index.ts index 9ce2150893..9383250bfb 100644 --- a/plugins/github-actions/src/apis/builds/index.ts +++ b/plugins/github-actions/src/api/index.ts @@ -14,5 +14,6 @@ * limitations under the License. */ -export { BuildsClient } from './BuildsClient'; +export * from './GithubActionsApi'; +export * from './GithubActionsClient'; export * from './types'; diff --git a/plugins/github-actions/src/api/types.ts b/plugins/github-actions/src/api/types.ts new file mode 100644 index 0000000000..8a7b2ca548 --- /dev/null +++ b/plugins/github-actions/src/api/types.ts @@ -0,0 +1,224 @@ +/* + * 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. + */ + +export enum BuildStatus { + Null, + Success, + Failure, + Pending, + Running, +} + +export type Build = { + commitId: string; + message: string; + branch: string; + status: BuildStatus; + uri: string; +}; + +export type BuildDetails = { + build: Build; + author: string; + logUrl: string; + overviewUrl: string; +}; + +export interface Author { + name: string; + email: string; +} + +export interface Committer { + name: string; + email: string; +} + +export interface HeadCommit { + id: string; + tree_id: string; + message: string; + timestamp: Date; + author: Author; + committer: Committer; +} + +export interface Owner { + login: string; + id: number; + node_id: string; + avatar_url: string; + gravatar_id: string; + url: string; + html_url: string; + followers_url: string; + following_url: string; + gists_url: string; + starred_url: string; + subscriptions_url: string; + organizations_url: string; + repos_url: string; + events_url: string; + received_events_url: string; + type: string; + site_admin: boolean; +} + +export interface Repository { + id: number; + node_id: string; + name: string; + full_name: string; + private: boolean; + owner: Owner; + html_url: string; + description?: any; + fork: boolean; + url: string; + forks_url: string; + keys_url: string; + collaborators_url: string; + teams_url: string; + hooks_url: string; + issue_events_url: string; + events_url: string; + assignees_url: string; + branches_url: string; + tags_url: string; + blobs_url: string; + git_tags_url: string; + git_refs_url: string; + trees_url: string; + statuses_url: string; + languages_url: string; + stargazers_url: string; + contributors_url: string; + subscribers_url: string; + subscription_url: string; + commits_url: string; + git_commits_url: string; + comments_url: string; + issue_comment_url: string; + contents_url: string; + compare_url: string; + merges_url: string; + archive_url: string; + downloads_url: string; + issues_url: string; + pulls_url: string; + milestones_url: string; + notifications_url: string; + labels_url: string; + releases_url: string; + deployments_url: string; +} + +export interface Owner2 { + login: string; + id: number; + node_id: string; + avatar_url: string; + gravatar_id: string; + url: string; + html_url: string; + followers_url: string; + following_url: string; + gists_url: string; + starred_url: string; + subscriptions_url: string; + organizations_url: string; + repos_url: string; + events_url: string; + received_events_url: string; + type: string; + site_admin: boolean; +} + +export interface HeadRepository { + id: number; + node_id: string; + name: string; + full_name: string; + private: boolean; + owner: Owner2; + html_url: string; + description?: any; + fork: boolean; + url: string; + forks_url: string; + keys_url: string; + collaborators_url: string; + teams_url: string; + hooks_url: string; + issue_events_url: string; + events_url: string; + assignees_url: string; + branches_url: string; + tags_url: string; + blobs_url: string; + git_tags_url: string; + git_refs_url: string; + trees_url: string; + statuses_url: string; + languages_url: string; + stargazers_url: string; + contributors_url: string; + subscribers_url: string; + subscription_url: string; + commits_url: string; + git_commits_url: string; + comments_url: string; + issue_comment_url: string; + contents_url: string; + compare_url: string; + merges_url: string; + archive_url: string; + downloads_url: string; + issues_url: string; + pulls_url: string; + milestones_url: string; + notifications_url: string; + labels_url: string; + releases_url: string; + deployments_url: string; +} + +export interface WorkflowRun { + id: number; + node_id: string; + head_branch: string; + head_sha: string; + run_number: number; + event: string; + status: string; + conclusion: string; + workflow_id: number; + url: string; + html_url: string; + pull_requests: any[]; + created_at: Date; + updated_at: Date; + jobs_url: string; + logs_url: string; + check_suite_url: string; + artifacts_url: string; + cancel_url: string; + rerun_url: string; + workflow_url: string; + head_commit: HeadCommit; + repository: Repository; + head_repository: HeadRepository; +} diff --git a/plugins/github-actions/src/apis/builds/BuildsClient.ts b/plugins/github-actions/src/apis/builds/BuildsClient.ts deleted file mode 100644 index 6fbc6a6c53..0000000000 --- a/plugins/github-actions/src/apis/builds/BuildsClient.ts +++ /dev/null @@ -1,44 +0,0 @@ -/* - * 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. - */ - -import { Build, BuildDetails, BuildStatus } from './types'; - -export class BuildsClient { - static create(): BuildsClient { - return new BuildsClient(); - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - async listBuilds(_entityUri: string): Promise { - return []; - } - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - async getBuild(_buildUri: string): Promise { - return { - build: { - commitId: 'TODO', - branch: 'TODO', - uri: 'TODO', - status: BuildStatus.Running, - message: 'TODO', - }, - author: 'TODO', - logUrl: 'TODO', - overviewUrl: 'TODO', - }; - } -} diff --git a/plugins/github-actions/src/components/BuildDetailsPage/BuildDetailsPage.tsx b/plugins/github-actions/src/components/BuildDetailsPage/BuildDetailsPage.tsx index bb07e0d6aa..707c670f40 100644 --- a/plugins/github-actions/src/components/BuildDetailsPage/BuildDetailsPage.tsx +++ b/plugins/github-actions/src/components/BuildDetailsPage/BuildDetailsPage.tsx @@ -14,7 +14,6 @@ * limitations under the License. */ -import { Link } from '@backstage/core'; import { Button, ButtonGroup, @@ -30,10 +29,11 @@ import { Typography, } from '@material-ui/core'; import React from 'react'; -import { useParams } from 'react-router-dom'; +import { useLocation } from 'react-router-dom'; import { useAsync } from 'react-use'; -import { BuildsClient } from '../../apis/builds'; import { BuildStatusIndicator } from '../BuildStatusIndicator'; +import { Link, useApi, githubAuthApiRef } from '@backstage/core'; +import { githubActionsApiRef } from '../../api'; const useStyles = makeStyles(theme => ({ root: { @@ -48,12 +48,18 @@ const useStyles = makeStyles(theme => ({ }, })); -const client = BuildsClient.create(); - export const BuildDetailsPage = () => { + const api = useApi(githubActionsApiRef); + const githubApi = useApi(githubAuthApiRef); + const token = githubApi.getAccessToken('repo'); + const classes = useStyles(); - const { buildUri } = useParams(); - const status = useAsync(() => client.getBuild(buildUri), [buildUri]); + const location = useLocation(); + const status = useAsync( + () => + api.getBuild(decodeURIComponent(location.search.split('uri=')[1]), token), + [location.search], + ); if (status.loading) { return ; @@ -70,7 +76,7 @@ export const BuildDetailsPage = () => { return (
- + < @@ -124,12 +130,12 @@ export const BuildDetailsPage = () => { > {details?.overviewUrl && ( )} {details?.logUrl && ( )} diff --git a/plugins/github-actions/src/components/BuildInfoCard/BuildInfoCard.tsx b/plugins/github-actions/src/components/BuildInfoCard/BuildInfoCard.tsx index a04189debc..cbbc1da339 100644 --- a/plugins/github-actions/src/components/BuildInfoCard/BuildInfoCard.tsx +++ b/plugins/github-actions/src/components/BuildInfoCard/BuildInfoCard.tsx @@ -14,7 +14,6 @@ * limitations under the License. */ -import { Link } from '@backstage/core'; import { LinearProgress, makeStyles, @@ -27,10 +26,9 @@ import { } from '@material-ui/core'; import React from 'react'; import { useAsync } from 'react-use'; -import { BuildsClient } from '../../apis/builds'; import { BuildStatusIndicator } from '../BuildStatusIndicator'; - -const client = BuildsClient.create(); +import { githubActionsApiRef } from '../../api'; +import { Link, useApi, githubAuthApiRef } from '@backstage/core'; const useStyles = makeStyles(theme => ({ root: { @@ -41,62 +39,69 @@ const useStyles = makeStyles(theme => ({ }, })); -export const BuildInfoCard = () => { - const classes = useStyles(); - const status = useAsync(() => client.listBuilds('entity:spotify:backstage')); +const BuildInfoCardContent = () => { + const api = useApi(githubActionsApiRef); + const githubApi = useApi(githubAuthApiRef); - let content: JSX.Element; + const status = useAsync(async () => { + const token = await githubApi.getAccessToken('repo'); + return api.listBuilds({ owner: 'spotify', repo: 'backstage', token }); + }); if (status.loading) { - content = ; + return ; } else if (status.error) { - content = ( + return ( Failed to load builds, {status.error.message} ); - } else { - const [build] = - status.value?.filter(({ branch }) => branch === 'master') ?? []; - - content = ( - - - - - Message - - - - {build?.message} - - - - - - Commit ID - - {build?.commitId} - - - - Status - - - - - - -
- ); } + const [build] = + status.value?.filter(({ branch }) => branch === 'master') ?? []; + + return ( + + + + + Message + + + + {build?.message} + + + + + + Commit ID + + {build?.commitId} + + + + Status + + + + + + +
+ ); +}; + +export const BuildInfoCard = () => { + const classes = useStyles(); + return (
Master Build - {content} +
); }; diff --git a/plugins/github-actions/src/components/BuildListPage/BuildListPage.tsx b/plugins/github-actions/src/components/BuildListPage/BuildListPage.tsx index fc784108d4..236bc182ef 100644 --- a/plugins/github-actions/src/components/BuildListPage/BuildListPage.tsx +++ b/plugins/github-actions/src/components/BuildListPage/BuildListPage.tsx @@ -14,7 +14,7 @@ * limitations under the License. */ -import { Link } from '@backstage/core'; +import { Link, useApi, githubAuthApiRef } from '@backstage/core'; import { LinearProgress, makeStyles, @@ -31,10 +31,8 @@ import { } from '@material-ui/core'; import React from 'react'; import { useAsync } from 'react-use'; -import { BuildsClient } from '../../apis/builds'; import { BuildStatusIndicator } from '../BuildStatusIndicator'; - -const client = BuildsClient.create(); +import { githubActionsApiRef, Build } from '../../api'; const LongText = ({ text, max }: { text: string; max: number }) => { if (text.length < max) { @@ -56,10 +54,15 @@ const useStyles = makeStyles(theme => ({ }, })); -const PageContents = () => { - const { loading, error, value } = useAsync(() => - client.listBuilds('entity:spotify:backstage'), - ); +const PageContents = ({ owner, repo }: { owner: string; repo: string }) => { + const api = useApi(githubActionsApiRef); + const githubApi = useApi(githubAuthApiRef); + + const { loading, error, value } = useAsync(async () => { + const token = await githubApi.getAccessToken('repo'); + + return api.listBuilds({ owner, repo, token }); + }, [githubApi, owner, repo]); if (loading) { return ; @@ -85,7 +88,7 @@ const PageContents = () => { - {value!.map(build => ( + {value?.map((build: Build) => ( @@ -96,7 +99,7 @@ const PageContents = () => {
- + @@ -117,12 +120,13 @@ const PageContents = () => { export const BuildListPage = () => { const classes = useStyles(); + return (
CI/CD Builds - +
); }; diff --git a/plugins/github-actions/src/components/BuildStatusIndicator/BuildStatusIndicator.tsx b/plugins/github-actions/src/components/BuildStatusIndicator/BuildStatusIndicator.tsx index 332a03d67a..1a198f4df2 100644 --- a/plugins/github-actions/src/components/BuildStatusIndicator/BuildStatusIndicator.tsx +++ b/plugins/github-actions/src/components/BuildStatusIndicator/BuildStatusIndicator.tsx @@ -21,7 +21,7 @@ import SuccessIcon from '@material-ui/icons/CheckCircle'; import FailureIcon from '@material-ui/icons/Error'; import UnknownIcon from '@material-ui/icons/Help'; import React from 'react'; -import { BuildStatus } from '../../apis/builds'; +import { BuildStatus } from '../../api/types'; type Props = { status?: BuildStatus; diff --git a/plugins/github-actions/src/index.ts b/plugins/github-actions/src/index.ts index 3a0a0fe2d3..d67bc6a864 100644 --- a/plugins/github-actions/src/index.ts +++ b/plugins/github-actions/src/index.ts @@ -15,3 +15,4 @@ */ export { plugin } from './plugin'; +export * from './api'; diff --git a/plugins/github-actions/src/plugin.ts b/plugins/github-actions/src/plugin.ts index 638c8d2c8c..8897fe53db 100644 --- a/plugins/github-actions/src/plugin.ts +++ b/plugins/github-actions/src/plugin.ts @@ -24,7 +24,7 @@ export const rootRouteRef = createRouteRef({ title: 'GitHub Actions', }); export const buildRouteRef = createRouteRef({ - path: '/github-actions/builds/:buildUri', + path: '/github-actions/builds', title: 'GitHub Actions Build', }); diff --git a/plugins/register-component/src/util/validate.test.ts b/plugins/register-component/src/util/validate.test.ts index 43e05a4671..d062655bd2 100644 --- a/plugins/register-component/src/util/validate.test.ts +++ b/plugins/register-component/src/util/validate.test.ts @@ -30,20 +30,6 @@ describe('ComponentIdValidators', () => { expect(ComponentIdValidators.httpsValidator(arg)).toBe(expected); }); }); - describe('masterValidator', () => { - const errorMessage = 'Must reference a file on the master branch.'; - test.each([ - [true, '/blob/master/'], - [true, 'http://example.com/blob/master/'], - [errorMessage, 'blob/master/'], - [errorMessage, '/blob/master'], - [errorMessage, '/master/'], - [errorMessage, ''], - [errorMessage, undefined], - ])('should return %p for %s', (expected: string | boolean, arg: any) => { - expect(ComponentIdValidators.masterValidator(arg)).toBe(expected); - }); - }); describe('yamlValidator', () => { const errorMessage = "Must end with '.yaml'."; test.each([ diff --git a/plugins/register-component/src/util/validate.ts b/plugins/register-component/src/util/validate.ts index 175d9378df..78d20995f5 100644 --- a/plugins/register-component/src/util/validate.ts +++ b/plugins/register-component/src/util/validate.ts @@ -18,9 +18,6 @@ export const ComponentIdValidators = { httpsValidator: (value: any) => (typeof value === 'string' && value.match(/^https:\/\//) !== null) || 'Must start with https://.', - masterValidator: (value: any) => - (typeof value === 'string' && value.match(/\/blob\/master\//) !== null) || - 'Must reference a file on the master branch.', yamlValidator: (value: any) => (typeof value === 'string' && value.match(/.yaml$/) !== null) || "Must end with '.yaml'.", diff --git a/plugins/techdocs/README.md b/plugins/techdocs/README.md index 36d00fc970..b763679a1c 100644 --- a/plugins/techdocs/README.md +++ b/plugins/techdocs/README.md @@ -15,3 +15,35 @@ Your plugin has been added to the example app in this repository, meaning you'll You can also serve the plugin in isolation by running `yarn start` in the plugin directory. This method of serving the plugin provides quicker iteration speed and a faster startup and hot reloads. It is only meant for local development, and the setup for it can be found inside the [/dev](/dev) directory. + +## Configuration + +### Custom Storage URL + +TechDocs currently reads a static HTML file, generated by Mkdocs (see our `packages/techdocs-container` folder for more documentation) and stored on an external server, and loads that into Backstage. By default, we have set up a mock server with some example documentation sites over in Google Cloud Storage: + +```md +# Base URL + +https://techdocs-mock-sites.storage.googleapis.com + +# Home Page for the "mkdocs" docs + +https://techdocs-mock-sites.storage.googleapis.com/mkdocs/index.html + +# Home Page for the "backstage-microsite" docs + +https://techdocs-mock-sites.storage.googleapis.com/backstage-microsite/index.html +``` + +Using your own setup (or ours which is being worked on as of Q3 2020), you can point it to your own server with your own hosted documentation sites. The only requirement is that it the output is from [Mkdocs](https://mkdocs.org) with the Material theme. You can always use our documentation generation tool located at `packages/techdocs-container` for easy setup. + +To point TechDocs to your own server, simply update the `techdocs.storageUrl` value in your `app-config.yaml` file or set the environment variable `APP_CONFIG_techdocs_storageUrl` in your application: + +```bash +git clone git@github.com:spotify/backstage.git +cd backstage/ +yarn install +export APP_CONFIG_techdocs_storageUrl='"http://example-docs-site-server.com"' +yarn start +``` diff --git a/plugins/techdocs/mkdocs/README.md b/plugins/techdocs/mkdocs/README.md deleted file mode 100644 index 5c2a7725aa..0000000000 --- a/plugins/techdocs/mkdocs/README.md +++ /dev/null @@ -1,15 +0,0 @@ -# MkDocs - -Welcome to MkDocs. This is the TechDocs implementation of MkDocs. - -**WIP: This is a work in progress. It is not ready for use yet. Follow our progress on [the Backstage Discord](https://discord.gg/MUpMjP2) under #docs-like-code or on [our GitHub Milestone](https://github.com/spotify/backstage/milestone/15).** - -## Getting started - -```bash -docker build ./container -t mkdocs-container - -docker run -w /content -v $(pwd)/mock-docs:/content -p 8000:8000 -it mkdocs-container serve -a 0.0.0.0:8000 -``` - -Then open up `http://localhost:8000` on your local machine. diff --git a/plugins/techdocs/package.json b/plugins/techdocs/package.json index 98fbac1492..4dcbeec668 100644 --- a/plugins/techdocs/package.json +++ b/plugins/techdocs/package.json @@ -4,7 +4,7 @@ "main": "src/index.ts", "types": "src/index.ts", "license": "Apache-2.0", - "private": true, + "private": false, "publishConfig": { "access": "public", "main": "dist/index.esm.js", diff --git a/plugins/techdocs/src/config.js b/plugins/techdocs/src/config.js deleted file mode 100644 index 296b9fc14c..0000000000 --- a/plugins/techdocs/src/config.js +++ /dev/null @@ -1,17 +0,0 @@ -/* - * 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. - */ -export const docStorageURL = - 'https://techdocs-mock-sites.storage.googleapis.com'; diff --git a/plugins/techdocs/src/reader/components/Reader.tsx b/plugins/techdocs/src/reader/components/Reader.tsx index c42a3657cf..a6b23bd454 100644 --- a/plugins/techdocs/src/reader/components/Reader.tsx +++ b/plugins/techdocs/src/reader/components/Reader.tsx @@ -15,6 +15,7 @@ */ import React from 'react'; +import { useApi, configApiRef } from '@backstage/core'; import { useShadowDom } from '..'; import { useAsync } from 'react-use'; import { AsyncState } from 'react-use/lib/useAsync'; @@ -30,7 +31,6 @@ import transformer, { onCssReady, sanitizeDOM, } from '../transformers'; -import { docStorageURL } from '../../config'; import URLFormatter from '../urlFormatter'; import { TechDocsNotFound } from './TechDocsNotFound'; import { TechDocsPageWrapper } from './TechDocsPageWrapper'; @@ -69,12 +69,16 @@ const useEnforcedTrailingSlash = (): void => { export const Reader = () => { useEnforcedTrailingSlash(); + const docStorageUrl = + useApi(configApiRef).getOptionalString('techdocs.storageUrl') ?? + 'https://techdocs-mock-sites.storage.googleapis.com'; + const location = useLocation(); const { componentId, '*': path } = useParams(); const [shadowDomRef, shadowRoot] = useShadowDom(); const navigate = useNavigate(); const normalizedUrl = new URLFormatter( - `${docStorageURL}${location.pathname.replace('/docs', '')}`, + `${docStorageUrl}${location.pathname.replace('/docs', '')}`, ).formatBaseURL(); const state = useFetch(`${normalizedUrl}index.html`); @@ -91,7 +95,7 @@ export const Reader = () => { const transformedElement = transformer(state.value as string, [ sanitizeDOM(), addBaseUrl({ - docStorageURL, + docStorageUrl, componentId, path, }), @@ -137,7 +141,7 @@ export const Reader = () => { }, }), onCssReady({ - docStorageURL, + docStorageUrl, onLoading: (dom: Element) => { (dom as HTMLElement).style.setProperty('opacity', '0'); }, diff --git a/plugins/techdocs/src/reader/transformers/addBaseUrl.test.ts b/plugins/techdocs/src/reader/transformers/addBaseUrl.test.ts index ceb2313b03..ce903c5d2f 100644 --- a/plugins/techdocs/src/reader/transformers/addBaseUrl.test.ts +++ b/plugins/techdocs/src/reader/transformers/addBaseUrl.test.ts @@ -41,7 +41,7 @@ describe('addBaseUrl', () => { const shadowDom = createTestShadowDom(FIXTURES.FIXTURE_STANDARD_PAGE, { preTransformers: [ addBaseUrl({ - docStorageURL: DOC_STORAGE_URL, + docStorageUrl: DOC_STORAGE_URL, componentId: 'example-docs', path: '', }), @@ -76,7 +76,7 @@ describe('addBaseUrl', () => { { preTransformers: [ addBaseUrl({ - docStorageURL: DOC_STORAGE_URL, + docStorageUrl: DOC_STORAGE_URL, componentId: 'example-docs', path: 'examplepath', }), @@ -112,7 +112,7 @@ describe('addBaseUrl', () => { { preTransformers: [ addBaseUrl({ - docStorageURL: DOC_STORAGE_URL, + docStorageUrl: DOC_STORAGE_URL, componentId: 'example-docs', path: 'examplepath/', }), diff --git a/plugins/techdocs/src/reader/transformers/addBaseUrl.ts b/plugins/techdocs/src/reader/transformers/addBaseUrl.ts index 91986db729..42ed531aea 100644 --- a/plugins/techdocs/src/reader/transformers/addBaseUrl.ts +++ b/plugins/techdocs/src/reader/transformers/addBaseUrl.ts @@ -18,13 +18,13 @@ import URLFormatter from '../urlFormatter'; import type { Transformer } from './index'; type AddBaseUrlOptions = { - docStorageURL: string; + docStorageUrl: string; componentId: string; path: string; }; export const addBaseUrl = ({ - docStorageURL, + docStorageUrl, componentId, path, }: AddBaseUrlOptions): Transformer => { @@ -38,8 +38,8 @@ export const addBaseUrl = ({ .forEach((elem: T) => { const urlFormatter = new URLFormatter( path.length < 1 || path.endsWith('/') - ? `${docStorageURL}/${componentId}/${path}` - : `${docStorageURL}/${componentId}/${path}/`, + ? `${docStorageUrl}/${componentId}/${path}` + : `${docStorageUrl}/${componentId}/${path}/`, ); elem.setAttribute( diff --git a/plugins/techdocs/src/reader/transformers/onCssReady.test.ts b/plugins/techdocs/src/reader/transformers/onCssReady.test.ts index 234100fc4b..13965bcf4b 100644 --- a/plugins/techdocs/src/reader/transformers/onCssReady.test.ts +++ b/plugins/techdocs/src/reader/transformers/onCssReady.test.ts @@ -23,7 +23,7 @@ import { } from '../../test-utils'; import { addBaseUrl, onCssReady } from '../transformers'; -const docStorageURL: string = +const docStorageUrl: string = 'https://techdocs-mock-sites.storage.googleapis.com'; jest.useFakeTimers(); @@ -45,7 +45,7 @@ describe('onCssReady', () => { preTransformers: [], postTransformers: [ onCssReady({ - docStorageURL, + docStorageUrl, onLoading, onLoaded, }), @@ -65,12 +65,12 @@ describe('onCssReady', () => { preTransformers: [], postTransformers: [ addBaseUrl({ - docStorageURL, + docStorageUrl, componentId: 'mkdocs', path: '', }), onCssReady({ - docStorageURL, + docStorageUrl, onLoading, onLoaded, }), diff --git a/plugins/techdocs/src/reader/transformers/onCssReady.ts b/plugins/techdocs/src/reader/transformers/onCssReady.ts index 2d355574b6..dd50879459 100644 --- a/plugins/techdocs/src/reader/transformers/onCssReady.ts +++ b/plugins/techdocs/src/reader/transformers/onCssReady.ts @@ -17,20 +17,20 @@ import type { Transformer } from './index'; type OnCssReadyOptions = { - docStorageURL: string; + docStorageUrl: string; onLoading: (dom: Element) => void; onLoaded: (dom: Element) => void; }; export const onCssReady = ({ - docStorageURL, + docStorageUrl, onLoading, onLoaded, }: OnCssReadyOptions): Transformer => { return dom => { const cssPages = Array.from( dom.querySelectorAll('head > link[rel="stylesheet"]'), - ).filter(elem => elem.getAttribute('href')?.startsWith(docStorageURL)); + ).filter(elem => elem.getAttribute('href')?.startsWith(docStorageUrl)); let count = cssPages.length;