nativescript 8.5.0-dev.0 → 8.5.0-dev.0-next-03-25-2023-4519382718
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/commands/appstore-upload.js +8 -8
- package/lib/constants.js +1 -0
- package/lib/declarations.d.ts +6 -1
- package/lib/services/apple-portal/apple-portal-session-service.js +77 -10
- package/lib/services/apple-portal/definitions.d.ts +1 -0
- package/lib/services/ios/spm-service.js +1 -1
- package/lib/services/itmstransporter-service.js +70 -3
- package/package.json +1 -1
|
@@ -37,13 +37,12 @@ class PublishIOS {
|
|
|
37
37
|
this.$projectData.initializeProjectData();
|
|
38
38
|
}
|
|
39
39
|
execute(args) {
|
|
40
|
+
var _a;
|
|
40
41
|
return __awaiter(this, void 0, void 0, function* () {
|
|
41
|
-
yield this.$itmsTransporterService.validate();
|
|
42
|
+
yield this.$itmsTransporterService.validate(this.$options.appleApplicationSpecificPassword);
|
|
42
43
|
const username = args[0] ||
|
|
43
44
|
(yield this.$prompter.getString("Apple ID", { allowEmpty: false }));
|
|
44
45
|
const password = args[1] || (yield this.$prompter.getPassword("Apple ID password"));
|
|
45
|
-
const mobileProvisionIdentifier = args[2];
|
|
46
|
-
const codeSignIdentity = args[3];
|
|
47
46
|
const user = yield this.$applePortalSessionService.createUserSession({ username, password }, {
|
|
48
47
|
applicationSpecificPassword: this.$options
|
|
49
48
|
.appleApplicationSpecificPassword,
|
|
@@ -54,21 +53,21 @@ class PublishIOS {
|
|
|
54
53
|
if (!user.areCredentialsValid) {
|
|
55
54
|
this.$errors.fail(`Invalid username and password combination. Used '${username}' as the username.`);
|
|
56
55
|
}
|
|
56
|
+
const mobileProvisionIdentifier = (_a = this.$options.provision) !== null && _a !== void 0 ? _a : args[2];
|
|
57
57
|
let ipaFilePath = this.$options.ipa
|
|
58
58
|
? path.resolve(this.$options.ipa)
|
|
59
59
|
: null;
|
|
60
60
|
if (!mobileProvisionIdentifier && !ipaFilePath) {
|
|
61
61
|
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");
|
|
62
62
|
}
|
|
63
|
-
if (!codeSignIdentity && !ipaFilePath) {
|
|
64
|
-
this.$logger.warn("No code sign identity set. A default code sign identity will be used. You can set one in app/App_Resources/iOS/build.xcconfig");
|
|
65
|
-
}
|
|
66
63
|
this.$options.release = true;
|
|
67
64
|
if (!ipaFilePath) {
|
|
68
65
|
const platform = this.$devicePlatformsConstants.iOS.toLowerCase();
|
|
69
|
-
if (mobileProvisionIdentifier
|
|
70
|
-
this.$logger.info("Building .ipa with the selected mobile provision and/or certificate."
|
|
66
|
+
if (mobileProvisionIdentifier) {
|
|
67
|
+
this.$logger.info("Building .ipa with the selected mobile provision and/or certificate. " +
|
|
68
|
+
mobileProvisionIdentifier);
|
|
71
69
|
this.$options.forDevice = true;
|
|
70
|
+
this.$options.provision = mobileProvisionIdentifier;
|
|
72
71
|
const buildData = new build_data_1.IOSBuildData(this.$projectData.projectDir, platform, Object.assign(Object.assign({}, this.$options.argv), { watch: false }));
|
|
73
72
|
ipaFilePath = yield this.$buildController.prepareAndBuild(buildData);
|
|
74
73
|
}
|
|
@@ -87,6 +86,7 @@ class PublishIOS {
|
|
|
87
86
|
ipaFilePath,
|
|
88
87
|
shouldExtractIpa: !!this.$options.ipa,
|
|
89
88
|
verboseLogging: this.$logger.getLevel() === "TRACE",
|
|
89
|
+
teamId: this.$options.teamId,
|
|
90
90
|
});
|
|
91
91
|
});
|
|
92
92
|
}
|
package/lib/constants.js
CHANGED
|
@@ -157,6 +157,7 @@ ITMSConstants.VerboseLoggingLevels = {
|
|
|
157
157
|
};
|
|
158
158
|
ITMSConstants.iTMSExecutableName = "iTMSTransporter";
|
|
159
159
|
ITMSConstants.iTMSDirectoryName = "itms";
|
|
160
|
+
ITMSConstants.altoolExecutableName = "altool";
|
|
160
161
|
class ItunesConnectApplicationTypesClass {
|
|
161
162
|
constructor() {
|
|
162
163
|
this.iOS = "iOS App";
|
package/lib/declarations.d.ts
CHANGED
|
@@ -765,13 +765,18 @@ interface IITMSData {
|
|
|
765
765
|
* @type {string}
|
|
766
766
|
*/
|
|
767
767
|
verboseLogging?: boolean;
|
|
768
|
+
/**
|
|
769
|
+
* Specifies the team id
|
|
770
|
+
* @type {string}
|
|
771
|
+
*/
|
|
772
|
+
teamId?: string;
|
|
768
773
|
}
|
|
769
774
|
|
|
770
775
|
/**
|
|
771
776
|
* Used for communicating with Xcode iTMS Transporter tool.
|
|
772
777
|
*/
|
|
773
778
|
interface IITMSTransporterService {
|
|
774
|
-
validate(): Promise<void>;
|
|
779
|
+
validate(appSpecificPassword?: string): Promise<void>;
|
|
775
780
|
/**
|
|
776
781
|
* Uploads an .ipa package to iTunes Connect.
|
|
777
782
|
* @param {IITMSData} data Data needed to upload the package
|
|
@@ -12,6 +12,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
12
12
|
exports.ApplePortalSessionService = void 0;
|
|
13
13
|
const helpers_1 = require("../../common/helpers");
|
|
14
14
|
const yok_1 = require("../../common/yok");
|
|
15
|
+
const crypto = require("crypto");
|
|
15
16
|
class ApplePortalSessionService {
|
|
16
17
|
constructor($applePortalCookieService, $errors, $httpClient, $logger, $prompter) {
|
|
17
18
|
this.$applePortalCookieService = $applePortalCookieService;
|
|
@@ -31,7 +32,7 @@ class ApplePortalSessionService {
|
|
|
31
32
|
if (!opts || !opts.sessionBase64) {
|
|
32
33
|
if (loginResult.isTwoFactorAuthenticationEnabled) {
|
|
33
34
|
const authServiceKey = (yield this.getLoginConfig()).authServiceKey;
|
|
34
|
-
yield this.handleTwoFactorAuthentication(loginResult.scnt, loginResult.xAppleIdSessionId, authServiceKey);
|
|
35
|
+
yield this.handleTwoFactorAuthentication(loginResult.scnt, loginResult.xAppleIdSessionId, authServiceKey, loginResult.hashcash);
|
|
35
36
|
}
|
|
36
37
|
const sessionResponse = yield this.$httpClient.httpRequest({
|
|
37
38
|
url: "https://appstoreconnect.apple.com/olympus/v1/session",
|
|
@@ -80,12 +81,14 @@ class ApplePortalSessionService {
|
|
|
80
81
|
});
|
|
81
82
|
}
|
|
82
83
|
login(credentials, opts) {
|
|
84
|
+
var _a, _b;
|
|
83
85
|
return __awaiter(this, void 0, void 0, function* () {
|
|
84
86
|
const result = {
|
|
85
87
|
scnt: null,
|
|
86
88
|
xAppleIdSessionId: null,
|
|
87
89
|
isTwoFactorAuthenticationEnabled: false,
|
|
88
90
|
areCredentialsValid: true,
|
|
91
|
+
hashcash: null,
|
|
89
92
|
};
|
|
90
93
|
if (opts && opts.sessionBase64) {
|
|
91
94
|
const decodedSession = Buffer.from(opts.sessionBase64, "base64").toString("utf8");
|
|
@@ -99,6 +102,10 @@ class ApplePortalSessionService {
|
|
|
99
102
|
}
|
|
100
103
|
catch (err) {
|
|
101
104
|
const statusCode = err && err.response && err.response.status;
|
|
105
|
+
const bits = (_a = err === null || err === void 0 ? void 0 : err.response) === null || _a === void 0 ? void 0 : _a.headers["x-apple-hc-bits"];
|
|
106
|
+
const challenge = (_b = err === null || err === void 0 ? void 0 : err.response) === null || _b === void 0 ? void 0 : _b.headers["x-apple-hc-challenge"];
|
|
107
|
+
const hashcash = makeHashCash(bits, challenge);
|
|
108
|
+
result.hashcash = hashcash;
|
|
102
109
|
result.areCredentialsValid = statusCode !== 401 && statusCode !== 403;
|
|
103
110
|
result.isTwoFactorAuthenticationEnabled = statusCode === 409;
|
|
104
111
|
if (result.isTwoFactorAuthenticationEnabled &&
|
|
@@ -166,12 +173,13 @@ For more details how to set up your environment, please execute "tns publish ios
|
|
|
166
173
|
return config || this.defaultLoginConfig;
|
|
167
174
|
});
|
|
168
175
|
}
|
|
169
|
-
handleTwoFactorAuthentication(scnt, xAppleIdSessionId, authServiceKey) {
|
|
176
|
+
handleTwoFactorAuthentication(scnt, xAppleIdSessionId, authServiceKey, hashcash) {
|
|
170
177
|
return __awaiter(this, void 0, void 0, function* () {
|
|
171
178
|
const headers = {
|
|
172
179
|
scnt: scnt,
|
|
173
180
|
"X-Apple-Id-Session-Id": xAppleIdSessionId,
|
|
174
181
|
"X-Apple-Widget-Key": authServiceKey,
|
|
182
|
+
"X-Apple-HC": hashcash,
|
|
175
183
|
Accept: "application/json",
|
|
176
184
|
};
|
|
177
185
|
const authResponse = yield this.$httpClient.httpRequest({
|
|
@@ -180,17 +188,35 @@ For more details how to set up your environment, please execute "tns publish ios
|
|
|
180
188
|
headers,
|
|
181
189
|
});
|
|
182
190
|
const data = JSON.parse(authResponse.body);
|
|
183
|
-
|
|
191
|
+
const isSMS = data.trustedPhoneNumbers &&
|
|
192
|
+
data.trustedPhoneNumbers.length === 1 &&
|
|
193
|
+
data.noTrustedDevices;
|
|
194
|
+
const multiSMS = data.trustedPhoneNumbers &&
|
|
195
|
+
data.trustedPhoneNumbers.length !== 1 &&
|
|
196
|
+
data.noTrustedDevices;
|
|
197
|
+
let token;
|
|
198
|
+
if (data.trustedPhoneNumbers &&
|
|
199
|
+
data.trustedPhoneNumbers.length &&
|
|
200
|
+
!multiSMS) {
|
|
184
201
|
const parsedAuthResponse = JSON.parse(authResponse.body);
|
|
185
|
-
|
|
202
|
+
token = yield this.$prompter.getString(`Please enter the ${parsedAuthResponse.securityCode.length} digit code`, { allowEmpty: false });
|
|
203
|
+
const body = {
|
|
204
|
+
securityCode: {
|
|
205
|
+
code: token.toString(),
|
|
206
|
+
},
|
|
207
|
+
};
|
|
208
|
+
let url = `https://idmsa.apple.com/appleauth/auth/verify/trusteddevice/securitycode`;
|
|
209
|
+
if (isSMS) {
|
|
210
|
+
body.mode = "sms";
|
|
211
|
+
body.phoneNumber = {
|
|
212
|
+
id: data.trustedPhoneNumbers[0].id,
|
|
213
|
+
};
|
|
214
|
+
url = `https://idmsa.apple.com/appleauth/auth/verify/phone/securitycode`;
|
|
215
|
+
}
|
|
186
216
|
yield this.$httpClient.httpRequest({
|
|
187
|
-
url
|
|
217
|
+
url,
|
|
188
218
|
method: "POST",
|
|
189
|
-
body
|
|
190
|
-
securityCode: {
|
|
191
|
-
code: token.toString(),
|
|
192
|
-
},
|
|
193
|
-
},
|
|
219
|
+
body,
|
|
194
220
|
headers: Object.assign(Object.assign({}, headers), { "Content-Type": "application/json" }),
|
|
195
221
|
});
|
|
196
222
|
const authTrustResponse = yield this.$httpClient.httpRequest({
|
|
@@ -200,6 +226,9 @@ For more details how to set up your environment, please execute "tns publish ios
|
|
|
200
226
|
});
|
|
201
227
|
this.$applePortalCookieService.updateUserSessionCookie(authTrustResponse.headers["set-cookie"]);
|
|
202
228
|
}
|
|
229
|
+
else if (multiSMS) {
|
|
230
|
+
this.$errors.fail(`The NativeScript CLI does not support SMS authenticaton with multiple registered phone numbers.`);
|
|
231
|
+
}
|
|
203
232
|
else {
|
|
204
233
|
this.$errors.fail(`Although response from Apple indicated activated Two-step Verification or Two-factor Authentication, NativeScript CLI don't know how to handle this response: ${data}`);
|
|
205
234
|
}
|
|
@@ -208,3 +237,41 @@ For more details how to set up your environment, please execute "tns publish ios
|
|
|
208
237
|
}
|
|
209
238
|
exports.ApplePortalSessionService = ApplePortalSessionService;
|
|
210
239
|
yok_1.injector.register("applePortalSessionService", ApplePortalSessionService);
|
|
240
|
+
function makeHashCash(bits, challenge) {
|
|
241
|
+
const version = 1;
|
|
242
|
+
const dateString = getHashCanDateString();
|
|
243
|
+
let result;
|
|
244
|
+
for (let counter = 0;; counter++) {
|
|
245
|
+
const hc = [version, bits, dateString, challenge, `:${counter}`].join(":");
|
|
246
|
+
const shasumData = crypto.createHash("sha1");
|
|
247
|
+
shasumData.update(hc);
|
|
248
|
+
const digest = shasumData.digest();
|
|
249
|
+
if (checkBits(+bits, digest)) {
|
|
250
|
+
result = hc;
|
|
251
|
+
break;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
return result;
|
|
255
|
+
}
|
|
256
|
+
function getHashCanDateString() {
|
|
257
|
+
const now = new Date();
|
|
258
|
+
return `${now.getFullYear()}${padTo2Digits(now.getMonth() + 1)}${padTo2Digits(now.getDate())}${padTo2Digits(now.getHours())}${padTo2Digits(now.getMinutes())}${padTo2Digits(now.getSeconds())}`;
|
|
259
|
+
}
|
|
260
|
+
function padTo2Digits(num) {
|
|
261
|
+
return num.toString().padStart(2, "0");
|
|
262
|
+
}
|
|
263
|
+
function checkBits(bits, digest) {
|
|
264
|
+
let result = true;
|
|
265
|
+
for (let i = 0; i < bits; ++i) {
|
|
266
|
+
result = checkBit(i, digest);
|
|
267
|
+
if (!result)
|
|
268
|
+
break;
|
|
269
|
+
}
|
|
270
|
+
return result;
|
|
271
|
+
}
|
|
272
|
+
function checkBit(position, buffer) {
|
|
273
|
+
const bitOffset = position & 7;
|
|
274
|
+
const byteIndex = position >> 3;
|
|
275
|
+
const bit = (buffer[byteIndex] >> bitOffset) & 1;
|
|
276
|
+
return bit === 0;
|
|
277
|
+
}
|
|
@@ -20,7 +20,7 @@ class SPMService {
|
|
|
20
20
|
this.$xcodebuildArgsService = $xcodebuildArgsService;
|
|
21
21
|
}
|
|
22
22
|
getSPMPackages(projectData) {
|
|
23
|
-
const spmPackages = this.$projectConfigService.getValue("ios.
|
|
23
|
+
const spmPackages = this.$projectConfigService.getValue("ios.SPMPackages", []);
|
|
24
24
|
return spmPackages;
|
|
25
25
|
}
|
|
26
26
|
hasSPMPackages(projectData) {
|
|
@@ -36,15 +36,38 @@ class ITMSTransporterService {
|
|
|
36
36
|
get $projectData() {
|
|
37
37
|
return this.$injector.resolve("projectData");
|
|
38
38
|
}
|
|
39
|
-
validate() {
|
|
39
|
+
validate(appSpecificPassword) {
|
|
40
40
|
return __awaiter(this, void 0, void 0, function* () {
|
|
41
41
|
const itmsTransporterPath = yield this.getITMSTransporterPath();
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
const version = yield this.$xcodeSelectService.getXcodeVersion();
|
|
43
|
+
if (+version.major < 14) {
|
|
44
|
+
if (!this.$fs.exists(itmsTransporterPath)) {
|
|
45
|
+
this.$errors.fail("iTMS Transporter not found on this machine - make sure your Xcode installation is not damaged.");
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
const altoolPath = yield this.getAltoolPath();
|
|
50
|
+
if (!this.$fs.exists(altoolPath)) {
|
|
51
|
+
this.$errors.fail("altool not found on this machine - make sure your Xcode installation is not damaged.");
|
|
52
|
+
}
|
|
53
|
+
if (!appSpecificPassword) {
|
|
54
|
+
this.$errors.fail("An app-specific password is required from xCode versions 14 and above, Use the --appleApplicationSpecificPassword to supply it.");
|
|
55
|
+
}
|
|
44
56
|
}
|
|
45
57
|
});
|
|
46
58
|
}
|
|
47
59
|
upload(data) {
|
|
60
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
61
|
+
const version = yield this.$xcodeSelectService.getXcodeVersion();
|
|
62
|
+
if (+version.major < 14) {
|
|
63
|
+
yield this.upload_iTMSTransporter(data);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
yield this.upload_altool(data);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
upload_iTMSTransporter(data) {
|
|
48
71
|
return __awaiter(this, void 0, void 0, function* () {
|
|
49
72
|
const itmsTransporterPath = yield this.getITMSTransporterPath();
|
|
50
73
|
const ipaFileName = "app.ipa";
|
|
@@ -81,6 +104,40 @@ class ITMSTransporterService {
|
|
|
81
104
|
], "close", { stdio: "inherit" });
|
|
82
105
|
});
|
|
83
106
|
}
|
|
107
|
+
upload_altool(data) {
|
|
108
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
109
|
+
const altoolPath = yield this.getAltoolPath();
|
|
110
|
+
const ipaFileName = "app.ipa";
|
|
111
|
+
const itmsDirectory = yield this.$tempService.mkdirSync("itms-");
|
|
112
|
+
const innerDirectory = path.join(itmsDirectory, "mybundle.itmsp");
|
|
113
|
+
const ipaFileLocation = path.join(innerDirectory, ipaFileName);
|
|
114
|
+
this.$fs.createDirectory(innerDirectory);
|
|
115
|
+
this.$fs.copyFile(data.ipaFilePath, ipaFileLocation);
|
|
116
|
+
const password = data.applicationSpecificPassword;
|
|
117
|
+
const args = [
|
|
118
|
+
"--upload-app",
|
|
119
|
+
"-t",
|
|
120
|
+
"ios",
|
|
121
|
+
"-f",
|
|
122
|
+
ipaFileLocation,
|
|
123
|
+
"-u",
|
|
124
|
+
data.credentials.username,
|
|
125
|
+
"-p",
|
|
126
|
+
password,
|
|
127
|
+
"-k 100000",
|
|
128
|
+
];
|
|
129
|
+
if (data.teamId) {
|
|
130
|
+
args.push("--asc-provider");
|
|
131
|
+
args.push(data.teamId);
|
|
132
|
+
}
|
|
133
|
+
if (data.verboseLogging) {
|
|
134
|
+
args.push("--verbose");
|
|
135
|
+
}
|
|
136
|
+
yield this.$childProcess.spawnFromEvent(altoolPath, args, "close", {
|
|
137
|
+
stdio: "inherit",
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
}
|
|
84
141
|
getBundleIdentifier(data) {
|
|
85
142
|
return __awaiter(this, void 0, void 0, function* () {
|
|
86
143
|
const { shouldExtractIpa, ipaFilePath } = data;
|
|
@@ -115,6 +172,13 @@ class ITMSTransporterService {
|
|
|
115
172
|
return this.$projectData.projectIdentifiers.ios;
|
|
116
173
|
});
|
|
117
174
|
}
|
|
175
|
+
getAltoolPath() {
|
|
176
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
177
|
+
const xcodePath = yield this.$xcodeSelectService.getContentsDirectoryPath();
|
|
178
|
+
let itmsTransporterPath = path.join(xcodePath, "..", "Contents", "Developer", "usr", "bin", constants_1.ITMSConstants.altoolExecutableName);
|
|
179
|
+
return itmsTransporterPath;
|
|
180
|
+
});
|
|
181
|
+
}
|
|
118
182
|
getITMSTransporterPath() {
|
|
119
183
|
return __awaiter(this, void 0, void 0, function* () {
|
|
120
184
|
const xcodePath = yield this.$xcodeSelectService.getContentsDirectoryPath();
|
|
@@ -142,6 +206,9 @@ class ITMSTransporterService {
|
|
|
142
206
|
</package>`;
|
|
143
207
|
}
|
|
144
208
|
}
|
|
209
|
+
__decorate([
|
|
210
|
+
(0, decorators_1.cache)()
|
|
211
|
+
], ITMSTransporterService.prototype, "getAltoolPath", null);
|
|
145
212
|
__decorate([
|
|
146
213
|
(0, decorators_1.cache)()
|
|
147
214
|
], ITMSTransporterService.prototype, "getITMSTransporterPath", null);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nativescript",
|
|
3
3
|
"preferGlobal": true,
|
|
4
|
-
"version": "8.5.0-dev.0",
|
|
4
|
+
"version": "8.5.0-dev.0-next-03-25-2023-4519382718",
|
|
5
5
|
"author": "NativeScript <support@nativescript.org>",
|
|
6
6
|
"description": "Command-line interface for building NativeScript projects",
|
|
7
7
|
"bin": {
|