diff --git a/plugins/scaffolder-backend/sample-templates/docs-template/{{cookiecutter.component_id}}/catalog-info.yaml b/plugins/scaffolder-backend/sample-templates/docs-template/{{cookiecutter.component_id}}/catalog-info.yaml index 90b55801a6..3464fc8d5d 100644 --- a/plugins/scaffolder-backend/sample-templates/docs-template/{{cookiecutter.component_id}}/catalog-info.yaml +++ b/plugins/scaffolder-backend/sample-templates/docs-template/{{cookiecutter.component_id}}/catalog-info.yaml @@ -3,8 +3,8 @@ kind: Component metadata: name: {{cookiecutter.component_id | jsonify}} description: {{cookiecutter.description | jsonify}} - annotations: - github.com/project-slug: {{cookiecutter.storePath | jsonify}} + github.com/project-slug: {{cookiecutter.destination.git.owner + "/" + cookiecutter.destination.git.name}} + backstage.io/source-ref: {{cookiecutter.storePath | jsonify}} backstage.io/techdocs-ref: url:{{cookiecutter.storePath}} spec: type: documentation diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/catalog-info.yaml b/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/catalog-info.yaml index 980d107c5b..b6e152ee72 100644 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/catalog-info.yaml +++ b/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/catalog-info.yaml @@ -4,7 +4,8 @@ metadata: name: {{cookiecutter.component_id | jsonify}} description: {{cookiecutter.description | jsonify}} annotations: - github.com/project-slug: {{cookiecutter.storePath | jsonify}} + github.com/project-slug: {{cookiecutter.destination.git.owner + "/" + cookiecutter.destination.git.name}} + backstage.io/source-url: {{cookiecutter.storePath | jsonify}} backstage.io/techdocs-ref: url:{{cookiecutter.storePath}} spec: type: website diff --git a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/{{cookiecutter.component_id}}/catalog-info.yaml b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/{{cookiecutter.component_id}}/catalog-info.yaml index 2addbe23da..83e0c58d20 100644 --- a/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/{{cookiecutter.component_id}}/catalog-info.yaml +++ b/plugins/scaffolder-backend/sample-templates/springboot-grpc-template/{{cookiecutter.component_id}}/catalog-info.yaml @@ -4,7 +4,8 @@ metadata: name: {{cookiecutter.component_id | jsonify}} description: {{cookiecutter.description | jsonify}} annotations: - github.com/project-slug: {{cookiecutter.storePath | jsonify}} + github.com/project-slug: {{cookiecutter.destination.git.owner + "/" + cookiecutter.destination.git.name}} + backstage.io/source-url: {{cookiecutter.storePath | jsonify}} backstage.io/techdocs-ref: url:{{cookiecutter.storePath}} spec: type: service diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/templater/cookiecutter.test.ts b/plugins/scaffolder-backend/src/scaffolder/stages/templater/cookiecutter.test.ts index 568c95a073..656abd9613 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/templater/cookiecutter.test.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/templater/cookiecutter.test.ts @@ -26,6 +26,7 @@ import path from 'path'; import { RunDockerContainerOptions, RunCommandOptions } from './helpers'; import { PassThrough } from 'stream'; import Docker from 'dockerode'; +import parseGitUrl from 'git-url-parse'; const commandExists = require('command-exists-promise'); @@ -56,16 +57,21 @@ describe('CookieCutter Templater', () => { const values = { owner: 'blobby', - storePath: 'backstage/end-repo', + storePath: 'https://github.com/org/repo', description: 'description', component_id: 'newthing', + destination: { + git: parseGitUrl('https://github.com/org/repo'), + }, }; await cookie.run({ directory: tempdir, values, dockerClient: mockDocker }); const cookieCutterJson = await fs.readJSON(`${tempdir}/cookiecutter.json`); - expect(cookieCutterJson).toEqual(expect.objectContaining(values)); + expect(cookieCutterJson).toEqual( + expect.objectContaining(JSON.parse(JSON.stringify(values))), + ); }); it('should merge any value that is in the cookiecutter.json path already', async () => { @@ -78,15 +84,24 @@ describe('CookieCutter Templater', () => { const values = { owner: 'blobby', - storePath: 'backstage/end-repo', + storePath: 'https://github.com/org/repo', component_id: 'something', + destination: { + git: parseGitUrl('https://github.com/org/repo'), + }, }; await cookie.run({ directory: tempdir, values, dockerClient: mockDocker }); const cookieCutterJson = await fs.readJSON(`${tempdir}/cookiecutter.json`); - expect(cookieCutterJson).toEqual({ ...existingJson, ...values }); + expect(cookieCutterJson).toEqual({ + ...existingJson, + ...values, + destination: { + git: expect.objectContaining({ organization: 'org', name: 'repo' }), + }, + }); }); it('should throw an error if the cookiecutter json is malformed and not missing', async () => { @@ -96,7 +111,10 @@ describe('CookieCutter Templater', () => { const values = { owner: 'blobby', - storePath: 'backstage/end-repo', + storePath: 'https://github.com/org/repo', + destination: { + git: parseGitUrl('https://github.com/org/repo'), + }, }; await expect( @@ -109,8 +127,11 @@ describe('CookieCutter Templater', () => { const values = { owner: 'blobby', - storePath: 'backstage/end-repo', + storePath: 'https://github.com/org/repo', component_id: 'newthing', + destination: { + git: parseGitUrl('https://github.com/org/repo'), + }, }; await cookie.run({ directory: tempdir, values, dockerClient: mockDocker }); @@ -137,8 +158,11 @@ describe('CookieCutter Templater', () => { const values = { owner: 'blobby', - storePath: 'backstage/end-repo', + storePath: 'https://github.com/org/repo', component_id: 'newthing', + destination: { + git: parseGitUrl('https://github.com/org/repo'), + }, }; const { resultDir } = await cookie.run({ @@ -157,8 +181,11 @@ describe('CookieCutter Templater', () => { const values = { owner: 'blobby', - storePath: 'backstage/end-repo', + storePath: 'https://github.com/org/repo', component_id: 'newthing', + destination: { + git: parseGitUrl('https://github.com/org/repo'), + }, }; await cookie.run({ @@ -203,8 +230,11 @@ describe('CookieCutter Templater', () => { const values = { owner: 'blobby', - storePath: 'backstage/end-repo', + storePath: 'https://github.com/org/repo', component_id: 'newthing', + destination: { + git: parseGitUrl('https://github.com/org/repo'), + }, }; await cookie.run({ @@ -243,7 +273,10 @@ describe('CookieCutter Templater', () => { directory: tempdir, values: { owner: 'blobby', - storePath: 'backstage/end-repo', + storePath: 'https://github.com/org/repo', + destination: { + git: parseGitUrl('https://github.com/org/repo'), + }, }, logStream: stream, dockerClient: mockDocker, diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/templater/cookiecutter.ts b/plugins/scaffolder-backend/src/scaffolder/stages/templater/cookiecutter.ts index 7be8730bd9..7f7fd3494f 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/templater/cookiecutter.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/templater/cookiecutter.ts @@ -18,6 +18,7 @@ import { JsonValue } from '@backstage/config'; import { runDockerContainer, runCommand } from './helpers'; import { TemplaterBase, TemplaterRunOptions } from '.'; import path from 'path'; + import { TemplaterRunResult } from './types'; const commandExists = require('command-exists-promise'); diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/templater/cra/index.ts b/plugins/scaffolder-backend/src/scaffolder/stages/templater/cra/index.ts index b30e86d903..601c2375f4 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/templater/cra/index.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/templater/cra/index.ts @@ -70,7 +70,9 @@ export class CreateReactAppTemplater implements TemplaterBase { `${finalDir}/.github/workflows/main.yml`, ); - extraAnnotations[GITHUB_ACTIONS_ANNOTATION] = options.values.storePath; + extraAnnotations[ + GITHUB_ACTIONS_ANNOTATION + ] = `${options.values.destination.git.owner}/${options.values.destination.git.name}`; } const componentInfo = { diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/templater/types.ts b/plugins/scaffolder-backend/src/scaffolder/stages/templater/types.ts index 5b01960612..9d03401467 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/templater/types.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/templater/types.ts @@ -17,6 +17,7 @@ import type { Writable } from 'stream'; import Docker from 'dockerode'; import { JsonValue } from '@backstage/config'; import { TemplateEntityV1alpha1 } from '@backstage/catalog-model'; +import gitUrlParse from 'git-url-parse'; /** * Currently the required template values. The owner @@ -25,6 +26,9 @@ import { TemplateEntityV1alpha1 } from '@backstage/catalog-model'; export type RequiredTemplateValues = { owner: string; storePath: string; + destination: { + git: gitUrlParse.GitUrl; + }; }; /** @@ -42,7 +46,7 @@ export type TemplaterRunResult = { */ export type TemplaterRunOptions = { directory: string; - values: RequiredTemplateValues & Record; + values: RequiredTemplateValues & Record; logStream?: Writable; dockerClient: Docker; }; diff --git a/plugins/scaffolder-backend/src/service/router.test.ts b/plugins/scaffolder-backend/src/service/router.test.ts index 785e099c3f..59d5f4696e 100644 --- a/plugins/scaffolder-backend/src/service/router.test.ts +++ b/plugins/scaffolder-backend/src/service/router.test.ts @@ -107,10 +107,14 @@ describe('createRouter - working directory', () => { }); const app = express().use(router); - await request(app).post('/v1/jobs').send({ - templateName: '', - values: {}, - }); + await request(app) + .post('/v1/jobs') + .send({ + templateName: '', + values: { + storePath: 'https://github.com/backstage/good', + }, + }); expect(mockPrepare).toBeCalledWith(expect.anything(), { logger: expect.anything(), @@ -130,10 +134,14 @@ describe('createRouter - working directory', () => { }); const app = express().use(router); - await request(app).post('/v1/jobs').send({ - templateName: '', - values: {}, - }); + await request(app) + .post('/v1/jobs') + .send({ + templateName: '', + values: { + storePath: 'https://github.com/backstage/goodrepo', + }, + }); expect(mockPrepare).toBeCalledWith(expect.anything(), { logger: expect.anything(), @@ -200,10 +208,14 @@ describe('createRouter', () => { describe('POST /v1/jobs', () => { it('rejects template values which do not match the template schema definition', async () => { - const response = await request(app).post('/v1/jobs').send({ - templateName: '', - values: {}, - }); + const response = await request(app) + .post('/v1/jobs') + .send({ + templateName: '', + values: { + storePath: 'https://github.com/backstage/backstage', + }, + }); expect(response.status).toEqual(400); }); diff --git a/plugins/scaffolder-backend/src/service/router.ts b/plugins/scaffolder-backend/src/service/router.ts index 8af32f6c0c..e74c06ecc2 100644 --- a/plugins/scaffolder-backend/src/service/router.ts +++ b/plugins/scaffolder-backend/src/service/router.ts @@ -32,6 +32,7 @@ import { } from '../scaffolder'; import { CatalogEntityClient } from '../lib/catalog'; import { validate, ValidatorResult } from 'jsonschema'; +import parseGitUrl from 'git-url-parse'; export interface RouterOptions { preparers: PreparerBuilder; @@ -109,8 +110,13 @@ export async function createRouter( }) .post('/v1/jobs', async (req, res) => { const templateName: string = req.body.templateName; - const values: RequiredTemplateValues & Record = - req.body.values; + const values: RequiredTemplateValues & Record = { + ...req.body.values, + destination: { + git: parseGitUrl(req.body.values.storePath), + }, + }; + req.body.values; const template = await entityClient.findTemplate(templateName); @@ -123,7 +129,6 @@ export async function createRouter( res.status(400).json({ errors: validationResult.errors }); return; } - const job = jobProcessor.create({ entity: template, values,