зеркало из https://github.com/microsoft/beachball.git
Prevent accidentally writing dep bumps to grouped changelog (and test improvements) (#984)
This commit is contained in:
Родитель
eade30dbb4
Коммит
ff4f5da25e
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"type": "patch",
|
||||
"comment": "Prevent accidentally writing dep bumps to grouped changelog",
|
||||
"packageName": "beachball",
|
||||
"email": "elcraig@microsoft.com",
|
||||
"dependentChangeType": "patch"
|
||||
}
|
|
@ -2,29 +2,51 @@ import fs from 'fs';
|
|||
import path from 'path';
|
||||
import { writeChangeFiles } from '../changefile/writeChangeFiles';
|
||||
import { getChangePath } from '../paths';
|
||||
import { ChangeFileInfo } from '../types/ChangeInfo';
|
||||
import { ChangeFileInfo, ChangeType } from '../types/ChangeInfo';
|
||||
import type { BeachballOptions } from '../types/BeachballOptions';
|
||||
|
||||
/** Change file with `packageName` required and other props optional */
|
||||
export type PartialChangeFile = { packageName: string } & Partial<ChangeFileInfo>;
|
||||
|
||||
/** Placeholder email/author */
|
||||
export const fakeEmail = 'test@test.com';
|
||||
|
||||
/**
|
||||
* Generate a change file for the given package.
|
||||
*/
|
||||
export function getChange(
|
||||
packageName: string,
|
||||
comment: string = `${packageName} comment`,
|
||||
type: ChangeType = 'minor'
|
||||
): ChangeFileInfo {
|
||||
return {
|
||||
comment,
|
||||
email: fakeEmail,
|
||||
packageName,
|
||||
type,
|
||||
dependentChangeType: 'patch',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates and writes change files for the given packages.
|
||||
* Also commits if `options.commit` is true.
|
||||
* @param changes Array of package names or partial change files (which must include `packageName`).
|
||||
* Default values are `type: 'minor'`, `dependentChangeType: 'patch'`, and placeholders for other fields.
|
||||
* Default values:
|
||||
* - `type: 'minor'`
|
||||
* - `dependentChangeType: 'patch'`
|
||||
* - `comment: '<packageName> comment'`
|
||||
* - `email: 'test@test.com'`
|
||||
*/
|
||||
export function generateChangeFiles(
|
||||
changes: (string | PartialChangeFile)[],
|
||||
options: Pick<BeachballOptions, 'path' | 'groupChanges' | 'changeDir'>
|
||||
options: Parameters<typeof writeChangeFiles>[1]
|
||||
): void {
|
||||
writeChangeFiles(
|
||||
changes.map(change => {
|
||||
change = typeof change === 'string' ? { packageName: change } : change;
|
||||
return {
|
||||
comment: `${change.packageName} test comment`,
|
||||
email: 'test@test.com',
|
||||
type: 'minor',
|
||||
dependentChangeType: 'patch',
|
||||
...getChange(change.packageName, undefined, 'minor'),
|
||||
...change,
|
||||
};
|
||||
}),
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
import _ from 'lodash';
|
||||
import { SortedChangeTypes } from '../changefile/changeTypes';
|
||||
import { ChangelogJson } from '../types/ChangeLog';
|
||||
import { markerComment } from '../changelog/renderChangelog';
|
||||
|
||||
/** Placeholder commit as replaced by cleanChangelogJson */
|
||||
export const fakeCommit = '(sha1)';
|
||||
|
||||
/**
|
||||
* Read the CHANGELOG.md under the given package path, sanitizing any dates for snapshots.
|
||||
|
@ -17,6 +20,11 @@ export function readChangelogMd(packagePath: string): string | null {
|
|||
return text.replace(/\w\w\w, \d\d \w\w\w [\d :]+?GMT/gm, '(date)');
|
||||
}
|
||||
|
||||
/** Get only the part of CHANGELOG.md after the marker comment. */
|
||||
export function trimChangelogMd(changelogMd: string): string {
|
||||
return changelogMd.split(markerComment)[1].trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the CHANGELOG.json under the given package path.
|
||||
* Returns null if it doesn't exist.
|
||||
|
@ -39,19 +47,17 @@ export function cleanChangelogJson(changelog: ChangelogJson | null): ChangelogJs
|
|||
return null;
|
||||
}
|
||||
changelog = _.cloneDeep(changelog);
|
||||
// for a better snapshot, make the fake commit match if the real commit did
|
||||
const fakeCommits: { [commit: string]: string } = {};
|
||||
let fakeHashNum = 0;
|
||||
|
||||
for (const entry of changelog.entries) {
|
||||
entry.date = '(date)';
|
||||
for (const changeType of SortedChangeTypes) {
|
||||
if (entry.comments[changeType]) {
|
||||
for (const comment of entry.comments[changeType]!) {
|
||||
if (!fakeCommits[comment.commit]) {
|
||||
fakeCommits[comment.commit] = `(sha1-${fakeHashNum++})`;
|
||||
}
|
||||
comment.commit = fakeCommits[comment.commit];
|
||||
// Only replace properties if they existed, to help catch bugs if things are no longer written
|
||||
if (entry.date) {
|
||||
entry.date = '(date)';
|
||||
}
|
||||
|
||||
for (const comments of Object.values(entry.comments)) {
|
||||
for (const comment of comments!) {
|
||||
if (comment.commit) {
|
||||
comment.commit = fakeCommit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,13 @@ import path from 'path';
|
|||
import * as fs from 'fs-extra';
|
||||
import { tmpdir } from './tmpdir';
|
||||
import { git } from 'workspace-tools';
|
||||
import { defaultBranchName, defaultRemoteName, optsWithLang, setDefaultBranchName } from './gitDefaults';
|
||||
import {
|
||||
defaultBranchName,
|
||||
defaultRemoteBranchName,
|
||||
defaultRemoteName,
|
||||
optsWithLang,
|
||||
setDefaultBranchName,
|
||||
} from './gitDefaults';
|
||||
import { env } from '../env';
|
||||
|
||||
/**
|
||||
|
@ -204,6 +210,12 @@ ${gitResult.stderr.toString()}`);
|
|||
this.git(['push', defaultRemoteName, `HEAD:${branchName}`]);
|
||||
}
|
||||
|
||||
/** `git reset --hard <ref>` and `git clean -dfx` */
|
||||
resetAndClean(ref: string = defaultRemoteBranchName) {
|
||||
this.git(['reset', '--hard', ref]);
|
||||
this.git(['clean', '-dfx']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clean up the repo IF this is a local build.
|
||||
*
|
||||
|
|
|
@ -15,6 +15,8 @@ describe('readChangeFiles', () => {
|
|||
let repositoryFactory: RepositoryFactory;
|
||||
let monoRepoFactory: RepositoryFactory;
|
||||
let repo: Repository | undefined;
|
||||
let sharedSingleRepo: Repository;
|
||||
let sharedMonoRepo: Repository;
|
||||
|
||||
const logs = initMockLogs();
|
||||
|
||||
|
@ -28,13 +30,17 @@ describe('readChangeFiles', () => {
|
|||
}
|
||||
|
||||
beforeAll(() => {
|
||||
// These tests can share the same repo factories because they don't push to origin
|
||||
// (the actual tests run against a clone)
|
||||
// These tests can share the same factories and repos because they don't push to the remote,
|
||||
// and the repo used is reset after each test (which is faster than making new clones).
|
||||
repositoryFactory = new RepositoryFactory('single');
|
||||
monoRepoFactory = new RepositoryFactory('monorepo');
|
||||
sharedSingleRepo = repositoryFactory.cloneRepository();
|
||||
sharedMonoRepo = monoRepoFactory.cloneRepository();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// Revert whichever shared repo was used to the original state
|
||||
repo?.resetAndClean();
|
||||
repo = undefined;
|
||||
});
|
||||
|
||||
|
@ -44,7 +50,7 @@ describe('readChangeFiles', () => {
|
|||
});
|
||||
|
||||
it('does not add commit hash', () => {
|
||||
repo = repositoryFactory.cloneRepository();
|
||||
repo = sharedSingleRepo;
|
||||
repo.commitChange('foo');
|
||||
|
||||
const options = getOptions();
|
||||
|
@ -57,7 +63,7 @@ describe('readChangeFiles', () => {
|
|||
});
|
||||
|
||||
it('reads from a custom changeDir', () => {
|
||||
repo = repositoryFactory.cloneRepository();
|
||||
repo = sharedSingleRepo;
|
||||
repo.commitChange('foo');
|
||||
|
||||
const options = getOptions({ changeDir: 'changeDir' });
|
||||
|
@ -69,7 +75,7 @@ describe('readChangeFiles', () => {
|
|||
});
|
||||
|
||||
it('excludes invalid change files', () => {
|
||||
repo = monoRepoFactory.cloneRepository();
|
||||
repo = sharedMonoRepo;
|
||||
repo.updateJsonFile('packages/bar/package.json', { private: true });
|
||||
const options = getOptions();
|
||||
|
||||
|
@ -87,7 +93,7 @@ describe('readChangeFiles', () => {
|
|||
});
|
||||
|
||||
it('excludes invalid changes from grouped change file in monorepo', () => {
|
||||
repo = monoRepoFactory.cloneRepository();
|
||||
repo = sharedMonoRepo;
|
||||
repo.updateJsonFile('packages/bar/package.json', { private: true });
|
||||
|
||||
const options = getOptions({ groupChanges: true });
|
||||
|
@ -106,7 +112,7 @@ describe('readChangeFiles', () => {
|
|||
});
|
||||
|
||||
it('excludes out of scope change files in monorepo', () => {
|
||||
repo = monoRepoFactory.cloneRepository();
|
||||
repo = sharedMonoRepo;
|
||||
|
||||
const options = getOptions({ scope: ['packages/foo'] });
|
||||
|
||||
|
@ -119,7 +125,7 @@ describe('readChangeFiles', () => {
|
|||
});
|
||||
|
||||
it('excludes out of scope changes from grouped change file in monorepo', () => {
|
||||
repo = monoRepoFactory.cloneRepository();
|
||||
repo = sharedMonoRepo;
|
||||
|
||||
const options = getOptions({ scope: ['packages/foo'], groupChanges: true });
|
||||
|
||||
|
@ -133,7 +139,7 @@ describe('readChangeFiles', () => {
|
|||
|
||||
it('runs transform.changeFiles functions if provided', async () => {
|
||||
const editedComment: string = 'Edited comment for testing';
|
||||
repo = monoRepoFactory.cloneRepository();
|
||||
repo = sharedMonoRepo;
|
||||
|
||||
const options = getOptions({
|
||||
command: 'change',
|
||||
|
|
|
@ -23,7 +23,7 @@ function cleanChangeFilePaths(root: string, changeFiles: string[]) {
|
|||
|
||||
describe('writeChangeFiles', () => {
|
||||
let monorepoFactory: RepositoryFactory;
|
||||
let repo: Repository | undefined;
|
||||
let repo: Repository;
|
||||
|
||||
initMockLogs();
|
||||
|
||||
|
@ -40,10 +40,11 @@ describe('writeChangeFiles', () => {
|
|||
// These tests can share the same repo factories because they don't push to origin
|
||||
// (the actual tests run against a clone)
|
||||
monorepoFactory = new RepositoryFactory('monorepo');
|
||||
repo = monorepoFactory.cloneRepository();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
repo = undefined;
|
||||
repo?.resetAndClean();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
|
@ -51,7 +52,6 @@ describe('writeChangeFiles', () => {
|
|||
});
|
||||
|
||||
it('writes individual change files', () => {
|
||||
repo = monorepoFactory.cloneRepository();
|
||||
const previousHead = repo.getCurrentHash();
|
||||
const options = getOptions();
|
||||
|
||||
|
@ -76,8 +76,6 @@ describe('writeChangeFiles', () => {
|
|||
});
|
||||
|
||||
it('respects changeDir option', () => {
|
||||
repo = monorepoFactory.cloneRepository();
|
||||
|
||||
const testChangeDir = 'myChangeDir';
|
||||
const options = getOptions({ changeDir: testChangeDir });
|
||||
|
||||
|
@ -95,7 +93,6 @@ describe('writeChangeFiles', () => {
|
|||
});
|
||||
|
||||
it('respects commit=false', () => {
|
||||
repo = monorepoFactory.cloneRepository();
|
||||
const previousHead = repo.getCurrentHash();
|
||||
|
||||
const options = getOptions({ commit: false });
|
||||
|
@ -117,8 +114,6 @@ describe('writeChangeFiles', () => {
|
|||
});
|
||||
|
||||
it('writes grouped change files', () => {
|
||||
repo = monorepoFactory.cloneRepository();
|
||||
|
||||
const options = getOptions({
|
||||
groupChanges: true,
|
||||
});
|
||||
|
|
|
@ -1,87 +1,6 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`writeChangelog generates correct changelog in monorepo with groupChanges (grouped change FILES): bar CHANGELOG.json 1`] = `
|
||||
{
|
||||
"entries": [
|
||||
{
|
||||
"comments": {
|
||||
"patch": [
|
||||
{
|
||||
"author": "test@testtestme.com",
|
||||
"comment": "comment from bar change ",
|
||||
"commit": "(sha1-0)",
|
||||
"package": "bar",
|
||||
},
|
||||
],
|
||||
},
|
||||
"date": "(date)",
|
||||
"tag": "bar_v1.3.4",
|
||||
"version": "1.3.4",
|
||||
},
|
||||
],
|
||||
"name": "bar",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`writeChangelog generates correct changelog in monorepo with groupChanges (grouped change FILES): bar CHANGELOG.md 1`] = `
|
||||
"# Change Log - bar
|
||||
|
||||
<!-- This log was last generated on (date) and should not be manually modified. -->
|
||||
|
||||
<!-- Start content -->
|
||||
|
||||
## 1.3.4
|
||||
|
||||
(date)
|
||||
|
||||
### Patches
|
||||
|
||||
- comment from bar change (test@testtestme.com)
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`writeChangelog generates correct changelog in monorepo with groupChanges (grouped change FILES): foo CHANGELOG.json 1`] = `
|
||||
{
|
||||
"entries": [
|
||||
{
|
||||
"comments": {
|
||||
"patch": [
|
||||
{
|
||||
"author": "test@testtestme.com",
|
||||
"comment": "comment 2",
|
||||
"commit": "(sha1-0)",
|
||||
"package": "foo",
|
||||
},
|
||||
{
|
||||
"author": "test@testtestme.com",
|
||||
"comment": "comment 1",
|
||||
"commit": "(sha1-1)",
|
||||
"package": "foo",
|
||||
},
|
||||
{
|
||||
"author": "test@testtestme.com",
|
||||
"comment": "additional comment 1",
|
||||
"commit": "(sha1-2)",
|
||||
"package": "foo",
|
||||
},
|
||||
{
|
||||
"author": "test@testtestme.com",
|
||||
"comment": "additional comment 2",
|
||||
"commit": "(sha1-3)",
|
||||
"package": "foo",
|
||||
},
|
||||
],
|
||||
},
|
||||
"date": "(date)",
|
||||
"tag": "foo_v1.0.0",
|
||||
"version": "1.0.0",
|
||||
},
|
||||
],
|
||||
"name": "foo",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`writeChangelog generates correct changelog in monorepo with groupChanges (grouped change FILES): foo CHANGELOG.md 1`] = `
|
||||
exports[`writeChangelog generates basic changelog: changelog md 1`] = `
|
||||
"# Change Log - foo
|
||||
|
||||
<!-- This log was last generated on (date) and should not be manually modified. -->
|
||||
|
@ -92,286 +11,18 @@ exports[`writeChangelog generates correct changelog in monorepo with groupChange
|
|||
|
||||
(date)
|
||||
|
||||
### Patches
|
||||
### Minor changes
|
||||
|
||||
- comment 2 (test@testtestme.com)
|
||||
- comment 1 (test@testtestme.com)
|
||||
- additional comment 1 (test@testtestme.com)
|
||||
- additional comment 2 (test@testtestme.com)
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`writeChangelog generates correct changelog with changeDir set: changelog json 1`] = `
|
||||
{
|
||||
"entries": [
|
||||
{
|
||||
"comments": {
|
||||
"patch": [
|
||||
{
|
||||
"author": "test@testtestme.com",
|
||||
"comment": "comment 2",
|
||||
"commit": "(sha1-0)",
|
||||
"package": "foo",
|
||||
},
|
||||
{
|
||||
"author": "test@testtestme.com",
|
||||
"comment": "comment 1",
|
||||
"commit": "(sha1-1)",
|
||||
"package": "foo",
|
||||
},
|
||||
{
|
||||
"author": "test@testtestme.com",
|
||||
"comment": "additional comment 1",
|
||||
"commit": "(sha1-2)",
|
||||
"package": "foo",
|
||||
},
|
||||
{
|
||||
"author": "test@testtestme.com",
|
||||
"comment": "additional comment 2",
|
||||
"commit": "(sha1-3)",
|
||||
"package": "foo",
|
||||
},
|
||||
],
|
||||
},
|
||||
"date": "(date)",
|
||||
"tag": "foo_v1.0.0",
|
||||
"version": "1.0.0",
|
||||
},
|
||||
],
|
||||
"name": "foo",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`writeChangelog generates correct changelog with changeDir set: changelog md 1`] = `
|
||||
"# Change Log - foo
|
||||
|
||||
<!-- This log was last generated on (date) and should not be manually modified. -->
|
||||
|
||||
<!-- Start content -->
|
||||
|
||||
## 1.0.0
|
||||
|
||||
(date)
|
||||
- new minor comment (test@test.com)
|
||||
- old minor comment (test@test.com)
|
||||
|
||||
### Patches
|
||||
|
||||
- comment 2 (test@testtestme.com)
|
||||
- comment 1 (test@testtestme.com)
|
||||
- additional comment 1 (test@testtestme.com)
|
||||
- additional comment 2 (test@testtestme.com)
|
||||
- patch comment (test@test.com)
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`writeChangelog generates correct changelog: changelog json 1`] = `
|
||||
{
|
||||
"entries": [
|
||||
{
|
||||
"comments": {
|
||||
"patch": [
|
||||
{
|
||||
"author": "test@testtestme.com",
|
||||
"comment": "comment 2",
|
||||
"commit": "(sha1-0)",
|
||||
"package": "foo",
|
||||
},
|
||||
{
|
||||
"author": "test@testtestme.com",
|
||||
"comment": "comment 1",
|
||||
"commit": "(sha1-1)",
|
||||
"package": "foo",
|
||||
},
|
||||
{
|
||||
"author": "test@testtestme.com",
|
||||
"comment": "additional comment 1",
|
||||
"commit": "(sha1-2)",
|
||||
"package": "foo",
|
||||
},
|
||||
{
|
||||
"author": "test@testtestme.com",
|
||||
"comment": "additional comment 2",
|
||||
"commit": "(sha1-3)",
|
||||
"package": "foo",
|
||||
},
|
||||
],
|
||||
},
|
||||
"date": "(date)",
|
||||
"tag": "foo_v1.0.0",
|
||||
"version": "1.0.0",
|
||||
},
|
||||
],
|
||||
"name": "foo",
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`writeChangelog generates correct changelog: changelog md 1`] = `
|
||||
"# Change Log - foo
|
||||
|
||||
<!-- This log was last generated on (date) and should not be manually modified. -->
|
||||
|
||||
<!-- Start content -->
|
||||
|
||||
## 1.0.0
|
||||
|
||||
(date)
|
||||
|
||||
### Patches
|
||||
|
||||
- comment 2 (test@testtestme.com)
|
||||
- comment 1 (test@testtestme.com)
|
||||
- additional comment 1 (test@testtestme.com)
|
||||
- additional comment 2 (test@testtestme.com)
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`writeChangelog generates correct grouped changelog in monorepo: bar CHANGELOG.md 1`] = `
|
||||
"# Change Log - bar
|
||||
|
||||
<!-- This log was last generated on (date) and should not be manually modified. -->
|
||||
|
||||
<!-- Start content -->
|
||||
|
||||
## 1.3.4
|
||||
|
||||
(date)
|
||||
|
||||
### Patches
|
||||
|
||||
- comment 3 (test@testtestme.com)
|
||||
- comment 2 (test@testtestme.com)
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`writeChangelog generates correct grouped changelog in monorepo: foo CHANGELOG.md 1`] = `
|
||||
"# Change Log - foo
|
||||
|
||||
<!-- This log was last generated on (date) and should not be manually modified. -->
|
||||
|
||||
<!-- Start content -->
|
||||
|
||||
## 1.0.0
|
||||
|
||||
(date)
|
||||
|
||||
### Patches
|
||||
|
||||
- comment 1 (test@testtestme.com)
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`writeChangelog generates correct grouped changelog in monorepo: grouped CHANGELOG.md 1`] = `
|
||||
"# Change Log - foo
|
||||
|
||||
<!-- This log was last generated on (date) and should not be manually modified. -->
|
||||
|
||||
<!-- Start content -->
|
||||
|
||||
## 1.0.0
|
||||
|
||||
(date)
|
||||
|
||||
### Patches
|
||||
|
||||
- \`bar\`
|
||||
- comment 3 (test@testtestme.com)
|
||||
- comment 2 (test@testtestme.com)
|
||||
- \`foo\`
|
||||
- comment 1 (test@testtestme.com)
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`writeChangelog generates correct grouped changelog when grouped change log is saved to the same dir as a regular changelog 1`] = `
|
||||
"# Change Log - bar
|
||||
|
||||
<!-- This log was last generated on (date) and should not be manually modified. -->
|
||||
|
||||
<!-- Start content -->
|
||||
|
||||
## 1.3.4
|
||||
|
||||
(date)
|
||||
|
||||
### Patches
|
||||
|
||||
- comment 2 (test@testtestme.com)
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`writeChangelog generates correct grouped changelog when grouped change log is saved to the same dir as a regular changelog 2`] = `
|
||||
"# Change Log - foo
|
||||
|
||||
<!-- This log was last generated on (date) and should not be manually modified. -->
|
||||
|
||||
<!-- Start content -->
|
||||
|
||||
## 1.0.0
|
||||
|
||||
(date)
|
||||
|
||||
### Patches
|
||||
|
||||
- \`bar\`
|
||||
- comment 2 (test@testtestme.com)
|
||||
- \`foo\`
|
||||
- comment 1 (test@testtestme.com)
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`writeChangelog generates grouped changelog without dependent change entries where packages have normal changes and dependency changes: bar CHANGELOG.md 1`] = `
|
||||
"# Change Log - bar
|
||||
|
||||
<!-- This log was last generated on (date) and should not be manually modified. -->
|
||||
|
||||
<!-- Start content -->
|
||||
|
||||
## 1.3.4
|
||||
|
||||
(date)
|
||||
|
||||
### Patches
|
||||
|
||||
- comment 1 (test@testtestme.com)
|
||||
- Bump baz to v1.3.4
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`writeChangelog generates grouped changelog without dependent change entries where packages have normal changes and dependency changes: baz CHANGELOG.md 1`] = `
|
||||
"# Change Log - baz
|
||||
|
||||
<!-- This log was last generated on (date) and should not be manually modified. -->
|
||||
|
||||
<!-- Start content -->
|
||||
|
||||
## 1.3.4
|
||||
|
||||
(date)
|
||||
|
||||
### Patches
|
||||
|
||||
- comment 1 (test@testtestme.com)
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`writeChangelog generates grouped changelog without dependent change entries where packages have normal changes and dependency changes: grouped CHANGELOG.md 1`] = `
|
||||
"# Change Log - foo
|
||||
|
||||
<!-- This log was last generated on (date) and should not be manually modified. -->
|
||||
|
||||
<!-- Start content -->
|
||||
|
||||
## 1.0.0
|
||||
|
||||
(date)
|
||||
|
||||
### Patches
|
||||
|
||||
- \`bar\`
|
||||
- comment 1 (test@testtestme.com)
|
||||
- \`baz\`
|
||||
- comment 1 (test@testtestme.com)
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`writeChangelog generates grouped changelog without dependent change entries: bar CHANGELOG.md 1`] = `
|
||||
exports[`writeChangelog generates changelogs with dependent changes in monorepo: bar CHANGELOG.md 1`] = `
|
||||
"# Change Log - bar
|
||||
|
||||
<!-- This log was last generated on (date) and should not be manually modified. -->
|
||||
|
@ -388,7 +39,7 @@ exports[`writeChangelog generates grouped changelog without dependent change ent
|
|||
"
|
||||
`;
|
||||
|
||||
exports[`writeChangelog generates grouped changelog without dependent change entries: baz CHANGELOG.md 1`] = `
|
||||
exports[`writeChangelog generates changelogs with dependent changes in monorepo: baz CHANGELOG.md 1`] = `
|
||||
"# Change Log - baz
|
||||
|
||||
<!-- This log was last generated on (date) and should not be manually modified. -->
|
||||
|
@ -399,13 +50,13 @@ exports[`writeChangelog generates grouped changelog without dependent change ent
|
|||
|
||||
(date)
|
||||
|
||||
### Patches
|
||||
### Minor changes
|
||||
|
||||
- comment 1 (test@testtestme.com)
|
||||
- baz comment (test@test.com)
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`writeChangelog generates grouped changelog without dependent change entries: grouped CHANGELOG.md 1`] = `
|
||||
exports[`writeChangelog generates changelogs with dependent changes in monorepo: foo CHANGELOG.md 1`] = `
|
||||
"# Change Log - foo
|
||||
|
||||
<!-- This log was last generated on (date) and should not be manually modified. -->
|
||||
|
@ -416,9 +67,30 @@ exports[`writeChangelog generates grouped changelog without dependent change ent
|
|||
|
||||
(date)
|
||||
|
||||
### Patches
|
||||
### Minor changes
|
||||
|
||||
- \`baz\`
|
||||
- comment 1 (test@testtestme.com)
|
||||
- foo comment (test@test.com)
|
||||
- Bump bar to v1.3.4
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`writeChangelog generates grouped changelog in monorepo: grouped CHANGELOG.md 1`] = `
|
||||
"# Change Log - foo
|
||||
|
||||
<!-- This log was last generated on (date) and should not be manually modified. -->
|
||||
|
||||
<!-- Start content -->
|
||||
|
||||
## 1.0.0
|
||||
|
||||
(date)
|
||||
|
||||
### Minor changes
|
||||
|
||||
- \`foo\`
|
||||
- foo comment 2 (test@test.com)
|
||||
- foo comment (test@test.com)
|
||||
- \`baz\`
|
||||
- baz comment (test@test.com)
|
||||
"
|
||||
`;
|
||||
|
|
|
@ -1,51 +1,83 @@
|
|||
import { describe, expect, it, beforeAll, afterAll, afterEach } from '@jest/globals';
|
||||
import { generateChangeFiles } from '../../__fixtures__/changeFiles';
|
||||
import { cleanChangelogJson, readChangelogJson, readChangelogMd } from '../../__fixtures__/changelog';
|
||||
import fs from 'fs-extra';
|
||||
import { generateChangeFiles, getChange, fakeEmail as author } from '../../__fixtures__/changeFiles';
|
||||
import {
|
||||
cleanChangelogJson,
|
||||
readChangelogJson,
|
||||
readChangelogMd,
|
||||
fakeCommit as commit,
|
||||
trimChangelogMd,
|
||||
} from '../../__fixtures__/changelog';
|
||||
import { initMockLogs } from '../../__fixtures__/mockLogs';
|
||||
import { RepositoryFactory } from '../../__fixtures__/repositoryFactory';
|
||||
|
||||
import { writeChangelog } from '../../changelog/writeChangelog';
|
||||
import { getPackageInfos } from '../../monorepo/getPackageInfos';
|
||||
import { readChangeFiles } from '../../changefile/readChangeFiles';
|
||||
import { BeachballOptions } from '../../types/BeachballOptions';
|
||||
import { ChangeFileInfo, ChangeType } from '../../types/ChangeInfo';
|
||||
import type { BeachballOptions } from '../../types/BeachballOptions';
|
||||
import type { Repository } from '../../__fixtures__/repository';
|
||||
import { getDefaultOptions } from '../../options/getDefaultOptions';
|
||||
|
||||
function getChange(packageName: string, comment: string, type: ChangeType = 'patch'): ChangeFileInfo {
|
||||
return {
|
||||
comment,
|
||||
email: 'test@testtestme.com',
|
||||
packageName,
|
||||
type,
|
||||
dependentChangeType: 'patch',
|
||||
};
|
||||
}
|
||||
import type { BumpInfo } from '../../types/BumpInfo';
|
||||
import { getMaxChangeType } from '../../changefile/changeTypes';
|
||||
import { getChangePath } from '../../paths';
|
||||
|
||||
describe('writeChangelog', () => {
|
||||
let repositoryFactory: RepositoryFactory;
|
||||
let monoRepoFactory: RepositoryFactory;
|
||||
let repo: Repository | undefined;
|
||||
let sharedSingleRepo: Repository;
|
||||
let sharedMonoRepo: Repository;
|
||||
|
||||
initMockLogs();
|
||||
|
||||
/**
|
||||
* Read package infos and change files, fill in default options, and call `writeChangelog`.
|
||||
*
|
||||
* `calculatedChangeTypes` will be generated based on the max change type of each package's change files,
|
||||
* and assuming every `dependentChangedBy` package has change type `patch`.
|
||||
*/
|
||||
async function writeChangelogWrapper(
|
||||
params: Partial<Pick<BumpInfo, 'dependentChangedBy'>> & {
|
||||
options: BeachballOptions;
|
||||
}
|
||||
) {
|
||||
const { options, dependentChangedBy = {} } = params;
|
||||
const packageInfos = getPackageInfos(repo!.rootPath);
|
||||
const changeFileChangeInfos = readChangeFiles(options, packageInfos);
|
||||
|
||||
// Generate a basic best guess at calculatedChangeTypes
|
||||
const calculatedChangeTypes: BumpInfo['calculatedChangeTypes'] = {};
|
||||
for (const { change } of changeFileChangeInfos) {
|
||||
const { packageName, type } = change;
|
||||
calculatedChangeTypes[packageName] = getMaxChangeType(type, calculatedChangeTypes[packageName]);
|
||||
}
|
||||
for (const pkgName of Object.keys(dependentChangedBy)) {
|
||||
calculatedChangeTypes[pkgName] = getMaxChangeType('patch', calculatedChangeTypes[pkgName]);
|
||||
}
|
||||
|
||||
await writeChangelog({ dependentChangedBy, calculatedChangeTypes, changeFileChangeInfos, packageInfos }, options);
|
||||
}
|
||||
|
||||
function getOptions(options?: Partial<BeachballOptions>): BeachballOptions {
|
||||
return {
|
||||
...getDefaultOptions(),
|
||||
// change to ?. if a future test uses a non-standard repo
|
||||
// change to ?. if a future test uses a non-standard repo var name
|
||||
path: repo!.rootPath,
|
||||
...options,
|
||||
};
|
||||
}
|
||||
|
||||
beforeAll(() => {
|
||||
// These tests can share the same repo factories because they don't push to origin
|
||||
// (the actual tests run against a clone)
|
||||
// These tests can share the same factories and repos because they don't push to the remote,
|
||||
// and the repo used is reset after each test (which is faster than making new clones).
|
||||
repositoryFactory = new RepositoryFactory('single');
|
||||
monoRepoFactory = new RepositoryFactory('monorepo');
|
||||
sharedSingleRepo = repositoryFactory.cloneRepository();
|
||||
sharedMonoRepo = monoRepoFactory.cloneRepository();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// Revert whichever shared repo was used to the original state
|
||||
repo?.resetAndClean();
|
||||
repo = undefined;
|
||||
});
|
||||
|
||||
|
@ -54,247 +86,343 @@ describe('writeChangelog', () => {
|
|||
monoRepoFactory.cleanUp();
|
||||
});
|
||||
|
||||
it('generates correct changelog', async () => {
|
||||
repo = repositoryFactory.cloneRepository();
|
||||
it('does not write changelogs if there are no changes', async () => {
|
||||
repo = sharedSingleRepo;
|
||||
const options = getOptions();
|
||||
|
||||
repo.commitChange('foo');
|
||||
generateChangeFiles([getChange('foo', 'additional comment 2')], options);
|
||||
generateChangeFiles([getChange('foo', 'additional comment 1')], options);
|
||||
generateChangeFiles([getChange('foo', 'comment 1')], options);
|
||||
await writeChangelogWrapper({ options });
|
||||
|
||||
repo.commitChange('bar');
|
||||
generateChangeFiles([getChange('foo', 'comment 2')], options);
|
||||
|
||||
const packageInfos = getPackageInfos(repo.rootPath);
|
||||
const changes = readChangeFiles(options, packageInfos);
|
||||
|
||||
await writeChangelog(options, changes, { foo: 'patch' }, { foo: new Set(['foo']) }, packageInfos);
|
||||
|
||||
expect(readChangelogMd(repo.rootPath)).toMatchSnapshot('changelog md');
|
||||
|
||||
const changelogJson = readChangelogJson(repo.rootPath);
|
||||
expect(cleanChangelogJson(changelogJson)).toMatchSnapshot('changelog json');
|
||||
|
||||
// Every entry should have a different commit hash
|
||||
const patchComments = changelogJson!.entries[0].comments.patch!;
|
||||
const commits = patchComments.map(entry => entry.commit);
|
||||
expect(new Set(commits).size).toEqual(patchComments.length);
|
||||
|
||||
// The first entry should be the newest
|
||||
expect(patchComments[0].commit).toBe(repo.getCurrentHash());
|
||||
expect(readChangelogMd(repo.rootPath)).toBeNull();
|
||||
expect(readChangelogJson(repo.rootPath)).toBeNull();
|
||||
});
|
||||
|
||||
it('generates correct changelog with changeDir set', async () => {
|
||||
repo = repositoryFactory.cloneRepository();
|
||||
it('generates basic changelog', async () => {
|
||||
repo = sharedSingleRepo;
|
||||
const options = getOptions();
|
||||
|
||||
const options = getOptions({
|
||||
changeDir: 'myChangeDir',
|
||||
});
|
||||
generateChangeFiles([getChange('foo', 'old minor comment')], options);
|
||||
generateChangeFiles([getChange('foo', 'patch comment', 'patch')], options);
|
||||
generateChangeFiles([getChange('foo', 'no comment', 'none')], options);
|
||||
generateChangeFiles([getChange('foo', 'new minor comment', 'minor')], options);
|
||||
|
||||
repo.commitChange('foo');
|
||||
generateChangeFiles([getChange('foo', 'additional comment 2')], options);
|
||||
generateChangeFiles([getChange('foo', 'additional comment 1')], options);
|
||||
generateChangeFiles([getChange('foo', 'comment 1')], options);
|
||||
await writeChangelogWrapper({ options });
|
||||
|
||||
repo.commitChange('bar');
|
||||
generateChangeFiles([getChange('foo', 'comment 2')], options);
|
||||
|
||||
const packageInfos = getPackageInfos(repo.rootPath);
|
||||
const changes = readChangeFiles(options, packageInfos);
|
||||
|
||||
await writeChangelog(options, changes, { foo: 'patch' }, { foo: new Set(['foo']) }, packageInfos);
|
||||
|
||||
expect(readChangelogMd(repo.rootPath)).toMatchSnapshot('changelog md');
|
||||
const changelogMd = readChangelogMd(repo.rootPath);
|
||||
// Do some explicit tests since snapshot changes are too easy to ignore
|
||||
expect(changelogMd).toMatch(/^# Change Log - foo/);
|
||||
expect(changelogMd).toMatch(/### Minor changes\n\n- new minor comment.*\n- old minor comment/);
|
||||
expect(changelogMd).toContain('### Patches\n\n- patch comment');
|
||||
expect(changelogMd).not.toContain('no comment');
|
||||
expect(changelogMd).toMatchSnapshot('changelog md');
|
||||
|
||||
const changelogJson = readChangelogJson(repo.rootPath);
|
||||
expect(cleanChangelogJson(changelogJson)).toMatchSnapshot('changelog json');
|
||||
|
||||
// Every entry should have a different commit hash
|
||||
const patchComments = changelogJson!.entries[0].comments.patch!;
|
||||
const commits = patchComments.map(entry => entry.commit);
|
||||
expect(new Set(commits).size).toEqual(patchComments.length);
|
||||
|
||||
// The first entry should be the newest
|
||||
expect(patchComments[0].commit).toBe(repo.getCurrentHash());
|
||||
});
|
||||
|
||||
it('generates correct changelog in monorepo with groupChanges (grouped change FILES)', async () => {
|
||||
repo = monoRepoFactory.cloneRepository();
|
||||
|
||||
const options = getOptions({
|
||||
groupChanges: true,
|
||||
expect(changelogJson).toEqual({ name: 'foo', entries: [expect.anything()] });
|
||||
expect(cleanChangelogJson(changelogJson)!.entries[0]).toEqual({
|
||||
version: '1.0.0',
|
||||
date: '(date)',
|
||||
tag: 'foo_v1.0.0',
|
||||
comments: {
|
||||
minor: [
|
||||
{ comment: 'new minor comment', package: 'foo', author, commit },
|
||||
{ comment: 'old minor comment', package: 'foo', author, commit },
|
||||
],
|
||||
patch: [{ comment: 'patch comment', package: 'foo', author, commit }],
|
||||
none: [{ comment: 'no comment', package: 'foo', author, commit }],
|
||||
},
|
||||
});
|
||||
|
||||
repo.commitChange('foo');
|
||||
generateChangeFiles(
|
||||
[getChange('foo', 'additional comment 2'), getChange('bar', 'comment from bar change ')],
|
||||
options
|
||||
);
|
||||
generateChangeFiles([getChange('foo', 'additional comment 1')], options);
|
||||
generateChangeFiles([getChange('foo', 'comment 1')], options);
|
||||
// Every entry should have a different commit hash
|
||||
const minorComments = changelogJson!.entries[0].comments.minor!;
|
||||
expect(minorComments).toBeTruthy();
|
||||
const commits = minorComments.map(entry => entry.commit);
|
||||
expect(new Set(commits).size).toEqual(minorComments.length);
|
||||
|
||||
repo.commitChange('bar');
|
||||
generateChangeFiles([getChange('foo', 'comment 2')], options);
|
||||
// The first entry should be the newest
|
||||
expect(minorComments[0].commit).toBe(repo!.getCurrentHash());
|
||||
});
|
||||
|
||||
const packageInfos = getPackageInfos(repo.rootPath);
|
||||
const changes = readChangeFiles(options, packageInfos);
|
||||
it('generates changelog with custom changeDir', async () => {
|
||||
repo = sharedSingleRepo;
|
||||
const changeDir = 'myChangeDir';
|
||||
const options = getOptions({ changeDir });
|
||||
|
||||
await writeChangelog(options, changes, { foo: 'patch', bar: 'patch' }, {}, packageInfos);
|
||||
generateChangeFiles([{ packageName: 'foo', comment: 'comment 1' }], options);
|
||||
// make sure the setup worked as expected
|
||||
expect(fs.readdirSync(repo.pathTo(changeDir))).toEqual([expect.stringMatching(/^foo-.*\.json$/)]);
|
||||
|
||||
// check changelogs for both foo and bar
|
||||
expect(readChangelogMd(repo.pathTo('packages/foo'))).toMatchSnapshot('foo CHANGELOG.md');
|
||||
expect(readChangelogMd(repo.pathTo('packages/bar'))).toMatchSnapshot('bar CHANGELOG.md');
|
||||
await writeChangelogWrapper({ options });
|
||||
|
||||
// Just check for a comment in the md to verify that the change file was found
|
||||
expect(readChangelogMd(repo.rootPath)).toContain('### Minor changes\n\n- comment 1');
|
||||
});
|
||||
|
||||
it('generates changelogs with dependent changes in monorepo', async () => {
|
||||
repo = sharedMonoRepo;
|
||||
const options = getOptions();
|
||||
|
||||
generateChangeFiles([{ packageName: 'foo', comment: 'foo comment' }], options);
|
||||
generateChangeFiles([{ packageName: 'baz', comment: 'baz comment' }], options);
|
||||
|
||||
await writeChangelogWrapper({
|
||||
options,
|
||||
// Per the fixture, bar depends on baz (and is bumped), and foo depends on bar.
|
||||
// Note that the changelogs will only include dependent bump entries as specified here
|
||||
// (which may be different than what would actually be calculated while bumping), and
|
||||
// NO actual bumping will occur (so the versions will be the same as the fixture).
|
||||
dependentChangedBy: { bar: new Set(['baz']), foo: new Set(['bar']) },
|
||||
});
|
||||
|
||||
// check changelogs for foo, bar, and baz
|
||||
const fooText = readChangelogMd(repo.pathTo('packages/foo'));
|
||||
expect(fooText).toMatch(/### Minor changes\n\n- foo comment.*\n- Bump bar to/);
|
||||
expect(fooText).not.toContain('baz comment');
|
||||
expect(fooText).toMatchSnapshot('foo CHANGELOG.md');
|
||||
|
||||
const barText = readChangelogMd(repo.pathTo('packages/bar'));
|
||||
expect(barText).toContain('### Patches\n\n- Bump baz to');
|
||||
expect(barText).not.toMatch(/(foo|baz) comment/);
|
||||
expect(barText).toMatchSnapshot('bar CHANGELOG.md');
|
||||
|
||||
const bazText = readChangelogMd(repo.pathTo('packages/baz'));
|
||||
expect(bazText).toContain('baz comment');
|
||||
expect(bazText).not.toContain('Bump');
|
||||
expect(bazText).toMatchSnapshot('baz CHANGELOG.md');
|
||||
|
||||
const fooJson = readChangelogJson(repo.pathTo('packages/foo'));
|
||||
expect(cleanChangelogJson(fooJson)).toMatchSnapshot('foo CHANGELOG.json');
|
||||
expect(readChangelogJson(repo.pathTo('packages/bar'), true /*clean*/)).toMatchSnapshot('bar CHANGELOG.json');
|
||||
expect(fooJson).toEqual({ name: 'foo', entries: [expect.anything()] });
|
||||
expect(cleanChangelogJson(fooJson)!.entries[0]).toEqual({
|
||||
version: '1.0.0',
|
||||
date: '(date)',
|
||||
tag: 'foo_v1.0.0',
|
||||
comments: {
|
||||
minor: [
|
||||
{ package: 'foo', comment: 'foo comment', author, commit },
|
||||
{ package: 'foo', comment: 'Bump bar to v1.3.4', author: 'beachball', commit },
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
// Every entry should have a different commit hash
|
||||
const patchComments = fooJson!.entries[0].comments.patch!;
|
||||
const commits = patchComments.map(entry => entry.commit);
|
||||
expect(new Set(commits).size).toEqual(patchComments.length);
|
||||
const barJson = readChangelogJson(repo.pathTo('packages/bar'));
|
||||
expect(barJson).toEqual({ name: 'bar', entries: [expect.anything()] });
|
||||
expect(cleanChangelogJson(barJson)!.entries[0]).toEqual({
|
||||
comments: {
|
||||
patch: [{ package: 'bar', comment: 'Bump baz to v1.3.4', author: 'beachball', commit }],
|
||||
},
|
||||
date: '(date)',
|
||||
tag: 'bar_v1.3.4',
|
||||
version: '1.3.4',
|
||||
});
|
||||
|
||||
// The first entry should be the newest
|
||||
expect(patchComments[0].commit).toBe(repo.getCurrentHash());
|
||||
const bazJson = readChangelogJson(repo.pathTo('packages/baz'));
|
||||
expect(bazJson).toEqual({ name: 'baz', entries: [expect.anything()] });
|
||||
expect(cleanChangelogJson(bazJson)!.entries[0]).toEqual({
|
||||
version: '1.3.4',
|
||||
date: '(date)',
|
||||
tag: 'baz_v1.3.4',
|
||||
comments: {
|
||||
minor: [{ package: 'baz', comment: 'baz comment', author, commit }],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('generates correct grouped changelog in monorepo', async () => {
|
||||
repo = monoRepoFactory.cloneRepository();
|
||||
it('generates changelog in monorepo with grouped change files (groupChanges)', async () => {
|
||||
repo = sharedMonoRepo;
|
||||
const options = getOptions({ groupChanges: true });
|
||||
|
||||
// these will be in one change file
|
||||
generateChangeFiles([getChange('foo', 'comment 2'), getChange('bar', 'bar comment')], options);
|
||||
// separate change file
|
||||
generateChangeFiles([getChange('foo', 'comment 1')], options);
|
||||
|
||||
await writeChangelogWrapper({ options, dependentChangedBy: { foo: new Set(['bar']) } });
|
||||
|
||||
// check changelogs for both foo and bar
|
||||
const fooText = readChangelogMd(repo.pathTo('packages/foo'));
|
||||
expect(fooText).toMatch(/- comment 1.*\n- comment 2/);
|
||||
expect(fooText).not.toContain('bar comment');
|
||||
|
||||
const barText = readChangelogMd(repo.pathTo('packages/bar'));
|
||||
expect(barText).toContain('bar comment');
|
||||
expect(barText).not.toMatch(/comment (1|2)/);
|
||||
|
||||
const fooJson = readChangelogJson(repo.pathTo('packages/foo'));
|
||||
expect(fooJson).toEqual({ name: 'foo', entries: [expect.anything()] });
|
||||
expect(cleanChangelogJson(fooJson)!.entries[0].comments).toEqual({
|
||||
minor: [
|
||||
expect.objectContaining({ comment: 'comment 1', package: 'foo' }),
|
||||
expect.objectContaining({ comment: 'comment 2', package: 'foo' }),
|
||||
expect.objectContaining({ comment: 'Bump bar to v1.3.4', package: 'foo' }),
|
||||
],
|
||||
});
|
||||
|
||||
const barJson = readChangelogJson(repo.pathTo('packages/bar'));
|
||||
expect(barJson).toEqual({ name: 'bar', entries: [expect.anything()] });
|
||||
expect(cleanChangelogJson(barJson)!.entries[0].comments).toEqual({
|
||||
minor: [expect.objectContaining({ comment: 'bar comment', package: 'bar' })],
|
||||
});
|
||||
});
|
||||
|
||||
it('generates grouped changelog in monorepo', async () => {
|
||||
repo = sharedMonoRepo;
|
||||
const options = getOptions({
|
||||
changelog: {
|
||||
groups: [
|
||||
{
|
||||
masterPackageName: 'foo',
|
||||
changelogPath: repo.rootPath,
|
||||
changelogPath: '.',
|
||||
include: ['packages/*'],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
// foo and baz have changes.
|
||||
// bar has no direct changes, but it depends on baz.
|
||||
generateChangeFiles(['foo', 'baz'], options);
|
||||
generateChangeFiles([getChange('foo', 'foo comment 2')], options);
|
||||
|
||||
await writeChangelogWrapper({
|
||||
options,
|
||||
// Per the fixture structure, bar will have a dependent change from baz, which changes foo
|
||||
dependentChangedBy: { bar: new Set(['baz']), foo: new Set(['bar']) },
|
||||
});
|
||||
|
||||
// Validate package changelogs
|
||||
const fooText = readChangelogMd(repo.pathTo('packages/foo'));
|
||||
// includes the dependent change from bar
|
||||
expect(fooText).toMatch(/- foo comment.*\n- Bump bar to/);
|
||||
expect(fooText).not.toContain('baz comment');
|
||||
|
||||
const barText = readChangelogMd(repo.pathTo('packages/bar'));
|
||||
// includes the dependent change from baz
|
||||
expect(barText).toContain('Bump baz to');
|
||||
expect(barText).not.toMatch(/(foo|baz) comment/);
|
||||
|
||||
const bazText = readChangelogMd(repo.pathTo('packages/baz'));
|
||||
expect(bazText).toContain('baz comment');
|
||||
expect(bazText).not.toMatch(/Bump|foo comment/);
|
||||
|
||||
// Verify that dependent entries are in foo CHANGELOG.json
|
||||
const fooJson = readChangelogJson(repo.pathTo('packages/foo'));
|
||||
expect(fooJson).toEqual({ name: 'foo', entries: [expect.anything()] });
|
||||
expect(fooJson!.entries[0].comments.minor).toContainEqual(
|
||||
expect.objectContaining({ comment: 'Bump bar to v1.3.4' })
|
||||
);
|
||||
|
||||
// Validate grouped changelog: it shouldn't have dependent entries
|
||||
const groupedText = readChangelogMd(repo.rootPath);
|
||||
expect(groupedText).not.toContain('Bump');
|
||||
expect(groupedText).toMatch(/- `foo`.*\n - foo comment 2.*\n - foo comment/);
|
||||
expect(groupedText).toMatch(/- `baz`.*\n - baz comment/);
|
||||
expect(groupedText).toMatchSnapshot('grouped CHANGELOG.md');
|
||||
|
||||
// Validate grouped CHANGELOG.json
|
||||
const groupedJson = readChangelogJson(repo.rootPath);
|
||||
expect(groupedJson).toEqual({ name: 'foo', entries: [expect.anything()] });
|
||||
expect(cleanChangelogJson(groupedJson)!.entries[0]).toEqual({
|
||||
comments: {
|
||||
minor: [
|
||||
{ comment: 'foo comment 2', package: 'foo', author, commit },
|
||||
{ comment: 'foo comment', package: 'foo', author, commit },
|
||||
{ comment: 'baz comment', package: 'baz', author, commit },
|
||||
],
|
||||
},
|
||||
date: '(date)',
|
||||
tag: 'foo_v1.0.0',
|
||||
version: '1.0.0',
|
||||
});
|
||||
});
|
||||
|
||||
it('generates grouped changelog when path overlaps with regular changelog', async () => {
|
||||
repo = sharedMonoRepo;
|
||||
const options = getOptions({
|
||||
changelog: {
|
||||
groups: [
|
||||
{
|
||||
masterPackageName: 'foo',
|
||||
changelogPath: 'packages/foo',
|
||||
include: ['packages/foo', 'packages/bar'],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
repo.commitChange('foo');
|
||||
generateChangeFiles([getChange('foo', 'comment 1')], options);
|
||||
generateChangeFiles(['foo', 'bar'], options);
|
||||
|
||||
repo.commitChange('bar');
|
||||
generateChangeFiles([getChange('bar', 'comment 2')], options);
|
||||
generateChangeFiles([getChange('bar', 'comment 3')], options);
|
||||
await writeChangelogWrapper({ options, dependentChangedBy: { foo: new Set(['bar']) } });
|
||||
|
||||
const packageInfos = getPackageInfos(repo.rootPath);
|
||||
const changes = readChangeFiles(options, packageInfos);
|
||||
// packages/foo changelog should be grouped, not regular.
|
||||
// We can verify this by just looking for the bar entry.
|
||||
const groupedChangelogMd = readChangelogMd(repo.pathTo('packages/foo'));
|
||||
expect(groupedChangelogMd).toContain('- `bar`\n - bar comment');
|
||||
|
||||
await writeChangelog(options, changes, {}, {}, packageInfos);
|
||||
|
||||
// Validate changelog for foo and bar packages
|
||||
expect(readChangelogMd(repo.pathTo('packages/foo'))).toMatchSnapshot('foo CHANGELOG.md');
|
||||
expect(readChangelogMd(repo.pathTo('packages/bar'))).toMatchSnapshot('bar CHANGELOG.md');
|
||||
|
||||
// Validate grouped changelog for foo and bar packages
|
||||
expect(readChangelogMd(repo.rootPath)).toMatchSnapshot('grouped CHANGELOG.md');
|
||||
const groupedJson = readChangelogJson(repo.pathTo('packages/foo'));
|
||||
expect(groupedJson).toEqual({ name: 'foo', entries: [expect.anything()] });
|
||||
expect(groupedJson!.entries[0].comments.minor).toContainEqual(expect.objectContaining({ comment: 'bar comment' }));
|
||||
});
|
||||
|
||||
it('generates grouped changelog without dependent change entries', async () => {
|
||||
repo = monoRepoFactory.cloneRepository();
|
||||
it('does not write grouped changelog if group would only have dependent bumps', async () => {
|
||||
repo = sharedMonoRepo;
|
||||
const options = getOptions({
|
||||
changelog: {
|
||||
groups: [{ masterPackageName: 'foo', changelogPath: '.', include: ['packages/foo', 'packages/baz'] }],
|
||||
},
|
||||
});
|
||||
|
||||
// bar is not in the group, but it causes a dependent change for foo
|
||||
generateChangeFiles(['bar'], options);
|
||||
await writeChangelogWrapper({ options, dependentChangedBy: { foo: new Set(['bar']) } });
|
||||
|
||||
// grouped changelog was not written
|
||||
expect(readChangelogMd(repo.rootPath)).toBeNull();
|
||||
expect(readChangelogJson(repo.rootPath)).toBeNull();
|
||||
|
||||
// foo changelog was written with the dependent bump
|
||||
expect(readChangelogMd(repo.pathTo('packages/foo'))).toBeTruthy();
|
||||
});
|
||||
|
||||
it('does not write grouped changelog overlapping regular changelog if it would contain only dependent bumps', async () => {
|
||||
repo = sharedMonoRepo;
|
||||
const options = getOptions({
|
||||
changelog: {
|
||||
groups: [
|
||||
{
|
||||
masterPackageName: 'foo',
|
||||
changelogPath: repo.rootPath,
|
||||
include: ['packages/foo', 'packages/bar', 'packages/baz'],
|
||||
},
|
||||
// The grouped changelog overlaps with the changelog for packages/foo.
|
||||
{ masterPackageName: 'foo', changelogPath: 'packages/foo', include: ['packages/foo', 'packages/baz'] },
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
repo.commitChange('baz');
|
||||
generateChangeFiles([getChange('baz', 'comment 1')], options);
|
||||
// bar is not in the group
|
||||
generateChangeFiles(['bar'], options);
|
||||
// but it causes a dependent change for foo (so normally foo's non-grouped changelog would be written)
|
||||
await writeChangelogWrapper({ options, dependentChangedBy: { foo: new Set(['bar']) } });
|
||||
|
||||
const packageInfos = getPackageInfos(repo.rootPath);
|
||||
const changes = readChangeFiles(options, packageInfos);
|
||||
|
||||
await writeChangelog(options, changes, { bar: 'patch', baz: 'patch' }, { bar: new Set(['baz']) }, packageInfos);
|
||||
|
||||
// Validate changelog for bar package
|
||||
const barChangelogText = readChangelogMd(repo.pathTo('packages/bar'));
|
||||
expect(barChangelogText).toContain('- Bump baz');
|
||||
expect(barChangelogText).toMatchSnapshot('bar CHANGELOG.md');
|
||||
|
||||
// Validate changelog for baz package
|
||||
expect(readChangelogMd(repo.pathTo('packages/baz'))).toMatchSnapshot('baz CHANGELOG.md');
|
||||
|
||||
// Validate grouped changelog for foo master package
|
||||
const groupedChangelogText = readChangelogMd(repo.rootPath);
|
||||
expect(groupedChangelogText).toContain('- comment 1');
|
||||
expect(groupedChangelogText).not.toContain('- Bump baz');
|
||||
expect(groupedChangelogText).toMatchSnapshot('grouped CHANGELOG.md');
|
||||
// Nothing was written (not the grouped changelog, and not a normal changelog for foo)
|
||||
expect(readChangelogMd(repo.pathTo('packages/foo'))).toBeNull();
|
||||
expect(readChangelogJson(repo.pathTo('packages/foo'))).toBeNull();
|
||||
});
|
||||
|
||||
it('generates grouped changelog without dependent change entries where packages have normal changes and dependency changes', async () => {
|
||||
repo = monoRepoFactory.cloneRepository();
|
||||
it('includes pre* changes', async () => {
|
||||
repo = sharedSingleRepo;
|
||||
const options = getOptions();
|
||||
|
||||
const options = getOptions({
|
||||
changelog: {
|
||||
groups: [
|
||||
{
|
||||
masterPackageName: 'foo',
|
||||
changelogPath: repo.rootPath,
|
||||
include: ['packages/foo', 'packages/bar', 'packages/baz'],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
generateChangeFiles(
|
||||
[
|
||||
{ packageName: 'foo', comment: 'comment 1', type: 'premajor' },
|
||||
{ packageName: 'foo', comment: 'comment 2', type: 'preminor' },
|
||||
{ packageName: 'foo', comment: 'comment 3', type: 'prepatch' },
|
||||
{ packageName: 'foo', comment: 'comment 4', type: 'prerelease' },
|
||||
],
|
||||
options
|
||||
);
|
||||
|
||||
repo.commitChange('baz');
|
||||
generateChangeFiles([getChange('baz', 'comment 1')], options);
|
||||
generateChangeFiles([getChange('bar', 'comment 1')], options);
|
||||
await writeChangelogWrapper({ options });
|
||||
|
||||
const packageInfos = getPackageInfos(repo.rootPath);
|
||||
const changes = readChangeFiles(options, packageInfos);
|
||||
|
||||
await writeChangelog(options, changes, { bar: 'patch', baz: 'patch' }, { bar: new Set(['baz']) }, packageInfos);
|
||||
|
||||
// Validate changelog for bar and baz packages
|
||||
expect(readChangelogMd(repo.pathTo('packages/bar'))).toMatchSnapshot('bar CHANGELOG.md');
|
||||
expect(readChangelogMd(repo.pathTo('packages/baz'))).toMatchSnapshot('baz CHANGELOG.md');
|
||||
|
||||
// Validate grouped changelog for foo master package
|
||||
expect(readChangelogMd(repo.rootPath)).toMatchSnapshot('grouped CHANGELOG.md');
|
||||
});
|
||||
|
||||
it('generates correct grouped changelog when grouped change log is saved to the same dir as a regular changelog', async () => {
|
||||
repo = monoRepoFactory.cloneRepository();
|
||||
|
||||
const options = getOptions({
|
||||
changelog: {
|
||||
groups: [
|
||||
{
|
||||
masterPackageName: 'foo',
|
||||
changelogPath: repo.pathTo('packages/foo'),
|
||||
include: ['packages/foo', 'packages/bar'],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
repo.commitChange('foo');
|
||||
generateChangeFiles([getChange('foo', 'comment 1')], options);
|
||||
|
||||
repo.commitChange('bar');
|
||||
generateChangeFiles([getChange('bar', 'comment 2')], options);
|
||||
|
||||
const packageInfos = getPackageInfos(repo.rootPath);
|
||||
const changes = readChangeFiles(options, packageInfos);
|
||||
|
||||
await writeChangelog(options, changes, {}, {}, packageInfos);
|
||||
|
||||
// Validate changelog for bar package
|
||||
expect(readChangelogMd(repo.pathTo('packages/bar'))).toMatchSnapshot();
|
||||
|
||||
// Validate grouped changelog for foo and bar packages
|
||||
expect(readChangelogMd(repo.pathTo('packages/foo'))).toMatchSnapshot();
|
||||
const changelogMd = readChangelogMd(repo.rootPath);
|
||||
expect(changelogMd).toContain('### Major changes (pre-release)\n\n- comment 1');
|
||||
expect(changelogMd).toContain('### Minor changes (pre-release)\n\n- comment 2');
|
||||
expect(changelogMd).toContain('### Patches (pre-release)\n\n- comment 3');
|
||||
expect(changelogMd).toContain('### Changes\n\n- comment 4');
|
||||
});
|
||||
|
||||
it('includes pre* changes', async () => {
|
||||
|
@ -303,16 +431,14 @@ describe('writeChangelog', () => {
|
|||
|
||||
generateChangeFiles(
|
||||
[
|
||||
{ packageName: 'foo', comment: 'comment 1', type: 'premajor' },
|
||||
{ packageName: 'foo', comment: 'comment 2', type: 'preminor' },
|
||||
{ packageName: 'foo', comment: 'comment 3', type: 'prepatch' },
|
||||
getChange('foo', 'comment 1', 'premajor'),
|
||||
getChange('foo', 'comment 2', 'preminor'),
|
||||
getChange('foo', 'comment 3', 'prepatch'),
|
||||
],
|
||||
options
|
||||
);
|
||||
|
||||
const packageInfos = getPackageInfos(repo.rootPath);
|
||||
const changes = readChangeFiles(options, packageInfos);
|
||||
await writeChangelog(options, changes, {}, {}, packageInfos);
|
||||
await writeChangelogWrapper({ options });
|
||||
|
||||
const changelogMd = readChangelogMd(repo.rootPath);
|
||||
expect(changelogMd).toContain('### Major changes (pre-release)\n\n- comment 1');
|
||||
|
@ -321,16 +447,12 @@ describe('writeChangelog', () => {
|
|||
});
|
||||
|
||||
it('writes only CHANGELOG.md if generateChangelog is "md"', async () => {
|
||||
repo = repositoryFactory.cloneRepository();
|
||||
repo = sharedSingleRepo;
|
||||
const options = getOptions({ generateChangelog: 'md' });
|
||||
|
||||
repo.commitChange('foo');
|
||||
generateChangeFiles([getChange('foo', 'comment 1')], options);
|
||||
generateChangeFiles(['foo'], options);
|
||||
|
||||
const packageInfos = getPackageInfos(repo.rootPath);
|
||||
const changes = readChangeFiles(options, packageInfos);
|
||||
|
||||
await writeChangelog(options, changes, { foo: 'patch' }, { foo: new Set(['foo']) }, packageInfos);
|
||||
await writeChangelogWrapper({ options });
|
||||
|
||||
// CHANGELOG.md is written
|
||||
expect(readChangelogMd(repo.rootPath)).toContain('## 1.0.0');
|
||||
|
@ -340,16 +462,12 @@ describe('writeChangelog', () => {
|
|||
});
|
||||
|
||||
it('writes only CHANGELOG.json if generateChangelog is "json"', async () => {
|
||||
repo = repositoryFactory.cloneRepository();
|
||||
repo = sharedSingleRepo;
|
||||
const options = getOptions({ generateChangelog: 'json' });
|
||||
|
||||
repo.commitChange('foo');
|
||||
generateChangeFiles([getChange('foo', 'comment 1')], options);
|
||||
generateChangeFiles(['foo'], options);
|
||||
|
||||
const packageInfos = getPackageInfos(repo.rootPath);
|
||||
const changes = readChangeFiles(options, packageInfos);
|
||||
|
||||
await writeChangelog(options, changes, { foo: 'patch' }, { foo: new Set(['foo']) }, packageInfos);
|
||||
await writeChangelogWrapper({ options });
|
||||
|
||||
// CHANGELOG.md is not written
|
||||
expect(readChangelogMd(repo.rootPath)).toBeNull();
|
||||
|
@ -357,6 +475,39 @@ describe('writeChangelog', () => {
|
|||
// 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' })]);
|
||||
expect(changelogJson!.entries[0].comments.minor).toEqual([expect.objectContaining({ comment: 'foo comment' })]);
|
||||
});
|
||||
|
||||
it('appends to existing changelog', async () => {
|
||||
// Most of the previous content tests are handled by renderChangelog, but writeChangelog is
|
||||
// responsible for reading that content and passing it in.
|
||||
repo = sharedSingleRepo;
|
||||
const options = getOptions();
|
||||
|
||||
// Write some changes and generate changelogs
|
||||
generateChangeFiles(['foo'], options);
|
||||
await writeChangelogWrapper({ options });
|
||||
|
||||
// Read and save the initial changelogs
|
||||
const firstChangelogMd = readChangelogMd(repo.rootPath);
|
||||
expect(firstChangelogMd).toContain('foo comment');
|
||||
const firstChangelogJson = cleanChangelogJson(readChangelogJson(repo.rootPath));
|
||||
expect(firstChangelogJson).toEqual({ name: 'foo', entries: [expect.anything()] });
|
||||
|
||||
// Delete the change files, generate new ones, and re-generate changelogs
|
||||
fs.emptyDirSync(getChangePath(options));
|
||||
generateChangeFiles([getChange('foo', 'extra change')], options);
|
||||
await writeChangelogWrapper({ options });
|
||||
|
||||
// Read the changelogs again and verify that the previous content is still there
|
||||
const secondChangelogMd = readChangelogMd(repo.rootPath);
|
||||
expect(secondChangelogMd).toContain('extra change');
|
||||
expect(secondChangelogMd).toContain(trimChangelogMd(firstChangelogMd!));
|
||||
|
||||
const secondChangelogJson = cleanChangelogJson(readChangelogJson(repo.rootPath));
|
||||
expect(secondChangelogJson).toEqual({
|
||||
name: 'foo',
|
||||
entries: [expect.anything(), firstChangelogJson!.entries[0]],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -89,7 +89,7 @@ export async function updatePackageLock(cwd: string): Promise<void> {
|
|||
* deletes change files, update package.json, and changelogs
|
||||
*/
|
||||
export async function performBump(bumpInfo: BumpInfo, options: BeachballOptions): Promise<BumpInfo> {
|
||||
const { modifiedPackages, packageInfos, changeFileChangeInfos, dependentChangedBy, calculatedChangeTypes } = bumpInfo;
|
||||
const { modifiedPackages, packageInfos, changeFileChangeInfos } = bumpInfo;
|
||||
|
||||
await callHook(options.hooks?.prebump, modifiedPackages, bumpInfo.packageInfos);
|
||||
|
||||
|
@ -98,7 +98,7 @@ export async function performBump(bumpInfo: BumpInfo, options: BeachballOptions)
|
|||
|
||||
if (options.generateChangelog) {
|
||||
// Generate changelog
|
||||
await writeChangelog(options, changeFileChangeInfos, calculatedChangeTypes, dependentChangedBy, packageInfos);
|
||||
await writeChangelog(bumpInfo, options);
|
||||
}
|
||||
|
||||
if (!options.keepChangeFiles) {
|
||||
|
|
|
@ -13,19 +13,17 @@ import { mergeChangelogs } from './mergeChangelogs';
|
|||
import { ChangeSet } from '../types/ChangeInfo';
|
||||
|
||||
export async function writeChangelog(
|
||||
options: BeachballOptions,
|
||||
changeFileChangeInfos: ChangeSet,
|
||||
calculatedChangeTypes: BumpInfo['calculatedChangeTypes'],
|
||||
dependentChangedBy: BumpInfo['dependentChangedBy'],
|
||||
packageInfos: PackageInfos
|
||||
bumpInfo: Pick<BumpInfo, 'changeFileChangeInfos' | 'calculatedChangeTypes' | 'dependentChangedBy' | 'packageInfos'>,
|
||||
options: BeachballOptions
|
||||
): Promise<void> {
|
||||
const groupedChangelogPaths = await writeGroupedChangelog(
|
||||
const { changeFileChangeInfos, calculatedChangeTypes, dependentChangedBy, packageInfos } = bumpInfo;
|
||||
|
||||
const groupedChangelogDirs = await writeGroupedChangelog(
|
||||
options,
|
||||
changeFileChangeInfos,
|
||||
calculatedChangeTypes,
|
||||
packageInfos
|
||||
);
|
||||
const groupedChangelogPathSet = new Set(groupedChangelogPaths);
|
||||
|
||||
const changelogs = getPackageChangelogs({
|
||||
changeFileChangeInfos,
|
||||
|
@ -34,84 +32,90 @@ export async function writeChangelog(
|
|||
packageInfos,
|
||||
options,
|
||||
});
|
||||
|
||||
// Write package changelogs.
|
||||
// Use a standard for loop here to prevent potentially firing off multiple network requests at once
|
||||
// (in case any custom renderers have network requests)
|
||||
// (in case any custom renderers have network requests).
|
||||
for (const pkg of Object.keys(changelogs)) {
|
||||
const packagePath = path.dirname(packageInfos[pkg].packageJsonPath);
|
||||
if (groupedChangelogPathSet?.has(packagePath)) {
|
||||
console.log(`Changelog for ${pkg} has been written as a group here: ${packagePath}`);
|
||||
} else {
|
||||
if (!groupedChangelogDirs.includes(packagePath)) {
|
||||
await writeChangelogFiles(options, changelogs[pkg], packagePath, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Write grouped changelogs.
|
||||
* @returns The list of directories where grouped changelogs were written.
|
||||
*/
|
||||
async function writeGroupedChangelog(
|
||||
options: BeachballOptions,
|
||||
changeFileChangeInfos: ChangeSet,
|
||||
calculatedChangeTypes: BumpInfo['calculatedChangeTypes'],
|
||||
packageInfos: PackageInfos
|
||||
): Promise<string[]> {
|
||||
if (!options.changelog) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const { groups: changelogGroups } = options.changelog;
|
||||
// Get the changelog groups with absolute paths.
|
||||
const changelogGroups = options.changelog?.groups?.map(({ changelogPath, ...rest }) => ({
|
||||
...rest,
|
||||
changelogAbsDir: path.resolve(options.path, changelogPath),
|
||||
}));
|
||||
if (!changelogGroups?.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Grouped changelogs should not contain dependency bump entries
|
||||
// Get changelogs without dependency bump entries
|
||||
const changelogs = getPackageChangelogs({
|
||||
changeFileChangeInfos,
|
||||
calculatedChangeTypes,
|
||||
packageInfos,
|
||||
options,
|
||||
});
|
||||
|
||||
const groupedChangelogs: {
|
||||
[path: string]: {
|
||||
changelogs: PackageChangelog[];
|
||||
masterPackage: PackageInfo;
|
||||
};
|
||||
[changelogAbsDir: string]: { changelogs: PackageChangelog[]; masterPackage: PackageInfo };
|
||||
} = {};
|
||||
|
||||
// Validate groups and initialize groupedChangelogs
|
||||
for (const { masterPackageName, changelogAbsDir } of changelogGroups) {
|
||||
const masterPackage = packageInfos[masterPackageName];
|
||||
if (!masterPackage) {
|
||||
console.warn(`master package ${masterPackageName} does not exist.`);
|
||||
continue;
|
||||
}
|
||||
if (!fs.existsSync(changelogAbsDir)) {
|
||||
console.warn(`changelog path ${changelogAbsDir} does not exist.`);
|
||||
continue;
|
||||
}
|
||||
groupedChangelogs[changelogAbsDir] = { masterPackage, changelogs: [] };
|
||||
}
|
||||
|
||||
// Put changelogs into groups
|
||||
for (const pkg of Object.keys(changelogs)) {
|
||||
const packagePath = path.dirname(packageInfos[pkg].packageJsonPath);
|
||||
const relativePath = path.relative(options.path, packagePath);
|
||||
for (const group of changelogGroups) {
|
||||
const { changelogPath, masterPackageName } = group;
|
||||
const masterPackage = packageInfos[masterPackageName];
|
||||
if (!masterPackage) {
|
||||
console.warn(`master package ${masterPackageName} does not exist.`);
|
||||
continue;
|
||||
}
|
||||
if (!fs.existsSync(changelogPath)) {
|
||||
console.warn(`changelog path ${changelogPath} does not exist.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const group of changelogGroups) {
|
||||
const isInGroup = isPathIncluded(relativePath, group.include, group.exclude);
|
||||
if (isInGroup) {
|
||||
groupedChangelogs[changelogPath] ??= {
|
||||
changelogs: [],
|
||||
masterPackage,
|
||||
};
|
||||
groupedChangelogs[changelogPath].changelogs.push(changelogs[pkg]);
|
||||
groupedChangelogs[group.changelogAbsDir].changelogs.push(changelogs[pkg]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const changelogAbsolutePaths: string[] = [];
|
||||
for (const changelogPath in groupedChangelogs) {
|
||||
const { masterPackage, changelogs } = groupedChangelogs[changelogPath];
|
||||
// Write each grouped changelog if it's not empty
|
||||
for (const [changelogAbsDir, { masterPackage, changelogs }] of Object.entries(groupedChangelogs)) {
|
||||
const groupedChangelog = mergeChangelogs(changelogs, masterPackage);
|
||||
if (groupedChangelog) {
|
||||
await writeChangelogFiles(options, groupedChangelog, changelogPath, true);
|
||||
changelogAbsolutePaths.push(path.resolve(changelogPath));
|
||||
await writeChangelogFiles(options, groupedChangelog, changelogAbsDir, true);
|
||||
}
|
||||
}
|
||||
|
||||
return changelogAbsolutePaths;
|
||||
// Return all the possible grouped changelog directories (even if there was nothing to write).
|
||||
// Otherwise if a grouped changelog location overlaps with a package changelog location, and
|
||||
// on one publish there are only dependent bump changes for that package (and no changes for
|
||||
// other packages in the group), we'd get the package changelog updates with dependent bumps
|
||||
// added to the otherwise-grouped changelog file.
|
||||
return Object.keys(groupedChangelogs);
|
||||
}
|
||||
|
||||
async function writeChangelogFiles(
|
||||
|
|
Загрузка…
Ссылка в новой задаче