diff --git a/change/beachball-6873c86b-d50c-4bcc-8219-c17d2f9cfe0c.json b/change/beachball-6873c86b-d50c-4bcc-8219-c17d2f9cfe0c.json new file mode 100644 index 000000000..e2a9d03f6 --- /dev/null +++ b/change/beachball-6873c86b-d50c-4bcc-8219-c17d2f9cfe0c.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "Add an index signature to ChangelogEntry", + "packageName": "beachball", + "email": "elcraig@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/change/beachball-7873c86b-d50c-4bcc-8219-c17d2f9cfe0d.json b/change/beachball-7873c86b-d50c-4bcc-8219-c17d2f9cfe0d.json new file mode 100644 index 000000000..302bb1e91 --- /dev/null +++ b/change/beachball-7873c86b-d50c-4bcc-8219-c17d2f9cfe0d.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Stop recording incorrect bump commits in CHANGELOG.json", + "packageName": "beachball", + "email": "elcraig@microsoft.com", + "dependentChangeType": "patch" +} diff --git a/src/__functional__/changelog/writeChangelog.test.ts b/src/__functional__/changelog/writeChangelog.test.ts index 7e6061fe0..f1a1bc448 100644 --- a/src/__functional__/changelog/writeChangelog.test.ts +++ b/src/__functional__/changelog/writeChangelog.test.ts @@ -255,7 +255,7 @@ describe('writeChangelog', () => { expect(readChangelogMd(monoRepo.pathTo('packages/foo'))).toMatchSnapshot(); }); - it('Verify that the changeFile transform functions are run, if provided', async () => { + it('runs transform.changeFiles functions if provided', async () => { const editedComment: string = 'Edited comment for testing'; const monoRepo = monoRepoFactory.cloneRepository(); monoRepo.commitChange('foo'); diff --git a/src/__tests__/changelog/getPackageChangelogs.test.ts b/src/__tests__/changelog/getPackageChangelogs.test.ts index d3256d931..5ceba10b1 100644 --- a/src/__tests__/changelog/getPackageChangelogs.test.ts +++ b/src/__tests__/changelog/getPackageChangelogs.test.ts @@ -1,123 +1,203 @@ -import { describe, expect, it } from '@jest/globals'; +import { describe, expect, it, jest } from '@jest/globals'; import { getPackageChangelogs } from '../../changelog/getPackageChangelogs'; import { BumpInfo } from '../../types/BumpInfo'; -import { ChangeSet } from '../../types/ChangeInfo'; +import { ChangeFileInfo, ChangeSet } from '../../types/ChangeInfo'; import { PackageInfos } from '../../types/PackageInfo'; +import { makePackageInfos } from '../../__fixtures__/packageInfos'; + +// Mock the methods used from workspace-tools so we don't access the filesystem +jest.mock('workspace-tools', () => ({ + findProjectRoot: () => '.', + getFileAddedHash: () => 'deadbeef', +})); + +function makeChangeInfo(pkg: string, overrides?: Partial): ChangeSet[number] { + return { + changeFile: `${pkg}.json`, + change: { + comment: `comment for ${pkg}`, + dependentChangeType: 'patch', + email: 'something@something.com', + packageName: pkg, + type: 'patch', + ...overrides, + }, + }; +} describe('getPackageChangelogs', () => { - it('should have multiple comment entries when a package has a changefile AND was part of a dependent bump', () => { + it('generates correct changelog entries for a single package', () => { const changeFileChangeInfos: ChangeSet = [ - { - changeFile: 'foo.json', - change: { - comment: 'comment for foo', - commit: 'deadbeef', - dependentChangeType: 'patch', - email: 'something@something.com', - packageName: 'foo', - type: 'patch', - }, + makeChangeInfo('foo'), + makeChangeInfo('foo', { type: 'minor', comment: 'other comment' }), + ]; + const packageInfos = makePackageInfos({ foo: { version: '1.0.0' } }); + + const changelogs = getPackageChangelogs({ + changeFileChangeInfos, + calculatedChangeTypes: { foo: 'patch' }, + packageInfos, + cwd: '.', + }); + + expect(changelogs.foo).toEqual({ + comments: { + minor: [{ author: 'something@something.com', comment: 'other comment', commit: 'deadbeef', package: 'foo' }], + patch: [{ author: 'something@something.com', comment: 'comment for foo', commit: 'deadbeef', package: 'foo' }], + }, + date: expect.any(Date), + name: 'foo', + tag: 'foo_v1.0.0', + version: '1.0.0', + }); + expect(changelogs.foo.comments.patch).toHaveLength(1); + }); + + it('generates correct changelog entries for multiple packages', () => { + const changeFileChangeInfos: ChangeSet = [makeChangeInfo('foo'), makeChangeInfo('bar')]; + const packageInfos = makePackageInfos({ + foo: { version: '1.0.0' }, + bar: { version: '2.0.0' }, + }); + + const changelogs = getPackageChangelogs({ + changeFileChangeInfos, + calculatedChangeTypes: { foo: 'patch', bar: 'patch' }, + packageInfos, + cwd: '.', + }); + + expect(changelogs.foo).toEqual({ + comments: { + patch: [{ author: 'something@something.com', comment: 'comment for foo', commit: 'deadbeef', package: 'foo' }], }, - { - changeFile: 'bar.json', - change: { - comment: 'comment for bar', - commit: 'deadbeef', - dependentChangeType: 'patch', - email: 'something@something.com', - packageName: 'bar', - type: 'patch', - }, + date: expect.any(Date), + name: 'foo', + tag: 'foo_v1.0.0', + version: '1.0.0', + }); + expect(changelogs.bar).toEqual({ + comments: { + patch: [{ author: 'something@something.com', comment: 'comment for bar', commit: 'deadbeef', package: 'bar' }], }, - ]; + date: expect.any(Date), + name: 'bar', + tag: 'bar_v2.0.0', + version: '2.0.0', + }); + }); + + it('preserves custom properties from change files', () => { + const changeFileChangeInfos: ChangeSet = [makeChangeInfo('foo', { extra: 'prop' })]; + const packageInfos: PackageInfos = makePackageInfos({ foo: { version: '1.0.0' } }); + + const changelogs = getPackageChangelogs({ + changeFileChangeInfos, + calculatedChangeTypes: { foo: 'patch' }, + packageInfos, + cwd: '.', + }); + + expect(changelogs.foo.comments.patch![0]).toMatchObject({ extra: 'prop' }); + }); + + it('records dependent bumps', () => { + const changeFileChangeInfos: ChangeSet = [makeChangeInfo('foo')]; const dependentChangedBy: BumpInfo['dependentChangedBy'] = { bar: new Set(['foo']), }; - const packageInfos: PackageInfos = { - foo: { - combinedOptions: {} as any, - name: 'foo', - packageJsonPath: 'packages/foo/package.json', - packageOptions: {}, - private: false, - version: '1.0.0', - dependencies: { - bar: '^1.0.0', - }, - }, - bar: { - combinedOptions: {} as any, - name: 'bar', - packageJsonPath: 'packages/bar/package.json', - packageOptions: {}, - private: false, - version: '1.0.0', + const packageInfos = makePackageInfos({ + foo: { version: '1.0.0' }, + bar: { version: '2.0.0', dependencies: { foo: '^1.0.0' } }, + }); + + const changelogs = getPackageChangelogs({ + changeFileChangeInfos, + calculatedChangeTypes: { foo: 'patch', bar: 'patch' }, + dependentChangedBy, + packageInfos, + cwd: '.', + }); + + expect(Object.keys(changelogs.foo.comments.patch!)).toHaveLength(1); + expect(changelogs.bar).toEqual({ + comments: { + patch: [ + { + author: 'beachball', + package: 'bar', + comment: 'Bump foo to v1.0.0', + // IMPORTANT: this should not record an actual commit hash, because it will be incorrect + commit: 'not available', + }, + ], }, + date: expect.any(Date), + name: 'bar', + tag: 'bar_v2.0.0', + version: '2.0.0', + }); + expect(Object.keys(changelogs.bar.comments.patch!)).toHaveLength(1); + }); + + it('records multiple comment entries when a package has a change file AND was part of a dependent bump', () => { + const changeFileChangeInfos: ChangeSet = [makeChangeInfo('foo'), makeChangeInfo('bar')]; + + const dependentChangedBy: BumpInfo['dependentChangedBy'] = { + bar: new Set(['foo']), }; - const changelogs = getPackageChangelogs( + const packageInfos = makePackageInfos({ + foo: { version: '1.0.0' }, + bar: { version: '2.0.0', dependencies: { foo: '^1.0.0' } }, + }); + + const changelogs = getPackageChangelogs({ changeFileChangeInfos, - { foo: 'patch', bar: 'patch' }, + calculatedChangeTypes: { foo: 'patch', bar: 'patch' }, dependentChangedBy, packageInfos, - '.' - ); + cwd: '.', + }); - expect(Object.keys(changelogs.bar.comments.patch!)).toHaveLength(2); - expect(Object.keys(changelogs.foo.comments.patch!)).toHaveLength(1); + expect(changelogs.bar.comments).toEqual({ + patch: [ + expect.objectContaining({ comment: 'comment for bar' }), + expect.objectContaining({ comment: 'Bump foo to v1.0.0' }), + ], + }); + expect(changelogs.foo.comments).toEqual({ + patch: [expect.objectContaining({ comment: 'comment for foo' })], + }); }); - it('should not generate change logs for dependent bumps of private packages', () => { - const changeFileChangeInfos: ChangeSet = [ - { - changeFile: 'bar.json', - change: { - comment: 'comment for bar', - commit: 'deadbeef', - dependentChangeType: 'patch', - email: 'something@something.com', - packageName: 'bar', - type: 'patch', - }, - }, - ]; + it('does not generate changelogs for dependent bumps of private packages', () => { + const changeFileChangeInfos: ChangeSet = [makeChangeInfo('bar')]; const dependentChangedBy: BumpInfo['dependentChangedBy'] = { 'private-pkg': new Set(['bar']), }; - const packageInfos: PackageInfos = { + const packageInfos = makePackageInfos({ 'private-pkg': { - combinedOptions: {} as any, - name: 'private-pkg', - packageJsonPath: 'packages/private-pkg/package.json', - packageOptions: {}, - private: true, - version: '1.0.0', - dependencies: { - bar: '^1.0.0', - }, - }, - bar: { - combinedOptions: {} as any, - name: 'bar', - packageJsonPath: 'packages/bar/package.json', - packageOptions: {}, - private: false, version: '1.0.0', + private: true, + dependencies: { bar: '^1.0.0' }, }, - }; + bar: { version: '1.0.0' }, + }); - const changelogs = getPackageChangelogs( + const changelogs = getPackageChangelogs({ changeFileChangeInfos, - { bar: 'patch', 'private-pkg': 'patch' }, + calculatedChangeTypes: { bar: 'patch', 'private-pkg': 'patch' }, dependentChangedBy, packageInfos, - '.' - ); + cwd: '.', + }); + expect(changelogs.bar).toBeTruthy(); expect(changelogs['private-pkg']).toBeUndefined(); }); }); diff --git a/src/__tests__/changelog/renderChangelog.test.ts b/src/__tests__/changelog/renderChangelog.test.ts index f35bbddcf..1d0158564 100644 --- a/src/__tests__/changelog/renderChangelog.test.ts +++ b/src/__tests__/changelog/renderChangelog.test.ts @@ -1,6 +1,7 @@ -import { describe, expect, it } from '@jest/globals'; +import { describe, expect, it, jest } from '@jest/globals'; import { initMockLogs } from '../../__fixtures__/mockLogs'; import { MarkdownChangelogRenderOptions, renderChangelog, markerComment } from '../../changelog/renderChangelog'; +import { ChangelogEntry } from '../../types/ChangeLog'; const previousHeader = `# Change Log - foo @@ -25,18 +26,8 @@ describe('renderChangelog', () => { comments: { major: [], minor: [ - { - comment: 'Awesome change', - author: 'user1@example.com', - commit: 'sha1', - package: 'foo', - }, - { - comment: 'Boring change', - author: 'user2@example.com', - commit: 'sha2', - package: 'foo', - }, + { comment: 'Awesome change', author: 'user1@example.com', commit: 'sha1', package: 'foo' }, + { comment: 'Boring change', author: 'user2@example.com', commit: 'sha2', package: 'foo' }, ], patch: [ { comment: 'Fix', author: 'user1@example.com', commit: 'sha3', package: 'foo' }, @@ -116,4 +107,29 @@ describe('renderChangelog', () => { expect(result).toContain('content here'); // includes previous content expect(result).toMatchSnapshot(); }); + + it('passes custom change file properties to renderers', async () => { + const options = getOptions(); + options.newVersionChangelog.comments = { + patch: [ + { + comment: 'Awesome change', + author: 'user1@example.com', + commit: 'sha1', + package: 'foo', + extra: 'custom', + }, + ], + }; + options.changelogOptions.customRenderers = { + renderEntry: jest.fn(async (entry: ChangelogEntry) => `- ${entry.comment} ${entry.extra})`), + }; + + const result = await renderChangelog(options); + expect(result).toContain('Awesome change custom'); + expect(options.changelogOptions.customRenderers.renderEntry).toHaveBeenCalledWith( + expect.objectContaining({ extra: 'custom' }), + expect.anything() + ); + }); }); diff --git a/src/__tests__/changelog/renderJsonChangelog.test.ts b/src/__tests__/changelog/renderJsonChangelog.test.ts new file mode 100644 index 000000000..af1c57325 --- /dev/null +++ b/src/__tests__/changelog/renderJsonChangelog.test.ts @@ -0,0 +1,63 @@ +import { describe, expect, it } from '@jest/globals'; +import { ChangelogJson, PackageChangelog } from '../..'; +import { renderJsonChangelog } from '../../changelog/renderJsonChangelog'; + +describe('renderJsonChangelog', () => { + function getChangelog(): PackageChangelog { + return { + date: new Date('Thu Aug 22 2019 14:20:40 GMT-0700 (Pacific Daylight Time)'), + name: 'foo', + tag: 'foo_v1.2.3', + version: '1.2.3', + comments: { + minor: [ + { comment: 'Awesome change', author: 'user1@example.com', commit: 'sha1', package: 'foo' }, + { comment: 'Boring change', author: 'user2@example.com', commit: 'sha2', package: 'foo' }, + ], + patch: [ + { comment: 'Fix', author: 'user1@example.com', commit: 'sha3', package: 'foo' }, + { comment: 'stuff', author: 'user2@example.com', commit: 'sha4', package: 'foo' }, + ], + }, + }; + } + + it('renders if no previous changelog', () => { + const changelog = getChangelog(); + const { name, ...rest } = changelog; + + const finalChangeLog = renderJsonChangelog(changelog, undefined); + expect(finalChangeLog).toEqual({ + name, + entries: [ + { + ...rest, + date: 'Thu, 22 Aug 2019 21:20:40 GMT', + }, + ], + }); + }); + + it('preserves previous entries', () => { + const changelog = getChangelog(); + const previousChangelog: ChangelogJson = { + name: 'foo', + entries: [ + { + date: 'Thu, 21 Aug 2019 20:20:40 GMT', + version: '1.2.2', + tag: 'foo_v1.2.2', + comments: { + patch: [{ comment: 'Fix', author: 'user1@example.com', commit: 'sha3', package: 'foo' }], + }, + }, + ], + }; + + const finalChangeLog = renderJsonChangelog(changelog, previousChangelog); + expect(finalChangeLog).toEqual({ + name: 'foo', + entries: [expect.objectContaining({ version: '1.2.3' }), expect.objectContaining({ version: '1.2.2' })], + }); + }); +}); diff --git a/src/__tests__/changelog/renderPackageChangelog.test.ts b/src/__tests__/changelog/renderPackageChangelog.test.ts index 3243a35a1..7a840dbd3 100644 --- a/src/__tests__/changelog/renderPackageChangelog.test.ts +++ b/src/__tests__/changelog/renderPackageChangelog.test.ts @@ -19,18 +19,8 @@ describe('changelog renderers -', () => { comments: { major: [], minor: [ - { - comment: 'Awesome change', - author: 'user1@example.com', - commit: 'sha1', - package: 'foo', - }, - { - comment: 'Boring change', - author: 'user2@example.com', - commit: 'sha2', - package: 'foo', - }, + { comment: 'Awesome change', author: 'user1@example.com', commit: 'sha1', package: 'foo' }, + { comment: 'Boring change', author: 'user2@example.com', commit: 'sha2', package: 'foo' }, ], patch: [ { comment: 'Fix', author: 'user1@example.com', commit: 'sha3', package: 'foo' }, diff --git a/src/changelog/getPackageChangelogs.ts b/src/changelog/getPackageChangelogs.ts index d986ae3df..f2595aa3a 100644 --- a/src/changelog/getPackageChangelogs.ts +++ b/src/changelog/getPackageChangelogs.ts @@ -1,35 +1,40 @@ import path from 'path'; -import { PackageInfo } from '../types/PackageInfo'; +import { PackageInfo, PackageInfos } from '../types/PackageInfo'; import { PackageChangelog } from '../types/ChangeLog'; import { generateTag } from '../git/generateTag'; import { BumpInfo } from '../types/BumpInfo'; import { getChangePath } from '../paths'; -import { getCurrentHash, getFileAddedHash } from 'workspace-tools'; +import { getFileAddedHash } from 'workspace-tools'; import { ChangeSet } from '../types/ChangeInfo'; -export function getPackageChangelogs( - changeFileChangeInfos: ChangeSet, - calculatedChangeTypes: BumpInfo['calculatedChangeTypes'], - dependentChangedBy: BumpInfo['dependentChangedBy'], - packageInfos: { - [pkg: string]: PackageInfo; - }, - cwd: string -): { [pkgName: string]: PackageChangelog } { - const changelogs: { [pkgName: string]: PackageChangelog } = {}; +/** + * Used for `ChangelogEntry.commit` if the commit hash is not available. + */ +const commitNotAvailable = 'not available'; + +/** + * Get the preliminary changelog info for each modified package, based on change files and dependent bumps. + * @returns Mapping from package name to package changelog. + */ +export function getPackageChangelogs(params: { + changeFileChangeInfos: ChangeSet; + calculatedChangeTypes: BumpInfo['calculatedChangeTypes']; + dependentChangedBy?: BumpInfo['dependentChangedBy']; + packageInfos: PackageInfos; + cwd: string; +}): Record { + const { changeFileChangeInfos, calculatedChangeTypes, dependentChangedBy = {}, packageInfos, cwd } = params; + + const changelogs: Record = {}; const changeFileCommits: { [changeFile: string]: string } = {}; const changePath = getChangePath(cwd); - for (let { change, changeFile } of changeFileChangeInfos) { + for (const { change, changeFile } of changeFileChangeInfos) { const { packageName, type: changeType, dependentChangeType, email, ...rest } = change; - if (!changelogs[packageName]) { - changelogs[packageName] = createChangeLog(packageInfos[packageName]); - } + changelogs[packageName] ??= createPackageChangelog(packageInfos[packageName]); - if (!changeFileCommits[changeFile]) { - changeFileCommits[changeFile] = getFileAddedHash(path.join(changePath, changeFile), cwd) || 'not available'; - } + changeFileCommits[changeFile] ??= getFileAddedHash(path.join(changePath, changeFile), cwd) || commitNotAvailable; changelogs[packageName].comments ??= {}; changelogs[packageName].comments[changeType] ??= []; @@ -43,18 +48,14 @@ export function getPackageChangelogs( }); } - const commit = getCurrentHash(cwd) || 'not available'; - - for (let [dependent, changedBy] of Object.entries(dependentChangedBy)) { + for (const [dependent, changedBy] of Object.entries(dependentChangedBy)) { if (packageInfos[dependent].private === true) { // Avoid creation of change log files for private packages since the version is // not managed by beachball and the log would only contain bumps to dependencies. continue; } - if (!changelogs[dependent]) { - changelogs[dependent] = createChangeLog(packageInfos[dependent]); - } + changelogs[dependent] ??= createPackageChangelog(packageInfos[dependent]); const changeType = calculatedChangeTypes[dependent]; @@ -67,7 +68,11 @@ export function getPackageChangelogs( author: 'beachball', package: dependent, comment: `Bump ${dep} to v${packageInfos[dep].version}`, - commit, + // This change will be made in the commit that is currently being created, so unless we + // split publishing into two commits (one for bumps and one for changelog updates), + // there's no way to know the hash yet. It's better to record nothing than incorrect info. + // https://github.com/microsoft/beachball/issues/901 + commit: commitNotAvailable, }); } } @@ -76,7 +81,7 @@ export function getPackageChangelogs( return changelogs; } -function createChangeLog(packageInfo: PackageInfo): PackageChangelog { +function createPackageChangelog(packageInfo: PackageInfo): PackageChangelog { const name = packageInfo.name; const version = packageInfo.version; return { diff --git a/src/changelog/renderJsonChangelog.ts b/src/changelog/renderJsonChangelog.ts index 9597e5982..d49af2278 100644 --- a/src/changelog/renderJsonChangelog.ts +++ b/src/changelog/renderJsonChangelog.ts @@ -1,20 +1,18 @@ -import { generateTag } from '../git/generateTag'; -import { PackageChangelog, ChangelogJson, ChangelogJsonEntry } from '../types/ChangeLog'; +import { PackageChangelog, ChangelogJson } from '../types/ChangeLog'; export function renderJsonChangelog( changelog: PackageChangelog, previousChangelog: ChangelogJson | undefined ): ChangelogJson { - const result: ChangelogJson = { - name: changelog.name, - entries: previousChangelog?.entries ? [...previousChangelog.entries] : [], + const { name, date, ...rest } = changelog; + return { + name, + entries: [ + { + date: changelog.date.toUTCString(), + ...rest, + }, + ...(previousChangelog?.entries || []), + ], }; - const newEntry: ChangelogJsonEntry = { - date: changelog.date.toUTCString(), - tag: generateTag(changelog.name, changelog.version), - version: changelog.version, - comments: changelog.comments, - }; - result.entries.unshift(newEntry); - return result; } diff --git a/src/changelog/writeChangelog.ts b/src/changelog/writeChangelog.ts index c4c7de4bd..93c572488 100644 --- a/src/changelog/writeChangelog.ts +++ b/src/changelog/writeChangelog.ts @@ -27,13 +27,13 @@ export async function writeChangelog( ); const groupedChangelogPathSet = new Set(groupedChangelogPaths); - const changelogs = getPackageChangelogs( + const changelogs = getPackageChangelogs({ changeFileChangeInfos, calculatedChangeTypes, dependentChangedBy, packageInfos, - options.path - ); + cwd: options.path, + }); // Use a standard for loop here to prevent potentially firing off multiple network requests at once // (in case any custom renderers have network requests) for (const pkg of Object.keys(changelogs)) { @@ -62,7 +62,12 @@ async function writeGroupedChangelog( } // Grouped changelogs should not contain dependency bump entries - const changelogs = getPackageChangelogs(changeFileChangeInfos, calculatedChangeTypes, {}, packageInfos, options.path); + const changelogs = getPackageChangelogs({ + changeFileChangeInfos, + calculatedChangeTypes, + packageInfos, + cwd: options.path, + }); const groupedChangelogs: { [path: string]: { changelogs: PackageChangelog[]; diff --git a/src/types/ChangeInfo.ts b/src/types/ChangeInfo.ts index 2e8c296f2..0cfc3dcda 100644 --- a/src/types/ChangeInfo.ts +++ b/src/types/ChangeInfo.ts @@ -2,12 +2,17 @@ export type ChangeType = 'prerelease' | 'patch' | 'minor' | 'major' | 'none'; /** * Info saved in each change file. + * (For entries in CHANGELOG.json, see `ChangelogEntry` in ./ChangeLog.ts.) */ export interface ChangeFileInfo { type: ChangeType; + /** Change comment */ comment: string; + /** Package name the change was in */ packageName: string; + /** Author email */ email: string; + /** How to bump packages that depend on this one */ dependentChangeType: ChangeType; /** Extra info added to the change file via custom prompts */ [extraInfo: string]: any; @@ -20,11 +25,14 @@ export interface ChangeInfo extends ChangeFileInfo { commit: string; } +/** + * Info saved in each grouped change file. + */ export interface ChangeInfoMultiple { changes: ChangeInfo[]; } /** - * List of change file infos + * List of change file infos (not actually a set). */ export type ChangeSet = { changeFile: string; change: ChangeFileInfo }[]; diff --git a/src/types/ChangeLog.ts b/src/types/ChangeLog.ts index bce25bc0e..3fb7a4aaf 100644 --- a/src/types/ChangeLog.ts +++ b/src/types/ChangeLog.ts @@ -5,20 +5,37 @@ import { ChangeType } from './ChangeInfo'; +/** + * Entry ("comment") in CHANGELOG.json from a change file or dependent bump. + * These objects are saved under the `ChangelogJson`'s `entries[].comments[type]`. + * + * (This is based on an individual `ChangeFileInfo` from ./ChangeInfo.ts, but with some different + * naming and details.) + */ export interface ChangelogEntry { /** Change comment */ comment: string; /** Author email */ author: string; - /** Commit hash */ + /** + * Commit hash. + * + * For changelogs generated by beachball versions \>=2.36.0, should be `"not available"` for + * bump entries, because the correct commit doesn't exist yet (see [issue](https://github.com/microsoft/beachball/issues/901)). + * + * Could also be `"not available"` for other commits if there was an issue determing the hash + * at changelog generation time. + */ commit: string; /** Package name the change was in */ package: string; + /** Extra info added to the change file via custom prompts */ + [extraInfo: string]: any; } /** - * Changelog info for an individual version. Usually this is for a single package. - * If using grouped changelogs, it could be for multiple packages. + * Intermediate info used to generate a CHANGELOG.json entry for an individual version. + * Usually this is for a single package. If using grouped changelogs, it could be for multiple packages. */ export interface PackageChangelog { /** Package name (if a grouped changelog, for the primary package) */ @@ -34,15 +51,20 @@ export interface PackageChangelog { } /** - * CHANGELOG.json entry for an individual version. Usually this is for a single package. - * If using grouped changelogs, it could be for multiple packages. + * CHANGELOG.json entry for an individual version (under `ChangelogJson`'s `entries`). + * Usually this is for a single package. If using grouped changelogs, it could be for multiple packages. */ export type ChangelogJsonEntry = Omit & { /** Version creation date as a string */ date: string; }; +/** + * CHANGELOG.json file contents. + */ export interface ChangelogJson { + /** Package name */ name: string; + /** Entries for each package version */ entries: ChangelogJsonEntry[]; }