diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3c0012c6a5..cd28a39141 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -52,7 +52,7 @@ jobs: asset_content_type: application/gzip build-mac: name: Build for macOS - # Use macOS to get the platform specific dependencies like traveling-fastlane-darwin + # Use macOS to get the correct platform specific dependencies runs-on: macos-latest needs: release steps: diff --git a/packages/eas-cli/package.json b/packages/eas-cli/package.json index cb9b7bb651..9f5d957464 100644 --- a/packages/eas-cli/package.json +++ b/packages/eas-cli/package.json @@ -83,10 +83,6 @@ "typescript": "^3.9.7", "wonka": "^4.0.14" }, - "optionalDependencies": { - "@expo/traveling-fastlane-darwin": "1.15.3", - "@expo/traveling-fastlane-linux": "1.15.3" - }, "engines": { "node": ">=8.0.0" }, diff --git a/packages/eas-cli/src/credentials/ios/appstore/AppStoreApi.ts b/packages/eas-cli/src/credentials/ios/appstore/AppStoreApi.ts index 2ef8c5204f..71b5140a04 100644 --- a/packages/eas-cli/src/credentials/ios/appstore/AppStoreApi.ts +++ b/packages/eas-cli/src/credentials/ios/appstore/AppStoreApi.ts @@ -7,7 +7,6 @@ import { PushKeyStoreInfo, } from './Credentials.types'; import { AuthCtx, authenticateAsync } from './authenticate'; -import { checkWSLAsync } from './checkWSL'; import { AppleTooManyCertsError, createDistributionCertificateAsync, @@ -15,7 +14,6 @@ import { revokeDistributionCertificateAsync, } from './distributionCertificate'; import { AppLookupParams, EnsureAppExistsOptions, ensureAppExistsAsync } from './ensureAppExists'; -import { USE_APPLE_UTILS } from './experimental'; import { ProfileClass, createProvisioningProfileAsync, @@ -45,10 +43,6 @@ class AppStoreApi { public async ensureAuthenticatedAsync(): Promise { if (!this._authCtx) { - if (!USE_APPLE_UTILS) { - // Only check Fastlane compat when using Fastlane (default). - await checkWSLAsync(); - } this._authCtx = await authenticateAsync(this.options); } return this._authCtx; diff --git a/packages/eas-cli/src/credentials/ios/appstore/authenticate.ts b/packages/eas-cli/src/credentials/ios/appstore/authenticate.ts index c9db18fd2d..70c14b1587 100644 --- a/packages/eas-cli/src/credentials/ios/appstore/authenticate.ts +++ b/packages/eas-cli/src/credentials/ios/appstore/authenticate.ts @@ -12,8 +12,6 @@ import wordwrap from 'wordwrap'; import log from '../../../log'; import { promptAsync, toggleConfirmAsync } from '../../../prompts'; import UserSettings from '../../../user/UserSettings'; -import { USE_APPLE_UTILS } from './experimental'; -import { runActionAsync, travelingFastlane } from './fastlane'; import * as Keychain from './keychain'; const APPLE_IN_HOUSE_TEAM_TYPE = 'in-house'; @@ -65,7 +63,7 @@ export function getRequestContext(authCtx: AuthCtx): RequestContext { return authCtx.authState.context; } -async function authenticateWithExperimentalAsync(options: Options = {}): Promise { +export async function authenticateAsync(options: Options = {}): Promise { const { appleId, appleIdPassword } = await requestAppleCredentialsAsync(options); log(`Authenticating to Apple Developer Portal...`); // use log instead of spinner in case we need to prompt user for 2fa @@ -114,36 +112,6 @@ async function authenticateWithExperimentalAsync(options: Options = {}): Promise } } -export async function authenticateAsync(options: Options = {}): Promise { - if (USE_APPLE_UTILS) { - return await authenticateWithExperimentalAsync(options); - } - const { appleId, appleIdPassword } = await requestAppleCredentialsAsync(options); - log(`Authenticating to Apple Developer Portal...`); // use log instead of spinner in case we need to prompt user for 2fa - try { - const { teams, fastlaneSession } = await runActionAsync( - travelingFastlane.authenticate, - [appleId, appleIdPassword], - { - pipeStdout: true, - } - ); - log(chalk.green('Authenticated with Apple Developer Portal successfully!')); - const team = await chooseTeamAsync(teams, options.teamId); - return { appleId, appleIdPassword, team, fastlaneSession }; - } catch (err) { - if (err.rawDump?.match(/Invalid username and password combination/)) { - log(chalk.red('Invalid username and password combination, try again.')); - const anotherPromptResult = await promptForAppleCredentialsAsync({ - firstAttempt: false, - }); - return authenticateAsync({ ...options, ...anotherPromptResult }); - } - log(chalk.red('Authentication with Apple Developer Portal failed!')); - throw err; - } -} - export async function requestAppleCredentialsAsync(options: Options): Promise { return getAppleCredentialsFromParams(options) ?? (await promptForAppleCredentialsAsync()); } @@ -238,42 +206,6 @@ async function promptForAppleCredentialsAsync({ return { appleId: promptAppleId, appleIdPassword }; } -async function chooseTeamAsync(teams: FastlaneTeam[], userProvidedTeamId?: string): Promise { - if (teams.length === 0) { - throw new Error(`You have no team associated with your Apple account, cannot proceed. -(Do you have a paid Apple Developer account?)`); - } - - if (userProvidedTeamId) { - const foundTeam = teams.find(({ teamId }) => teamId === userProvidedTeamId); - if (foundTeam) { - log(`Using Apple Team with ID: ${userProvidedTeamId}`); - return formatTeam(foundTeam); - } else { - log.warn(`Your account is not associated with Apple Team with ID: ${userProvidedTeamId}`); - } - } - - if (teams.length === 1) { - const [team] = teams; - log(`Only 1 team associated with your account, using Apple Team with ID: ${team.teamId}`); - return formatTeam(team); - } else { - log(`You have ${teams.length} teams associated with your account`); - const choices = teams.map((team, i) => ({ - title: `${i + 1}) ${team.teamId} "${team.name}" (${team.type})`, - value: team, - })); - const { team } = await promptAsync({ - type: 'select', - name: 'team', - message: 'Which team would you like to use?', - choices, - }); - return formatTeam(team); - } -} - function formatTeam({ teamId, name, type }: FastlaneTeam): Team { return { id: teamId, diff --git a/packages/eas-cli/src/credentials/ios/appstore/checkWSL.ts b/packages/eas-cli/src/credentials/ios/appstore/checkWSL.ts deleted file mode 100644 index c15e09233f..0000000000 --- a/packages/eas-cli/src/credentials/ios/appstore/checkWSL.ts +++ /dev/null @@ -1,35 +0,0 @@ -import fs from 'fs-extra'; -import { release } from 'os'; - -import { WSL_BASH_PATH } from './fastlane'; - -const ENABLE_WSL = ` -Does not seem like WSL is enabled on this machine. Download from -the Windows app store a distribution of Linux, then in an admin powershell run: - -Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux - -and run the new Linux installation at least once -`; - -let checkCompleted = false; -export async function checkWSLAsync() { - if (checkCompleted) { - return; - } - - if (process.platform === 'win32') { - const [version] = release().match(/\d./) || [null]; - if (version !== '10') { - throw new Error('Must be on at least Windows version 10 for WSL support to work'); - } - - try { - await fs.access(WSL_BASH_PATH, fs.constants.F_OK); - } catch (e) { - throw new Error(ENABLE_WSL); - } - } - - checkCompleted = true; -} diff --git a/packages/eas-cli/src/credentials/ios/appstore/distributionCertificate.ts b/packages/eas-cli/src/credentials/ios/appstore/distributionCertificate.ts index 0f4dc9a551..cf08e9ae79 100644 --- a/packages/eas-cli/src/credentials/ios/appstore/distributionCertificate.ts +++ b/packages/eas-cli/src/credentials/ios/appstore/distributionCertificate.ts @@ -8,8 +8,6 @@ import ora from 'ora'; import { DistributionCertificate, DistributionCertificateStoreInfo } from './Credentials.types'; import { AuthCtx, getRequestContext } from './authenticate'; -import { USE_APPLE_UTILS } from './experimental'; -import { runActionAsync, travelingFastlane } from './fastlane'; export class AppleTooManyCertsError extends Error {} @@ -82,33 +80,20 @@ export async function listDistributionCertificatesAsync( ): Promise { const spinner = ora(`Getting Distribution Certificates from Apple...`).start(); try { - if (USE_APPLE_UTILS) { - const context = getRequestContext(authCtx); - const certs = ( - await Certificate.getAsync(context, { - query: { - filter: { - certificateType: [ - CertificateType.DISTRIBUTION, - CertificateType.IOS_DISTRIBUTION, - CertificateType.MAC_APP_DISTRIBUTION, - ], - }, + const context = getRequestContext(authCtx); + const certs = ( + await Certificate.getAsync(context, { + query: { + filter: { + certificateType: [ + CertificateType.DISTRIBUTION, + CertificateType.IOS_DISTRIBUTION, + CertificateType.MAC_APP_DISTRIBUTION, + ], }, - }) - ).map(transformCertificate); - spinner.succeed(); - return certs; - } - - const args = [ - 'list', - authCtx.appleId, - authCtx.appleIdPassword, - authCtx.team.id, - String(authCtx.team.inHouse), - ]; - const { certs } = await runActionAsync(travelingFastlane.manageDistCerts, args); + }, + }) + ).map(transformCertificate); spinner.succeed(); return certs; } catch (error) { @@ -124,59 +109,32 @@ export async function createDistributionCertificateAsync( authCtx: AuthCtx ): Promise { const spinner = ora(`Creating Distribution Certificate on Apple Servers...`).start(); - if (USE_APPLE_UTILS) { - try { - const context = getRequestContext(authCtx); - const results = await createCertificateAndP12Async(context, { - certificateType: CertificateType.IOS_DISTRIBUTION, - }); - spinner.succeed(); - return { - certId: results.certificate.id, - certP12: results.certificateP12, - certPassword: results.password, - certPrivateSigningKey: results.privateSigningKey, - distCertSerialNumber: results.certificate.attributes.serialNumber, - teamId: authCtx.team.id, - teamName: authCtx.team.name, - }; - } catch (error) { - spinner.fail('Failed to create Distribution Certificate on Apple Servers'); - // TODO: Move check into apple-utils - const resultString = error.message; - if ( - resultString?.match( - /You already have a current .* certificate or a pending certificate request./ - ) - ) { - throw new AppleTooManyCertsError('Maximum number of certificates generated'); - } - throw error; - } - } - try { - const args = [ - 'create', - authCtx.appleId, - authCtx.appleIdPassword, - authCtx.team.id, - String(authCtx.team.inHouse), - ]; - const result = { - ...(await runActionAsync(travelingFastlane.manageDistCerts, args)), + const context = getRequestContext(authCtx); + const results = await createCertificateAndP12Async(context, { + certificateType: CertificateType.IOS_DISTRIBUTION, + }); + spinner.succeed(); + return { + certId: results.certificate.id, + certP12: results.certificateP12, + certPassword: results.password, + certPrivateSigningKey: results.privateSigningKey, + distCertSerialNumber: results.certificate.attributes.serialNumber, teamId: authCtx.team.id, teamName: authCtx.team.name, }; - spinner.succeed(); - return result; - } catch (err) { + } catch (error) { spinner.fail('Failed to create Distribution Certificate on Apple Servers'); - const resultString = err.rawDump?.resultString; - if (resultString && resultString.match(/Maximum number of certificates generated/)) { + // TODO: Move check into apple-utils + if ( + /You already have a current .* certificate or a pending certificate request/.test( + error.message + ) + ) { throw new AppleTooManyCertsError('Maximum number of certificates generated'); } - throw err; + throw error; } } @@ -186,20 +144,8 @@ export async function revokeDistributionCertificateAsync( ): Promise { const spinner = ora(`Revoking Distribution Certificate on Apple Servers...`).start(); try { - if (USE_APPLE_UTILS) { - const context = getRequestContext(authCtx); - await Promise.all(ids.map(id => Certificate.deleteAsync(context, { id }))); - } else { - const args = [ - 'revoke', - authCtx.appleId, - authCtx.appleIdPassword, - authCtx.team.id, - String(authCtx.team.inHouse), - ids.join(','), - ]; - await runActionAsync(travelingFastlane.manageDistCerts, args); - } + const context = getRequestContext(authCtx); + await Promise.all(ids.map(id => Certificate.deleteAsync(context, { id }))); spinner.succeed(); } catch (error) { diff --git a/packages/eas-cli/src/credentials/ios/appstore/ensureAppExists.ts b/packages/eas-cli/src/credentials/ios/appstore/ensureAppExists.ts index cafa9ce133..f1d41bcade 100644 --- a/packages/eas-cli/src/credentials/ios/appstore/ensureAppExists.ts +++ b/packages/eas-cli/src/credentials/ios/appstore/ensureAppExists.ts @@ -2,8 +2,6 @@ import { BundleId, CapabilityType, CapabilityTypeOption } from '@expo/apple-util import ora from 'ora'; import { AuthCtx, getRequestContext } from './authenticate'; -import { USE_APPLE_UTILS } from './experimental'; -import { runActionAsync, travelingFastlane } from './fastlane'; export interface EnsureAppExistsOptions { enablePushNotifications?: boolean; @@ -15,7 +13,7 @@ export interface AppLookupParams { bundleIdentifier: string; } -async function ensureBundleIdExistsAsync( +export async function ensureAppExistsAsync( authCtx: AuthCtx, { accountName, projectName, bundleIdentifier }: AppLookupParams, options: EnsureAppExistsOptions = {} @@ -60,35 +58,3 @@ async function ensureBundleIdExistsAsync( throw err; } } - -export async function ensureAppExistsAsync( - authCtx: AuthCtx, - app: AppLookupParams, - options: EnsureAppExistsOptions = {} -) { - if (USE_APPLE_UTILS) { - return await ensureBundleIdExistsAsync(authCtx, app, options); - } - - const spinner = ora(`Ensuring App ID exists on Apple Developer Portal...`).start(); - try { - const { created } = await runActionAsync(travelingFastlane.ensureAppExists, [ - ...(options.enablePushNotifications ? ['--push-notifications'] : []), - authCtx.appleId, - authCtx.appleIdPassword, - authCtx.team.id, - app.bundleIdentifier, - `@${app.accountName}/${app.projectName}`, - ]); - if (created) { - spinner.succeed(`App ID created with bundle identifier ${app.bundleIdentifier}.`); - } else { - spinner.succeed('App ID found on Apple Developer Portal.'); - } - } catch (err) { - spinner.fail( - 'Something went wrong when trying to ensure App ID exists on Apple Developer Portal!' - ); - throw err; - } -} diff --git a/packages/eas-cli/src/credentials/ios/appstore/experimental.ts b/packages/eas-cli/src/credentials/ios/appstore/experimental.ts deleted file mode 100644 index 82c7d80c92..0000000000 --- a/packages/eas-cli/src/credentials/ios/appstore/experimental.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { BundleId, Profile, RequestContext } from '@expo/apple-utils'; -import { boolish } from 'getenv'; - -export const USE_APPLE_UTILS = boolish('USE_APPLE_UTILS', false); - -export async function getProfilesForBundleIdAsync( - context: RequestContext, - bundleIdentifier: string -): Promise { - const [bundleId] = await BundleId.getAsync(context, { - query: { - filter: { - identifier: bundleIdentifier, - }, - }, - }); - if (bundleId) { - return bundleId.getProfilesAsync(); - } - return []; -} - -export async function getBundleIdForIdentifierAsync( - context: RequestContext, - bundleIdentifier: string -): Promise { - const bundleId = await BundleId.findAsync(context, { identifier: bundleIdentifier }); - if (!bundleId) { - throw new Error(`Failed to find Bundle ID item with identifier "${bundleIdentifier}"`); - } - return bundleId; -} diff --git a/packages/eas-cli/src/credentials/ios/appstore/fastlane.ts b/packages/eas-cli/src/credentials/ios/appstore/fastlane.ts deleted file mode 100644 index 44776d55fe..0000000000 --- a/packages/eas-cli/src/credentials/ios/appstore/fastlane.ts +++ /dev/null @@ -1,58 +0,0 @@ -import spawnAsync from '@expo/spawn-async'; -import slash from 'slash'; - -const travelingFastlane = - process.platform === 'darwin' - ? require('@expo/traveling-fastlane-darwin')() - : require('@expo/traveling-fastlane-linux')(); - -const WSL_BASH_PATH = 'C:\\Windows\\system32\\bash.exe'; -const WSL_BASH = 'bash'; -const WSL_ONLY_PATH = 'PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'; - -type Options = { - pipeStdout?: boolean; -}; - -async function runActionAsync(fastlaneAction: string, args: string[], options: Options = {}) { - const { pipeStdout = false } = options; - const { command, commandArgs } = getCommandAndArgsForPlatform(fastlaneAction, args); - const { stderr } = await spawnAsync(command, commandArgs, { - stdio: ['inherit', pipeStdout ? 'inherit' : 'pipe', 'pipe'], - }); - const { result, ...rest } = JSON.parse(stderr.trim()); - if (result === 'success') { - return rest; - } else { - const { reason, rawDump } = rest; - const err = new Error(`Reason: ${reason}, raw: ${JSON.stringify(rawDump)}`); - // @ts-ignore - err.rawDump = rawDump; - throw err; - } -} - -function getCommandAndArgsForPlatform(fastlaneAction: string, args: string[]) { - if (process.platform === 'win32') { - const command = WSL_BASH; - const argsJoined = args.map(i => `"${i}"`).join(' '); - const commandArgs = [ - '-c', - `${WSL_ONLY_PATH} ${windowsToWSLPath(fastlaneAction)} ${argsJoined}`, - ]; - return { command, commandArgs }; - } else { - const command = fastlaneAction; - const commandArgs = [...args]; - return { command, commandArgs }; - } -} - -function windowsToWSLPath(_path: string) { - const slashPath = slash(_path); - const diskLetter = _path[0].toLowerCase(); - const pathOnDisk = slashPath.slice(2); - return `/mnt/${diskLetter}${pathOnDisk}`; -} - -export { travelingFastlane, runActionAsync, WSL_BASH_PATH }; diff --git a/packages/eas-cli/src/credentials/ios/appstore/provisioningProfile.ts b/packages/eas-cli/src/credentials/ios/appstore/provisioningProfile.ts index d5e8416d08..575856a89a 100644 --- a/packages/eas-cli/src/credentials/ios/appstore/provisioningProfile.ts +++ b/packages/eas-cli/src/credentials/ios/appstore/provisioningProfile.ts @@ -10,36 +10,12 @@ import { import { AuthCtx, getRequestContext } from './authenticate'; import { getBundleIdForIdentifierAsync, getProfilesForBundleIdAsync } from './bundleId'; import { getCertificateBySerialNumberAsync, transformCertificate } from './distributionCertificate'; -import { USE_APPLE_UTILS } from './experimental'; -import { runActionAsync, travelingFastlane } from './fastlane'; export enum ProfileClass { Adhoc = 'ad_hoc', General = 'general', } -enum TravelingFastlaneProfileType { - AppStoreAdhoc = 'app_store_adhoc', - AppStoreDist = 'app_store_dist', - InHouseAdhoc = 'in_house_adhoc', - InHouseDist = 'in_house_dist', -} - -function resolveTravelingFastlaneProfileType( - profileClass: ProfileClass, - isEnterprise?: boolean -): TravelingFastlaneProfileType { - if (isEnterprise) { - return profileClass === ProfileClass.Adhoc - ? TravelingFastlaneProfileType.InHouseAdhoc - : TravelingFastlaneProfileType.InHouseDist; - } else { - return profileClass === ProfileClass.Adhoc - ? TravelingFastlaneProfileType.AppStoreAdhoc - : TravelingFastlaneProfileType.AppStoreDist; - } -} - function resolveProfileType(profileClass: ProfileClass, isEnterprise?: boolean): ProfileType { if (isEnterprise) { return profileClass === ProfileClass.Adhoc @@ -117,41 +93,25 @@ export async function useExistingProvisioningProfileAsync( ); } - let result: ProvisioningProfile; - - if (USE_APPLE_UTILS) { - const context = getRequestContext(authCtx); - const profile = await addCertificateToProfileAsync(context, { - serialNumber: distCert.distCertSerialNumber, - profileId: provisioningProfile.provisioningProfileId, - bundleIdentifier, - }); - const content = profile.attributes.profileContent; - if (!content) { - // this should never happen because of the regen. - throw new Error( - `Provisioning profile "${profile.attributes.name}" (${profile.id}) is expired!` - ); - } - result = { - provisioningProfileId: profile.id, - provisioningProfile: content, - teamId: authCtx.team.id, - teamName: authCtx.team.name, - }; - } else { - const args = [ - 'use-existing', - authCtx.appleId, - authCtx.appleIdPassword, - authCtx.team.id, - resolveTravelingFastlaneProfileType(profileClass, authCtx.team.inHouse), - bundleIdentifier, - provisioningProfile.provisioningProfileId, - distCert.distCertSerialNumber, - ]; - result = await runActionAsync(travelingFastlane.newManageProvisioningProfiles, args); + const context = getRequestContext(authCtx); + const profile = await addCertificateToProfileAsync(context, { + serialNumber: distCert.distCertSerialNumber, + profileId: provisioningProfile.provisioningProfileId, + bundleIdentifier, + }); + const content = profile.attributes.profileContent; + if (!content) { + // this should never happen because of the regen. + throw new Error( + `Provisioning profile "${profile.attributes.name}" (${profile.id}) is expired!` + ); } + const result = { + provisioningProfileId: profile.id, + provisioningProfile: content, + teamId: authCtx.team.id, + teamName: authCtx.team.name, + }; spinner.succeed(); return { ...result, @@ -171,38 +131,18 @@ export async function listProvisioningProfilesAsync( ): Promise { const spinner = ora(`Getting Provisioning Profiles from Apple...`).start(); try { - if (USE_APPLE_UTILS) { - const context = getRequestContext(authCtx); - const profileType = resolveProfileType(profileClass, authCtx.team.inHouse); - const profiles = (await getProfilesForBundleIdAsync(context, bundleIdentifier)).filter( - profile => profile.attributes.profileType === profileType - ); + const context = getRequestContext(authCtx); + const profileType = resolveProfileType(profileClass, authCtx.team.inHouse); + const profiles = (await getProfilesForBundleIdAsync(context, bundleIdentifier)).filter( + profile => profile.attributes.profileType === profileType + ); - const result = await Promise.all( - profiles.map(profile => transformProfileAsync(profile, authCtx)) - ); - spinner.succeed(); - return result; - } else { - const args = [ - 'list', - authCtx.appleId, - authCtx.appleIdPassword, - authCtx.team.id, - resolveTravelingFastlaneProfileType(profileClass, authCtx.team.inHouse), - bundleIdentifier, - ]; - const { profiles } = await runActionAsync( - travelingFastlane.newManageProvisioningProfiles, - args - ); - spinner.succeed(); - return profiles.map((profile: Omit) => ({ - ...profile, - teamId: authCtx.team.id, - teamName: authCtx.team.name, - })); - } + const result = await Promise.all( + profiles.map(profile => transformProfileAsync(profile, authCtx)) + ); + + spinner.succeed(); + return result; } catch (error) { spinner.fail(); throw error; @@ -225,47 +165,28 @@ export async function createProvisioningProfileAsync( ); } - if (USE_APPLE_UTILS) { - const context = getRequestContext(authCtx); - const profileType = resolveProfileType(profileClass, authCtx.team.inHouse); + const context = getRequestContext(authCtx); + const profileType = resolveProfileType(profileClass, authCtx.team.inHouse); - const certificate = await getCertificateBySerialNumberAsync( - context, - distCert.distCertSerialNumber - ); + const certificate = await getCertificateBySerialNumberAsync( + context, + distCert.distCertSerialNumber + ); - const bundleIdItem = await getBundleIdForIdentifierAsync(context, bundleIdentifier); + const bundleIdItem = await getBundleIdForIdentifierAsync(context, bundleIdentifier); - const profile = await Profile.createAsync(context, { - bundleId: bundleIdItem.id, - name: profileName, - certificates: [certificate.id], - devices: [], - profileType, - }); + const profile = await Profile.createAsync(context, { + bundleId: bundleIdItem.id, + name: profileName, + certificates: [certificate.id], + devices: [], + profileType, + }); - const result = await transformProfileAsync(profile, authCtx); - spinner.succeed(); - return result; - } else { - const args = [ - 'create', - authCtx.appleId, - authCtx.appleIdPassword, - authCtx.team.id, - resolveTravelingFastlaneProfileType(profileClass, authCtx.team.inHouse), - bundleIdentifier, - distCert.distCertSerialNumber, - profileName, - ]; - const result = await runActionAsync(travelingFastlane.newManageProvisioningProfiles, args); - spinner.succeed(); - return { - ...result, - teamId: authCtx.team.id, - teamName: authCtx.team.name, - }; - } + const result = await transformProfileAsync(profile, authCtx); + + spinner.succeed(); + return result; } catch (error) { spinner.fail('Failed to create Provisioning Profile on Apple Servers'); throw error; @@ -279,28 +200,15 @@ export async function revokeProvisioningProfileAsync( ): Promise { const spinner = ora(`Revoking Provisioning Profile on Apple Servers...`).start(); try { - if (USE_APPLE_UTILS) { - const context = getRequestContext(authCtx); - - const profiles = await getProfilesForBundleIdAsync(context, bundleIdentifier); - const profileType = resolveProfileType(profileClass, authCtx.team.inHouse); - await Promise.all( - profiles - .filter(profile => profile.attributes.profileType === profileType) - .map(profile => Profile.deleteAsync(context, { id: profile.id })) - ); - } else { - const args = [ - 'revoke', - authCtx.appleId, - authCtx.appleIdPassword, - authCtx.team.id, - resolveTravelingFastlaneProfileType(profileClass, authCtx.team.inHouse), - bundleIdentifier, - ]; - await runActionAsync(travelingFastlane.newManageProvisioningProfiles, args); - } - + const context = getRequestContext(authCtx); + + const profiles = await getProfilesForBundleIdAsync(context, bundleIdentifier); + const profileType = resolveProfileType(profileClass, authCtx.team.inHouse); + await Promise.all( + profiles + .filter(profile => profile.attributes.profileType === profileType) + .map(profile => Profile.deleteAsync(context, { id: profile.id })) + ); spinner.succeed(); } catch (error) { spinner.fail('Failed to revoke Provisioning Profile on Apple Servers'); diff --git a/packages/eas-cli/src/credentials/ios/appstore/provisioningProfileAdhoc.ts b/packages/eas-cli/src/credentials/ios/appstore/provisioningProfileAdhoc.ts index 83b9aa8e3c..12ff75ae35 100644 --- a/packages/eas-cli/src/credentials/ios/appstore/provisioningProfileAdhoc.ts +++ b/packages/eas-cli/src/credentials/ios/appstore/provisioningProfileAdhoc.ts @@ -5,8 +5,6 @@ import { ProvisioningProfile } from './Credentials.types'; import { AuthCtx, getRequestContext } from './authenticate'; import { getBundleIdForIdentifierAsync, getProfilesForBundleIdAsync } from './bundleId'; import { getDistributionCertificateAync } from './distributionCertificate'; -import { USE_APPLE_UTILS } from './experimental'; -import { runActionAsync, travelingFastlane } from './fastlane'; interface ProfileResults { didUpdate?: boolean; @@ -228,40 +226,18 @@ export async function createOrReuseAdhocProvisioningProfileAsync( ): Promise { const spinner = ora(`Handling Adhoc provisioning profiles on Apple Developer Portal...`).start(); try { - let adhocProvisioningProfile: ProfileResults; + const context = getRequestContext(authCtx); + const { + didUpdate, + didCreate, + profileName, + ...adhocProvisioningProfile + } = await manageAdHocProfilesAsync(context, { + udids, + bundleId: bundleIdentifier, + certSerialNumber: distCertSerialNumber, + }); - if (USE_APPLE_UTILS) { - const context = getRequestContext(authCtx); - adhocProvisioningProfile = await manageAdHocProfilesAsync(context, { - udids, - bundleId: bundleIdentifier, - certSerialNumber: distCertSerialNumber, - }); - } else { - const args = [ - '--apple-id', - authCtx.appleId, - '--apple-password', - authCtx.appleIdPassword, - authCtx.team.id, - udids.join(','), - bundleIdentifier, - distCertSerialNumber, - ]; - const travelingFastlaneProfile = await runActionAsync( - travelingFastlane.manageAdHocProvisioningProfile, - args - ); - adhocProvisioningProfile = { - provisioningProfile: travelingFastlaneProfile.provisioningProfile, - provisioningProfileId: travelingFastlaneProfile.provisioningProfileId, - profileName: travelingFastlaneProfile.provisioningProfileName, - didUpdate: !!travelingFastlaneProfile.provisioningProfileUpdateTimestamp, - didCreate: !!travelingFastlaneProfile.provisioningProfileCreateTimestamp, - }; - } - - const { didUpdate, didCreate, profileName } = adhocProvisioningProfile; if (didCreate) { spinner.succeed(`Created new profile: ${profileName}`); } else if (didUpdate) { @@ -270,10 +246,6 @@ export async function createOrReuseAdhocProvisioningProfileAsync( spinner.succeed(`Used existing profile: ${profileName}`); } - delete adhocProvisioningProfile.didUpdate; - delete adhocProvisioningProfile.didCreate; - delete adhocProvisioningProfile.profileName; - return { ...adhocProvisioningProfile, teamId: authCtx.team.id, diff --git a/packages/eas-cli/src/credentials/ios/appstore/pushKey.ts b/packages/eas-cli/src/credentials/ios/appstore/pushKey.ts index 7277d0c2d5..6a1ff2244c 100644 --- a/packages/eas-cli/src/credentials/ios/appstore/pushKey.ts +++ b/packages/eas-cli/src/credentials/ios/appstore/pushKey.ts @@ -6,8 +6,6 @@ import ora from 'ora'; import log from '../../../log'; import { PushKey, PushKeyStoreInfo } from './Credentials.types'; import { AuthCtx, getRequestContext } from './authenticate'; -import { USE_APPLE_UTILS } from './experimental'; -import { runActionAsync, travelingFastlane } from './fastlane'; const { MaxKeysCreatedError } = Keys; @@ -20,17 +18,10 @@ Please remember that Apple Keys are not application specific! export async function listPushKeysAsync(authCtx: AuthCtx): Promise { const spinner = ora(`Getting Push Keys from Apple...`).start(); try { - if (USE_APPLE_UTILS) { - const context = getRequestContext(authCtx); - const keys = await Keys.getKeysAsync(context); - spinner.succeed(); - return keys; - } else { - const args = ['list', authCtx.appleId, authCtx.appleIdPassword, authCtx.team.id]; - const { keys } = await runActionAsync(travelingFastlane.managePushKeys, args); - spinner.succeed(); - return keys; - } + const context = getRequestContext(authCtx); + const keys = await Keys.getKeysAsync(context); + spinner.succeed(); + return keys; } catch (error) { spinner.fail(); throw error; @@ -43,28 +34,16 @@ export async function createPushKeyAsync( ): Promise { const spinner = ora(`Creating Push Key on Apple Servers...`).start(); try { - if (USE_APPLE_UTILS) { - const context = getRequestContext(authCtx); - const key = await Keys.createKeyAsync(context, { name, isApns: true }); - const apnsKeyP8 = await Keys.downloadKeyAsync(context, { id: key.id }); - spinner.succeed(); - return { - apnsKeyId: key.id, - apnsKeyP8, - teamId: authCtx.team.id, - teamName: authCtx.team.name, - }; - } else { - const args = ['create', authCtx.appleId, authCtx.appleIdPassword, authCtx.team.id, name]; - const { apnsKeyId, apnsKeyP8 } = await runActionAsync(travelingFastlane.managePushKeys, args); - spinner.succeed(); - return { - apnsKeyId, - apnsKeyP8, - teamId: authCtx.team.id, - teamName: authCtx.team.name, - }; - } + const context = getRequestContext(authCtx); + const key = await Keys.createKeyAsync(context, { name, isApns: true }); + const apnsKeyP8 = await Keys.downloadKeyAsync(context, { id: key.id }); + spinner.succeed(); + return { + apnsKeyId: key.id, + apnsKeyP8, + teamId: authCtx.team.id, + teamName: authCtx.team.name, + }; } catch (err) { spinner.fail('Failed to create Push Notifications Key'); const resultString = err.rawDump?.resultString; @@ -81,19 +60,9 @@ export async function createPushKeyAsync( export async function revokePushKeyAsync(authCtx: AuthCtx, ids: string[]): Promise { const spinner = ora(`Revoking Push Key on Apple Servers...`).start(); try { - if (USE_APPLE_UTILS) { - const context = getRequestContext(authCtx); - await Promise.all(ids.map(id => Keys.revokeKeyAsync(context, { id }))); - } else { - const args = [ - 'revoke', - authCtx.appleId, - authCtx.appleIdPassword, - authCtx.team.id, - ids.join(','), - ]; - await runActionAsync(travelingFastlane.managePushKeys, args); - } + const context = getRequestContext(authCtx); + await Promise.all(ids.map(id => Keys.revokeKeyAsync(context, { id }))); + spinner.succeed(); } catch (error) { log.error(error); diff --git a/packages/eas-cli/src/submissions/ios/AppProduce.ts b/packages/eas-cli/src/submissions/ios/AppProduce.ts index 06484d156b..3bb0b59d12 100644 --- a/packages/eas-cli/src/submissions/ios/AppProduce.ts +++ b/packages/eas-cli/src/submissions/ios/AppProduce.ts @@ -1,17 +1,14 @@ import { App, RequestContext, Session, User } from '@expo/apple-utils/build'; import { getConfig } from '@expo/config'; -import wordwrap from 'wordwrap'; import { getBundleIdentifier } from '../../build/ios/bundleIdentifer'; import { authenticateAsync, getRequestContext } from '../../credentials/ios/appstore/authenticate'; -import { USE_APPLE_UTILS } from '../../credentials/ios/appstore/experimental'; import log from '../../log'; import { promptAsync } from '../../prompts'; import { IosSubmissionContext } from '../types'; -import { runFastlaneAsync, travelingFastlane } from '../utils/fastlane'; import { sanitizeLanguage } from './utils/language'; -interface ProduceOptions { +interface CreateAppOptions { appleId?: string; appName: string; bundleIdentifier: string; @@ -22,15 +19,6 @@ interface ProduceOptions { sku?: string; } -interface ProduceCredentials { - appleId?: string; - appleIdPassword?: string; - appleTeamId?: string; - itcTeamId?: string; - companyName?: string; - sku?: string; -} - type AppStoreResult = { appleId: string; ascAppId: string; @@ -64,11 +52,7 @@ Learn more here: https://expo.fyi/bundle-identifier` language: sanitizeLanguage(language), }; - if (USE_APPLE_UTILS) { - return await runProduceExperimentalAsync(options); - } - - return await runProduceAsync(options); + return await createAppStoreConnectAppAsync(options); } async function isProvisioningAvailableAsync(requestCtx: RequestContext): Promise { @@ -79,7 +63,7 @@ async function isProvisioningAvailableAsync(requestCtx: RequestContext): Promise return user.attributes.provisioningAllowed; } -async function runProduceExperimentalAsync(options: ProduceOptions): Promise { +async function createAppStoreConnectAppAsync(options: CreateAppOptions): Promise { const { appleId, appleTeamId, @@ -144,69 +128,6 @@ async function runProduceExperimentalAsync(options: ProduceOptions): Promise { - const { bundleIdentifier, appName, language, companyName, sku } = options; - - const { appleId, appleIdPassword, team } = await authenticateAsync({ - appleId: options.appleId, - teamId: options.appleTeamId, - }); - - const appleCreds: ProduceCredentials = { - appleId, - appleIdPassword, - appleTeamId: team.id, - }; - const itcTeamId = options.itcTeamId ?? (await resolveItcTeamId(appleCreds)); - const updatedAppleCreds = { - ...appleCreds, - itcTeamId, - companyName, - sku, - }; - - log('Ensuring the app exists on App Store Connect, this may take a while...'); - try { - const { appleId: ascAppId } = await runFastlaneAsync( - travelingFastlane.appProduce, - [bundleIdentifier, appName, appleId, language], - updatedAppleCreds, - true - ); - - return { appleId, ascAppId }; - } catch (err) { - const wrap = wordwrap(process.stdout.columns || 80); - if (err.message.match(/You must provide a company name to use on the App Store/)) { - log.error( - wrap( - 'You haven\'t uploaded any app to App Store yet. Please provide your company name with --company-name "COMPANY NAME"' - ) - ); - } else if (err.message.match(/The Bundle ID you entered has already been used./)) { - log.warn( - wrap( - 'The Bundle ID you entered has already been used. If you already have app on App Store Connect, ' + - 'please skip this step and provide App Apple ID directly' - ) - ); - } else if (err.message.match(/The app name you entered is already being used./)) { - log.error('The app name you entered is already being used.'); - } - throw err; - } -} - -async function resolveItcTeamId(appleCreds: ProduceCredentials): Promise { - log('Resolving your App Store Connect team...'); - const { itc_team_id: itcTeamId } = await runFastlaneAsync( - travelingFastlane.resolveItcTeamId, - [], - appleCreds - ); - return itcTeamId; -} - async function promptForAppNameAsync(): Promise { const { appName } = await promptAsync({ type: 'text', diff --git a/packages/eas-cli/src/submissions/ios/utils/__tests__/language-test.ts b/packages/eas-cli/src/submissions/ios/utils/__tests__/language-test.ts index 83a1aaf1c3..50adf6d618 100644 --- a/packages/eas-cli/src/submissions/ios/utils/__tests__/language-test.ts +++ b/packages/eas-cli/src/submissions/ios/utils/__tests__/language-test.ts @@ -1,11 +1,5 @@ import { sanitizeLanguage } from '../language'; -jest.mock('../../../../credentials/ios/appstore/experimental', () => ({ - get USE_APPLE_UTILS() { - return true; - }, -})); - describe(sanitizeLanguage, () => { it('throws when language not found', () => { expect(() => sanitizeLanguage('Ponglish')).toThrowError(); @@ -19,7 +13,7 @@ describe(sanitizeLanguage, () => { expect(sanitizeLanguage(undefined, { defaultLang: 'en-US' })).toBe('en-US'); }); - it('returns language iTunes code when USE_APPLE_UTILS is true', () => { + it('returns language iTunes code', () => { expect(sanitizeLanguage('English')).toBe('en-US'); expect(sanitizeLanguage('pl-PL')).toBe('pl'); }); diff --git a/packages/eas-cli/src/submissions/ios/utils/language.ts b/packages/eas-cli/src/submissions/ios/utils/language.ts index a7002563ca..ba020e693b 100644 --- a/packages/eas-cli/src/submissions/ios/utils/language.ts +++ b/packages/eas-cli/src/submissions/ios/utils/language.ts @@ -1,4 +1,3 @@ -import { USE_APPLE_UTILS } from '../../../credentials/ios/appstore/experimental'; import log from '../../../log'; type Language = { @@ -23,7 +22,7 @@ export function sanitizeLanguage( if (!found) { throw new Error('Invalid default language provided: ' + defaultLang); } - return USE_APPLE_UTILS ? found.itcLocale ?? found.locale : found.name; + return found.itcLocale ?? found.locale; } const foundLang = findLanguage(lang); @@ -34,10 +33,6 @@ export function sanitizeLanguage( ); } - if (!USE_APPLE_UTILS) { - return foundLang.name; - } - return foundLang.itcLocale ?? foundLang.locale; } @@ -49,10 +44,6 @@ export function sanitizeLanguage( * - English */ function languageListToString(): string { - if (!USE_APPLE_UTILS) { - return LANGUAGES.map(lang => `- ${lang.name}`).join('\n'); - } - return LANGUAGES.map(lang => { const code = lang.itcLocale || lang.locale; const name = lang.displayName || lang.name; diff --git a/packages/eas-cli/src/submissions/utils/fastlane.ts b/packages/eas-cli/src/submissions/utils/fastlane.ts deleted file mode 100644 index 0e335c8775..0000000000 --- a/packages/eas-cli/src/submissions/utils/fastlane.ts +++ /dev/null @@ -1,72 +0,0 @@ -import spawnAsync from '@expo/spawn-async'; - -export const travelingFastlane = - process.platform === 'darwin' - ? require('@expo/traveling-fastlane-darwin')() - : require('@expo/traveling-fastlane-linux')(); - -export async function runFastlaneAsync( - program: string, - args: any, - { - appleId, - appleIdPassword, - appleTeamId, - itcTeamId, - companyName, - sku, - }: { - appleId?: string; - appleIdPassword?: string; - appleTeamId?: string; - itcTeamId?: string; - companyName?: string; - sku?: string; - }, - pipeToLogger = false -): Promise<{ [key: string]: any }> { - const fastlaneData = - appleId && appleIdPassword - ? { - FASTLANE_USER: appleId, - FASTLANE_PASSWORD: appleIdPassword, - FASTLANE_DONT_STORE_PASSWORD: '1', - FASTLANE_TEAM_ID: appleTeamId, - ...(itcTeamId && { FASTLANE_ITC_TEAM_ID: itcTeamId }), - ...(companyName && { PRODUCE_COMPANY_NAME: companyName }), - ...(sku && { PRODUCE_SKU: sku }), - } - : {}; - - const env = { - ...process.env, - ...fastlaneData, - }; - - const { stderr } = await spawnAsync(program, args, { - env, - stdio: ['inherit', pipeToLogger ? 'inherit' : 'pipe', 'pipe'], - }); - - /* TODO: Fix handling this message, it causes JSON parse error: - "No entry for terminal type "xterm-256color"; - using dumb terminal settings." - - It works when pipeToLogger = true - */ - const res = JSON.parse(stderr.trim()); - if (res.result !== 'failure') { - return res; - } else { - let message = - res.reason !== 'Unknown reason' - ? res.reason - : res.rawDump?.message ?? 'Unknown error when running fastlane'; - message = `${message}${ - res?.rawDump?.backtrace - ? `\n${res.rawDump.backtrace.map((i: string) => ` ${i}`).join('\n')}` - : '' - }`; - throw new Error(message); - } -} diff --git a/yarn.lock b/yarn.lock index 6023b39c55..375d0dc8cb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1571,16 +1571,6 @@ dependencies: cross-spawn "^6.0.5" -"@expo/traveling-fastlane-darwin@1.15.3": - version "1.15.3" - resolved "https://registry.yarnpkg.com/@expo/traveling-fastlane-darwin/-/traveling-fastlane-darwin-1.15.3.tgz#9019b1691ab356c6a97384dd63794156c83751f4" - integrity sha512-B8x45xV2HpyG6mVN1pUL7s1y2LGWYuK4ls3SU9DHsBJIvQpTbLOTv5jmr5fcNN8v9iLNvxQef3AMUdFEGE9hpg== - -"@expo/traveling-fastlane-linux@1.15.3": - version "1.15.3" - resolved "https://registry.yarnpkg.com/@expo/traveling-fastlane-linux/-/traveling-fastlane-linux-1.15.3.tgz#13a3031b1732234574da0246074d5fae41faf382" - integrity sha512-vDw4TT0i3W2AjFUsqyFddcPNyHH1kDPZzcnGbBjSc82Oo33r/YE/KT4yjg248oBokfmzAEIhlPQTL7lfCn51pg== - "@graphql-codegen/cli@^1.19.2": version "1.19.2" resolved "https://registry.yarnpkg.com/@graphql-codegen/cli/-/cli-1.19.2.tgz#c13f44e144b3d6ff880f3b418b3b3af58917e889"