From 7dd3b2d38b4e4ee2f1daa24c90f503b2e7ad0422 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Thu, 19 Apr 2018 11:40:22 +0900 Subject: Add removeGitTags script that can be run after a failed Lerna publish --- packages/monorepo-scripts/src/constants.ts | 1 + packages/monorepo-scripts/src/remove_tags.ts | 56 ++++++++++++++++++++++++++++ packages/monorepo-scripts/src/utils.ts | 45 ++++++++++++++++++++++ 3 files changed, 102 insertions(+) create mode 100644 packages/monorepo-scripts/src/remove_tags.ts (limited to 'packages/monorepo-scripts/src') diff --git a/packages/monorepo-scripts/src/constants.ts b/packages/monorepo-scripts/src/constants.ts index 081a49332..e93b27820 100644 --- a/packages/monorepo-scripts/src/constants.ts +++ b/packages/monorepo-scripts/src/constants.ts @@ -3,4 +3,5 @@ import * as path from 'path'; export const constants = { monorepoRootPath: path.join(__dirname, '../../..'), stagingWebsite: 'http://staging-0xproject.s3-website-us-east-1.amazonaws.com', + lernaExecutable: './node_modules/lerna/bin/lerna.js', }; diff --git a/packages/monorepo-scripts/src/remove_tags.ts b/packages/monorepo-scripts/src/remove_tags.ts new file mode 100644 index 000000000..a91c6ec39 --- /dev/null +++ b/packages/monorepo-scripts/src/remove_tags.ts @@ -0,0 +1,56 @@ +#!/usr/bin/env node + +import lernaGetPackages = require('lerna-get-packages'); +import * as _ from 'lodash'; +import * as path from 'path'; +import { exec as execAsync } from 'promisify-child-process'; +import semverSort = require('semver-sort'); + +import { constants } from './constants'; +import { Changelog } from './types'; +import { utils } from './utils'; + +(async () => { + const shouldIncludePrivate = true; + const updatedPublicLernaPackages = await utils.getUpdatedLernaPackagesAsync(shouldIncludePrivate); + + for (const lernaPackage of updatedPublicLernaPackages) { + const packageName = lernaPackage.package.name; + const currentVersion = lernaPackage.package.version; + const changelogJSONPath = path.join(lernaPackage.location, 'CHANGELOG.json'); + const changelogJSONIfExists = utils.getChangelogJSONIfExists(changelogJSONPath); + + let latestChangelogVersion: string; + if (!_.isUndefined(changelogJSONIfExists)) { + let changelogs: Changelog[]; + try { + changelogs = JSON.parse(changelogJSONIfExists); + } catch (err) { + throw new Error( + `${lernaPackage.package.name}'s CHANGELOG.json contains invalid JSON. Please fix and try again.`, + ); + } + latestChangelogVersion = changelogs[0].version; + } else { + latestChangelogVersion = utils.getNextPatchVersion(currentVersion); + } + + const sortedVersions = semverSort.desc([latestChangelogVersion, currentVersion]); + if (sortedVersions[0] === latestChangelogVersion && latestChangelogVersion !== currentVersion) { + const tagName = `${packageName}@${latestChangelogVersion}`; + try { + await execAsync(`git tag -d ${tagName}`, { cwd: constants.monorepoRootPath }); + utils.log(`removed tag: ${tagName}`); + } catch (err) { + if (_.includes(err.message, 'not found')) { + utils.log(`Could not find tag: ${tagName}`); + } else { + throw err; + } + } + } + } +})().catch(err => { + utils.log(err); + process.exit(1); +}); diff --git a/packages/monorepo-scripts/src/utils.ts b/packages/monorepo-scripts/src/utils.ts index 9aa37e272..3a16bf91d 100644 --- a/packages/monorepo-scripts/src/utils.ts +++ b/packages/monorepo-scripts/src/utils.ts @@ -1,6 +1,11 @@ +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 @@ -17,4 +22,44 @@ export const utils = { 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) { + let changelogJSON: string; + try { + 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([], null, 4); + fs.writeFileSync(changelogPath, emptyChangelogJSON); + return emptyChangelogJSON; + } + return changelogIfExists; + }, }; -- cgit v1.2.3 From fc2b7f747bf15b8c9329f2940ed2f6cf71353c33 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Thu, 19 Apr 2018 14:10:48 +0900 Subject: Add checks for the required local setup before running the publish script --- packages/monorepo-scripts/src/publish.ts | 50 ++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) (limited to 'packages/monorepo-scripts/src') diff --git a/packages/monorepo-scripts/src/publish.ts b/packages/monorepo-scripts/src/publish.ts index a2d641ff9..9bd9ff6be 100644 --- a/packages/monorepo-scripts/src/publish.ts +++ b/packages/monorepo-scripts/src/publish.ts @@ -38,6 +38,11 @@ const packageNameToWebsitePath: { [name: string]: string } = { }; (async () => { + const hasRequiredSetup = await checkPublishRequiredSetupAsync(); + if (!hasRequiredSetup) { + return; // abort + } + // Fetch public, updated Lerna packages const updatedPublicLernaPackages = await getUpdatedPublicLernaPackagesAsync(); @@ -107,6 +112,51 @@ package.ts. Please add an entry for it and try again.`, } } +async function checkPublishRequiredSetupAsync(): Promise { + // check to see if logged into npm before publishing (npm whoami) + try { + 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.'); + return false; + } + + // Check to see if Git creds setup (check for ENV 'GITHUB_PERSONAL_ACCESS_TOKEN_0X_JS') + if (_.isUndefined(process.env.GITHUB_PERSONAL_ACCESS_TOKEN_0X_JS)) { + utils.log( + 'You must have a Github personal access token set to an envVar named `GITHUB_PERSONAL_ACCESS_TOKEN_0X_JS`. Add it then try again.', + ); + return false; + } + // Check NPM version is 5.X + const result = await execAsync(`npm --version`); + const version = result.stdout; + const versionSegments = version.split('.'); + const majorVersion = _.parseInt(versionSegments[0]); + if (majorVersion < 5) { + utils.log('You npm version must be v5.x or higher. Upgrade your npm and try again.'); + return false; + } + + // Check that `aws` commandline is installed + try { + await execAsync(`aws help`); + } catch (err) { + utils.log('You must have `awscli` commandline tool installed. Install it and try again.'); + return false; + } + + // Check that `aws` creds are setup + try { + await execAsync(`aws sts get-caller-identity`); + } catch (err) { + utils.log('You must setup your AWS credentials by running `aws configure`. Do this and try again.'); + return false; + } + + return true; +} + async function pushChangelogsToGithubAsync() { await execAsync(`git add . --all`, { cwd: constants.monorepoRootPath }); await execAsync(`git commit -m "Updated CHANGELOGS"`, { cwd: constants.monorepoRootPath }); -- cgit v1.2.3 From 858d1768cecf165adefcdf860f247dad258d8cae Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Thu, 19 Apr 2018 14:14:10 +0900 Subject: Improve comments --- packages/monorepo-scripts/src/publish.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'packages/monorepo-scripts/src') diff --git a/packages/monorepo-scripts/src/publish.ts b/packages/monorepo-scripts/src/publish.ts index 9bd9ff6be..827d6165e 100644 --- a/packages/monorepo-scripts/src/publish.ts +++ b/packages/monorepo-scripts/src/publish.ts @@ -113,7 +113,7 @@ package.ts. Please add an entry for it and try again.`, } async function checkPublishRequiredSetupAsync(): Promise { - // check to see if logged into npm before publishing (npm whoami) + // check to see if logged into npm before publishing try { await execAsync(`sudo npm whoami`); } catch (err) { @@ -121,13 +121,14 @@ async function checkPublishRequiredSetupAsync(): Promise { return false; } - // Check to see if Git creds setup (check for ENV 'GITHUB_PERSONAL_ACCESS_TOKEN_0X_JS') + // Check to see if Git personal token setup if (_.isUndefined(process.env.GITHUB_PERSONAL_ACCESS_TOKEN_0X_JS)) { utils.log( 'You must have a Github personal access token set to an envVar named `GITHUB_PERSONAL_ACCESS_TOKEN_0X_JS`. Add it then try again.', ); return false; } + // Check NPM version is 5.X const result = await execAsync(`npm --version`); const version = result.stdout; @@ -138,7 +139,7 @@ async function checkPublishRequiredSetupAsync(): Promise { return false; } - // Check that `aws` commandline is installed + // Check that `aws` commandline tool is installed try { await execAsync(`aws help`); } catch (err) { @@ -146,7 +147,7 @@ async function checkPublishRequiredSetupAsync(): Promise { return false; } - // Check that `aws` creds are setup + // Check that `aws` credentials are setup try { await execAsync(`aws sts get-caller-identity`); } catch (err) { -- cgit v1.2.3 From 60d879e04537f2af4d7476d384fd0e2002a0fbd3 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Thu, 19 Apr 2018 14:16:11 +0900 Subject: Add comment --- packages/monorepo-scripts/src/remove_tags.ts | 2 ++ 1 file changed, 2 insertions(+) (limited to 'packages/monorepo-scripts/src') diff --git a/packages/monorepo-scripts/src/remove_tags.ts b/packages/monorepo-scripts/src/remove_tags.ts index a91c6ec39..6d09729c7 100644 --- a/packages/monorepo-scripts/src/remove_tags.ts +++ b/packages/monorepo-scripts/src/remove_tags.ts @@ -18,6 +18,8 @@ import { utils } from './utils'; const packageName = lernaPackage.package.name; const currentVersion = lernaPackage.package.version; const changelogJSONPath = path.join(lernaPackage.location, 'CHANGELOG.json'); + // Private packages don't have changelogs, and their versions are always incremented + // by a patch version. const changelogJSONIfExists = utils.getChangelogJSONIfExists(changelogJSONPath); let latestChangelogVersion: string; -- cgit v1.2.3 From 2b15c03b9ac516f7e38eef174503096026162b90 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Thu, 19 Apr 2018 14:19:34 +0900 Subject: Use methods moved to utils since now shared by multiple scripts --- packages/monorepo-scripts/src/publish.ts | 36 +++----------------------------- 1 file changed, 3 insertions(+), 33 deletions(-) (limited to 'packages/monorepo-scripts/src') diff --git a/packages/monorepo-scripts/src/publish.ts b/packages/monorepo-scripts/src/publish.ts index 827d6165e..2ae9bbb60 100644 --- a/packages/monorepo-scripts/src/publish.ts +++ b/packages/monorepo-scripts/src/publish.ts @@ -44,7 +44,8 @@ const packageNameToWebsitePath: { [name: string]: string } = { } // Fetch public, updated Lerna packages - const updatedPublicLernaPackages = await getUpdatedPublicLernaPackagesAsync(); + const shouldIncludePrivate = false; + const updatedPublicLernaPackages = await utils.getUpdatedLernaPackagesAsync(shouldIncludePrivate); await confirmDocPagesRenderAsync(updatedPublicLernaPackages); @@ -165,23 +166,12 @@ async function pushChangelogsToGithubAsync() { utils.log(`Pushed CHANGELOG updates to Github`); } -async function getUpdatedPublicLernaPackagesAsync(): Promise { - const updatedPublicPackages = await getPublicLernaUpdatedPackagesAsync(); - 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 function updateChangeLogsAsync(updatedPublicLernaPackages: LernaPackage[]): Promise { const packageToVersionChange: PackageToVersionChange = {}; for (const lernaPackage of updatedPublicLernaPackages) { const packageName = lernaPackage.package.name; const changelogJSONPath = path.join(lernaPackage.location, 'CHANGELOG.json'); - const changelogJSON = getChangelogJSONOrCreateIfMissing(lernaPackage.package.name, changelogJSONPath); + const changelogJSON = utils.getChangelogJSONOrCreateIfMissing(changelogJSONPath); let changelogs: Changelog[]; try { changelogs = JSON.parse(changelogJSON); @@ -276,13 +266,6 @@ async function lernaPublishAsync(packageToVersionChange: { [name: string]: strin }); } -async function getPublicLernaUpdatedPackagesAsync(): Promise { - const result = await execAsync(`${LERNA_EXECUTABLE} updated --json`, { cwd: constants.monorepoRootPath }); - const updatedPackages = JSON.parse(result.stdout); - const updatedPublicPackages = _.filter(updatedPackages, updatedPackage => !updatedPackage.private); - return updatedPublicPackages; -} - function updateVersionNumberIfNeeded(currentVersion: string, proposedNextVersion: string) { if (proposedNextVersion === currentVersion) { return utils.getNextPatchVersion(currentVersion); @@ -294,19 +277,6 @@ function updateVersionNumberIfNeeded(currentVersion: string, proposedNextVersion return proposedNextVersion; } -function getChangelogJSONOrCreateIfMissing(packageName: string, changelogPath: string): string { - let changelogJSON: string; - try { - changelogJSON = fs.readFileSync(changelogPath, 'utf-8'); - return changelogJSON; - } catch (err) { - // If none exists, create new, empty one. - const emptyChangelogJSON = JSON.stringify([], null, 4); - fs.writeFileSync(changelogPath, emptyChangelogJSON); - return emptyChangelogJSON; - } -} - function shouldAddNewChangelogEntry(currentVersion: string, changelogs: Changelog[]): boolean { if (_.isEmpty(changelogs)) { return true; -- cgit v1.2.3 From 417cec9e04af84935c38ed77cdb53a8026b0004e Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Fri, 20 Apr 2018 09:36:03 +0900 Subject: Consolidate github personal access token env to one place: constants.ts --- packages/monorepo-scripts/src/constants.ts | 1 + packages/monorepo-scripts/src/postpublish_utils.ts | 3 +-- packages/monorepo-scripts/src/publish.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'packages/monorepo-scripts/src') diff --git a/packages/monorepo-scripts/src/constants.ts b/packages/monorepo-scripts/src/constants.ts index e93b27820..154a86069 100644 --- a/packages/monorepo-scripts/src/constants.ts +++ b/packages/monorepo-scripts/src/constants.ts @@ -4,4 +4,5 @@ export const constants = { monorepoRootPath: path.join(__dirname, '../../..'), stagingWebsite: 'http://staging-0xproject.s3-website-us-east-1.amazonaws.com', lernaExecutable: './node_modules/lerna/bin/lerna.js', + githubPersonalAccessToken: process.env.GITHUB_PERSONAL_ACCESS_TOKEN_0X_JS, }; diff --git a/packages/monorepo-scripts/src/postpublish_utils.ts b/packages/monorepo-scripts/src/postpublish_utils.ts index ca4c92f5d..df2bcb128 100644 --- a/packages/monorepo-scripts/src/postpublish_utils.ts +++ b/packages/monorepo-scripts/src/postpublish_utils.ts @@ -10,7 +10,6 @@ import { constants } from './constants'; import { utils } from './utils'; const publishReleaseAsync = promisify(publishRelease); -const githubPersonalAccessToken = process.env.GITHUB_PERSONAL_ACCESS_TOKEN_0X_JS; const generatedDocsDirectoryName = 'generated_docs'; export interface PostpublishConfigs { @@ -97,7 +96,7 @@ export const postpublishUtils = { const finalAssets = this.adjustAssetPaths(cwd, assets); utils.log('POSTPUBLISH: Releasing ', releaseName, '...'); const result = await publishReleaseAsync({ - token: githubPersonalAccessToken, + token: constants.githubPersonalAccessToken, owner: '0xProject', repo: '0x-monorepo', tag, diff --git a/packages/monorepo-scripts/src/publish.ts b/packages/monorepo-scripts/src/publish.ts index 2ae9bbb60..6c728467a 100644 --- a/packages/monorepo-scripts/src/publish.ts +++ b/packages/monorepo-scripts/src/publish.ts @@ -123,7 +123,7 @@ async function checkPublishRequiredSetupAsync(): Promise { } // Check to see if Git personal token setup - if (_.isUndefined(process.env.GITHUB_PERSONAL_ACCESS_TOKEN_0X_JS)) { + if (_.isUndefined(constants.githubPersonalAccessToken)) { utils.log( 'You must have a Github personal access token set to an envVar named `GITHUB_PERSONAL_ACCESS_TOKEN_0X_JS`. Add it then try again.', ); -- cgit v1.2.3 From 7f46e9af2cef99667da798205bd0dd6fa590f897 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Fri, 20 Apr 2018 10:00:37 +0900 Subject: Use path for platform independence --- packages/monorepo-scripts/src/constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/monorepo-scripts/src') diff --git a/packages/monorepo-scripts/src/constants.ts b/packages/monorepo-scripts/src/constants.ts index 154a86069..3aaf881cb 100644 --- a/packages/monorepo-scripts/src/constants.ts +++ b/packages/monorepo-scripts/src/constants.ts @@ -3,6 +3,6 @@ import * as path from 'path'; export const constants = { monorepoRootPath: path.join(__dirname, '../../..'), stagingWebsite: 'http://staging-0xproject.s3-website-us-east-1.amazonaws.com', - lernaExecutable: './node_modules/lerna/bin/lerna.js', + lernaExecutable: path.join('node_modules', 'lerna', 'bin', 'lerna.js'), githubPersonalAccessToken: process.env.GITHUB_PERSONAL_ACCESS_TOKEN_0X_JS, }; -- cgit v1.2.3 From b6fb8dbb528a28cfc3d9395262fa39e3eecca2d5 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Fri, 20 Apr 2018 10:03:53 +0900 Subject: Remove outside declaration --- packages/monorepo-scripts/src/utils.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'packages/monorepo-scripts/src') diff --git a/packages/monorepo-scripts/src/utils.ts b/packages/monorepo-scripts/src/utils.ts index 3a16bf91d..b1bb78ab2 100644 --- a/packages/monorepo-scripts/src/utils.ts +++ b/packages/monorepo-scripts/src/utils.ts @@ -44,9 +44,8 @@ export const utils = { return updatedPackages; }, getChangelogJSONIfExists(changelogPath: string) { - let changelogJSON: string; try { - changelogJSON = fs.readFileSync(changelogPath, 'utf-8'); + const changelogJSON = fs.readFileSync(changelogPath, 'utf-8'); return changelogJSON; } catch (err) { return undefined; -- cgit v1.2.3 From 1f82c7eadfe9e9cddb56b40562a2f0f69cc7dfd9 Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Fri, 20 Apr 2018 10:04:22 +0900 Subject: Remove unnecessary additional params --- packages/monorepo-scripts/src/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'packages/monorepo-scripts/src') diff --git a/packages/monorepo-scripts/src/utils.ts b/packages/monorepo-scripts/src/utils.ts index b1bb78ab2..4412f753a 100644 --- a/packages/monorepo-scripts/src/utils.ts +++ b/packages/monorepo-scripts/src/utils.ts @@ -55,7 +55,7 @@ export const utils = { const changelogIfExists = this.getChangelogJSONIfExists(changelogPath); if (_.isUndefined(changelogIfExists)) { // If none exists, create new, empty one. - const emptyChangelogJSON = JSON.stringify([], null, 4); + const emptyChangelogJSON = JSON.stringify([]); fs.writeFileSync(changelogPath, emptyChangelogJSON); return emptyChangelogJSON; } -- cgit v1.2.3 From a3cc5c1dd7e88aea50859560f7d751435cb577bd Mon Sep 17 00:00:00 2001 From: Fabio Berger Date: Tue, 24 Apr 2018 10:32:44 +0900 Subject: Add hack comment about the use of sudo --- packages/monorepo-scripts/src/publish.ts | 2 ++ 1 file changed, 2 insertions(+) (limited to 'packages/monorepo-scripts/src') diff --git a/packages/monorepo-scripts/src/publish.ts b/packages/monorepo-scripts/src/publish.ts index 6c728467a..0ca3ea7a1 100644 --- a/packages/monorepo-scripts/src/publish.ts +++ b/packages/monorepo-scripts/src/publish.ts @@ -116,6 +116,8 @@ package.ts. Please add an entry for it and try again.`, 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). 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.'); -- cgit v1.2.3