Merge pull request #490 from spotify/rugvip/with-cache

packages/cli: make plugin:build have built-in caching
This commit is contained in:
Patrik Oldsberg
2020-04-07 13:23:50 +02:00
committed by GitHub
7 changed files with 61 additions and 33 deletions
+26 -13
View File
@@ -14,27 +14,26 @@
* limitations under the License.
*/
import fs from 'fs-extra';
import { Command } from 'commander';
import { run } from 'helpers/run';
import { Cache } from './cache';
import { parseOptions } from './options';
import { parseOptions, Options } from './options';
function print(msg: string) {
process.stdout.write(`[build-cache] ${msg}\n`);
}
/*
* The build-cache command is used to make builds a no-op if there are no changes to the package.
* It supports both local development where the output directory remains intact, as well as CI
* where the output directory is stored in a separate cache dir.
*/
export default async (cmd: Command, args: string[]) => {
const options = await parseOptions(cmd);
// Wrap a build function with a cache, which won't call the build function on cache hit
export async function withCache(
options: Options,
buildFunc: () => Promise<void>,
): Promise<void> {
const key = await Cache.readInputKey(options.inputs);
if (!key) {
print('input directory is dirty, skipping cache');
await run(args[0], args.slice(1));
await fs.remove(options.output);
await buildFunc();
return;
}
@@ -52,9 +51,23 @@ export default async (cmd: Command, args: string[]) => {
}
print('cache miss, need to build');
await fs.remove(options.output);
await buildFunc();
await run(args[0], args.slice(1));
print('caching build output');
await cacheResult.archive(options.output, options.maxCacheEntries);
}
/*
* The build-cache command is used to make builds a no-op if there are no changes to the package.
* It supports both local development where the output directory remains intact, as well as CI
* where the output directory is stored in a separate cache dir.
*/
export default async (cmd: Command, args: string[]) => {
const options = await parseOptions(cmd);
await withCache(options, async () => {
await run(args[0], args.slice(1));
});
};
export * from './options';
@@ -18,6 +18,7 @@ import { resolve as resolvePath } from 'path';
import { Command } from 'commander';
import { paths } from 'helpers/paths';
const DEFAULT_CACHE_DIR = '<repoRoot>/node_modules/.cache/backstage-builds';
const DEFAULT_MAX_ENTRIES = 10;
export type Options = {
@@ -27,19 +28,34 @@ export type Options = {
maxCacheEntries: number;
};
export async function parseOptions(cmd: Command): Promise<Options> {
const argTransformer = (arg: string) =>
resolvePath(arg.replace(/<repoRoot>/g, paths.targetRoot).replace(/'/g, ''));
function transformPath(path: string): string {
return resolvePath(
path.replace(/<repoRoot>/g, paths.targetRoot).replace(/'/g, ''),
);
}
const inputs = cmd.input.map(argTransformer) as string[];
export async function parseOptions(cmd: Command): Promise<Options> {
const inputs = cmd.input.map(transformPath) as string[];
if (inputs.length === 0) {
inputs.push(argTransformer('.'));
inputs.push(transformPath('.'));
}
const output = argTransformer(cmd.output);
const cacheDir = argTransformer(
const output = transformPath(cmd.output);
const cacheDir = transformPath(
process.env.BACKSTAGE_CACHE_DIR || cmd.cacheDir,
);
const maxCacheEntries =
Number(process.env.BACKSTAGE_CACHE_MAX_ENTRIES) || DEFAULT_MAX_ENTRIES;
return { inputs, output, cacheDir, maxCacheEntries };
}
export function getDefaultCacheOptions(): Options {
return {
inputs: [transformPath('.')],
output: transformPath('dist'),
cacheDir: transformPath(
process.env.BACKSTAGE_CACHE_DIR || DEFAULT_CACHE_DIR,
),
maxCacheEntries:
Number(process.env.BACKSTAGE_CACHE_MAX_ENTRIES) || DEFAULT_MAX_ENTRIES,
};
}
+8 -5
View File
@@ -17,6 +17,7 @@
import { rollup, watch, OutputOptions } from 'rollup';
import conf from './rollup.config';
import { Command } from 'commander';
import { withCache, getDefaultCacheOptions } from 'commands/build-cache';
export default async (cmd: Command) => {
if (cmd.watch) {
@@ -40,10 +41,12 @@ export default async (cmd: Command) => {
});
}
const bundle = await rollup({
input: conf.input,
plugins: conf.plugins,
await withCache(getDefaultCacheOptions(), async () => {
const bundle = await rollup({
input: conf.input,
plugins: conf.plugins,
});
await bundle.generate(conf.output as OutputOptions);
await bundle.write(conf.output as OutputOptions);
});
await bundle.generate(conf.output as OutputOptions);
await bundle.write(conf.output as OutputOptions);
};
@@ -6,8 +6,7 @@
"license": "Apache-2.0",
"private": true,
"scripts": {
"build:watch": "backstage-cli plugin:build --watch",
"build": "backstage-cli build-cache -- backstage-cli plugin:build",
"build": "backstage-cli plugin:build",
"lint": "backstage-cli lint",
"test": "backstage-cli test"
},
+1 -2
View File
@@ -19,8 +19,7 @@
"main": "dist/index.cjs.js",
"types": "dist/index.d.ts",
"scripts": {
"build:watch": "backstage-cli plugin:build --watch",
"build": "backstage-cli build-cache -- backstage-cli plugin:build",
"build": "backstage-cli plugin:build",
"lint": "backstage-cli lint",
"test": "backstage-cli test"
},
+1 -2
View File
@@ -6,8 +6,7 @@
"license": "Apache-2.0",
"private": true,
"scripts": {
"build:watch": "backstage-cli plugin:build --watch",
"build": "backstage-cli build-cache -- backstage-cli plugin:build",
"build": "backstage-cli plugin:build",
"lint": "backstage-cli lint",
"test": "backstage-cli test"
},
+1 -2
View File
@@ -6,8 +6,7 @@
"private": true,
"license": "Apache-2.0",
"scripts": {
"build:watch": "backstage-cli plugin:build --watch",
"build": "backstage-cli build-cache -- backstage-cli plugin:build",
"build": "backstage-cli plugin:build",
"lint": "backstage-cli lint",
"test": "backstage-cli test"
},