Skip to content

Commit 8cc23e4

Browse files
author
Martin Bektchiev
authored
fix(ios-inspector): Correctly update cached inspector package to latest compatible version (#3302)
* Currently it is never updated and is left at the first version ever installed in `~/.local`. As a result newly published inspector packages are not used unless the user installs them explicitly in the project or deletes the cache manually. * The iOS inspector package version must be determined according to project's iOS runtime version instead of the version of CLI as it has been till now
1 parent f0b02b9 commit 8cc23e4

File tree

4 files changed

+156
-128
lines changed

4 files changed

+156
-128
lines changed

lib/declarations.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ interface INpmInstallationManager {
3838
install(packageName: string, packageDir: string, options?: INpmInstallOptions): Promise<any>;
3939
getLatestVersion(packageName: string): Promise<string>;
4040
getNextVersion(packageName: string): Promise<string>;
41-
getLatestCompatibleVersion(packageName: string): Promise<string>;
41+
getLatestCompatibleVersion(packageName: string, referenceVersion?: string): Promise<string>;
4242
getInspectorFromCache(inspectorNpmPackageName: string, projectDir: string): Promise<string>;
4343
}
4444

lib/npm-installation-manager.ts

+33-23
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ export class NpmInstallationManager implements INpmInstallationManager {
99
private $options: IOptions,
1010
private $settingsService: ISettingsService,
1111
private $fs: IFileSystem,
12-
private $staticConfig: IStaticConfig) {
12+
private $staticConfig: IStaticConfig,
13+
private $projectDataService: IProjectDataService) {
1314
}
1415

1516
public async getLatestVersion(packageName: string): Promise<string> {
@@ -20,23 +21,21 @@ export class NpmInstallationManager implements INpmInstallationManager {
2021
return await this.getVersion(packageName, constants.PackageVersion.NEXT);
2122
}
2223

23-
public async getLatestCompatibleVersion(packageName: string): Promise<string> {
24-
const configVersion = this.$staticConfig.version;
25-
const isPreReleaseVersion = semver.prerelease(configVersion) !== null;
26-
let cliVersionRange = `~${semver.major(configVersion)}.${semver.minor(configVersion)}.0`;
27-
if (isPreReleaseVersion) {
28-
// if the user has some 0-19 pre-release version, include pre-release versions in the search query.
29-
cliVersionRange = `~${configVersion}`;
30-
}
31-
24+
public async getLatestCompatibleVersion(packageName: string, referenceVersion?: string): Promise<string> {
25+
referenceVersion = referenceVersion || this.$staticConfig.version;
26+
const isPreReleaseVersion = semver.prerelease(referenceVersion) !== null;
27+
// if the user has some v.v.v-prerelease-xx.xx pre-release version, include pre-release versions in the search query.
28+
const compatibleVersionRange = isPreReleaseVersion
29+
? `~${referenceVersion}`
30+
: `~${semver.major(referenceVersion)}.${semver.minor(referenceVersion)}.0`;
3231
const latestVersion = await this.getLatestVersion(packageName);
33-
if (semver.satisfies(latestVersion, cliVersionRange)) {
32+
if (semver.satisfies(latestVersion, compatibleVersionRange)) {
3433
return latestVersion;
3534
}
3635

3736
const data = await this.$npm.view(packageName, { "versions": true });
3837

39-
const maxSatisfying = semver.maxSatisfying(data, cliVersionRange);
38+
const maxSatisfying = semver.maxSatisfying(data, compatibleVersionRange);
4039
return maxSatisfying || latestVersion;
4140
}
4241

@@ -59,21 +58,32 @@ export class NpmInstallationManager implements INpmInstallationManager {
5958
const inspectorPath = path.join(projectDir, constants.NODE_MODULES_FOLDER_NAME, inspectorNpmPackageName);
6059

6160
// local installation takes precedence over cache
62-
if (!this.inspectorAlreadyInstalled(inspectorPath)) {
63-
const cachePath = path.join(this.$settingsService.getProfileDir(), constants.INSPECTOR_CACHE_DIRNAME);
64-
this.prepareCacheDir(cachePath);
65-
const pathToPackageInCache = path.join(cachePath, constants.NODE_MODULES_FOLDER_NAME, inspectorNpmPackageName);
66-
67-
if (!this.$fs.exists(pathToPackageInCache)) {
68-
const version = await this.getLatestCompatibleVersion(inspectorNpmPackageName);
69-
await this.$childProcess.exec(`npm install ${inspectorNpmPackageName}@${version} --prefix ${cachePath}`);
61+
if (this.inspectorAlreadyInstalled(inspectorPath)) {
62+
return inspectorPath;
63+
}
64+
65+
const cachePath = path.join(this.$settingsService.getProfileDir(), constants.INSPECTOR_CACHE_DIRNAME);
66+
this.prepareCacheDir(cachePath);
67+
const pathToPackageInCache = path.join(cachePath, constants.NODE_MODULES_FOLDER_NAME, inspectorNpmPackageName);
68+
const iOSFrameworkNSValue = this.$projectDataService.getNSValue(projectDir, constants.TNS_IOS_RUNTIME_NAME);
69+
const version = await this.getLatestCompatibleVersion(inspectorNpmPackageName, iOSFrameworkNSValue.version);
70+
let shouldInstall = !this.$fs.exists(pathToPackageInCache);
71+
72+
if (!shouldInstall) {
73+
try {
74+
const installedVersion = this.$fs.readJson(path.join(pathToPackageInCache, constants.PACKAGE_JSON_FILE_NAME)).version;
75+
shouldInstall = version !== installedVersion;
76+
} catch (err) {
77+
shouldInstall = true;
7078
}
79+
}
7180

72-
this.$logger.out("Using inspector from cache.");
73-
return pathToPackageInCache;
81+
if (shouldInstall) {
82+
await this.$childProcess.exec(`npm install ${inspectorNpmPackageName}@${version} --prefix ${cachePath}`);
7483
}
7584

76-
return inspectorPath;
85+
this.$logger.out("Using inspector from cache.");
86+
return pathToPackageInCache;
7787
}
7888

7989
private prepareCacheDir(cacheDirName: string): void {

lib/services/ios-project-service.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
6464
const projectRoot = path.join(projectData.platformsDir, this.$devicePlatformsConstants.iOS.toLowerCase());
6565

6666
this._platformData = {
67-
frameworkPackageName: "tns-ios",
67+
frameworkPackageName: constants.TNS_IOS_RUNTIME_NAME,
6868
normalizedPlatformName: "iOS",
6969
appDestinationDirectoryPath: path.join(projectRoot, projectData.projectName),
7070
platformProjectService: this,

test/npm-installation-manager.ts

+121-103
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import * as StaticConfigLib from "../lib/config";
1010
import * as yok from "../lib/common/yok";
1111
import ChildProcessLib = require("../lib/common/child-process");
1212
import { SettingsService } from "../lib/common/test/unit-tests/stubs";
13+
import { ProjectDataService } from "../lib/services/project-data-service";
1314

1415
function createTestInjector(): IInjector {
1516
const testInjector = new yok.Yok();
@@ -23,6 +24,7 @@ function createTestInjector(): IInjector {
2324
testInjector.register("staticConfig", StaticConfigLib.StaticConfig);
2425
testInjector.register("childProcess", ChildProcessLib.ChildProcess);
2526
testInjector.register("settingsService", SettingsService);
27+
testInjector.register("projectDataService", ProjectDataService);
2628

2729
testInjector.register("npmInstallationManager", NpmInstallationManagerLib.NpmInstallationManager);
2830

@@ -57,121 +59,137 @@ interface ITestData {
5759
*/
5860
cliVersion: string;
5961

62+
/**
63+
* Version, based on which the version of the package that will be installed is detected.
64+
* Used when semantically the correct reference version is different than the CLI version.
65+
* (e.g. inspector package version should be determined by the project's ios runtime version)
66+
*/
67+
referenceVersion?: string;
68+
6069
/**
6170
* Expected result
6271
*/
6372
expectedResult: string;
6473
}
6574

6675
describe("Npm installation manager tests", () => {
67-
const testData: IDictionary<ITestData> = {
68-
"when there's only one available version and it matches CLI's version": {
69-
versions: ["1.4.0"],
70-
packageLatestVersion: "1.4.0",
71-
cliVersion: "1.4.0",
72-
expectedResult: "1.4.0"
73-
},
74-
75-
"when there's only one available version and it is higher than match CLI's version": {
76-
versions: ["1.4.0"],
77-
packageLatestVersion: "1.4.0",
78-
cliVersion: "1.2.0",
79-
expectedResult: "1.4.0"
80-
},
81-
82-
"when there's only one available version and it is lower than CLI's version": {
83-
versions: ["1.4.0"],
84-
packageLatestVersion: "1.4.0",
85-
cliVersion: "1.6.0",
86-
expectedResult: "1.4.0"
87-
},
88-
89-
"when there are multiple package versions and the latest one matches ~<cli-version>": {
90-
versions: ["1.2.0", "1.3.0", "1.3.1", "1.3.2", "1.3.3", "1.4.0"],
91-
packageLatestVersion: "1.3.3",
92-
cliVersion: "1.3.0",
93-
expectedResult: "1.3.3"
94-
},
95-
96-
"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": {
97-
versions: ["1.2.0", "1.3.0", "1.3.1", "1.3.2", "1.3.3", "1.4.0"],
98-
packageLatestVersion: "1.3.2",
99-
cliVersion: "1.3.0",
100-
expectedResult: "1.3.2"
101-
},
102-
103-
"when there are multiple package versions and the latest one is lower than ~<cli-version>": {
104-
versions: ["1.2.0", "1.3.0", "1.3.1", "1.3.2", "1.3.3", "1.4.0"],
105-
packageLatestVersion: "1.4.0",
106-
cliVersion: "1.5.0",
107-
expectedResult: "1.4.0"
108-
},
109-
110-
"when there are multiple package versions and there's beta version matching CLI's semver": {
111-
versions: ["1.2.0", "1.3.0", "1.3.1", "1.4.0", "1.5.0-2016-02-25-182"],
112-
packageLatestVersion: "1.4.0",
113-
cliVersion: "1.5.0",
114-
expectedResult: "1.4.0"
115-
},
116-
117-
"when there are multiple package versions and package's latest version is greater than CLI's version": {
118-
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"],
119-
packageLatestVersion: "1.6.0",
120-
cliVersion: "1.5.0",
121-
expectedResult: "1.5.0"
122-
},
123-
124-
"when there are multiple versions latest one does not match CLI's semver and other versions are not matching either": {
125-
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"],
126-
packageLatestVersion: "1.0.0",
127-
cliVersion: "1.1.0",
128-
expectedResult: "1.0.0"
129-
},
130-
131-
"when CLI's version is beta (has dash) latest matching beta version is returned": {
132-
versions: ["1.0.0", "1.0.1", "1.4.0", "1.5.0-2016-02-25-182", "1.5.0-2016-02-26-202"],
133-
packageLatestVersion: "1.4.0",
134-
cliVersion: "1.5.0-182",
135-
expectedResult: "1.5.0-2016-02-26-202"
136-
},
137-
138-
"when CLI's version is beta (has dash) latest matching official version is returned when beta versions do not match": {
139-
versions: ["1.0.0", "1.0.1", "1.4.0", "1.5.0-2016-02-25-182", "1.5.0-2016-02-26-202"],
140-
packageLatestVersion: "1.4.0",
141-
cliVersion: "1.6.0-2016-03-01-182",
142-
expectedResult: "1.4.0"
143-
},
144-
145-
"when CLI's version is beta (has dash) latest matching official version is returned when beta versions do not match (when the prerelease of CLI is higher than prerelease version of runtime)": {
146-
versions: ["1.0.0", "1.0.1", "1.4.0", "1.6.0-2016-02-25-182", "1.6.0-2016-02-26-202"],
147-
packageLatestVersion: "1.4.0",
148-
cliVersion: "1.6.0-2016-10-01-182",
149-
expectedResult: "1.4.0"
150-
},
151-
"When CLI Version has patch version larger than an existing package, should return max compliant package from the same major.minor version": {
152-
versions: ["1.0.0", "1.0.1", "1.4.0", "2.5.0", "2.5.1", "2.5.2", "3.0.0"],
153-
packageLatestVersion: "3.0.0",
154-
cliVersion: "2.5.4",
155-
expectedResult: "2.5.2"
156-
}
157-
};
76+
describe("getLatestCompatibleVersion", () => {
77+
const testData: IDictionary<ITestData> = {
78+
"when there's only one available version and it matches CLI's version": {
79+
versions: ["1.4.0"],
80+
packageLatestVersion: "1.4.0",
81+
cliVersion: "1.4.0",
82+
expectedResult: "1.4.0"
83+
},
84+
85+
"when there's only one available version and it is higher than match CLI's version": {
86+
versions: ["1.4.0"],
87+
packageLatestVersion: "1.4.0",
88+
cliVersion: "1.2.0",
89+
expectedResult: "1.4.0"
90+
},
91+
92+
"when there's only one available version and it is lower than CLI's version": {
93+
versions: ["1.4.0"],
94+
packageLatestVersion: "1.4.0",
95+
cliVersion: "1.6.0",
96+
expectedResult: "1.4.0"
97+
},
98+
99+
"when there are multiple package versions and the latest one matches ~<cli-version>": {
100+
versions: ["1.2.0", "1.3.0", "1.3.1", "1.3.2", "1.3.3", "1.4.0"],
101+
packageLatestVersion: "1.3.3",
102+
cliVersion: "1.3.0",
103+
expectedResult: "1.3.3"
104+
},
105+
106+
"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": {
107+
versions: ["1.2.0", "1.3.0", "1.3.1", "1.3.2", "1.3.3", "1.4.0"],
108+
packageLatestVersion: "1.3.2",
109+
cliVersion: "1.3.0",
110+
expectedResult: "1.3.2"
111+
},
112+
113+
"when there are multiple package versions and the latest one is lower than ~<cli-version>": {
114+
versions: ["1.2.0", "1.3.0", "1.3.1", "1.3.2", "1.3.3", "1.4.0"],
115+
packageLatestVersion: "1.4.0",
116+
cliVersion: "1.5.0",
117+
expectedResult: "1.4.0"
118+
},
119+
120+
"when there are multiple package versions and there's beta version matching CLI's semver": {
121+
versions: ["1.2.0", "1.3.0", "1.3.1", "1.4.0", "1.5.0-2016-02-25-182"],
122+
packageLatestVersion: "1.4.0",
123+
cliVersion: "1.5.0",
124+
expectedResult: "1.4.0"
125+
},
126+
127+
"when there are multiple package versions and package's latest version is greater than CLI's version": {
128+
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"],
129+
packageLatestVersion: "1.6.0",
130+
cliVersion: "1.5.0",
131+
expectedResult: "1.5.0"
132+
},
133+
134+
"when there are multiple versions latest one does not match CLI's semver and other versions are not matching either": {
135+
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"],
136+
packageLatestVersion: "1.0.0",
137+
cliVersion: "1.1.0",
138+
expectedResult: "1.0.0"
139+
},
140+
141+
"when CLI's version is beta (has dash) latest matching beta version is returned": {
142+
versions: ["1.0.0", "1.0.1", "1.4.0", "1.5.0-2016-02-25-182", "1.5.0-2016-02-26-202"],
143+
packageLatestVersion: "1.4.0",
144+
cliVersion: "1.5.0-182",
145+
expectedResult: "1.5.0-2016-02-26-202"
146+
},
147+
148+
"when CLI's version is beta (has dash) latest matching official version is returned when beta versions do not match": {
149+
versions: ["1.0.0", "1.0.1", "1.4.0", "1.5.0-2016-02-25-182", "1.5.0-2016-02-26-202"],
150+
packageLatestVersion: "1.4.0",
151+
cliVersion: "1.6.0-2016-03-01-182",
152+
expectedResult: "1.4.0"
153+
},
154+
155+
"when CLI's version is beta (has dash) latest matching official version is returned when beta versions do not match (when the prerelease of CLI is higher than prerelease version of runtime)": {
156+
versions: ["1.0.0", "1.0.1", "1.4.0", "1.6.0-2016-02-25-182", "1.6.0-2016-02-26-202"],
157+
packageLatestVersion: "1.4.0",
158+
cliVersion: "1.6.0-2016-10-01-182",
159+
expectedResult: "1.4.0"
160+
},
161+
"When CLI Version has patch version larger than an existing package, should return max compliant package from the same major.minor version": {
162+
versions: ["1.0.0", "1.0.1", "1.4.0", "2.5.0", "2.5.1", "2.5.2", "3.0.0"],
163+
packageLatestVersion: "3.0.0",
164+
cliVersion: "2.5.4",
165+
expectedResult: "2.5.2"
166+
},
167+
"When reference version is specified as argument": {
168+
versions: ["122.0.4", "123.0.0", "123.0.1", "123.1.0", "124.0.0"],
169+
packageLatestVersion: "124.0.0",
170+
cliVersion: "0.0.0", // should not matter
171+
expectedResult: "123.0.1",
172+
referenceVersion: "123.0.5"
173+
}
174+
};
158175

159-
_.each(testData, (currentTestData: ITestData, testName: string) => {
160-
it(`returns correct latest compatible version, ${testName}`, async () => {
161-
const testInjector = createTestInjector();
176+
_.each(testData, (currentTestData: ITestData, testName: string) => {
177+
it(`returns correct latest compatible version, ${testName}`, async () => {
178+
const testInjector = createTestInjector();
162179

163-
mockNpm(testInjector, currentTestData.versions, currentTestData.packageLatestVersion);
180+
mockNpm(testInjector, currentTestData.versions, currentTestData.packageLatestVersion);
164181

165-
// Mock staticConfig.version
166-
const staticConfig = testInjector.resolve("staticConfig");
167-
staticConfig.version = currentTestData.cliVersion;
182+
// Mock staticConfig.version
183+
const staticConfig = testInjector.resolve("staticConfig");
184+
staticConfig.version = currentTestData.cliVersion;
168185

169-
// Mock npmInstallationManager.getLatestVersion
170-
const npmInstallationManager = testInjector.resolve("npmInstallationManager");
171-
npmInstallationManager.getLatestVersion = (packageName: string) => Promise.resolve(currentTestData.packageLatestVersion);
186+
// Mock npmInstallationManager.getLatestVersion
187+
const npmInstallationManager = testInjector.resolve("npmInstallationManager");
188+
npmInstallationManager.getLatestVersion = (packageName: string) => Promise.resolve(currentTestData.packageLatestVersion);
172189

173-
const actualLatestCompatibleVersion = await npmInstallationManager.getLatestCompatibleVersion("");
174-
assert.equal(actualLatestCompatibleVersion, currentTestData.expectedResult);
190+
const actualLatestCompatibleVersion = await npmInstallationManager.getLatestCompatibleVersion("", currentTestData.referenceVersion);
191+
assert.equal(actualLatestCompatibleVersion, currentTestData.expectedResult);
192+
});
175193
});
176194
});
177195
});

0 commit comments

Comments
 (0)