Skip to content

Commit 39854a1

Browse files
Fix installation of latest compatible version
CLI should install latest compatible version of runtimes, based on it's current version. For example in case CLI's version is "1.6.0", when `tns platform add android` is called, CLI should install latest 1.6.x version of android runtime, no matter if 1.7.x exists. This was not working correctly as it was using beta versions, incorrect sorting, etc. Fix it by using semver methods and always use the `@latest` tag of the package. In case it doesn't match CLI version, try finding latest matching tag with maxSatisfying method of semver, which dismisses beta versions. Required for publishing `@next` in npm.
1 parent 3965c4b commit 39854a1

File tree

2 files changed

+117
-65
lines changed

2 files changed

+117
-65
lines changed

lib/npm-installation-manager.ts

Lines changed: 26 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,9 @@ export class NpmInstallationManager implements INpmInstallationManager {
6565
public getLatestVersion(packageName: string): IFuture<string> {
6666
return (() => {
6767
let data = this.$npm.view(packageName, "dist-tags").wait();
68+
// data is something like :
69+
// { '1.0.1': { 'dist-tags': { latest: '1.0.1', next: '1.0.2-2016-02-25-181', next1: '1.0.2' } }
70+
// There's only one key and it's always the @latest tag.
6871
let latestVersion = _.first(_.keys(data));
6972
this.$logger.trace("Using version %s. ", latestVersion);
7073

@@ -74,22 +77,31 @@ export class NpmInstallationManager implements INpmInstallationManager {
7477

7578
public getLatestCompatibleVersion(packageName: string): IFuture<string> {
7679
return (() => {
80+
let cliVersionRange = `~${this.$staticConfig.version}`;
7781
let latestVersion = this.getLatestVersion(packageName).wait();
78-
let data = this.$npm.view(packageName, "versions").wait();
79-
let versions: string[] = data[latestVersion].versions;
80-
81-
let versionData = this.getVersionData(this.$staticConfig.version);
82-
83-
let compatibleVersions = _(versions)
84-
.map(ver => this.getVersionData(ver))
85-
.filter(verData => versionData.major === verData.major && versionData.minor === verData.minor)
86-
.sortBy(verData => verData.patch)
87-
.value();
88-
89-
let result = _.last(compatibleVersions) || this.getVersionData(latestVersion);
82+
if(semver.satisfies(latestVersion, cliVersionRange)) {
83+
return latestVersion;
84+
}
9085

91-
let latestCompatibleVersion = `${result.major}.${result.minor}.${result.patch}`;
92-
return latestCompatibleVersion;
86+
let data: any = this.$npm.view(packageName, "versions").wait();
87+
/* data is something like:
88+
{
89+
"1.1.0":{
90+
"versions":[
91+
"1.0.0",
92+
"1.0.1-2016-02-25-181",
93+
"1.0.1",
94+
"1.0.2-2016-02-25-182",
95+
"1.0.2",
96+
"1.1.0-2016-02-25-183",
97+
"1.1.0",
98+
"1.2.0-2016-02-25-184"
99+
]
100+
}
101+
}
102+
*/
103+
let versions: string[] = data && data[latestVersion] && data[latestVersion].versions;
104+
return semver.maxSatisfying(versions, cliVersionRange) || latestVersion;
93105
}).future<string>()();
94106
}
95107

@@ -204,10 +216,5 @@ export class NpmInstallationManager implements INpmInstallationManager {
204216
return this.$fs.exists(directory).wait() && this.$fs.enumerateFilesInDirectorySync(directory).length > 0;
205217
}).future<boolean>()();
206218
}
207-
208-
private getVersionData(version: string): IVersionData {
209-
let [ major, minor, patch ] = version.split(".");
210-
return { major, minor, patch };
211-
}
212219
}
213220
$injector.register("npmInstallationManager", NpmInstallationManager);

test/npm-installation-manager.ts

Lines changed: 91 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -50,65 +50,110 @@ function mockNpm(testInjector: IInjector, versions: string[], latestVersion: str
5050
});
5151
}
5252

53-
describe("Npm installation manager tests", () => {
54-
it("returns correct latest compatible version when only one exists", () => {
55-
let testInjector = createTestInjector();
56-
57-
let versions = ["1.4.0"];
58-
let latestVersion = "1.4.0";
59-
60-
mockNpm(testInjector, versions, latestVersion);
53+
interface ITestData {
54+
/**
55+
* All versions of the package, including the ones from another tags.
56+
*/
57+
versions: string[];
58+
59+
/**
60+
* The version under latest tag.
61+
*/
62+
packageLatestVersion: string;
63+
64+
/**
65+
* Version of nativescript-cli, based on which the version of the package that will be installed is detected.
66+
*/
67+
cliVersion: string;
68+
69+
/**
70+
* Expected result
71+
*/
72+
expectedResult: string;
73+
}
6174

62-
// Mock staticConfig.version
63-
let staticConfig = testInjector.resolve("staticConfig");
64-
staticConfig.version = "1.4.0";
75+
describe("Npm installation manager tests", () => {
76+
let testData: IDictionary<ITestData> = {
77+
"when there's only one available version and it matches CLI's version": {
78+
versions: ["1.4.0"],
79+
packageLatestVersion: "1.4.0",
80+
cliVersion: "1.4.0",
81+
expectedResult: "1.4.0"
82+
},
6583

66-
// Mock npmInstallationManager.getLatestVersion
67-
let npmInstallationManager = testInjector.resolve("npmInstallationManager");
68-
npmInstallationManager.getLatestVersion = (packageName: string) => Future.fromResult(latestVersion);
84+
"when there's only one available version and it is higher than match CLI's version": {
85+
versions: ["1.4.0"],
86+
packageLatestVersion: "1.4.0",
87+
cliVersion: "1.2.0",
88+
expectedResult: "1.4.0"
89+
},
6990

70-
let actualLatestCompatibleVersion = npmInstallationManager.getLatestCompatibleVersion("").wait();
71-
let expectedLatestCompatibleVersion = "1.4.0";
72-
assert.equal(actualLatestCompatibleVersion, expectedLatestCompatibleVersion);
73-
});
91+
"when there's only one available version and it is lower than CLI's version": {
92+
versions: ["1.4.0"],
93+
packageLatestVersion: "1.4.0",
94+
cliVersion: "1.6.0",
95+
expectedResult: "1.4.0"
96+
},
7497

75-
it("returns correct latest compatible version", () => {
76-
let testInjector = createTestInjector();
98+
"when there are multiple package versions and the latest one matches ~<cli-version>":{
99+
versions: ["1.2.0", "1.3.0", "1.3.1", "1.3.2", "1.3.3", "1.4.0"],
100+
packageLatestVersion: "1.3.3",
101+
cliVersion: "1.3.0",
102+
expectedResult: "1.3.3"
103+
},
77104

78-
let versions = ["1.2.0", "1.3.0", "1.3.1", "1.3.2", "1.3.3", "1.4.0"];
79-
let latestVersion = "1.3.3";
105+
"when there are multiple package versions and the latest one matches ~<cli-version> when there are newer matching versions but they are not under latest tag":{
106+
versions: ["1.2.0", "1.3.0", "1.3.1", "1.3.2", "1.3.3", "1.4.0"],
107+
packageLatestVersion: "1.3.2",
108+
cliVersion: "1.3.0",
109+
expectedResult: "1.3.2"
110+
},
80111

81-
mockNpm(testInjector, versions, latestVersion);
112+
"when there are multiple package versions and the latest one is lower than ~<cli-version>": {
113+
versions: ["1.2.0", "1.3.0", "1.3.1", "1.3.2", "1.3.3", "1.4.0"],
114+
packageLatestVersion: "1.4.0",
115+
cliVersion: "1.5.0",
116+
expectedResult: "1.4.0"
117+
},
82118

83-
// Mock staticConfig.version
84-
let staticConfig = testInjector.resolve("staticConfig");
85-
staticConfig.version = "1.3.0";
119+
"when there are multiple package versions and there's beta version matching CLI's semver": {
120+
versions: ["1.2.0", "1.3.0", "1.3.1", "1.4.0", "1.5.0-2016-02-25-182"],
121+
packageLatestVersion: "1.4.0",
122+
cliVersion: "1.5.0",
123+
expectedResult: "1.4.0"
124+
},
86125

87-
// Mock npmInstallationManager.getLatestVersion
88-
let npmInstallationManager = testInjector.resolve("npmInstallationManager");
89-
npmInstallationManager.getLatestVersion = (packageName: string) => Future.fromResult(latestVersion);
126+
"when there are multiple package versions and package's latest version is greater than CLI's version": {
127+
versions: ["1.2.0", "1.3.0", "1.3.1", "1.4.0", "1.5.0-2016-02-25-182", "1.5.0", "1.6.0"],
128+
packageLatestVersion: "1.6.0",
129+
cliVersion: "1.5.0",
130+
expectedResult: "1.5.0"
131+
},
90132

91-
let actualLatestCompatibleVersion = npmInstallationManager.getLatestCompatibleVersion("").wait();
92-
let expectedLatestCompatibleVersion = "1.3.3";
93-
assert.equal(actualLatestCompatibleVersion, expectedLatestCompatibleVersion);
94-
});
133+
"when there are multiple versions latest one does not match CLI's semver and other versions are not matching either": {
134+
versions: ["1.0.0", "1.0.1", "1.2.0", "1.3.1", "1.4.0", "1.5.0-2016-02-25-182", "1.5.0"],
135+
packageLatestVersion: "1.0.0",
136+
cliVersion: "1.1.0",
137+
expectedResult: "1.0.0"
138+
}
139+
};
95140

96-
it("returns correct latest compatible version", () => {
97-
let testInjector = createTestInjector();
141+
_.each(testData, (currentTestData: ITestData, testName: string) => {
142+
it(`returns correct latest compatible version, ${testName}`, () => {
143+
let testInjector = createTestInjector();
98144

99-
let versions = ["1.2.0", "1.3.0", "1.3.1", "1.3.2", "1.3.3", "1.4.0"];
100-
let latestVersion = _.last(versions);
101-
mockNpm(testInjector, versions, latestVersion);
145+
mockNpm(testInjector, currentTestData.versions, currentTestData.packageLatestVersion);
102146

103-
// Mock staticConfig.version
104-
let staticConfig = testInjector.resolve("staticConfig");
105-
staticConfig.version = "1.5.0";
147+
// Mock staticConfig.version
148+
let staticConfig = testInjector.resolve("staticConfig");
149+
staticConfig.version = currentTestData.cliVersion;
106150

107-
// Mock npmInstallationManager.getLatestVersion
108-
let npmInstallationManager = testInjector.resolve("npmInstallationManager");
109-
npmInstallationManager.getLatestVersion = (packageName: string) => Future.fromResult(latestVersion);
151+
// Mock npmInstallationManager.getLatestVersion
152+
let npmInstallationManager = testInjector.resolve("npmInstallationManager");
153+
npmInstallationManager.getLatestVersion = (packageName: string) => Future.fromResult(currentTestData.packageLatestVersion);
110154

111-
let actualLatestCompatibleVersion = npmInstallationManager.getLatestCompatibleVersion("").wait();
112-
assert.equal(actualLatestCompatibleVersion, latestVersion);
155+
let actualLatestCompatibleVersion = npmInstallationManager.getLatestCompatibleVersion("").wait();
156+
assert.equal(actualLatestCompatibleVersion, currentTestData.expectedResult);
157+
});
113158
});
114159
});

0 commit comments

Comments
 (0)