Skip to content

Commit

Permalink
Add option to write only changelog md or json (#980)
Browse files Browse the repository at this point in the history
  • Loading branch information
ecraig12345 authored Sep 6, 2024
1 parent cb2cca8 commit dbdde9c
Show file tree
Hide file tree
Showing 7 changed files with 97 additions and 32 deletions.
7 changes: 7 additions & 0 deletions change/beachball-526ee2eb-1931-449a-b24e-cc00cf631525.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"comment": "Add `'md'` and `'json'` options for `generateChangelog`",
"type": "minor",
"packageName": "beachball",
"email": "[email protected]",
"dependentChangeType": "patch"
}
2 changes: 1 addition & 1 deletion docs/overview/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ For the latest full list of supported options, see `RepoOptions` [in this file](
| `defaultNpmTag` | string | `'latest'` | package | the default dist-tag used for NPM publish |
| `disallowedChangeTypes` | string[] | | repo, group, package | what change types are disallowed |
| `fetch` | bool | `true` | repo | fetch from remote before doing diff comparisons |
| `generateChangelog` | bool | `true` | repo | whether to generate changelog files |
| `generateChangelog` | bool, `'md'`, or `'json'` | `true` | repo | whether to generate `CHANGELOG.md/json` (`'md'` or `'json'` to generate only that type) |
| `gitTags` | bool | `true` | repo, package | whether to create git tags for published packages (eg: foo_v1.0.1) |
| `groups` | `VersionGroupOptions[]` ([details][3]) | | repo | specifies groups of packages that need to be version bumped at the same time |
| `groupChanges` | bool | `false` | repo | will write multiple changes to a single changefile |
Expand Down
2 changes: 1 addition & 1 deletion src/__e2e__/bump.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -602,7 +602,7 @@ describe('version bumping', () => {
expect(modified).toContain('package2');

const changelogJson = readChangelogJson(repo.pathTo('packages/package2'));
expect(changelogJson.entries[0].comments.patch![0].comment).toBe('Bump package1 to v0.0.2');
expect(changelogJson?.entries[0].comments.patch![0].comment).toBe('Bump package1 to v0.0.2');
});

it('calls sync prebump hook before packages are bumped', async () => {
Expand Down
25 changes: 20 additions & 5 deletions src/__fixtures__/changelog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,28 @@ import _ from 'lodash';
import { SortedChangeTypes } from '../changefile/changeTypes';
import { ChangelogJson } from '../types/ChangeLog';

/** Read the CHANGELOG.md under the given package path, sanitizing any dates for snapshots */
export function readChangelogMd(packagePath: string): string {
/**
* Read the CHANGELOG.md under the given package path, sanitizing any dates for snapshots.
* Returns null if it doesn't exist.
*/
export function readChangelogMd(packagePath: string): string | null {
const changelogFile = path.join(packagePath, 'CHANGELOG.md');
if (!fs.existsSync(changelogFile)) {
return null;
}
const text = fs.readFileSync(changelogFile, { encoding: 'utf-8' });
return text.replace(/\w\w\w, \d\d \w\w\w [\d :]+?GMT/gm, '(date)');
}

/** Read the CHANGELOG.json under the given package path */
export function readChangelogJson(packagePath: string, cleanForSnapshot: boolean = false): ChangelogJson {
/**
* Read the CHANGELOG.json under the given package path.
* Returns null if it doesn't exist.
*/
export function readChangelogJson(packagePath: string, cleanForSnapshot: boolean = false): ChangelogJson | null {
const changelogJsonFile = path.join(packagePath, 'CHANGELOG.json');
if (!fs.existsSync(changelogJsonFile)) {
return null;
}
const json = fs.readJSONSync(changelogJsonFile, { encoding: 'utf-8' });
return cleanForSnapshot ? cleanChangelogJson(json) : json;
}
Expand All @@ -22,7 +34,10 @@ export function readChangelogJson(packagePath: string, cleanForSnapshot: boolean
* Clean changelog json for a snapshot: replace dates and SHAs with placeholders.
* Note: this clones the changelog object rather than modifying the original.
*/
export function cleanChangelogJson(changelog: ChangelogJson): ChangelogJson {
export function cleanChangelogJson(changelog: ChangelogJson | null): ChangelogJson | null {
if (!changelog) {
return null;
}
changelog = _.cloneDeep(changelog);
// for a better snapshot, make the fake commit match if the real commit did
const fakeCommits: { [commit: string]: string } = {};
Expand Down
52 changes: 46 additions & 6 deletions src/__functional__/changelog/writeChangelog.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@ import { writeChangelog } from '../../changelog/writeChangelog';
import { getPackageInfos } from '../../monorepo/getPackageInfos';
import { readChangeFiles } from '../../changefile/readChangeFiles';
import { BeachballOptions } from '../../types/BeachballOptions';
import { ChangeFileInfo } from '../../types/ChangeInfo';
import { ChangeFileInfo, ChangeType } from '../../types/ChangeInfo';
import type { Repository } from '../../__fixtures__/repository';
import { getDefaultOptions } from '../../options/getDefaultOptions';

function getChange(packageName: string, comment: string): ChangeFileInfo {
function getChange(packageName: string, comment: string, type: ChangeType = 'patch'): ChangeFileInfo {
return {
comment,
email: '[email protected]',
packageName,
type: 'patch',
type,
dependentChangeType: 'patch',
};
}
Expand Down Expand Up @@ -77,7 +77,7 @@ describe('writeChangelog', () => {
expect(cleanChangelogJson(changelogJson)).toMatchSnapshot('changelog json');

// Every entry should have a different commit hash
const patchComments = changelogJson.entries[0].comments.patch!;
const patchComments = changelogJson!.entries[0].comments.patch!;
const commits = patchComments.map(entry => entry.commit);
expect(new Set(commits).size).toEqual(patchComments.length);

Expand Down Expand Up @@ -111,7 +111,7 @@ describe('writeChangelog', () => {
expect(cleanChangelogJson(changelogJson)).toMatchSnapshot('changelog json');

// Every entry should have a different commit hash
const patchComments = changelogJson.entries[0].comments.patch!;
const patchComments = changelogJson!.entries[0].comments.patch!;
const commits = patchComments.map(entry => entry.commit);
expect(new Set(commits).size).toEqual(patchComments.length);

Expand Down Expand Up @@ -151,7 +151,7 @@ describe('writeChangelog', () => {
expect(readChangelogJson(repo.pathTo('packages/bar'), true /*clean*/)).toMatchSnapshot('bar CHANGELOG.json');

// Every entry should have a different commit hash
const patchComments = fooJson.entries[0].comments.patch!;
const patchComments = fooJson!.entries[0].comments.patch!;
const commits = patchComments.map(entry => entry.commit);
expect(new Set(commits).size).toEqual(patchComments.length);

Expand Down Expand Up @@ -296,4 +296,44 @@ describe('writeChangelog', () => {
// Validate grouped changelog for foo and bar packages
expect(readChangelogMd(repo.pathTo('packages/foo'))).toMatchSnapshot();
});

it('writes only CHANGELOG.md if generateChangelog is "md"', async () => {
repo = repositoryFactory.cloneRepository();
const options = getOptions({ generateChangelog: 'md' });

repo.commitChange('foo');
generateChangeFiles([getChange('foo', 'comment 1')], options);

const packageInfos = getPackageInfos(repo.rootPath);
const changes = readChangeFiles(options, packageInfos);

await writeChangelog(options, changes, { foo: 'patch' }, { foo: new Set(['foo']) }, packageInfos);

// CHANGELOG.md is written
expect(readChangelogMd(repo.rootPath)).toContain('## 1.0.0');

// CHANGELOG.json is not written
expect(readChangelogJson(repo.rootPath)).toBeNull();
});

it('writes only CHANGELOG.json if generateChangelog is "json"', async () => {
repo = repositoryFactory.cloneRepository();
const options = getOptions({ generateChangelog: 'json' });

repo.commitChange('foo');
generateChangeFiles([getChange('foo', 'comment 1')], options);

const packageInfos = getPackageInfos(repo.rootPath);
const changes = readChangeFiles(options, packageInfos);

await writeChangelog(options, changes, { foo: 'patch' }, { foo: new Set(['foo']) }, packageInfos);

// CHANGELOG.md is not written
expect(readChangelogMd(repo.rootPath)).toBeNull();

// CHANGELOG.json is written
const changelogJson = readChangelogJson(repo.rootPath);
expect(changelogJson).not.toBeNull();
expect(changelogJson!.entries[0].comments.patch).toEqual([expect.objectContaining({ comment: 'comment 1' })]);
});
});
32 changes: 16 additions & 16 deletions src/changelog/writeChangelog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -123,25 +123,25 @@ async function writeChangelogFiles(
let previousJson: ChangelogJson | undefined;

// Update CHANGELOG.json
const changelogJsonFile = path.join(changelogPath, 'CHANGELOG.json');
try {
previousJson = fs.existsSync(changelogJsonFile) ? fs.readJSONSync(changelogJsonFile) : undefined;
} catch (e) {
console.warn(`${changelogJsonFile} is invalid: ${e}`);
}
try {
const nextJson = renderJsonChangelog(newVersionChangelog, previousJson);
fs.writeJSONSync(changelogJsonFile, nextJson, { spaces: 2 });
} catch (e) {
console.warn(`Problem writing to ${changelogJsonFile}: ${e}`);
if (options.generateChangelog === true || options.generateChangelog === 'json') {
const changelogJsonFile = path.join(changelogPath, 'CHANGELOG.json');
try {
previousJson = fs.existsSync(changelogJsonFile) ? fs.readJSONSync(changelogJsonFile) : undefined;
} catch (e) {
console.warn(`${changelogJsonFile} is invalid: ${e}`);
}
try {
const nextJson = renderJsonChangelog(newVersionChangelog, previousJson);
fs.writeJSONSync(changelogJsonFile, nextJson, { spaces: 2 });
} catch (e) {
console.warn(`Problem writing to ${changelogJsonFile}: ${e}`);
}
}

// Update CHANGELOG.md
// Update CHANGELOG.md if there are changes of types besides "none"
if (
newVersionChangelog.comments.major ||
newVersionChangelog.comments.minor ||
newVersionChangelog.comments.patch ||
newVersionChangelog.comments.prerelease
(options.generateChangelog === true || options.generateChangelog === 'md') &&
Object.entries(newVersionChangelog.comments).some(([type, comments]) => type !== 'none' && comments?.length)
) {
const changelogFile = path.join(changelogPath, 'CHANGELOG.md');
const previousContent = fs.existsSync(changelogFile) ? fs.readFileSync(changelogFile).toString() : '';
Expand Down
9 changes: 6 additions & 3 deletions src/types/BeachballOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,13 @@ export interface RepoOptions {
*/
fetch: boolean;
/**
* Whether to generate changelog files
* @default true
* Whether to generate changelog files.
* - `true` (default) to generate both CHANGELOG.md and CHANGELOG.json
* - `false` to skip changelog generation
* - `'md'` to generate only CHANGELOG.md
* - `'json'` to generate only CHANGELOG.json
*/
generateChangelog: boolean;
generateChangelog: boolean | 'md' | 'json';
/** Options for bumping package versions together */
groups?: VersionGroupOptions[];
/**
Expand Down

0 comments on commit dbdde9c

Please sign in to comment.