๐Ÿ“ฆ ionic-team / capacitor

๐Ÿ“„ build.ts ยท 117 lines
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117import { writeFileSync, unlinkSync } from 'fs-extra';
import { basename, join } from 'path';
import { rimraf } from 'rimraf';

import { runTask } from '../common';
import { XcodeExportMethod, type Config } from '../definitions';
import { logSuccess } from '../log';
import { type BuildCommandOptions } from '../tasks/build';
import { checkPackageManager } from '../util/spm';
import { runCommand } from '../util/subprocess';

export async function buildiOS(config: Config, buildOptions: BuildCommandOptions): Promise<void> {
  const theScheme = buildOptions.scheme ?? 'App';

  const packageManager = await checkPackageManager(config);

  let typeOfBuild: string;
  let projectName: string;

  if (packageManager == 'Cocoapods') {
    typeOfBuild = '-workspace';
    projectName = basename(await config.ios.nativeXcodeWorkspaceDirAbs);
  } else {
    typeOfBuild = '-project';
    projectName = basename(await config.ios.nativeXcodeProjDirAbs);
  }

  if (
    buildOptions.xcodeSigningType == 'manual' &&
    (!buildOptions.xcodeSigningCertificate || !buildOptions.xcodeProvisioningProfile)
  ) {
    throw 'Manually signed Xcode builds require a signing certificate and provisioning profile.';
  }

  const buildArgs = [
    typeOfBuild,
    projectName,
    '-scheme',
    `${theScheme}`,
    '-destination',
    `generic/platform=iOS`,
    '-archivePath',
    `${theScheme}.xcarchive`,
    'archive',
    '-configuration',
    buildOptions.configuration,
  ];

  if (buildOptions.xcodeTeamId) {
    buildArgs.push(`DEVELOPMENT_TEAM=${buildOptions.xcodeTeamId}`);
  }

  if (buildOptions.xcodeSigningType == 'manual') {
    buildArgs.push(`PROVISIONING_PROFILE_SPECIFIER=${buildOptions.xcodeProvisioningProfile}`);
  }

  await runTask('Building xArchive', async () =>
    runCommand('xcodebuild', buildArgs, {
      cwd: config.ios.nativeProjectDirAbs,
    }),
  );

  const manualSigningContents = `<key>provisioningProfiles</key>
<dict>
<key>${config.app.appId}</key>
<string>${buildOptions.xcodeProvisioningProfile ?? ''}</string>
</dict>
<key>signingCertificate</key>
<string>${buildOptions.xcodeSigningCertificate ?? ''}</string>`;

  const archivePlistContents = `<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>method</key>
<string>${buildOptions.xcodeExportMethod ?? XcodeExportMethod.AppStoreConnect}</string>
<key>signingStyle</key>
<string>${buildOptions.xcodeSigningType}</string>
${buildOptions.xcodeSigningType == 'manual' ? manualSigningContents : ''}
</dict>
</plist>`;

  const archivePlistPath = join(`${config.ios.nativeProjectDirAbs}`, 'archive.plist');

  writeFileSync(archivePlistPath, archivePlistContents);

  const archiveArgs = [
    'archive',
    '-archivePath',
    `${theScheme}.xcarchive`,
    '-exportArchive',
    '-exportOptionsPlist',
    'archive.plist',
    '-exportPath',
    'output',
    '-configuration',
    buildOptions.configuration,
  ];

  if (buildOptions.xcodeSigningType == 'automatic') {
    archiveArgs.push('-allowProvisioningUpdates');
  }

  await runTask('Building IPA', async () =>
    runCommand('xcodebuild', archiveArgs, {
      cwd: config.ios.nativeProjectDirAbs,
    }),
  );

  await runTask('Cleaning up', async () => {
    unlinkSync(archivePlistPath);
    rimraf.sync(join(config.ios.nativeProjectDirAbs, `${theScheme}.xcarchive`));
  });

  logSuccess(`Successfully generated an IPA at: ${join(config.ios.nativeProjectDirAbs, 'output')}`);
}