From baeb8ba5be8e0448df975457e4df09821060c30f Mon Sep 17 00:00:00 2001 From: Anton Nikolaev Date: Wed, 10 Mar 2021 08:06:19 -0800 Subject: [PATCH] Performance improvements for "build-plugin" task Summary: Few improvements for "build-plugin" task which together with Sandcastle command changes (D26872427) helps to build all plugins in CI ~30% faster if most of them has not changed (which is usually the case): 1) compute package checksum in the same script to not call additional yarn scripts for each plugin 2) avoid packaging plugin if it's checksum has not changed since last release Reviewed By: mweststrate Differential Revision: D26872253 fbshipit-source-id: 968102d32a1550ea7503f1169f0ef2863296383f --- desktop/pkg-lib/package.json | 4 +- .../src}/computePackageChecksum.ts | 0 desktop/pkg-lib/src/index.ts | 1 + desktop/pkg-lib/src/runBuild.ts | 27 +++++-- desktop/pkg-lib/tsconfig.json | 18 ++++- desktop/pkg/package.json | 1 - desktop/pkg/src/commands/bundle.ts | 42 ++--------- desktop/pkg/src/commands/checksum.ts | 2 +- desktop/pkg/src/commands/pack.ts | 11 +-- desktop/scripts/build-plugin.ts | 75 ++++++++++++++----- 10 files changed, 105 insertions(+), 76 deletions(-) rename desktop/{pkg/src/utils => pkg-lib/src}/computePackageChecksum.ts (100%) diff --git a/desktop/pkg-lib/package.json b/desktop/pkg-lib/package.json index 276a2315b..26c3ab477 100644 --- a/desktop/pkg-lib/package.json +++ b/desktop/pkg-lib/package.json @@ -10,9 +10,11 @@ "bugs": "https://github.com/facebook/flipper/issues", "dependencies": { "flipper-babel-transformer": "0.0.0", + "flipper-plugin-lib": "0.0.0", "fs-extra": "^9.0.1", "metro": "^0.65.2", - "metro-minify-terser": "^0.65.2" + "metro-minify-terser": "^0.65.2", + "npm-packlist": "^2.1.4" }, "devDependencies": { "@types/fs-extra": "^9.0.1", diff --git a/desktop/pkg/src/utils/computePackageChecksum.ts b/desktop/pkg-lib/src/computePackageChecksum.ts similarity index 100% rename from desktop/pkg/src/utils/computePackageChecksum.ts rename to desktop/pkg-lib/src/computePackageChecksum.ts diff --git a/desktop/pkg-lib/src/index.ts b/desktop/pkg-lib/src/index.ts index e5ab0987d..41282807c 100644 --- a/desktop/pkg-lib/src/index.ts +++ b/desktop/pkg-lib/src/index.ts @@ -9,3 +9,4 @@ export {default as runBuild} from './runBuild'; export {default as getWatchFolders} from './getWatchFolders'; +export {default as computePackageChecksum} from './computePackageChecksum'; diff --git a/desktop/pkg-lib/src/runBuild.ts b/desktop/pkg-lib/src/runBuild.ts index cdc7a522f..c6f45ed25 100644 --- a/desktop/pkg-lib/src/runBuild.ts +++ b/desktop/pkg-lib/src/runBuild.ts @@ -11,6 +11,7 @@ import Metro from 'metro'; import getWatchFolders from './getWatchFolders'; import path from 'path'; import fs from 'fs-extra'; +import {getInstalledPluginDetails} from 'flipper-plugin-lib'; let metroDir: string | undefined; const metroDirPromise = getMetroDir().then((dir) => (metroDir = dir)); @@ -30,19 +31,29 @@ async function getMetroDir() { return __dirname; } -export default async function runBuild( - inputDirectory: string, - entry: string, - out: string, - dev: boolean, -) { +export default async function bundlePlugin(pluginDir: string, dev: boolean) { + const stat = await fs.lstat(pluginDir); + if (!stat.isDirectory()) { + throw new Error(`Plugin source ${pluginDir} is not a directory.`); + } + const packageJsonPath = path.join(pluginDir, 'package.json'); + if (!(await fs.pathExists(packageJsonPath))) { + throw new Error( + `package.json is not found in plugin source directory ${pluginDir}.`, + ); + } + const plugin = await getInstalledPluginDetails(pluginDir); + const entry = plugin.source; + const out = path.resolve(pluginDir, plugin.main); + await fs.ensureDir(path.dirname(out)); + const sourceMapUrl = null; // inline source map const baseConfig = await Metro.loadConfig(); const config = Object.assign({}, baseConfig, { reporter: {update: () => {}}, - projectRoot: inputDirectory, + projectRoot: pluginDir, watchFolders: [metroDir || (await metroDirPromise)].concat( - await getWatchFolders(inputDirectory), + await getWatchFolders(pluginDir), ), serializer: { ...baseConfig.serializer, diff --git a/desktop/pkg-lib/tsconfig.json b/desktop/pkg-lib/tsconfig.json index d40807250..21f1c3a3b 100644 --- a/desktop/pkg-lib/tsconfig.json +++ b/desktop/pkg-lib/tsconfig.json @@ -4,7 +4,19 @@ "outDir": "lib", "rootDir": "src" }, - "references": [{"path": "../babel-transformer"}], - "include": ["src"], - "exclude": ["node_modules", "**/__tests__/*"] + "references": [ + { + "path": "../babel-transformer" + }, + { + "path": "../plugin-lib" + } + ], + "include": [ + "src" + ], + "exclude": [ + "node_modules", + "**/__tests__/*" + ] } diff --git a/desktop/pkg/package.json b/desktop/pkg/package.json index 897f6134c..87c295c5b 100644 --- a/desktop/pkg/package.json +++ b/desktop/pkg/package.json @@ -25,7 +25,6 @@ "fs-extra": "^9.0.1", "inquirer": "^7.3.3", "lodash": "^4.17.19", - "npm-packlist": "^2.1.4", "recursive-readdir": "^2.2.2" }, "devDependencies": { diff --git a/desktop/pkg/src/commands/bundle.ts b/desktop/pkg/src/commands/bundle.ts index db9b9c493..fe7bb0880 100644 --- a/desktop/pkg/src/commands/bundle.ts +++ b/desktop/pkg/src/commands/bundle.ts @@ -12,7 +12,6 @@ import {args} from '@oclif/parser'; import fs from 'fs-extra'; import path from 'path'; import {runBuild} from 'flipper-pkg-lib'; -import {getInstalledPluginDetails} from 'flipper-plugin-lib'; export default class Bundle extends Command { public static description = 'transpiles and bundles plugin'; @@ -45,42 +44,18 @@ export default class Bundle extends Command { public async run() { const {args, flags} = this.parse(Bundle); const inputDirectory: string = path.resolve(process.cwd(), args.directory); - const stat = await fs.lstat(inputDirectory); - if (!stat.isDirectory()) { - this.error(`Plugin source ${inputDirectory} is not a directory.`); - } - const packageJsonPath = path.join(inputDirectory, 'package.json'); - if (!(await fs.pathExists(packageJsonPath))) { - this.error( - `package.json is not found in plugin source directory ${inputDirectory}.`, - ); - } - const plugin = await getInstalledPluginDetails(inputDirectory); - const out = path.resolve(inputDirectory, plugin.main); - await fs.ensureDir(path.dirname(out)); - - const success = await runBuildOnce( - inputDirectory, - plugin.source, - out, - !flags.production, - ); + const success = await runBuildOnce(inputDirectory, !flags.production); if (!flags.watch) { process.exit(success ? 0 : 1); } else { - enterWatchMode(inputDirectory, plugin.source, out, !flags.production); + enterWatchMode(inputDirectory, !flags.production); } } } -async function runBuildOnce( - inputDirectory: string, - source: string, - out: string, - dev: boolean, -) { +async function runBuildOnce(inputDirectory: string, dev: boolean) { try { - await runBuild(inputDirectory, source, out, dev); + await runBuild(inputDirectory, dev); console.log('✅ Build succeeded'); return true; } catch (e) { @@ -90,12 +65,7 @@ async function runBuildOnce( } } -function enterWatchMode( - inputDirectory: string, - source: string, - out: string, - dev: boolean, -) { +function enterWatchMode(inputDirectory: string, dev: boolean) { console.log(`⏳ Waiting for changes...`); let isBuilding = false; let pendingChanges = false; @@ -112,7 +82,7 @@ function enterWatchMode( isBuilding = true; while (pendingChanges) { pendingChanges = false; - await runBuildOnce(inputDirectory, source, out, dev); + await runBuildOnce(inputDirectory, dev); } isBuilding = false; console.log(`⏳ Waiting for changes...`); diff --git a/desktop/pkg/src/commands/checksum.ts b/desktop/pkg/src/commands/checksum.ts index d2ca93b0a..f0a4e29b4 100644 --- a/desktop/pkg/src/commands/checksum.ts +++ b/desktop/pkg/src/commands/checksum.ts @@ -10,7 +10,7 @@ import {Command} from '@oclif/command'; import {args} from '@oclif/parser'; import path from 'path'; -import computePackageChecksum from '../utils/computePackageChecksum'; +import {computePackageChecksum} from 'flipper-pkg-lib'; export default class Lint extends Command { public static description = diff --git a/desktop/pkg/src/commands/pack.ts b/desktop/pkg/src/commands/pack.ts index 0ac8598ec..44674762f 100644 --- a/desktop/pkg/src/commands/pack.ts +++ b/desktop/pkg/src/commands/pack.ts @@ -10,13 +10,12 @@ import {Command, flags} from '@oclif/command'; import {args} from '@oclif/parser'; import {promises as fs} from 'fs'; -import {mkdirp, pathExists, readJSON, ensureDir} from 'fs-extra'; +import {mkdirp, pathExists, readJSON} from 'fs-extra'; import * as inquirer from 'inquirer'; import * as path from 'path'; import * as yarn from '../utils/yarn'; import cli from 'cli-ux'; import {runBuild} from 'flipper-pkg-lib'; -import {getInstalledPluginDetails} from 'flipper-plugin-lib'; async function deriveOutputFileName(inputDirectory: string): Promise { const packageJson = await readJSON(path.join(inputDirectory, 'package.json')); @@ -115,14 +114,8 @@ export default class Pack extends Command { await yarn.install(inputDirectory); cli.action.stop(); - cli.action.start('Reading plugin details'); - const plugin = await getInstalledPluginDetails(inputDirectory); - const out = path.resolve(inputDirectory, plugin.main); - cli.action.stop(`done. Source: ${plugin.source}. Main: ${plugin.main}.`); - cli.action.start(`Compiling`); - await ensureDir(path.dirname(out)); - await runBuild(inputDirectory, plugin.source, out, parsedFlags.production); + await runBuild(inputDirectory, parsedFlags.production); cli.action.stop(); cli.action.start(`Packing to ${outputFile}`); diff --git a/desktop/scripts/build-plugin.ts b/desktop/scripts/build-plugin.ts index ad12445b0..6b77b2b42 100644 --- a/desktop/scripts/build-plugin.ts +++ b/desktop/scripts/build-plugin.ts @@ -12,27 +12,68 @@ import path from 'path'; import fs from 'fs-extra'; import {execSync} from 'child_process'; import {resolvePluginDir} from './workspaces'; +import {runBuild, computePackageChecksum} from 'flipper-pkg-lib'; +import yargs from 'yargs'; -async function buildPlugin(argv: string[]) { - const pluginName = argv[2]; +const argv = yargs + .usage('yarn build-plugin [args]') + .version(false) + .options({ + plugin: { + description: + 'Plugin ID or path relative to "plugins" dir (e.g. "layout")', + type: 'string', + demandOption: true, + alias: 'p', + }, + version: { + description: 'New version to set', + type: 'string', + alias: 'v', + }, + checksum: { + description: + 'Checksum of the previous plugin package which is used to determine whether the plugin is changed or not. If it is not changed, it will not be packaged.', + type: 'string', + alias: 'c', + }, + output: { + description: 'Where to save the plugin package', + type: 'string', + alias: 'o', + }, + }) + .help() + .strict() + .parse(process.argv.slice(1)); + +async function buildPlugin() { + const pluginName = argv.plugin; + const previousChecksum = argv.checksum; const pluginDir = await resolvePluginDir(pluginName); - const outputFileArg = argv.length > 3 ? argv[3] : null; - const outputFile = outputFileArg - ? path.resolve(outputFileArg) - : path.join( - distDir, - 'plugins', - path.relative(pluginsDir, pluginDir) + '.tgz', - ); - await fs.ensureDir(path.dirname(outputFile)); - await fs.remove(outputFile); - const bundleCmd = `yarn flipper-pkg bundle "${pluginDir}" --production`; - const packCmd = `yarn pack --cwd "${pluginDir}" --filename ${outputFile}`; - execSync(bundleCmd, {cwd: rootDir, stdio: 'inherit'}); - execSync(packCmd, {cwd: rootDir, stdio: 'inherit'}); + const outputFileArg = argv.output; + await runBuild(pluginDir, false); + const checksum = await computePackageChecksum(pluginDir); + if (previousChecksum !== checksum && argv.version) { + console.log(`Plugin changed. Packaging new version ${argv.version}...`); + const outputFile = outputFileArg + ? path.resolve(outputFileArg) + : path.join( + distDir, + 'plugins', + path.relative(pluginsDir, pluginDir) + '.tgz', + ); + await fs.ensureDir(path.dirname(outputFile)); + await fs.remove(outputFile); + const versionCmd = `yarn version --cwd "${pluginDir}" --new-version ${argv.version}`; + execSync(versionCmd, {cwd: rootDir, stdio: 'inherit'}); + const packCmd = `yarn pack --cwd "${pluginDir}" --filename ${outputFile}`; + execSync(packCmd, {cwd: rootDir, stdio: 'inherit'}); + await fs.writeFile(outputFile + '.hash', checksum); + } } -buildPlugin(process.argv) +buildPlugin() .then(() => { process.exit(0); })