Skip to content

Commit

Permalink
Merge pull request #433 from kaoz70/main
Browse files Browse the repository at this point in the history
feat(semver): filter out prerelease tags if trying to release
  • Loading branch information
edbzn authored Jan 31, 2022
2 parents fd7f4e3 + c508323 commit 377662c
Show file tree
Hide file tree
Showing 4 changed files with 173 additions and 33 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,48 @@ describe(getLastVersion.name, () => {
});

it('should compute current version from previous semver tag', async () => {
mockGitSemverTags.mockResolvedValue(['my-lib-2.1.0', 'my-lib-2.0.0', 'my-lib-1.0.0']);
mockGitSemverTags.mockResolvedValue([
'my-lib-2.1.0',
'my-lib-2.0.0',
'my-lib-1.0.0',
]);

const tag = await lastValueFrom(getLastVersion({ tagPrefix }));

expect(tag).toEqual('2.1.0');
});

it('should compute current version from previous semver prerelease tag', async () => {
mockGitSemverTags.mockResolvedValue([
'my-lib-2.1.0-beta.0',
'my-lib-2.0.0',
'my-lib-1.0.0',
]);

const tag = await lastValueFrom(getLastVersion({ tagPrefix }));

expect(tag).toEqual('2.1.0-beta.0');
});

it('should compute current version from previous semver release tag', async () => {
mockGitSemverTags.mockResolvedValue([
'my-lib-2.1.0-beta.0',
'my-lib-2.0.0',
'my-lib-1.0.0',
]);

const tag = await lastValueFrom(
getLastVersion({ tagPrefix, includePrerelease: false })
);

expect(tag).toEqual('2.0.0');
});

it('should throw error if no tag available', async () => {
mockGitSemverTags.mockResolvedValue([]);

expect(lastValueFrom(getLastVersion({ tagPrefix }))).rejects.toThrow('No semver tag found');
expect(lastValueFrom(getLastVersion({ tagPrefix }))).rejects.toThrow(
'No semver tag found'
);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,20 @@ import type { Observable } from 'rxjs';

export function getLastVersion({
tagPrefix,
includePrerelease = true,
}: {
tagPrefix: string;
includePrerelease?: boolean;
}): Observable<string> {
return from(
(promisify(gitSemverTags) as any)({ tagPrefix }) as Promise<string[]>
).pipe(
switchMap((tags: string[]) => {
const versions = tags.map((tag) => tag.substring(tagPrefix.length));
const versions = tags
.map((tag) => tag.substring(tagPrefix.length))
.filter((v) =>
includePrerelease ? true : semver.prerelease(v) === null
);
const [version] = versions.sort(semver.rcompare);

if (version == null) {
Expand Down
157 changes: 128 additions & 29 deletions packages/semver/src/executors/version/utils/try-bump.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ import { callbackify } from 'util';
import { getLastVersion } from './get-last-version';
import { getCommits, getFirstCommitRef } from './git';
import { tryBump } from './try-bump';
import * as gitSemverTags from 'git-semver-tags';

jest.mock('conventional-recommended-bump');
jest.mock('./get-last-version');
jest.mock('./git');
jest.mock('git-semver-tags', () => jest.fn());

describe('tryBump', () => {
const mockConventionalRecommendedBump =
Expand All @@ -23,10 +25,15 @@ describe('tryBump', () => {
const mockGetFirstCommitRef = getFirstCommitRef as jest.MockedFunction<
typeof getFirstCommitRef
>;
let mockGitSemverTags: jest.Mock;

let loggerSpy: jest.SpyInstance;

beforeEach(() => {
mockGitSemverTags = jest.fn();
(gitSemverTags as jest.Mock).mockImplementation(
callbackify(mockGitSemverTags)
);
mockGetLastVersion.mockReturnValue(of('2.1.0'));
loggerSpy = jest.spyOn(logger, 'warn');
});
Expand Down Expand Up @@ -82,27 +89,30 @@ describe('tryBump', () => {
.mockReturnValueOnce(of(['fix: A', 'feat: B']));

/* Mock bump to return "minor". */
mockConventionalRecommendedBump
.mockImplementation(
callbackify(
jest.fn().mockResolvedValueOnce({
releaseType: undefined,
})
.mockResolvedValueOnce({
releaseType: undefined,
})
.mockResolvedValueOnce({
releaseType: 'minor',
})
) as () => void
);

const newVersion = await lastValueFrom(tryBump({
preset: 'angular',
projectRoot: '/libs/demo',
dependencyRoots: ['/libs/dep1', '/libs/dep2'],
tagPrefix: 'v',
}));
mockConventionalRecommendedBump.mockImplementation(
callbackify(
jest
.fn()
.mockResolvedValueOnce({
releaseType: undefined,
})
.mockResolvedValueOnce({
releaseType: undefined,
})
.mockResolvedValueOnce({
releaseType: 'minor',
})
) as () => void
);

const newVersion = await lastValueFrom(
tryBump({
preset: 'angular',
projectRoot: '/libs/demo',
dependencyRoots: ['/libs/dep1', '/libs/dep2'],
tagPrefix: 'v',
})
);

expect(newVersion).toEqual('2.1.1');

Expand Down Expand Up @@ -150,13 +160,15 @@ describe('tryBump', () => {
it('should use given type to calculate next version', async () => {
mockGetCommits.mockReturnValue(of(['feat: A', 'feat: B']));

const newVersion = await lastValueFrom(tryBump({
preset: 'angular',
projectRoot: '/libs/demo',
tagPrefix: 'v',
releaseType: 'premajor',
preid: 'alpha',
}));
const newVersion = await lastValueFrom(
tryBump({
preset: 'angular',
projectRoot: '/libs/demo',
tagPrefix: 'v',
releaseType: 'premajor',
preid: 'alpha',
})
);

expect(newVersion).toEqual('3.0.0-alpha.0');

Expand All @@ -169,6 +181,93 @@ describe('tryBump', () => {
});
});

it('should use prerelease to calculate next major release version', async () => {
mockGitSemverTags.mockResolvedValue([
'my-lib-3.0.0-beta.0',
'my-lib-2.1.0',
'my-lib-2.0.0',
'my-lib-1.0.0',
]);
mockGetCommits.mockReturnValue(of(['feat: A', 'feat: B']));

const newVersion = await lastValueFrom(
tryBump({
preset: 'angular',
projectRoot: '/libs/demo',
tagPrefix: 'v',
releaseType: 'major',
})
);

expect(newVersion).toEqual('3.0.0');

expect(mockConventionalRecommendedBump).not.toBeCalled();

expect(mockGetCommits).toBeCalledTimes(1);
expect(mockGetCommits).toBeCalledWith({
projectRoot: '/libs/demo',
since: 'v2.1.0',
});
});

it('should use prerelease to calculate next patch release version', async () => {
mockGitSemverTags.mockResolvedValue([
'my-lib-2.1.1-beta.0',
'my-lib-2.1.0',
'my-lib-2.0.0',
'my-lib-1.0.0',
]);
mockGetCommits.mockReturnValue(of(['feat: A', 'feat: B']));

const newVersion = await lastValueFrom(
tryBump({
preset: 'angular',
projectRoot: '/libs/demo',
tagPrefix: 'v',
releaseType: 'patch',
})
);

expect(newVersion).toEqual('2.1.1');

expect(mockConventionalRecommendedBump).not.toBeCalled();

expect(mockGetCommits).toBeCalledTimes(1);
expect(mockGetCommits).toBeCalledWith({
projectRoot: '/libs/demo',
since: 'v2.1.0',
});
});

it('should use prerelease to calculate next minor release version', async () => {
mockGitSemverTags.mockResolvedValue([
'my-lib-2.2.0-beta.0',
'my-lib-2.1.0',
'my-lib-2.0.0',
'my-lib-1.0.0',
]);
mockGetCommits.mockReturnValue(of(['feat: A', 'feat: B']));

const newVersion = await lastValueFrom(
tryBump({
preset: 'angular',
projectRoot: '/libs/demo',
tagPrefix: 'v',
releaseType: 'minor',
})
);

expect(newVersion).toEqual('2.2.0');

expect(mockConventionalRecommendedBump).not.toBeCalled();

expect(mockGetCommits).toBeCalledTimes(1);
expect(mockGetCommits).toBeCalledWith({
projectRoot: '/libs/demo',
since: 'v2.1.0',
});
});

it('should use given type to calculate next version even if there are no changes', async () => {
mockGetCommits.mockReturnValue(of([]));

Expand All @@ -195,7 +294,7 @@ describe('tryBump', () => {
tryBump({
preset: 'angular',
projectRoot: '/libs/demo',
tagPrefix: 'v'
tagPrefix: 'v',
})
);

Expand Down
5 changes: 4 additions & 1 deletion packages/semver/src/executors/version/utils/try-bump.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ export function tryBump({
preid?: string;
}): Observable<string | null> {
const initialVersion = '0.0.0';
const lastVersion$ = getLastVersion({ tagPrefix }).pipe(
const lastVersion$ = getLastVersion({
tagPrefix,
includePrerelease: releaseType === 'prerelease',
}).pipe(
catchError(() => {
logger.warn(
`🟠 No previous version tag found, fallback to version 0.0.0.
Expand Down

0 comments on commit 377662c

Please sign in to comment.