diff options
author | Alex Browne <stephenalexbrowne@gmail.com> | 2018-08-14 07:49:50 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-08-14 07:49:50 +0800 |
commit | 283175df9827be38f8cc9f18d3914e3661456fc4 (patch) | |
tree | 569f4ce716ff15c533adb36021c9b95b5f27e24b /packages | |
parent | aeb368a1d92cd0f558db3e7456eccbb7cccd50aa (diff) | |
download | dexon-sol-tools-283175df9827be38f8cc9f18d3914e3661456fc4.tar.gz dexon-sol-tools-283175df9827be38f8cc9f18d3914e3661456fc4.tar.zst dexon-sol-tools-283175df9827be38f8cc9f18d3914e3661456fc4.zip |
Run publish/installation tests in CircleCI (#951)
feat(monorepo-scripts): Run publish tests in CircleCI
Diffstat (limited to 'packages')
-rw-r--r-- | packages/monorepo-scripts/package.json | 2 | ||||
-rw-r--r-- | packages/monorepo-scripts/src/test_installation.ts | 163 |
2 files changed, 119 insertions, 46 deletions
diff --git a/packages/monorepo-scripts/package.json b/packages/monorepo-scripts/package.json index b5fae9835..7d83d5a51 100644 --- a/packages/monorepo-scripts/package.json +++ b/packages/monorepo-scripts/package.json @@ -30,6 +30,7 @@ "homepage": "https://github.com/0xProject/0x-monorepo/packages/monorepo-scripts/README.md", "devDependencies": { "@types/glob": "^5.0.33", + "@types/mkdirp": "^0.5.2", "@types/node": "^8.0.53", "@types/opn": "^5.1.0", "@types/rimraf": "^2.0.2", @@ -50,6 +51,7 @@ "glob": "^7.1.2", "isomorphic-fetch": "2.2.1", "lodash": "^4.17.5", + "mkdirp": "^0.5.1", "moment": "2.21.0", "opn": "^5.3.0", "promisify-child-process": "^1.0.5", diff --git a/packages/monorepo-scripts/src/test_installation.ts b/packages/monorepo-scripts/src/test_installation.ts index a8ddf0c58..87c4ad1d7 100644 --- a/packages/monorepo-scripts/src/test_installation.ts +++ b/packages/monorepo-scripts/src/test_installation.ts @@ -2,10 +2,13 @@ import * as fs from 'fs'; import * as _ from 'lodash'; +import * as mkdirp from 'mkdirp'; import * as path from 'path'; import { exec as execAsync } from 'promisify-child-process'; import * as rimraf from 'rimraf'; +import { promisify } from 'util'; +import { Package } from './types'; import { utils } from './utils/utils'; // Packages might not be runnable if they are command-line tools or only run in browsers. @@ -16,64 +19,132 @@ const UNRUNNABLE_PACKAGES = [ '@0xproject/react-docs', ]; +const mkdirpAsync = promisify(mkdirp); +const rimrafAsync = promisify(rimraf); +const writeFileAsync = promisify(fs.writeFile); + +interface PackageErr { + packageName: string; + error: ExecError; +} + +interface ExecError { + message: string; + stack: string; + stderr: string; + stdout: string; +} + +// returns the index for the given package name. +function findPackageIndex(packages: Package[], packageName: string): number { + return _.findIndex(packages, pkg => pkg.packageJson.name === packageName); +} + +function logIfDefined(x: any): void { + if (!_.isUndefined(x)) { + utils.log(x); + } +} + (async () => { const IS_LOCAL_PUBLISH = process.env.IS_LOCAL_PUBLISH === 'true'; const registry = IS_LOCAL_PUBLISH ? 'http://localhost:4873/' : 'https://registry.npmjs.org/'; const monorepoRootPath = path.join(__dirname, '../../..'); - const packages = utils.getTopologicallySortedPackages(monorepoRootPath); + const packages = utils.getPackages(monorepoRootPath); const installablePackages = _.filter( packages, pkg => !pkg.packageJson.private && !_.isUndefined(pkg.packageJson.main) && pkg.packageJson.main.endsWith('.js'), ); utils.log('Testing packages:'); _.map(installablePackages, pkg => utils.log(`* ${pkg.packageJson.name}`)); + // Run all package tests asynchronously and push promises into an array so + // we can wait for all of them to resolve. + const promises: Array<Promise<void>> = []; + const errors: PackageErr[] = []; for (const installablePackage of installablePackages) { - const changelogPath = path.join(installablePackage.location, 'CHANGELOG.json'); - const lastChangelogVersion = JSON.parse(fs.readFileSync(changelogPath).toString())[0].version; - const packageName = installablePackage.packageJson.name; - utils.log(`Testing ${packageName}@${lastChangelogVersion}`); - const testDirectory = path.join(monorepoRootPath, '../test-env'); - rimraf.sync(testDirectory); - fs.mkdirSync(testDirectory); - await execAsync('yarn init --yes', { cwd: testDirectory }); - const npmrcFilePath = path.join(testDirectory, '.npmrc'); - fs.writeFileSync(npmrcFilePath, `registry=${registry}`); - utils.log(`Installing ${packageName}@${lastChangelogVersion}`); - await execAsync(`npm install --save ${packageName}@${lastChangelogVersion} --registry=${registry}`, { - cwd: testDirectory, + const packagePromise = testInstallPackageAsync(monorepoRootPath, registry, installablePackage).catch(error => { + errors.push({ packageName: installablePackage.packageJson.name, error }); }); - const indexFilePath = path.join(testDirectory, 'index.ts'); - fs.writeFileSync(indexFilePath, `import * as Package from '${packageName}';\nconsole.log(Package);\n`); - const tsConfig = { - compilerOptions: { - typeRoots: ['node_modules/@0xproject/typescript-typings/types', 'node_modules/@types'], - module: 'commonjs', - target: 'es5', - lib: ['es2017', 'dom'], - declaration: true, - noImplicitReturns: true, - pretty: true, - strict: true, - }, - include: ['index.ts'], - }; - const tsconfigFilePath = path.join(testDirectory, 'tsconfig.json'); - fs.writeFileSync(tsconfigFilePath, JSON.stringify(tsConfig, null, '\t')); - utils.log(`Compiling ${packageName}`); - const tscBinaryPath = path.join(monorepoRootPath, './node_modules/typescript/bin/tsc'); - await execAsync(tscBinaryPath, { cwd: testDirectory }); - utils.log(`Successfully compiled with ${packageName} as a dependency`); - const isUnrunnablePkg = _.includes(UNRUNNABLE_PACKAGES, packageName); - if (!isUnrunnablePkg) { - const transpiledIndexFilePath = path.join(testDirectory, 'index.js'); - utils.log(`Running test script with ${packageName} imported`); - await execAsync(`node ${transpiledIndexFilePath}`); - utils.log(`Successfilly ran test script with ${packageName} imported`); - } - rimraf.sync(testDirectory); + promises.push(packagePromise); + } + await Promise.all(promises); + if (errors.length > 0) { + // We sort error messages according to package topology so that we can + // them in a more intuitive order. E.g. if package A has an error and + // package B imports it, the tests for both package A and package B will + // fail. But package B only fails because of an error in package A. + // Since the error in package A is the root cause, we log it first. + const topologicallySortedPackages = utils.getTopologicallySortedPackages(monorepoRootPath); + const topologicallySortedErrors = _.sortBy(errors, packageErr => + findPackageIndex(topologicallySortedPackages, packageErr.packageName), + ); + _.forEach(topologicallySortedErrors, packageError => { + utils.log(`ERROR in package ${packageError.packageName}:`); + logIfDefined(packageError.error.message); + logIfDefined(packageError.error.stderr); + logIfDefined(packageError.error.stdout); + logIfDefined(packageError.error.stack); + }); + process.exit(0); } })().catch(err => { - utils.log(err.stderr); - utils.log(err.stdout); - process.exit(1); + utils.log(`Unexpected error: ${err.message}`); + process.exit(0); }); + +async function testInstallPackageAsync( + monorepoRootPath: string, + registry: string, + installablePackage: Package, +): Promise<void> { + const changelogPath = path.join(installablePackage.location, 'CHANGELOG.json'); + const lastChangelogVersion = JSON.parse(fs.readFileSync(changelogPath).toString())[0].version; + const packageName = installablePackage.packageJson.name; + utils.log(`Testing ${packageName}@${lastChangelogVersion}`); + const packageDirName = path.join(...(packageName + '-test').split('/')); + const testDirectory = path.join( + monorepoRootPath, + 'packages', + 'monorepo-scripts', + '.installation-test', + packageDirName, + ); + await rimrafAsync(testDirectory); + await mkdirpAsync(testDirectory); + await execAsync('yarn init --yes', { cwd: testDirectory }); + const npmrcFilePath = path.join(testDirectory, '.npmrc'); + await writeFileAsync(npmrcFilePath, `registry=${registry}`); + utils.log(`Installing ${packageName}@${lastChangelogVersion}`); + await execAsync(`npm install --save ${packageName}@${lastChangelogVersion} --registry=${registry}`, { + cwd: testDirectory, + }); + const indexFilePath = path.join(testDirectory, 'index.ts'); + await writeFileAsync(indexFilePath, `import * as Package from '${packageName}';\nconsole.log(Package);\n`); + const tsConfig = { + compilerOptions: { + typeRoots: ['node_modules/@0xproject/typescript-typings/types', 'node_modules/@types'], + module: 'commonjs', + target: 'es5', + lib: ['es2017', 'dom'], + declaration: true, + noImplicitReturns: true, + pretty: true, + strict: true, + }, + include: ['index.ts'], + }; + const tsconfigFilePath = path.join(testDirectory, 'tsconfig.json'); + await writeFileAsync(tsconfigFilePath, JSON.stringify(tsConfig, null, '\t')); + utils.log(`Compiling ${packageName}`); + const tscBinaryPath = path.join(monorepoRootPath, './node_modules/typescript/bin/tsc'); + await execAsync(tscBinaryPath, { cwd: testDirectory }); + utils.log(`Successfully compiled with ${packageName} as a dependency`); + const isUnrunnablePkg = _.includes(UNRUNNABLE_PACKAGES, packageName); + if (!isUnrunnablePkg) { + const transpiledIndexFilePath = path.join(testDirectory, 'index.js'); + utils.log(`Running test script with ${packageName} imported`); + await execAsync(`node ${transpiledIndexFilePath}`); + utils.log(`Successfilly ran test script with ${packageName} imported`); + } + await rimrafAsync(testDirectory); +} |