-
Notifications
You must be signed in to change notification settings - Fork 4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore(cdk-release): create our release notes as part of the build
Currently, our bump process creates `CHANGELOG.md` for the `master` branch, and `CHANGELOG.v2.md` and `CHANGELOG.v2.alpha.md` on the `v2-main` branch. For the latter, we need to combine the two changelogs into one for the GitHub release, where all of the artifacts of the release (both aws-cdk-lib and all the alpha modules) are bundled together. As proposed in the [RFC], this combined format shows the stable/aws-cdk-lib changes first, and then the alpha module changes under their own heading. This generated -- but not saved -- RELEASE_NOTES.md will be used by our publishing pipeline to generate the GitHub release notes. [RFC]: https://github.com/aws/aws-cdk-rfcs/blob/master/text/0249-v2-experiments.md#changelog--release-notes fixes #16802
- Loading branch information
Showing
11 changed files
with
252 additions
and
21 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -45,3 +45,4 @@ yarn-error.log | |
.nzm-* | ||
|
||
/.versionrc.json | ||
RELEASE_NOTES.md |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
#!/usr/bin/env node | ||
|
||
const cdkRelease = require('@aws-cdk/cdk-release'); | ||
const ver = require('./resolve-version'); | ||
|
||
async function main() { | ||
await cdkRelease.createReleaseNotes({ | ||
versionFile: ver.versionFile, | ||
changelogFile: ver.changelogFile, | ||
alphaChangelogFile: ver.alphaChangelogFile, | ||
releaseNotesFile: 'RELEASE_NOTES.md', | ||
}); | ||
} | ||
|
||
main().catch(err => { | ||
console.error(err.stack); | ||
process.exit(1); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
// eslint-disable-next-line @typescript-eslint/no-require-imports | ||
import parseChangelog = require('changelog-parser'); | ||
import { WriteFileOpts, writeFile } from './private/files'; | ||
import { debugObject, LoggingOptions } from './private/print'; | ||
import { Versions } from './types'; | ||
import { readVersion } from './versions'; | ||
|
||
export interface ReleaseNotesOpts { | ||
/** path to the version file for the current branch (e.g., version.v2.json) */ | ||
versionFile: string; | ||
/** path to the primary changelog file (e.g., 'CHANGELOG.v2.md') */ | ||
changelogFile: string; | ||
/** (optional) path to the independent alpha changelog file (e.g., 'CHANGELOG.v2.alpha.md') */ | ||
alphaChangelogFile?: string; | ||
/** path to write out the final release notes (e.g., 'RELEASE_NOTES.md'). */ | ||
releaseNotesFile: string; | ||
} | ||
|
||
/** | ||
* Creates a release notes file from one (or more) changelog files for the current version. | ||
* If an alpha version and alpha changelog file aren't present, this is identical to the contents | ||
* of the (main) changelog for the current version. Otherwise, a combined release is put together | ||
* from the contents of the stable and alpha changelogs. | ||
*/ | ||
export async function createReleaseNotes(opts: ReleaseNotesOpts & LoggingOptions & WriteFileOpts) { | ||
const currentVersion = readVersion(opts.versionFile); | ||
debugObject(opts, 'Current version info', currentVersion); | ||
|
||
writeFile(opts, opts.releaseNotesFile, await releaseNoteContents(currentVersion, opts)); | ||
} | ||
|
||
async function releaseNoteContents(currentVersion: Versions, opts: ReleaseNotesOpts) { | ||
const stableChangelogContents = await readChangelogSection(opts.changelogFile, currentVersion.stableVersion); | ||
// If we don't have an alpha version and distinct alpha changelog, the release notes are just the main changelog section. | ||
if (!opts.alphaChangelogFile || !currentVersion.alphaVersion) { return stableChangelogContents; } | ||
|
||
const alphaChangelogContents = await readChangelogSection(opts.alphaChangelogFile, currentVersion.alphaVersion); | ||
|
||
// See https://github.com/aws/aws-cdk-rfcs/blob/master/text/0249-v2-experiments.md#changelog--release-notes for format | ||
return [ | ||
'## aws-cdk-lib', // Assumption: we only have stable + alpha changelogs on v2+, where aws-cdk-lib is the stable module. | ||
stableChangelogContents, | ||
'---', | ||
`## Alpha modules (${currentVersion.alphaVersion})`, | ||
alphaChangelogContents, | ||
].join('\n'); | ||
} | ||
|
||
async function readChangelogSection(changelogFile: string, version: string) { | ||
const changelog = await parseChangelog(changelogFile) as Changelog; | ||
const entry = (changelog.versions || []).find(section => section.version === version); | ||
if (!entry) { | ||
throw new Error(`No changelog entry found for version ${version} in ${changelogFile}`); | ||
} | ||
return entry.body; | ||
} | ||
|
||
/** @types/changelog-parser only returns `object`; this is slightly more helpful */ | ||
interface Changelog { | ||
title: string; | ||
description: string; | ||
versions?: ChangelogVersion[]; | ||
} | ||
interface ChangelogVersion { | ||
version: string; | ||
title: string; | ||
body: string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import * as path from 'path'; | ||
import * as fs from 'fs-extra'; | ||
import { Versions } from './types'; | ||
|
||
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, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import * as files from '../lib/private/files'; | ||
import { createReleaseNotes } from '../lib/release-notes'; | ||
import * as versions from '../lib/versions'; | ||
|
||
/** MOCKS */ | ||
const mockWriteFile = jest.spyOn(files, 'writeFile').mockImplementation(() => jest.fn()); | ||
const mockReadVersion = jest.spyOn(versions, 'readVersion'); | ||
jest.mock('changelog-parser', () => { return jest.fn(); }); | ||
// eslint-disable-next-line @typescript-eslint/no-require-imports | ||
const changelogParser = require('changelog-parser'); | ||
/** MOCKS */ | ||
|
||
beforeEach(() => { jest.resetAllMocks(); }); | ||
|
||
const DEFAULT_OPTS = { | ||
changelogFile: 'CHANGELOG.md', | ||
releaseNotesFile: 'RELEASE_NOTES.md', | ||
versionFile: 'versions.json', | ||
}; | ||
|
||
test('without alpha releases, only the stable changelog is returned', async () => { | ||
mockReadVersion.mockImplementation((_) => { return { stableVersion: '1.2.3' }; }); | ||
mockChangelogOnceForVersion('1.2.3', 'foo'); | ||
|
||
await createReleaseNotes(DEFAULT_OPTS); | ||
|
||
expectReleaseNotes('foo'); | ||
}); | ||
|
||
test('with alpha releases the contents of both are returned as separate sections', async () => { | ||
mockReadVersion.mockImplementation((_) => { return { stableVersion: '1.2.3', alphaVersion: '1.2.3-alpha' }; }); | ||
mockChangelogOnceForVersion('1.2.3', 'foo'); // stable | ||
mockChangelogOnceForVersion('1.2.3-alpha', 'bar'); // alpha | ||
|
||
await createReleaseNotes({ ...DEFAULT_OPTS, alphaChangelogFile: 'CHANGELOG.alpha.md' }); | ||
|
||
expectReleaseNotes([ | ||
'## aws-cdk-lib', | ||
'foo', | ||
'---', | ||
'## Alpha modules (1.2.3-alpha)', | ||
'bar', | ||
]); | ||
}); | ||
|
||
test('throws if no matching version is found in the changelog', async () => { | ||
mockReadVersion.mockImplementation((_) => { return { stableVersion: '1.2.3' }; }); | ||
mockChangelogOnceForVersion('4.5.6', 'foo'); | ||
|
||
await expect(createReleaseNotes(DEFAULT_OPTS)) | ||
.rejects | ||
.toThrow(/No changelog entry found for version 1.2.3 in CHANGELOG.md/); | ||
}); | ||
|
||
function mockChangelogOnceForVersion(version: string, body: string) { | ||
changelogParser.mockImplementationOnce((_: string) => { return { versions: [{ version, body }] }; }); | ||
} | ||
|
||
function expectReleaseNotes(contents: string | string[]) { | ||
const data = (typeof contents === 'string') ? contents : contents.join('\n'); | ||
expect(mockWriteFile).toBeCalledWith(expect.any(Object), 'RELEASE_NOTES.md', data); | ||
} |
Oops, something went wrong.