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

fix: do not use tags for normal prerelease dependency bump #96

Closed
wants to merge 1 commit into from
Closed
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
2 changes: 2 additions & 0 deletions lib/multiSemanticRelease.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ import { createRequire } from "module";
* @param {Package[]} localDeps Array of local dependencies this package relies on.
* @param {context|void} context The semantic-release context for this package's release (filled in once semantic-release runs).
* @param {undefined|Result|false} result The result of semantic-release (object with lastRelease, nextRelease, commits, releases), false if this package was skipped (no changes or similar), or undefined if the package's release hasn't completed yet.
* @param {Object} _lastRelease The last release object for the package before its current release (set during anaylze-commit)
* @param {Object} _nextRelease The next release object (the release the package is releasing for this cycle) (set during generateNotes)
*/

/**
Expand Down
50 changes: 37 additions & 13 deletions lib/updateDeps.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,25 +41,49 @@ const getVersionFromTag = (pkg, tag) => {
return strMatch && strMatch[0] && semver.valid(strMatch[0]) ? strMatch[0] : null;
};

/**
* Options for NextPreVersion
*
* @typedef {Object} NextPreVersionOptions
* @param {Array<string>|undefined} tags - will use the tags as a reference, overrides useGitTags
* @param {boolean|undefined} useGitTags - if true, will look up all git tags for this prerelease
*/

/**
* Resolve next package version on prereleases.
*
* Will resolve highest next version of either:
*
* 1. The last release for the package during this multi-release cycle
* 2. (if tag options provided):
* a. the highest increment of the tags array provided
* b. the highest increment of the gitTags for the prerelease
*
* @param {Package} pkg Package object.
* @param {Array<string>} tags Override list of tags from specific pkg and branch.
* @param {NextPreVersionOptions|undefined} options additional options
* @returns {string|undefined} Next pkg version.
* @internal
*/
const getNextPreVersion = (pkg, tags) => {
const getNextPreVersion = (pkg, options) => {
const tagFilters = [pkg._preRelease];
const lastVersion = pkg._lastRelease && pkg._lastRelease.version;
// Note: this is only set is a current multi-semantic-release released
const lastVersionForCurrentRelease = pkg._lastRelease && pkg._lastRelease.version;

// Ensure options don't have a conflict
const normalizedOptions = options || {};
if (normalizedOptions.tags && normalizedOptions.useGitTags) {
throw new Error("You can only separately provide a set of tags or specify useGitTags!");
}
let { tags = [] } = normalizedOptions;
const { useGitTags } = normalizedOptions;

// Extract tags:
// 1. Set filter to extract only package tags
// 2. Get tags from a branch considering the filters established
// 3. Resolve the versions from the tags
// TODO: replace {cwd: '.'} with multiContext.cwd
if (pkg.name) tagFilters.push(pkg.name);
if (!tags) {
if (useGitTags) {
try {
tags = getTags(pkg._branch, { cwd: process.cwd() }, tagFilters);
} catch (e) {
Expand All @@ -69,15 +93,15 @@ const getNextPreVersion = (pkg, tags) => {
}
}

const lastPreRelTag = getPreReleaseTag(lastVersion);
const lastPreRelTag = getPreReleaseTag(lastVersionForCurrentRelease);
const isNewPreRelTag = lastPreRelTag && lastPreRelTag !== pkg._preRelease;

const versionToSet =
isNewPreRelTag || !lastVersion
isNewPreRelTag || !lastVersionForCurrentRelease
? `1.0.0-${pkg._preRelease}.1`
: _nextPreVersionCases(
tags.map((tag) => getVersionFromTag(pkg, tag)).filter((tag) => tag),
lastVersion,
lastVersionForCurrentRelease,
pkg._nextType,
pkg._preRelease
);
Expand All @@ -101,23 +125,23 @@ const getPreReleaseTag = (version) => {
/**
* Resolve next prerelease special cases: highest version from tags or major/minor/patch.
*
* @param {Array} tags List of all released tags from package.
* @param {string} lastVersion Last package version released.
* @param {Array<string>} tags - if non-empty, we will use these tags as part fo the comparison
* @param {string} lastVersionForCurrentMultiRelease Last package version released from multi-semantic-release
* @param {string} pkgNextType Next type evaluated for the next package type.
* @param {string} pkgPreRelease Package prerelease suffix.
* @returns {string|undefined} Next pkg version.
* @internal
*/
const _nextPreVersionCases = (tags, lastVersion, pkgNextType, pkgPreRelease) => {
const _nextPreVersionCases = (tags, lastVersionForCurrentMultiRelease, pkgNextType, pkgPreRelease) => {
// Case 1: Normal release on last version and is now converted to a prerelease
if (!semver.prerelease(lastVersion)) {
const { major, minor, patch } = semver.parse(lastVersion);
if (!semver.prerelease(lastVersionForCurrentMultiRelease)) {
const { major, minor, patch } = semver.parse(lastVersionForCurrentMultiRelease);
return `${semver.inc(`${major}.${minor}.${patch}`, pkgNextType || "patch")}-${pkgPreRelease}.1`;
}

// Case 2: Validates version with tags
const latestTag = getLatestVersion(tags, { withPrerelease: true });
return _nextPreHighestVersion(latestTag, lastVersion, pkgPreRelease);
return _nextPreHighestVersion(latestTag, lastVersionForCurrentMultiRelease, pkgPreRelease);
};

/**
Expand Down
97 changes: 91 additions & 6 deletions test/lib/updateDeps.test.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,26 @@
import {
resolveReleaseType,
import { beforeAll, beforeEach, jest } from "@jest/globals";
jest.unstable_mockModule("../../lib/git.js", () => ({
getTags: jest.fn(),
}));
let resolveReleaseType,
resolveNextVersion,
getNextVersion,
getNextPreVersion,
getPreReleaseTag,
getVersionFromTag,
} from "../../lib/updateDeps.js";
getTags;

beforeAll(async () => {
({ getTags } = await import("../../lib/git.js"));
({
resolveReleaseType,
resolveNextVersion,
getNextVersion,
getNextPreVersion,
getPreReleaseTag,
getVersionFromTag,
} = await import("../../lib/updateDeps.js"));
});

describe("resolveNextVersion()", () => {
// prettier-ignore
Expand Down Expand Up @@ -201,6 +216,9 @@ describe("getNextVersion()", () => {
});

describe("getNextPreVersion()", () => {
beforeEach(() => {
jest.clearAllMocks();
});
// prettier-ignore
const cases = [
[undefined, "patch", "rc", [], "1.0.0-rc.1"],
Expand All @@ -226,10 +244,77 @@ describe("getNextPreVersion()", () => {
_nextType: releaseType,
_lastRelease: {version: lastVersion},
_preRelease: preRelease,
_branch: "master",
name: "testing-package"
_branch: "master",
name: "testing-package"
},
{
tags: lastTags,
}
)).toBe(nextVersion);
});
it(`${lastVersion} and ${releaseType} ${
lastTags.length ? "with looked up branch tags " : ""
}gives ${nextVersion}`, () => {
getTags.mockImplementation(() => {
return lastTags;
});
// prettier-ignore
expect(getNextPreVersion(
{
_nextType: releaseType,
_lastRelease: {version: lastVersion},
_preRelease: preRelease,
_branch: "master",
name: "testing-package"
},
{
useGitTags: true,
}
)).toBe(nextVersion);
expect(getTags).toHaveBeenCalledTimes(1);
});
});
it("does not allow tags and useGitTags", () => {
expect(() =>
getNextPreVersion(
{
_nextType: "patch",
_lastRelease: { version: "1.0.0" },
_preRelease: "dev",
_branch: "master",
name: "testing-package",
},
{
useGitTags: true,
tags: [],
}
)
).toThrowError("You can only separately provide a set of tags or specify useGitTags!");
});
// Simulates us not using tags as criteria

const noTagCases = [
// prerelease channels just bump up the pre-release
["1.0.0-rc.0", "minor", "rc", "1.0.0-rc.1"],
["1.0.0-dev.0", "major", "dev", "1.0.0-dev.1"],
["1.0.0-dev.0", "major", "dev", "1.0.0-dev.1"],
["1.0.1-dev.0", "major", "dev", "1.0.1-dev.1"],
// main channels obey the release type
["11.0.0", "major", "beta", "12.0.0-beta.1"],
["1.0.0", "minor", "beta", "1.1.0-beta.1"],
["1.0.0", "patch", "beta", "1.0.1-beta.1"],
];
noTagCases.forEach(([lastVersion, releaseType, preRelease, nextVersion]) => {
it(`${lastVersion} and ${releaseType} for channel ${preRelease} gives ${nextVersion}`, () => {
// prettier-ignore
expect(getNextPreVersion(
{
_nextType: releaseType,
_lastRelease: {version: lastVersion},
_preRelease: preRelease,
_branch: "master",
name: "testing-package"
},
lastTags
)).toBe(nextVersion);
});
});
Expand Down
Loading