From 32e7de8e46fc624b2972e12362cc35ae8a1a4976 Mon Sep 17 00:00:00 2001 From: Yosif Yosifov Date: Mon, 24 Apr 2017 16:50:25 +0300 Subject: [PATCH 01/19] Extract entitlements and xcconfig services. --- lib/bootstrap.ts | 2 + lib/constants.ts | 2 + lib/services/ios-entitlements-service.ts | 86 ++++++++++++++++++++++++ lib/services/ios-project-service.ts | 45 +++++-------- lib/services/xcconfig-service.ts | 37 ++++++++++ 5 files changed, 145 insertions(+), 27 deletions(-) create mode 100644 lib/services/ios-entitlements-service.ts create mode 100644 lib/services/xcconfig-service.ts diff --git a/lib/bootstrap.ts b/lib/bootstrap.ts index 5c7a73533d..c91e1667b5 100644 --- a/lib/bootstrap.ts +++ b/lib/bootstrap.ts @@ -9,8 +9,10 @@ $injector.require("projectData", "./project-data"); $injector.require("projectDataService", "./services/project-data-service"); $injector.requirePublic("projectService", "./services/project-service"); $injector.require("androidProjectService", "./services/android-project-service"); +$injector.require("iOSEntitlementsService", "./services/ios-entitlements-service"); $injector.require("iOSProjectService", "./services/ios-project-service"); $injector.require("iOSProvisionService", "./services/ios-provision-service"); +$injector.require("xCConfigService", "./services/xcconfig-service"); $injector.require("cocoapodsService", "./services/cocoapods-service"); diff --git a/lib/constants.ts b/lib/constants.ts index 21a4488697..dbaf54b816 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -16,6 +16,8 @@ export const TEST_RUNNER_NAME = "nativescript-unit-test-runner"; export const LIVESYNC_EXCLUDED_FILE_PATTERNS = ["**/*.js.map", "**/*.ts"]; export const XML_FILE_EXTENSION = ".xml"; export const PLATFORMS_DIR_NAME = "platforms"; +export const IOS_PLATFORM_NAME = "ios"; +export const IOS_PLATFORM_NORMALIZED_NAME = "iOS"; export class PackageVersion { static NEXT = "next"; diff --git a/lib/services/ios-entitlements-service.ts b/lib/services/ios-entitlements-service.ts new file mode 100644 index 0000000000..b07eef30ff --- /dev/null +++ b/lib/services/ios-entitlements-service.ts @@ -0,0 +1,86 @@ +import * as path from "path"; +import * as constants from "../constants"; +import { PlistSession } from "plist-merge-patch"; + +export class IOSEntitlementsService { + constructor(private $fs: IFileSystem, + private $injector: IInjector, + private $logger: ILogger) { + } + + private getDefaultEntitlementsName() : string { + return "app.entitlements"; + } + + private getDefaultAppEntitlementsPath(projectData: IProjectData) : string { + let entitlementsName = this.getDefaultEntitlementsName(); + let entitlementsPath = path.join(projectData.projectDir, + constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME, + constants.IOS_PLATFORM_NORMALIZED_NAME, + entitlementsName); + return entitlementsPath; + } + + private getPlatformsEntitlementsPath(projectData: IProjectData) : string { + return path.join(projectData.platformsDir, constants.IOS_PLATFORM_NAME, + projectData.projectName, projectData.projectName + ".entitlements"); + } + + public async merge(projectData: IProjectData): Promise { + let session = new PlistSession({ log: (txt: string) => this.$logger.trace("App.entitlements: " + txt) }); + + let projectDir = projectData.projectDir; + let makePatch = (plistPath: string) => { + if (!this.$fs.exists(plistPath)) { + this.$logger.trace("No plist found at: " + plistPath); + return; + } + + this.$logger.trace("Schedule merge plist at: " + plistPath); + session.patch({ + name: path.relative(projectDir, plistPath), + read: () => this.$fs.readText(plistPath) + }); + }; + + // Start with a default empty entitlements file. + session.patch({ + name: "Default entitlements file", + read: () => + ` + + + ` + }); + + let allPlugins = await this.getAllInstalledPlugins(projectData); + for (let plugin of allPlugins) { + let pluginInfoPlistPath = path.join(plugin.pluginPlatformsFolderPath(constants.IOS_PLATFORM_NAME), this.getDefaultEntitlementsName()); + makePatch(pluginInfoPlistPath); + } + + // for older ios-app-templates or if has been deleted. + let appEntitlementsPath = this.getDefaultAppEntitlementsPath(projectData); + if (this.$fs.exists(appEntitlementsPath)) { + makePatch(appEntitlementsPath); + } + + let plistContent = session.build(); + this.$logger.trace("App.entitlements: Write to: " + this.getPlatformsEntitlementsPath(projectData)); + this.$fs.writeFile(this.getPlatformsEntitlementsPath(projectData), plistContent); + return; + } + + public async patchXcconfigFile(projectData: IProjectData) { + //CODE_SIGN_ENTITLEMENTS = fgomobile/fgomobile.entitlements + //let entitlementsRelativePath = path.join(projectData.projectName, projectData.projectName + ".entitlements"); + + return; + } + + private getAllInstalledPlugins(projectData: IProjectData): Promise { + return (this.$injector.resolve("pluginsService")).getAllInstalledPlugins(projectData); + } +} + +$injector.register("iOSEntitlementsService", IOSEntitlementsService); diff --git a/lib/services/ios-project-service.ts b/lib/services/ios-project-service.ts index f68525d998..4e08304e85 100644 --- a/lib/services/ios-project-service.ts +++ b/lib/services/ios-project-service.ts @@ -13,6 +13,8 @@ import * as temp from "temp"; import * as plist from "plist"; import { Xcode } from "pbxproj-dom/xcode"; import { IOSProvisionService } from "./ios-provision-service"; +import { IOSEntitlementsService } from "./ios-entitlements-service"; +import { XCConfigService } from "./xcconfig-service"; export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServiceBase implements IPlatformProjectService { private static XCODE_PROJECT_EXT_NAME = ".xcodeproj"; @@ -42,7 +44,9 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ private $pluginVariablesService: IPluginVariablesService, private $xcprojService: IXcprojService, private $iOSProvisionService: IOSProvisionService, - private $sysInfo: ISysInfo) { + private $iOSEntitlementsService: IOSEntitlementsService, + private $sysInfo: ISysInfo, + private xCConfigService: XCConfigService) { super($fs, $projectDataService); } @@ -666,6 +670,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f public async processConfigurationFilesFromAppResources(release: boolean, projectData: IProjectData): Promise { await this.mergeInfoPlists(projectData); + await this.$iOSEntitlementsService.merge(projectData); await this.mergeProjectXcconfigFiles(release, projectData); for (let pluginData of await this.getAllInstalledPlugins(projectData)) { await this.$pluginVariablesService.interpolatePluginVariables(pluginData, this.getPlatformData(projectData).configurationFilePath, projectData); @@ -1147,43 +1152,29 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f return null; } - private readXCConfig(flag: string, projectData: IProjectData): string { - let xcconfigFile = path.join(projectData.appResourcesDirectoryPath, this.getPlatformData(projectData).normalizedPlatformName, "build.xcconfig"); - if (this.$fs.exists(xcconfigFile)) { - let text = this.$fs.readText(xcconfigFile); - let teamId: string; - text.split(/\r?\n/).forEach((line) => { - line = line.replace(/\/(\/)[^\n]*$/, ""); - if (line.indexOf(flag) >= 0) { - teamId = line.split("=")[1].trim(); - if (teamId[teamId.length - 1] === ';') { - teamId = teamId.slice(0, -1); - } - } - }); - if (teamId) { - return teamId; - } - } + private getBuildXCConfigFilePath(projectData: IProjectData): string { + let buildXCConfig = path.join(projectData.appResourcesDirectoryPath, + this.getPlatformData(projectData).normalizedPlatformName, "build.xcconfig"); + return buildXCConfig; + } + + private readTeamId(projectData: IProjectData): string { + let teamId = this.xCConfigService.readPropertyValue(this.getBuildXCConfigFilePath(projectData), "DEVELOPMENT_TEAM"); let fileName = path.join(this.getPlatformData(projectData).projectRoot, "teamid"); if (this.$fs.exists(fileName)) { - return this.$fs.readText(fileName); + teamId = this.$fs.readText(fileName); } - return null; - } - - private readTeamId(projectData: IProjectData): string { - return this.readXCConfig("DEVELOPMENT_TEAM", projectData); + return teamId; } private readXCConfigProvisioningProfile(projectData: IProjectData): string { - return this.readXCConfig("PROVISIONING_PROFILE", projectData); + return this.xCConfigService.readPropertyValue(this.getBuildXCConfigFilePath(projectData), "PROVISIONING_PROFILE"); } private readXCConfigProvisioningProfileForIPhoneOs(projectData: IProjectData): string { - return this.readXCConfig("PROVISIONING_PROFILE[sdk=iphoneos*]", projectData); + return this.xCConfigService.readPropertyValue(this.getBuildXCConfigFilePath(projectData), "PROVISIONING_PROFILE[sdk=iphoneos*]"); } private async getDevelopmentTeam(projectData: IProjectData, teamId?: string): Promise { diff --git a/lib/services/xcconfig-service.ts b/lib/services/xcconfig-service.ts new file mode 100644 index 0000000000..392550462c --- /dev/null +++ b/lib/services/xcconfig-service.ts @@ -0,0 +1,37 @@ +export class XCConfigService { + constructor(private $fs: IFileSystem) { + } + + /** + * Returns the Value of a Property from a XC Config file. + * @param xcconfigFilePath + * @param propertyName + */ + public readPropertyValue(xcconfigFilePath: string, propertyName: string): string { + if (this.$fs.exists(xcconfigFilePath)) { + let text = this.$fs.readText(xcconfigFilePath); + + let property: string; + text.split(/\r?\n/).forEach((line) => { + line = line.replace(/\/(\/)[^\n]*$/, ""); + if (line.indexOf(propertyName) >= 0) { + // todo this can have undefined. + property = line.split("=")[1].trim(); + // todo this can have undefined. + if (property[property.length - 1] === ';') { + property = property.slice(0, -1); + } + } + }); + + // what if the property is set to empty value? + if (property) { + return property; + } + } + + return null; + } +} + +$injector.register("xCConfigService", XCConfigService); \ No newline at end of file From e14ec25fd3ee2a1ca7f38209dc5b863c74dff528 Mon Sep 17 00:00:00 2001 From: Yosif Yosifov Date: Mon, 24 Apr 2017 17:12:20 +0300 Subject: [PATCH 02/19] Fix tests setup with the new services in ios-project-service tests --- test/ios-project-service.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/ios-project-service.ts b/test/ios-project-service.ts index abf9288b69..ee2c0f7860 100644 --- a/test/ios-project-service.ts +++ b/test/ios-project-service.ts @@ -6,6 +6,8 @@ import * as FileSystemLib from "../lib/common/file-system"; import * as HostInfoLib from "../lib/common/host-info"; import * as iOSProjectServiceLib from "../lib/services/ios-project-service"; import { IOSProjectService } from "../lib/services/ios-project-service"; +import { IOSEntitlementsService } from "../lib/services/ios-entitlements-service"; +import { XCConfigService } from "../lib/services/xcconfig-service"; import * as LoggerLib from "../lib/common/logger"; import * as OptionsLib from "../lib/options"; import * as yok from "../lib/common/yok"; @@ -51,6 +53,8 @@ function createTestInjector(projectPath: string, projectName: string): IInjector testInjector.register("cocoapodsService", CocoaPodsService); testInjector.register("iOSProjectService", iOSProjectServiceLib.IOSProjectService); testInjector.register("iOSProvisionService", {}); + testInjector.register("xCConfigService", XCConfigService); + testInjector.register("iOSEntitlementsService", IOSEntitlementsService); testInjector.register("logger", LoggerLib.Logger); testInjector.register("options", OptionsLib.Options); testInjector.register("projectData", { From a1611176a7da41feb517334b5a94d7c712ba8b59 Mon Sep 17 00:00:00 2001 From: Yosif Yosifov Date: Mon, 24 Apr 2017 17:24:55 +0300 Subject: [PATCH 03/19] Adding newline in xcconfig-service to fix lint error --- lib/services/xcconfig-service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/services/xcconfig-service.ts b/lib/services/xcconfig-service.ts index 392550462c..5028408101 100644 --- a/lib/services/xcconfig-service.ts +++ b/lib/services/xcconfig-service.ts @@ -34,4 +34,4 @@ export class XCConfigService { } } -$injector.register("xCConfigService", XCConfigService); \ No newline at end of file +$injector.register("xCConfigService", XCConfigService); From ee7b20c648fd6ff37d95d6d6669d01fe13670742 Mon Sep 17 00:00:00 2001 From: Yosif Yosifov Date: Tue, 25 Apr 2017 17:19:51 +0300 Subject: [PATCH 04/19] Add Tests for XCConfig-Service --- lib/services/xcconfig-service.ts | 17 +-- test/xcconfig-service.ts | 188 +++++++++++++++++++++++++++++++ 2 files changed, 198 insertions(+), 7 deletions(-) create mode 100644 test/xcconfig-service.ts diff --git a/lib/services/xcconfig-service.ts b/lib/services/xcconfig-service.ts index 5028408101..63fb6b2fca 100644 --- a/lib/services/xcconfig-service.ts +++ b/lib/services/xcconfig-service.ts @@ -12,20 +12,23 @@ export class XCConfigService { let text = this.$fs.readText(xcconfigFilePath); let property: string; + let isPropertyParsed: boolean = false; text.split(/\r?\n/).forEach((line) => { line = line.replace(/\/(\/)[^\n]*$/, ""); if (line.indexOf(propertyName) >= 0) { - // todo this can have undefined. - property = line.split("=")[1].trim(); - // todo this can have undefined. - if (property[property.length - 1] === ';') { - property = property.slice(0, -1); + let parts = line.split("="); + if (parts.length > 1 && parts[1]) { + property = parts[1].trim(); + isPropertyParsed = true; + if (property[property.length - 1] === ';') { + property = property.slice(0, -1); + } } } }); - // what if the property is set to empty value? - if (property) { + if (isPropertyParsed) { + // property can be an empty string, so we don't check for that. return property; } } diff --git a/test/xcconfig-service.ts b/test/xcconfig-service.ts new file mode 100644 index 0000000000..099ee44b47 --- /dev/null +++ b/test/xcconfig-service.ts @@ -0,0 +1,188 @@ +import temp = require("temp"); +import { assert } from "chai"; +import { XCConfigService } from "../lib/services/xcconfig-service"; +import * as yok from "../lib/common/yok"; + +// start tracking temporary folders/files +temp.track(); + +describe("XCConfig Service Tests", () => { + const createTestInjector = (): IInjector => { + const testInjector = new yok.Yok(); + testInjector.register("fs", { + exists: (path: string) => { + return true; + } + }); + + testInjector.register('xCConfigService', XCConfigService); + + return testInjector; + }; + + const assertPropertyValues = (expected: any, injector: IInjector) => { + let service = getXCConfigService(injector); + _.forOwn(expected, (value, key) => { + let actual = service.readPropertyValue("any", key); + assert.equal(actual, value); + }); + }; + + function getXCConfigService(injector: IInjector): XCConfigService { + return injector.resolve("xCConfigService"); + } + + function getFileSystemMock(injector: IInjector): any { + return injector.resolve('$fs'); + } + + describe("Read Property Value", () => { + it("Return empty value, for unexistent file", () => { + let injector = createTestInjector(); + injector.register("fs", { + exists: (path: string) => { + return false; + } + }); + + let service = getXCConfigService(injector); + let actual = service.readPropertyValue("any", "any"); + + assert.isNull(actual); + }); + + it("Returns correct value for well-formatted document", () => { + let injector = createTestInjector(); + let fs = getFileSystemMock(injector); + fs.readText = (filename: string, options?: IReadFileOptions | string): string => { + return `// You can add custom settings here + // for example you can uncomment the following line to force distribution code signing + CODE_SIGN_IDENTITY = iPhone Distribution + // To build for device with XCode 8 you need to specify your development team. More info: https://developer.apple.com/library/prerelease/content/releasenotes/DeveloperTools/RN-Xcode/Introduction.html + // DEVELOPMENT_TEAM = YOUR_TEAM_ID; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;`; + }; + + let expected = { + 'ASSETCATALOG_COMPILER_APPICON_NAME': 'AppIcon', + 'ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME': 'LaunchImage', + 'CODE_SIGN_IDENTITY': 'iPhone Distribution' + }; + + assertPropertyValues(expected, injector); + }); + + it("Returns correct value for values with missing ; at the end of the line.", () => { + let injector = createTestInjector(); + let fs = getFileSystemMock(injector); + fs.readText = (filename: string, options?: IReadFileOptions | string): string => { + return `// You can add custom settings here + // for example you can uncomment the following line to force distribution code signing + CODE_SIGN_IDENTITY = iPhone Distribution + // To build for device with XCode 8 you need to specify your development team. More info: https://developer.apple.com/library/prerelease/content/releasenotes/DeveloperTools/RN-Xcode/Introduction.html + // DEVELOPMENT_TEAM = YOUR_TEAM_ID + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage`; + }; + + let expected = { + 'ASSETCATALOG_COMPILER_APPICON_NAME': 'AppIcon', + 'ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME': 'LaunchImage', + 'CODE_SIGN_IDENTITY': 'iPhone Distribution' + }; + + assertPropertyValues(expected, injector); + }); + + it("Doesn't read value from a commented-out line.", () => { + let injector = createTestInjector(); + let fs = getFileSystemMock(injector); + fs.readText = (filename: string, options?: IReadFileOptions | string): string => { + return `// DEVELOPMENT_TEAM = YOUR_TEAM_ID; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;`; + }; + + let expected = { + 'ASSETCATALOG_COMPILER_APPICON_NAME': 'AppIcon', + 'DEVELOPMENT_TEAM': null + }; + + assertPropertyValues(expected, injector); + }); + + it("Returns correct empty value.", () => { + let injector = createTestInjector(); + let fs = getFileSystemMock(injector); + fs.readText = (filename: string, options?: IReadFileOptions | string): string => { + return ` + ASSETCATALOG_COMPILER_APPICON_NAME = ; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + `; + }; + + let expected = { + 'ASSETCATALOG_COMPILER_APPICON_NAME': '', + 'ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME': 'LaunchImage' + }; + + assertPropertyValues(expected, injector); + }); + + it("First part only property doesn't break the service.", () => { + let injector = createTestInjector(); + let fs = getFileSystemMock(injector); + fs.readText = (filename: string, options?: IReadFileOptions | string): string => { + return `ASSETCATALOG_COMPILER_APPICON_NAME`; + }; + + let expected = { + 'ASSETCATALOG_COMPILER_APPICON_NAME': null + }; + + assertPropertyValues(expected, injector); + }); + + it("Invalid config property value with = doesn't break the service.", () => { + let injector = createTestInjector(); + let fs = getFileSystemMock(injector); + fs.readText = (filename: string, options?: IReadFileOptions | string): string => { + return `ASSETCATALOG_COMPILER_APPICON_NAME=`; + }; + + let expected = { + 'ASSETCATALOG_COMPILER_APPICON_NAME': null + }; + + assertPropertyValues(expected, injector); + }); + + it("Property with space is read correctly.", () => { + let injector = createTestInjector(); + let fs = getFileSystemMock(injector); + fs.readText = (filename: string, options?: IReadFileOptions | string): string => { + return `ASSETCATALOG_COMPILER_APPICON_NAME= `; + }; + + let expected = { + 'ASSETCATALOG_COMPILER_APPICON_NAME': '' + }; + + assertPropertyValues(expected, injector); + }); + + it("Ensure property can be an empty value.", () => { + let injector = createTestInjector(); + let fs = getFileSystemMock(injector); + fs.readText = (filename: string, options?: IReadFileOptions | string): string => { + return `ASSETCATALOG_COMPILER_APPICON_NAME= ;`; + }; + + let expected = { + 'ASSETCATALOG_COMPILER_APPICON_NAME': '' + }; + + assertPropertyValues(expected, injector); + }); + }); +}); From dd374a08a2e5b48cfa6df091e705c37adad6434a Mon Sep 17 00:00:00 2001 From: Yosif Yosifov Date: Wed, 26 Apr 2017 17:24:45 +0300 Subject: [PATCH 05/19] First test for the merging of the Build XCConfig files --- test/ios-project-service.ts | 75 ++++++++++++++++++++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/test/ios-project-service.ts b/test/ios-project-service.ts index ee2c0f7860..475167d39a 100644 --- a/test/ios-project-service.ts +++ b/test/ios-project-service.ts @@ -22,9 +22,14 @@ import { DeviceDiscovery } from "../lib/common/mobile/mobile-core/device-discove import { IOSDeviceDiscovery } from "../lib/common/mobile/mobile-core/ios-device-discovery"; import { AndroidDeviceDiscovery } from "../lib/common/mobile/mobile-core/android-device-discovery"; import { PluginVariablesService } from "../lib/services/plugin-variables-service"; +import { PluginsService } from "../lib/services/plugins-service"; import { PluginVariablesHelper } from "../lib/common/plugin-variables-helper"; import { Utils } from "../lib/common/utils"; import { CocoaPodsService } from "../lib/services/cocoapods-service"; +import { NpmInstallationManager } from "../lib/npm-installation-manager"; +import { NodePackageManager } from "../lib/node-package-manager"; +import * as constants from "../lib/constants"; + import { assert } from "chai"; import temp = require("temp"); @@ -82,13 +87,23 @@ function createTestInjector(projectPath: string, projectName: string): IInjector testInjector.register("loggingLevels", LoggingLevels); testInjector.register("utils", Utils); testInjector.register("iTunesValidator", {}); - testInjector.register("xcprojService", {}); + testInjector.register("xcprojService", { + getXcprojInfo: () => { + return { + shouldUseXcproj: false + }; + } + }); testInjector.register("iosDeviceOperations", {}); testInjector.register("pluginVariablesService", PluginVariablesService); testInjector.register("pluginVariablesHelper", PluginVariablesHelper); + testInjector.register("pluginsService", PluginsService); testInjector.register("androidProcessService", {}); testInjector.register("processService", {}); testInjector.register("sysInfo", {}); + testInjector.register("npmInstallationManager", NpmInstallationManager); + testInjector.register("npm", NodePackageManager); + testInjector.register("xCConfigService", XCConfigService); return testInjector; } @@ -490,3 +505,61 @@ describe("Relative paths", () => { assert.equal(result, path.join("..", "..", "sub", "path")); }); }); + +describe("Merge Project XCConfig files", () => { + const assertPropertyValues = (expected: any, xcconfigPath: string, injector: IInjector) => { + let service = injector.resolve('xCConfigService'); + _.forOwn(expected, (value, key) => { + let actual = service.readPropertyValue(xcconfigPath, key); + assert.equal(actual, value); + }); + }; + + let projectName: string, + projectPath: string, + testInjector: IInjector, + iOSProjectService: IOSProjectService, + projectData: IProjectData, + fs: IFileSystem; + + beforeEach(() => { + projectName = "projectDirectory"; + projectPath = temp.mkdirSync(projectName); + + testInjector = createTestInjector(projectPath, projectName); + iOSProjectService = testInjector.resolve("iOSProjectService"); + projectData = testInjector.resolve("projectData"); + projectData.projectDir = projectPath; + + let testPackageJson = { + "name": "test-project", + "version": "0.0.1" + }; + fs = testInjector.resolve("fs"); + fs.writeJson(path.join(projectPath, "package.json"), testPackageJson); + }); + + it("Uses the build.xcconfig file content from App_Resources", async () => { + let appResourcesXcconfigPath = path.join(projectData.projectDir, constants.APP_FOLDER_NAME, + constants.APP_RESOURCES_FOLDER_NAME, constants.IOS_PLATFORM_NORMALIZED_NAME, "build.xcconfig"); + let appResourceXCConfigContent = `CODE_SIGN_IDENTITY = iPhone Distribution + // To build for device with XCode 8 you need to specify your development team. More info: https://developer.apple.com/library/prerelease/content/releasenotes/DeveloperTools/RN-Xcode/Introduction.html + // DEVELOPMENT_TEAM = YOUR_TEAM_ID; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + `; + fs.writeFile(appResourcesXcconfigPath, appResourceXCConfigContent); + + await (iOSProjectService).mergeProjectXcconfigFiles(true, projectData); + + let destinationFilePath = path.join(projectData.platformsDir, constants.IOS_PLATFORM_NAME, "plugins-release.xcconfig"); + + assert.isTrue(fs.exists(destinationFilePath), 'Target build xcconfig is missing.'); + let expected = { + 'ASSETCATALOG_COMPILER_APPICON_NAME': 'AppIcon', + 'ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME': 'LaunchImage', + 'CODE_SIGN_IDENTITY': 'iPhone Distribution' + }; + assertPropertyValues(expected, destinationFilePath, testInjector); + }); +}); From f02841b1612a0968af750d5949352ca9a883cc16 Mon Sep 17 00:00:00 2001 From: Yosif Yosifov Date: Wed, 26 Apr 2017 18:00:37 +0300 Subject: [PATCH 06/19] trying to add gems to the Travis CI machine setup --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 795bfd9141..f0114888f5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,8 @@ node_js: git: submodules: true before_script: +- gem install xcodeproj +- gem install cocoapods - npm install grunt - node_modules/.bin/grunt enableScripts:false - grunt rebuild From b44a5b89a24f33b36540d28defd6fbfe16eb7dd9 Mon Sep 17 00:00:00 2001 From: Yosif Yosifov Date: Wed, 26 Apr 2017 19:07:55 +0300 Subject: [PATCH 07/19] Adding patching the xcconfig file with a default entitlements file location if the user hasn't set one --- lib/constants.ts | 1 + lib/services/ios-entitlements-service.ts | 3 ++ lib/services/ios-project-service.ts | 19 ++++++-- test/ios-project-service.ts | 61 +++++++++++++++++------- 4 files changed, 63 insertions(+), 21 deletions(-) diff --git a/lib/constants.ts b/lib/constants.ts index dbaf54b816..b96ed725a7 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -18,6 +18,7 @@ export const XML_FILE_EXTENSION = ".xml"; export const PLATFORMS_DIR_NAME = "platforms"; export const IOS_PLATFORM_NAME = "ios"; export const IOS_PLATFORM_NORMALIZED_NAME = "iOS"; +export const CODE_SIGN_ENTITLEMENTS = "CODE_SIGN_ENTITLEMENTS"; export class PackageVersion { static NEXT = "next"; diff --git a/lib/services/ios-entitlements-service.ts b/lib/services/ios-entitlements-service.ts index b07eef30ff..cbc94d5168 100644 --- a/lib/services/ios-entitlements-service.ts +++ b/lib/services/ios-entitlements-service.ts @@ -25,6 +25,9 @@ export class IOSEntitlementsService { return path.join(projectData.platformsDir, constants.IOS_PLATFORM_NAME, projectData.projectName, projectData.projectName + ".entitlements"); } + public getPlatformsEntitlementsRelativePath(projectData: IProjectData): string { + return path.join(projectData.projectName, projectData.projectName + ".entitlements"); + } public async merge(projectData: IProjectData): Promise { let session = new PlistSession({ log: (txt: string) => this.$logger.trace("App.entitlements: " + txt) }); diff --git a/lib/services/ios-project-service.ts b/lib/services/ios-project-service.ts index 4e08304e85..bd78b8c9e5 100644 --- a/lib/services/ios-project-service.ts +++ b/lib/services/ios-project-service.ts @@ -1059,20 +1059,33 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f } private async mergeProjectXcconfigFiles(release: boolean, projectData: IProjectData): Promise { - this.$fs.deleteFile(release ? this.getPluginsReleaseXcconfigFilePath(projectData) : this.getPluginsDebugXcconfigFilePath(projectData)); + let pluginsXcconfigFilePath = release ? this.getPluginsReleaseXcconfigFilePath(projectData) : this.getPluginsDebugXcconfigFilePath(projectData); + this.$fs.deleteFile(pluginsXcconfigFilePath); let allPlugins: IPluginData[] = await (this.$injector.resolve("pluginsService")).getAllInstalledPlugins(projectData); for (let plugin of allPlugins) { let pluginPlatformsFolderPath = plugin.pluginPlatformsFolderPath(IOSProjectService.IOS_PLATFORM_NAME); let pluginXcconfigFilePath = path.join(pluginPlatformsFolderPath, "build.xcconfig"); if (this.$fs.exists(pluginXcconfigFilePath)) { - await this.mergeXcconfigFiles(pluginXcconfigFilePath, release ? this.getPluginsReleaseXcconfigFilePath(projectData) : this.getPluginsDebugXcconfigFilePath(projectData)); + await this.mergeXcconfigFiles(pluginXcconfigFilePath, pluginsXcconfigFilePath); } } let appResourcesXcconfigPath = path.join(projectData.projectDir, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME, this.getPlatformData(projectData).normalizedPlatformName, "build.xcconfig"); if (this.$fs.exists(appResourcesXcconfigPath)) { - await this.mergeXcconfigFiles(appResourcesXcconfigPath, release ? this.getPluginsReleaseXcconfigFilePath(projectData) : this.getPluginsDebugXcconfigFilePath(projectData)); + await this.mergeXcconfigFiles(appResourcesXcconfigPath, pluginsXcconfigFilePath); + } + + // Set Entitlements Property to point to default file if not set explicitly by the user. + let entitlementsPropertyValue = this.xCConfigService.readPropertyValue(pluginsXcconfigFilePath, constants.CODE_SIGN_ENTITLEMENTS); + if (entitlementsPropertyValue == null) { + temp.track(); + let tempEntitlementsDir = temp.mkdirSync("entitlements"); + let tempEntitlementsFilePath = path.join(tempEntitlementsDir, "set-entitlements.xcconfig"); + const entitlementsRelativePath = this.$iOSEntitlementsService.getPlatformsEntitlementsRelativePath(projectData); + this.$fs.writeFile(tempEntitlementsFilePath, `CODE_SIGN_ENTITLEMENTS = ${entitlementsRelativePath}${EOL}`); + + await this.mergeXcconfigFiles(tempEntitlementsFilePath, pluginsXcconfigFilePath); } let podFilesRootDirName = path.join("Pods", "Target Support Files", `Pods-${projectData.projectName}`); diff --git a/test/ios-project-service.ts b/test/ios-project-service.ts index 475167d39a..6ecf082603 100644 --- a/test/ios-project-service.ts +++ b/test/ios-project-service.ts @@ -520,7 +520,10 @@ describe("Merge Project XCConfig files", () => { testInjector: IInjector, iOSProjectService: IOSProjectService, projectData: IProjectData, - fs: IFileSystem; + fs: IFileSystem, + appResourcesXcconfigPath: string, + appResourceXCConfigContent: string, + iOSEntitlementsService: IOSEntitlementsService; beforeEach(() => { projectName = "projectDirectory"; @@ -531,6 +534,16 @@ describe("Merge Project XCConfig files", () => { projectData = testInjector.resolve("projectData"); projectData.projectDir = projectPath; + iOSEntitlementsService = testInjector.resolve("iOSEntitlementsService"); + + appResourcesXcconfigPath = path.join(projectData.projectDir, constants.APP_FOLDER_NAME, + constants.APP_RESOURCES_FOLDER_NAME, constants.IOS_PLATFORM_NORMALIZED_NAME, "build.xcconfig"); + appResourceXCConfigContent = `CODE_SIGN_IDENTITY = iPhone Distribution + // To build for device with XCode 8 you need to specify your development team. More info: https://developer.apple.com/library/prerelease/content/releasenotes/DeveloperTools/RN-Xcode/Introduction.html + // DEVELOPMENT_TEAM = YOUR_TEAM_ID; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + `; let testPackageJson = { "name": "test-project", "version": "0.0.1" @@ -540,26 +553,38 @@ describe("Merge Project XCConfig files", () => { }); it("Uses the build.xcconfig file content from App_Resources", async () => { - let appResourcesXcconfigPath = path.join(projectData.projectDir, constants.APP_FOLDER_NAME, - constants.APP_RESOURCES_FOLDER_NAME, constants.IOS_PLATFORM_NORMALIZED_NAME, "build.xcconfig"); - let appResourceXCConfigContent = `CODE_SIGN_IDENTITY = iPhone Distribution - // To build for device with XCode 8 you need to specify your development team. More info: https://developer.apple.com/library/prerelease/content/releasenotes/DeveloperTools/RN-Xcode/Introduction.html - // DEVELOPMENT_TEAM = YOUR_TEAM_ID; - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; - `; + // setup app_resource build.xcconfig fs.writeFile(appResourcesXcconfigPath, appResourceXCConfigContent); - await (iOSProjectService).mergeProjectXcconfigFiles(true, projectData); + // run merge for all release: debug|release + for (let release in [true, false]) { + await (iOSProjectService).mergeProjectXcconfigFiles(release, projectData); - let destinationFilePath = path.join(projectData.platformsDir, constants.IOS_PLATFORM_NAME, "plugins-release.xcconfig"); + let destinationFilePath = release ? (iOSProjectService).getPluginsReleaseXcconfigFilePath(projectData) + : (iOSProjectService).getPluginsDebugXcconfigFilePath(projectData); - assert.isTrue(fs.exists(destinationFilePath), 'Target build xcconfig is missing.'); - let expected = { - 'ASSETCATALOG_COMPILER_APPICON_NAME': 'AppIcon', - 'ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME': 'LaunchImage', - 'CODE_SIGN_IDENTITY': 'iPhone Distribution' - }; - assertPropertyValues(expected, destinationFilePath, testInjector); + assert.isTrue(fs.exists(destinationFilePath), 'Target build xcconfig is missing for release: ' + release); + let expected = { + 'ASSETCATALOG_COMPILER_APPICON_NAME': 'AppIcon', + 'ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME': 'LaunchImage', + 'CODE_SIGN_IDENTITY': 'iPhone Distribution' + }; + assertPropertyValues(expected, destinationFilePath, testInjector); + } + }); + + it("Adds the entitlements property if not set by the user", async () => { + for (let release in [true, false]) { + await (iOSProjectService).mergeProjectXcconfigFiles(release, projectData); + + let destinationFilePath = release ? (iOSProjectService).getPluginsReleaseXcconfigFilePath(projectData) + : (iOSProjectService).getPluginsDebugXcconfigFilePath(projectData); + + assert.isTrue(fs.exists(destinationFilePath), 'Target build xcconfig is missing for release: ' + release); + let expected = { + 'CODE_SIGN_ENTITLEMENTS': iOSEntitlementsService.getPlatformsEntitlementsRelativePath(projectData) + }; + assertPropertyValues(expected, destinationFilePath, testInjector); + } }); }); From c2e7561894467b8d9062a0f7f714b2a5ff693acb Mon Sep 17 00:00:00 2001 From: Yosif Yosifov Date: Thu, 27 Apr 2017 18:00:16 +0300 Subject: [PATCH 08/19] Fix default empty entitlements file - was needless and caused errors. --- lib/services/ios-entitlements-service.ts | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/lib/services/ios-entitlements-service.ts b/lib/services/ios-entitlements-service.ts index cbc94d5168..79e9f545f0 100644 --- a/lib/services/ios-entitlements-service.ts +++ b/lib/services/ios-entitlements-service.ts @@ -46,16 +46,6 @@ export class IOSEntitlementsService { }); }; - // Start with a default empty entitlements file. - session.patch({ - name: "Default entitlements file", - read: () => - ` - - - ` - }); - let allPlugins = await this.getAllInstalledPlugins(projectData); for (let plugin of allPlugins) { let pluginInfoPlistPath = path.join(plugin.pluginPlatformsFolderPath(constants.IOS_PLATFORM_NAME), this.getDefaultEntitlementsName()); @@ -74,13 +64,6 @@ export class IOSEntitlementsService { return; } - public async patchXcconfigFile(projectData: IProjectData) { - //CODE_SIGN_ENTITLEMENTS = fgomobile/fgomobile.entitlements - //let entitlementsRelativePath = path.join(projectData.projectName, projectData.projectName + ".entitlements"); - - return; - } - private getAllInstalledPlugins(projectData: IProjectData): Promise { return (this.$injector.resolve("pluginsService")).getAllInstalledPlugins(projectData); } From 08f48acf6e91573c5e6c9207a03f62c43ce978c2 Mon Sep 17 00:00:00 2001 From: Yosif Yosifov Date: Mon, 24 Apr 2017 16:50:25 +0300 Subject: [PATCH 09/19] Extract entitlements and xcconfig services. --- lib/bootstrap.ts | 2 + lib/constants.ts | 2 + lib/services/ios-entitlements-service.ts | 86 ++++++++++++++++++++++++ lib/services/ios-project-service.ts | 47 ++++++------- lib/services/xcconfig-service.ts | 37 ++++++++++ 5 files changed, 146 insertions(+), 28 deletions(-) create mode 100644 lib/services/ios-entitlements-service.ts create mode 100644 lib/services/xcconfig-service.ts diff --git a/lib/bootstrap.ts b/lib/bootstrap.ts index 25ec914ba8..00e4fdf2dc 100644 --- a/lib/bootstrap.ts +++ b/lib/bootstrap.ts @@ -9,8 +9,10 @@ $injector.require("projectData", "./project-data"); $injector.require("projectDataService", "./services/project-data-service"); $injector.requirePublic("projectService", "./services/project-service"); $injector.require("androidProjectService", "./services/android-project-service"); +$injector.require("iOSEntitlementsService", "./services/ios-entitlements-service"); $injector.require("iOSProjectService", "./services/ios-project-service"); $injector.require("iOSProvisionService", "./services/ios-provision-service"); +$injector.require("xCConfigService", "./services/xcconfig-service"); $injector.require("cocoapodsService", "./services/cocoapods-service"); diff --git a/lib/constants.ts b/lib/constants.ts index 21a4488697..dbaf54b816 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -16,6 +16,8 @@ export const TEST_RUNNER_NAME = "nativescript-unit-test-runner"; export const LIVESYNC_EXCLUDED_FILE_PATTERNS = ["**/*.js.map", "**/*.ts"]; export const XML_FILE_EXTENSION = ".xml"; export const PLATFORMS_DIR_NAME = "platforms"; +export const IOS_PLATFORM_NAME = "ios"; +export const IOS_PLATFORM_NORMALIZED_NAME = "iOS"; export class PackageVersion { static NEXT = "next"; diff --git a/lib/services/ios-entitlements-service.ts b/lib/services/ios-entitlements-service.ts new file mode 100644 index 0000000000..b07eef30ff --- /dev/null +++ b/lib/services/ios-entitlements-service.ts @@ -0,0 +1,86 @@ +import * as path from "path"; +import * as constants from "../constants"; +import { PlistSession } from "plist-merge-patch"; + +export class IOSEntitlementsService { + constructor(private $fs: IFileSystem, + private $injector: IInjector, + private $logger: ILogger) { + } + + private getDefaultEntitlementsName() : string { + return "app.entitlements"; + } + + private getDefaultAppEntitlementsPath(projectData: IProjectData) : string { + let entitlementsName = this.getDefaultEntitlementsName(); + let entitlementsPath = path.join(projectData.projectDir, + constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME, + constants.IOS_PLATFORM_NORMALIZED_NAME, + entitlementsName); + return entitlementsPath; + } + + private getPlatformsEntitlementsPath(projectData: IProjectData) : string { + return path.join(projectData.platformsDir, constants.IOS_PLATFORM_NAME, + projectData.projectName, projectData.projectName + ".entitlements"); + } + + public async merge(projectData: IProjectData): Promise { + let session = new PlistSession({ log: (txt: string) => this.$logger.trace("App.entitlements: " + txt) }); + + let projectDir = projectData.projectDir; + let makePatch = (plistPath: string) => { + if (!this.$fs.exists(plistPath)) { + this.$logger.trace("No plist found at: " + plistPath); + return; + } + + this.$logger.trace("Schedule merge plist at: " + plistPath); + session.patch({ + name: path.relative(projectDir, plistPath), + read: () => this.$fs.readText(plistPath) + }); + }; + + // Start with a default empty entitlements file. + session.patch({ + name: "Default entitlements file", + read: () => + ` + + + ` + }); + + let allPlugins = await this.getAllInstalledPlugins(projectData); + for (let plugin of allPlugins) { + let pluginInfoPlistPath = path.join(plugin.pluginPlatformsFolderPath(constants.IOS_PLATFORM_NAME), this.getDefaultEntitlementsName()); + makePatch(pluginInfoPlistPath); + } + + // for older ios-app-templates or if has been deleted. + let appEntitlementsPath = this.getDefaultAppEntitlementsPath(projectData); + if (this.$fs.exists(appEntitlementsPath)) { + makePatch(appEntitlementsPath); + } + + let plistContent = session.build(); + this.$logger.trace("App.entitlements: Write to: " + this.getPlatformsEntitlementsPath(projectData)); + this.$fs.writeFile(this.getPlatformsEntitlementsPath(projectData), plistContent); + return; + } + + public async patchXcconfigFile(projectData: IProjectData) { + //CODE_SIGN_ENTITLEMENTS = fgomobile/fgomobile.entitlements + //let entitlementsRelativePath = path.join(projectData.projectName, projectData.projectName + ".entitlements"); + + return; + } + + private getAllInstalledPlugins(projectData: IProjectData): Promise { + return (this.$injector.resolve("pluginsService")).getAllInstalledPlugins(projectData); + } +} + +$injector.register("iOSEntitlementsService", IOSEntitlementsService); diff --git a/lib/services/ios-project-service.ts b/lib/services/ios-project-service.ts index c39d6c013d..c38f99d87a 100644 --- a/lib/services/ios-project-service.ts +++ b/lib/services/ios-project-service.ts @@ -11,6 +11,8 @@ import { EOL } from "os"; import * as temp from "temp"; import * as plist from "plist"; import { IOSProvisionService } from "./ios-provision-service"; +import { IOSEntitlementsService } from "./ios-entitlements-service"; +import { XCConfigService } from "./xcconfig-service"; export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServiceBase implements IPlatformProjectService { private static XCODE_PROJECT_EXT_NAME = ".xcodeproj"; @@ -40,9 +42,11 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ private $pluginVariablesService: IPluginVariablesService, private $xcprojService: IXcprojService, private $iOSProvisionService: IOSProvisionService, - private $sysInfo: ISysInfo, private $pbxprojDomXcode: IPbxprojDomXcode, - private $xcode: IXcode) { + private $xcode: IXcode, + private $iOSEntitlementsService: IOSEntitlementsService, + private $sysInfo: ISysInfo, + private xCConfigService: XCConfigService) { super($fs, $projectDataService); } @@ -668,6 +672,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f public async processConfigurationFilesFromAppResources(release: boolean, projectData: IProjectData): Promise { await this.mergeInfoPlists(projectData); + await this.$iOSEntitlementsService.merge(projectData); await this.mergeProjectXcconfigFiles(release, projectData); for (let pluginData of await this.getAllInstalledPlugins(projectData)) { await this.$pluginVariablesService.interpolatePluginVariables(pluginData, this.getPlatformData(projectData).configurationFilePath, projectData); @@ -1178,43 +1183,29 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f return null; } - private readXCConfig(flag: string, projectData: IProjectData): string { - let xcconfigFile = path.join(projectData.appResourcesDirectoryPath, this.getPlatformData(projectData).normalizedPlatformName, "build.xcconfig"); - if (this.$fs.exists(xcconfigFile)) { - let text = this.$fs.readText(xcconfigFile); - let teamId: string; - text.split(/\r?\n/).forEach((line) => { - line = line.replace(/\/(\/)[^\n]*$/, ""); - if (line.indexOf(flag) >= 0) { - teamId = line.split("=")[1].trim(); - if (teamId[teamId.length - 1] === ';') { - teamId = teamId.slice(0, -1); - } - } - }); - if (teamId) { - return teamId; - } - } + private getBuildXCConfigFilePath(projectData: IProjectData): string { + let buildXCConfig = path.join(projectData.appResourcesDirectoryPath, + this.getPlatformData(projectData).normalizedPlatformName, "build.xcconfig"); + return buildXCConfig; + } + + private readTeamId(projectData: IProjectData): string { + let teamId = this.xCConfigService.readPropertyValue(this.getBuildXCConfigFilePath(projectData), "DEVELOPMENT_TEAM"); let fileName = path.join(this.getPlatformData(projectData).projectRoot, "teamid"); if (this.$fs.exists(fileName)) { - return this.$fs.readText(fileName); + teamId = this.$fs.readText(fileName); } - return null; - } - - private readTeamId(projectData: IProjectData): string { - return this.readXCConfig("DEVELOPMENT_TEAM", projectData); + return teamId; } private readXCConfigProvisioningProfile(projectData: IProjectData): string { - return this.readXCConfig("PROVISIONING_PROFILE", projectData); + return this.xCConfigService.readPropertyValue(this.getBuildXCConfigFilePath(projectData), "PROVISIONING_PROFILE"); } private readXCConfigProvisioningProfileForIPhoneOs(projectData: IProjectData): string { - return this.readXCConfig("PROVISIONING_PROFILE[sdk=iphoneos*]", projectData); + return this.xCConfigService.readPropertyValue(this.getBuildXCConfigFilePath(projectData), "PROVISIONING_PROFILE[sdk=iphoneos*]"); } private readXCConfigProvisioningProfileSpecifier(projectData: IProjectData): string { diff --git a/lib/services/xcconfig-service.ts b/lib/services/xcconfig-service.ts new file mode 100644 index 0000000000..392550462c --- /dev/null +++ b/lib/services/xcconfig-service.ts @@ -0,0 +1,37 @@ +export class XCConfigService { + constructor(private $fs: IFileSystem) { + } + + /** + * Returns the Value of a Property from a XC Config file. + * @param xcconfigFilePath + * @param propertyName + */ + public readPropertyValue(xcconfigFilePath: string, propertyName: string): string { + if (this.$fs.exists(xcconfigFilePath)) { + let text = this.$fs.readText(xcconfigFilePath); + + let property: string; + text.split(/\r?\n/).forEach((line) => { + line = line.replace(/\/(\/)[^\n]*$/, ""); + if (line.indexOf(propertyName) >= 0) { + // todo this can have undefined. + property = line.split("=")[1].trim(); + // todo this can have undefined. + if (property[property.length - 1] === ';') { + property = property.slice(0, -1); + } + } + }); + + // what if the property is set to empty value? + if (property) { + return property; + } + } + + return null; + } +} + +$injector.register("xCConfigService", XCConfigService); \ No newline at end of file From 13f3b6ae279be079010a60b31a66a4d486fa8196 Mon Sep 17 00:00:00 2001 From: Yosif Yosifov Date: Mon, 24 Apr 2017 17:12:20 +0300 Subject: [PATCH 10/19] Fix tests setup with the new services in ios-project-service tests --- test/ios-project-service.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/ios-project-service.ts b/test/ios-project-service.ts index 7ab5b0252a..1dace11f09 100644 --- a/test/ios-project-service.ts +++ b/test/ios-project-service.ts @@ -6,6 +6,8 @@ import * as FileSystemLib from "../lib/common/file-system"; import * as HostInfoLib from "../lib/common/host-info"; import * as iOSProjectServiceLib from "../lib/services/ios-project-service"; import { IOSProjectService } from "../lib/services/ios-project-service"; +import { IOSEntitlementsService } from "../lib/services/ios-entitlements-service"; +import { XCConfigService } from "../lib/services/xcconfig-service"; import * as LoggerLib from "../lib/common/logger"; import * as OptionsLib from "../lib/options"; import * as yok from "../lib/common/yok"; @@ -52,6 +54,8 @@ function createTestInjector(projectPath: string, projectName: string): IInjector testInjector.register("cocoapodsService", CocoaPodsService); testInjector.register("iOSProjectService", iOSProjectServiceLib.IOSProjectService); testInjector.register("iOSProvisionService", {}); + testInjector.register("xCConfigService", XCConfigService); + testInjector.register("iOSEntitlementsService", IOSEntitlementsService); testInjector.register("logger", LoggerLib.Logger); testInjector.register("options", OptionsLib.Options); testInjector.register("projectData", { From 9cf033b423f581e74a12088048f2172666b453fb Mon Sep 17 00:00:00 2001 From: Yosif Yosifov Date: Mon, 24 Apr 2017 17:24:55 +0300 Subject: [PATCH 11/19] Adding newline in xcconfig-service to fix lint error --- lib/services/xcconfig-service.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/services/xcconfig-service.ts b/lib/services/xcconfig-service.ts index 392550462c..5028408101 100644 --- a/lib/services/xcconfig-service.ts +++ b/lib/services/xcconfig-service.ts @@ -34,4 +34,4 @@ export class XCConfigService { } } -$injector.register("xCConfigService", XCConfigService); \ No newline at end of file +$injector.register("xCConfigService", XCConfigService); From 5cd3f7a421202480b5c574009092b18fa7d477c7 Mon Sep 17 00:00:00 2001 From: Yosif Yosifov Date: Tue, 25 Apr 2017 17:19:51 +0300 Subject: [PATCH 12/19] Add Tests for XCConfig-Service --- lib/services/xcconfig-service.ts | 17 +-- test/xcconfig-service.ts | 188 +++++++++++++++++++++++++++++++ 2 files changed, 198 insertions(+), 7 deletions(-) create mode 100644 test/xcconfig-service.ts diff --git a/lib/services/xcconfig-service.ts b/lib/services/xcconfig-service.ts index 5028408101..63fb6b2fca 100644 --- a/lib/services/xcconfig-service.ts +++ b/lib/services/xcconfig-service.ts @@ -12,20 +12,23 @@ export class XCConfigService { let text = this.$fs.readText(xcconfigFilePath); let property: string; + let isPropertyParsed: boolean = false; text.split(/\r?\n/).forEach((line) => { line = line.replace(/\/(\/)[^\n]*$/, ""); if (line.indexOf(propertyName) >= 0) { - // todo this can have undefined. - property = line.split("=")[1].trim(); - // todo this can have undefined. - if (property[property.length - 1] === ';') { - property = property.slice(0, -1); + let parts = line.split("="); + if (parts.length > 1 && parts[1]) { + property = parts[1].trim(); + isPropertyParsed = true; + if (property[property.length - 1] === ';') { + property = property.slice(0, -1); + } } } }); - // what if the property is set to empty value? - if (property) { + if (isPropertyParsed) { + // property can be an empty string, so we don't check for that. return property; } } diff --git a/test/xcconfig-service.ts b/test/xcconfig-service.ts new file mode 100644 index 0000000000..099ee44b47 --- /dev/null +++ b/test/xcconfig-service.ts @@ -0,0 +1,188 @@ +import temp = require("temp"); +import { assert } from "chai"; +import { XCConfigService } from "../lib/services/xcconfig-service"; +import * as yok from "../lib/common/yok"; + +// start tracking temporary folders/files +temp.track(); + +describe("XCConfig Service Tests", () => { + const createTestInjector = (): IInjector => { + const testInjector = new yok.Yok(); + testInjector.register("fs", { + exists: (path: string) => { + return true; + } + }); + + testInjector.register('xCConfigService', XCConfigService); + + return testInjector; + }; + + const assertPropertyValues = (expected: any, injector: IInjector) => { + let service = getXCConfigService(injector); + _.forOwn(expected, (value, key) => { + let actual = service.readPropertyValue("any", key); + assert.equal(actual, value); + }); + }; + + function getXCConfigService(injector: IInjector): XCConfigService { + return injector.resolve("xCConfigService"); + } + + function getFileSystemMock(injector: IInjector): any { + return injector.resolve('$fs'); + } + + describe("Read Property Value", () => { + it("Return empty value, for unexistent file", () => { + let injector = createTestInjector(); + injector.register("fs", { + exists: (path: string) => { + return false; + } + }); + + let service = getXCConfigService(injector); + let actual = service.readPropertyValue("any", "any"); + + assert.isNull(actual); + }); + + it("Returns correct value for well-formatted document", () => { + let injector = createTestInjector(); + let fs = getFileSystemMock(injector); + fs.readText = (filename: string, options?: IReadFileOptions | string): string => { + return `// You can add custom settings here + // for example you can uncomment the following line to force distribution code signing + CODE_SIGN_IDENTITY = iPhone Distribution + // To build for device with XCode 8 you need to specify your development team. More info: https://developer.apple.com/library/prerelease/content/releasenotes/DeveloperTools/RN-Xcode/Introduction.html + // DEVELOPMENT_TEAM = YOUR_TEAM_ID; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;`; + }; + + let expected = { + 'ASSETCATALOG_COMPILER_APPICON_NAME': 'AppIcon', + 'ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME': 'LaunchImage', + 'CODE_SIGN_IDENTITY': 'iPhone Distribution' + }; + + assertPropertyValues(expected, injector); + }); + + it("Returns correct value for values with missing ; at the end of the line.", () => { + let injector = createTestInjector(); + let fs = getFileSystemMock(injector); + fs.readText = (filename: string, options?: IReadFileOptions | string): string => { + return `// You can add custom settings here + // for example you can uncomment the following line to force distribution code signing + CODE_SIGN_IDENTITY = iPhone Distribution + // To build for device with XCode 8 you need to specify your development team. More info: https://developer.apple.com/library/prerelease/content/releasenotes/DeveloperTools/RN-Xcode/Introduction.html + // DEVELOPMENT_TEAM = YOUR_TEAM_ID + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage`; + }; + + let expected = { + 'ASSETCATALOG_COMPILER_APPICON_NAME': 'AppIcon', + 'ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME': 'LaunchImage', + 'CODE_SIGN_IDENTITY': 'iPhone Distribution' + }; + + assertPropertyValues(expected, injector); + }); + + it("Doesn't read value from a commented-out line.", () => { + let injector = createTestInjector(); + let fs = getFileSystemMock(injector); + fs.readText = (filename: string, options?: IReadFileOptions | string): string => { + return `// DEVELOPMENT_TEAM = YOUR_TEAM_ID; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;`; + }; + + let expected = { + 'ASSETCATALOG_COMPILER_APPICON_NAME': 'AppIcon', + 'DEVELOPMENT_TEAM': null + }; + + assertPropertyValues(expected, injector); + }); + + it("Returns correct empty value.", () => { + let injector = createTestInjector(); + let fs = getFileSystemMock(injector); + fs.readText = (filename: string, options?: IReadFileOptions | string): string => { + return ` + ASSETCATALOG_COMPILER_APPICON_NAME = ; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + `; + }; + + let expected = { + 'ASSETCATALOG_COMPILER_APPICON_NAME': '', + 'ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME': 'LaunchImage' + }; + + assertPropertyValues(expected, injector); + }); + + it("First part only property doesn't break the service.", () => { + let injector = createTestInjector(); + let fs = getFileSystemMock(injector); + fs.readText = (filename: string, options?: IReadFileOptions | string): string => { + return `ASSETCATALOG_COMPILER_APPICON_NAME`; + }; + + let expected = { + 'ASSETCATALOG_COMPILER_APPICON_NAME': null + }; + + assertPropertyValues(expected, injector); + }); + + it("Invalid config property value with = doesn't break the service.", () => { + let injector = createTestInjector(); + let fs = getFileSystemMock(injector); + fs.readText = (filename: string, options?: IReadFileOptions | string): string => { + return `ASSETCATALOG_COMPILER_APPICON_NAME=`; + }; + + let expected = { + 'ASSETCATALOG_COMPILER_APPICON_NAME': null + }; + + assertPropertyValues(expected, injector); + }); + + it("Property with space is read correctly.", () => { + let injector = createTestInjector(); + let fs = getFileSystemMock(injector); + fs.readText = (filename: string, options?: IReadFileOptions | string): string => { + return `ASSETCATALOG_COMPILER_APPICON_NAME= `; + }; + + let expected = { + 'ASSETCATALOG_COMPILER_APPICON_NAME': '' + }; + + assertPropertyValues(expected, injector); + }); + + it("Ensure property can be an empty value.", () => { + let injector = createTestInjector(); + let fs = getFileSystemMock(injector); + fs.readText = (filename: string, options?: IReadFileOptions | string): string => { + return `ASSETCATALOG_COMPILER_APPICON_NAME= ;`; + }; + + let expected = { + 'ASSETCATALOG_COMPILER_APPICON_NAME': '' + }; + + assertPropertyValues(expected, injector); + }); + }); +}); From de96b76804e4ac9d65a9d76349e563c069ed17e2 Mon Sep 17 00:00:00 2001 From: Yosif Yosifov Date: Wed, 26 Apr 2017 17:24:45 +0300 Subject: [PATCH 13/19] First test for the merging of the Build XCConfig files --- test/ios-project-service.ts | 117 ++++++++++++++++++++++++++++++------ 1 file changed, 97 insertions(+), 20 deletions(-) diff --git a/test/ios-project-service.ts b/test/ios-project-service.ts index 1dace11f09..47380e404a 100644 --- a/test/ios-project-service.ts +++ b/test/ios-project-service.ts @@ -22,9 +22,14 @@ import { DeviceDiscovery } from "../lib/common/mobile/mobile-core/device-discove import { IOSDeviceDiscovery } from "../lib/common/mobile/mobile-core/ios-device-discovery"; import { AndroidDeviceDiscovery } from "../lib/common/mobile/mobile-core/android-device-discovery"; import { PluginVariablesService } from "../lib/services/plugin-variables-service"; +import { PluginsService } from "../lib/services/plugins-service"; import { PluginVariablesHelper } from "../lib/common/plugin-variables-helper"; import { Utils } from "../lib/common/utils"; import { CocoaPodsService } from "../lib/services/cocoapods-service"; +import { NpmInstallationManager } from "../lib/npm-installation-manager"; +import { NodePackageManager } from "../lib/node-package-manager"; +import * as constants from "../lib/constants"; + import { assert } from "chai"; import { IOSProvisionService } from "../lib/services/ios-provision-service"; import temp = require("temp"); @@ -83,10 +88,17 @@ function createTestInjector(projectPath: string, projectName: string): IInjector testInjector.register("loggingLevels", LoggingLevels); testInjector.register("utils", Utils); testInjector.register("iTunesValidator", {}); - testInjector.register("xcprojService", {}); + testInjector.register("xcprojService", { + getXcprojInfo: () => { + return { + shouldUseXcproj: false + }; + } + }); testInjector.register("iosDeviceOperations", {}); testInjector.register("pluginVariablesService", PluginVariablesService); testInjector.register("pluginVariablesHelper", PluginVariablesHelper); + testInjector.register("pluginsService", PluginsService); testInjector.register("androidProcessService", {}); testInjector.register("processService", {}); testInjector.register("sysInfo", {}); @@ -98,6 +110,9 @@ function createTestInjector(projectPath: string, projectName: string): IInjector pbxGroupByName() { /* */ } } }); + testInjector.register("npmInstallationManager", NpmInstallationManager); + testInjector.register("npm", NodePackageManager); + testInjector.register("xCConfigService", XCConfigService); return testInjector; } @@ -602,7 +617,7 @@ describe("iOS Project Service Signing", () => { }); it("sets signingChanged if the Xcode projects is configured with Automatic signing, but proivsion is specified", () => { files[pbxproj] = ""; - pbxprojDomXcode.Xcode.open = function(path: string) { + pbxprojDomXcode.Xcode.open = function (path: string) { assert.equal(path, pbxproj); return { getSigning(x: string) { @@ -616,14 +631,16 @@ describe("iOS Project Service Signing", () => { }); it("sets signingChanged if the Xcode projects is configured with Manual signing, but the proivsion specified differs the selected in the pbxproj", () => { files[pbxproj] = ""; - pbxprojDomXcode.Xcode.open = function(path: string) { + pbxprojDomXcode.Xcode.open = function (path: string) { assert.equal(path, pbxproj); return { getSigning() { - return { style: "Manual", configurations: { - Debug: { name: "NativeScriptDev2" }, - Release: { name: "NativeScriptDev2" } - }}; + return { + style: "Manual", configurations: { + Debug: { name: "NativeScriptDev2" }, + Release: { name: "NativeScriptDev2" } + } + }; } }; }; @@ -633,14 +650,16 @@ describe("iOS Project Service Signing", () => { }); it("does not set signingChanged if the Xcode projects is configured with Manual signing and proivsion matches", () => { files[pbxproj] = ""; - pbxprojDomXcode.Xcode.open = function(path: string) { + pbxprojDomXcode.Xcode.open = function (path: string) { assert.equal(path, pbxproj); return { getSigning() { - return { style: "Manual", configurations: { - Debug: { name: "NativeScriptDev" }, - Release: { name: "NativeScriptDev" } - }}; + return { + style: "Manual", configurations: { + Debug: { name: "NativeScriptDev" }, + Release: { name: "NativeScriptDev" } + } + }; } }; }; @@ -654,7 +673,7 @@ describe("iOS Project Service Signing", () => { describe("from Automatic to provision name", () => { beforeEach(() => { files[pbxproj] = ""; - pbxprojDomXcode.Xcode.open = function(path: string) { + pbxprojDomXcode.Xcode.open = function (path: string) { return { getSigning(x: string) { return { style: "Automatic", teamID: "AutoTeam" }; @@ -669,9 +688,9 @@ describe("iOS Project Service Signing", () => { assert.isTrue(e.toString().indexOf("Failed to find mobile provision with UUID or Name: NativeScriptDev2") >= 0); } }); - it("succeeds if the provision name is provided for development cert", async() => { + it("succeeds if the provision name is provided for development cert", async () => { let stack: any = []; - pbxprojDomXcode.Xcode.open = function(path: string) { + pbxprojDomXcode.Xcode.open = function (path: string) { assert.equal(path, pbxproj); return { getSigning() { @@ -686,11 +705,11 @@ describe("iOS Project Service Signing", () => { }; }; await iOSProjectService.prepareProject(projectData, { sdk: undefined, provision: "NativeScriptDev" }); - assert.deepEqual(stack, [{targetName: projectDirName, manualSigning: { team: "TKID101", uuid: "12345", name: "NativeScriptDev", identity: "iPhone Developer" }}, "save()"]); + assert.deepEqual(stack, [{ targetName: projectDirName, manualSigning: { team: "TKID101", uuid: "12345", name: "NativeScriptDev", identity: "iPhone Developer" } }, "save()"]); }); it("succeds if the provision name is provided for distribution cert", async () => { let stack: any = []; - pbxprojDomXcode.Xcode.open = function(path: string) { + pbxprojDomXcode.Xcode.open = function (path: string) { assert.equal(path, pbxproj); return { getSigning() { @@ -705,11 +724,11 @@ describe("iOS Project Service Signing", () => { }; }; await iOSProjectService.prepareProject(projectData, { sdk: undefined, provision: "NativeScriptDist" }); - assert.deepEqual(stack, [{targetName: projectDirName, manualSigning: { team: "TKID202", uuid: "6789", name: "NativeScriptDist", identity: "iPhone Distribution" }}, "save()"]); + assert.deepEqual(stack, [{ targetName: projectDirName, manualSigning: { team: "TKID202", uuid: "6789", name: "NativeScriptDist", identity: "iPhone Distribution" } }, "save()"]); }); it("succeds if the provision name is provided for adhoc cert", async () => { let stack: any = []; - pbxprojDomXcode.Xcode.open = function(path: string) { + pbxprojDomXcode.Xcode.open = function (path: string) { assert.equal(path, pbxproj); return { getSigning() { @@ -724,8 +743,66 @@ describe("iOS Project Service Signing", () => { }; }; await iOSProjectService.prepareProject(projectData, { sdk: undefined, provision: "NativeScriptAdHoc" }); - assert.deepEqual(stack, [{targetName: projectDirName, manualSigning: { team: "TKID303", uuid: "1010", name: "NativeScriptAdHoc", identity: "iPhone Distribution" }}, "save()"]); + assert.deepEqual(stack, [{ targetName: projectDirName, manualSigning: { team: "TKID303", uuid: "1010", name: "NativeScriptAdHoc", identity: "iPhone Distribution" } }, "save()"]); }); }); }); }); + +describe("Merge Project XCConfig files", () => { + const assertPropertyValues = (expected: any, xcconfigPath: string, injector: IInjector) => { + let service = injector.resolve('xCConfigService'); + _.forOwn(expected, (value, key) => { + let actual = service.readPropertyValue(xcconfigPath, key); + assert.equal(actual, value); + }); + }; + + let projectName: string, + projectPath: string, + testInjector: IInjector, + iOSProjectService: IOSProjectService, + projectData: IProjectData, + fs: IFileSystem; + + beforeEach(() => { + projectName = "projectDirectory"; + projectPath = temp.mkdirSync(projectName); + + testInjector = createTestInjector(projectPath, projectName); + iOSProjectService = testInjector.resolve("iOSProjectService"); + projectData = testInjector.resolve("projectData"); + projectData.projectDir = projectPath; + + let testPackageJson = { + "name": "test-project", + "version": "0.0.1" + }; + fs = testInjector.resolve("fs"); + fs.writeJson(path.join(projectPath, "package.json"), testPackageJson); + }); + + it("Uses the build.xcconfig file content from App_Resources", async () => { + let appResourcesXcconfigPath = path.join(projectData.projectDir, constants.APP_FOLDER_NAME, + constants.APP_RESOURCES_FOLDER_NAME, constants.IOS_PLATFORM_NORMALIZED_NAME, "build.xcconfig"); + let appResourceXCConfigContent = `CODE_SIGN_IDENTITY = iPhone Distribution + // To build for device with XCode 8 you need to specify your development team. More info: https://developer.apple.com/library/prerelease/content/releasenotes/DeveloperTools/RN-Xcode/Introduction.html + // DEVELOPMENT_TEAM = YOUR_TEAM_ID; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + `; + fs.writeFile(appResourcesXcconfigPath, appResourceXCConfigContent); + + await (iOSProjectService).mergeProjectXcconfigFiles(true, projectData); + + let destinationFilePath = path.join(projectData.platformsDir, constants.IOS_PLATFORM_NAME, "plugins-release.xcconfig"); + + assert.isTrue(fs.exists(destinationFilePath), 'Target build xcconfig is missing.'); + let expected = { + 'ASSETCATALOG_COMPILER_APPICON_NAME': 'AppIcon', + 'ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME': 'LaunchImage', + 'CODE_SIGN_IDENTITY': 'iPhone Distribution' + }; + assertPropertyValues(expected, destinationFilePath, testInjector); + }); +}); From 7222e98d7a2e807280240f9b35f2b08386cb91b8 Mon Sep 17 00:00:00 2001 From: Yosif Yosifov Date: Wed, 26 Apr 2017 18:00:37 +0300 Subject: [PATCH 14/19] trying to add gems to the Travis CI machine setup --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 795bfd9141..f0114888f5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,6 +9,8 @@ node_js: git: submodules: true before_script: +- gem install xcodeproj +- gem install cocoapods - npm install grunt - node_modules/.bin/grunt enableScripts:false - grunt rebuild From 835133a798ca23112042737ec0034b61599396de Mon Sep 17 00:00:00 2001 From: Yosif Yosifov Date: Wed, 26 Apr 2017 19:07:55 +0300 Subject: [PATCH 15/19] Adding patching the xcconfig file with a default entitlements file location if the user hasn't set one --- lib/constants.ts | 1 + lib/services/ios-entitlements-service.ts | 3 ++ lib/services/ios-project-service.ts | 19 ++++++-- test/ios-project-service.ts | 61 +++++++++++++++++------- 4 files changed, 63 insertions(+), 21 deletions(-) diff --git a/lib/constants.ts b/lib/constants.ts index dbaf54b816..b96ed725a7 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -18,6 +18,7 @@ export const XML_FILE_EXTENSION = ".xml"; export const PLATFORMS_DIR_NAME = "platforms"; export const IOS_PLATFORM_NAME = "ios"; export const IOS_PLATFORM_NORMALIZED_NAME = "iOS"; +export const CODE_SIGN_ENTITLEMENTS = "CODE_SIGN_ENTITLEMENTS"; export class PackageVersion { static NEXT = "next"; diff --git a/lib/services/ios-entitlements-service.ts b/lib/services/ios-entitlements-service.ts index b07eef30ff..cbc94d5168 100644 --- a/lib/services/ios-entitlements-service.ts +++ b/lib/services/ios-entitlements-service.ts @@ -25,6 +25,9 @@ export class IOSEntitlementsService { return path.join(projectData.platformsDir, constants.IOS_PLATFORM_NAME, projectData.projectName, projectData.projectName + ".entitlements"); } + public getPlatformsEntitlementsRelativePath(projectData: IProjectData): string { + return path.join(projectData.projectName, projectData.projectName + ".entitlements"); + } public async merge(projectData: IProjectData): Promise { let session = new PlistSession({ log: (txt: string) => this.$logger.trace("App.entitlements: " + txt) }); diff --git a/lib/services/ios-project-service.ts b/lib/services/ios-project-service.ts index c38f99d87a..9b1a21bdd5 100644 --- a/lib/services/ios-project-service.ts +++ b/lib/services/ios-project-service.ts @@ -1090,20 +1090,33 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f } private async mergeProjectXcconfigFiles(release: boolean, projectData: IProjectData): Promise { - this.$fs.deleteFile(release ? this.getPluginsReleaseXcconfigFilePath(projectData) : this.getPluginsDebugXcconfigFilePath(projectData)); + let pluginsXcconfigFilePath = release ? this.getPluginsReleaseXcconfigFilePath(projectData) : this.getPluginsDebugXcconfigFilePath(projectData); + this.$fs.deleteFile(pluginsXcconfigFilePath); let allPlugins: IPluginData[] = await (this.$injector.resolve("pluginsService")).getAllInstalledPlugins(projectData); for (let plugin of allPlugins) { let pluginPlatformsFolderPath = plugin.pluginPlatformsFolderPath(IOSProjectService.IOS_PLATFORM_NAME); let pluginXcconfigFilePath = path.join(pluginPlatformsFolderPath, "build.xcconfig"); if (this.$fs.exists(pluginXcconfigFilePath)) { - await this.mergeXcconfigFiles(pluginXcconfigFilePath, release ? this.getPluginsReleaseXcconfigFilePath(projectData) : this.getPluginsDebugXcconfigFilePath(projectData)); + await this.mergeXcconfigFiles(pluginXcconfigFilePath, pluginsXcconfigFilePath); } } let appResourcesXcconfigPath = path.join(projectData.projectDir, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME, this.getPlatformData(projectData).normalizedPlatformName, "build.xcconfig"); if (this.$fs.exists(appResourcesXcconfigPath)) { - await this.mergeXcconfigFiles(appResourcesXcconfigPath, release ? this.getPluginsReleaseXcconfigFilePath(projectData) : this.getPluginsDebugXcconfigFilePath(projectData)); + await this.mergeXcconfigFiles(appResourcesXcconfigPath, pluginsXcconfigFilePath); + } + + // Set Entitlements Property to point to default file if not set explicitly by the user. + let entitlementsPropertyValue = this.xCConfigService.readPropertyValue(pluginsXcconfigFilePath, constants.CODE_SIGN_ENTITLEMENTS); + if (entitlementsPropertyValue == null) { + temp.track(); + let tempEntitlementsDir = temp.mkdirSync("entitlements"); + let tempEntitlementsFilePath = path.join(tempEntitlementsDir, "set-entitlements.xcconfig"); + const entitlementsRelativePath = this.$iOSEntitlementsService.getPlatformsEntitlementsRelativePath(projectData); + this.$fs.writeFile(tempEntitlementsFilePath, `CODE_SIGN_ENTITLEMENTS = ${entitlementsRelativePath}${EOL}`); + + await this.mergeXcconfigFiles(tempEntitlementsFilePath, pluginsXcconfigFilePath); } let podFilesRootDirName = path.join("Pods", "Target Support Files", `Pods-${projectData.projectName}`); diff --git a/test/ios-project-service.ts b/test/ios-project-service.ts index 47380e404a..644f779f53 100644 --- a/test/ios-project-service.ts +++ b/test/ios-project-service.ts @@ -763,7 +763,10 @@ describe("Merge Project XCConfig files", () => { testInjector: IInjector, iOSProjectService: IOSProjectService, projectData: IProjectData, - fs: IFileSystem; + fs: IFileSystem, + appResourcesXcconfigPath: string, + appResourceXCConfigContent: string, + iOSEntitlementsService: IOSEntitlementsService; beforeEach(() => { projectName = "projectDirectory"; @@ -774,6 +777,16 @@ describe("Merge Project XCConfig files", () => { projectData = testInjector.resolve("projectData"); projectData.projectDir = projectPath; + iOSEntitlementsService = testInjector.resolve("iOSEntitlementsService"); + + appResourcesXcconfigPath = path.join(projectData.projectDir, constants.APP_FOLDER_NAME, + constants.APP_RESOURCES_FOLDER_NAME, constants.IOS_PLATFORM_NORMALIZED_NAME, "build.xcconfig"); + appResourceXCConfigContent = `CODE_SIGN_IDENTITY = iPhone Distribution + // To build for device with XCode 8 you need to specify your development team. More info: https://developer.apple.com/library/prerelease/content/releasenotes/DeveloperTools/RN-Xcode/Introduction.html + // DEVELOPMENT_TEAM = YOUR_TEAM_ID; + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; + `; let testPackageJson = { "name": "test-project", "version": "0.0.1" @@ -783,26 +796,38 @@ describe("Merge Project XCConfig files", () => { }); it("Uses the build.xcconfig file content from App_Resources", async () => { - let appResourcesXcconfigPath = path.join(projectData.projectDir, constants.APP_FOLDER_NAME, - constants.APP_RESOURCES_FOLDER_NAME, constants.IOS_PLATFORM_NORMALIZED_NAME, "build.xcconfig"); - let appResourceXCConfigContent = `CODE_SIGN_IDENTITY = iPhone Distribution - // To build for device with XCode 8 you need to specify your development team. More info: https://developer.apple.com/library/prerelease/content/releasenotes/DeveloperTools/RN-Xcode/Introduction.html - // DEVELOPMENT_TEAM = YOUR_TEAM_ID; - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage; - `; + // setup app_resource build.xcconfig fs.writeFile(appResourcesXcconfigPath, appResourceXCConfigContent); - await (iOSProjectService).mergeProjectXcconfigFiles(true, projectData); + // run merge for all release: debug|release + for (let release in [true, false]) { + await (iOSProjectService).mergeProjectXcconfigFiles(release, projectData); - let destinationFilePath = path.join(projectData.platformsDir, constants.IOS_PLATFORM_NAME, "plugins-release.xcconfig"); + let destinationFilePath = release ? (iOSProjectService).getPluginsReleaseXcconfigFilePath(projectData) + : (iOSProjectService).getPluginsDebugXcconfigFilePath(projectData); - assert.isTrue(fs.exists(destinationFilePath), 'Target build xcconfig is missing.'); - let expected = { - 'ASSETCATALOG_COMPILER_APPICON_NAME': 'AppIcon', - 'ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME': 'LaunchImage', - 'CODE_SIGN_IDENTITY': 'iPhone Distribution' - }; - assertPropertyValues(expected, destinationFilePath, testInjector); + assert.isTrue(fs.exists(destinationFilePath), 'Target build xcconfig is missing for release: ' + release); + let expected = { + 'ASSETCATALOG_COMPILER_APPICON_NAME': 'AppIcon', + 'ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME': 'LaunchImage', + 'CODE_SIGN_IDENTITY': 'iPhone Distribution' + }; + assertPropertyValues(expected, destinationFilePath, testInjector); + } + }); + + it("Adds the entitlements property if not set by the user", async () => { + for (let release in [true, false]) { + await (iOSProjectService).mergeProjectXcconfigFiles(release, projectData); + + let destinationFilePath = release ? (iOSProjectService).getPluginsReleaseXcconfigFilePath(projectData) + : (iOSProjectService).getPluginsDebugXcconfigFilePath(projectData); + + assert.isTrue(fs.exists(destinationFilePath), 'Target build xcconfig is missing for release: ' + release); + let expected = { + 'CODE_SIGN_ENTITLEMENTS': iOSEntitlementsService.getPlatformsEntitlementsRelativePath(projectData) + }; + assertPropertyValues(expected, destinationFilePath, testInjector); + } }); }); From 6e37aa87a996b1cde8c5650437dd2883bde2522a Mon Sep 17 00:00:00 2001 From: Yosif Yosifov Date: Thu, 27 Apr 2017 18:00:16 +0300 Subject: [PATCH 16/19] Fix default empty entitlements file - was needless and caused errors. --- lib/services/ios-entitlements-service.ts | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/lib/services/ios-entitlements-service.ts b/lib/services/ios-entitlements-service.ts index cbc94d5168..79e9f545f0 100644 --- a/lib/services/ios-entitlements-service.ts +++ b/lib/services/ios-entitlements-service.ts @@ -46,16 +46,6 @@ export class IOSEntitlementsService { }); }; - // Start with a default empty entitlements file. - session.patch({ - name: "Default entitlements file", - read: () => - ` - - - ` - }); - let allPlugins = await this.getAllInstalledPlugins(projectData); for (let plugin of allPlugins) { let pluginInfoPlistPath = path.join(plugin.pluginPlatformsFolderPath(constants.IOS_PLATFORM_NAME), this.getDefaultEntitlementsName()); @@ -74,13 +64,6 @@ export class IOSEntitlementsService { return; } - public async patchXcconfigFile(projectData: IProjectData) { - //CODE_SIGN_ENTITLEMENTS = fgomobile/fgomobile.entitlements - //let entitlementsRelativePath = path.join(projectData.projectName, projectData.projectName + ".entitlements"); - - return; - } - private getAllInstalledPlugins(projectData: IProjectData): Promise { return (this.$injector.resolve("pluginsService")).getAllInstalledPlugins(projectData); } From dc508e6107d3230db8645837f43da7348f663f15 Mon Sep 17 00:00:00 2001 From: Yosif Yosifov Date: Fri, 28 Apr 2017 18:09:24 +0300 Subject: [PATCH 17/19] Adding merge entitlements tests --- lib/services/ios-entitlements-service.ts | 11 +- test/ios-entitlements-service.ts | 131 +++++++++++++++++++++++ test/ios-project-service.ts | 25 +++++ 3 files changed, 160 insertions(+), 7 deletions(-) create mode 100644 test/ios-entitlements-service.ts diff --git a/lib/services/ios-entitlements-service.ts b/lib/services/ios-entitlements-service.ts index 79e9f545f0..d281d2010e 100644 --- a/lib/services/ios-entitlements-service.ts +++ b/lib/services/ios-entitlements-service.ts @@ -8,12 +8,10 @@ export class IOSEntitlementsService { private $logger: ILogger) { } - private getDefaultEntitlementsName() : string { - return "app.entitlements"; - } + public static readonly DefaultEntitlementsName: string = "app.entitlements"; private getDefaultAppEntitlementsPath(projectData: IProjectData) : string { - let entitlementsName = this.getDefaultEntitlementsName(); + let entitlementsName = IOSEntitlementsService.DefaultEntitlementsName; let entitlementsPath = path.join(projectData.projectDir, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME, constants.IOS_PLATFORM_NORMALIZED_NAME, @@ -21,7 +19,7 @@ export class IOSEntitlementsService { return entitlementsPath; } - private getPlatformsEntitlementsPath(projectData: IProjectData) : string { + public getPlatformsEntitlementsPath(projectData: IProjectData) : string { return path.join(projectData.platformsDir, constants.IOS_PLATFORM_NAME, projectData.projectName, projectData.projectName + ".entitlements"); } @@ -48,11 +46,10 @@ export class IOSEntitlementsService { let allPlugins = await this.getAllInstalledPlugins(projectData); for (let plugin of allPlugins) { - let pluginInfoPlistPath = path.join(plugin.pluginPlatformsFolderPath(constants.IOS_PLATFORM_NAME), this.getDefaultEntitlementsName()); + let pluginInfoPlistPath = path.join(plugin.pluginPlatformsFolderPath(constants.IOS_PLATFORM_NAME), IOSEntitlementsService.DefaultEntitlementsName); makePatch(pluginInfoPlistPath); } - // for older ios-app-templates or if has been deleted. let appEntitlementsPath = this.getDefaultAppEntitlementsPath(projectData); if (this.$fs.exists(appEntitlementsPath)) { makePatch(appEntitlementsPath); diff --git a/test/ios-entitlements-service.ts b/test/ios-entitlements-service.ts new file mode 100644 index 0000000000..84540f85fa --- /dev/null +++ b/test/ios-entitlements-service.ts @@ -0,0 +1,131 @@ +import temp = require("temp"); +import { EOL } from "os"; +import { assert } from "chai"; +import { IOSEntitlementsService } from "../lib/services/ios-entitlements-service"; +import * as yok from "../lib/common/yok"; +import * as stubs from "./stubs"; +import * as FsLib from "../lib/common/file-system"; +import * as path from "path"; + +// start tracking temporary folders/files +temp.track(); + +describe("IOSEntitlements Service Tests", () => { + const createTestInjector = (): IInjector => { + const testInjector = new yok.Yok(); + + testInjector.register('platformsData', stubs.PlatformsDataStub); + testInjector.register("logger", stubs.LoggerStub); + testInjector.register('iOSEntitlementsService', IOSEntitlementsService); + + testInjector.register("fs", FsLib.FileSystem); + + testInjector.register("pluginsService", { + getAllInstalledPlugins: async () => [] + }); + + return testInjector; + }; + + let injector: IInjector, + platformsData: any, + projectData: IProjectData, + fs: IFileSystem, + iOSEntitlementsService: IOSEntitlementsService, + destinationFilePath: string; + + beforeEach(function() { + injector = createTestInjector(); + + platformsData = injector.resolve("platformsData"); + projectData = platformsData.getPlatformData(); + projectData.projectName = 'testApp'; + + projectData.platformsDir = temp.mkdirSync("platformsDir"); + projectData.projectDir = temp.mkdirSync("projectDir"); + + fs = injector.resolve("$fs"); + + iOSEntitlementsService = injector.resolve("iOSEntitlementsService"); + destinationFilePath = iOSEntitlementsService.getPlatformsEntitlementsPath(projectData); + }); + + describe("Ensure paths constructed are correct", () => { + it("Ensure destination entitlements relative path is calculated correctly.", () => { + const expected = "testApp/testApp.entitlements"; + let actual = iOSEntitlementsService.getPlatformsEntitlementsRelativePath(projectData); + assert.equal(actual, expected); + }); + }); + + describe("Merge", () => { + const defaultPlistContent = ` + + + +`; + const defaultAppResourcesEntitlementsContent = ` + + + + aps-environment + development + +`; + const defaultPluginEntitlementsContent = ` + + + + aps-environment + production + +`; + + function assertContent(actual: string, expected: string) { + let strip = (x: string) => { + return x.replace(EOL, '').trim(); + }; + assert.equal(strip(actual), strip(expected)); + } + + it("Merge creates a default entitlements file.", async () => { + // act + await iOSEntitlementsService.merge(projectData); + + // assert + let actual = fs.readText(destinationFilePath); + assertContent(actual, defaultPlistContent); + }); + + it("Merge uses the entitlements from App_Resources folder", async () => { + let appResourcesEntitlement = (iOSEntitlementsService).getDefaultAppEntitlementsPath(projectData); + fs.writeFile(appResourcesEntitlement, defaultAppResourcesEntitlementsContent); + + // act + await iOSEntitlementsService.merge(projectData); + + // assert + let actual = fs.readText(destinationFilePath); + assertContent(actual, defaultAppResourcesEntitlementsContent); + }); + + it("Merge uses the entitlements file from a Plugin", async () => { + let pluginsService = injector.resolve("pluginsService"); + let testPluginFolderPath = temp.mkdirSync("testPlugin"); + pluginsService.getAllInstalledPlugins = async() => [{ + pluginPlatformsFolderPath: (platform: string) => { + return testPluginFolderPath; + } + }]; + let pluginAppEntitlementsPath = path.join(testPluginFolderPath, IOSEntitlementsService.DefaultEntitlementsName); + fs.writeFile(pluginAppEntitlementsPath, defaultPluginEntitlementsContent); + + // act + await iOSEntitlementsService.merge(projectData); + + // assert + let actual = fs.readText(destinationFilePath); + assertContent(actual, defaultPluginEntitlementsContent); + }); + }); +}); diff --git a/test/ios-project-service.ts b/test/ios-project-service.ts index 644f779f53..8149b36f85 100644 --- a/test/ios-project-service.ts +++ b/test/ios-project-service.ts @@ -1,4 +1,5 @@ import * as path from "path"; +import { EOL } from "os"; import * as ChildProcessLib from "../lib/common/child-process"; import * as ConfigLib from "../lib/config"; import * as ErrorsLib from "../lib/common/errors"; @@ -830,4 +831,28 @@ describe("Merge Project XCConfig files", () => { assertPropertyValues(expected, destinationFilePath, testInjector); } }); + + it("The user specified entitlements property takes percedence", async () => { + // setup app_resource build.xcconfig + const expectedEntitlementsFile = 'user.entitlements'; + let xcconfigEntitlements = appResourceXCConfigContent + `${EOL}CODE_SIGN_ENTITLEMENTS = ${expectedEntitlementsFile}`; + fs.writeFile(appResourcesXcconfigPath, xcconfigEntitlements); + + // run merge for all release: debug|release + for (let release in [true, false]) { + await (iOSProjectService).mergeProjectXcconfigFiles(release, projectData); + + let destinationFilePath = release ? (iOSProjectService).getPluginsReleaseXcconfigFilePath(projectData) + : (iOSProjectService).getPluginsDebugXcconfigFilePath(projectData); + + assert.isTrue(fs.exists(destinationFilePath), 'Target build xcconfig is missing for release: ' + release); + let expected = { + 'ASSETCATALOG_COMPILER_APPICON_NAME': 'AppIcon', + 'ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME': 'LaunchImage', + 'CODE_SIGN_IDENTITY': 'iPhone Distribution', + 'CODE_SIGN_ENTITLEMENTS': expectedEntitlementsFile + }; + assertPropertyValues(expected, destinationFilePath, testInjector); + } + }); }); From 6b74cbe6e067644d13643a81677aa83182bea0ed Mon Sep 17 00:00:00 2001 From: Yosif Yosifov Date: Fri, 28 Apr 2017 18:13:02 +0300 Subject: [PATCH 18/19] Add merge from App_Resources and Plugin integration test --- test/ios-entitlements-service.ts | 42 ++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/test/ios-entitlements-service.ts b/test/ios-entitlements-service.ts index 84540f85fa..3f8b9c72de 100644 --- a/test/ios-entitlements-service.ts +++ b/test/ios-entitlements-service.ts @@ -80,6 +80,24 @@ describe("IOSEntitlements Service Tests", () => { production `; + const namedAppResourcesEntitlementsContent = ` + + + + nameKey + appResources + +`; + const mergedEntitlementsContent = ` + + + + aps-environment + production + nameKey + appResources + +`; function assertContent(actual: string, expected: string) { let strip = (x: string) => { @@ -127,5 +145,29 @@ describe("IOSEntitlements Service Tests", () => { let actual = fs.readText(destinationFilePath); assertContent(actual, defaultPluginEntitlementsContent); }); + + it("Merge uses App_Resources and Plugins and merges all keys", async () => { + // setup app resoruces + let appResourcesEntitlement = (iOSEntitlementsService).getDefaultAppEntitlementsPath(projectData); + fs.writeFile(appResourcesEntitlement, namedAppResourcesEntitlementsContent); + + // setup plugin entitlements + let pluginsService = injector.resolve("pluginsService"); + let testPluginFolderPath = temp.mkdirSync("testPlugin"); + pluginsService.getAllInstalledPlugins = async() => [{ + pluginPlatformsFolderPath: (platform: string) => { + return testPluginFolderPath; + } + }]; + let pluginAppEntitlementsPath = path.join(testPluginFolderPath, IOSEntitlementsService.DefaultEntitlementsName); + fs.writeFile(pluginAppEntitlementsPath, defaultPluginEntitlementsContent); + + // act + await iOSEntitlementsService.merge(projectData); + + // assert + let actual = fs.readText(destinationFilePath); + assertContent(actual, mergedEntitlementsContent); + }); }); }); From ac8eb91e2ac5ec833e77d18b7843c7897d8c90f6 Mon Sep 17 00:00:00 2001 From: Yosif Yosifov Date: Thu, 11 May 2017 10:13:45 +0300 Subject: [PATCH 19/19] Fix PR comments --- lib/constants.ts | 2 -- lib/services/ios-entitlements-service.ts | 19 +++++++++++-------- lib/services/ios-project-service.ts | 20 ++++++++++---------- test/ios-entitlements-service.ts | 10 +++++++++- test/ios-project-service.ts | 4 ++-- 5 files changed, 32 insertions(+), 23 deletions(-) diff --git a/lib/constants.ts b/lib/constants.ts index b96ed725a7..6750b710a7 100644 --- a/lib/constants.ts +++ b/lib/constants.ts @@ -16,8 +16,6 @@ export const TEST_RUNNER_NAME = "nativescript-unit-test-runner"; export const LIVESYNC_EXCLUDED_FILE_PATTERNS = ["**/*.js.map", "**/*.ts"]; export const XML_FILE_EXTENSION = ".xml"; export const PLATFORMS_DIR_NAME = "platforms"; -export const IOS_PLATFORM_NAME = "ios"; -export const IOS_PLATFORM_NORMALIZED_NAME = "iOS"; export const CODE_SIGN_ENTITLEMENTS = "CODE_SIGN_ENTITLEMENTS"; export class PackageVersion { diff --git a/lib/services/ios-entitlements-service.ts b/lib/services/ios-entitlements-service.ts index d281d2010e..e30be51f54 100644 --- a/lib/services/ios-entitlements-service.ts +++ b/lib/services/ios-entitlements-service.ts @@ -4,23 +4,25 @@ import { PlistSession } from "plist-merge-patch"; export class IOSEntitlementsService { constructor(private $fs: IFileSystem, - private $injector: IInjector, - private $logger: ILogger) { + private $logger: ILogger, + private $devicePlatformsConstants: Mobile.IDevicePlatformsConstants, + private $mobileHelper: Mobile.IMobileHelper, + private $pluginsService: IPluginsService) { } public static readonly DefaultEntitlementsName: string = "app.entitlements"; private getDefaultAppEntitlementsPath(projectData: IProjectData) : string { - let entitlementsName = IOSEntitlementsService.DefaultEntitlementsName; - let entitlementsPath = path.join(projectData.projectDir, + const entitlementsName = IOSEntitlementsService.DefaultEntitlementsName; + const entitlementsPath = path.join(projectData.projectDir, constants.APP_FOLDER_NAME, constants.APP_RESOURCES_FOLDER_NAME, - constants.IOS_PLATFORM_NORMALIZED_NAME, + this.$mobileHelper.normalizePlatformName(this.$devicePlatformsConstants.iOS), entitlementsName); return entitlementsPath; } public getPlatformsEntitlementsPath(projectData: IProjectData) : string { - return path.join(projectData.platformsDir, constants.IOS_PLATFORM_NAME, + return path.join(projectData.platformsDir, this.$devicePlatformsConstants.iOS, projectData.projectName, projectData.projectName + ".entitlements"); } public getPlatformsEntitlementsRelativePath(projectData: IProjectData): string { @@ -46,7 +48,8 @@ export class IOSEntitlementsService { let allPlugins = await this.getAllInstalledPlugins(projectData); for (let plugin of allPlugins) { - let pluginInfoPlistPath = path.join(plugin.pluginPlatformsFolderPath(constants.IOS_PLATFORM_NAME), IOSEntitlementsService.DefaultEntitlementsName); + let pluginInfoPlistPath = path.join(plugin.pluginPlatformsFolderPath(this.$devicePlatformsConstants.iOS), + IOSEntitlementsService.DefaultEntitlementsName); makePatch(pluginInfoPlistPath); } @@ -62,7 +65,7 @@ export class IOSEntitlementsService { } private getAllInstalledPlugins(projectData: IProjectData): Promise { - return (this.$injector.resolve("pluginsService")).getAllInstalledPlugins(projectData); + return this.$pluginsService.getAllInstalledPlugins(projectData); } } diff --git a/lib/services/ios-project-service.ts b/lib/services/ios-project-service.ts index f7abfe84b1..4ffe535f31 100644 --- a/lib/services/ios-project-service.ts +++ b/lib/services/ios-project-service.ts @@ -46,7 +46,7 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ private $xcode: IXcode, private $iOSEntitlementsService: IOSEntitlementsService, private $sysInfo: ISysInfo, - private xCConfigService: XCConfigService) { + private $xCConfigService: XCConfigService) { super($fs, $projectDataService); } @@ -1108,11 +1108,11 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f } // Set Entitlements Property to point to default file if not set explicitly by the user. - let entitlementsPropertyValue = this.xCConfigService.readPropertyValue(pluginsXcconfigFilePath, constants.CODE_SIGN_ENTITLEMENTS); - if (entitlementsPropertyValue == null) { + let entitlementsPropertyValue = this.$xCConfigService.readPropertyValue(pluginsXcconfigFilePath, constants.CODE_SIGN_ENTITLEMENTS); + if (entitlementsPropertyValue === null) { temp.track(); - let tempEntitlementsDir = temp.mkdirSync("entitlements"); - let tempEntitlementsFilePath = path.join(tempEntitlementsDir, "set-entitlements.xcconfig"); + const tempEntitlementsDir = temp.mkdirSync("entitlements"); + const tempEntitlementsFilePath = path.join(tempEntitlementsDir, "set-entitlements.xcconfig"); const entitlementsRelativePath = this.$iOSEntitlementsService.getPlatformsEntitlementsRelativePath(projectData); this.$fs.writeFile(tempEntitlementsFilePath, `CODE_SIGN_ENTITLEMENTS = ${entitlementsRelativePath}${EOL}`); @@ -1203,7 +1203,7 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f } private readTeamId(projectData: IProjectData): string { - let teamId = this.xCConfigService.readPropertyValue(this.getBuildXCConfigFilePath(projectData), "DEVELOPMENT_TEAM"); + let teamId = this.$xCConfigService.readPropertyValue(this.getBuildXCConfigFilePath(projectData), "DEVELOPMENT_TEAM"); let fileName = path.join(this.getPlatformData(projectData).projectRoot, "teamid"); if (this.$fs.exists(fileName)) { @@ -1214,19 +1214,19 @@ We will now place an empty obsolete compatability white screen LauncScreen.xib f } private readXCConfigProvisioningProfile(projectData: IProjectData): string { - return this.xCConfigService.readPropertyValue(this.getBuildXCConfigFilePath(projectData), "PROVISIONING_PROFILE"); + return this.$xCConfigService.readPropertyValue(this.getBuildXCConfigFilePath(projectData), "PROVISIONING_PROFILE"); } private readXCConfigProvisioningProfileForIPhoneOs(projectData: IProjectData): string { - return this.xCConfigService.readPropertyValue(this.getBuildXCConfigFilePath(projectData), "PROVISIONING_PROFILE[sdk=iphoneos*]"); + return this.$xCConfigService.readPropertyValue(this.getBuildXCConfigFilePath(projectData), "PROVISIONING_PROFILE[sdk=iphoneos*]"); } private readXCConfigProvisioningProfileSpecifier(projectData: IProjectData): string { - return this.xCConfigService.readPropertyValue(this.getBuildXCConfigFilePath(projectData), "PROVISIONING_PROFILE_SPECIFIER"); + return this.$xCConfigService.readPropertyValue(this.getBuildXCConfigFilePath(projectData), "PROVISIONING_PROFILE_SPECIFIER"); } private readXCConfigProvisioningProfileSpecifierForIPhoneOs(projectData: IProjectData): string { - return this.xCConfigService.readPropertyValue(this.getBuildXCConfigFilePath(projectData), "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]"); + return this.$xCConfigService.readPropertyValue(this.getBuildXCConfigFilePath(projectData), "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]"); } private async getDevelopmentTeam(projectData: IProjectData, teamId?: string): Promise { diff --git a/test/ios-entitlements-service.ts b/test/ios-entitlements-service.ts index 3f8b9c72de..23fbc93621 100644 --- a/test/ios-entitlements-service.ts +++ b/test/ios-entitlements-service.ts @@ -5,6 +5,10 @@ import { IOSEntitlementsService } from "../lib/services/ios-entitlements-service import * as yok from "../lib/common/yok"; import * as stubs from "./stubs"; import * as FsLib from "../lib/common/file-system"; +import * as MobilePlatformsCapabilitiesLib from "../lib/common/appbuilder/mobile-platforms-capabilities"; +import * as MobileHelperLib from "../lib/common/mobile/mobile-helper"; +import * as DevicePlatformsConstantsLib from "../lib/common/mobile/device-platforms-constants"; +import * as ErrorsLib from "../lib/common/errors"; import * as path from "path"; // start tracking temporary folders/files @@ -19,6 +23,10 @@ describe("IOSEntitlements Service Tests", () => { testInjector.register('iOSEntitlementsService', IOSEntitlementsService); testInjector.register("fs", FsLib.FileSystem); + testInjector.register("mobileHelper", MobileHelperLib.MobileHelper); + testInjector.register("devicePlatformsConstants", DevicePlatformsConstantsLib.DevicePlatformsConstants); + testInjector.register("mobilePlatformsCapabilities", MobilePlatformsCapabilitiesLib.MobilePlatformsCapabilities); + testInjector.register("errors", ErrorsLib.Errors); testInjector.register("pluginsService", { getAllInstalledPlugins: async () => [] @@ -34,7 +42,7 @@ describe("IOSEntitlements Service Tests", () => { iOSEntitlementsService: IOSEntitlementsService, destinationFilePath: string; - beforeEach(function() { + beforeEach(() => { injector = createTestInjector(); platformsData = injector.resolve("platformsData"); diff --git a/test/ios-project-service.ts b/test/ios-project-service.ts index 8149b36f85..51f45cb151 100644 --- a/test/ios-project-service.ts +++ b/test/ios-project-service.ts @@ -781,7 +781,7 @@ describe("Merge Project XCConfig files", () => { iOSEntitlementsService = testInjector.resolve("iOSEntitlementsService"); appResourcesXcconfigPath = path.join(projectData.projectDir, constants.APP_FOLDER_NAME, - constants.APP_RESOURCES_FOLDER_NAME, constants.IOS_PLATFORM_NORMALIZED_NAME, "build.xcconfig"); + constants.APP_RESOURCES_FOLDER_NAME, "iOS", "build.xcconfig"); appResourceXCConfigContent = `CODE_SIGN_IDENTITY = iPhone Distribution // To build for device with XCode 8 you need to specify your development team. More info: https://developer.apple.com/library/prerelease/content/releasenotes/DeveloperTools/RN-Xcode/Introduction.html // DEVELOPMENT_TEAM = YOUR_TEAM_ID; @@ -832,7 +832,7 @@ describe("Merge Project XCConfig files", () => { } }); - it("The user specified entitlements property takes percedence", async () => { + it("The user specified entitlements property takes precedence", async () => { // setup app_resource build.xcconfig const expectedEntitlementsFile = 'user.entitlements'; let xcconfigEntitlements = appResourceXCConfigContent + `${EOL}CODE_SIGN_ENTITLEMENTS = ${expectedEntitlementsFile}`;