feat(scaffolder): working more on the scaffolder to make it nice

This commit is contained in:
blam
2020-04-26 03:10:11 +02:00
committed by blam
parent a86f111b97
commit c62c43d12f
5 changed files with 129 additions and 19 deletions
+8 -2
View File
@@ -11,9 +11,15 @@
"clean": "backstage-cli clean"
},
"devDependencies": {
"@backstage/cli": "^0.1.1-alpha.4"
"@backstage/cli": "^0.1.1-alpha.4",
"@types/fs-extra": "^8.1.0",
"@types/supertest": "^2.0.8",
"supertest": "^4.0.2"
},
"dependencies": {
"express": "^4.17.1"
"express": "^4.17.1",
"fs-extra": "^9.0.0",
"glob": "^7.1.6",
"winston": "^3.2.1"
}
}
+34 -14
View File
@@ -15,12 +15,18 @@
*/
import glob from 'glob';
import { promises as fs } from 'fs';
import * as fs from 'fs-extra';
import { logger } from 'lib/logger';
import { Template, RepositoryBase as Base } from '.';
interface DiskIndexEntry {
contents: Template;
location: string;
}
export class DiskRepository implements Base {
private repository: Template[] = [];
private localIndex: DiskIndexEntry[] = [];
constructor(private repoDir = `${__dirname}/templates`) {
this.reindex();
@@ -31,14 +37,25 @@ export class DiskRepository implements Base {
}
public async reindex(): Promise<void> {
this.repository = await this.index();
this.localIndex = await this.index();
this.repository = this.localIndex.map(({ contents }) => contents);
}
public async prepare(template: string): Promise<string> {
return template;
public async prepare(templateId: string): Promise<string> {
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<Template[]> {
private async index(): Promise<DiskIndexEntry[]> {
return new Promise((resolve, reject) => {
glob(`${this.repoDir}/**/template-info.json`, async (err, matches) => {
if (err) {
@@ -46,32 +63,35 @@ export class DiskRepository implements Base {
}
const fileContents: Array<{
path: string;
location: string;
contents: string;
}> = await Promise.all(
matches.map(async (path: string) => ({
path,
contents: await fs.readFile(path, 'utf-8'),
matches.map(async (location: string) => ({
location,
contents: await fs.readFile(location, 'utf-8'),
})),
);
const validFiles = fileContents.reduce(
(templates: Template[], currentFile) => {
(diskIndexEntries: DiskIndexEntry[], currentFile) => {
try {
const parsed: Template = JSON.parse(currentFile.contents);
return [...templates, parsed];
return [
...diskIndexEntries,
{ location: currentFile.location, contents: parsed },
];
} catch (ex) {
logger.error('Failure parsing JSON for template', {
path: currentFile.path,
path: currentFile.location,
});
}
return templates;
return diskIndexEntries;
},
[],
);
resolve(validFiles as Template[]);
resolve(validFiles);
});
});
}
@@ -23,13 +23,15 @@ export interface Template {
}
export abstract class RepositoryBase {
// lists all templates available
abstract async list(): Promise<Template[]>;
// can be used to build an index of the available templates;
abstract async reindex(): Promise<void>;
// returns a directory to run cookiecutter in
// returns a directory to run the templaterin
abstract async prepare(id: string): Promise<string>;
}
class Interface implements RepositoryBase {
class RepositoryImplementation implements RepositoryBase {
repo?: RepositoryBase;
constructor() {
@@ -45,4 +47,4 @@ class Interface implements RepositoryBase {
reindex = () => this.repo!.reindex();
}
export const Repository = new Interface();
export const Repository = new RepositoryImplementation();
@@ -0,0 +1,34 @@
import { TemplaterBase, TemplaterRunOptions } from '.';
/*
* 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 * as fs from 'fs-extra';
class CookieCutter implements TemplaterBase {
public async run(options: TemplaterRunOptions): Promise<string> {
// first we need to make cookiecutter.json in the directory provided with the input values.
const cookieInfo = {
_copy_without_render: ['.github/workflows/*'],
...options.values,
};
await fs.writeJSON(options.directory, cookieInfo);
// run cookie cutter with new json
}
}
export const CookieCutterTemplater = new CookieCutter();
@@ -0,0 +1,48 @@
/*
* 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 { CookieCutterTemplater } from './cookiecutter';
export interface RequiredTemplateValues {
componentId: string;
}
export interface TemplaterRunOptions {
directory: string;
values: RequiredTemplateValues & object;
}
export abstract class TemplaterBase {
// runs the templating with the values and returns the directory to push the VCS
abstract async run(opts: TemplaterRunOptions): Promise<string>;
}
class TemplaterImplementation implements TemplaterBase {
templater?: TemplaterBase;
constructor() {
this.templater = new CookieCutterTemplaterutterTemplater();
}
public setTemplater(templater: TemplaterBase) {
this.templater = templater;
}
public async run(opts: TemplaterRunOptions) {
return this.templater!.run(opts);
}
}
export const Templater = new TemplaterImplementation();