Skip to content

Commit

Permalink
feat: add cli build parallel flag
Browse files Browse the repository at this point in the history
  • Loading branch information
andrewthauer committed Oct 14, 2020
1 parent 11000d9 commit 72f6cda
Show file tree
Hide file tree
Showing 11 changed files with 156 additions and 4 deletions.
12 changes: 12 additions & 0 deletions .changeset/cli-parallel-build-options.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
'@backstage/cli': minor
---

Adds a new `BACKSTAGE_CLI_BUILD_PARELLEL` environment variable to control
parallelism for some build steps.

This is useful in CI to help avoid out of memory issues when using `terser`. The
`BACKSTAGE_CLI_BUILD_PARELLEL` environment variable can be set to
`true | false | [integer]` to override the default behaviour. See
[terser-webpack-plugin](https://github.com/webpack-contrib/terser-webpack-plugin#parallel)
for more details.
1 change: 1 addition & 0 deletions .github/styles/vocab.txt
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ validators
Voi
Wealthsimple
Weaveworks
Webpack
xyz
yaml
Zalando
Expand Down
1 change: 1 addition & 0 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
"style-loader": "^1.2.1",
"sucrase": "^3.14.1",
"tar": "^6.0.1",
"terser-webpack-plugin": "^1.4.3",
"ts-jest": "^26.0.0",
"ts-loader": "^7.0.4",
"typescript": "^3.9.3",
Expand Down
2 changes: 2 additions & 0 deletions packages/cli/src/commands/app/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { loadConfig } from '@backstage/config-loader';
import { ConfigReader } from '@backstage/config';
import { paths } from '../../lib/paths';
import { buildBundle } from '../../lib/bundler';
import { parseParallel, PARALLEL_ENV_VAR } from '../../lib/parallel';

export default async (cmd: Command) => {
const appConfigs = await loadConfig({
Expand All @@ -27,6 +28,7 @@ export default async (cmd: Command) => {
});
await buildBundle({
entry: 'src/index',
parallel: parseParallel(process.env[PARALLEL_ENV_VAR]),
statsJsonEnabled: cmd.stats,
config: ConfigReader.fromConfigs(appConfigs),
appConfigs,
Expand Down
4 changes: 3 additions & 1 deletion packages/cli/src/commands/backend/buildImage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@
* limitations under the License.
*/

import { Command } from 'commander';
import fs from 'fs-extra';
import { join as joinPath, relative as relativePath } from 'path';
import { createDistWorkspace } from '../../lib/packager';
import { paths } from '../../lib/paths';
import { run } from '../../lib/run';
import { Command } from 'commander';
import { parseParallel, PARALLEL_ENV_VAR } from '../../lib/parallel';

const PKG_PATH = 'package.json';

Expand All @@ -41,6 +42,7 @@ export default async (cmd: Command) => {
...appConfigs,
{ src: paths.resolveTarget('Dockerfile'), dest: 'Dockerfile' },
],
parallel: parseParallel(process.env[PARALLEL_ENV_VAR]),
skeleton: 'skeleton.tar',
});
console.log(`Dist workspace ready at ${tempDistWorkspace}`);
Expand Down
12 changes: 12 additions & 0 deletions packages/cli/src/lib/bundler/optimization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@
*/

import { Options } from 'webpack';
import TerserPlugin from 'terser-webpack-plugin';
import { BundlingOptions } from './types';
import { isParallelDefault } from '../parallel';

export const optimization = (
options: BundlingOptions,
Expand All @@ -24,6 +26,16 @@ export const optimization = (

return {
minimize: !isDev,
// Only configure when parallel is explicitly overriden from the default
...(!isParallelDefault(options.parallel)
? {
minimizer: [
new TerserPlugin({
parallel: options.parallel,
}),
],
}
: {}),
runtimeChunk: 'single',
splitChunks: {
automaticNameDelimiter: '-',
Expand Down
3 changes: 3 additions & 0 deletions packages/cli/src/lib/bundler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@

import { AppConfig, Config } from '@backstage/config';
import { BundlingPathsOptions } from './paths';
import { ParallelOption } from '../parallel';

export type BundlingOptions = {
checksEnabled: boolean;
isDev: boolean;
config: Config;
appConfigs: AppConfig[];
baseUrl: URL;
parallel?: ParallelOption;
};

export type BackendBundlingOptions = Omit<BundlingOptions, 'baseUrl'> & {
Expand All @@ -37,6 +39,7 @@ export type ServeOptions = BundlingPathsOptions & {

export type BuildOptions = BundlingPathsOptions & {
statsJsonEnabled: boolean;
parallel?: ParallelOption;
config: Config;
appConfigs: AppConfig[];
};
17 changes: 14 additions & 3 deletions packages/cli/src/lib/packager/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@ import {
resolve as resolvePath,
relative as relativePath,
} from 'path';
import { tmpdir } from 'os';
import tar, { CreateOptions } from 'tar';
import { paths } from '../paths';
import { run } from '../run';
import tar, { CreateOptions } from 'tar';
import { tmpdir } from 'os';
import { ParallelOption } from '../parallel';

type LernaPackage = {
name: string;
Expand Down Expand Up @@ -58,6 +59,11 @@ type Options = {
*/
buildDependencies?: boolean;

/**
* Enable (true/false) or control amount of (number) parallelism in some build steps.
*/
parallel?: ParallelOption;

/**
* If set, creates a skeleton tarball that contains all package.json files
* with the same structure as the workspace dir.
Expand Down Expand Up @@ -85,7 +91,12 @@ export async function createDistWorkspace(

if (options.buildDependencies) {
const scopeArgs = targets.flatMap(target => ['--scope', target.name]);
await run('yarn', ['lerna', 'run', ...scopeArgs, 'build'], {
const lernaArgs =
options.parallel && Number.isInteger(options.parallel)
? ['--concurrency', options.parallel.toString()]
: [];

await run('yarn', ['lerna', ...lernaArgs, 'run', ...scopeArgs, 'build'], {
cwd: paths.targetRoot,
});
}
Expand Down
59 changes: 59 additions & 0 deletions packages/cli/src/lib/parallel.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
/*
* 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 { isParallelDefault, parseParallel } from './parallel';

describe('parallel', () => {
describe(parseParallel, () => {
it('coerces "false" string to boolean', () => {
expect(parseParallel('false')).toBeFalsy();
});

it('coerces "true" to boolean', () => {
expect(parseParallel('true')).toBeTruthy();
});

it('coerces number string to number', () => {
expect(parseParallel('2')).toBe(2);
});
it.each([[true], [false], [2]])('returns itself for %p', value => {
expect(parseParallel(value as any)).toEqual(value);
});

it.each([[undefined], [null]])('returns true for %p', value => {
expect(parseParallel(value as any)).toBe(true);
});

it.each([['on'], [2.5], ['2.5']])('throws error for %p', value => {
expect(() => parseParallel(value as any)).toThrowError(
`Parallel option value '${value}' is not a boolean or integer`,
);
});
});

describe(isParallelDefault, () => {
it('returns true if default value', () => {
expect(isParallelDefault(undefined)).toBeTruthy();
expect(isParallelDefault(true)).toBeTruthy();
});

it('returns false if not default value', () => {
expect(isParallelDefault(false)).toBeFalsy();
expect(isParallelDefault(2)).toBeFalsy();
expect(isParallelDefault('true' as any)).toBeFalsy();
});
});
});
47 changes: 47 additions & 0 deletions packages/cli/src/lib/parallel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* 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.
*/

export const PARALLEL_ENV_VAR = 'BACKSTAGE_CLI_BUILD_PARALLEL';

export type ParallelOption = boolean | number | undefined;

export function isParallelDefault(parallel: ParallelOption) {
return parallel === undefined || parallel === true;
}

export function parseParallel(
parallel: boolean | string | number | undefined,
): ParallelOption {
if (parallel === undefined || parallel === null) {
return true;
} else if (typeof parallel === 'boolean') {
return parallel;
} else if (typeof parallel === 'number' && Number.isInteger(parallel)) {
return parallel;
} else if (typeof parallel === 'string') {
if (parallel === 'true') {
return true;
} else if (parallel === 'false') {
return false;
} else if (Number.isInteger(parseFloat(parallel.toString()))) {
return Number(parallel);
}
}

throw Error(
`Parallel option value '${parallel}' is not a boolean or integer`,
);
}
2 changes: 2 additions & 0 deletions packages/cli/src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,5 @@ declare module '@svgr/rollup' {
}

declare module '@rollup/plugin-yaml';

declare module 'terser-webpack-plugin';

0 comments on commit 72f6cda

Please sign in to comment.