upload static method

Future<void> upload({
  1. required String ipaPath,
  2. required AppleCredentials credentials,
  3. ProcessRunner processRunner = const SystemProcessRunner(),
  4. String? homeDirectory,
})

Uploads an IPA with App Store Connect API credentials.

Implementation

static Future<void> upload({
  required String ipaPath,
  required AppleCredentials credentials,
  ProcessRunner processRunner = const SystemProcessRunner(),
  String? homeDirectory,
}) async {
  SmfError.check(
    RegExp(r'^[A-Za-z0-9]+$').hasMatch(credentials.keyId),
    'The App Store Connect key ID must contain only letters and digits.',
    SmfErrorCode.invalidCredential,
  );
  final home = p.normalize(
    p.absolute(
      homeDirectory ?? Platform.environment['HOME'] ?? Directory.current.path,
    ),
  );
  final privateKeysDirectory = p.join(
    home,
    '.appstoreconnect',
    'private_keys',
  );
  final keyPath = p.join(
    privateKeysDirectory,
    'AuthKey_${credentials.keyId}.p8',
  );
  await _rejectSymbolicLinks(
    home,
    keyPath,
    description: 'The App Store Connect private-key path',
    code: SmfErrorCode.privateKeyCollision,
  );
  final didPrivateKeysDirectoryExist = await Directory(
    privateKeysDirectory,
  ).exists();
  final didKeyExist = await File(keyPath).exists();
  if (didKeyExist) {
    final existing = (await File(keyPath).readAsString()).trim();
    SmfError.check(
      existing == credentials.privateKey.trim(),
      '$keyPath already exists with different contents.',
      SmfErrorCode.privateKeyCollision,
    );
  } else {
    await Directory(privateKeysDirectory).create(recursive: true);
    await _rejectSymbolicLinks(
      home,
      keyPath,
      description: 'The App Store Connect private-key path',
      code: SmfErrorCode.privateKeyCollision,
    );
    if (!didPrivateKeysDirectoryExist) {
      await processRunner.run('/bin/chmod', <String>[
        '700',
        privateKeysDirectory,
      ]);
    }
    await File(keyPath).writeAsString(credentials.privateKey);
  }
  await processRunner.run('/bin/chmod', <String>['600', keyPath]);

  try {
    await processRunner.run('xcrun', <String>[
      'altool',
      '--upload-app',
      '--type',
      'ios',
      '-f',
      ipaPath,
      '--apiKey',
      credentials.keyId,
      '--apiIssuer',
      credentials.issuerId,
    ]);
  } finally {
    if (!didKeyExist) {
      try {
        await File(keyPath).delete();
      } on FileSystemException {
        // Best-effort cleanup must not replace the upload result.
      }
    }
  }
}