diff --git a/README.md b/README.md index 384cb54b65..5c357471bf 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,33 @@ Backstage is an open platform for building developer portals. ## Getting started +## Plugins + +### Creating a Plugin + +Run the following: + +```bash +$ ./tools/cookiecutter/init.sh frontend/packages/plugins/_template --output-dir frontend/packages/plugins +``` + +This will generate a plugin in the `frontend/packages/plugins` folder. It is important to note you will still need to include the plugin in your `frontend/packages/app` in the `package.json`. You will then be able to import it as follows: + +```bash +$ ./tools/cookiecutter/init.sh frontend/packages/plugins/_template --output-dir frontend/packages/plugins +plugin_name [example-plugin]: github-api + +$ vim frontend/packages/app/package.json +# Add the following line to your package.json +# Note all plugins are prefixed with "plugin-" by default with a version number of "0.0.0" +# "@backstage/plugin-github-api": "0.0.0", + +$ vim frontend/packages/app/src/App.tsx +# Add the following line to import your generated component +# import { ExampleComponent } from '@backstage/plugin-github-api'; +# +``` + ## Documentation ## License diff --git a/frontend/packages/plugins/_template/cookiecutter.json b/frontend/packages/plugins/_template/cookiecutter.json new file mode 100644 index 0000000000..2cc21e895e --- /dev/null +++ b/frontend/packages/plugins/_template/cookiecutter.json @@ -0,0 +1,3 @@ +{ + "plugin_name": "example-plugin" +} diff --git a/frontend/packages/plugins/_template/hooks/pre_gen_project.py b/frontend/packages/plugins/_template/hooks/pre_gen_project.py new file mode 100644 index 0000000000..04f63eb219 --- /dev/null +++ b/frontend/packages/plugins/_template/hooks/pre_gen_project.py @@ -0,0 +1,14 @@ +import re +import sys + + +def validate(name, regex, error_message): + if not re.match(regex, name): + print(error_message % name) + + # exits with status 1 to indicate failure + sys.exit(1) + + +validate('{{ cookiecutter.plugin_name }}', + r'^[a-z-]+$', 'ERROR: %s is not a valid plugin name! Only lowercase letters and hyphens are valid (Examples: my-amazing-plugin, myamazingplugin).') diff --git a/frontend/packages/plugins/_template/src/index.ts b/frontend/packages/plugins/_template/src/index.ts deleted file mode 100644 index b68aea57f9..0000000000 --- a/frontend/packages/plugins/_template/src/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { default } from './plugin'; diff --git a/frontend/packages/plugins/_template/src/plugin.test.ts b/frontend/packages/plugins/_template/src/plugin.test.ts deleted file mode 100644 index eda9eeb72a..0000000000 --- a/frontend/packages/plugins/_template/src/plugin.test.ts +++ /dev/null @@ -1,7 +0,0 @@ -import plugin from './plugin'; - -describe('plugin-1', () => { - it('should export plugin', () => { - expect(plugin.id).toBe('change-me-id'); - }); -}); diff --git a/frontend/packages/plugins/_template/{{ cookiecutter.plugin_name }}/README.md b/frontend/packages/plugins/_template/{{ cookiecutter.plugin_name }}/README.md new file mode 100644 index 0000000000..86e39457da --- /dev/null +++ b/frontend/packages/plugins/_template/{{ cookiecutter.plugin_name }}/README.md @@ -0,0 +1 @@ +Welcome to your {{cookiecutter.plugin_name}} plugin! diff --git a/frontend/packages/plugins/_template/jest.config.js b/frontend/packages/plugins/_template/{{ cookiecutter.plugin_name }}/jest.config.js similarity index 100% rename from frontend/packages/plugins/_template/jest.config.js rename to frontend/packages/plugins/_template/{{ cookiecutter.plugin_name }}/jest.config.js diff --git a/frontend/packages/plugins/_template/jest.setup.ts b/frontend/packages/plugins/_template/{{ cookiecutter.plugin_name }}/jest.setup.ts similarity index 100% rename from frontend/packages/plugins/_template/jest.setup.ts rename to frontend/packages/plugins/_template/{{ cookiecutter.plugin_name }}/jest.setup.ts diff --git a/frontend/packages/plugins/_template/package.json b/frontend/packages/plugins/_template/{{ cookiecutter.plugin_name }}/package.json similarity index 91% rename from frontend/packages/plugins/_template/package.json rename to frontend/packages/plugins/_template/{{ cookiecutter.plugin_name }}/package.json index 1039d0a49b..85f56f7fa0 100644 --- a/frontend/packages/plugins/_template/package.json +++ b/frontend/packages/plugins/_template/{{ cookiecutter.plugin_name }}/package.json @@ -1,5 +1,5 @@ { - "name": "@backstage/__CHANGE_ME__", + "name": "@backstage/plugin-{{ cookiecutter.plugin_name }}", "version": "0.0.0", "main": "src/index.ts", "main:src": "src/index.ts", diff --git a/frontend/packages/plugins/_template/{{ cookiecutter.plugin_name }}/src/components/ExampleComponent/ExampleComponent.test.tsx b/frontend/packages/plugins/_template/{{ cookiecutter.plugin_name }}/src/components/ExampleComponent/ExampleComponent.test.tsx new file mode 100644 index 0000000000..c231eea700 --- /dev/null +++ b/frontend/packages/plugins/_template/{{ cookiecutter.plugin_name }}/src/components/ExampleComponent/ExampleComponent.test.tsx @@ -0,0 +1,10 @@ +import React from 'react'; +import { render } from '@testing-library/react'; +import ExampleComponent from './ExampleComponent'; + +describe('ExampleComponent', () => { + it('should render', () => { + const rendered = render(); + expect(rendered.getByText('Hello!')).toBeInTheDocument(); + }); +}); diff --git a/frontend/packages/plugins/_template/{{ cookiecutter.plugin_name }}/src/components/ExampleComponent/ExampleComponent.tsx b/frontend/packages/plugins/_template/{{ cookiecutter.plugin_name }}/src/components/ExampleComponent/ExampleComponent.tsx new file mode 100644 index 0000000000..bd7c9235ae --- /dev/null +++ b/frontend/packages/plugins/_template/{{ cookiecutter.plugin_name }}/src/components/ExampleComponent/ExampleComponent.tsx @@ -0,0 +1,16 @@ +import React, { FC } from 'react'; +import Button from '@material-ui/core/Button'; + +const ExampleComponent: FC<{}> = () => { + return ( + + ); +}; + +export default ExampleComponent; diff --git a/frontend/packages/plugins/_template/{{ cookiecutter.plugin_name }}/src/components/ExampleComponent/index.ts b/frontend/packages/plugins/_template/{{ cookiecutter.plugin_name }}/src/components/ExampleComponent/index.ts new file mode 100644 index 0000000000..520a3bf553 --- /dev/null +++ b/frontend/packages/plugins/_template/{{ cookiecutter.plugin_name }}/src/components/ExampleComponent/index.ts @@ -0,0 +1 @@ +export { default } from './ExampleComponent'; diff --git a/frontend/packages/plugins/_template/{{ cookiecutter.plugin_name }}/src/index.ts b/frontend/packages/plugins/_template/{{ cookiecutter.plugin_name }}/src/index.ts new file mode 100644 index 0000000000..a2e45f3a79 --- /dev/null +++ b/frontend/packages/plugins/_template/{{ cookiecutter.plugin_name }}/src/index.ts @@ -0,0 +1,2 @@ +export { default } from './plugin'; +export { default as ExampleComponent } from './components/ExampleComponent'; diff --git a/frontend/packages/plugins/_template/{{ cookiecutter.plugin_name }}/src/plugin.test.ts b/frontend/packages/plugins/_template/{{ cookiecutter.plugin_name }}/src/plugin.test.ts new file mode 100644 index 0000000000..869fa6f67f --- /dev/null +++ b/frontend/packages/plugins/_template/{{ cookiecutter.plugin_name }}/src/plugin.test.ts @@ -0,0 +1,7 @@ +import plugin from './plugin'; + +describe('{{ cookiecutter.plugin_name }}', () => { + it('should export plugin', () => { + expect(plugin.id).toBe('{{ cookiecutter.plugin_name }}'); + }); +}); diff --git a/frontend/packages/plugins/_template/src/plugin.ts b/frontend/packages/plugins/_template/{{ cookiecutter.plugin_name }}/src/plugin.ts similarity index 67% rename from frontend/packages/plugins/_template/src/plugin.ts rename to frontend/packages/plugins/_template/{{ cookiecutter.plugin_name }}/src/plugin.ts index a20c7ccd89..0424b5d999 100644 --- a/frontend/packages/plugins/_template/src/plugin.ts +++ b/frontend/packages/plugins/_template/{{ cookiecutter.plugin_name }}/src/plugin.ts @@ -1,5 +1,5 @@ import { createPlugin } from '@backstage/core'; export default createPlugin({ - id: 'change-me-id', + id: '{{ cookiecutter.plugin_name }}', }); diff --git a/tools/cookiecutter/Dockerfile b/tools/cookiecutter/Dockerfile new file mode 100644 index 0000000000..9affb40d0f --- /dev/null +++ b/tools/cookiecutter/Dockerfile @@ -0,0 +1,3 @@ +FROM python:3.8-slim +RUN pip install cookiecutter==1.7.0 --index-url https://pypi.python.org/simple +ENTRYPOINT [ "cookiecutter" ] \ No newline at end of file diff --git a/tools/cookiecutter/init.sh b/tools/cookiecutter/init.sh new file mode 100755 index 0000000000..717ab50cd3 --- /dev/null +++ b/tools/cookiecutter/init.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +set -e + +# Build the container +docker build $(pwd)/tools/cookiecutter -t backstage/tools/cookiecutter --quiet + +# Run cookiecutter with our arguments +COOKIECUTTER_FLAGS=${@:-""} +docker run -it -v $(pwd):/app -w /app backstage/tools/cookiecutter $COOKIECUTTER_FLAGS