@flun/node-mobile-app 4.1.1 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,24 +1,10 @@
1
- import path from 'path';
2
- import fs from 'fs';
3
- import { run, ensureDir } from './utils.js';
4
1
  import chalk from 'chalk';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import { ensureDir, run } from './utils.js';
5
5
 
6
+ /** 应用 Scheme 名称,与 iOS 工程一致 */
6
7
  const SCHEME = 'MyApp',
7
- /** iOS 相关命令只能在 macOS 上跑 */
8
- ensureMac = () => {
9
- if (process.platform !== 'darwin') throw new Error('iOS 构建只能在 macOS 上执行(当前平台: ' + process.platform + ')');
10
- },
11
- /** 检查 xcodebuild 是否存在 */
12
- checkIosTools = () => {
13
- const r = run('which', ['xcodebuild'], { stdio: 'pipe' });
14
- if (r.status !== 0) throw new Error('未找到 xcodebuild,请安装 Xcode 并运行 xcode-select --install');
15
- },
16
- /**
17
- * 执行 pod install(若 Pods/ 已存在且 Podfile.lock 未变,cocoapods 会自动跳过)
18
- */
19
- podInstall = iosDir => {
20
- console.log(chalk.cyan(' pod install ...')), run('pod', ['install'], { cwd: iosDir, inherit: true });
21
- },
22
8
  /**
23
9
  * 把 config.ios.signing.certificate 导入临时 keychain,供 xcodebuild 签名用
24
10
  * 返回 { keychainPath, keychainPassword } 或 null(未配置 / 文件缺失)
@@ -44,98 +30,128 @@ const SCHEME = 'MyApp',
44
30
  run('security', ['set-keychain-settings', '-lut', '3600', keychainPath], { quiet: true });
45
31
  run('security', ['unlock-keychain', '-p', keychainPassword, keychainPath], { quiet: true });
46
32
  run('security', [
47
- 'import', certPath,
48
- '-k', keychainPath,
49
- '-P', signing.certificatePassword || '',
50
- '-T', '/usr/bin/codesign',
51
- '-T', '/usr/bin/security'
33
+ 'import', certPath, '-k', keychainPath, '-P', signing.certificatePassword || '',
34
+ '-T', '/usr/bin/codesign', '-T', '/usr/bin/security'
52
35
  ], { quiet: true });
53
36
  run('security', [
54
- 'set-key-partition-list',
55
- '-S', 'apple-tool:,apple:,codesign:',
56
- '-s', '-k', keychainPassword, keychainPath
37
+ 'set-key-partition-list', '-S', 'apple-tool:,apple:,codesign:', '-s', '-k', keychainPassword, keychainPath
57
38
  ], { quiet: true });
58
39
 
59
40
  return { keychainPath, keychainPassword };
60
- },
61
- /**
62
- * xcodebuild archive
63
- * 返回 xcarchive 路径
64
- */
65
- xcodebuildArchive = (buildDir, config, release, userProjectDir) => {
66
- const iosDir = path.join(buildDir, 'ios'), workspace = path.join(iosDir, SCHEME + '.xcworkspace');
67
- if (!fs.existsSync(workspace)) throw new Error('未找到 ' + workspace + '(pod install 是否成功?)');
68
-
69
- const archiveDir = path.join(iosDir, 'build');
70
- ensureDir(archiveDir);
71
- const archivePath = path.join(archiveDir, SCHEME + '.xcarchive'), configName = release ? 'Release' : 'Debug',
72
- teamId = (config.ios && config.ios.signing && config.ios.signing.teamId) || '',
73
- args = [
74
- '-workspace', workspace,
75
- '-scheme', SCHEME,
76
- '-configuration', configName,
77
- '-destination', 'generic/platform=iOS',
78
- '-archivePath', archivePath,
79
- 'archive',
80
- ];
81
-
82
- if (teamId) args.push('DEVELOPMENT_TEAM=' + teamId);
83
-
84
- // 若配置了 certificate,导入临时 keychain 并让 xcodebuild 使用
85
- const keychain = setupSigningKeychain(buildDir, userProjectDir, config);
86
- if (keychain) args.push('OTHER_CODE_SIGN_FLAGS=--keychain ' + keychain.keychainPath);
87
-
88
- console.log(chalk.cyan(' xcodebuild archive ...')), run('xcodebuild', args, { cwd: iosDir, quiet: true });
89
- return archivePath;
90
- },
91
- /**
92
- * xcodebuild -exportArchive,导出 .ipa
93
- * 返回 .ipa 路径
94
- */
95
- xcodebuildExport = (buildDir, config, release, outDir, userProjectDir) => {
96
- const iosDir = path.join(buildDir, 'ios'), exportDir = path.join(iosDir, 'build', 'export');
97
- ensureDir(exportDir);
98
- const archivePath = path.join(iosDir, 'build', SCHEME + '.xcarchive'), method = release ? 'ad-hoc' : 'development',
99
- signing = (config.ios && config.ios.signing) || {}, teamId = signing.teamId || '',
100
- // provisioningProfile 路径 → 取 basename(去扩展名),写进 exportOptions.plist
101
- profilePath = signing.provisioningProfile ? path.resolve(userProjectDir, signing.provisioningProfile) : '',
102
- profileName = profilePath ? path.basename(profilePath).replace(/\.mobileprovision$/i, '') : '',
103
- plistPath = path.join(iosDir, 'build', 'exportOptions.plist'),
104
- plist = [
105
- '<?xml version="1.0" encoding="UTF-8"?>',
106
- '<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">',
107
- '<plist version="1.0">',
108
- '<dict>',
109
- ' <key>method</key>',
110
- ' <string>' + method + '</string>',
111
- ' <key>stripSwiftSymbols</key>',
112
- ' <true/>',
113
- ' <key>compileBitcode</key>',
114
- ' <false/>',
115
- teamId ? ' <key>teamID</key>' : '',
116
- teamId ? ' <string>' + teamId + '</string>' : '',
117
- profileName ? ' <key>provisioningProfiles</key>' : '',
118
- profileName ? ' <dict>' : '',
119
- profileName ? ' <key>' + config.appId + '</key>' : '',
120
- profileName ? ' <string>' + profileName + '</string>' : '',
121
- profileName ? ' </dict>' : '',
122
- '</dict>',
123
- '</plist>',
124
- ].filter(Boolean).join('\n');
125
- fs.writeFileSync(plistPath, plist, 'utf-8'); // 生成 exportOptions.plist
126
-
127
- console.log(chalk.cyan(' xcodebuild -exportArchive ...'));
128
- run('xcodebuild', [
129
- '-exportArchive',
130
- '-archivePath', archivePath,
131
- '-exportOptionsPlist', plistPath,
132
- '-exportPath', exportDir,
133
- ], { cwd: iosDir, quiet: true });
134
-
135
- // 找 .ipa
136
- const files = fs.readdirSync(exportDir).filter(f => f.endsWith('.ipa'));
137
- if (files.length === 0) throw new Error('导出目录里没有 .ipa: ' + exportDir);
138
- return path.join(exportDir, files[0]);
139
41
  };
140
42
 
141
- export { ensureMac, checkIosTools, podInstall, xcodebuildArchive, xcodebuildExport }
43
+ /**
44
+ * 检查 xcodebuild 是否存在,缺失时抛错提示安装 Xcode
45
+ * >查看定义:@see {@link checkIosTools}
46
+ * @returns {void}
47
+ */
48
+ const checkIosTools = () => {
49
+ const r = run('which', ['xcodebuild'], { stdio: 'pipe' });
50
+ if (r.status !== 0) throw new Error('未找到 xcodebuild,请安装 Xcode 并运行 xcode-select --install');
51
+ };
52
+
53
+ /**
54
+ * iOS 相关命令只能在 macOS 上跑,非 darwin 抛错
55
+ * >查看定义:@see {@link ensureMac}
56
+ * @returns {void}
57
+ */
58
+ const ensureMac = () => {
59
+ if (process.platform !== 'darwin') throw new Error('iOS 构建只能在 macOS 上执行(当前平台: ' + process.platform + ')');
60
+ };
61
+
62
+ /**
63
+ * 执行 pod install(若 Pods/ 已存在且 Podfile.lock 未变,cocoapods 会自动跳过)
64
+ * >查看定义:@see {@link podInstall}
65
+ * @param {string} iosDir iOS 工程目录
66
+ * @returns {void}
67
+ */
68
+ const podInstall = iosDir => {
69
+ console.log(chalk.cyan(' pod install ...')), run('pod', ['install'], { cwd: iosDir, inherit: true });
70
+ };
71
+
72
+ /**
73
+ * xcodebuild archive,返回 xcarchive 路径
74
+ * >查看定义:@see {@link xcodebuildArchive}
75
+ * @param {string} buildDir 构建目录
76
+ * @param {object} config 用户配置
77
+ * @param {boolean} release 是否 Release 配置
78
+ * @param {string} userProjectDir 用户项目目录
79
+ * @returns {string} xcarchive 路径
80
+ */
81
+ const xcodebuildArchive = (buildDir, config, release, userProjectDir) => {
82
+ const iosDir = path.join(buildDir, 'ios'), workspace = path.join(iosDir, SCHEME + '.xcworkspace');
83
+ if (!fs.existsSync(workspace)) throw new Error('未找到 ' + workspace + '(pod install 是否成功?)');
84
+
85
+ const archiveDir = path.join(iosDir, 'build');
86
+ ensureDir(archiveDir);
87
+ const archivePath = path.join(archiveDir, SCHEME + '.xcarchive'), configName = release ? 'Release' : 'Debug',
88
+ teamId = (config.ios && config.ios.signing && config.ios.signing.teamId) || '',
89
+ args = [
90
+ '-workspace', workspace, '-scheme', SCHEME, '-configuration', configName,
91
+ '-destination', 'generic/platform=iOS', '-archivePath', archivePath, 'archive'
92
+ ];
93
+
94
+ if (teamId) args.push('DEVELOPMENT_TEAM=' + teamId);
95
+
96
+ // 若配置了 certificate,导入临时 keychain 并让 xcodebuild 使用
97
+ const keychain = setupSigningKeychain(buildDir, userProjectDir, config);
98
+ if (keychain) args.push('OTHER_CODE_SIGN_FLAGS=--keychain ' + keychain.keychainPath);
99
+
100
+ console.log(chalk.cyan(' xcodebuild archive ...')), run('xcodebuild', args, { cwd: iosDir, quiet: true });
101
+ return archivePath;
102
+ };
103
+
104
+ /**
105
+ * xcodebuild -exportArchive,导出 .ipa,返回 .ipa 路径
106
+ * >查看定义:@see {@link xcodebuildExport}
107
+ * @param {string} buildDir 构建目录
108
+ * @param {object} config 用户配置
109
+ * @param {boolean} release 是否 Release 导出
110
+ * @param {string} outDir 输出目录
111
+ * @param {string} userProjectDir 用户项目目录
112
+ * @returns {string} .ipa 文件路径
113
+ */
114
+ const xcodebuildExport = (buildDir, config, release, outDir, userProjectDir) => {
115
+ const iosDir = path.join(buildDir, 'ios'), exportDir = path.join(iosDir, 'build', 'export');
116
+ ensureDir(exportDir);
117
+ const archivePath = path.join(iosDir, 'build', SCHEME + '.xcarchive'), method = release ? 'ad-hoc' : 'development',
118
+ signing = (config.ios && config.ios.signing) || {}, teamId = signing.teamId || '',
119
+ // provisioningProfile 路径 → 取 basename(去扩展名),写进 exportOptions.plist
120
+ profilePath = signing.provisioningProfile ? path.resolve(userProjectDir, signing.provisioningProfile) : '',
121
+ profileName = profilePath ? path.basename(profilePath).replace(/\.mobileprovision$/i, '') : '',
122
+ plistPath = path.join(iosDir, 'build', 'exportOptions.plist'),
123
+ plist = [
124
+ '<?xml version="1.0" encoding="UTF-8"?>',
125
+ '<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">',
126
+ '<plist version="1.0">',
127
+ '<dict>',
128
+ ' <key>method</key>',
129
+ ' <string>' + method + '</string>',
130
+ ' <key>stripSwiftSymbols</key>',
131
+ ' <true/>',
132
+ ' <key>compileBitcode</key>',
133
+ ' <false/>',
134
+ teamId ? ' <key>teamID</key>' : '',
135
+ teamId ? ' <string>' + teamId + '</string>' : '',
136
+ profileName ? ' <key>provisioningProfiles</key>' : '',
137
+ profileName ? ' <dict>' : '',
138
+ profileName ? ' <key>' + config.appId + '</key>' : '',
139
+ profileName ? ' <string>' + profileName + '</string>' : '',
140
+ profileName ? ' </dict>' : '',
141
+ '</dict>',
142
+ '</plist>',
143
+ ].filter(Boolean).join('\n');
144
+ fs.writeFileSync(plistPath, plist, 'utf-8'); // 生成 exportOptions.plist
145
+
146
+ console.log(chalk.cyan(' xcodebuild -exportArchive ...'));
147
+ run('xcodebuild', [
148
+ '-exportArchive', '-archivePath', archivePath, '-exportOptionsPlist', plistPath, '-exportPath', exportDir
149
+ ], { cwd: iosDir, quiet: true });
150
+
151
+ // 找 .ipa
152
+ const files = fs.readdirSync(exportDir).filter(f => f.endsWith('.ipa'));
153
+ if (files.length === 0) throw new Error('导出目录里没有 .ipa: ' + exportDir);
154
+ return path.join(exportDir, files[0]);
155
+ };
156
+
157
+ export { checkIosTools, ensureMac, podInstall, xcodebuildArchive, xcodebuildExport };