From 06e5fc233c512d152d61c4acbed8d9344e769abe Mon Sep 17 00:00:00 2001 From: Leonid Logvinov Date: Fri, 1 Jun 2018 13:12:45 -0700 Subject: Refactor changelog utils to a separate module --- packages/monorepo-scripts/src/deps_versions.ts | 2 +- .../src/find_unused_dependencies.ts | 2 +- packages/monorepo-scripts/src/postpublish_utils.ts | 2 +- packages/monorepo-scripts/src/publish.ts | 62 ++++----------------- packages/monorepo-scripts/src/remove_tags.ts | 4 +- packages/monorepo-scripts/src/test_installation.ts | 2 +- packages/monorepo-scripts/src/types.ts | 8 ++- packages/monorepo-scripts/src/utils.ts | 64 ---------------------- .../monorepo-scripts/src/utils/changelog_utils.ts | 55 +++++++++++++++++++ packages/monorepo-scripts/src/utils/utils.ts | 64 ++++++++++++++++++++++ 10 files changed, 140 insertions(+), 125 deletions(-) delete mode 100644 packages/monorepo-scripts/src/utils.ts create mode 100644 packages/monorepo-scripts/src/utils/changelog_utils.ts create mode 100644 packages/monorepo-scripts/src/utils/utils.ts diff --git a/packages/monorepo-scripts/src/deps_versions.ts b/packages/monorepo-scripts/src/deps_versions.ts index 07292a160..f090d12e9 100644 --- a/packages/monorepo-scripts/src/deps_versions.ts +++ b/packages/monorepo-scripts/src/deps_versions.ts @@ -5,7 +5,7 @@ import * as fs from 'fs'; import { sync as globSync } from 'glob'; import * as _ from 'lodash'; -import { utils } from './utils'; +import { utils } from './utils/utils'; interface Dependencies { [depName: string]: string; diff --git a/packages/monorepo-scripts/src/find_unused_dependencies.ts b/packages/monorepo-scripts/src/find_unused_dependencies.ts index df303f6ce..71e707224 100644 --- a/packages/monorepo-scripts/src/find_unused_dependencies.ts +++ b/packages/monorepo-scripts/src/find_unused_dependencies.ts @@ -7,7 +7,7 @@ import * as _ from 'lodash'; import { exec as execAsync } from 'promisify-child-process'; import { constants } from './constants'; -import { utils } from './utils'; +import { utils } from './utils/utils'; // For some reason, `depcheck` hangs on some packages. Add them here. const IGNORE_PACKAGES = ['@0xproject/sol-compiler']; diff --git a/packages/monorepo-scripts/src/postpublish_utils.ts b/packages/monorepo-scripts/src/postpublish_utils.ts index f5785343d..dbbde894d 100644 --- a/packages/monorepo-scripts/src/postpublish_utils.ts +++ b/packages/monorepo-scripts/src/postpublish_utils.ts @@ -7,7 +7,7 @@ import * as publishRelease from 'publish-release'; import semverSort = require('semver-sort'); import { constants } from './constants'; -import { utils } from './utils'; +import { utils } from './utils/utils'; const publishReleaseAsync = promisify(publishRelease); const generatedDocsDirectoryName = 'generated_docs'; diff --git a/packages/monorepo-scripts/src/publish.ts b/packages/monorepo-scripts/src/publish.ts index 73106821a..022fe172e 100644 --- a/packages/monorepo-scripts/src/publish.ts +++ b/packages/monorepo-scripts/src/publish.ts @@ -13,14 +13,14 @@ import semverDiff = require('semver-diff'); import semverSort = require('semver-sort'); import { constants } from './constants'; -import { Changelog, Changes, PackageToVersionChange, SemVerIndex, UpdatedPackage } from './types'; -import { utils } from './utils'; +import { Changelog, PackageToVersionChange, SemVerIndex, VersionChangelog } from './types'; +import { changelogUtils } from './utils/changelog_utils'; +import { utils } from './utils/utils'; const DOC_GEN_COMMAND = 'docs:json'; const NPM_NAMESPACE = '@0xproject/'; const IS_DRY_RUN = process.env.IS_DRY_RUN === 'true'; const TODAYS_TIMESTAMP = moment().unix(); -const LERNA_EXECUTABLE = './node_modules/lerna/bin/lerna.js'; const semverNameToIndex: { [semver: string]: number } = { patch: SemVerIndex.Patch, minor: SemVerIndex.Minor, @@ -118,7 +118,7 @@ async function checkPublishRequiredSetupAsync(): Promise { // check to see if logged into npm before publishing try { // HACK: for some reason on some setups, the `npm whoami` will not recognize a logged-in user - // unless run with `sudo` (i.e Fabio's NVM setup) but is fine for others (Jacob's N setup). + // unless run with `sudo` (i.e Fabio's NVM setup) but is fine for others (Jacob's NVM setup). await execAsync(`sudo npm whoami`); } catch (err) { utils.log('You must be logged into npm in the commandline to publish. Run `npm login` and try again.'); @@ -175,7 +175,7 @@ async function updateChangeLogsAsync(updatedPublicLernaPackages: LernaPackage[]) const packageName = lernaPackage.package.name; const changelogJSONPath = path.join(lernaPackage.location, 'CHANGELOG.json'); const changelogJSON = utils.getChangelogJSONOrCreateIfMissing(changelogJSONPath); - let changelogs: Changelog[]; + let changelogs: Changelog; try { changelogs = JSON.parse(changelogJSON); } catch (err) { @@ -185,11 +185,11 @@ async function updateChangeLogsAsync(updatedPublicLernaPackages: LernaPackage[]) } const currentVersion = lernaPackage.package.version; - const shouldAddNewEntry = shouldAddNewChangelogEntry(currentVersion, changelogs); + const shouldAddNewEntry = changelogUtils.shouldAddNewChangelogEntry(currentVersion, changelogs); if (shouldAddNewEntry) { // Create a new entry for a patch version with generic changelog entry. const nextPatchVersion = utils.getNextPatchVersion(currentVersion); - const newChangelogEntry: Changelog = { + const newChangelogEntry: VersionChangelog = { timestamp: TODAYS_TIMESTAMP, version: nextPatchVersion, changes: [ @@ -218,7 +218,7 @@ async function updateChangeLogsAsync(updatedPublicLernaPackages: LernaPackage[]) await utils.prettifyAsync(changelogJSONPath, constants.monorepoRootPath); utils.log(`${packageName}: Updated CHANGELOG.json`); // Generate updated CHANGELOG.md - const changelogMd = generateChangelogMd(changelogs); + const changelogMd = changelogUtils.generateChangelogMd(changelogs); const changelogMdPath = path.join(lernaPackage.location, 'CHANGELOG.md'); fs.writeFileSync(changelogMdPath, changelogMd); await utils.prettifyAsync(changelogMdPath, constants.monorepoRootPath); @@ -231,7 +231,8 @@ async function updateChangeLogsAsync(updatedPublicLernaPackages: LernaPackage[]) async function lernaPublishAsync(packageToVersionChange: { [name: string]: string }): Promise { // HACK: Lerna publish does not provide a way to specify multiple package versions via // flags so instead we need to interact with their interactive prompt interface. - const child = spawn('lerna', ['publish', '--registry=https://registry.npmjs.org/'], { + const PACKAGE_REGISTRY = 'https://registry.npmjs.org/'; + const child = spawn('lerna', ['publish', `--registry=${PACKAGE_REGISTRY}`], { cwd: constants.monorepoRootPath, }); let shouldPrintOutput = false; @@ -279,46 +280,3 @@ function updateVersionNumberIfNeeded(currentVersion: string, proposedNextVersion } return proposedNextVersion; } - -function shouldAddNewChangelogEntry(currentVersion: string, changelogs: Changelog[]): boolean { - if (_.isEmpty(changelogs)) { - return true; - } - const lastEntry = changelogs[0]; - const isLastEntryCurrentVersion = lastEntry.version === currentVersion; - return isLastEntryCurrentVersion; -} - -function generateChangelogMd(changelogs: Changelog[]): string { - let changelogMd = ` - -CHANGELOG - `; - - _.each(changelogs, changelog => { - if (_.isUndefined(changelog.timestamp)) { - throw new Error( - 'All CHANGELOG.json entries must be updated to include a timestamp before generating their MD version', - ); - } - const date = moment(`${changelog.timestamp}`, 'X').format('MMMM D, YYYY'); - const title = `\n## v${changelog.version} - _${date}_\n\n`; - changelogMd += title; - - let changes = ''; - _.each(changelog.changes, change => { - let line = ` * ${change.note}`; - if (!_.isUndefined(change.pr)) { - line += ` (#${change.pr})`; - } - line += '\n'; - changes += line; - }); - changelogMd += `${changes}`; - }); - - return changelogMd; -} diff --git a/packages/monorepo-scripts/src/remove_tags.ts b/packages/monorepo-scripts/src/remove_tags.ts index 6d09729c7..affdf2751 100644 --- a/packages/monorepo-scripts/src/remove_tags.ts +++ b/packages/monorepo-scripts/src/remove_tags.ts @@ -8,7 +8,7 @@ import semverSort = require('semver-sort'); import { constants } from './constants'; import { Changelog } from './types'; -import { utils } from './utils'; +import { utils } from './utils/utils'; (async () => { const shouldIncludePrivate = true; @@ -24,7 +24,7 @@ import { utils } from './utils'; let latestChangelogVersion: string; if (!_.isUndefined(changelogJSONIfExists)) { - let changelogs: Changelog[]; + let changelogs: Changelog; try { changelogs = JSON.parse(changelogJSONIfExists); } catch (err) { diff --git a/packages/monorepo-scripts/src/test_installation.ts b/packages/monorepo-scripts/src/test_installation.ts index e84221f9d..b67154667 100644 --- a/packages/monorepo-scripts/src/test_installation.ts +++ b/packages/monorepo-scripts/src/test_installation.ts @@ -7,7 +7,7 @@ import * as path from 'path'; import { exec as execAsync } from 'promisify-child-process'; import * as rimraf from 'rimraf'; -import { utils } from './utils'; +import { utils } from './utils/utils'; (async () => { const monorepoRootPath = path.join(__dirname, '../../..'); diff --git a/packages/monorepo-scripts/src/types.ts b/packages/monorepo-scripts/src/types.ts index 28d00d710..36fb923b3 100644 --- a/packages/monorepo-scripts/src/types.ts +++ b/packages/monorepo-scripts/src/types.ts @@ -4,15 +4,17 @@ export interface UpdatedPackage { private: boolean; } -export interface Changes { +export interface Change { note: string; pr?: number; } -export interface Changelog { +export type Changelog = VersionChangelog[]; + +export interface VersionChangelog { timestamp?: number; version: string; - changes: Changes[]; + changes: Change[]; } export enum SemVerIndex { diff --git a/packages/monorepo-scripts/src/utils.ts b/packages/monorepo-scripts/src/utils.ts deleted file mode 100644 index c2d92c86a..000000000 --- a/packages/monorepo-scripts/src/utils.ts +++ /dev/null @@ -1,64 +0,0 @@ -import * as fs from 'fs'; -import lernaGetPackages = require('lerna-get-packages'); -import * as _ from 'lodash'; -import { exec as execAsync, spawn } from 'promisify-child-process'; - -import { constants } from './constants'; -import { UpdatedPackage } from './types'; - -export const utils = { - log(...args: any[]): void { - console.log(...args); // tslint:disable-line:no-console - }, - getNextPatchVersion(currentVersion: string): string { - const versionSegments = currentVersion.split('.'); - const patch = _.parseInt(_.last(versionSegments) as string); - const newPatch = patch + 1; - const newPatchVersion = `${versionSegments[0]}.${versionSegments[1]}.${newPatch}`; - return newPatchVersion; - }, - async prettifyAsync(filePath: string, cwd: string): Promise { - await execAsync(`prettier --write ${filePath} --config .prettierrc`, { - cwd, - }); - }, - async getUpdatedLernaPackagesAsync(shouldIncludePrivate: boolean): Promise { - const updatedPublicPackages = await this.getLernaUpdatedPackagesAsync(shouldIncludePrivate); - const updatedPackageNames = _.map(updatedPublicPackages, pkg => pkg.name); - - const allLernaPackages = lernaGetPackages(constants.monorepoRootPath); - const updatedPublicLernaPackages = _.filter(allLernaPackages, pkg => { - return _.includes(updatedPackageNames, pkg.package.name); - }); - return updatedPublicLernaPackages; - }, - async getLernaUpdatedPackagesAsync(shouldIncludePrivate: boolean): Promise { - const result = await execAsync(`${constants.lernaExecutable} updated --json`, { - cwd: constants.monorepoRootPath, - }); - const updatedPackages = JSON.parse(result.stdout); - if (!shouldIncludePrivate) { - const updatedPublicPackages = _.filter(updatedPackages, updatedPackage => !updatedPackage.private); - return updatedPublicPackages; - } - return updatedPackages; - }, - getChangelogJSONIfExists(changelogPath: string): string | undefined { - try { - const changelogJSON = fs.readFileSync(changelogPath, 'utf-8'); - return changelogJSON; - } catch (err) { - return undefined; - } - }, - getChangelogJSONOrCreateIfMissing(changelogPath: string): string { - const changelogIfExists = this.getChangelogJSONIfExists(changelogPath); - if (_.isUndefined(changelogIfExists)) { - // If none exists, create new, empty one. - const emptyChangelogJSON = JSON.stringify([]); - fs.writeFileSync(changelogPath, emptyChangelogJSON); - return emptyChangelogJSON; - } - return changelogIfExists; - }, -}; diff --git a/packages/monorepo-scripts/src/utils/changelog_utils.ts b/packages/monorepo-scripts/src/utils/changelog_utils.ts new file mode 100644 index 000000000..edfe65a80 --- /dev/null +++ b/packages/monorepo-scripts/src/utils/changelog_utils.ts @@ -0,0 +1,55 @@ +import * as _ from 'lodash'; +import * as moment from 'moment'; + +import { Change, Changelog, VersionChangelog } from '../types'; + +const CHANGELOG_MD_HEADER = ` + + +CHANGELOG +`; + +export const changelogUtils = { + getChangelogMdTitle(versionChangelog: VersionChangelog): string { + if (_.isUndefined(versionChangelog.timestamp)) { + throw new Error( + 'All CHANGELOG.json entries must be updated to include a timestamp before generating their MD version', + ); + } + const date = moment(`${versionChangelog.timestamp}`, 'X').format('MMMM D, YYYY'); + const title = `\n## v${versionChangelog.version} - _${date}_\n\n`; + return title; + }, + getChangelogMdChange(change: Change): string { + let line = ` * ${change.note}`; + if (!_.isUndefined(change.pr)) { + line += ` (#${change.pr})`; + } + return line; + }, + generateChangelogMd(changelog: Changelog): string { + let changelogMd = CHANGELOG_MD_HEADER; + _.each(changelog, versionChangelog => { + const title = changelogUtils.getChangelogMdTitle(versionChangelog); + changelogMd += title; + const changelogVersionLines = _.map( + versionChangelog.changes, + changelogUtils.getChangelogMdChange.bind(changelogUtils), + ); + changelogMd += `${_.join(changelogVersionLines, '\n')}`; + }); + + return changelogMd; + }, + shouldAddNewChangelogEntry(currentVersion: string, changelog: Changelog): boolean { + if (_.isEmpty(changelog)) { + return true; + } + const lastEntry = changelog[0]; + const isLastEntryCurrentVersion = lastEntry.version === currentVersion; + return isLastEntryCurrentVersion; + }, +}; diff --git a/packages/monorepo-scripts/src/utils/utils.ts b/packages/monorepo-scripts/src/utils/utils.ts new file mode 100644 index 000000000..8f2a0bbaa --- /dev/null +++ b/packages/monorepo-scripts/src/utils/utils.ts @@ -0,0 +1,64 @@ +import * as fs from 'fs'; +import lernaGetPackages = require('lerna-get-packages'); +import * as _ from 'lodash'; +import { exec as execAsync, spawn } from 'promisify-child-process'; + +import { constants } from '../constants'; +import { UpdatedPackage } from '../types'; + +export const utils = { + log(...args: any[]): void { + console.log(...args); // tslint:disable-line:no-console + }, + getNextPatchVersion(currentVersion: string): string { + const versionSegments = currentVersion.split('.'); + const patch = _.parseInt(_.last(versionSegments) as string); + const newPatch = patch + 1; + const newPatchVersion = `${versionSegments[0]}.${versionSegments[1]}.${newPatch}`; + return newPatchVersion; + }, + async prettifyAsync(filePath: string, cwd: string): Promise { + await execAsync(`prettier --write ${filePath} --config .prettierrc`, { + cwd, + }); + }, + async getUpdatedLernaPackagesAsync(shouldIncludePrivate: boolean): Promise { + const updatedPublicPackages = await this.getLernaUpdatedPackagesAsync(shouldIncludePrivate); + const updatedPackageNames = _.map(updatedPublicPackages, pkg => pkg.name); + + const allLernaPackages = lernaGetPackages(constants.monorepoRootPath); + const updatedPublicLernaPackages = _.filter(allLernaPackages, pkg => { + return _.includes(updatedPackageNames, pkg.package.name); + }); + return updatedPublicLernaPackages; + }, + async getLernaUpdatedPackagesAsync(shouldIncludePrivate: boolean): Promise { + const result = await execAsync(`${constants.lernaExecutable} updated --json`, { + cwd: constants.monorepoRootPath, + }); + const updatedPackages = JSON.parse(result.stdout); + if (!shouldIncludePrivate) { + const updatedPublicPackages = _.filter(updatedPackages, updatedPackage => !updatedPackage.private); + return updatedPublicPackages; + } + return updatedPackages; + }, + getChangelogJSONIfExists(changelogPath: string): string | undefined { + try { + const changelogJSON = fs.readFileSync(changelogPath, 'utf-8'); + return changelogJSON; + } catch (err) { + return undefined; + } + }, + getChangelogJSONOrCreateIfMissing(changelogPath: string): string { + const changelogIfExists = this.getChangelogJSONIfExists(changelogPath); + if (_.isUndefined(changelogIfExists)) { + // If none exists, create new, empty one. + const emptyChangelogJSON = JSON.stringify([]); + fs.writeFileSync(changelogPath, emptyChangelogJSON); + return emptyChangelogJSON; + } + return changelogIfExists; + }, +}; -- cgit v1.2.3