diff --git a/docs/FAQ.md b/docs/FAQ.md index 85c8f09bef..5167139d45 100644 --- a/docs/FAQ.md +++ b/docs/FAQ.md @@ -156,10 +156,12 @@ Integrators also configure closed source plugins locally from the monorepo. ​ ​ We chose GitHub because it is the tool that we are most familiar with, so that will naturally lead to integrations for GitHub being developed at an early stage. ​ Hosting this project on GitHub does not exclude integrations with -alternatives, such as [GitLab](https://github.com/spotify/backstage/issues?q=is%3Aissue+is%3Aopen+GitLab) or Bitbucket. We believe that in time there will be -plugins that will provide functionality for these tools as well. Hopefully, -contributed by the community! ​ Also note, implementations of Backstage can be -hosted wherever you feel suits your needs best. ​ +alternatives, such as +[GitLab](https://github.com/spotify/backstage/issues?q=is%3Aissue+is%3Aopen+GitLab) +or Bitbucket. We believe that in time there will be plugins that will provide +functionality for these tools as well. Hopefully, contributed by the community! +​ Also note, implementations of Backstage can be hosted wherever you feel suits +your needs best. ​ ### Who maintains Backstage? diff --git a/packages/backend/src/plugins/scaffolder.ts b/packages/backend/src/plugins/scaffolder.ts index 08e700bc74..ce3a7ea2bd 100644 --- a/packages/backend/src/plugins/scaffolder.ts +++ b/packages/backend/src/plugins/scaffolder.ts @@ -17,13 +17,17 @@ import { CookieCutter, createRouter, - DiskStorage, + FilePreparer, + Preparers, } from '@backstage/plugin-scaffolder-backend'; import type { PluginEnvironment } from '../types'; export default async function createPlugin({ logger }: PluginEnvironment) { - const storage = new DiskStorage({ logger }); const templater = new CookieCutter(); + const filePreparer = new FilePreparer(); + const preparers = new Preparers(); - return await createRouter({ storage, templater, logger }); + preparers.register('file', filePreparer); + + return await createRouter({ preparers, templater, logger }); } diff --git a/packages/catalog-model/src/kinds/TemplateEntityV1alpha1.ts b/packages/catalog-model/src/kinds/TemplateEntityV1alpha1.ts index 090c892294..0bef220a63 100644 --- a/packages/catalog-model/src/kinds/TemplateEntityV1alpha1.ts +++ b/packages/catalog-model/src/kinds/TemplateEntityV1alpha1.ts @@ -26,6 +26,7 @@ export interface TemplateEntityV1alpha1 extends Entity { kind: typeof KIND; spec: { type: string; + path?: string; }; } @@ -39,6 +40,7 @@ export class TemplateEntityV1alpha1Policy implements EntityPolicy { spec: yup .object({ type: yup.string().required().min(1), + path: yup.string(), }) .required(), }); diff --git a/plugins/scaffolder-backend/src/run.ts b/plugins/scaffolder-backend/fixtures/test-nested-template/template-1/expected_file.ts similarity index 52% rename from plugins/scaffolder-backend/src/run.ts rename to plugins/scaffolder-backend/fixtures/test-nested-template/template-1/expected_file.ts index 133aad163e..19b0312029 100644 --- a/plugins/scaffolder-backend/src/run.ts +++ b/plugins/scaffolder-backend/fixtures/test-nested-template/template-1/expected_file.ts @@ -13,22 +13,4 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -import { getRootLogger } from '@backstage/backend-common'; -import { startStandaloneServer } from './service/standaloneServer'; - -const port = process.env.PLUGIN_PORT ? Number(process.env.PLUGIN_PORT) : 3004; -const enableCors = process.env.PLUGIN_CORS - ? Boolean(process.env.PLUGIN_CORS) - : false; -const logger = getRootLogger(); - -startStandaloneServer({ port, enableCors, logger }).catch((err) => { - logger.error(err); - process.exit(1); -}); - -process.on('SIGINT', () => { - logger.info('CTRL+C pressed; exiting.'); - process.exit(0); -}); +console.warn('here!'); diff --git a/plugins/scaffolder-backend/fixtures/test-nested-template/template.yaml b/plugins/scaffolder-backend/fixtures/test-nested-template/template.yaml new file mode 100644 index 0000000000..713094defe --- /dev/null +++ b/plugins/scaffolder-backend/fixtures/test-nested-template/template.yaml @@ -0,0 +1,9 @@ +apiVersion: backstage.io/v1alpha1 +kind: Template +metadata: + name: react-ssr-template + title: React SSR Template + description: Next.js application skeleton for creating isomorphic web applications. +spec: + type: cookiecutter + path: ./template-1 diff --git a/plugins/scaffolder-backend/fixtures/test-simple-template/expected_file.ts b/plugins/scaffolder-backend/fixtures/test-simple-template/expected_file.ts new file mode 100644 index 0000000000..19b0312029 --- /dev/null +++ b/plugins/scaffolder-backend/fixtures/test-simple-template/expected_file.ts @@ -0,0 +1,16 @@ +/* + * 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. + */ +console.warn('here!'); diff --git a/plugins/scaffolder-backend/fixtures/test-simple-template/template.yaml b/plugins/scaffolder-backend/fixtures/test-simple-template/template.yaml new file mode 100644 index 0000000000..7ee2132d59 --- /dev/null +++ b/plugins/scaffolder-backend/fixtures/test-simple-template/template.yaml @@ -0,0 +1,8 @@ +apiVersion: backstage.io/v1alpha1 +kind: Template +metadata: + name: react-ssr-template + title: React SSR Template + description: Next.js application skeleton for creating isomorphic web applications. +spec: + type: cookiecutter diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index d74c8ffdb6..0edf7a8fe9 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -22,6 +22,7 @@ }, "dependencies": { "@backstage/backend-common": "^0.1.1-alpha.9", + "@backstage/catalog-model": "^0.1.1-alpha.9", "@types/express": "^4.17.6", "compression": "^1.7.4", "cors": "^2.8.5", @@ -38,7 +39,8 @@ "@backstage/cli": "^0.1.1-alpha.9", "@types/fs-extra": "^9.0.1", "@types/supertest": "^2.0.8", - "supertest": "^4.0.2" + "supertest": "^4.0.2", + "yaml": "^1.10.0" }, "files": [ "dist/**/*.{js,d.ts}" diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/hooks/post_gen_project.sh b/plugins/scaffolder-backend/sample-templates/react-ssr-template/hooks/post_gen_project.sh index c6d477d91a..033a102fed 100644 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/hooks/post_gen_project.sh +++ b/plugins/scaffolder-backend/sample-templates/react-ssr-template/hooks/post_gen_project.sh @@ -9,4 +9,4 @@ sed -i -e "s/__component_id__/{{ cookiecutter.component_id }}/g" package.json mv ../../node_modules.tmp ../../\{\{cookiecutter.component_id\}\}/node_modules 2>/dev/null ||: # move back the build directory that was moved out in the pre_gen hook (if it exists) -mv ../../build.tmp ../../\{\{cookiecutter.component_id\}\}/build 2>/dev/null ||: \ No newline at end of file +mv ../../build.tmp ../../\{\{cookiecutter.component_id\}\}/build 2>/dev/null ||: diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/template-info.json b/plugins/scaffolder-backend/sample-templates/react-ssr-template/template-info.json deleted file mode 100644 index a733b790fa..0000000000 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/template-info.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "id": "react-ssr-template", - "name": "SSR React Website", - "description": "Next.js application skeleton for creating isomorphic web applications.", - "ownerId": "something" -} diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/.editorconfig b/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/.editorconfig similarity index 100% rename from plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/.editorconfig rename to plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/.editorconfig diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/.eslintignore b/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/.eslintignore similarity index 100% rename from plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/.eslintignore rename to plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/.eslintignore diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/.eslintrc.js b/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/.eslintrc.js similarity index 100% rename from plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/.eslintrc.js rename to plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/.eslintrc.js diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/.github/workflows/build.yml b/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/.github/workflows/build.yml similarity index 100% rename from plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/.github/workflows/build.yml rename to plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/.github/workflows/build.yml diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/.gitignore b/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/.gitignore similarity index 100% rename from plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/.gitignore rename to plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/.gitignore diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/.nvmrc b/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/.nvmrc similarity index 100% rename from plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/.nvmrc rename to plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/.nvmrc diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/README.md b/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/README.md similarity index 100% rename from plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/README.md rename to plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/README.md diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/babel.config.js b/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/babel.config.js similarity index 100% rename from plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/babel.config.js rename to plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/babel.config.js diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/jest.config.js b/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/jest.config.js similarity index 100% rename from plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/jest.config.js rename to plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/jest.config.js diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/next-env.d.ts b/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/next-env.d.ts similarity index 100% rename from plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/next-env.d.ts rename to plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/next-env.d.ts diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/next.config.js b/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/next.config.js similarity index 100% rename from plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/next.config.js rename to plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/next.config.js diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/package.json b/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/package.json similarity index 100% rename from plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/package.json rename to plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/package.json diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/prettier.config.js b/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/prettier.config.js similarity index 100% rename from plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/prettier.config.js rename to plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/prettier.config.js diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/public/static/fonts.css b/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/public/static/fonts.css similarity index 100% rename from plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/public/static/fonts.css rename to plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/public/static/fonts.css diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/src/__tests__/index.test.tsx b/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/src/__tests__/index.test.tsx similarity index 100% rename from plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/src/__tests__/index.test.tsx rename to plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/src/__tests__/index.test.tsx diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/src/components/Header.tsx b/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/src/components/Header.tsx similarity index 100% rename from plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/src/components/Header.tsx rename to plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/src/components/Header.tsx diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/src/pages/_app.tsx b/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/src/pages/_app.tsx similarity index 100% rename from plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/src/pages/_app.tsx rename to plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/src/pages/_app.tsx diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/src/pages/_document.tsx b/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/src/pages/_document.tsx similarity index 100% rename from plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/src/pages/_document.tsx rename to plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/src/pages/_document.tsx diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/src/pages/api/ping.ts b/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/src/pages/api/ping.ts similarity index 100% rename from plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/src/pages/api/ping.ts rename to plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/src/pages/api/ping.ts diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/src/pages/index.tsx b/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/src/pages/index.tsx similarity index 100% rename from plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/src/pages/index.tsx rename to plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/src/pages/index.tsx diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/tsconfig.json b/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/tsconfig.json similarity index 100% rename from plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.componentId}}/tsconfig.json rename to plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/tsconfig.json diff --git a/plugins/scaffolder-backend/src/scaffolder/index.ts b/plugins/scaffolder-backend/src/scaffolder/index.ts index a665b32ead..4cc3f21a9d 100644 --- a/plugins/scaffolder-backend/src/scaffolder/index.ts +++ b/plugins/scaffolder-backend/src/scaffolder/index.ts @@ -13,8 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -export * from './storage'; export * from './templater'; - -export * from './storage/disk'; +export * from './prepare'; export * from './templater/cookiecutter'; diff --git a/plugins/scaffolder-backend/src/scaffolder/prepare/file.test.ts b/plugins/scaffolder-backend/src/scaffolder/prepare/file.test.ts new file mode 100644 index 0000000000..b9472fbc8d --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/prepare/file.test.ts @@ -0,0 +1,65 @@ +/* + * 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 fs from 'fs-extra'; +import YAML from 'yaml'; +import { FilePreparer } from './file'; +import path from 'path'; +import { + TemplateEntityV1alpha1, + LOCATION_ANNOTATION, +} from '@backstage/catalog-model'; + +const setupTest = async (fixturePath: string) => { + const locationForTemplateYaml = path.resolve( + path.dirname( + require.resolve('@backstage/plugin-scaffolder-backend/package'), + ), + 'fixtures', + fixturePath, + ); + + const [parsedDocument] = YAML.parseAllDocuments( + await fs.readFile(locationForTemplateYaml, 'utf-8'), + ); + + const template: TemplateEntityV1alpha1 = parsedDocument.toJSON(); + template.metadata.annotations = { + [LOCATION_ANNOTATION]: `file:${locationForTemplateYaml}`, + }; + + const filePreparer = new FilePreparer(); + const resultDir = await filePreparer.prepare(template); + + return { filePreparer, template, resultDir }; +}; + +describe('File preparer', () => { + it('excludes the yaml file from the temp folder', async () => { + const { resultDir } = await setupTest('test-simple-template/template.yaml'); + expect(fs.existsSync(`${resultDir}/template.yaml`)).toBe(false); + }); + + it('resolves relative path from the template', async () => { + const { resultDir } = await setupTest('test-simple-template/template.yaml'); + expect(fs.existsSync(`${resultDir}/expected_file.ts`)).toBe(true); + }); + + it('resolves relative path from the nested template', async () => { + const { resultDir } = await setupTest('test-nested-template/template.yaml'); + expect(fs.existsSync(`${resultDir}/expected_file.ts`)).toBe(true); + }); +}); diff --git a/plugins/scaffolder-backend/src/scaffolder/prepare/file.ts b/plugins/scaffolder-backend/src/scaffolder/prepare/file.ts new file mode 100644 index 0000000000..7a924d9a99 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/prepare/file.ts @@ -0,0 +1,50 @@ +/* + * 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 fs from 'fs-extra'; +import path from 'path'; +import os from 'os'; +import { TemplateEntityV1alpha1 } from '@backstage/catalog-model'; +import { parseLocationAnnotation } from './helpers'; +import { InputError } from '@backstage/backend-common'; +import { PreparerBase } from './types'; + +export class FilePreparer implements PreparerBase { + async prepare(template: TemplateEntityV1alpha1): Promise { + const { protocol, location } = parseLocationAnnotation(template); + + if (protocol !== 'file') { + throw new InputError( + `Wrong location protocol: ${protocol}, should be 'file'`, + ); + } + const templateId = template.metadata.name; + + const tempDir = await fs.promises.mkdtemp( + path.join(os.tmpdir(), templateId), + ); + + const parentDirectory = path.resolve( + path.dirname(location), + template.spec.path ?? '.', + ); + + await fs.copy(parentDirectory, tempDir, { + filter: src => src !== location, + }); + + return tempDir; + } +} diff --git a/plugins/scaffolder-backend/src/scaffolder/prepare/helpers.ts b/plugins/scaffolder-backend/src/scaffolder/prepare/helpers.ts new file mode 100644 index 0000000000..3f31f19d59 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/prepare/helpers.ts @@ -0,0 +1,56 @@ +/* + * 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 { + TemplateEntityV1alpha1, + LOCATION_ANNOTATION, +} from '@backstage/catalog-model'; +import { InputError } from '@backstage/backend-common'; +import { RemoteProtocol } from './types'; + +export type ParsedLocationAnnotation = { + protocol: RemoteProtocol; + location: string; +}; + +export const parseLocationAnnotation = ( + entity: TemplateEntityV1alpha1, +): ParsedLocationAnnotation => { + const annotation = entity.metadata.annotations?.[LOCATION_ANNOTATION]; + + if (!annotation) { + throw new InputError( + `No location annotation provided in entity: ${entity.metadata.name}`, + ); + } + + // split on the first colon for the protocol and the rest after the first split + // is the location. + const [protocol, location] = annotation.split(/:(.+)/) as [ + RemoteProtocol?, + string?, + ]; + + if (!protocol || !location) { + throw new InputError( + `Failure to parse either protocol or location for entity: ${entity.metadata.name}`, + ); + } + + return { + protocol, + location, + }; +}; diff --git a/plugins/scaffolder-backend/src/scaffolder/prepare/index.ts b/plugins/scaffolder-backend/src/scaffolder/prepare/index.ts new file mode 100644 index 0000000000..0b1a72eaa3 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/prepare/index.ts @@ -0,0 +1,19 @@ +/* + * 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 * from './preparers'; +export * from './types'; +export * from './helpers'; +export * from './file'; diff --git a/plugins/scaffolder-backend/src/scaffolder/prepare/preparers.test.ts b/plugins/scaffolder-backend/src/scaffolder/prepare/preparers.test.ts new file mode 100644 index 0000000000..a8f4b4ae99 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/prepare/preparers.test.ts @@ -0,0 +1,88 @@ +/* + * 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 { Preparers } from '.'; +import { TemplateEntityV1alpha1 } from '@backstage/catalog-model'; +import { FilePreparer } from './file'; + +describe('Preparers', () => { + const mockTemplate: TemplateEntityV1alpha1 = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Template', + metadata: { + annotations: { + 'backstage.io/managed-by-location': + 'file:/Users/blam/dev/spotify/backstage/plugins/scaffolder-backend/sample-templates/react-ssr-template/template.yaml', + }, + name: 'react-ssr-template', + title: 'React SSR Template', + description: + 'Next.js application skeleton for creating isomorphic web applications.', + uid: '7357f4c5-aa58-4a1e-9670-18931eef771f', + etag: 'YWUxZWQyY2EtZDkxMC00MDM0LWI0ODAtMDgwMWY0YzdlMWIw', + generation: 1, + }, + spec: { + type: 'cookiecutter', + path: '.', + }, + }; + it('should throw an error when the preparer for the source location is not registered', () => { + const preparers = new Preparers(); + + expect(() => preparers.get(mockTemplate)).toThrow( + expect.objectContaining({ + message: 'No preparer registered for type: "file"', + }), + ); + }); + it('should return the correct preparer when the source matches', () => { + const preparers = new Preparers(); + const preparer = new FilePreparer(); + + preparers.register('file', preparer); + + expect(preparers.get(mockTemplate)).toBe(preparer); + }); + + it('should throw an error if the metadata tag does not exist in the entity', () => { + const brokenTemplate: TemplateEntityV1alpha1 = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Template', + metadata: { + annotations: {}, + name: 'react-ssr-template', + title: 'React SSR Template', + description: + 'Next.js application skeleton for creating isomorphic web applications.', + uid: '7357f4c5-aa58-4a1e-9670-18931eef771f', + etag: 'YWUxZWQyY2EtZDkxMC00MDM0LWI0ODAtMDgwMWY0YzdlMWIw', + generation: 1, + }, + spec: { + type: 'cookiecutter', + path: '.', + }, + }; + + const preparers = new Preparers(); + + expect(() => preparers.get(brokenTemplate)).toThrow( + expect.objectContaining({ + message: expect.stringContaining('No location annotation provided'), + }), + ); + }); +}); diff --git a/plugins/scaffolder-backend/src/scaffolder/prepare/preparers.ts b/plugins/scaffolder-backend/src/scaffolder/prepare/preparers.ts new file mode 100644 index 0000000000..046890bdca --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/prepare/preparers.ts @@ -0,0 +1,38 @@ +/* + * 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 { PreparerBase, RemoteProtocol, PreparerBuilder } from './types'; +import { TemplateEntityV1alpha1 } from '@backstage/catalog-model'; +import { parseLocationAnnotation } from './helpers'; + +export class Preparers implements PreparerBuilder { + private preparerMap = new Map(); + + register(protocol: RemoteProtocol, preparer: PreparerBase) { + this.preparerMap.set(protocol, preparer); + } + + get(template: TemplateEntityV1alpha1): PreparerBase { + const { protocol } = parseLocationAnnotation(template); + const preparer = this.preparerMap.get(protocol); + + if (!preparer) { + throw new Error(`No preparer registered for type: "${protocol}"`); + } + + return preparer; + } +} diff --git a/plugins/scaffolder-backend/src/scaffolder/prepare/types.ts b/plugins/scaffolder-backend/src/scaffolder/prepare/types.ts new file mode 100644 index 0000000000..3251bd2780 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/prepare/types.ts @@ -0,0 +1,32 @@ +/* + * 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 type { TemplateEntityV1alpha1 } from '@backstage/catalog-model'; + +export type PreparerBase = { + /** + * Given an Entity definition from the Service Catalog, go and prepare a directory + * with contents from the remote location in temporary storage and return the path + * @param template The template entity from the Service Catalog + */ + prepare(template: TemplateEntityV1alpha1): Promise; +}; + +export type PreparerBuilder = { + register(protocol: RemoteProtocol, preparer: PreparerBase): void; + get(template: TemplateEntityV1alpha1): PreparerBase; +}; + +export type RemoteProtocol = 'file'; diff --git a/plugins/scaffolder-backend/src/scaffolder/storage/disk.test.ts b/plugins/scaffolder-backend/src/scaffolder/storage/disk.test.ts deleted file mode 100644 index 9cebb0d380..0000000000 --- a/plugins/scaffolder-backend/src/scaffolder/storage/disk.test.ts +++ /dev/null @@ -1,84 +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 { DiskStorage } from './disk'; -import * as path from 'path'; - -describe('Disk Storage', () => { - it('should load a simple template from a simple directory', async () => { - const testTemplateDir = path.resolve( - __dirname, - '../../../test/mock-simple-template-dir', - ); - const templateInfo = require(`${testTemplateDir}/mock-template/template-info.json`); - - const repository = new DiskStorage({ directory: testTemplateDir }); - - await repository.reindex(); - - const templates = await repository.list(); - - expect(templates).toHaveLength(1); - expect(templates[0].id).toBe(templateInfo.id); - expect(templates[0].name).toBe(templateInfo.name); - expect(templates[0].description).toBe(templateInfo.description); - expect(templates[0].ownerId).toBe(templateInfo.ownerId); - }); - - it('should successfully load multiple templates from the same folder', async () => { - const testTemplateDir = path.resolve( - __dirname, - '../../../test/mock-multiple-templates-dir', - ); - - const repository = new DiskStorage({ directory: testTemplateDir }); - - await repository.reindex(); - - const templates = await repository.list(); - - expect(templates).toHaveLength(2); - }); - - it('should return empty array when there are no templates', async () => { - const testTemplateDir = path.resolve( - __dirname, - '/some-folder-that-deffo-does-not-exist', - ); - - const repository = new DiskStorage({ directory: testTemplateDir }); - - await repository.reindex(); - - const templates = await repository.list(); - - expect(templates).toHaveLength(0); - }); - - it('should be able to handle templates with invalid json and ignore them from the returned array', async () => { - const testTemplateDir = path.resolve( - __dirname, - '../../../test/mock-failing-template-dir', - ); - - const repository = new DiskStorage({ directory: testTemplateDir }); - - await repository.reindex(); - - const templates = await repository.list(); - - expect(templates).toHaveLength(1); - }); -}); diff --git a/plugins/scaffolder-backend/src/scaffolder/storage/disk.ts b/plugins/scaffolder-backend/src/scaffolder/storage/disk.ts deleted file mode 100644 index 28236cb86f..0000000000 --- a/plugins/scaffolder-backend/src/scaffolder/storage/disk.ts +++ /dev/null @@ -1,99 +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 globby from 'globby'; -import fs from 'fs-extra'; -import { Template, StorageBase as Base } from '.'; -import { Logger } from 'winston'; - -interface DiskIndexEntry { - contents: Template; - location: string; -} - -export class DiskStorage implements Base { - private repository: Template[] = []; - private localIndex: DiskIndexEntry[] = []; - - private repoDir: string; - private logger?: Logger; - constructor({ - directory = `${__dirname}/../../../sample-templates`, - logger, - }: { - directory?: string; - logger?: Logger; - }) { - this.repoDir = directory; - this.logger = logger; - } - - public async list(): Promise { - if (this.repository.length === 0) { - await this.reindex(); - } - - return this.repository; - } - - public async reindex(): Promise { - this.localIndex = await this.index(); - this.repository = this.localIndex.map(({ contents }) => contents); - } - - public async prepare(templateId: string): Promise { - const template = this.localIndex.find( - ({ contents }) => contents.id === templateId, - ); - - if (!template) { - throw new Error('Template no found'); - } - - const tempDir = await fs.promises.mkdtemp(templateId); - await fs.copy(template.location, tempDir); - return tempDir; - } - - private async index(): Promise { - const matches = await globby(`${this.repoDir}/**/template-info.json`); - - const fileContents: Array<{ - location: string; - contents: string; - }> = await Promise.all( - matches.map(async (location: string) => ({ - location, - contents: await fs.readFile(location, 'utf-8'), - })), - ); - - const validFiles: DiskIndexEntry[] = []; - - for (const file of fileContents) { - try { - const contents: Template = JSON.parse(file.contents); - validFiles.push({ location: file.location, contents }); - } catch (ex) { - this.logger?.error('Failure parsing JSON for template', { - path: file.location, - }); - } - } - - return validFiles; - } -} diff --git a/plugins/scaffolder-backend/src/scaffolder/storage/index.test.ts b/plugins/scaffolder-backend/src/scaffolder/storage/index.test.ts deleted file mode 100644 index b8e3131b63..0000000000 --- a/plugins/scaffolder-backend/src/scaffolder/storage/index.test.ts +++ /dev/null @@ -1,58 +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 { StorageBase, createStorage } from '.'; -import * as winston from 'winston'; - -describe('Storage Interface Test', () => { - const mockStore = new (class MockStorage implements StorageBase { - list = jest.fn(); - prepare = jest.fn(); - reindex = jest.fn(); - - public reset = () => { - this.list.mockReset(); - this.prepare.mockReset(); - this.reindex.mockReset(); - }; - })(); - - const logger = winston.createLogger(); - - afterEach(() => mockStore.reset()); - - it('should call list of the set repo when calling list', async () => { - const store = createStorage({ store: mockStore, logger }); - await store.list(); - - expect(mockStore.list).toHaveBeenCalled(); - }); - - it('should reindex on the repo when calling reindex', async () => { - const store = createStorage({ store: mockStore, logger }); - - await store.reindex(); - - expect(mockStore.reindex).toHaveBeenCalled(); - }); - - it('should call prepare with the correct id when calling prepare', async () => { - const store = createStorage({ store: mockStore, logger }); - - await store.prepare('testid'); - - expect(mockStore.prepare).toHaveBeenCalledWith('testid'); - }); -}); diff --git a/plugins/scaffolder-backend/src/scaffolder/storage/index.ts b/plugins/scaffolder-backend/src/scaffolder/storage/index.ts deleted file mode 100644 index 4d16224ef7..0000000000 --- a/plugins/scaffolder-backend/src/scaffolder/storage/index.ts +++ /dev/null @@ -1,53 +0,0 @@ -import { Logger } from 'winston'; - -/* - * 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 interface Template { - id: string; - name: string; - description: string; - ownerId: string; -} - -export abstract class StorageBase { - // lists all templates available - abstract async list(): Promise; - // can be used to build an index of the available templates; - abstract async reindex(): Promise; - // returns a directory to run the templaterin - abstract async prepare(id: string): Promise; -} - -export interface StorageConfig { - store?: StorageBase; - logger?: Logger; -} - -class Storage implements StorageBase { - store?: StorageBase; - - constructor({ store }: StorageConfig) { - this.store = store; - } - - list = () => this.store!.list(); - prepare = (id: string) => this.store!.prepare(id); - reindex = () => this.store!.reindex(); -} - -export const createStorage = (storageConfig: StorageConfig): StorageBase => { - return new Storage(storageConfig); -}; diff --git a/plugins/scaffolder-backend/src/scaffolder/templater/cookiecutter.ts b/plugins/scaffolder-backend/src/scaffolder/templater/cookiecutter.ts index c2bf472165..ee5265fde8 100644 --- a/plugins/scaffolder-backend/src/scaffolder/templater/cookiecutter.ts +++ b/plugins/scaffolder-backend/src/scaffolder/templater/cookiecutter.ts @@ -25,7 +25,7 @@ export class CookieCutter implements TemplaterBase { ...options.values, }; - await fs.writeJSON(options.directory, cookieInfo); + await fs.writeJSON(`${options.directory}/cookiecutter.json`, cookieInfo); return ''; // run cookie cutter with new json } diff --git a/plugins/scaffolder-backend/src/scaffolder/templater/index.ts b/plugins/scaffolder-backend/src/scaffolder/templater/index.ts index 6f728d4829..80570a1c48 100644 --- a/plugins/scaffolder-backend/src/scaffolder/templater/index.ts +++ b/plugins/scaffolder-backend/src/scaffolder/templater/index.ts @@ -15,7 +15,7 @@ */ export interface RequiredTemplateValues { - componentId: string; + component_id: string; } export interface TemplaterRunOptions { diff --git a/plugins/scaffolder-backend/src/service/router.ts b/plugins/scaffolder-backend/src/service/router.ts index 97c177b915..c9b92494fd 100644 --- a/plugins/scaffolder-backend/src/service/router.ts +++ b/plugins/scaffolder-backend/src/service/router.ts @@ -17,10 +17,11 @@ import { Logger } from 'winston'; import Router from 'express-promise-router'; import express from 'express'; -import { StorageBase, TemplaterBase } from '../scaffolder'; +import { PreparerBuilder, TemplaterBase } from '../scaffolder'; +import { TemplateEntityV1alpha1 } from '@backstage/catalog-model'; export interface RouterOptions { - storage: StorageBase; + preparers: PreparerBuilder; templater: TemplaterBase; logger: Logger; } @@ -29,22 +30,47 @@ export async function createRouter( options: RouterOptions, ): Promise { const router = Router(); - const { storage, templater, logger: parentLogger } = options; + const { preparers, templater, logger: parentLogger } = options; const logger = parentLogger.child({ plugin: 'scaffolder' }); - router - .get('/v1/templates', async (_, res) => { - const templates = await storage.list(); - res.status(200).json(templates); - }) - .post('/v1/jobs', async (_, res) => { - // TODO(blam): Actually make this function work - const mock = 'templateid'; - res.status(201).json({ accepted: true }); + router.post('/v1/jobs', async (_, res) => { + // TODO(blam): Create a unique job here and return the ID so that + // The end user can poll for updates on the current job + res.status(201).json({ accepted: true }); - const path = await storage.prepare(mock); - await templater.run({ directory: path, values: { componentId: 'test' } }); - }); + // TODO(blam): Take this entity from the post body sent from the frontend + const mockEntity: TemplateEntityV1alpha1 = { + apiVersion: 'backstage.io/v1alpha1', + kind: 'Template', + metadata: { + annotations: { + 'backstage.io/managed-by-location': `file:${__dirname}/../../sample-templates/react-ssr-template/template.yaml`, + }, + name: 'react-ssr-template', + title: 'React SSR Template', + description: + 'Next.js application skeleton for creating isomorphic web applications.', + uid: '7357f4c5-aa58-4a1e-9670-18931eef771f', + etag: 'YWUxZWQyY2EtZDkxMC00MDM0LWI0ODAtMDgwMWY0YzdlMWIw', + generation: 1, + }, + spec: { + type: 'cookiecutter', + path: '.', + }, + }; + + // Get the preparer for the mock entity + const preparer = preparers.get(mockEntity); + + // Run the preparer for the mock entity to produce a temporary directory with template in + const path = await preparer.prepare(mockEntity); + + // Run the templater on the mock directory with values from the post body + await templater.run({ directory: path, values: { component_id: 'test' } }); + + console.warn(path); + }); const app = express(); app.set('logger', logger); diff --git a/plugins/scaffolder-backend/src/service/standaloneServer.ts b/plugins/scaffolder-backend/src/service/standaloneServer.ts deleted file mode 100644 index 7f553bf4bf..0000000000 --- a/plugins/scaffolder-backend/src/service/standaloneServer.ts +++ /dev/null @@ -1,57 +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 { Server } from 'http'; -import { Logger } from 'winston'; -import { - createStorage, - createTemplater, - DiskStorage, - CookieCutter, -} from '../scaffolder'; -import { createServiceBuilder, useHotMemoize } from '@backstage/backend-common'; -import { createRouter } from './router'; -export interface ServerOptions { - port: number; - enableCors: boolean; - logger: Logger; -} - -export async function startStandaloneServer( - options: ServerOptions, -): Promise { - const logger = options.logger.child({ service: 'scaffolder-backend' }); - const store = useHotMemoize(module, () => new DiskStorage({ logger })); - const templater = new CookieCutter(); - logger.debug('Creating application...'); - - const router = await createRouter({ - storage: createStorage({ store, logger }), - templater: createTemplater({ templater }), - logger, - }); - - const service = createServiceBuilder(module) - .enableCors({ origin: 'http://localhost:3000' }) - .addRouter('/catalog', router); - - return await service.start().catch(err => { - logger.error(err); - process.exit(1); - }); -} - -module.hot?.accept(); diff --git a/plugins/scaffolder-backend/test/mock-failing-template-dir/mock-template-2/template-info.json b/plugins/scaffolder-backend/test/mock-failing-template-dir/mock-template-2/template-info.json deleted file mode 100644 index 47f83f9f5c..0000000000 --- a/plugins/scaffolder-backend/test/mock-failing-template-dir/mock-template-2/template-info.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "id": "mock-template-2", - "name": "mockmannen", - "description": "mock template for building stuff in backstage", - "ownerId": "blam" -} diff --git a/plugins/scaffolder-backend/test/mock-failing-template-dir/mock-template/template-info.json b/plugins/scaffolder-backend/test/mock-failing-template-dir/mock-template/template-info.json deleted file mode 100644 index 9a3458fba1..0000000000 --- a/plugins/scaffolder-backend/test/mock-failing-template-dir/mock-template/template-info.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "id": "mock-template", - "name": "mockmannen", - "description": "mock template for building stuff in backstage", - "ownerId": "blam", - -} diff --git a/plugins/scaffolder-backend/test/mock-multiple-templates-dir/mock-template-2/template-info.json b/plugins/scaffolder-backend/test/mock-multiple-templates-dir/mock-template-2/template-info.json deleted file mode 100644 index 47f83f9f5c..0000000000 --- a/plugins/scaffolder-backend/test/mock-multiple-templates-dir/mock-template-2/template-info.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "id": "mock-template-2", - "name": "mockmannen", - "description": "mock template for building stuff in backstage", - "ownerId": "blam" -} diff --git a/plugins/scaffolder-backend/test/mock-multiple-templates-dir/mock-template/template-info.json b/plugins/scaffolder-backend/test/mock-multiple-templates-dir/mock-template/template-info.json deleted file mode 100644 index e0d323c987..0000000000 --- a/plugins/scaffolder-backend/test/mock-multiple-templates-dir/mock-template/template-info.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "id": "mock-template", - "name": "mockmannen", - "description": "mock template for building stuff in backstage", - "ownerId": "blam" -} diff --git a/plugins/scaffolder-backend/test/mock-simple-template-dir/mock-template/template-info.json b/plugins/scaffolder-backend/test/mock-simple-template-dir/mock-template/template-info.json deleted file mode 100644 index e0d323c987..0000000000 --- a/plugins/scaffolder-backend/test/mock-simple-template-dir/mock-template/template-info.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "id": "mock-template", - "name": "mockmannen", - "description": "mock template for building stuff in backstage", - "ownerId": "blam" -}