Skip to content

Commit

Permalink
chore(cdk-release): track and bump separate alpha version (#16043)
Browse files Browse the repository at this point in the history
As part of the project to release our alpha modules as independent modules, this
change extends our `cdk-release` tool to support tracking and bumping two
versions: the primary, stable version, and an additional, optional alpha
version.

If the local `version.vX.json` file has an `alphaVersion` property, this version
will be bumped alongside the main (stable) version. If the main version is also
a prerelease, then the alphaVersion is simply incremented (e.g., `2.0.0-alpha.0`
-> `2.0.0-alpha.1`); if the main version is stable, the alpha is incremented to
match (e.g., the `2.1.0` release will get an alpha of `2.1.0-alpha.0`). If no
`alphaVersion` is present, it is simply ignored.

This is still only part of the work to release the alpha modules. Remaining:
- Create the indepedent module CHANGELOGs, and aggregate into the main CHANGELOG
- Change the `align-versions` script to read and apply the alphaVersion if the
  module is an alpha module.
- Create the first alpha version in the v2-main branch.

*Implementation details:*

I was frustrated by the generic 'Updater' and 'UpdaterModule' pattern that I
needed to work around, so I decided to just simplify by removing the generic
capabilities that we're not using. This involved deleting a lot of code that
wasn't doing much. Lots of refactoring ensured. Similarly, there simply weren't
nearly enough tests in this module yet; I added at least a few more to improve
coverage in anticipation of the next tasks (which will require even more tests).
My apologies to the reviewer(s) for the muddled diff, but I strongly believe
these simplifications will make it easier to maintain `cdk-release` going
forward.

related #15591


----

*By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license*
  • Loading branch information
njlynch authored Aug 13, 2021
1 parent 9618fd4 commit 1b29ca8
Show file tree
Hide file tree
Showing 15 changed files with 294 additions and 293 deletions.
3 changes: 3 additions & 0 deletions scripts/bump.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ async function main() {
// this is incredible, but passing this option to standard-version actually makes it crash!
// good thing we're getting rid of it...
opts.verbose = !!process.env.VERBOSE;
// Rename some options to match cdk-release inputs (replaces bumpFiles, packageFiles, and infile)
opts.versionFile = ver.versionFile;
opts.changelogFile = ver.changelogFile;
console.error("🎉 Calling our 'cdk-release' package to make the bump");
console.error("ℹ️ Set the LEGACY_BUMP env variable to use the old 'standard-version' bump instead");
const cdkRelease = require('cdk-release');
Expand Down
2 changes: 1 addition & 1 deletion tools/cdk-release/lib/conventional-commits.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,10 +72,10 @@ export async function getConventionalCommitsFromGitHistory(args: ReleaseOptions,
const ret = new Array<any>();
return new Promise((resolve, reject) => {
const conventionalCommitsStream = gitRawCommits({
// Raw body (subject + body) + '\n-hash-\n' + commit hash
format: '%B%n-hash-%n%H',
// our tags have the 'v' prefix
from: gitTag,
// path: options.path,
}).pipe(conventionalCommitsParser());

conventionalCommitsStream.on('data', function (data: any) {
Expand Down
19 changes: 1 addition & 18 deletions tools/cdk-release/lib/defaults.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,14 @@
import { ReleaseOptions } from './types';

const defaultPackageFiles = [
'package.json',
'bower.json',
'manifest.json',
];

export const defaultBumpFiles = defaultPackageFiles.concat([
'package-lock.json',
'npm-shrinkwrap.json',
]);

export const defaults: Partial<ReleaseOptions> = {
infile: 'CHANGELOG.md',
// firstRelease: false,
changelogFile: 'CHANGELOG.md',
sign: false,
// noVerify: false,
// commitAll: false,
silent: false,
scripts: {},
skip: {
tag: true,
},
packageFiles: defaultPackageFiles,
bumpFiles: defaultBumpFiles,
dryRun: false,
// gitTagFallback: true,
releaseCommitMessageFormat: 'chore(release): {{currentTag}}',
changeLogHeader: '# Changelog\n\nAll notable changes to this project will be documented in this file. ' +
'See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.\n',
Expand Down
48 changes: 17 additions & 31 deletions tools/cdk-release/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@ import { bump } from './lifecycles/bump';
import { changelog } from './lifecycles/changelog';
import { commit } from './lifecycles/commit';
import { debug, debugObject } from './private/print';
import { ReleaseOptions } from './types';
import { resolveUpdaterObjectFromArgument } from './updaters';
import { ReleaseOptions, Versions } from './types';

module.exports = async function main(opts: ReleaseOptions): Promise<void> {
// handle the default options
Expand All @@ -17,41 +16,28 @@ module.exports = async function main(opts: ReleaseOptions): Promise<void> {
};
debugObject(args, 'options are (including defaults)', args);

const packageInfo = determinePackageInfo(args);
debugObject(args, 'packageInfo is', packageInfo);
const currentVersion = readVersion(args.versionFile);
debugObject(args, 'Current version info', currentVersion);

const currentVersion = packageInfo.version;
debug(args, 'Current version is: ' + currentVersion);

const commits = await getConventionalCommitsFromGitHistory(args, `v${currentVersion}`);
const commits = await getConventionalCommitsFromGitHistory(args, `v${currentVersion.stableVersion}`);
const filteredCommits = filterCommits(args, commits);
debugObject(args, 'Found and filtered commits', filteredCommits);

const bumpResult = await bump(args, currentVersion);
const newVersion = bumpResult.newVersion;
debug(args, 'New version is: ' + newVersion);
const newVersion = await bump(args, currentVersion);
debugObject(args, 'New version is', newVersion);

const changelogResult = await changelog(args, currentVersion, newVersion, filteredCommits);
debug(args, 'Writing Changelog');
await changelog(args, currentVersion.stableVersion, newVersion.stableVersion, filteredCommits);

await commit(args, newVersion, [...bumpResult.changedFiles, ...changelogResult.changedFiles]);
debug(args, 'Committing result');
await commit(args, newVersion.stableVersion, [args.versionFile, args.changelogFile]);
};

interface PackageInfo {
version: string;
private: string | boolean | null | undefined;
}

function determinePackageInfo(args: ReleaseOptions): PackageInfo {
for (const packageFile of args.packageFiles ?? []) {
const updater = resolveUpdaterObjectFromArgument(packageFile);
const pkgPath = path.resolve(process.cwd(), updater.filename);
const contents = fs.readFileSync(pkgPath, 'utf8');
// we stop on the first (successful) option
return {
version: updater.updater.readVersion(contents),
private: typeof updater.updater.isPrivate === 'function' ? updater.updater.isPrivate(contents) : false,
};
}

throw new Error('Could not establish the version to bump!');
export function readVersion(versionFile: string): Versions {
const versionPath = path.resolve(process.cwd(), versionFile);
const contents = JSON.parse(fs.readFileSync(versionPath, { encoding: 'utf-8' }));
return {
stableVersion: contents.version,
alphaVersion: contents.alphaVersion,
};
}
92 changes: 48 additions & 44 deletions tools/cdk-release/lib/lifecycles/bump.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,48 @@
import * as fs from 'fs';
import * as path from 'path';
import * as semver from 'semver';
import { writeFile } from '../private/files';
import { notify } from '../private/print';
import { ReleaseOptions, ReleaseType } from '../types';
import { resolveUpdaterObjectFromArgument } from '../updaters/index';
import { LifecyclesSkip, ReleaseType, Versions } from '../types';

export interface BumpResult {
readonly newVersion: string;
readonly changedFiles: string[];
export interface BumpOptions {
releaseAs: ReleaseType;
skip?: LifecyclesSkip;
versionFile: string;
prerelease?: string;

dryRun?: boolean;
verbose?: boolean;
silent?: boolean;
}

export async function bump(args: ReleaseOptions, currentVersion: string): Promise<BumpResult> {
export async function bump(args: BumpOptions, currentVersion: Versions): Promise<Versions> {
if (args.skip?.bump) {
return {
newVersion: currentVersion,
changedFiles: [],
};
return currentVersion;
}

const releaseType = getReleaseType(args.prerelease, args.releaseAs, currentVersion);
const newVersion = semver.inc(currentVersion, releaseType, args.prerelease);
if (!newVersion) {
throw new Error('Could not increment version: ' + currentVersion);
const releaseType = getReleaseType(args.prerelease, args.releaseAs, currentVersion.stableVersion);
const newStableVersion = semver.inc(currentVersion.stableVersion, releaseType, args.prerelease);
if (!newStableVersion) {
throw new Error('Could not increment version: ' + currentVersion.stableVersion);
}
const changedFiles = updateBumpFiles(args, newVersion);
return { newVersion, changedFiles };

const newVersion: Versions = {
stableVersion: newStableVersion,
alphaVersion: bumpAlphaReleaseVersion(currentVersion, releaseType),
};

notify(args,
'bumping version in ' + args.versionFile + ' from %s to %s',
[currentVersion, newVersion],
);
const versionPath = path.resolve(process.cwd(), args.versionFile);
const versionFileContents = JSON.stringify({
version: newVersion.stableVersion,
alphaVersion: newVersion.alphaVersion,
}, undefined, 2);
writeFile(args, versionPath, versionFileContents);

return newVersion;
}

function getReleaseType(prerelease: string | undefined, expectedReleaseType: ReleaseType, currentVersion: string): semver.ReleaseType {
Expand Down Expand Up @@ -89,34 +106,21 @@ function getTypePriority(type: string): number {
}

/**
* attempt to update the version number in provided `bumpFiles`
* @param args config object
* @param newVersion version number to update to.
* @return the collection of file paths that were actually changed
* https://github.com/aws/aws-cdk/issues/15581
* We version any alpha modules in one of two ways, depending on the main/stable release.
* If the main release is itself a prerelease (e.g., 2.0.0-rc.17),
* we will increment the current alpha release.
* If the main release is not a prerelease, we use the main release version, but with an alpha tag.
*/
function updateBumpFiles(args: ReleaseOptions, newVersion: string): string[] {
const ret = new Array<string>();
function bumpAlphaReleaseVersion(currentVersion: Versions, releaseType: semver.ReleaseType): string | undefined {
if (!currentVersion.alphaVersion) { return undefined; }

for (const bumpFile of (args.bumpFiles ?? [])) {
const updater = resolveUpdaterObjectFromArgument(bumpFile);
if (!updater) {
continue;
}
const configPath = path.resolve(process.cwd(), updater.filename);
const stat = fs.lstatSync(configPath);
if (!stat.isFile()) {
continue;
}
const contents = fs.readFileSync(configPath, 'utf8');
notify(args,
'bumping version in ' + updater.filename + ' from %s to %s',
[updater.updater.readVersion(contents), newVersion],
);
writeFile(args, configPath,
updater.updater.writeVersion(contents, newVersion),
);
ret.push(updater.filename);
}
const newAlphaVersion = releaseType.startsWith('pre')
? semver.inc(currentVersion.alphaVersion, releaseType, 'alpha')
: semver.inc(currentVersion.stableVersion, 'pre' + releaseType as semver.ReleaseType, 'alpha');

return ret;
if (!newAlphaVersion) {
throw new Error('Could not increment alpha version: ' + currentVersion.alphaVersion);
}
return newAlphaVersion;
}
33 changes: 22 additions & 11 deletions tools/cdk-release/lib/lifecycles/changelog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,33 @@ import * as fs from 'fs-extra';
import { ConventionalCommit } from '../conventional-commits';
import { writeFile } from '../private/files';
import { notify, debug, debugObject } from '../private/print';
import { ReleaseOptions } from '../types';
import { LifecyclesSkip } from '../types';
// eslint-disable-next-line @typescript-eslint/no-require-imports
const conventionalChangelogPresetLoader = require('conventional-changelog-preset-loader');
// eslint-disable-next-line @typescript-eslint/no-require-imports
const conventionalChangelogWriter = require('conventional-changelog-writer');

const START_OF_LAST_RELEASE_PATTERN = /(^#+ \[?[0-9]+\.[0-9]+\.[0-9]+|<a name=)/m;

export interface ChangelogOptions {
skip?: LifecyclesSkip;
changelogFile: string;
dryRun?: boolean;
verbose?: boolean;
silent?: boolean;

changeLogHeader?: string;
includeDateInChangelog?: boolean;
releaseCommitMessageFormat?: string;
}

export interface ChangelogResult {
readonly contents: string;
readonly changedFiles: string[];
}

export async function changelog(
args: ReleaseOptions, currentVersion: string, newVersion: string, commits: ConventionalCommit[],
args: ChangelogOptions, currentVersion: string, newVersion: string, commits: ConventionalCommit[],
): Promise<ChangelogResult> {
if (args.skip?.changelog) {
return {
Expand All @@ -28,7 +40,7 @@ export async function changelog(
createChangelogIfMissing(args);

// find the position of the last release and remove header
let oldContent = args.dryRun ? '' : fs.readFileSync(args.infile!, 'utf-8');
let oldContent = args.dryRun ? '' : fs.readFileSync(args.changelogFile, 'utf-8');
const oldContentStart = oldContent.search(START_OF_LAST_RELEASE_PATTERN);
if (oldContentStart !== -1) {
oldContent = oldContent.substring(oldContentStart);
Expand Down Expand Up @@ -100,24 +112,23 @@ export async function changelog(
content += buffer.toString();
});
changelogStream.on('end', function () {
notify(args, 'outputting changes to %s', [args.infile]);
notify(args, 'outputting changes to %s', [args.changelogFile]);
if (args.dryRun) {
debug(args, `\n---\n${content.trim()}\n---\n`);
} else {
writeFile(args, args.infile!, args.changeLogHeader + '\n' + (content + oldContent).replace(/\n+$/, '\n'));
writeFile(args, args.changelogFile, args.changeLogHeader + '\n' + (content + oldContent).replace(/\n+$/, '\n'));
}
return resolve({
contents: content,
changedFiles: [args.infile!],
changedFiles: [args.changelogFile],
});
});
});
}

function createChangelogIfMissing(args: ReleaseOptions) {
if (!fs.existsSync(args.infile!)) {
notify(args, 'created %s', [args.infile]);
// args.outputUnreleased = true
writeFile(args, args.infile!, '\n');
function createChangelogIfMissing(args: ChangelogOptions) {
if (!fs.existsSync(args.changelogFile)) {
notify(args, 'created %s', [args.changelogFile]);
writeFile(args, args.changelogFile, '\n');
}
}
7 changes: 5 additions & 2 deletions tools/cdk-release/lib/private/files.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import * as fs from 'fs';
import { ReleaseOptions } from '../types';

export function writeFile(args: ReleaseOptions, filePath: string, content: string): void {
interface WriteFileOpts {
readonly dryRun?: boolean;
}

export function writeFile(args: WriteFileOpts, filePath: string, content: string): void {
if (args.dryRun) {
return;
}
Expand Down
12 changes: 8 additions & 4 deletions tools/cdk-release/lib/private/print.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
import * as util from 'util';
import { ReleaseOptions } from '../types';

export function debug(opts: ReleaseOptions, message: string): void {
export interface LoggingOptions {
verbose?: boolean;
silent?: boolean;
}

export function debug(opts: LoggingOptions, message: string): void {
if (opts.verbose) {
// eslint-disable-next-line no-console
console.log(`[cdk-release] ${message}`);
}
}

export function debugObject(opts: ReleaseOptions, message: string, object: any): void {
export function debugObject(opts: LoggingOptions, message: string, object: any): void {
if (opts.verbose) {
// eslint-disable-next-line no-console
console.log(`[cdk-release] ${message}:\n`, object);
}
}

export function notify(opts: ReleaseOptions, msg: string, args: any[]) {
export function notify(opts: LoggingOptions, msg: string, args: any[]) {
if (!opts.silent) {
// eslint-disable-next-line no-console
console.info('✔ ' + util.format(msg, ...args));
Expand Down
Loading

0 comments on commit 1b29ca8

Please sign in to comment.