Skip to content
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-e88a3f18-8aca-4ca2-a348-d8f0c2a9ff71.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "minor",
"comment": "Adding a new option that allows to specify a pre-release prefix",
"packageName": "beachball",
"email": "arabisho@microsoft.com",
"dependentChangeType": "patch"
}
4 changes: 4 additions & 0 deletions docs/cli/options.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,7 @@ show help message
###### `--yes, -y`

skips the prompts for publish

###### `--prerelease-prefix`

sets a prerelease prefix for packages that are expected to receive a prerelease bump (for example, --prerelease-prefix "beta" will produce the "x.y.z-beta.prerelease" version)
273 changes: 273 additions & 0 deletions src/__e2e__/bump.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -587,4 +587,277 @@ describe('version bumping', () => {
const changeFiles = getChangeFiles(repo.rootPath);
expect(changeFiles.length).toBe(1);
});

it('bumps all packages and uses prefix in the version', async () => {
repositoryFactory = new RepositoryFactory();
await repositoryFactory.create();
const repo = await repositoryFactory.cloneRepository();

await repo.commitChange(
'packages/pkg-1/package.json',
JSON.stringify({
name: 'pkg-1',
version: '1.0.0',
})
);

await repo.commitChange(
'packages/pkg-2/package.json',
JSON.stringify({
name: 'pkg-2',
version: '1.0.0',
dependencies: {
'pkg-1': '1.0.0',
},
})
);

await repo.commitChange(
'packages/pkg-3/package.json',
JSON.stringify({
name: 'pkg-3',
version: '1.0.0',
devDependencies: {
'pkg-2': '1.0.0',
},
})
);

await repo.commitChange(
'packages/pkg-4/package.json',
JSON.stringify({
name: 'pkg-4',
version: '1.0.0',
peerDependencies: {
'pkg-3': '1.0.0',
},
})
);

await repo.commitChange(
'package.json',
JSON.stringify({
name: 'foo-repo',
version: '1.0.0',
private: true,
})
);

writeChangeFiles(
{
'pkg-1': {
type: 'prerelease',
comment: 'test',
email: 'test@test.com',
packageName: 'pkg-1',
dependentChangeType: 'patch',
},
},
repo.rootPath
);

git(['push', 'origin', 'master'], { cwd: repo.rootPath });

await bump({
path: repo.rootPath,
bumpDeps: true,
keepChangeFiles: false,
prereleasePrefix: 'beta',
} as BeachballOptions);

const packageInfos = getPackageInfos(repo.rootPath);

expect(packageInfos['pkg-1'].version).toBe('1.0.1-beta.0');
expect(packageInfos['pkg-2'].version).toBe('1.0.1');
expect(packageInfos['pkg-3'].version).toBe('1.0.1');

expect(packageInfos['pkg-2'].dependencies!['pkg-1']).toBe('1.0.1-beta.0');
expect(packageInfos['pkg-3'].devDependencies!['pkg-2']).toBe('1.0.1');
expect(packageInfos['pkg-4'].peerDependencies!['pkg-3']).toBe('1.0.1');

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

it('bumps all packages and uses prefixed versions in dependents', async () => {
repositoryFactory = new RepositoryFactory();
await repositoryFactory.create();
const repo = await repositoryFactory.cloneRepository();

await repo.commitChange(
'packages/pkg-1/package.json',
JSON.stringify({
name: 'pkg-1',
version: '1.0.0',
})
);

await repo.commitChange(
'packages/pkg-2/package.json',
JSON.stringify({
name: 'pkg-2',
version: '1.0.0',
dependencies: {
'pkg-1': '1.0.0',
},
})
);

await repo.commitChange(
'packages/pkg-3/package.json',
JSON.stringify({
name: 'pkg-3',
version: '1.0.0',
devDependencies: {
'pkg-2': '1.0.0',
},
})
);

await repo.commitChange(
'packages/pkg-4/package.json',
JSON.stringify({
name: 'pkg-4',
version: '1.0.0',
peerDependencies: {
'pkg-3': '1.0.0',
},
})
);

await repo.commitChange(
'package.json',
JSON.stringify({
name: 'foo-repo',
version: '1.0.0',
private: true,
})
);

writeChangeFiles(
{
'pkg-1': {
type: 'prerelease',
comment: 'test',
email: 'test@test.com',
packageName: 'pkg-1',
dependentChangeType: 'prerelease',
},
},
repo.rootPath
);

git(['push', 'origin', 'master'], { cwd: repo.rootPath });

await bump({
path: repo.rootPath,
bumpDeps: true,
keepChangeFiles: false,
prereleasePrefix: 'beta',
} as BeachballOptions);

const packageInfos = getPackageInfos(repo.rootPath);

expect(packageInfos['pkg-1'].version).toBe('1.0.1-beta.0');
expect(packageInfos['pkg-2'].version).toBe('1.0.1-beta.0');
expect(packageInfos['pkg-3'].version).toBe('1.0.1-beta.0');

expect(packageInfos['pkg-2'].dependencies!['pkg-1']).toBe('1.0.1-beta.0');
expect(packageInfos['pkg-3'].devDependencies!['pkg-2']).toBe('1.0.1-beta.0');
expect(packageInfos['pkg-4'].peerDependencies!['pkg-3']).toBe('1.0.1-beta.0');

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

it('bumps all packages and increments prefixed versions in dependents', async () => {
repositoryFactory = new RepositoryFactory();
await repositoryFactory.create();
const repo = await repositoryFactory.cloneRepository();

await repo.commitChange(
'packages/pkg-1/package.json',
JSON.stringify({
name: 'pkg-1',
version: '1.0.1-beta.0',
})
);

await repo.commitChange(
'packages/pkg-2/package.json',
JSON.stringify({
name: 'pkg-2',
version: '1.0.0',
dependencies: {
'pkg-1': '1.0.0',
},
})
);

await repo.commitChange(
'packages/pkg-3/package.json',
JSON.stringify({
name: 'pkg-3',
version: '1.0.0',
devDependencies: {
'pkg-2': '1.0.0',
},
})
);

await repo.commitChange(
'packages/pkg-4/package.json',
JSON.stringify({
name: 'pkg-4',
version: '1.0.0',
peerDependencies: {
'pkg-3': '1.0.0',
},
})
);

await repo.commitChange(
'package.json',
JSON.stringify({
name: 'foo-repo',
version: '1.0.0',
private: true,
})
);

writeChangeFiles(
{
'pkg-1': {
type: 'prerelease',
comment: 'test',
email: 'test@test.com',
packageName: 'pkg-1',
dependentChangeType: 'prerelease',
},
},
repo.rootPath
);

git(['push', 'origin', 'master'], { cwd: repo.rootPath });

await bump({
path: repo.rootPath,
bumpDeps: true,
keepChangeFiles: false,
prereleasePrefix: 'beta',
} as BeachballOptions);

const packageInfos = getPackageInfos(repo.rootPath);

expect(packageInfos['pkg-1'].version).toBe('1.0.1-beta.1');
expect(packageInfos['pkg-2'].version).toBe('1.0.1-beta.0');
expect(packageInfos['pkg-3'].version).toBe('1.0.1-beta.0');

expect(packageInfos['pkg-2'].dependencies!['pkg-1']).toBe('1.0.1-beta.1');
expect(packageInfos['pkg-3'].devDependencies!['pkg-2']).toBe('1.0.1-beta.0');
expect(packageInfos['pkg-4'].peerDependencies!['pkg-3']).toBe('1.0.1-beta.0');

const changeFiles = getChangeFiles(repo.rootPath);
expect(changeFiles.length).toBe(0);
});
});
2 changes: 1 addition & 1 deletion src/bump/bumpInPlace.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export function bumpInPlace(bumpInfo: BumpInfo, options: BeachballOptions) {

// pass 2: actually bump the packages in the bumpInfo in memory (no disk writes at this point)
Object.keys(packageChangeTypes).forEach(pkgName => {
bumpPackageInfoVersion(pkgName, bumpInfo);
bumpPackageInfoVersion(pkgName, bumpInfo, options);
});

// pass 3: Bump all the dependencies packages
Expand Down
5 changes: 3 additions & 2 deletions src/bump/bumpPackageInfoVersion.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { BumpInfo } from '../types/BumpInfo';
import semver from 'semver';
import { BeachballOptions } from '../types/BeachballOptions';

/**
* Bumps an individual package version based on the change type
*/
export function bumpPackageInfoVersion(pkgName: string, bumpInfo: BumpInfo) {
export function bumpPackageInfoVersion(pkgName: string, bumpInfo: BumpInfo, options: BeachballOptions) {
const { packageChangeTypes, packageInfos, modifiedPackages } = bumpInfo;
const info = packageInfos[pkgName];
const changeType = packageChangeTypes[pkgName];
Expand All @@ -21,7 +22,7 @@ export function bumpPackageInfoVersion(pkgName: string, bumpInfo: BumpInfo) {
return;
}
if (!info.private) {
info.version = semver.inc(info.version, changeType) as string;
info.version = semver.inc(info.version, changeType, options.prereleasePrefix) as string;
modifiedPackages.add(pkgName);
}
}
1 change: 1 addition & 0 deletions src/help.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ Options:
--force - force the sync command to skip the version comparison and use the version in the registry as is.
--dependent-change-type - for the change command: override the default dependent-change-type that will end-up in the change file.
--disallow-deleted-change-files - for the check command: verifies that no change files were deleted between head and target branch.
--prerelease-prefix - for the bump and publish commands: specify a prerelease prefix for packages that will receive a prerelease bump.

Examples:

Expand Down
1 change: 1 addition & 0 deletions src/types/BeachballOptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export interface CliOptions {
disallowedChangeTypes: ChangeType[] | null;
dependentChangeType: ChangeType | null;
disallowDeletedChangeFiles?: boolean;
prereleasePrefix?: string | null;
}

export interface RepoOptions {
Expand Down