From 9dfafedf61c4c6747c239c8ad8394efeb9da8dc1 Mon Sep 17 00:00:00 2001 From: andersnm Date: Sun, 21 Apr 2019 07:32:50 +0200 Subject: [PATCH] Pack to ./artifacts, implemented pack:deploy --- README.md | 2 ++ packages/blerf/README.md | 24 +++++++++++---- packages/blerf/package.json | 2 +- packages/blerf/src/commands/build.ts | 1 - packages/blerf/src/commands/bundle.ts | 44 +++++++++++++++++++++++++++ packages/blerf/src/commands/pack.ts | 40 ++++++++++++++++++------ packages/blerf/src/index.ts | 44 ++++++++++++++++----------- 7 files changed, 121 insertions(+), 36 deletions(-) create mode 100644 packages/blerf/src/commands/bundle.ts diff --git a/README.md b/README.md index cca6e96..e93423c 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,8 @@ Monorepo for blerf, a monorepo tool. See [packages/blerf](packages/blerf) +[![Build status](https://ci.appveyor.com/api/projects/status/ivy1wa5f6dsmdmym?svg=true)](https://ci.appveyor.com/project/andersnm/blerf) + ## Building the source code Check out and bootstrap the monorepo: diff --git a/packages/blerf/README.md b/packages/blerf/README.md index d46bac5..7465604 100644 --- a/packages/blerf/README.md +++ b/packages/blerf/README.md @@ -12,9 +12,13 @@ Opinionated build tool for nodejs monorepos working alongside npm. Helps manage ## Commands -`blerf build` +### `blerf build` -In each directory under ./packages containing a package.json, first runs `npm install` if any of the top level dependencies are missing, and then executes any build steps. A build step is skipped if there are no changes in the filesystem based on the glob patterns in `srcPath` and `outPath`. The code in `script` is spawned similar to npm scripts, where the PATH environment variable is modified to include node_modules/.bin. +Installs dependencies and executes any build steps in each directory under ./packages containing a package.json. + +Dependencies are skipped if all top level dependencies are present in a project's node_modules folder. Detects and recovers from certain types of corruption in package-lock.json. Uses `npm install` under the hood. + +Build steps are specified in package.json. Build steps are skipped if there are no changes in the filesystem based on the glob patterns in `srcPath` and `outPath`. The code in `script` is spawned similar to npm scripts, where the PATH environment variable is modified to include node_modules/.bin. Example blerf section in package.json with a build step for TypeScript: @@ -32,11 +36,19 @@ Example blerf section in package.json with a build step for TypeScript: The values for outPath and srcPath must match the tsconfig.json compiler options. -`blerf pack:publish` +### `blerf pack:publish` + +Creates tarballs for each directory under ./packages containing a package.json. The output *.tgz files are located in ./artifacts/publish and are suitable for publishing to a registry. + +Uses `npm pack` under the hood. The final tarballs will have fixed project references pointing to their corresponding version number with a ^-modifier. + +### `blerf pack:deploy` + +Creates standalone tarballs for each directory under ./packages containing a package.json. The output *.tgz files are located in ./artifacts/deploy and are suitable for application deployments. -Executes `npm pack` in each directory under ./packages containing a package.json and fixes up any project references in the tarballs. This extracts each tarball to a temp directory, changes any `file:` based dependencies in package.json to their corresponding version, updates the tarball and cleans up. +Uses `npm pack` and `npm install` under the hood. The final tarballs will have all dependencies included. -`blerf test` +### `blerf test` Executes `npm run test` in each directory under ./packages containing a package.json having a test script. If `coverageFrom` is set to a valid path, code coverage information will be collected and reported using Node's built-in `NODE_V8_COVERAGE` coverage facilities, with source map support. The built-in code coverage requires Node 10.12 or newer, and a test runner which does not transform/wrap the source code. @@ -48,7 +60,7 @@ Example blerf section in package.json enabling coverage on files in a sibling pr } ``` -`blerf run [xxx]` +### `blerf run [xxx]` Executes `npm run [xxx]` in each directory under ./packages containing a package.json having a corresponding script. diff --git a/packages/blerf/package.json b/packages/blerf/package.json index b53f62a..7992b08 100644 --- a/packages/blerf/package.json +++ b/packages/blerf/package.json @@ -1,6 +1,6 @@ { "name": "blerf", - "version": "0.0.2", + "version": "0.0.3", "description": "Blerf monorepo/solution/project management tool. Handle dependencies and run build tasks when source files change. Pack for publish, pack for deployment.", "author": "andersnm", "license": "MIT", diff --git a/packages/blerf/src/commands/build.ts b/packages/blerf/src/commands/build.ts index 45a0372..6a1d0b5 100644 --- a/packages/blerf/src/commands/build.ts +++ b/packages/blerf/src/commands/build.ts @@ -183,7 +183,6 @@ export class BuildEnumerator extends PackageEnumerator { private hasAllDependencies(packageJson: any, dependencyNames: string[]) { for (const dependencyName of dependencyNames) { if (!this.hasDependency(packageJson, dependencyName)) { - console.log("MISSING" + dependencyName) return false; } } diff --git a/packages/blerf/src/commands/bundle.ts b/packages/blerf/src/commands/bundle.ts new file mode 100644 index 0000000..324ca58 --- /dev/null +++ b/packages/blerf/src/commands/bundle.ts @@ -0,0 +1,44 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import * as os from 'os'; +import * as childProcess from 'child_process'; +import { PackageEnumerator, PackagesType } from "../packageEnumerator"; +const tar = require('tar') + +export class BundleEnumerator extends PackageEnumerator { + private artifactPackPath: string; + private artifactDeployPath: string; + + constructor(rootPath: string, artifactPackPath: string, artifactDeployPath: string) { + super(rootPath); + this.artifactPackPath = artifactPackPath; + this.artifactDeployPath = artifactDeployPath; + } + + public async enumeratePackages(): Promise { + await super.enumeratePackages(); + + // Remove artifacts/deploy-temp created by PackEnumerator + this.rimraf(this.artifactPackPath); + } + + protected async processPackage(packagePath: string, packageJson: any, packages: PackagesType): Promise { + console.log("blerf: bundling node_modules"); + + // NOTE: assuming file name of tarball; can also get it from the output of npm pack + const tempPath = fs.mkdtempSync(path.join(os.tmpdir(), "blerf-")); + const artifactPackTarPath = path.join(this.artifactPackPath, packageJson.name + "-" + packageJson.version + ".tgz"); + const artifactTarPath = path.join(this.artifactDeployPath, packageJson.name + "-" + packageJson.version + ".tgz"); + + fs.mkdirSync(this.artifactDeployPath, { recursive: true }); + + try { + tar.extract({ file: artifactPackTarPath, cwd: tempPath, sync: true }); + childProcess.execSync("npm install", {stdio: 'inherit', cwd: path.join(tempPath, "package") }); + + tar.create({ file: artifactTarPath, cwd: tempPath, gzip: true, sync: true, }, ["package"]); + } finally { + this.rimraf(tempPath); + } + } +} diff --git a/packages/blerf/src/commands/pack.ts b/packages/blerf/src/commands/pack.ts index 789f78a..4d5b08f 100644 --- a/packages/blerf/src/commands/pack.ts +++ b/packages/blerf/src/commands/pack.ts @@ -8,10 +8,19 @@ const tar = require('tar') export class PackEnumerator extends PackageEnumerator { private isDeploy: boolean; + private artifactCleanPath: string; + private artifactPackPath: string; - constructor(rootPath: string, isDeploy: boolean) { + constructor(rootPath: string, artifactPath: string, artifactCleanPath: string, isDeploy: boolean) { super(rootPath); this.isDeploy = isDeploy; + this.artifactCleanPath = artifactCleanPath; + this.artifactPackPath = artifactPath; + } + + public async enumeratePackages(): Promise { + this.rimraf(this.artifactCleanPath); + await super.enumeratePackages(); } protected async processPackage(packagePath: string, packageJson: any, packages: PackagesType): Promise { @@ -23,16 +32,24 @@ export class PackEnumerator extends PackageEnumerator { const sourcePackageTarPath = path.join(packagePath, packageJson.name + "-" + packageJson.version + ".tgz"); const tempPath = fs.mkdtempSync(path.join(os.tmpdir(), "blerf-")); + const artifactPackTarPath = path.join(this.artifactPackPath, packageJson.name + "-" + packageJson.version + ".tgz"); + + fs.mkdirSync(this.artifactPackPath, { recursive: true }); + try { tar.extract({ file: sourcePackageTarPath, cwd: tempPath, sync: true }); - this.patchPackageJson(packagePath, path.join(tempPath, "package", "package.json"), packages); - tar.create({ file: sourcePackageTarPath, cwd: tempPath, gzip: true, sync: true, }, ["package"]); + this.patchPackageJson(packagePath, path.join(tempPath, "package", "package.json"), path.resolve(this.artifactPackPath), packages); + if (this.isDeploy) { + fs.copyFileSync(path.join(packagePath, "package-lock.json"), path.join(tempPath, "package", "package-lock.json")); + } + tar.create({ file: artifactPackTarPath, cwd: tempPath, gzip: true, sync: true, }, ["package"]); } finally { + fs.unlinkSync(sourcePackageTarPath); this.rimraf(tempPath); } } - updateDependencyVersions(packagePath: string, packageDependencies: any, packages: PackagesType) { + updateDependencyVersions(packagePath: string, artifactPackFullPath: string, packageDependencies: any, packages: PackagesType) { if (!packageDependencies) { return; } @@ -46,9 +63,9 @@ export class PackEnumerator extends PackageEnumerator { const dependencyPackageInfo = packages[dependencyName]; if (dependencyPackageInfo) { if (this.isDeploy) { - packageDependencies[dependencyName] = dependencyPackageInfo.packageJson.name + "-" + dependencyPackageInfo.packageJson.version + ".tgz"; + packageDependencies[dependencyName] = path.join(artifactPackFullPath, dependencyPackageInfo.packageJson.name + "-" + dependencyPackageInfo.packageJson.version + ".tgz"); } else { - packageDependencies[dependencyName] = dependencyPackageInfo.packageJson.version; + packageDependencies[dependencyName] = "^" + dependencyPackageInfo.packageJson.version; } } else { // TODO: possibly noop instead? @@ -57,12 +74,15 @@ export class PackEnumerator extends PackageEnumerator { } } - private patchPackageJson(packagePath: string, packageJsonPath: string, packages: PackagesType) { + private patchPackageJson(packagePath: string, packageJsonPath: string, artifactPackFullPath: string, packages: PackagesType) { // Resolve all file:-based dependencies to explicit versions const packageJson = this.readPackageJson(packageJsonPath); - this.updateDependencyVersions(packagePath, packageJson.dependencies, packages); - this.updateDependencyVersions(packagePath, packageJson.devDependencies, packages); + this.updateDependencyVersions(packagePath, artifactPackFullPath, packageJson.dependencies, packages); + this.updateDependencyVersions(packagePath, artifactPackFullPath, packageJson.devDependencies, packages); + // Remove stuff not needed in "binary" packge + delete packageJson.scripts; + delete packageJson.blerf; + delete packageJson.devDependencies; fs.writeFileSync(packageJsonPath, stringifyPackage(packageJson), 'utf8'); } - } diff --git a/packages/blerf/src/index.ts b/packages/blerf/src/index.ts index f809bed..8b12ba7 100644 --- a/packages/blerf/src/index.ts +++ b/packages/blerf/src/index.ts @@ -4,24 +4,32 @@ import { RunEnumerator } from './commands/run'; import { PackEnumerator } from './commands/pack'; import { BuildEnumerator } from './commands/build'; import { TestEnumerator } from './commands/test'; +import { BundleEnumerator } from './commands/bundle'; const rootPath = "packages"; -if (process.argv[2] === "run") { - const cmd = new RunEnumerator(rootPath, process.argv.slice(3)); - cmd.enumeratePackages(); -} else if (process.argv[2] === "pack:publish") { - const cmd = new PackEnumerator(rootPath, false); - cmd.enumeratePackages(); -} else if (process.argv[2] === "pack:deploy") { - const cmd = new PackEnumerator(rootPath, true); - cmd.enumeratePackages(); -} else if (process.argv[2] === "build") { - const cmd = new BuildEnumerator(rootPath); - cmd.enumeratePackages(); -} else if (process.argv[2] === "test") { - const cmd = new TestEnumerator(rootPath); - cmd.enumeratePackages(); -} else { - console.log("usage: blerf [run|install|pack|build|test]") -} +(async () => { + + if (process.argv[2] === "run") { + const cmd = new RunEnumerator(rootPath, process.argv.slice(3)); + await cmd.enumeratePackages(); + } else if (process.argv[2] === "pack:publish") { + const cmd = new PackEnumerator(rootPath, "artifacts/publish", "artifacts/publish", false); + await cmd.enumeratePackages(); + } else if (process.argv[2] === "pack:deploy") { + const pack = new PackEnumerator(rootPath, "artifacts/deploy-temp", "artifacts/deploy", true); + await pack.enumeratePackages(); + + const bundle = new BundleEnumerator(rootPath, "artifacts/deploy-temp", "artifacts/deploy"); + await bundle.enumeratePackages(); + } else if (process.argv[2] === "build") { + const cmd = new BuildEnumerator(rootPath); + await cmd.enumeratePackages(); + } else if (process.argv[2] === "test") { + const cmd = new TestEnumerator(rootPath); + await cmd.enumeratePackages(); + } else { + console.log("usage: blerf [run|install|pack|build|test]") + } + +})();