Skip to content
This repository was archived by the owner on Sep 30, 2024. It is now read-only.

Commit 2d17511

Browse files
burmudarsanderginnvdavid
authored
rework plugin structure and implement frontside blogpost (#46883)
Rework dir plugin structure `plugin-backstage` -> `backstage-common` * Convert to our lord and saviour `esbuild` - Plugin now uses the common-plugin type for functionality shared between backend and frontend - Implement the Entity provider as described by Frontside blogpost - Able to dev in Sourcegraph mono repo and install the plugin in Backstage Closes #46727 ## Test plan ### In plugin directory * `pnpm dist` Co-authored-by: Sander Ginn <[email protected]> Co-authored-by: David Veszelovszki <[email protected]>
1 parent ff466c5 commit 2d17511

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

54 files changed

+3164
-3159
lines changed

Diff for: .github/workflows/licenses-check.yml

+4-3
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,11 @@ jobs:
3030
with: { go-version: '1.19' }
3131

3232
# set up correct version of node
33-
- id: nvmrc
34-
run: echo ::set-output name=NODE_VERSION::$(cat .nvmrc)
33+
- name: Setup Node
34+
id: node-setup
35+
run: echo "NODE_VERSION=16.18.1" >> ${GITHUB_OUTPUT}
3536
- uses: actions/setup-node@v2
36-
with: { node-version: '${{ steps.nvmrc.outputs.NODE_VERSION }}' }
37+
with: { node-version: '${{ steps.node-setup.outputs.NODE_VERSION }}' }
3738

3839
- name: Install license_finder
3940
run: gem install license_finder:7.1.0 # sync with licenses-update.yml

Diff for: .prettierignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ vendor/
1616
.nyc_output/
1717
out/
1818
dist/
19+
dist-types/
1920
client/shared/src/schema/*.d.ts
2021
ts-node-*
2122
testdata
@@ -37,10 +38,10 @@ cmd/symbols/squirrel/language-file-extensions.json
3738
client/jetbrains/build
3839
client/jetbrains/.idea
3940
client/jetbrains/.gradle
41+
client/backstage-common/node_modules
4042
.browserslistrc
4143
code-intel-extensions.json
4244
.direnv
4345
pnpm-lock.yaml
44-
client/plugin-backstage/
4546
node_modules/
4647
client/web-sveltekit/.svelte-kit

Diff for: .stylelintignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@
55
**/*.svg
66
node_modules/
77
__mocks__/
8-
client/plugin-backstage/
8+
client/backstage-common

Diff for: client/backstage-common/.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
dist/
2+
dist-types/
3+
node_modules/

Diff for: client/backstage-common/CONTRIBUTING.md

+187
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
# Sourcegraph Backstage Common library plugin
2+
3+
Welcome to the Sourcegraph Backstage common plugin!
4+
5+
## How this plugin was generated.
6+
7+
Backstage has a cli named `backstage-cli` which performs a bunch of operations relating to Backstage maintenance and developing, including generating scaffolding for plugins.
8+
9+
There are a few plugin types but we're only interested in the following types for now:
10+
11+
- frontend-plugin: a plugin that adds components and integration with the Backstage frontend. The generated scaffolding allows running the plugin on its own without Backstage, as long as there is an `app-config.yaml` for configuration.
12+
- backend-plugin: a plugin that integrates with the Backstage backend. Typically does not contain any visual components. The generated scaffolding allows you to run the plugin on its own as a backend application, meaning there won't be a web page to visit.
13+
- common-plugin: a plugin that is more a library, with common functionality you want to share across plugins. Unlike the other plugin types, this plugin cannot be run on its own.
14+
15+
### How to generate plugin scaffolding.
16+
17+
1. Make sure you're in the Backstage repo generated with `@backstage/create-app` (Not a hard requirement, but the rest of the points assume this directory).
18+
2. Run `backstage-cli new`, where a mini wizard will ask you questions about your plugin.
19+
3. Once the command completes, the generated plugin scaffolding can be found under `plugins/`.
20+
21+
The plugin scaffolding now has been generated but the scaffolding assumes the plugin will stay in the Backstage repo which it won't. To make the plugin 'workable' outside of the Backstage repository we have make some additional changes.
22+
23+
1. Add the following `tsconfig.json` to the plugin directory.
24+
25+
```json
26+
{
27+
"extends": "../../tsconfig.json",
28+
"compilerOptions": {
29+
"module": "commonjs",
30+
"target": "es2020",
31+
"lib": ["esnext", "DOM", "DOM.Iterable"],
32+
"sourceMap": true,
33+
"sourceRoot": "src",
34+
"baseUrl": "./src",
35+
"paths": {
36+
"@sourcegraph/*": ["../*"],
37+
"*": ["types/*", "../../shared/src/types/*", "../../common/src/types/*", "*"]
38+
},
39+
"esModuleInterop": true,
40+
"resolveJsonModule": true,
41+
"strict": true,
42+
"jsx": "react-jsx"
43+
},
44+
"references": [
45+
{
46+
"path": "../shared"
47+
}
48+
],
49+
"include": ["./package.json", "**/*", ".*", "**/*.d.ts"],
50+
"exclude": ["node_modules", "../../node_modules", "dist"]
51+
}
52+
```
53+
54+
The above `tsconfig` was copied from `client/jetbrains` and adapted to fit our usecase.
55+
56+
2. Take note of the plugin directory structure.
57+
58+
```console
59+
60+
.
61+
├── README.md
62+
├── dist
63+
│   ├── index.cjs.js
64+
│   ├── index.cjs.js.map
65+
│   ├── index.d.ts
66+
│   ├── index.esm.js
67+
│   └── index.esm.js.map
68+
├── dist-types
69+
│   ├── src
70+
│   │   ├── catalog
71+
│   │   │   ├── index.d.ts
72+
│   │   │   └── parsers.d.ts
73+
│   │   ├── client
74+
│   │   │   ├── SourcegraphClient.d.ts
75+
│   │   │   ├── SourcegraphClient.test.d.ts
76+
│   │   │   └── index.d.ts
77+
│   │   ├── index.d.ts
78+
│   │   ├── providers
79+
│   │   │   ├── SourcegraphEntityProvider.d.ts
80+
│   │   │   └── index.d.ts
81+
│   │   └── setupTests.d.ts
82+
│   └── tsconfig.tsbuildinfo
83+
├── node_modules
84+
├── package.json
85+
├── src
86+
│   ├── catalog
87+
│   │   ├── index.ts
88+
│   │   └── parsers.ts
89+
│   ├── client
90+
│   │   ├── SourcegraphClient.test.ts
91+
│   │   ├── SourcegraphClient.ts
92+
│   │   └── index.ts
93+
│   ├── index.ts
94+
│   ├── providers
95+
│   │   ├── SourcegraphEntityProvider.ts
96+
│   │   └── index.ts
97+
│   └── setupTests.ts
98+
└── tsconfig.json
99+
100+
17 directories, 27 files
101+
```
102+
103+
3. The plugin will use libraries and components from the Sourcegraph repo, which are all private and not available on the npm registry. Which means, when this plugin is installed into Backstage, it will try to look for any dependencies defined in the `package.json` on the npm registry. It will find some, but any Sourcegraph referenced projects, it won't be able to find anything. We thus need to make some changes to the `package.json` and also look into using a bundler like `esbuild`.
104+
105+
4. The notable changes we make in the `package.json` are that:
106+
107+
- change the `main` attribute to have the `dist/index.js` - note no `cjs`. This is due to use using a bundler, which bundles everything into a `js` file.
108+
- add a script `es` which invokes `esbuild` with a particular config and we copy `package.dist.json` to `dist/package.json`. `package.dist.json` is a slimmed down version of our root package.json and defines how one should install the bundled artefact of our plugin, namely `index.js`.
109+
110+
```json
111+
{
112+
"name": "backstage-plugin-sourcegraph-common",
113+
"description": "Common functionalities for the Sourcegraph plugin",
114+
"version": "0.1.0",
115+
"main": "dist/index.js",
116+
"types": "dist/index.d.ts",
117+
"license": "Apache-2.0",
118+
"private": true,
119+
"publishConfig": {
120+
"access": "public",
121+
"main": "dist/index.js",
122+
"module": "dist/index.esm.js",
123+
"types": "dist/index.d.ts"
124+
},
125+
"backstage": {
126+
"role": "common-library"
127+
},
128+
"scripts": {
129+
"es": "ts-node --transpile-only ./scripts/esbuild.ts && cp package.dist.json dist/",
130+
"build": "backstage-cli package build",
131+
"lint": "backstage-cli package lint",
132+
"test": "backstage-cli package test",
133+
"clean": "backstage-cli package clean",
134+
"prepack": "backstage-cli package prepack",
135+
"postpack": "backstage-cli package postpack"
136+
},
137+
"devDependencies": {
138+
"@backstage/cli": "^0.22.1",
139+
"@jest/globals": "^29.4.0",
140+
"@sourcegraph/tsconfig": "^4.0.1",
141+
"@types/jest": "^29.4.0",
142+
"ts-jest": "^29.0.5"
143+
},
144+
"dependencies": {
145+
"@backstage/catalog-model": "^1.1.5",
146+
"@backstage/config": "^1.0.6",
147+
"@backstage/plugin-catalog-backend": "^1.7.1",
148+
"@sourcegraph/shared": "workspace:^1.0.0"
149+
},
150+
"files": ["dist"]
151+
}
152+
```
153+
154+
5. If we take a look at the `esbuild` config defined in `scripts/esbuild.ts` there are a few options to take note of:
155+
156+
- we use `external` and define most of our dependencies except the Sourcegraph related ones. External tells `esbuild` that it shouldn't bundle these dependencies into the final artefact. We set it to the values we defined in our `package.json` because they can be found on the npm registry. This also explains why we don't have `@sourcegraph/*` defined there, since we **do** want them bundled. Unfortunately, some transitive dependencies will also be bundled as they're imported, which why `lodash` and `apollo` are defined. It is a bit of a cat and mouse game, to get the right dependencies excluded.
157+
158+
6. We can now copy the directory to wherever we want, like the Sourcgraph repo `cp plugins/backstage-plugin-test ~/sourcegraph/client/backstage/test`.
159+
160+
7. Depending on the type of plugin that was generated, a dependency entry was added to either the `packages/app/package.json or `packages/backend/packages.json`. Now that the plugin has been 'moved' we don't want Backstage to still refer to these locations, so remove the plugin with `yarn workspace backend remove <name>`or`yarn workspace app remove <name>`.
161+
162+
### Run the plugin with Backstage / Local Development
163+
164+
Since we copied the plugin to a different directory we need to tell Backstage where to find the plugin.
165+
166+
1. Make sure the plugin is built. Note that we use pnpm here and not yarn, which is because Sourcegraph uses pnpm. Both yarn and pnpm work with the command defined in the `package.json` which just executes `backstage-cli` with some args.
167+
168+
```console
169+
$ cd sourcegraph/client/backstage/plugin
170+
$ pnpm es
171+
```
172+
173+
2. There should now be a dist directory in `sourcegraph/client/backstage/plugin`.
174+
3. We're now ready to show Backstage where the plugin is. Move to the Backstage root directory and execute:
175+
For a plugin that integrates with the backend:
176+
177+
```console
178+
$ yarn workspace backend add link:~/sourcegraph/client/backstage/plugin/dist
179+
```
180+
181+
For a plugin that integrates with the frontend:
182+
183+
```console
184+
$ yarn workspace app add link:~/sourcegraph/client/backstage/plugin/dist
185+
```
186+
187+
4. You should be able to start the Backstage app now with `yarn dev`

Diff for: client/backstage-common/app-config.example.yaml

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# an example config to use with Backstage
2+
app:
3+
title: Sourcegraph Backstage App
4+
baseUrl: http://localhost:3000
5+
6+
organization:
7+
name: Sourcegraph
8+
9+
sourcegraph:
10+
endpoint: https://sourcegraph.test:3443
11+
orgId: sourcegraph
12+
token: ${SG_SUDO_TOKEN}
13+
file:
14+
query: "lang:yaml repo:^ghe\\.sgdev\\.org/william/service-catalog$"
15+
grpc:
16+
query: "repo:^github\\.com/sourcegraph/sourcegraph$ file:.proto$"
17+
graphql:
18+
query: "repo:^github\\.com/sourcegraph/sourcegraph$ file:schema\\.graphql"

Diff for: client/backstage-common/babel.config.js

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// @ts-check
2+
3+
/** @type {import('@babel/core').TransformOptions} */
4+
const config = {
5+
extends: '../../babel.config.js',
6+
}
7+
8+
module.exports = config

Diff for: client/backstage-common/jest.config.js

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// @ts-check
2+
const path = require('path')
3+
4+
const config = require('../../jest.config.base')
5+
6+
/** @type {import('@jest/types').Config.InitialOptions} */
7+
const exportedConfig = {
8+
...config,
9+
displayName: 'backstage-common',
10+
rootDir: __dirname,
11+
setupFiles: [...config.setupFiles],
12+
}
13+
14+
module.exports = exportedConfig

Diff for: client/backstage-common/package.dist.json

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"name": "@sourcegraph/backstage-common",
3+
"description": "Common functionalities for the Sourcegraph plugin. This differs from the base package.json since it lists internal packages which cannot be fetched and are bundled instead.",
4+
"version": "0.1.0",
5+
"main": "index.js",
6+
"types": "types/index.d.ts",
7+
"license": "Apache-2.0",
8+
"private": true,
9+
"publishConfig": {
10+
"access": "public"
11+
},
12+
"backstage": {
13+
"role": "common-library"
14+
},
15+
"dependencies": {
16+
"@backstage/catalog-model": "^1.1.5",
17+
"@backstage/config": "^1.0.6",
18+
"@backstage/plugin-catalog-backend": "^1.7.1"
19+
}
20+
}

Diff for: client/backstage-common/package.json

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
{
2+
"name": "@sourcegraph/backstage-common",
3+
"description": "Common functionalities for the Sourcegraph plugin",
4+
"version": "0.1.0",
5+
"main": "dist/index.js",
6+
"types": "dist/index.d.ts",
7+
"license": "Apache-2.0",
8+
"private": true,
9+
"publishConfig": {
10+
"access": "public",
11+
"main": "dist/index.js",
12+
"module": "dist/index.esm.js",
13+
"types": "dist/index.d.ts"
14+
},
15+
"backstage": {
16+
"role": "common-library"
17+
},
18+
"scripts": {
19+
"dist": "ts-node --transpile-only ./scripts/esbuild.ts && pnpm types && cp package.dist.json dist/package.json",
20+
"types": "tsc -d -p tsconfig.json",
21+
"format": "prettier -w true ./src ",
22+
"test": "jest"
23+
},
24+
"devDependencies": {
25+
"@backstage/cli": "^0.22.1",
26+
"@jest/globals": "^29.4.0",
27+
"@sourcegraph/build-config": "workspace:^0.0.1",
28+
"@sourcegraph/tsconfig": "^4.0.1",
29+
"babel-jest": "^28.1.3",
30+
"babel-loader": "^9.1.0",
31+
"esbuild": "^0.17.8",
32+
"winston": "^3.8.2"
33+
},
34+
"dependencies": {
35+
"@backstage/backend-common": "^0.18.1",
36+
"@backstage/catalog-model": "^1.1.5",
37+
"@backstage/config": "^1.0.6",
38+
"@backstage/plugin-catalog-backend": "^1.7.1",
39+
"@sourcegraph/http-client": "workspace:^0.0.1",
40+
"@sourcegraph/shared": "workspace:^1.0.0"
41+
},
42+
"files": [
43+
"dist"
44+
]
45+
}

Diff for: client/backstage-common/scripts/esbuild.ts

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { existsSync } from 'fs'
2+
import path from 'path'
3+
4+
import * as esbuild from 'esbuild'
5+
import { rm } from 'shelljs'
6+
7+
import { buildTimerPlugin } from '@sourcegraph/build-config'
8+
9+
const distributionPath = path.resolve(__dirname, '..', 'dist')
10+
11+
;(async function build(): Promise<void> {
12+
if (existsSync(distributionPath)) {
13+
rm('-rf', distributionPath)
14+
}
15+
16+
await esbuild.build({
17+
entryPoints: [path.resolve(__dirname, '..', 'src', 'index.ts')],
18+
bundle: true,
19+
external: [
20+
'@backstage/cli',
21+
'@backstage/catalog-model',
22+
'@backstage/config',
23+
'@backstage/backend-common',
24+
'@backstage/plugin-catalog-backend',
25+
'graphql-request',
26+
'react',
27+
'react-dom',
28+
'lodash',
29+
'apollo',
30+
'winston',
31+
],
32+
// If we don't specify module first, esbuild bundles somethings "incorrectly" and you'll get a error with './impl/format' error
33+
mainFields: ['module', 'main'],
34+
format: 'cjs',
35+
platform: 'node',
36+
define: {
37+
'process.env.IS_TEST': 'false',
38+
global: 'globalThis',
39+
},
40+
plugins: [buildTimerPlugin],
41+
ignoreAnnotations: true,
42+
treeShaking: true,
43+
sourcemap: true,
44+
outdir: distributionPath,
45+
})
46+
})()

0 commit comments

Comments
 (0)