Skip to content

Commit

Permalink
[FEATURE] AbstractResolver: Resolve version ranges specifying major v…
Browse files Browse the repository at this point in the history
…ersion only

For example 'ui5 use 1' should resolve to the latest 1.x.x version
  • Loading branch information
RandomByte committed Aug 22, 2023
1 parent 9df9abd commit 1f8cfdf
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 25 deletions.
24 changes: 14 additions & 10 deletions lib/ui5Framework/AbstractResolver.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import semver from "semver";
const SEMVER_VERSION_REGEXP = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;

// Reduced Semantic Versioning pattern
// Matches MAJOR.MINOR as a simple version range to be resolved to the latest patch
const VERSION_RANGE_REGEXP = /^(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-SNAPSHOT)?$/;
// Matches MAJOR or MAJOR.MINOR as a simple version range to be resolved to the latest minor/patch
const VERSION_RANGE_REGEXP = /^(0|[1-9]\d*)(?:\.(0|[1-9]\d*))?(?:-SNAPSHOT)?$/;

/**
* Abstract Resolver
Expand Down Expand Up @@ -223,16 +223,20 @@ class AbstractResolver {
} else if (SEMVER_VERSION_REGEXP.test(version)) {
// Fully qualified version, can be used directly
spec = version;
} else if (VERSION_RANGE_REGEXP.test(version)) {
if (isSnapshotVersion) {
// For snapshot version ranges we need to insert a stand-in "x" for the patch level
// in order to make the semver check work: "1.112-SNAPSHOT" becomes "1.112.x-SNAPSHOT"
spec = version.replace(/-SNAPSHOT$/, ".x-SNAPSHOT");
} else {
const versionMatch = version.match(VERSION_RANGE_REGEXP);
if (versionMatch) {
if (isSnapshotVersion) {
// For snapshot version ranges we need to insert a stand-in "x" for the patch level
// and - in case none is provided - another "x" for the major version in order to
// make the semver check work: "1-SNAPSHOT" or "1.112-SNAPSHOT" becomes "1.112.x-SNAPSHOT"
spec = `${versionMatch[1]}.${versionMatch[2] || "x"}.x-SNAPSHOT`;
} else {
spec = version;
}
} else {
spec = version;
throw new Error(`Framework version specifier "${version}" is incorrect or not supported`);
}
} else {
throw new Error(`Framework version specifier "${version}" is incorrect or not supported`);
}
const versions = await this.fetchAllVersions({ui5HomeDir, cwd});
const resolvedVersion = semver.maxSatisfying(versions, spec, {
Expand Down
73 changes: 58 additions & 15 deletions test/lib/ui5framework/AbstractResolver.js
Original file line number Diff line number Diff line change
Expand Up @@ -576,6 +576,44 @@ test.serial("AbstractResolver: Static resolveVersion resolves 'latest'", async (
}], "fetchAllVersions should be called with expected arguments");
});

test.serial("AbstractResolver: Static resolveVersion resolves 'MAJOR'", async (t) => {
const {MyResolver} = t.context;
const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions")
.returns(["1.75.0", "1.75.1", "1.76.0"]);

const version = await MyResolver.resolveVersion("1", {
cwd: "/cwd",
ui5HomeDir: "/ui5HomeDir"
});

t.is(version, "1.76.0", "Resolved version should be correct");

t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once");
t.deepEqual(fetchAllVersionsStub.getCall(0).args, [{
cwd: "/cwd",
ui5HomeDir: "/ui5HomeDir"
}], "fetchAllVersions should be called with expected arguments");
});

test.serial("AbstractResolver: Static resolveVersion resolves 'MAJOR-prerelease'", async (t) => {
const {MyResolver} = t.context;
const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions")
.returns(["1.76.0", "1.77.0", "1.77.0-SNAPSHOT", "1.78.0", "1.79.0-SNAPSHOT"]);

const version = await MyResolver.resolveVersion("1-SNAPSHOT", {
cwd: "/cwd",
ui5HomeDir: "/ui5HomeDir"
});

t.is(version, "1.79.0-SNAPSHOT", "Resolved version should be correct");

t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once");
t.deepEqual(fetchAllVersionsStub.getCall(0).args, [{
cwd: "/cwd",
ui5HomeDir: "/ui5HomeDir"
}], "fetchAllVersions should be called with expected arguments");
});

test.serial("AbstractResolver: Static resolveVersion resolves 'MAJOR.MINOR'", async (t) => {
const {MyResolver} = t.context;
const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions")
Expand All @@ -595,6 +633,25 @@ test.serial("AbstractResolver: Static resolveVersion resolves 'MAJOR.MINOR'", as
}], "fetchAllVersions should be called with expected arguments");
});

test.serial("AbstractResolver: Static resolveVersion resolves 'MAJOR.MINOR-prerelease'", async (t) => {
const {MyResolver} = t.context;
const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions")
.returns(["1.76.0", "1.77.0", "1.77.0-SNAPSHOT", "1.78.0", "1.79.0-SNAPSHOT"]);

const version = await MyResolver.resolveVersion("1.79-SNAPSHOT", {
cwd: "/cwd",
ui5HomeDir: "/ui5HomeDir"
});

t.is(version, "1.79.0-SNAPSHOT", "Resolved version should be correct");

t.is(fetchAllVersionsStub.callCount, 1, "fetchAllVersions should be called once");
t.deepEqual(fetchAllVersionsStub.getCall(0).args, [{
cwd: "/cwd",
ui5HomeDir: "/ui5HomeDir"
}], "fetchAllVersions should be called with expected arguments");
});

test.serial("AbstractResolver: Static resolveVersion resolves 'MAJOR.MINOR.PATCH'", async (t) => {
const {MyResolver} = t.context;
const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions")
Expand All @@ -617,7 +674,7 @@ test.serial("AbstractResolver: Static resolveVersion resolves 'MAJOR.MINOR.PATCH
test.serial("AbstractResolver: Static resolveVersion resolves 'MAJOR.MINOR.PATCH-prerelease'", async (t) => {
const {MyResolver} = t.context;
const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions")
.returns(["1.76.0", "1.77.0", "1.78.0", "1.79.0-SNAPSHOT"]);
.returns(["1.76.0", "1.77.0", "1.77.0-SNAPSHOT", "1.78.0", "1.79.0-SNAPSHOT"]);

const version = await MyResolver.resolveVersion("1.79.0-SNAPSHOT", {
cwd: "/cwd",
Expand Down Expand Up @@ -758,20 +815,6 @@ test.serial("AbstractResolver: Static resolveVersion throws error for 'lts'", as
t.is(fetchAllVersionsStub.callCount, 0, "fetchAllVersions should not be called");
});

test.serial("AbstractResolver: Static resolveVersion throws error for '1'", async (t) => {
const {MyResolver} = t.context;
const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions");

const error = await t.throwsAsync(MyResolver.resolveVersion("1", {
cwd: "/cwd",
ui5HomeDir: "/ui5HomeDir"
}));

t.is(error.message, `Framework version specifier "1" is incorrect or not supported`);

t.is(fetchAllVersionsStub.callCount, 0, "fetchAllVersions should not be called");
});

test.serial("AbstractResolver: Static resolveVersion throws error for '1.x'", async (t) => {
const {MyResolver} = t.context;
const fetchAllVersionsStub = sinon.stub(MyResolver, "fetchAllVersions");
Expand Down

0 comments on commit 1f8cfdf

Please sign in to comment.