From bbef36c91a41751b26c68fc9f2d9d45f6b49aff2 Mon Sep 17 00:00:00 2001 From: blam Date: Wed, 17 Jun 2020 10:49:26 +0200 Subject: [PATCH 01/10] chore(scaffolder): Remove the old storage API's as the catalog api is now in charge of thestorage of templkates --- packages/backend/src/plugins/scaffolder.ts | 4 +- plugins/scaffolder-backend/package.json | 1 + .../src/scaffolder/index.ts | 2 - .../src/scaffolder/storage/disk.test.ts | 84 ---------------- .../src/scaffolder/storage/disk.ts | 99 ------------------- .../src/scaffolder/storage/index.test.ts | 58 ----------- .../src/scaffolder/storage/index.ts | 38 ------- .../src/scaffolder/storage/types.ts | 24 +++++ .../src/service/standaloneServer.ts | 10 +- 9 files changed, 28 insertions(+), 292 deletions(-) delete mode 100644 plugins/scaffolder-backend/src/scaffolder/storage/disk.test.ts delete mode 100644 plugins/scaffolder-backend/src/scaffolder/storage/disk.ts delete mode 100644 plugins/scaffolder-backend/src/scaffolder/storage/index.test.ts create mode 100644 plugins/scaffolder-backend/src/scaffolder/storage/types.ts diff --git a/packages/backend/src/plugins/scaffolder.ts b/packages/backend/src/plugins/scaffolder.ts index 08e700bc74..35840daa5b 100644 --- a/packages/backend/src/plugins/scaffolder.ts +++ b/packages/backend/src/plugins/scaffolder.ts @@ -17,13 +17,11 @@ import { CookieCutter, createRouter, - DiskStorage, } 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(); - return await createRouter({ storage, templater, logger }); + return await createRouter({ storage: null, templater, logger }); } diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index 3d8d95164e..907efde362 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.8", + "@backstage/catalog-model": "^0.1.1-alpha.8", "compression": "^1.7.4", "cors": "^2.8.5", "dockerode": "^3.2.0", diff --git a/plugins/scaffolder-backend/src/scaffolder/index.ts b/plugins/scaffolder-backend/src/scaffolder/index.ts index a665b32ead..29a3d99ef0 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 './templater/cookiecutter'; 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 index 4d16224ef7..f3b69cc361 100644 --- a/plugins/scaffolder-backend/src/scaffolder/storage/index.ts +++ b/plugins/scaffolder-backend/src/scaffolder/storage/index.ts @@ -1,5 +1,3 @@ -import { Logger } from 'winston'; - /* * Copyright 2020 Spotify AB * @@ -15,39 +13,3 @@ import { Logger } from 'winston'; * 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/storage/types.ts b/plugins/scaffolder-backend/src/scaffolder/storage/types.ts new file mode 100644 index 0000000000..80c860a911 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/storage/types.ts @@ -0,0 +1,24 @@ +/* + * 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 StorageBase = { + /** + * + * @param id + */ + prepare(template: TemplateEntityV1alpha1): Promise; +}; diff --git a/plugins/scaffolder-backend/src/service/standaloneServer.ts b/plugins/scaffolder-backend/src/service/standaloneServer.ts index 7f553bf4bf..cad937d304 100644 --- a/plugins/scaffolder-backend/src/service/standaloneServer.ts +++ b/plugins/scaffolder-backend/src/service/standaloneServer.ts @@ -16,12 +16,7 @@ import { Server } from 'http'; import { Logger } from 'winston'; -import { - createStorage, - createTemplater, - DiskStorage, - CookieCutter, -} from '../scaffolder'; +import { createTemplater, CookieCutter } from '../scaffolder'; import { createServiceBuilder, useHotMemoize } from '@backstage/backend-common'; import { createRouter } from './router'; export interface ServerOptions { @@ -34,12 +29,11 @@ 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 }), + storage: null, templater: createTemplater({ templater }), logger, }); From 55e6414d858f1cc52091b3f05cfcc71fde6e8ff1 Mon Sep 17 00:00:00 2001 From: blam Date: Wed, 17 Jun 2020 11:56:42 +0200 Subject: [PATCH 02/10] feat(scaffolder): Moving the storage to more task based appraches, and creating preparers --- packages/catalog-model/src/entity/Entity.ts | 2 +- .../src/kinds/TemplateEntityV1alpha1.ts | 2 + .../src/scaffolder/index.ts | 2 +- .../{storage/index.ts => prepare/file.ts} | 0 .../{storage/types.ts => prepare/index.ts} | 11 +--- .../src/scaffolder/prepare/preparers.ts | 51 ++++++++++++++++++ .../src/scaffolder/prepare/types.ts | 32 ++++++++++++ .../scaffolder-backend/src/service/router.ts | 52 +++++++++++++------ 8 files changed, 126 insertions(+), 26 deletions(-) rename plugins/scaffolder-backend/src/scaffolder/{storage/index.ts => prepare/file.ts} (100%) rename plugins/scaffolder-backend/src/scaffolder/{storage/types.ts => prepare/index.ts} (75%) create mode 100644 plugins/scaffolder-backend/src/scaffolder/prepare/preparers.ts create mode 100644 plugins/scaffolder-backend/src/scaffolder/prepare/types.ts diff --git a/packages/catalog-model/src/entity/Entity.ts b/packages/catalog-model/src/entity/Entity.ts index 70d2ba4d2b..d4f10ab883 100644 --- a/packages/catalog-model/src/entity/Entity.ts +++ b/packages/catalog-model/src/entity/Entity.ts @@ -48,7 +48,7 @@ export type Entity = { * @see https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.18/#objectmeta-v1-meta * @see https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/ */ -export type EntityMeta = { +export type EntityMeta = Record & { /** * A globally unique ID for the entity. * 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/scaffolder/index.ts b/plugins/scaffolder-backend/src/scaffolder/index.ts index 29a3d99ef0..4cc3f21a9d 100644 --- a/plugins/scaffolder-backend/src/scaffolder/index.ts +++ b/plugins/scaffolder-backend/src/scaffolder/index.ts @@ -14,5 +14,5 @@ * limitations under the License. */ export * from './templater'; - +export * from './prepare'; export * from './templater/cookiecutter'; diff --git a/plugins/scaffolder-backend/src/scaffolder/storage/index.ts b/plugins/scaffolder-backend/src/scaffolder/prepare/file.ts similarity index 100% rename from plugins/scaffolder-backend/src/scaffolder/storage/index.ts rename to plugins/scaffolder-backend/src/scaffolder/prepare/file.ts diff --git a/plugins/scaffolder-backend/src/scaffolder/storage/types.ts b/plugins/scaffolder-backend/src/scaffolder/prepare/index.ts similarity index 75% rename from plugins/scaffolder-backend/src/scaffolder/storage/types.ts rename to plugins/scaffolder-backend/src/scaffolder/prepare/index.ts index 80c860a911..748e5cebc7 100644 --- a/plugins/scaffolder-backend/src/scaffolder/storage/types.ts +++ b/plugins/scaffolder-backend/src/scaffolder/prepare/index.ts @@ -13,12 +13,5 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import type { TemplateEntityV1alpha1 } from '@backstage/catalog-model'; - -export type StorageBase = { - /** - * - * @param id - */ - prepare(template: TemplateEntityV1alpha1): Promise; -}; +export * from './preparers'; +export * from './types'; 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..dc3e318926 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/prepare/preparers.ts @@ -0,0 +1,51 @@ +/* + * 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, RemoteLocation, PreparerBuilder } from './types'; +import { TemplateEntityV1alpha1 } from '@backstage/catalog-model'; + +export class Preparers implements PreparerBuilder { + private preparerMap = new Map(); + + register(key: RemoteLocation, processor: PreparerBase) { + this.preparerMap.set(key, processor); + } + + get(template: TemplateEntityV1alpha1): PreparerBase { + const preparerKey = this.getPreparerKeyFromEntity(template); + const preparer = this.preparerMap.get(preparerKey); + + if (!preparer) { + throw new Error(`No preparer registered for type ${preparerKey}`); + } + + return preparer; + } + + private getPreparerKeyFromEntity( + entity: TemplateEntityV1alpha1, + ): RemoteLocation { + const annotation = + entity.metadata.annotations?.['backstage.io/managed-by-location'] ?? ''; + const [key] = annotation?.split(':'); + + if (!key) { + throw new Error('Failed to parse the location data'); + } + + return key as RemoteLocation; + } +} 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..1c703d10fd --- /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(key: RemoteLocation, preparer: PreparerBase): void; + get(key: TemplateEntityV1alpha1): PreparerBase; +}; + +export type RemoteLocation = 'file'; diff --git a/plugins/scaffolder-backend/src/service/router.ts b/plugins/scaffolder-backend/src/service/router.ts index 97c177b915..57ac1413fc 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,43 @@ 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): Actually make this function work + // res.status(201).json({ accepted: true }); - const path = await storage.prepare(mock); - await templater.run({ directory: path, values: { componentId: 'test' } }); - }); + const mockEntity: 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: '.', + }, + }; + + // fetch the entity from service catalog here. + const preparer = preparers.get(mockEntity); + + // + const path = await preparer.prepare(mockEntity); + + await templater.run({ directory: path, values: { componentId: 'test' } }); + }); const app = express(); app.set('logger', logger); From b01217cdb54dbf87bb887c1df89ba91236436159 Mon Sep 17 00:00:00 2001 From: blam Date: Wed, 17 Jun 2020 14:56:30 +0200 Subject: [PATCH 03/10] feat(scaffolder): starting to move things around and remove the old scaffolder data --- plugins/scaffolder-backend/package.json | 3 +- .../src/scaffolder/prepare/file.ts | 34 +++++++++++++++++++ .../src/scaffolder/prepare/preparers.ts | 8 +++-- .../scaffolder-backend/src/service/router.ts | 2 +- .../mock-template-2/template-info.json | 6 ---- .../mock-template/template-info.json | 7 ---- .../mock-template-2/template-info.json | 6 ---- .../mock-template/template-info.json | 6 ---- .../mock-template/template-info.json | 6 ---- 9 files changed, 42 insertions(+), 36 deletions(-) delete mode 100644 plugins/scaffolder-backend/test/mock-failing-template-dir/mock-template-2/template-info.json delete mode 100644 plugins/scaffolder-backend/test/mock-failing-template-dir/mock-template/template-info.json delete mode 100644 plugins/scaffolder-backend/test/mock-multiple-templates-dir/mock-template-2/template-info.json delete mode 100644 plugins/scaffolder-backend/test/mock-multiple-templates-dir/mock-template/template-info.json delete mode 100644 plugins/scaffolder-backend/test/mock-simple-template-dir/mock-template/template-info.json diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index 907efde362..1838738c01 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -38,6 +38,7 @@ "@backstage/cli": "^0.1.1-alpha.8", "@types/fs-extra": "^9.0.1", "@types/supertest": "^2.0.8", - "supertest": "^4.0.2" + "supertest": "^4.0.2", + "yaml": "^1.10.0" } } diff --git a/plugins/scaffolder-backend/src/scaffolder/prepare/file.ts b/plugins/scaffolder-backend/src/scaffolder/prepare/file.ts index f3b69cc361..d7d21baa29 100644 --- a/plugins/scaffolder-backend/src/scaffolder/prepare/file.ts +++ b/plugins/scaffolder-backend/src/scaffolder/prepare/file.ts @@ -13,3 +13,37 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +// import fs from 'fs-extra'; +import fs from 'fs'; +import { + TemplateEntityV1alpha1, + LOCATION_ANNOTATION, +} from '@backstage/catalog-model'; +import { InputError } from '@backstage/backend-common'; +import { PreparerBase } from './types'; + +export class FilePreparer implements PreparerBase { + async prepare(template: TemplateEntityV1alpha1): Promise { + const location = template?.metadata?.annotations?.[LOCATION_ANNOTATION]; + + const [locationType, actualLocation] = (location ?? '').split(/:(.+)/); + if (locationType !== 'file') { + throw new InputError( + `Wrong location type: ${locationType}, should be 'file'`, + ); + } + + if (!actualLocation) { + throw new InputError( + `Couldn't parse location for template: ${template.metadata.name}`, + ); + } + + const templateId = template.metadata.name; + + const tempDir = await fs.promises.mkdtemp(templateId); + + // await fs.copy(actualLocation, tempDir); + return tempDir; + } +} diff --git a/plugins/scaffolder-backend/src/scaffolder/prepare/preparers.ts b/plugins/scaffolder-backend/src/scaffolder/prepare/preparers.ts index dc3e318926..97db3a3100 100644 --- a/plugins/scaffolder-backend/src/scaffolder/prepare/preparers.ts +++ b/plugins/scaffolder-backend/src/scaffolder/prepare/preparers.ts @@ -15,7 +15,10 @@ */ import { PreparerBase, RemoteLocation, PreparerBuilder } from './types'; -import { TemplateEntityV1alpha1 } from '@backstage/catalog-model'; +import { + TemplateEntityV1alpha1, + LOCATION_ANNOTATION, +} from '@backstage/catalog-model'; export class Preparers implements PreparerBuilder { private preparerMap = new Map(); @@ -38,8 +41,7 @@ export class Preparers implements PreparerBuilder { private getPreparerKeyFromEntity( entity: TemplateEntityV1alpha1, ): RemoteLocation { - const annotation = - entity.metadata.annotations?.['backstage.io/managed-by-location'] ?? ''; + const annotation = entity.metadata.annotations?.[LOCATION_ANNOTATION] ?? ''; const [key] = annotation?.split(':'); if (!key) { diff --git a/plugins/scaffolder-backend/src/service/router.ts b/plugins/scaffolder-backend/src/service/router.ts index 57ac1413fc..74507d528c 100644 --- a/plugins/scaffolder-backend/src/service/router.ts +++ b/plugins/scaffolder-backend/src/service/router.ts @@ -35,7 +35,7 @@ export async function createRouter( router.post('/v1/jobs', async (_, res) => { // TODO(blam): Actually make this function work - // res.status(201).json({ accepted: true }); + res.status(201).json({ accepted: true }); const mockEntity: TemplateEntityV1alpha1 = { apiVersion: 'backstage.io/v1alpha1', 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" -} From 58866e6a620db438f663850913557fa890f1e99d Mon Sep 17 00:00:00 2001 From: blam Date: Wed, 17 Jun 2020 17:39:47 +0200 Subject: [PATCH 04/10] feat(scaffolder): Fixing the path resolution for temporary directory and copy all files apart from the template.yaml definition --- .../src/scaffolder/prepare/file.test.ts | 50 ++++++++++++++++++ .../src/scaffolder/prepare/file.ts | 24 ++++++--- .../src/scaffolder/prepare/preparers.test.ts | 52 +++++++++++++++++++ .../template-1/expected_file.ts | 16 ++++++ .../test/test-nested-template/template.yaml | 9 ++++ .../test-simple-template/expected_file.ts | 16 ++++++ .../test/test-simple-template/template.yaml | 8 +++ 7 files changed, 169 insertions(+), 6 deletions(-) create mode 100644 plugins/scaffolder-backend/src/scaffolder/prepare/file.test.ts create mode 100644 plugins/scaffolder-backend/src/scaffolder/prepare/preparers.test.ts create mode 100644 plugins/scaffolder-backend/test/test-nested-template/template-1/expected_file.ts create mode 100644 plugins/scaffolder-backend/test/test-nested-template/template.yaml create mode 100644 plugins/scaffolder-backend/test/test-simple-template/expected_file.ts create mode 100644 plugins/scaffolder-backend/test/test-simple-template/template.yaml 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..6ab0aa3fb0 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/prepare/file.test.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 YAML from 'yaml'; +import { FilePreparer } from './file'; +import path from 'path'; +import { + TemplateEntityV1alpha1, + LOCATION_ANNOTATION, +} from '@backstage/catalog-model'; + +describe('File preparer', () => { + it('resolves relative path from the template', async () => { + const locationForTemplateYaml = path.resolve( + __dirname, + '../../..', + 'test/test-simple-template/template.yaml', + ); + + 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); + + expect(fs.existsSync(`${resultDir}/expected_file.ts`)).toBe(true); + expect(fs.existsSync(`${resultDir}/template.yaml`)).toBe(false); + }); +}); diff --git a/plugins/scaffolder-backend/src/scaffolder/prepare/file.ts b/plugins/scaffolder-backend/src/scaffolder/prepare/file.ts index d7d21baa29..184e1a6b1f 100644 --- a/plugins/scaffolder-backend/src/scaffolder/prepare/file.ts +++ b/plugins/scaffolder-backend/src/scaffolder/prepare/file.ts @@ -13,8 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -// import fs from 'fs-extra'; -import fs from 'fs'; +import fs from 'fs-extra'; +import path from 'path'; +import os from 'os'; import { TemplateEntityV1alpha1, LOCATION_ANNOTATION, @@ -26,14 +27,16 @@ export class FilePreparer implements PreparerBase { async prepare(template: TemplateEntityV1alpha1): Promise { const location = template?.metadata?.annotations?.[LOCATION_ANNOTATION]; - const [locationType, actualLocation] = (location ?? '').split(/:(.+)/); + const [locationType, templateEntityLocation] = (location ?? '').split( + /:(.+)/, + ); if (locationType !== 'file') { throw new InputError( `Wrong location type: ${locationType}, should be 'file'`, ); } - if (!actualLocation) { + if (!templateEntityLocation) { throw new InputError( `Couldn't parse location for template: ${template.metadata.name}`, ); @@ -41,9 +44,18 @@ export class FilePreparer implements PreparerBase { const templateId = template.metadata.name; - const tempDir = await fs.promises.mkdtemp(templateId); + const tempDir = await fs.promises.mkdtemp( + path.join(os.tmpdir(), templateId), + ); + + const parentDirectory = path.dirname(templateEntityLocation); + + await fs.copy(parentDirectory, tempDir, { + filter: src => src !== templateEntityLocation, + }); + + // TODO(blam): Need to use the `spec.path` with path.resolve to ensure that the test case for test-nested-templates will work - // await fs.copy(actualLocation, tempDir); return tempDir; } } 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..96e6b7d5b9 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/prepare/preparers.test.ts @@ -0,0 +1,52 @@ +/* + * 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'; + +describe('Preparers', () => { + it('should throw an error when the preparer for the source location is not registered', () => { + const preparers = new 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: '.', + }, + }; + + expect(() => preparers.get(mockTemplate)).toThrow( + expect.objectContaining({ + message: 'No preparer registered for type file', + }), + ); + }); + it('should return the correct preparer when the source matches'); + it('should throw an error if the srouce is not available'); +}); diff --git a/plugins/scaffolder-backend/test/test-nested-template/template-1/expected_file.ts b/plugins/scaffolder-backend/test/test-nested-template/template-1/expected_file.ts new file mode 100644 index 0000000000..19b0312029 --- /dev/null +++ b/plugins/scaffolder-backend/test/test-nested-template/template-1/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/test/test-nested-template/template.yaml b/plugins/scaffolder-backend/test/test-nested-template/template.yaml new file mode 100644 index 0000000000..713094defe --- /dev/null +++ b/plugins/scaffolder-backend/test/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/test/test-simple-template/expected_file.ts b/plugins/scaffolder-backend/test/test-simple-template/expected_file.ts new file mode 100644 index 0000000000..19b0312029 --- /dev/null +++ b/plugins/scaffolder-backend/test/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/test/test-simple-template/template.yaml b/plugins/scaffolder-backend/test/test-simple-template/template.yaml new file mode 100644 index 0000000000..7ee2132d59 --- /dev/null +++ b/plugins/scaffolder-backend/test/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 From 23c58be5254d98edb1a08be3d492ff073bc27a3b Mon Sep 17 00:00:00 2001 From: Ivan Shmidt Date: Wed, 17 Jun 2020 20:49:14 +0200 Subject: [PATCH 05/10] test(scaffolder): add nested template test --- .../template-1/expected_file.ts | 0 .../test-nested-template/template.yaml | 0 .../test-simple-template/expected_file.ts | 0 .../test-simple-template/template.yaml | 0 .../src/scaffolder/prepare/file.test.ts | 55 ++++++++++++------- .../src/scaffolder/prepare/file.ts | 7 ++- 6 files changed, 38 insertions(+), 24 deletions(-) rename plugins/scaffolder-backend/{test => fixtures}/test-nested-template/template-1/expected_file.ts (100%) rename plugins/scaffolder-backend/{test => fixtures}/test-nested-template/template.yaml (100%) rename plugins/scaffolder-backend/{test => fixtures}/test-simple-template/expected_file.ts (100%) rename plugins/scaffolder-backend/{test => fixtures}/test-simple-template/template.yaml (100%) diff --git a/plugins/scaffolder-backend/test/test-nested-template/template-1/expected_file.ts b/plugins/scaffolder-backend/fixtures/test-nested-template/template-1/expected_file.ts similarity index 100% rename from plugins/scaffolder-backend/test/test-nested-template/template-1/expected_file.ts rename to plugins/scaffolder-backend/fixtures/test-nested-template/template-1/expected_file.ts diff --git a/plugins/scaffolder-backend/test/test-nested-template/template.yaml b/plugins/scaffolder-backend/fixtures/test-nested-template/template.yaml similarity index 100% rename from plugins/scaffolder-backend/test/test-nested-template/template.yaml rename to plugins/scaffolder-backend/fixtures/test-nested-template/template.yaml diff --git a/plugins/scaffolder-backend/test/test-simple-template/expected_file.ts b/plugins/scaffolder-backend/fixtures/test-simple-template/expected_file.ts similarity index 100% rename from plugins/scaffolder-backend/test/test-simple-template/expected_file.ts rename to plugins/scaffolder-backend/fixtures/test-simple-template/expected_file.ts diff --git a/plugins/scaffolder-backend/test/test-simple-template/template.yaml b/plugins/scaffolder-backend/fixtures/test-simple-template/template.yaml similarity index 100% rename from plugins/scaffolder-backend/test/test-simple-template/template.yaml rename to plugins/scaffolder-backend/fixtures/test-simple-template/template.yaml diff --git a/plugins/scaffolder-backend/src/scaffolder/prepare/file.test.ts b/plugins/scaffolder-backend/src/scaffolder/prepare/file.test.ts index 6ab0aa3fb0..b279b56ea3 100644 --- a/plugins/scaffolder-backend/src/scaffolder/prepare/file.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/prepare/file.test.ts @@ -23,28 +23,41 @@ import { LOCATION_ANNOTATION, } from '@backstage/catalog-model'; +const setupTest = async (fixturePath: string) => { + const locationForTemplateYaml = path.resolve( + __dirname, + '../../../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('resolves relative path from the template', async () => { - const locationForTemplateYaml = path.resolve( - __dirname, - '../../..', - 'test/test-simple-template/template.yaml', - ); - - 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); - - expect(fs.existsSync(`${resultDir}/expected_file.ts`)).toBe(true); + 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 index 184e1a6b1f..b9e8942aae 100644 --- a/plugins/scaffolder-backend/src/scaffolder/prepare/file.ts +++ b/plugins/scaffolder-backend/src/scaffolder/prepare/file.ts @@ -48,14 +48,15 @@ export class FilePreparer implements PreparerBase { path.join(os.tmpdir(), templateId), ); - const parentDirectory = path.dirname(templateEntityLocation); + const parentDirectory = path.resolve( + path.dirname(templateEntityLocation), + template.spec.path ?? '.', + ); await fs.copy(parentDirectory, tempDir, { filter: src => src !== templateEntityLocation, }); - // TODO(blam): Need to use the `spec.path` with path.resolve to ensure that the test case for test-nested-templates will work - return tempDir; } } From 5835f06524c6de4bed99a0291443b4e0add8f4db Mon Sep 17 00:00:00 2001 From: blam Date: Thu, 18 Jun 2020 14:10:02 +0200 Subject: [PATCH 06/10] chore(scaffolder): Tidy up some things around the PR to make a litte cleaner and add some comments in places --- .../react-ssr-template/template-info.json | 6 --- plugins/scaffolder-backend/src/run.ts | 34 ------------- .../src/scaffolder/prepare/file.test.ts | 6 ++- .../scaffolder-backend/src/service/router.ts | 12 +++-- .../src/service/standaloneServer.ts | 51 ------------------- 5 files changed, 11 insertions(+), 98 deletions(-) delete mode 100644 plugins/scaffolder-backend/sample-templates/react-ssr-template/template-info.json delete mode 100644 plugins/scaffolder-backend/src/run.ts delete mode 100644 plugins/scaffolder-backend/src/service/standaloneServer.ts 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/src/run.ts b/plugins/scaffolder-backend/src/run.ts deleted file mode 100644 index 133aad163e..0000000000 --- a/plugins/scaffolder-backend/src/run.ts +++ /dev/null @@ -1,34 +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 { 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); -}); diff --git a/plugins/scaffolder-backend/src/scaffolder/prepare/file.test.ts b/plugins/scaffolder-backend/src/scaffolder/prepare/file.test.ts index b279b56ea3..b9472fbc8d 100644 --- a/plugins/scaffolder-backend/src/scaffolder/prepare/file.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/prepare/file.test.ts @@ -25,8 +25,10 @@ import { const setupTest = async (fixturePath: string) => { const locationForTemplateYaml = path.resolve( - __dirname, - '../../../fixtures', + path.dirname( + require.resolve('@backstage/plugin-scaffolder-backend/package'), + ), + 'fixtures', fixturePath, ); diff --git a/plugins/scaffolder-backend/src/service/router.ts b/plugins/scaffolder-backend/src/service/router.ts index 74507d528c..b05983e1b4 100644 --- a/plugins/scaffolder-backend/src/service/router.ts +++ b/plugins/scaffolder-backend/src/service/router.ts @@ -34,16 +34,17 @@ export async function createRouter( const logger = parentLogger.child({ plugin: 'scaffolder' }); router.post('/v1/jobs', async (_, res) => { - // TODO(blam): Actually make this function work + // 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 }); + // 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:/Users/blam/dev/spotify/backstage/plugins/scaffolder-backend/sample-templates/react-ssr-template/template.yaml', + 'backstage.io/managed-by-location': `file:${__dirname}/../../sample-templates/react-ssr-template/template.yaml`, }, name: 'react-ssr-template', title: 'React SSR Template', @@ -59,12 +60,13 @@ export async function createRouter( }, }; - // fetch the entity from service catalog here. + // 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: { componentId: 'test' } }); }); diff --git a/plugins/scaffolder-backend/src/service/standaloneServer.ts b/plugins/scaffolder-backend/src/service/standaloneServer.ts deleted file mode 100644 index cad937d304..0000000000 --- a/plugins/scaffolder-backend/src/service/standaloneServer.ts +++ /dev/null @@ -1,51 +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 { createTemplater, 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 templater = new CookieCutter(); - logger.debug('Creating application...'); - - const router = await createRouter({ - storage: null, - 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(); From 299609a0207ab23402c9421f5b065f380a22a325 Mon Sep 17 00:00:00 2001 From: blam Date: Thu, 18 Jun 2020 17:42:38 +0200 Subject: [PATCH 07/10] chore(scaffolder): refactoring some of the entity parsing away so that we can re-use it. and fix some of the semantic namings --- .../src/scaffolder/prepare/file.ts | 26 +++------ .../src/scaffolder/prepare/helpers.ts | 56 +++++++++++++++++++ .../src/scaffolder/prepare/index.ts | 1 + .../src/scaffolder/prepare/preparers.test.ts | 54 +++++++++++++++--- .../src/scaffolder/prepare/preparers.ts | 33 +++-------- .../src/scaffolder/prepare/types.ts | 6 +- 6 files changed, 121 insertions(+), 55 deletions(-) create mode 100644 plugins/scaffolder-backend/src/scaffolder/prepare/helpers.ts diff --git a/plugins/scaffolder-backend/src/scaffolder/prepare/file.ts b/plugins/scaffolder-backend/src/scaffolder/prepare/file.ts index b9e8942aae..7a924d9a99 100644 --- a/plugins/scaffolder-backend/src/scaffolder/prepare/file.ts +++ b/plugins/scaffolder-backend/src/scaffolder/prepare/file.ts @@ -16,32 +16,20 @@ import fs from 'fs-extra'; import path from 'path'; import os from 'os'; -import { - TemplateEntityV1alpha1, - LOCATION_ANNOTATION, -} from '@backstage/catalog-model'; +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 location = template?.metadata?.annotations?.[LOCATION_ANNOTATION]; + const { protocol, location } = parseLocationAnnotation(template); - const [locationType, templateEntityLocation] = (location ?? '').split( - /:(.+)/, - ); - if (locationType !== 'file') { + if (protocol !== 'file') { throw new InputError( - `Wrong location type: ${locationType}, should be 'file'`, + `Wrong location protocol: ${protocol}, should be 'file'`, ); } - - if (!templateEntityLocation) { - throw new InputError( - `Couldn't parse location for template: ${template.metadata.name}`, - ); - } - const templateId = template.metadata.name; const tempDir = await fs.promises.mkdtemp( @@ -49,12 +37,12 @@ export class FilePreparer implements PreparerBase { ); const parentDirectory = path.resolve( - path.dirname(templateEntityLocation), + path.dirname(location), template.spec.path ?? '.', ); await fs.copy(parentDirectory, tempDir, { - filter: src => src !== templateEntityLocation, + 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 index 748e5cebc7..b9bc3a4294 100644 --- a/plugins/scaffolder-backend/src/scaffolder/prepare/index.ts +++ b/plugins/scaffolder-backend/src/scaffolder/prepare/index.ts @@ -15,3 +15,4 @@ */ export * from './preparers'; export * from './types'; +export * from './helpers'; diff --git a/plugins/scaffolder-backend/src/scaffolder/prepare/preparers.test.ts b/plugins/scaffolder-backend/src/scaffolder/prepare/preparers.test.ts index 96e6b7d5b9..a8f4b4ae99 100644 --- a/plugins/scaffolder-backend/src/scaffolder/prepare/preparers.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/prepare/preparers.test.ts @@ -15,18 +15,54 @@ */ 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(); - const mockTemplate: TemplateEntityV1alpha1 = { + + 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: { - 'backstage.io/managed-by-location': - 'file:/Users/blam/dev/spotify/backstage/plugins/scaffolder-backend/sample-templates/react-ssr-template/template.yaml', - }, + annotations: {}, name: 'react-ssr-template', title: 'React SSR Template', description: @@ -41,12 +77,12 @@ describe('Preparers', () => { }, }; - expect(() => preparers.get(mockTemplate)).toThrow( + const preparers = new Preparers(); + + expect(() => preparers.get(brokenTemplate)).toThrow( expect.objectContaining({ - message: 'No preparer registered for type file', + message: expect.stringContaining('No location annotation provided'), }), ); }); - it('should return the correct preparer when the source matches'); - it('should throw an error if the srouce is not available'); }); diff --git a/plugins/scaffolder-backend/src/scaffolder/prepare/preparers.ts b/plugins/scaffolder-backend/src/scaffolder/prepare/preparers.ts index 97db3a3100..046890bdca 100644 --- a/plugins/scaffolder-backend/src/scaffolder/prepare/preparers.ts +++ b/plugins/scaffolder-backend/src/scaffolder/prepare/preparers.ts @@ -14,40 +14,25 @@ * limitations under the License. */ -import { PreparerBase, RemoteLocation, PreparerBuilder } from './types'; -import { - TemplateEntityV1alpha1, - LOCATION_ANNOTATION, -} from '@backstage/catalog-model'; +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(); + private preparerMap = new Map(); - register(key: RemoteLocation, processor: PreparerBase) { - this.preparerMap.set(key, processor); + register(protocol: RemoteProtocol, preparer: PreparerBase) { + this.preparerMap.set(protocol, preparer); } get(template: TemplateEntityV1alpha1): PreparerBase { - const preparerKey = this.getPreparerKeyFromEntity(template); - const preparer = this.preparerMap.get(preparerKey); + const { protocol } = parseLocationAnnotation(template); + const preparer = this.preparerMap.get(protocol); if (!preparer) { - throw new Error(`No preparer registered for type ${preparerKey}`); + throw new Error(`No preparer registered for type: "${protocol}"`); } return preparer; } - - private getPreparerKeyFromEntity( - entity: TemplateEntityV1alpha1, - ): RemoteLocation { - const annotation = entity.metadata.annotations?.[LOCATION_ANNOTATION] ?? ''; - const [key] = annotation?.split(':'); - - if (!key) { - throw new Error('Failed to parse the location data'); - } - - return key as RemoteLocation; - } } diff --git a/plugins/scaffolder-backend/src/scaffolder/prepare/types.ts b/plugins/scaffolder-backend/src/scaffolder/prepare/types.ts index 1c703d10fd..3251bd2780 100644 --- a/plugins/scaffolder-backend/src/scaffolder/prepare/types.ts +++ b/plugins/scaffolder-backend/src/scaffolder/prepare/types.ts @@ -25,8 +25,8 @@ export type PreparerBase = { }; export type PreparerBuilder = { - register(key: RemoteLocation, preparer: PreparerBase): void; - get(key: TemplateEntityV1alpha1): PreparerBase; + register(protocol: RemoteProtocol, preparer: PreparerBase): void; + get(template: TemplateEntityV1alpha1): PreparerBase; }; -export type RemoteLocation = 'file'; +export type RemoteProtocol = 'file'; From ed0539769441f550c539b956ff93c5238a0ccb9a Mon Sep 17 00:00:00 2001 From: blam Date: Thu, 18 Jun 2020 18:09:36 +0200 Subject: [PATCH 08/10] chore(scaffolder): fix the exporting of the FilePreparer so we can actually setup a scaffolder in the backend --- packages/backend/src/plugins/scaffolder.ts | 8 +++++++- .../scaffolder-backend/src/scaffolder/prepare/index.ts | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/packages/backend/src/plugins/scaffolder.ts b/packages/backend/src/plugins/scaffolder.ts index 35840daa5b..ce3a7ea2bd 100644 --- a/packages/backend/src/plugins/scaffolder.ts +++ b/packages/backend/src/plugins/scaffolder.ts @@ -17,11 +17,17 @@ import { CookieCutter, createRouter, + FilePreparer, + Preparers, } from '@backstage/plugin-scaffolder-backend'; import type { PluginEnvironment } from '../types'; export default async function createPlugin({ logger }: PluginEnvironment) { const templater = new CookieCutter(); + const filePreparer = new FilePreparer(); + const preparers = new Preparers(); - return await createRouter({ storage: null, templater, logger }); + preparers.register('file', filePreparer); + + return await createRouter({ preparers, templater, logger }); } diff --git a/plugins/scaffolder-backend/src/scaffolder/prepare/index.ts b/plugins/scaffolder-backend/src/scaffolder/prepare/index.ts index b9bc3a4294..0b1a72eaa3 100644 --- a/plugins/scaffolder-backend/src/scaffolder/prepare/index.ts +++ b/plugins/scaffolder-backend/src/scaffolder/prepare/index.ts @@ -16,3 +16,4 @@ export * from './preparers'; export * from './types'; export * from './helpers'; +export * from './file'; From f04844441cf2130953e426dfb68ea6c00d5dfd0f Mon Sep 17 00:00:00 2001 From: blam Date: Fri, 19 Jun 2020 11:05:25 +0200 Subject: [PATCH 09/10] chore(scaffolder): move express to dependencies for reasons --- plugins/scaffolder-backend/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/scaffolder-backend/package.json b/plugins/scaffolder-backend/package.json index d6ac8d9ec9..0edf7a8fe9 100644 --- a/plugins/scaffolder-backend/package.json +++ b/plugins/scaffolder-backend/package.json @@ -23,6 +23,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", "dockerode": "^3.2.0", @@ -37,7 +38,6 @@ "devDependencies": { "@backstage/cli": "^0.1.1-alpha.9", "@types/fs-extra": "^9.0.1", - "@types/express": "^4.17.6", "@types/supertest": "^2.0.8", "supertest": "^4.0.2", "yaml": "^1.10.0" From 95344229ab11c432d52a229f6410e1c23eaedc50 Mon Sep 17 00:00:00 2001 From: blam Date: Sat, 20 Jun 2020 03:33:41 +0200 Subject: [PATCH 10/10] chore(scaffolder): moving things around to make things work for the actually scaffoling --- .../react-ssr-template/hooks/post_gen_project.sh | 2 +- .../.editorconfig | 0 .../.eslintignore | 0 .../.eslintrc.js | 0 .../.github/workflows/build.yml | 0 .../.gitignore | 0 .../.nvmrc | 0 .../README.md | 0 .../babel.config.js | 0 .../jest.config.js | 0 .../next-env.d.ts | 0 .../next.config.js | 0 .../package.json | 0 .../prettier.config.js | 0 .../public/static/fonts.css | 0 .../src/__tests__/index.test.tsx | 0 .../src/components/Header.tsx | 0 .../src/pages/_app.tsx | 0 .../src/pages/_document.tsx | 0 .../src/pages/api/ping.ts | 0 .../src/pages/index.tsx | 0 .../tsconfig.json | 0 .../src/scaffolder/templater/cookiecutter.ts | 2 +- plugins/scaffolder-backend/src/scaffolder/templater/index.ts | 2 +- plugins/scaffolder-backend/src/service/router.ts | 4 +++- 25 files changed, 6 insertions(+), 4 deletions(-) rename plugins/scaffolder-backend/sample-templates/react-ssr-template/{{{cookiecutter.componentId}} => {{cookiecutter.component_id}}}/.editorconfig (100%) rename plugins/scaffolder-backend/sample-templates/react-ssr-template/{{{cookiecutter.componentId}} => {{cookiecutter.component_id}}}/.eslintignore (100%) rename plugins/scaffolder-backend/sample-templates/react-ssr-template/{{{cookiecutter.componentId}} => {{cookiecutter.component_id}}}/.eslintrc.js (100%) rename plugins/scaffolder-backend/sample-templates/react-ssr-template/{{{cookiecutter.componentId}} => {{cookiecutter.component_id}}}/.github/workflows/build.yml (100%) rename plugins/scaffolder-backend/sample-templates/react-ssr-template/{{{cookiecutter.componentId}} => {{cookiecutter.component_id}}}/.gitignore (100%) rename plugins/scaffolder-backend/sample-templates/react-ssr-template/{{{cookiecutter.componentId}} => {{cookiecutter.component_id}}}/.nvmrc (100%) rename plugins/scaffolder-backend/sample-templates/react-ssr-template/{{{cookiecutter.componentId}} => {{cookiecutter.component_id}}}/README.md (100%) rename plugins/scaffolder-backend/sample-templates/react-ssr-template/{{{cookiecutter.componentId}} => {{cookiecutter.component_id}}}/babel.config.js (100%) rename plugins/scaffolder-backend/sample-templates/react-ssr-template/{{{cookiecutter.componentId}} => {{cookiecutter.component_id}}}/jest.config.js (100%) rename plugins/scaffolder-backend/sample-templates/react-ssr-template/{{{cookiecutter.componentId}} => {{cookiecutter.component_id}}}/next-env.d.ts (100%) rename plugins/scaffolder-backend/sample-templates/react-ssr-template/{{{cookiecutter.componentId}} => {{cookiecutter.component_id}}}/next.config.js (100%) rename plugins/scaffolder-backend/sample-templates/react-ssr-template/{{{cookiecutter.componentId}} => {{cookiecutter.component_id}}}/package.json (100%) rename plugins/scaffolder-backend/sample-templates/react-ssr-template/{{{cookiecutter.componentId}} => {{cookiecutter.component_id}}}/prettier.config.js (100%) rename plugins/scaffolder-backend/sample-templates/react-ssr-template/{{{cookiecutter.componentId}} => {{cookiecutter.component_id}}}/public/static/fonts.css (100%) rename plugins/scaffolder-backend/sample-templates/react-ssr-template/{{{cookiecutter.componentId}} => {{cookiecutter.component_id}}}/src/__tests__/index.test.tsx (100%) rename plugins/scaffolder-backend/sample-templates/react-ssr-template/{{{cookiecutter.componentId}} => {{cookiecutter.component_id}}}/src/components/Header.tsx (100%) rename plugins/scaffolder-backend/sample-templates/react-ssr-template/{{{cookiecutter.componentId}} => {{cookiecutter.component_id}}}/src/pages/_app.tsx (100%) rename plugins/scaffolder-backend/sample-templates/react-ssr-template/{{{cookiecutter.componentId}} => {{cookiecutter.component_id}}}/src/pages/_document.tsx (100%) rename plugins/scaffolder-backend/sample-templates/react-ssr-template/{{{cookiecutter.componentId}} => {{cookiecutter.component_id}}}/src/pages/api/ping.ts (100%) rename plugins/scaffolder-backend/sample-templates/react-ssr-template/{{{cookiecutter.componentId}} => {{cookiecutter.component_id}}}/src/pages/index.tsx (100%) rename plugins/scaffolder-backend/sample-templates/react-ssr-template/{{{cookiecutter.componentId}} => {{cookiecutter.component_id}}}/tsconfig.json (100%) 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/{{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/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 b05983e1b4..c9b92494fd 100644 --- a/plugins/scaffolder-backend/src/service/router.ts +++ b/plugins/scaffolder-backend/src/service/router.ts @@ -67,7 +67,9 @@ export async function createRouter( 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: { componentId: 'test' } }); + await templater.run({ directory: path, values: { component_id: 'test' } }); + + console.warn(path); }); const app = express();