"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.buildAndCopyArtifactAsync = buildAndCopyArtifactAsync; exports.configureAndCopyArchiveAsync = configureAndCopyArchiveAsync; exports.createTurtleWorkspaceAsync = createTurtleWorkspaceAsync; exports.EXPONENT_APP = exports.EXPOKIT_APP = void 0; function _fsExtra() { const data = _interopRequireDefault(require("fs-extra")); _fsExtra = function () { return data; }; return data; } function _path() { const data = _interopRequireDefault(require("path")); _path = function () { return data; }; return data; } function _rimraf() { const data = _interopRequireDefault(require("rimraf")); _rimraf = function () { return data; }; return data; } function _get() { const data = _interopRequireDefault(require("lodash/get")); _get = function () { return data; }; return data; } function _has() { const data = _interopRequireDefault(require("lodash/has")); _has = function () { return data; }; return data; } function _pascalCase() { const data = _interopRequireDefault(require("pascal-case")); _pascalCase = function () { return data; }; return data; } function _ExponentTools() { const data = require("./ExponentTools"); _ExponentTools = function () { return data; }; return data; } function IosNSBundle() { const data = _interopRequireWildcard(require("./IosNSBundle")); IosNSBundle = function () { return data; }; return data; } function IosWorkspace() { const data = _interopRequireWildcard(require("./IosWorkspace")); IosWorkspace = function () { return data; }; return data; } function _StandaloneBuildFlags() { const data = _interopRequireDefault(require("./StandaloneBuildFlags")); _StandaloneBuildFlags = function () { return data; }; return data; } function _StandaloneContext() { const data = _interopRequireDefault(require("./StandaloneContext")); _StandaloneContext = function () { return data; }; return data; } function _Logger() { const data = _interopRequireDefault(require("./Logger")); _Logger = function () { return data; }; return data; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } const EXPOKIT_APP = 'ExpoKitApp'; exports.EXPOKIT_APP = EXPOKIT_APP; const EXPONENT_APP = 'Exponent'; exports.EXPONENT_APP = EXPONENT_APP; function _validateCLIArgs(args) { args.type = args.type || 'archive'; args.configuration = args.configuration || 'Release'; args.verbose = args.verbose || false; args.testEnvironment = args.testEnvironment || 'none'; switch (args.type) { case 'simulator': { if (args.configuration !== 'Debug' && args.configuration !== 'Release') { throw new Error(`Unsupported build configuration ${args.configuration}`); } break; } case 'archive': { if (args.configuration !== 'Release') { throw new Error('Release is the only supported configuration when archiving'); } break; } case 'client': break; default: { throw new Error(`Unsupported build type ${args.type}`); } } switch (args.action) { case 'configure': { if (args.type === 'client') { break; } if (!args.url) { throw new Error('Must run with `--url MANIFEST_URL`'); } if (!args.sdkVersion) { throw new Error('Must run with `--sdkVersion SDK_VERSION`'); } if (!args.archivePath) { throw new Error('Need to provide --archivePath <path to existing archive for configuration>'); } if (args.testEnvironment !== 'local' && args.testEnvironment !== 'ci' && args.testEnvironment !== 'none') { throw new Error(`Unsupported test environment ${args.testEnvironment}`); } break; } case 'build': { break; } case 'create-workspace': { break; } default: { throw new Error(`Unsupported build action ${args.action}`); } } return args; } /** * Build the iOS workspace at the given path. * @return the path to the resulting build artifact */ async function _buildAsync(projectName, workspacePath, configuration, type, relativeBuildDestination, verbose, useModernBuildSystem = false) { const modernBuildSystemFragment = `-UseModernBuildSystem=${useModernBuildSystem ? 'YES' : 'NO'}`; const buildDest = `${relativeBuildDestination}-${type}`; let buildCmd = `set -o pipefail && xcodebuild -workspace ${projectName}.xcworkspace -scheme ${projectName} -configuration ${configuration} -derivedDataPath ${buildDest} ${modernBuildSystemFragment}`, pathToArtifact; if (type === 'simulator') { buildCmd += ` -sdk iphonesimulator CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO ARCHS="i386 x86_64" ONLY_ACTIVE_ARCH=NO | xcpretty`; pathToArtifact = _path().default.join(buildDest, 'Build', 'Products', `${configuration}-iphonesimulator`, `${projectName}.app`); } else if (type === 'archive') { buildCmd += ` -sdk iphoneos -destination generic/platform=iOS archive -archivePath ${buildDest}/${projectName}.xcarchive CODE_SIGNING_ALLOWED=NO | xcpretty`; pathToArtifact = _path().default.join(buildDest, `${projectName}.xcarchive`); } else { throw new Error(`Unsupported build type: ${type}`); } _Logger().default.info(`Building iOS workspace at ${workspacePath} to ${buildDest}:\n`); _Logger().default.info(buildCmd); if (!verbose) { _Logger().default.info('\nxcodebuild is running. Logging errors only. To see full output, use --verbose 1...'); } await (0, _ExponentTools().spawnAsyncThrowError)(buildCmd, null, { // only stderr stdio: verbose ? 'inherit' : ['ignore', 'ignore', 'inherit'], cwd: workspacePath, shell: true }); return _path().default.resolve(workspacePath, pathToArtifact); } async function _podInstallAsync(workspacePath, isRepoUpdateEnabled) { // ensure pods are clean const pathsToClean = [_path().default.join(workspacePath, 'Pods'), _path().default.join(workspacePath, 'Podfile.lock')]; pathsToClean.forEach(path => { if (_fsExtra().default.existsSync(path)) { _rimraf().default.sync(path); } }); // Disable cocoapod stats to speed up the install const COCOAPODS_DISABLE_STATS = process.env.COCOAPODS_DISABLE_STATS; process.env.COCOAPODS_DISABLE_STATS = true; // install let cocoapodsArgs = ['install']; if (isRepoUpdateEnabled) { cocoapodsArgs.push('--repo-update'); } _Logger().default.info('Installing iOS workspace dependencies...'); _Logger().default.info(`pod ${cocoapodsArgs.join(' ')}`); try { await (0, _ExponentTools().spawnAsyncThrowError)('pod', cocoapodsArgs, { stdio: 'inherit', cwd: workspacePath }); } finally { // Revert the stats to the user preference process.env.COCOAPODS_DISABLE_STATS = COCOAPODS_DISABLE_STATS; } } /** * @param workspacePath optionally provide a path for the unbuilt xcode workspace to create/use. * @param expoSourcePath path to expo client app sourcecode (/ios dir from expo/expo repo) * @param shellAppSdkVersion sdk version for shell app */ async function _createStandaloneContextAsync(args) { // right now we only ever build a single detached workspace for service contexts. // TODO: support multiple different pod configurations, assemble a cache of those builds. const expoSourcePath = args.expoSourcePath || '../ios'; let workspaceSourcePath; if (args.workspacePath) { workspaceSourcePath = args.workspacePath; } else { workspaceSourcePath = _path().default.join(expoSourcePath, '..', 'shellAppWorkspaces', 'default', 'ios'); } let { privateConfigFile, privateConfigData } = args; let privateConfig; if (privateConfigData) { privateConfig = privateConfigData; } else if (privateConfigFile) { let privateConfigContents = await _fsExtra().default.readFile(privateConfigFile, 'utf8'); privateConfig = JSON.parse(privateConfigContents); } let manifest; if (args.manifest) { manifest = args.manifest; _Logger().default.withFields({ buildPhase: 'reading manifest' }).info('Using manifest:', JSON.stringify(manifest)); } else if (args.url && args.sdkVersion) { const { url, sdkVersion, releaseChannel } = args; manifest = await (0, _ExponentTools().getManifestAsync)(url, { 'Exponent-SDK-Version': sdkVersion, 'Exponent-Platform': 'ios', 'Expo-Release-Channel': releaseChannel ? releaseChannel : 'default', Accept: 'application/expo+json,application/json' }); } let bundleExecutable = args.type === 'client' ? EXPONENT_APP : EXPOKIT_APP; if ((0, _has().default)(manifest, 'ios.infoPlist.CFBundleExecutable')) { bundleExecutable = (0, _get().default)(manifest, 'ios.infoPlist.CFBundleExecutable'); } else if (privateConfig && privateConfig.bundleIdentifier) { bundleExecutable = (0, _pascalCase().default)(privateConfig.bundleIdentifier); } const buildFlags = _StandaloneBuildFlags().default.createIos(args.configuration, { workspaceSourcePath, appleTeamId: args.appleTeamId, buildType: args.type, bundleExecutable }); const context = _StandaloneContext().default.createServiceContext(expoSourcePath, args.archivePath, manifest, privateConfig, args.testEnvironment, buildFlags, args.url, args.releaseChannel, args.shellAppSdkVersion); return context; } /** * possible args: * @param url manifest url for shell experience * @param sdkVersion sdk to use when requesting the manifest * @param releaseChannel channel to pull manifests from, default is 'default' * @param archivePath path to existing NSBundle to configure * @param privateConfigFile path to a private config file containing, e.g., private api keys * @param appleTeamId Apple Developer's account Team ID * @param output specify the output path of the configured archive (ie) /tmp/my-app-archive-build.xcarchive or /tmp/my-app-ios-build.tar.gz * @param type type of artifact to configure (simulator or archive) * @param expoSourcePath path to expo client app sourcecode (/ios dir from expo/expo repo) */ async function configureAndCopyArchiveAsync(args) { args = _validateCLIArgs(args); const { output, type } = args; const context = await _createStandaloneContextAsync(args); await IosNSBundle().configureAsync(context); if (output) { const workspaceName = type === 'client' ? EXPONENT_APP : EXPOKIT_APP; if (context.build.ios.bundleExecutable !== workspaceName) { await (0, _ExponentTools().spawnAsync)('/bin/mv', [workspaceName, context.build.ios.bundleExecutable], { pipeToLogger: true, cwd: context.data.archivePath, loggerFields: { buildPhase: 'renaming bundle executable' } }); } if (type === 'simulator') { const archiveName = context.config.slug.replace(/[^0-9a-z_-]/gi, '_'); const appReleasePath = _path().default.resolve(context.data.archivePath, '..'); await (0, _ExponentTools().spawnAsync)(`mv ${workspaceName}.app ${archiveName}.app && tar -czvf ${output} ${archiveName}.app`, null, { stdoutOnly: true, pipeToLogger: true, loggerFields: { buildPhase: 'creating an archive for simulator' }, cwd: appReleasePath, shell: true }); } else if (type === 'archive' || type === 'client') { await (0, _ExponentTools().spawnAsync)('/bin/mv', [`${workspaceName}.xcarchive`, output], { pipeToLogger: true, cwd: _path().default.join(context.data.archivePath, '../../../..'), loggerFields: { buildPhase: 'renaming archive' } }); } } return context.data.manifest; } /** * possible args: * @param skipRepoUpdate if true, omit `--repo-update` cocoapods flag. */ async function _createTurtleWorkspaceAsync(context, args) { const { skipRepoUpdate } = args; if (_fsExtra().default.existsSync(context.build.ios.workspaceSourcePath)) { _Logger().default.info(`Removing existing workspace at ${context.build.ios.workspaceSourcePath}...`); try { _rimraf().default.sync(context.build.ios.workspaceSourcePath); } catch (_) {} } await IosWorkspace().createDetachedAsync(context); await _podInstallAsync(context.build.ios.workspaceSourcePath, !skipRepoUpdate); } /** * External-facing version can be used to create a turtle workspace without building it. * Probably only useful for local testing. * * @param workspacePath (optional) provide some other path to create the workspace besides the default * @param url (optional, with sdkVersion) url to an expo manifest, if you want the workspace to be configured automatically * @param sdkVersion (optional, with url) sdkVersion to an expo manifest, if you want the workspace to be configured automatically */ async function createTurtleWorkspaceAsync(args) { args = _validateCLIArgs(args); if (!args.workspacePath) { _Logger().default.info('No workspace path was provided with --workspacePath, so the default will be used.'); } const context = await _createStandaloneContextAsync(args); await _createTurtleWorkspaceAsync(context, args); _Logger().default.info(`Created turtle workspace at ${context.build.ios.workspaceSourcePath}. You can open and run this in Xcode.`); if (context.config) { await IosNSBundle().configureAsync(context); _Logger().default.info(`The turtle workspace was configured for the url ${args.url}. To run this app with a Debug scheme, make sure to add a development url to 'EXBuildConstants.plist'.`); } else { _Logger().default.info(`You can specify --url <manifestUrl> --sdkVersion <version> to configure this workspace as a particular Expo app.\n\nBecause those arguments were omitted, the workspace has not been configured. It will compile but not run. The minimum configuration to get something running is to specify a manifest url in 'EXShell.plist' (for Release builds) or 'EXBuildConstants.plist' (for Debug builds).`); } } /** * possible args: * @param configuration StandaloneBuildConfiguration (Debug or Release) * @param verbose show all xcodebuild output (default false) * @param reuseWorkspace if true, when building, assume a detached workspace already exists rather than creating a new one. * @param type type of artifact to build (simulator or archive) * @param shellAppSdkVersion sdk version for shell app */ async function buildAndCopyArtifactAsync(args) { args = _validateCLIArgs(args); const context = await _createStandaloneContextAsync(args); const { verbose, type, reuseWorkspace } = args; const { projectName } = IosWorkspace().getPaths(context); if (!reuseWorkspace) { await _createTurtleWorkspaceAsync(context, args); } const pathToArtifact = await _buildAsync(projectName, context.build.ios.workspaceSourcePath, context.build.configuration, type, _path().default.relative(context.build.ios.workspaceSourcePath, '../shellAppBase'), verbose, (0, _ExponentTools().parseSdkMajorVersion)(args.shellAppSdkVersion) > 33); const artifactDestPath = _path().default.join('../shellAppBase-builds', type, context.build.configuration); _Logger().default.info(`\nFinished building, copying artifact to ${_path().default.resolve(artifactDestPath)}...`); if (_fsExtra().default.existsSync(artifactDestPath)) { await (0, _ExponentTools().spawnAsyncThrowError)('/bin/rm', ['-rf', artifactDestPath]); } _Logger().default.info(`mkdir -p ${artifactDestPath}`); await (0, _ExponentTools().spawnAsyncThrowError)('/bin/mkdir', ['-p', artifactDestPath]); _Logger().default.info(`cp -R ${pathToArtifact} ${artifactDestPath}`); await (0, _ExponentTools().spawnAsyncThrowError)('/bin/cp', ['-R', pathToArtifact, artifactDestPath]); } //# sourceMappingURL=../__sourcemaps__/detach/IosShellApp.js.map