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 75c35f2034..1e302afa5e 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 @@ -1,7 +1,3 @@ #!/bin/sh -# Move all template files to the root folder -mv ./* ../ -cd .. -rm -rf {{cookiecutter.component_id}} -# # # # # # # # +echo "Could do something here?" diff --git a/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/.github/workflows/build.yml b/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/.github/workflows/build.yml index d1563ba3e6..e3ca9f44e2 100644 --- a/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/.github/workflows/build.yml +++ b/plugins/scaffolder-backend/sample-templates/react-ssr-template/{{cookiecutter.component_id}}/.github/workflows/build.yml @@ -1,9 +1,6 @@ name: Frontend CI -on: - push: - paths: - - '.' +on: [push, pull_request] jobs: build: diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/prepare/file.ts b/plugins/scaffolder-backend/src/scaffolder/stages/prepare/file.ts index 7a924d9a99..7a8361d6ea 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/prepare/file.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/prepare/file.ts @@ -43,6 +43,7 @@ export class FilePreparer implements PreparerBase { await fs.copy(parentDirectory, tempDir, { filter: src => src !== location, + recursive: true, }); return tempDir; diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/store/github.ts b/plugins/scaffolder-backend/src/scaffolder/stages/store/github.ts index f3b69cc361..a59e6c90cd 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/store/github.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/store/github.ts @@ -13,3 +13,64 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + +import { Storer } from './types'; +import { Octokit } from '@octokit/rest'; +import { TemplateEntityV1alpha1 } from '@backstage/catalog-model'; +import { JsonValue } from '@backstage/config'; +import { RequiredTemplateValues } from '../templater'; +import { Repository, Remote, Signature, Cred } from 'nodegit'; +import gitUrlParse from 'git-url-parse'; + +export class GithubStorer implements Storer { + private client: Octokit; + constructor({ client }: { client: Octokit }) { + this.client = client; + } + + async createRemote({ + values, + }: { + entity: TemplateEntityV1alpha1; + values: RequiredTemplateValues & Record; + }) { + const { + data: { clone_url: cloneUrl }, + } = await this.client.repos.createInOrg({ + name: values.component_id, + org: values.org as string, + }); + + console.warn(cloneUrl); + + return cloneUrl; + } + + async pushToRemote(directory: string, remote: string): Promise { + const repo = await Repository.init(directory, 0); + const index = await repo.refreshIndex(); + await index.addAll(); + await index.write(); + const oid = await index.writeTree(); + await repo.createCommit( + 'HEAD', + Signature.now('Foo bar', 'foo@bar.com'), + Signature.now('Foo bar', 'foo@bar.com'), + 'initial commit', + oid, + [], + ); + + const remoteRepo = await Remote.create(repo, 'origin', remote); + await remoteRepo.push(['refs/heads/master:refs/heads/master'], { + callbacks: { + credentials: () => { + return Cred.userpassPlaintextNew( + process.env.GITHUB_ACCESS_TOKEN as string, + 'x-oauth-basic', + ); + }, + }, + }); + } +} diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/store/types.ts b/plugins/scaffolder-backend/src/scaffolder/stages/store/types.ts new file mode 100644 index 0000000000..ae63a05197 --- /dev/null +++ b/plugins/scaffolder-backend/src/scaffolder/stages/store/types.ts @@ -0,0 +1,23 @@ +/* + * 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 } from "@backstage/catalog-model"; +import { RequiredTemplateValues } from "../templater"; +import { JsonValue } from "@backstage/config"; + +export type Storer = { + createRemote(opts: { entity: TemplateEntityV1alpha1, values: RequiredTemplateValues & Record}): Promise; + pushToRemote(directory: string, remote: string): Promise; +} diff --git a/plugins/scaffolder-backend/src/scaffolder/stages/templater/cookiecutter.ts b/plugins/scaffolder-backend/src/scaffolder/stages/templater/cookiecutter.ts index cef8a64b60..5e875b6838 100644 --- a/plugins/scaffolder-backend/src/scaffolder/stages/templater/cookiecutter.ts +++ b/plugins/scaffolder-backend/src/scaffolder/stages/templater/cookiecutter.ts @@ -1,5 +1,3 @@ -import { TemplaterBase, TemplaterRunOptions } from '.'; - /* * Copyright 2020 Spotify AB * @@ -18,7 +16,8 @@ import { TemplaterBase, TemplaterRunOptions } from '.'; import fs from 'fs-extra'; import { JsonValue } from '@backstage/config'; import { runDockerContainer } from './helpers'; - +import { TemplaterBase, TemplaterRunOptions } from '.'; +import path from 'path'; export class CookieCutter implements TemplaterBase { private async fetchTemplateCookieCutter( directory: string, @@ -66,6 +65,6 @@ export class CookieCutter implements TemplaterBase { dockerClient: options.dockerClient, }); - return resultDir; + return path.resolve(resultDir, options.values.component_id); } } diff --git a/plugins/scaffolder-backend/src/service/router.ts b/plugins/scaffolder-backend/src/service/router.ts index 0f43c3e0de..da7dce40f9 100644 --- a/plugins/scaffolder-backend/src/service/router.ts +++ b/plugins/scaffolder-backend/src/service/router.ts @@ -22,6 +22,8 @@ import { TemplateEntityV1alpha1 } from '@backstage/catalog-model'; import Docker from 'dockerode'; import {} from '@backstage/backend-common'; import { StageContext } from '../scaffolder/jobs/types'; +import { Octokit } from '@octokit/rest'; +import { GithubStorer } from '../scaffolder/stages/store/github'; export interface RouterOptions { preparers: PreparerBuilder; templater: TemplaterBase; @@ -33,9 +35,11 @@ export async function createRouter( options: RouterOptions, ): Promise { const router = Router(); - const { preparers, templater, logger: parentLogger, dockerClient } = options; - const logger = parentLogger.child({ plugin: 'scaffolder' }); + const githubClient = new Octokit({ auth: process.env.GITHUB_ACCESS_TOKEN }); + const { preparers, templater, logger: parentLogger, dockerClient } = options; + const githubStorer = new GithubStorer({ client: githubClient }); + const logger = parentLogger.child({ plugin: 'scaffolder' }); const jobProcessor = new JobProcessor(); router @@ -83,7 +87,7 @@ export async function createRouter( metadata: { annotations: { 'backstage.io/managed-by-location': - 'github:https://github.com/benjdlambert/backstage-graphql-template/blob/master/template.yaml', + 'file:/Users/blam/dev/spotify/backstage/plugins/scaffolder-backend/sample-templates/react-ssr-template/template.yaml', }, name: 'graphql-starter', title: 'GraphQL Service', @@ -96,13 +100,17 @@ export async function createRouter( }, spec: { type: 'cookiecutter', - path: './template', + path: '.', }, }; const job = jobProcessor.create({ entity: mockEntity, - values: { component_id: 'blob' }, + values: { + component_id: `blob${Date.now()}`, + org: 'hojden', + description: 'test', + }, stages: [ { name: 'Prepare the skeleton', @@ -131,12 +139,21 @@ export async function createRouter( name: 'Create VCS Repo', handler: async (ctx: StageContext<{ resultDir: string }>) => { ctx.logger.info('Should now create the VCS repo'); + const remoteUrl = await githubStorer.createRemote({ + values: ctx.values, + entity: ctx.entity, + }); + + return { remoteUrl }; }, }, { name: 'Push to remote', - handler: async ctx => { + handler: async ( + ctx: StageContext<{ resultDir: string; remoteUrl: string }>, + ) => { ctx.logger.info('Should now push to the remote'); + await githubStorer.pushToRemote(ctx.resultDir, ctx.remoteUrl); }, }, ], diff --git a/yarn.lock b/yarn.lock index 04388bb623..18be8c3246 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2328,6 +2328,18 @@ dependencies: "@octokit/types" "^2.0.0" +"@octokit/core@^3.0.0": + version "3.1.0" + resolved "https://registry.npmjs.org/@octokit/core/-/core-3.1.0.tgz#9c3c9b23f7504668cfa057f143ccbf0c645a0ac9" + integrity sha512-yPyQSmxIXLieEIRikk2w8AEtWkFdfG/LXcw1KvEtK3iP0ENZLW/WYQmdzOKqfSaLhooz4CJ9D+WY79C8ZliACw== + dependencies: + "@octokit/auth-token" "^2.4.0" + "@octokit/graphql" "^4.3.1" + "@octokit/request" "^5.4.0" + "@octokit/types" "^5.0.0" + before-after-hook "^2.1.0" + universal-user-agent "^5.0.0" + "@octokit/endpoint@^5.5.0": version "5.5.3" resolved "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-5.5.3.tgz#0397d1baaca687a4c8454ba424a627699d97c978" @@ -2337,6 +2349,24 @@ is-plain-object "^3.0.0" universal-user-agent "^5.0.0" +"@octokit/endpoint@^6.0.1": + version "6.0.3" + resolved "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.3.tgz#dd09b599662d7e1b66374a177ab620d8cdf73487" + integrity sha512-Y900+r0gIz+cWp6ytnkibbD95ucEzDSKzlEnaWS52hbCDNcCJYO5mRmWW7HRAnDc7am+N/5Lnd8MppSaTYx1Yg== + dependencies: + "@octokit/types" "^5.0.0" + is-plain-object "^3.0.0" + universal-user-agent "^5.0.0" + +"@octokit/graphql@^4.3.1": + version "4.5.1" + resolved "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.5.1.tgz#162aed1490320b88ce34775b3f6b8de945529fa9" + integrity sha512-qgMsROG9K2KxDs12CO3bySJaYoUu2aic90qpFrv7A8sEBzZ7UFGvdgPKiLw5gOPYEYbS0Xf8Tvf84tJutHPulQ== + dependencies: + "@octokit/request" "^5.3.0" + "@octokit/types" "^5.0.0" + universal-user-agent "^5.0.0" + "@octokit/plugin-enterprise-rest@^6.0.1": version "6.0.1" resolved "https://registry.npmjs.org/@octokit/plugin-enterprise-rest/-/plugin-enterprise-rest-6.0.1.tgz#e07896739618dab8da7d4077c658003775f95437" @@ -2349,6 +2379,13 @@ dependencies: "@octokit/types" "^2.0.1" +"@octokit/plugin-paginate-rest@^2.2.0": + version "2.2.3" + resolved "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.2.3.tgz#a6ad4377e7e7832fb4bdd9d421e600cb7640ac27" + integrity sha512-eKTs91wXnJH8Yicwa30jz6DF50kAh7vkcqCQ9D7/tvBAP5KKkg6I2nNof8Mp/65G0Arjsb4QcOJcIEQY+rK1Rg== + dependencies: + "@octokit/types" "^5.0.0" + "@octokit/plugin-request-log@^1.0.0": version "1.0.0" resolved "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.0.tgz#eef87a431300f6148c39a7f75f8cfeb218b2547e" @@ -2362,6 +2399,14 @@ "@octokit/types" "^2.0.1" deprecation "^2.3.1" +"@octokit/plugin-rest-endpoint-methods@4.0.0": + version "4.0.0" + resolved "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-4.0.0.tgz#b02a2006dda8e908c3f8ab381dd5475ef5a810a8" + integrity sha512-emS6gysz4E9BNi9IrCl7Pm4kR+Az3MmVB0/DoDCmF4U48NbYG3weKyDlgkrz6Jbl4Mu4nDx8YWZwC4HjoTdcCA== + dependencies: + "@octokit/types" "^5.0.0" + deprecation "^2.3.1" + "@octokit/request-error@^1.0.1", "@octokit/request-error@^1.0.2": version "1.2.1" resolved "https://registry.npmjs.org/@octokit/request-error/-/request-error-1.2.1.tgz#ede0714c773f32347576c25649dc013ae6b31801" @@ -2371,6 +2416,15 @@ deprecation "^2.0.0" once "^1.4.0" +"@octokit/request-error@^2.0.0": + version "2.0.2" + resolved "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.0.2.tgz#0e76b83f5d8fdda1db99027ea5f617c2e6ba9ed0" + integrity sha512-2BrmnvVSV1MXQvEkrb9zwzP0wXFNbPJij922kYBTLIlIafukrGOb+ABBT2+c6wZiuyWDH1K1zmjGQ0toN/wMWw== + dependencies: + "@octokit/types" "^5.0.1" + deprecation "^2.0.0" + once "^1.4.0" + "@octokit/request@^5.2.0": version "5.3.2" resolved "https://registry.npmjs.org/@octokit/request/-/request-5.3.2.tgz#1ca8b90a407772a1ee1ab758e7e0aced213b9883" @@ -2385,6 +2439,20 @@ once "^1.4.0" universal-user-agent "^5.0.0" +"@octokit/request@^5.3.0", "@octokit/request@^5.4.0": + version "5.4.5" + resolved "https://registry.npmjs.org/@octokit/request/-/request-5.4.5.tgz#8df65bd812047521f7e9db6ff118c06ba84ac10b" + integrity sha512-atAs5GAGbZedvJXXdjtKljin+e2SltEs48B3naJjqWupYl2IUBbB/CJisyjbNHcKpHzb3E+OYEZ46G8eakXgQg== + dependencies: + "@octokit/endpoint" "^6.0.1" + "@octokit/request-error" "^2.0.0" + "@octokit/types" "^5.0.0" + deprecation "^2.0.0" + is-plain-object "^3.0.0" + node-fetch "^2.3.0" + once "^1.4.0" + universal-user-agent "^5.0.0" + "@octokit/rest@^16.28.4": version "16.43.1" resolved "https://registry.npmjs.org/@octokit/rest/-/rest-16.43.1.tgz#3b11e7d1b1ac2bbeeb23b08a17df0b20947eda6b" @@ -2407,6 +2475,16 @@ once "^1.4.0" universal-user-agent "^4.0.0" +"@octokit/rest@^18.0.0": + version "18.0.0" + resolved "https://registry.npmjs.org/@octokit/rest/-/rest-18.0.0.tgz#7f401d9ce13530ad743dfd519ae62ce49bcc0358" + integrity sha512-4G/a42lry9NFGuuECnua1R1eoKkdBYJap97jYbWDNYBOUboWcM75GJ1VIcfvwDV/pW0lMPs7CEmhHoVrSV5shg== + dependencies: + "@octokit/core" "^3.0.0" + "@octokit/plugin-paginate-rest" "^2.2.0" + "@octokit/plugin-request-log" "^1.0.0" + "@octokit/plugin-rest-endpoint-methods" "4.0.0" + "@octokit/types@^2.0.0", "@octokit/types@^2.0.1": version "2.5.0" resolved "https://registry.npmjs.org/@octokit/types/-/types-2.5.0.tgz#f1bbd147e662ae2c79717d518aac686e58257773" @@ -2414,6 +2492,13 @@ dependencies: "@types/node" ">= 8" +"@octokit/types@^5.0.0", "@octokit/types@^5.0.1": + version "5.0.1" + resolved "https://registry.npmjs.org/@octokit/types/-/types-5.0.1.tgz#5459e9a5e9df8565dcc62c17a34491904d71971e" + integrity sha512-GorvORVwp244fGKEt3cgt/P+M0MGy4xEDbckw+K5ojEezxyMDgCaYPKVct+/eWQfZXOT7uq0xRpmrl/+hliabA== + dependencies: + "@types/node" ">= 8" + "@open-draft/until@^1.0.0": version "1.0.3" resolved "https://registry.npmjs.org/@open-draft/until/-/until-1.0.3.tgz#db9cc719191a62e7d9200f6e7bab21c5b848adca" @@ -5364,7 +5449,7 @@ bcrypt-pbkdf@^1.0.0, bcrypt-pbkdf@^1.0.2: dependencies: tweetnacl "^0.14.3" -before-after-hook@^2.0.0: +before-after-hook@^2.0.0, before-after-hook@^2.1.0: version "2.1.0" resolved "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.1.0.tgz#b6c03487f44e24200dd30ca5e6a1979c5d2fb635" integrity sha512-IWIbu7pMqyw3EAJHzzHbWa85b6oud/yfKYg5rqB5hNE8CeMi3nX+2C2sj0HswfblST86hpVEOAb9x34NZd6P7A==