From a9ed61f864c38ef187bbe88496a294cd558d8c4c Mon Sep 17 00:00:00 2001 From: fatme Date: Fri, 23 Aug 2019 00:05:36 +0300 Subject: [PATCH 1/2] feat(publish): Enable publishing of iOS apps with Xcode 11 As `Application Loader` is no longer included in Xcode 11, CLI is not able to find the path to itmsTransporter and throws an error. However, itmsTransporter app is already part of ContentDeliveryServices.framework which is part of SharedFrameworks which are included in Xcode 11 itself. So this PR returns the correct path to itmsTransporter app based on xcode's version. --- lib/commands/appstore-upload.ts | 19 +++++++------------ lib/declarations.d.ts | 1 + lib/services/itmstransporter-service.ts | 16 ++++++++++++---- test/tns-appstore-upload.ts | 1 + 4 files changed, 21 insertions(+), 16 deletions(-) diff --git a/lib/commands/appstore-upload.ts b/lib/commands/appstore-upload.ts index 60642b2352..cc2235bc96 100644 --- a/lib/commands/appstore-upload.ts +++ b/lib/commands/appstore-upload.ts @@ -25,19 +25,12 @@ export class PublishIOS implements ICommand { } public async execute(args: string[]): Promise { - let username = args[0]; - let password = args[1]; + await this.$itmsTransporterService.validate(); + + const username = args[0] || await this.$prompter.getString("Apple ID", { allowEmpty: false }); + const password = args[1] || await this.$prompter.getPassword("Apple ID password"); const mobileProvisionIdentifier = args[2]; const codeSignIdentity = args[3]; - let ipaFilePath = this.$options.ipa ? path.resolve(this.$options.ipa) : null; - - if (!username) { - username = await this.$prompter.getString("Apple ID", { allowEmpty: false }); - } - - if (!password) { - password = await this.$prompter.getPassword("Apple ID password"); - } const user = await this.$applePortalSessionService.createUserSession({ username, password }, { applicationSpecificPassword: this.$options.appleApplicationSpecificPassword, @@ -49,6 +42,8 @@ export class PublishIOS implements ICommand { this.$errors.fail(`Invalid username and password combination. Used '${username}' as the username.`); } + let ipaFilePath = this.$options.ipa ? path.resolve(this.$options.ipa) : null; + if (!mobileProvisionIdentifier && !ipaFilePath) { this.$logger.warn("No mobile provision identifier set. A default mobile provision will be used. You can set one in app/App_Resources/iOS/build.xcconfig"); } @@ -58,9 +53,9 @@ export class PublishIOS implements ICommand { } this.$options.release = true; - const platform = this.$devicePlatformsConstants.iOS.toLowerCase(); if (!ipaFilePath) { + const platform = this.$devicePlatformsConstants.iOS.toLowerCase(); // No .ipa path provided, build .ipa on out own. if (mobileProvisionIdentifier || codeSignIdentity) { // This is not very correct as if we build multiple targets we will try to sign all of them using the signing identity here. diff --git a/lib/declarations.d.ts b/lib/declarations.d.ts index d0e91d2507..7272e7cfd5 100644 --- a/lib/declarations.d.ts +++ b/lib/declarations.d.ts @@ -639,6 +639,7 @@ interface IITMSData { * Used for communicating with Xcode's iTMS Transporter tool. */ interface IITMSTransporterService { + validate(): Promise; /** * Uploads an .ipa package to iTunes Connect. * @param {IITMSData} data Data needed to upload the package diff --git a/lib/services/itmstransporter-service.ts b/lib/services/itmstransporter-service.ts index 5d769f9d42..411a3713b5 100644 --- a/lib/services/itmstransporter-service.ts +++ b/lib/services/itmstransporter-service.ts @@ -19,6 +19,13 @@ export class ITMSTransporterService implements IITMSTransporterService { return this.$injector.resolve("projectData"); } + public async validate(): Promise { + const itmsTransporterPath = await this.getITMSTransporterPath(); + if (!this.$fs.exists(itmsTransporterPath)) { + this.$errors.fail('iTMS Transporter not found on this machine - make sure your Xcode installation is not damaged.'); + } + } + public async upload(data: IITMSData): Promise { temp.track(); const itmsTransporterPath = await this.getITMSTransporterPath(); @@ -96,11 +103,12 @@ export class ITMSTransporterService implements IITMSTransporterService { @cache() private async getITMSTransporterPath(): Promise { const xcodePath = await this.$xcodeSelectService.getContentsDirectoryPath(); - const loaderAppContentsPath = path.join(xcodePath, "Applications", "Application Loader.app", "Contents"); - const itmsTransporterPath = path.join(loaderAppContentsPath, ITMSConstants.iTMSDirectoryName, "bin", ITMSConstants.iTMSExecutableName); + let itmsTransporterPath = path.join(xcodePath, "..", "Contents", "SharedFrameworks", "ContentDeliveryServices.framework", "Versions", "A", "itms", "bin", ITMSConstants.iTMSExecutableName); - if (!this.$fs.exists(itmsTransporterPath)) { - this.$errors.fail('iTMS Transporter not found on this machine - make sure your Xcode installation is not damaged.'); + const xcodeVersionData = await this.$xcodeSelectService.getXcodeVersion(); + if (+xcodeVersionData.major < 11) { + const loaderAppContentsPath = path.join(xcodePath, "Applications", "Application Loader.app", "Contents"); + itmsTransporterPath = path.join(loaderAppContentsPath, ITMSConstants.iTMSDirectoryName, "bin", ITMSConstants.iTMSExecutableName); } return itmsTransporterPath; diff --git a/test/tns-appstore-upload.ts b/test/tns-appstore-upload.ts index 9c5fa90520..b668bee89f 100644 --- a/test/tns-appstore-upload.ts +++ b/test/tns-appstore-upload.ts @@ -124,6 +124,7 @@ class AppStore { expectITMSTransporterUpload() { this.expectedItmsTransporterServiceUploadCalls = 1; + this.itmsTransporterService.validate = () => Promise.resolve(); this.itmsTransporterService.upload = (options: IITMSData) => { this.itmsTransporterServiceUploadCalls++; chai.assert.equal(options.ipaFilePath, "/Users/person/git/MyProject/platforms/ios/archive/MyProject.ipa"); From 3bf1eec97d0fd6c065a6225754cfc3f86a7a0ae6 Mon Sep 17 00:00:00 2001 From: fatme Date: Fri, 23 Aug 2019 00:16:42 +0300 Subject: [PATCH 2/2] fix(xcode): fix getXcodeVersion() method Currently `getXcodeVersion` always returns `{ major: null, minor: null, patch: null }`. This method uses internally `sysInfo.getXcodeVersion()`. On the other side, `sysInfo.getXcodeVersion()` returns the xocde's version in format `major.minor.patch` - for example `10.3.4`. After getting the result of `sysInfo.getXcodeVersion()`, NativeScript CLI matches the received output with `/Xcode (.*)/` regex. As the received output doesn't contain `Xcode`, the match returns null and as result `getXcodeVersion` method returns `{ major: null, minor: null, patch: null }`. --- lib/common/services/xcode-select-service.ts | 12 +++--------- lib/common/test/unit-tests/xcode-select-service.ts | 2 +- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/lib/common/services/xcode-select-service.ts b/lib/common/services/xcode-select-service.ts index 98dd487961..dc2f7b13e5 100644 --- a/lib/common/services/xcode-select-service.ts +++ b/lib/common/services/xcode-select-service.ts @@ -35,15 +35,9 @@ export class XcodeSelectService implements IXcodeSelectService { this.$errors.fail("xcodebuild execution failed. Make sure that you have latest Xcode and tools installed."); } - const xcodeVersionMatch = xcodeVer.match(/Xcode (.*)/), - xcodeVersionGroup = xcodeVersionMatch && xcodeVersionMatch[1], - xcodeVersionSplit = xcodeVersionGroup && xcodeVersionGroup.split("."); - - return { - major: xcodeVersionSplit && xcodeVersionSplit[0], - minor: xcodeVersionSplit && xcodeVersionSplit[1], - patch: xcodeVersionSplit && xcodeVersionSplit[2] - }; + const [ major, minor, patch ] = xcodeVer.split("."); + + return { major, minor, patch }; } } diff --git a/lib/common/test/unit-tests/xcode-select-service.ts b/lib/common/test/unit-tests/xcode-select-service.ts index c54bbff223..9783a3a73a 100644 --- a/lib/common/test/unit-tests/xcode-select-service.ts +++ b/lib/common/test/unit-tests/xcode-select-service.ts @@ -59,7 +59,7 @@ describe("xcode-select-service", () => { }); it("gets correct Xcode version", async () => { - injector = createTestInjector({ xcodeSelectStdout: null, isDarwin: true, xcodeVersionOutput: "Xcode 7.3\nBuild version 7D175" }); + injector = createTestInjector({ xcodeSelectStdout: null, isDarwin: true, xcodeVersionOutput: "7.3" }); service = injector.resolve("$xcodeSelectService"); const xcodeVersion = await service.getXcodeVersion();