aboutsummaryrefslogtreecommitdiffstats
path: root/packages/monorepo-scripts/src
diff options
context:
space:
mode:
authorFabio Berger <me@fabioberger.com>2018-06-19 05:33:17 +0800
committerGitHub <noreply@github.com>2018-06-19 05:33:17 +0800
commit8c83f4ba3b72660c1a7a0b0829e8800a2a8687fe (patch)
treec73dc3abd7e94510b8f2d6476cff53e021746a4f /packages/monorepo-scripts/src
parent3f02631b98368503718a1c1977e7f83220c284a2 (diff)
parent25b6d1a232835b76decbb1c66878da1285afe404 (diff)
downloaddexon-sol-tools-8c83f4ba3b72660c1a7a0b0829e8800a2a8687fe.tar.gz
dexon-sol-tools-8c83f4ba3b72660c1a7a0b0829e8800a2a8687fe.tar.zst
dexon-sol-tools-8c83f4ba3b72660c1a7a0b0829e8800a2a8687fe.zip
Merge pull request #722 from 0xProject/improvement/publishing-v2
Improvements to pre-publishing checks
Diffstat (limited to 'packages/monorepo-scripts/src')
-rw-r--r--packages/monorepo-scripts/src/prepublish_checks.ts121
-rw-r--r--packages/monorepo-scripts/src/publish.ts46
-rw-r--r--packages/monorepo-scripts/src/remove_tags.ts57
-rw-r--r--packages/monorepo-scripts/src/types.ts13
-rw-r--r--packages/monorepo-scripts/src/utils/changelog_utils.ts53
-rw-r--r--packages/monorepo-scripts/src/utils/npm_utils.ts28
-rw-r--r--packages/monorepo-scripts/src/utils/utils.ts110
7 files changed, 317 insertions, 111 deletions
diff --git a/packages/monorepo-scripts/src/prepublish_checks.ts b/packages/monorepo-scripts/src/prepublish_checks.ts
index 2c096d8f6..1c4ee1fc6 100644
--- a/packages/monorepo-scripts/src/prepublish_checks.ts
+++ b/packages/monorepo-scripts/src/prepublish_checks.ts
@@ -1,9 +1,128 @@
import * as _ from 'lodash';
import { exec as execAsync } from 'promisify-child-process';
+import semver = require('semver');
+import semverSort = require('semver-sort');
import { constants } from './constants';
+import { changelogUtils } from './utils/changelog_utils';
+import { npmUtils } from './utils/npm_utils';
import { utils } from './utils/utils';
+async function prepublishChecksAsync(): Promise<void> {
+ const shouldIncludePrivate = false;
+ const updatedPublicLernaPackages = await utils.getUpdatedLernaPackagesAsync(shouldIncludePrivate);
+
+ await checkCurrentVersionMatchesLatestPublishedNPMPackageAsync(updatedPublicLernaPackages);
+ await checkChangelogFormatAsync(updatedPublicLernaPackages);
+ await checkGitTagsForNextVersionAndDeleteIfExistAsync(updatedPublicLernaPackages);
+ await checkPublishRequiredSetupAsync();
+}
+
+async function checkGitTagsForNextVersionAndDeleteIfExistAsync(
+ updatedPublicLernaPackages: LernaPackage[],
+): Promise<void> {
+ const packageNames = _.map(updatedPublicLernaPackages, lernaPackage => lernaPackage.package.name);
+ const localGitTags = await utils.getLocalGitTagsAsync();
+ const localTagVersionsByPackageName = await utils.getGitTagsByPackageNameAsync(packageNames, localGitTags);
+
+ const remoteGitTags = await utils.getRemoteGitTagsAsync();
+ const remoteTagVersionsByPackageName = await utils.getGitTagsByPackageNameAsync(packageNames, remoteGitTags);
+
+ for (const lernaPackage of updatedPublicLernaPackages) {
+ const currentVersion = lernaPackage.package.version;
+ const packageName = lernaPackage.package.name;
+ const packageLocation = lernaPackage.location;
+ const nextVersion = await utils.getNextPackageVersionAsync(currentVersion, packageName, packageLocation);
+
+ const localTagVersions = localTagVersionsByPackageName[packageName];
+ if (_.includes(localTagVersions, nextVersion)) {
+ const tagName = `${packageName}@${nextVersion}`;
+ await utils.removeLocalTagAsync(tagName);
+ }
+
+ const remoteTagVersions = remoteTagVersionsByPackageName[packageName];
+ if (_.includes(remoteTagVersions, nextVersion)) {
+ const tagName = `:refs/tags/${packageName}@${nextVersion}`;
+ await utils.removeRemoteTagAsync(tagName);
+ }
+ }
+}
+
+async function checkCurrentVersionMatchesLatestPublishedNPMPackageAsync(
+ updatedPublicLernaPackages: LernaPackage[],
+): Promise<void> {
+ utils.log('Check package versions against npmjs.org...');
+ const versionMismatches = [];
+ for (const lernaPackage of updatedPublicLernaPackages) {
+ const packageName = lernaPackage.package.name;
+ const packageVersion = lernaPackage.package.version;
+ const packageRegistryJsonIfExists = await npmUtils.getPackageRegistryJsonIfExistsAsync(packageName);
+ if (_.isUndefined(packageRegistryJsonIfExists)) {
+ continue; // noop for packages not yet published to NPM
+ }
+ const allVersionsIncludingUnpublished = npmUtils.getPreviouslyPublishedVersions(packageRegistryJsonIfExists);
+ const sortedVersions = semverSort.desc(allVersionsIncludingUnpublished);
+ const latestNPMVersion = sortedVersions[0];
+ if (packageVersion !== latestNPMVersion) {
+ versionMismatches.push({
+ packageJsonVersion: packageVersion,
+ npmVersion: latestNPMVersion,
+ packageName,
+ });
+ }
+ }
+ if (!_.isEmpty(versionMismatches)) {
+ utils.log(`Found version mismatches between package.json and NPM published versions (might be unpublished).`);
+ _.each(versionMismatches, versionMismatch => {
+ utils.log(
+ `${versionMismatch.packageName}: ${versionMismatch.packageJsonVersion} package.json, ${
+ versionMismatch.npmVersion
+ } on NPM`,
+ );
+ });
+ throw new Error(`Please fix the above package.json/NPM inconsistencies.`);
+ }
+}
+
+async function checkChangelogFormatAsync(updatedPublicLernaPackages: LernaPackage[]): Promise<void> {
+ utils.log('Check CHANGELOGs for inconsistencies...');
+ const changeLogInconsistencies = [];
+ for (const lernaPackage of updatedPublicLernaPackages) {
+ const packageName = lernaPackage.package.name;
+ const changelog = changelogUtils.getChangelogOrCreateIfMissing(packageName, lernaPackage.location);
+
+ const currentVersion = lernaPackage.package.version;
+ if (!_.isEmpty(changelog)) {
+ const lastEntry = changelog[0];
+ const doesLastEntryHaveTimestamp = !_.isUndefined(lastEntry.timestamp);
+ if (semver.lt(lastEntry.version, currentVersion)) {
+ changeLogInconsistencies.push({
+ packageJsonVersion: currentVersion,
+ changelogVersion: lastEntry.version,
+ packageName,
+ });
+ } else if (semver.gt(lastEntry.version, currentVersion) && doesLastEntryHaveTimestamp) {
+ // Remove incorrectly added timestamp
+ delete changelog[0].timestamp;
+ // Save updated CHANGELOG.json
+ await changelogUtils.writeChangelogJsonFileAsync(lernaPackage.location, changelog);
+ utils.log(`${packageName}: Removed timestamp from latest CHANGELOG.json entry.`);
+ }
+ }
+ }
+ if (!_.isEmpty(changeLogInconsistencies)) {
+ utils.log(`CHANGELOG versions cannot below package.json versions:`);
+ _.each(changeLogInconsistencies, inconsistency => {
+ utils.log(
+ `${inconsistency.packageName}: ${inconsistency.packageJsonVersion} package.json, ${
+ inconsistency.changelogVersion
+ } CHANGELOG.json`,
+ );
+ });
+ throw new Error('Fix the above inconsistencies to continue.');
+ }
+}
+
async function checkPublishRequiredSetupAsync(): Promise<void> {
// check to see if logged into npm before publishing
try {
@@ -65,7 +184,7 @@ async function checkPublishRequiredSetupAsync(): Promise<void> {
}
}
-checkPublishRequiredSetupAsync().catch(err => {
+prepublishChecksAsync().catch(err => {
utils.log(err.message);
process.exit(1);
});
diff --git a/packages/monorepo-scripts/src/publish.ts b/packages/monorepo-scripts/src/publish.ts
index 2efbc8bf2..72c6c0a71 100644
--- a/packages/monorepo-scripts/src/publish.ts
+++ b/packages/monorepo-scripts/src/publish.ts
@@ -1,18 +1,17 @@
#!/usr/bin/env node
import * as promisify from 'es6-promisify';
-import * as fs from 'fs';
import * as _ from 'lodash';
import * as moment from 'moment';
import opn = require('opn');
-import * as path from 'path';
import { exec as execAsync, spawn } from 'promisify-child-process';
import * as prompt from 'prompt';
+import semver = require('semver');
import semverDiff = require('semver-diff');
import semverSort = require('semver-sort');
import { constants } from './constants';
-import { Changelog, PackageToVersionChange, SemVerIndex, VersionChangelog } from './types';
+import { PackageToVersionChange, SemVerIndex, VersionChangelog } from './types';
import { changelogUtils } from './utils/changelog_utils';
import { utils } from './utils/utils';
@@ -119,25 +118,23 @@ async function updateChangeLogsAsync(updatedPublicLernaPackages: LernaPackage[])
const packageToVersionChange: PackageToVersionChange = {};
for (const lernaPackage of updatedPublicLernaPackages) {
const packageName = lernaPackage.package.name;
- const changelogJSONPath = path.join(lernaPackage.location, 'CHANGELOG.json');
- const changelogJSON = utils.getChangelogJSONOrCreateIfMissing(changelogJSONPath);
- let changelog: Changelog;
- try {
- changelog = JSON.parse(changelogJSON);
- } catch (err) {
- throw new Error(
- `${lernaPackage.package.name}'s CHANGELOG.json contains invalid JSON. Please fix and try again.`,
- );
- }
+ let changelog = changelogUtils.getChangelogOrCreateIfMissing(packageName, lernaPackage.location);
const currentVersion = lernaPackage.package.version;
- const shouldAddNewEntry = changelogUtils.shouldAddNewChangelogEntry(currentVersion, changelog);
+ const shouldAddNewEntry = changelogUtils.shouldAddNewChangelogEntry(
+ lernaPackage.package.name,
+ currentVersion,
+ changelog,
+ );
if (shouldAddNewEntry) {
// Create a new entry for a patch version with generic changelog entry.
- const nextPatchVersion = utils.getNextPatchVersion(currentVersion);
+ const nextPatchVersionIfValid = semver.inc(currentVersion, 'patch');
+ if (_.isNull(nextPatchVersionIfValid)) {
+ throw new Error(`Encountered invalid semver version: ${currentVersion} for package: ${packageName}`);
+ }
const newChangelogEntry: VersionChangelog = {
timestamp: TODAYS_TIMESTAMP,
- version: nextPatchVersion,
+ version: nextPatchVersionIfValid,
changes: [
{
note: 'Dependencies updated',
@@ -145,7 +142,7 @@ async function updateChangeLogsAsync(updatedPublicLernaPackages: LernaPackage[])
],
};
changelog = [newChangelogEntry, ...changelog];
- packageToVersionChange[packageName] = semverDiff(currentVersion, nextPatchVersion);
+ packageToVersionChange[packageName] = semverDiff(currentVersion, nextPatchVersionIfValid);
} else {
// Update existing entry with timestamp
const lastEntry = changelog[0];
@@ -160,14 +157,11 @@ async function updateChangeLogsAsync(updatedPublicLernaPackages: LernaPackage[])
}
// Save updated CHANGELOG.json
- fs.writeFileSync(changelogJSONPath, JSON.stringify(changelog, null, '\t'));
- await utils.prettifyAsync(changelogJSONPath, constants.monorepoRootPath);
+ await changelogUtils.writeChangelogJsonFileAsync(lernaPackage.location, changelog);
utils.log(`${packageName}: Updated CHANGELOG.json`);
// Generate updated CHANGELOG.md
const changelogMd = changelogUtils.generateChangelogMd(changelog);
- const changelogMdPath = path.join(lernaPackage.location, 'CHANGELOG.md');
- fs.writeFileSync(changelogMdPath, changelogMd);
- await utils.prettifyAsync(changelogMdPath, constants.monorepoRootPath);
+ await changelogUtils.writeChangelogMdFileAsync(lernaPackage.location, changelogMd);
utils.log(`${packageName}: Updated CHANGELOG.md`);
}
@@ -217,12 +211,16 @@ async function lernaPublishAsync(packageToVersionChange: { [name: string]: strin
}
function updateVersionNumberIfNeeded(currentVersion: string, proposedNextVersion: string): string {
+ const updatedVersionIfValid = semver.inc(currentVersion, 'patch');
+ if (_.isNull(updatedVersionIfValid)) {
+ throw new Error(`Encountered invalid semver: ${currentVersion}`);
+ }
if (proposedNextVersion === currentVersion) {
- return utils.getNextPatchVersion(currentVersion);
+ return updatedVersionIfValid;
}
const sortedVersions = semverSort.desc([proposedNextVersion, currentVersion]);
if (sortedVersions[0] !== proposedNextVersion) {
- return utils.getNextPatchVersion(currentVersion);
+ return updatedVersionIfValid;
}
return proposedNextVersion;
}
diff --git a/packages/monorepo-scripts/src/remove_tags.ts b/packages/monorepo-scripts/src/remove_tags.ts
deleted file mode 100644
index 50e413495..000000000
--- a/packages/monorepo-scripts/src/remove_tags.ts
+++ /dev/null
@@ -1,57 +0,0 @@
-#!/usr/bin/env node
-
-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/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');
- // Private packages don't have changelogs, and their versions are always incremented
- // by a patch version.
- 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/types.ts b/packages/monorepo-scripts/src/types.ts
index 36fb923b3..61bd75732 100644
--- a/packages/monorepo-scripts/src/types.ts
+++ b/packages/monorepo-scripts/src/types.ts
@@ -27,3 +27,16 @@ export enum SemVerIndex {
export interface PackageToVersionChange {
[name: string]: string;
}
+
+export interface PackageRegistryJson {
+ versions: {
+ [version: string]: any;
+ };
+ time: {
+ [version: string]: string;
+ };
+}
+
+export interface GitTagsByPackageName {
+ [packageName: string]: string[];
+}
diff --git a/packages/monorepo-scripts/src/utils/changelog_utils.ts b/packages/monorepo-scripts/src/utils/changelog_utils.ts
index edfe65a80..dbafb6f16 100644
--- a/packages/monorepo-scripts/src/utils/changelog_utils.ts
+++ b/packages/monorepo-scripts/src/utils/changelog_utils.ts
@@ -1,6 +1,11 @@
+import * as fs from 'fs';
import * as _ from 'lodash';
import * as moment from 'moment';
+import * as path from 'path';
+import { exec as execAsync } from 'promisify-child-process';
+import semver = require('semver');
+import { constants } from '../constants';
import { Change, Changelog, VersionChangelog } from '../types';
const CHANGELOG_MD_HEADER = `
@@ -44,12 +49,58 @@ export const changelogUtils = {
return changelogMd;
},
- shouldAddNewChangelogEntry(currentVersion: string, changelog: Changelog): boolean {
+ shouldAddNewChangelogEntry(packageName: string, currentVersion: string, changelog: Changelog): boolean {
if (_.isEmpty(changelog)) {
return true;
}
const lastEntry = changelog[0];
+ if (semver.lt(lastEntry.version, currentVersion)) {
+ throw new Error(
+ `Found CHANGELOG version lower then current package version. ${packageName} current: ${currentVersion}, Changelog: ${
+ lastEntry.version
+ }`,
+ );
+ }
const isLastEntryCurrentVersion = lastEntry.version === currentVersion;
return isLastEntryCurrentVersion;
},
+ getChangelogJSONIfExists(changelogPath: string): string | undefined {
+ try {
+ const changelogJSON = fs.readFileSync(changelogPath, 'utf-8');
+ return changelogJSON;
+ } catch (err) {
+ return undefined;
+ }
+ },
+ getChangelogOrCreateIfMissing(packageName: string, packageLocation: string): Changelog {
+ const changelogJSONPath = path.join(packageLocation, 'CHANGELOG.json');
+ let changelogJsonIfExists = this.getChangelogJSONIfExists(changelogJSONPath);
+ if (_.isUndefined(changelogJsonIfExists)) {
+ // If none exists, create new, empty one.
+ changelogJsonIfExists = '[]';
+ fs.writeFileSync(changelogJSONPath, changelogJsonIfExists);
+ }
+ let changelog: Changelog;
+ try {
+ changelog = JSON.parse(changelogJsonIfExists);
+ } catch (err) {
+ throw new Error(`${packageName}'s CHANGELOG.json contains invalid JSON. Please fix and try again.`);
+ }
+ return changelog;
+ },
+ async writeChangelogJsonFileAsync(packageLocation: string, changelog: Changelog): Promise<void> {
+ const changelogJSONPath = path.join(packageLocation, 'CHANGELOG.json');
+ fs.writeFileSync(changelogJSONPath, JSON.stringify(changelog, null, '\t'));
+ await this.prettifyAsync(changelogJSONPath, constants.monorepoRootPath);
+ },
+ async writeChangelogMdFileAsync(packageLocation: string, changelogMdString: string): Promise<void> {
+ const changelogMarkdownPath = path.join(packageLocation, 'CHANGELOG.md');
+ fs.writeFileSync(changelogMarkdownPath, changelogMdString);
+ await this.prettifyAsync(changelogMarkdownPath, constants.monorepoRootPath);
+ },
+ async prettifyAsync(filePath: string, cwd: string): Promise<void> {
+ await execAsync(`prettier --write ${filePath} --config .prettierrc`, {
+ cwd,
+ });
+ },
};
diff --git a/packages/monorepo-scripts/src/utils/npm_utils.ts b/packages/monorepo-scripts/src/utils/npm_utils.ts
new file mode 100644
index 000000000..cc1e046e7
--- /dev/null
+++ b/packages/monorepo-scripts/src/utils/npm_utils.ts
@@ -0,0 +1,28 @@
+import 'isomorphic-fetch';
+import * as _ from 'lodash';
+
+import { PackageRegistryJson } from '../types';
+
+const NPM_REGISTRY_BASE_URL = 'https://registry.npmjs.org';
+const SUCCESS_STATUS = 200;
+const NOT_FOUND_STATUS = 404;
+
+export const npmUtils = {
+ async getPackageRegistryJsonIfExistsAsync(packageName: string): Promise<PackageRegistryJson | undefined> {
+ const url = `${NPM_REGISTRY_BASE_URL}/${packageName}`;
+ const response = await fetch(url);
+
+ if (response.status === NOT_FOUND_STATUS) {
+ return undefined;
+ } else if (response.status !== SUCCESS_STATUS) {
+ throw new Error(`Request to ${url} failed. Check your internet connection and that npmjs.org is up.`);
+ }
+ const packageRegistryJson = await response.json();
+ return packageRegistryJson;
+ },
+ getPreviouslyPublishedVersions(packageRegistryJson: PackageRegistryJson): string[] {
+ const timeWithOnlyVersions = _.omit(packageRegistryJson.time, ['modified', 'created']);
+ const versions = _.keys(timeWithOnlyVersions);
+ return versions;
+ },
+};
diff --git a/packages/monorepo-scripts/src/utils/utils.ts b/packages/monorepo-scripts/src/utils/utils.ts
index 0b8ac4c0b..20bc57bae 100644
--- a/packages/monorepo-scripts/src/utils/utils.ts
+++ b/packages/monorepo-scripts/src/utils/utils.ts
@@ -1,27 +1,17 @@
-import * as fs from 'fs';
import lernaGetPackages = require('lerna-get-packages');
import * as _ from 'lodash';
import { exec as execAsync } from 'promisify-child-process';
+import semver = require('semver');
import { constants } from '../constants';
-import { UpdatedPackage } from '../types';
+import { GitTagsByPackageName, UpdatedPackage } from '../types';
+
+import { changelogUtils } from './changelog_utils';
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<void> {
- await execAsync(`prettier --write ${filePath} --config .prettierrc`, {
- cwd,
- });
- },
async getUpdatedLernaPackagesAsync(shouldIncludePrivate: boolean): Promise<LernaPackage[]> {
const updatedPublicPackages = await this.getLernaUpdatedPackagesAsync(shouldIncludePrivate);
const updatedPackageNames = _.map(updatedPublicPackages, pkg => pkg.name);
@@ -43,22 +33,86 @@ export const utils = {
}
return updatedPackages;
},
- getChangelogJSONIfExists(changelogPath: string): string | undefined {
- try {
- const changelogJSON = fs.readFileSync(changelogPath, 'utf-8');
- return changelogJSON;
- } catch (err) {
- return undefined;
+ async getNextPackageVersionAsync(
+ currentVersion: string,
+ packageName: string,
+ packageLocation: string,
+ ): Promise<string> {
+ let nextVersionIfValid;
+ const changelog = changelogUtils.getChangelogOrCreateIfMissing(packageName, packageLocation);
+ if (_.isEmpty(changelog)) {
+ nextVersionIfValid = semver.inc(currentVersion, 'patch');
+ }
+ const lastEntry = changelog[0];
+ nextVersionIfValid = semver.eq(lastEntry.version, currentVersion)
+ ? semver.inc(currentVersion, 'patch')
+ : lastEntry.version;
+ if (_.isNull(nextVersionIfValid)) {
+ throw new Error(`Encountered invalid semver: ${currentVersion} associated with ${packageName}`);
}
+ return nextVersionIfValid;
},
- 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;
+ async getRemoteGitTagsAsync(): Promise<string[]> {
+ const result = await execAsync(`git ls-remote --tags`, {
+ cwd: constants.monorepoRootPath,
+ });
+ const tagsString = result.stdout;
+ const tagOutputs: string[] = tagsString.split('\n');
+ const tags = _.compact(
+ _.map(tagOutputs, tagOutput => {
+ const tag = tagOutput.split('refs/tags/')[1];
+ // Tags with `^{}` are duplicateous so we ignore them
+ // Source: https://stackoverflow.com/questions/15472107/when-listing-git-ls-remote-why-theres-after-the-tag-name
+ if (_.endsWith(tag, '^{}')) {
+ return undefined;
+ }
+ return tag;
+ }),
+ );
+ return tags;
+ },
+ async getLocalGitTagsAsync(): Promise<string[]> {
+ const result = await execAsync(`git tags`, {
+ cwd: constants.monorepoRootPath,
+ });
+ const tagsString = result.stdout;
+ const tags = tagsString.split('\n');
+ return tags;
+ },
+ async getGitTagsByPackageNameAsync(packageNames: string[], gitTags: string[]): Promise<GitTagsByPackageName> {
+ const tagVersionByPackageName: GitTagsByPackageName = {};
+ _.each(gitTags, tag => {
+ const packageNameIfExists = _.find(packageNames, name => {
+ return _.includes(tag, `${name}@`);
+ });
+ if (_.isUndefined(packageNameIfExists)) {
+ return; // ignore tags not related to a package we care about.
+ }
+ const splitTag = tag.split(`${packageNameIfExists}@`);
+ if (splitTag.length !== 2) {
+ throw new Error(`Unexpected tag name found: ${tag}`);
+ }
+ const version = splitTag[1];
+ (tagVersionByPackageName[packageNameIfExists] || (tagVersionByPackageName[packageNameIfExists] = [])).push(
+ version,
+ );
+ });
+ return tagVersionByPackageName;
+ },
+ async removeLocalTagAsync(tagName: string): Promise<void> {
+ const result = await execAsync(`git tag -d ${tagName}`, {
+ cwd: constants.monorepoRootPath,
+ });
+ if (!_.isEmpty(result.stderr)) {
+ throw new Error(`Failed to delete local git tag. Got err: ${result.stderr}`);
+ }
+ },
+ async removeRemoteTagAsync(tagName: string): Promise<void> {
+ const result = await execAsync(`git push origin ${tagName}`, {
+ cwd: constants.monorepoRootPath,
+ });
+ if (!_.isEmpty(result.stderr)) {
+ throw new Error(`Failed to delete remote git tag. Got err: ${result.stderr}`);
}
- return changelogIfExists;
},
};