"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