2024-02-20 15:48:40 +01:00
|
|
|
// eslint-disable-line unicorn/filename-case
|
2024-08-06 22:30:30 +02:00
|
|
|
/* eslint-disable no-console */
|
2024-03-21 16:28:25 +01:00
|
|
|
import { execa } from 'execa';
|
2024-05-20 11:54:37 +02:00
|
|
|
import { readFile } from 'node:fs/promises';
|
2022-02-21 07:18:44 +01:00
|
|
|
|
|
|
|
// we need a wrapper script because altool tends to error out very often
|
|
|
|
// https://developer.apple.com/forums/thread/698477
|
|
|
|
// and it errors if binary already exists, we want it to just silently fail in that case
|
|
|
|
|
|
|
|
const args = process.argv.slice(2);
|
|
|
|
|
|
|
|
const filePath = args[0];
|
2023-10-19 10:09:14 +02:00
|
|
|
const apiKeyId = args[1];
|
2022-02-21 07:18:44 +01:00
|
|
|
const apiIssuer = args[2];
|
|
|
|
const appleId = args[3];
|
|
|
|
const bundleId = args[4];
|
|
|
|
|
|
|
|
// seems to be the same
|
|
|
|
const ascPublicId = apiIssuer;
|
|
|
|
|
2024-03-21 16:28:25 +01:00
|
|
|
const packageJson = JSON.parse(await readFile(new URL('../package.json', import.meta.url)) as unknown as string);
|
2022-02-21 07:18:44 +01:00
|
|
|
|
2022-02-21 08:39:34 +01:00
|
|
|
console.log('Using version', packageJson.version);
|
|
|
|
|
|
|
|
const packageVersion = packageJson.version;
|
2022-02-21 07:18:44 +01:00
|
|
|
// const packageVersion = '3.39.2';
|
|
|
|
|
|
|
|
const bundleVersion = packageVersion;
|
|
|
|
const bundleShortVersionString = packageVersion;
|
|
|
|
|
|
|
|
/*
|
|
|
|
Example JSON response:
|
|
|
|
{
|
|
|
|
"tool-version": "4.060.1220",
|
|
|
|
"tool-path": "\\/Applications\\/Xcode.app\\/Contents\\/SharedFrameworks\\/ContentDeliveryServices.framework\\/Versions\\/A\\/Frameworks\\/AppStoreService.framework",
|
|
|
|
"os-version": "11.6.0",
|
|
|
|
"product-errors": [
|
|
|
|
{
|
|
|
|
"message": "Unable to upload archive.",
|
|
|
|
"userInfo": {
|
|
|
|
"NSLocalizedDescription": "Unable to upload archive.",
|
|
|
|
"NSLocalizedFailureReason": "The file 'test' cannot be found."
|
|
|
|
},
|
|
|
|
"code": -43
|
|
|
|
},
|
|
|
|
{
|
|
|
|
"message": "Unable to upload archive.",
|
|
|
|
"userInfo": {
|
|
|
|
"NSLocalizedDescription": "Unable to upload archive.",
|
|
|
|
"NSLocalizedFailureReason": "--upload-app is missing one or more required options: --type."
|
|
|
|
},
|
|
|
|
"code": -1027
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
|
|
|
|
async function runAttempt() {
|
2023-10-19 10:09:14 +02:00
|
|
|
// const xcrunArgs = ['altool', '--list-apps', '--output-format', 'json', '--apiKey', apiKeyId, '--apiIssuer', apiIssuer];
|
2022-02-21 07:18:44 +01:00
|
|
|
|
|
|
|
const xcrunArgs = [
|
|
|
|
'altool',
|
|
|
|
'--output-format', 'json',
|
|
|
|
'--upload-package', filePath, '--type', 'macos',
|
2023-10-19 10:09:14 +02:00
|
|
|
'--apiKey', apiKeyId, '--apiIssuer', apiIssuer,
|
2022-02-21 07:18:44 +01:00
|
|
|
'--asc-public-id', ascPublicId,
|
|
|
|
'--apple-id', appleId,
|
|
|
|
'--bundle-id', bundleId,
|
|
|
|
'--bundle-version', bundleVersion,
|
|
|
|
'--bundle-short-version-string', bundleShortVersionString,
|
|
|
|
];
|
|
|
|
|
|
|
|
try {
|
|
|
|
const { stdout } = await execa('xcrun', xcrunArgs);
|
|
|
|
console.log('stdout', stdout);
|
|
|
|
return false;
|
|
|
|
} catch (err) {
|
2024-03-21 16:28:25 +01:00
|
|
|
if (err instanceof Error && 'exitCode' in err && err.exitCode === 1 && 'stdout' in err && err.stdout && typeof err.stdout === 'string') {
|
|
|
|
const errorJson = JSON.parse(err.stdout) as unknown;
|
|
|
|
if (!(errorJson != null && typeof errorJson === 'object' && 'product-errors' in errorJson && Array.isArray(errorJson['product-errors']))) {
|
|
|
|
throw new TypeError('Invalid JSON');
|
|
|
|
}
|
2022-02-21 07:18:44 +01:00
|
|
|
const productErrors = errorJson['product-errors'];
|
|
|
|
// Unable to authenticate
|
|
|
|
if (productErrors.some((error) => error.code === -19209)) {
|
|
|
|
console.log(productErrors);
|
|
|
|
return true; // retry
|
|
|
|
}
|
|
|
|
// "The bundle version, x.y.z, must be a higher than the previously uploaded version."
|
|
|
|
if (productErrors.some((error) => error.code === -19210)) {
|
|
|
|
console.log(productErrors);
|
|
|
|
// ignore
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
throw err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
const maxRetries = 3;
|
|
|
|
|
|
|
|
async function run() {
|
|
|
|
for (let i = 0; i < maxRetries; i += 1) {
|
|
|
|
// eslint-disable-next-line no-await-in-loop
|
|
|
|
const wantRetry = await runAttempt();
|
|
|
|
if (!wantRetry) return; // success
|
|
|
|
console.log('Retrying soon');
|
|
|
|
// eslint-disable-next-line no-await-in-loop
|
|
|
|
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
|
|
}
|
|
|
|
|
|
|
|
console.log('gave up');
|
|
|
|
process.exitCode = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
await run();
|