Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add changedir cli option #969

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions change/beachball-f710676c-c7fa-4b79-bc20-4b65f5687d5b.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "Add `changeDir` CLI option",
"packageName": "beachball",
"email": "[email protected]",
"dependentChangeType": "patch"
}
1 change: 1 addition & 0 deletions docs/overview/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ For the latest full list of supported options, see `RepoOptions` [in this file](
| `bumpDeps` | bool | `true` | repo | bump dependent packages during publish (bump A if A depends on B) |
| `changeFilePrompt` | `ChangeFilePromptOptions` ([details][1]) | | repo | customize the prompt for change files (can be used to add custom fields) |
| `changehint` | string | | repo | hint message for when change files are not detected but required |
| `changeDir` | string | `change` | repo | directory of the change files |
| `changelog` | `ChangelogOptions` ([details][2]) | | repo | changelog rendering and grouping options |
| `defaultNpmTag` | string | `'latest'` | package | the default dist-tag used for NPM publish |
| `disallowedChangeTypes` | string[] | | repo, group, package | what change types are disallowed |
Expand Down
38 changes: 38 additions & 0 deletions src/__e2e__/bump.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,44 @@ describe('version bumping', () => {
expect(changeFiles).toHaveLength(0);
});

it('correctly bumps packages with change files when changeDir is set', async () => {
const testChangedir = 'changeDir';

const monorepo: RepoFixture['folders'] = {
packages: {
'pkg-1': { version: '1.0.0' },
'pkg-2': { version: '1.0.0', dependencies: { 'pkg-1': '1.0.0' } },
'pkg-3': { version: '1.0.0', devDependencies: { 'pkg-2': '1.0.0' } },
'pkg-4': { version: '1.0.0', peerDependencies: { 'pkg-3': '1.0.0' } },
'pkg-5': { version: '1.0.0', optionalDependencies: { 'pkg-4': '1.0.0' } },
},
};
repositoryFactory = new RepositoryFactory({ folders: monorepo });
const repo = repositoryFactory.cloneRepository();

generateChangeFiles(['pkg-1'], repo.rootPath, undefined, testChangedir);

repo.push();

await bump({ path: repo.rootPath, bumpDeps: false, changeDir: testChangedir } as BeachballOptions);

const packageInfos = getPackageInfos(repo.rootPath);

const pkg1NewVersion = '1.1.0';
expect(packageInfos['pkg-1'].version).toBe(pkg1NewVersion);
expect(packageInfos['pkg-2'].version).toBe(monorepo['packages']['pkg-2'].version);
expect(packageInfos['pkg-3'].version).toBe(monorepo['packages']['pkg-3'].version);
expect(packageInfos['pkg-4'].version).toBe(monorepo['packages']['pkg-4'].version);

expect(packageInfos['pkg-2'].dependencies!['pkg-1']).toBe(pkg1NewVersion);
expect(packageInfos['pkg-3'].devDependencies!['pkg-2']).toBe(monorepo['packages']['pkg-2'].version);
expect(packageInfos['pkg-4'].peerDependencies!['pkg-3']).toBe(monorepo['packages']['pkg-3'].version);
expect(packageInfos['pkg-5'].optionalDependencies!['pkg-4']).toBe(monorepo['packages']['pkg-4'].version);

const changeFiles = getChangeFiles(repo.rootPath);
expect(changeFiles).toHaveLength(0);
});

it('for multi-workspace (multi-monorepo), only bumps packages in the current workspace', async () => {
repositoryFactory = new RepositoryFactory('multi-workspace');
expect(Object.keys(repositoryFactory.fixtures)).toEqual(['workspace-a', 'workspace-b']);
Expand Down
32 changes: 32 additions & 0 deletions src/__e2e__/change.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,38 @@ describe('change command', () => {
});
});

it('creates and commits a change file with changeDir set', async () => {
const testChangedir = 'changeDir';

repositoryFactory = new RepositoryFactory('single');
const repo = repositoryFactory.cloneRepository();

repo.checkout('-b', 'test');
repo.commitChange('file.js');

const changePromise = change({
path: repo.rootPath,
branch: defaultBranchName,
changeDir: testChangedir,
} as BeachballOptions);

expect(logs.mocks.log).toHaveBeenLastCalledWith('Please describe the changes for: foo');
await stdin.sendByChar('\n'); // default change type
await stdin.sendByChar('commit me please\n'); // custom message
await changePromise;

expect(logs.mocks.log).toHaveBeenLastCalledWith(expect.stringMatching(/^git committed these change files:/));
expect(repo.status()).toBe('');

const changeFiles = getChangeFiles(repo.rootPath, testChangedir);
expect(changeFiles).toHaveLength(1);
expect(fs.readJSONSync(changeFiles[0])).toMatchObject({
comment: 'commit me please',
packageName: 'foo',
type: 'patch',
});
});

it('creates a change file when there are no changes but package name is provided', async () => {
repositoryFactory = new RepositoryFactory('single');
const repo = repositoryFactory.cloneRepository();
Expand Down
9 changes: 6 additions & 3 deletions src/__fixtures__/changeFiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ export type PartialChangeFile = { packageName: string } & Partial<ChangeFileInfo
* Default values are `type: 'minor'`, `dependentChangeType: 'patch'`, and placeholders for other fields.
* @param cwd Working directory
* @param groupChanges Whether to group all changes into one change file.
* @param changeDir Directory to write change files to.
*/
export function generateChangeFiles(
changes: (string | PartialChangeFile)[],
cwd: string,
groupChanges?: boolean
groupChanges?: boolean,
changeDir?: string
): void {
writeChangeFiles({
changes: changes.map(change => {
Expand All @@ -32,11 +34,12 @@ export function generateChangeFiles(
}),
groupChanges,
cwd,
changeDir,
});
}

/** Get full paths to existing change files under `cwd` */
export function getChangeFiles(cwd: string): string[] {
const changePath = getChangePath(cwd);
export function getChangeFiles(cwd: string, changeDir?: string): string[] {
const changePath = getChangePath(cwd, changeDir);
return changePath && fs.existsSync(changePath) ? fs.readdirSync(changePath).map(p => path.join(changePath, p)) : [];
}
15 changes: 15 additions & 0 deletions src/__functional__/changefile/readChangeFiles.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,21 @@ describe('readChangeFiles', () => {
expect(changeSet[0].change.commit).toBe(undefined);
});

it('reads from a custom changeDir', () => {
const testChangedir = 'changeDir';

const repository = repositoryFactory.cloneRepository();
repository.commitChange('foo');
generateChangeFiles(['foo'], repository.rootPath, undefined, testChangedir);

const packageInfos = getPackageInfos(repository.rootPath);
const changeSet = readChangeFiles(
{ path: repository.rootPath, changeDir: testChangedir } as BeachballOptions,
packageInfos
);
expect(changeSet).toHaveLength(1);
});

it('excludes invalid change files', () => {
const monoRepo = monoRepoFactory.cloneRepository();
monoRepo.updateJsonFile('packages/bar/package.json', { private: true });
Expand Down
30 changes: 30 additions & 0 deletions src/__functional__/changefile/writeChangeFiles.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,36 @@ describe('writeChangeFiles', () => {
expect(changeFileContents).toEqual({ packageName: 'bar' });
});

it('respects changeDir option', () => {
const repo = monorepoFactory.cloneRepository();
const previousHead = repo.getCurrentHash();

const testChangeDir = 'myChangeDir';

writeChangeFiles({
changes: [{ packageName: 'foo' }, { packageName: 'bar' }] as ChangeFileInfo[],
cwd: repo.rootPath,
changeDir: testChangeDir,
});

const expectedFiles = [`${testChangeDir}/bar-${uuidGeneric}.json`, `${testChangeDir}/foo-${uuidGeneric}.json`];

// change files are created
const changeFiles = getChangeFiles(repo.rootPath, testChangeDir);
expect(cleanChangeFilePaths(repo.rootPath, changeFiles)).toEqual(expectedFiles);

// and tracked
const trackedFiles = listAllTrackedFiles([`${testChangeDir}/*`], repo.rootPath);
expect(cleanChangeFilePaths(repo.rootPath, trackedFiles)).toEqual(expectedFiles);

// and committed
expect(repo.getCurrentHash()).not.toEqual(previousHead);

// also verify contents of one file
const changeFileContents = fs.readJSONSync(changeFiles[0]);
expect(changeFileContents).toEqual({ packageName: 'bar' });
});

it('respects commitChangeFiles=false', () => {
const repo = monorepoFactory.cloneRepository();
const previousHead = repo.getCurrentHash();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,67 @@ This log was last generated on (date) and should not be manually modified.
"
`;

exports[`writeChangelog generates correct changelog with changeDir set: changelog json 1`] = `
{
"entries": [
{
"comments": {
"patch": [
{
"author": "[email protected]",
"comment": "comment 2",
"commit": "(sha1-0)",
"package": "foo",
},
{
"author": "[email protected]",
"comment": "comment 1",
"commit": "(sha1-1)",
"package": "foo",
},
{
"author": "[email protected]",
"comment": "additional comment 1",
"commit": "(sha1-2)",
"package": "foo",
},
{
"author": "[email protected]",
"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)

### Patches

- comment 2 ([email protected])
- comment 1 ([email protected])
- additional comment 1 ([email protected])
- additional comment 2 ([email protected])
"
`;

exports[`writeChangelog generates correct changelog: changelog json 1`] = `
{
"entries": [
Expand Down
31 changes: 31 additions & 0 deletions src/__functional__/changelog/writeChangelog.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,37 @@ describe('writeChangelog', () => {
expect(patchComments[0].commit).toBe(repository.getCurrentHash());
});

it('generates correct changelog with changeDir set', async () => {
const testChangeDir = 'myChangeDir';
const repository = repositoryFactory.cloneRepository();
repository.commitChange('foo');
generateChangeFiles([getChange('foo', 'additional comment 2')], repository.rootPath, undefined, testChangeDir);
generateChangeFiles([getChange('foo', 'additional comment 1')], repository.rootPath, undefined, testChangeDir);
generateChangeFiles([getChange('foo', 'comment 1')], repository.rootPath, undefined, testChangeDir);

repository.commitChange('bar');
generateChangeFiles([getChange('foo', 'comment 2')], repository.rootPath, undefined, testChangeDir);

const beachballOptions = { path: repository.rootPath, changeDir: testChangeDir } as BeachballOptions;
const packageInfos = getPackageInfos(repository.rootPath);
const changes = readChangeFiles(beachballOptions, packageInfos);

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

expect(readChangelogMd(repository.rootPath)).toMatchSnapshot('changelog md');

const changelogJson = readChangelogJson(repository.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(repository.getCurrentHash());
});

it('generates correct changelog in monorepo with groupChanges (grouped change FILES)', async () => {
const monoRepo = monoRepoFactory.cloneRepository();
monoRepo.commitChange('foo');
Expand Down
19 changes: 19 additions & 0 deletions src/__functional__/validation/areChangeFilesDeleted.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,23 @@ describe('areChangeFilesDeleted', () => {
} as BeachballOptions);
expect(result).toBeTruthy();
});

it('deletes change files when changeDir option is specified', () => {
const testChangedir = 'changeDir';
generateChangeFiles(['pkg-1'], repository.rootPath, undefined, testChangedir);
repository.push();
repository.checkout('-b', 'feature-0');

const changeDirPath = getChangePath(repository.rootPath, testChangedir);
fs.removeSync(changeDirPath);

repository.commitAll();

const result = areChangeFilesDeleted({
branch: defaultRemoteBranchName,
path: repository.rootPath,
changeDir: testChangedir,
} as BeachballOptions);
expect(result).toBeTruthy();
});
});
2 changes: 1 addition & 1 deletion src/bump/performBump.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export async function performBump(bumpInfo: BumpInfo, options: BeachballOptions)

if (!options.keepChangeFiles) {
// Unlink changelogs
unlinkChangeFiles(changeFileChangeInfos, packageInfos, options.path);
unlinkChangeFiles(changeFileChangeInfos, packageInfos, options.path, options.changeDir);
}

await callHook(options.hooks?.postbump, modifiedPackages, bumpInfo.packageInfos);
Expand Down
11 changes: 6 additions & 5 deletions src/changefile/getChangedPackages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import fs from 'fs-extra';
import path from 'path';
import minimatch from 'minimatch';
import { ChangeFileInfo, ChangeInfoMultiple } from '../types/ChangeInfo';
import { changeFolder, getChangePath } from '../paths';
import { defaultChangeFolder, getChangePath } from '../paths';
import { getChanges, getStagedChanges, git } from 'workspace-tools';
import { getScopedPackages } from '../monorepo/getScopedPackages';
import { BeachballOptions } from '../types/BeachballOptions';
Expand Down Expand Up @@ -87,7 +87,8 @@ function getAllChangedPackages(options: BeachballOptions, packageInfos: PackageI

// Filter out changed files which are ignored by ignorePatterns.
// Also ignore the CHANGELOG files and change files because they're generated by beachball.
const ignorePatterns = [...(options.ignorePatterns || []), `${changeFolder}/*.json`, 'CHANGELOG.{md,json}'];
const changeDir = options.changeDir ?? defaultChangeFolder;
const ignorePatterns = [...(options.ignorePatterns || []), `${changeDir}/*.json`, 'CHANGELOG.{md,json}'];
const nonIgnoredChanges = changes.filter(moddedFile => {
const ignorePattern = ignorePatterns.find(pattern => minimatch(moddedFile, pattern, { matchBase: true }));
ignorePattern && logIgnored(moddedFile, `ignored by pattern "${ignorePattern}"`);
Expand Down Expand Up @@ -131,9 +132,9 @@ function getAllChangedPackages(options: BeachballOptions, packageInfos: PackageI
* Gets all the changed packages which do not already have a change file
*/
export function getChangedPackages(options: BeachballOptions, packageInfos: PackageInfos): string[] {
const { path: cwd, branch } = options;
const { path: cwd, branch, changeDir = defaultChangeFolder } = options;

const changePath = getChangePath(cwd);
const changePath = getChangePath(cwd, changeDir);

ensureSharedHistory(options);

Expand All @@ -149,7 +150,7 @@ export function getChangedPackages(options: BeachballOptions, packageInfos: Pack
}

const changes = changeFilesResult.stdout.split(/\n/);
const changeFiles = changes.filter(name => path.dirname(name) === changeFolder);
const changeFiles = changes.filter(name => path.dirname(name) === changeDir);
const changeFilePackageSet = new Set<string>();

// Loop through the change files, building up a set of packages that we can skip
Expand Down
4 changes: 2 additions & 2 deletions src/changefile/readChangeFiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ import { PackageInfos } from '../types/PackageInfo';
* (so it's possible that multiple entries will have the same filename).
*/
export function readChangeFiles(options: BeachballOptions, packageInfos: PackageInfos): ChangeSet {
const { path: cwd, fromRef, command } = options;
const { path: cwd, fromRef, command, changeDir } = options;
const scopedPackages = getScopedPackages(options, packageInfos);
const changePath = getChangePath(cwd);
const changePath = getChangePath(cwd, changeDir);

if (!fs.existsSync(changePath)) {
return [];
Expand Down
5 changes: 3 additions & 2 deletions src/changefile/unlinkChangeFiles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ export function unlinkChangeFiles(
packageInfos: {
[pkg: string]: PackageInfo;
},
cwd: string
cwd: string,
changeDir?: string
): void {
const changePath = getChangePath(cwd);
const changePath = getChangePath(cwd, changeDir);
if (!changeSet || !changeSet.length) {
return;
}
Expand Down
Loading