From e290695e2fb03b5ab2872fa200840f3c1d031f0c Mon Sep 17 00:00:00 2001 From: alex-pex Date: Tue, 10 Jul 2018 13:31:19 +0200 Subject: [PATCH 1/2] feat: allow "Unreleased" title to be customized --- README.md | 3 ++ src/__mocks__/changelog.ts | 1 + .../markdown-renderer.spec.ts.snap | 14 ++++++ src/changelog.ts | 1 + src/cli.ts | 11 ++++- src/configuration.spec.ts | 6 ++- src/configuration.ts | 6 ++- src/markdown-renderer.spec.ts | 47 ++++++++++++++++++- src/markdown-renderer.ts | 7 +-- 9 files changed, 87 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 1fd12b27..7869cc2a 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,9 @@ The supported options are: - `repo`: Your "org/repo" on GitHub (automatically inferred from the `package.json` file) +- `nextVersion`: Title for unreleased commits + (e.g. `Unreleased`) + - `labels`: GitHub PR labels mapped to changelog section headers - `ignoreCommitters`: List of committers to ignore (exact or partial match). diff --git a/src/__mocks__/changelog.ts b/src/__mocks__/changelog.ts index 678dad59..e52a73f9 100644 --- a/src/__mocks__/changelog.ts +++ b/src/__mocks__/changelog.ts @@ -15,6 +15,7 @@ const defaultConfig = { }, ignoreCommitters: [], cacheDir: ".changelog", + nextVersion: "Unreleased", }; class MockedChangelog extends Changelog { diff --git a/src/__snapshots__/markdown-renderer.spec.ts.snap b/src/__snapshots__/markdown-renderer.spec.ts.snap index 7d3d46d9..d108c6c0 100644 --- a/src/__snapshots__/markdown-renderer.spec.ts.snap +++ b/src/__snapshots__/markdown-renderer.spec.ts.snap @@ -59,3 +59,17 @@ exports[`MarkdownRenderer renderContributorList renders a list of GitHub users 1 - Tobias Bieniek ([@Turbo87](https://github.com/Turbo87)) - [@hzoo](https://github.com/hzoo)" `; + +exports[`MarkdownRenderer renderRelease renders unreleased commits 1`] = ` +"## Unreleased (2018-07-10) + +#### :rocket: New Feature +* My cool PR ([@hzoo](http://hzoo.com))" +`; + +exports[`MarkdownRenderer renderRelease renders unreleased commits, with named next release 1`] = ` +"## v2.0.0-alpha.0 (2018-07-10) + +#### :rocket: New Feature +* My cool PR ([@hzoo](http://hzoo.com))" +`; diff --git a/src/changelog.ts b/src/changelog.ts index eea11b5e..a2f3069f 100644 --- a/src/changelog.ts +++ b/src/changelog.ts @@ -26,6 +26,7 @@ export default class Changelog { this.renderer = new MarkdownRenderer({ categories: Object.keys(this.config.labels).map(key => this.config.labels[key]), baseIssueUrl: this.github.getBaseIssueUrl(this.config.repo), + unreleasedName: this.config.nextVersion, }); } diff --git a/src/cli.ts b/src/cli.ts index bfda663e..599bd48c 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -32,8 +32,16 @@ export async function run() { type: "string", desc: "A git tag that determines the upper bound of the range of commits", }, + "next-version": { + type: "string", + desc: "The name of the next version", + default: "Unreleased", + }, }) - .example("lerna-changelog", "create a changelog for the changes after the latest available tag") + .example( + "lerna-changelog", + 'create a changelog for the changes after the latest available tag, under "Unreleased" section' + ) .example( "lerna-changelog --from=0.1.0 --to=0.3.0", "create a changelog for the changes in all tags within the given range" @@ -45,6 +53,7 @@ export async function run() { let options = { tagFrom: argv["from"] || argv["tag-from"], tagTo: argv["to"] || argv["tag-to"], + nextVersion: argv["next-version"], }; try { diff --git a/src/configuration.spec.ts b/src/configuration.spec.ts index 79137190..72ddc3a4 100644 --- a/src/configuration.spec.ts +++ b/src/configuration.spec.ts @@ -19,19 +19,21 @@ describe("Configuration", function() { it("reads the configuration from 'lerna.json'", function() { fs.writeJsonSync(path.join(tmpDir, "lerna.json"), { - changelog: { repo: "foo/bar" }, + changelog: { repo: "foo/bar", nextVersion: "next" }, }); const result = fromPath(tmpDir); + expect(result.nextVersion).toEqual("next"); expect(result.repo).toEqual("foo/bar"); }); it("reads the configuration from 'package.json'", function() { fs.writeJsonSync(path.join(tmpDir, "package.json"), { - changelog: { repo: "foo/bar" }, + changelog: { repo: "foo/bar", nextVersion: "next" }, }); const result = fromPath(tmpDir); + expect(result.nextVersion).toEqual("next"); expect(result.repo).toEqual("foo/bar"); }); diff --git a/src/configuration.ts b/src/configuration.ts index 181c903e..f82aa0a1 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -11,6 +11,7 @@ export interface Configuration { labels: { [key: string]: string }; ignoreCommitters: string[]; cacheDir?: string; + nextVersion: string; } export function load(options: Partial = {}): Configuration { @@ -28,12 +29,12 @@ export function fromPath(rootPath: string, options: Partial = {}) Object.assign(config, options); // Step 3: fill partial config with defaults - let { repo, labels, cacheDir, ignoreCommitters } = config; + let { repo, nextVersion, labels, cacheDir, ignoreCommitters } = config; if (!repo) { repo = findRepo(rootPath); if (!repo) { - throw new ConfigurationError('Could not infer "repo” from the "package.json" file.'); + throw new ConfigurationError('Could not infer "repo" from the "package.json" file.'); } } @@ -60,6 +61,7 @@ export function fromPath(rootPath: string, options: Partial = {}) return { repo, + nextVersion, rootPath, labels, ignoreCommitters, diff --git a/src/markdown-renderer.spec.ts b/src/markdown-renderer.spec.ts index 4faecbcd..30fc798f 100644 --- a/src/markdown-renderer.spec.ts +++ b/src/markdown-renderer.spec.ts @@ -1,6 +1,8 @@ -import { CommitInfo } from "./interfaces"; +import { CommitInfo, Release } from "./interfaces"; import MarkdownRenderer from "./markdown-renderer"; +const UNRELEASED_TAG = "___unreleased___"; + const BASIC_COMMIT = { githubIssue: { title: "My cool PR", @@ -53,6 +55,10 @@ function renderer(options: any = {}): MarkdownRenderer { }); } +function getToday() { + return "2018-07-10"; +} + describe("MarkdownRenderer", () => { describe("renderPackageNames", () => { it(`renders an empty list of package names as "Other"`, () => { @@ -172,4 +178,43 @@ describe("MarkdownRenderer", () => { expect(commitsByCategory).toMatchSnapshot(); }); }); + + describe("renderRelease", () => { + it(`renders unreleased commits`, () => { + const release: Release = { + name: UNRELEASED_TAG, + date: getToday(), + commits: [ + { + ...BASIC_COMMIT, + categories: [":rocket: New Feature"], + }, + ], + }; + const options = { + categories: [":rocket: New Feature"], + }; + const result = renderer(options).renderRelease(release); + expect(result).toMatchSnapshot(); + }); + + it(`renders unreleased commits, with named next release`, () => { + const release: Release = { + name: UNRELEASED_TAG, + date: getToday(), + commits: [ + { + ...BASIC_COMMIT, + categories: [":rocket: New Feature"], + }, + ], + }; + const options = { + categories: [":rocket: New Feature"], + unreleasedName: "v2.0.0-alpha.0", + }; + const result = renderer(options).renderRelease(release); + expect(result).toMatchSnapshot(); + }); + }); }); diff --git a/src/markdown-renderer.ts b/src/markdown-renderer.ts index 82583cb9..20c4bea5 100644 --- a/src/markdown-renderer.ts +++ b/src/markdown-renderer.ts @@ -12,13 +12,14 @@ interface CategoryInfo { interface Options { categories: string[]; baseIssueUrl: string; + unreleasedName: string; } export default class MarkdownRenderer { private options: Options; - constructor(options: Options) { - this.options = options; + constructor({ unreleasedName = "Unreleased", ...options }: Options) { + this.options = { unreleasedName, ...options }; } public renderMarkdown(releases: Release[]) { @@ -36,7 +37,7 @@ export default class MarkdownRenderer { // Skip this iteration if there are no commits available for the release if (categoriesWithCommits.length === 0) return ""; - const releaseTitle = release.name === UNRELEASED_TAG ? "Unreleased" : release.name; + const releaseTitle = release.name === UNRELEASED_TAG ? this.options.unreleasedName : release.name; let markdown = `## ${releaseTitle} (${release.date})`; From 2eaf3534e7d4ae1ca863ce590aa930712e5d4a0a Mon Sep 17 00:00:00 2001 From: alex-pex Date: Fri, 3 Aug 2018 14:44:17 +0200 Subject: [PATCH 2/2] feat: infer "nextVersion" option from package.json --- src/cli.ts | 6 ++++++ src/configuration.spec.ts | 7 +++++-- src/configuration.ts | 21 ++++++++++++++++++++- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/src/cli.ts b/src/cli.ts index 599bd48c..1be9e014 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -37,6 +37,11 @@ export async function run() { desc: "The name of the next version", default: "Unreleased", }, + "next-version-from-metadata": { + type: "boolean", + desc: "Infer the name of the next version from package metadata", + default: false, + }, }) .example( "lerna-changelog", @@ -54,6 +59,7 @@ export async function run() { tagFrom: argv["from"] || argv["tag-from"], tagTo: argv["to"] || argv["tag-to"], nextVersion: argv["next-version"], + nextVersionFromMetadata: argv["next-version-from-metadata"], }; try { diff --git a/src/configuration.spec.ts b/src/configuration.spec.ts index 72ddc3a4..ef040d50 100644 --- a/src/configuration.spec.ts +++ b/src/configuration.spec.ts @@ -39,14 +39,17 @@ describe("Configuration", function() { it("prefers 'package.json' over 'lerna.json'", function() { fs.writeJsonSync(path.join(tmpDir, "lerna.json"), { - changelog: { repo: "foo/lerna" }, + version: "1.0.0-lerna.0", + changelog: { repo: "foo/lerna", nextVersionFromMetadata: true }, }); fs.writeJsonSync(path.join(tmpDir, "package.json"), { - changelog: { repo: "foo/package" }, + version: "1.0.0-package.0", + changelog: { repo: "foo/package", nextVersionFromMetadata: true }, }); const result = fromPath(tmpDir); + expect(result.nextVersion).toEqual("v1.0.0-package.0"); expect(result.repo).toEqual("foo/package"); }); diff --git a/src/configuration.ts b/src/configuration.ts index f82aa0a1..ad3ad9ea 100644 --- a/src/configuration.ts +++ b/src/configuration.ts @@ -12,6 +12,7 @@ export interface Configuration { ignoreCommitters: string[]; cacheDir?: string; nextVersion: string; + nextVersionFromMetadata?: boolean; } export function load(options: Partial = {}): Configuration { @@ -29,7 +30,7 @@ export function fromPath(rootPath: string, options: Partial = {}) Object.assign(config, options); // Step 3: fill partial config with defaults - let { repo, nextVersion, labels, cacheDir, ignoreCommitters } = config; + let { repo, nextVersion, nextVersionFromMetadata, labels, cacheDir, ignoreCommitters } = config; if (!repo) { repo = findRepo(rootPath); @@ -38,6 +39,14 @@ export function fromPath(rootPath: string, options: Partial = {}) } } + if (nextVersionFromMetadata) { + nextVersion = findNextVersion(rootPath); + } + + if (!nextVersion) { + throw new ConfigurationError('Could not infer "nextVersion" from the "package.json" file.'); + } + if (!labels) { labels = { breaking: ":boom: Breaking Change", @@ -97,6 +106,16 @@ function findRepo(rootPath: string): string | undefined { return findRepoFromPkg(pkg); } +function findNextVersion(rootPath: string): string | undefined { + const pkgPath = path.join(rootPath, "package.json"); + const lernaPath = path.join(rootPath, "lerna.json"); + + const pkg = fs.existsSync(pkgPath) ? JSON.parse(fs.readFileSync(pkgPath)) : {}; + const lerna = fs.existsSync(lernaPath) ? JSON.parse(fs.readFileSync(lernaPath)) : {}; + + return pkg.version ? `v${pkg.version}` : lerna.version ? `v${lerna.version}` : undefined; +} + export function findRepoFromPkg(pkg: any): string | undefined { const url = pkg.repository.url || pkg.repository; const normalized = normalize(url).url;