mobileBuild.ts 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. import os from 'node:os'
  2. import { Listr } from 'listr2'
  3. import { config as initDE } from 'dotenv'
  4. import { logLogo } from './logo'
  5. import { downloadFile } from './download'
  6. import { config, currentPath } from './config'
  7. import {
  8. fileExistsAsync,
  9. removeDir,
  10. createDir,
  11. runCommand,
  12. ensureDownloadedAndExtracted,
  13. renameWithOverwrite,
  14. } from './utils'
  15. initDE()
  16. logLogo()
  17. // ----------------------------
  18. // Main Listr Tasks definition
  19. // ----------------------------
  20. await new Listr(
  21. [
  22. {
  23. title: 'Rebuilding Nuxt.js Sources',
  24. task: () =>
  25. new Listr(
  26. [
  27. {
  28. title: 'Removing old Nuxt build',
  29. task: async () => {
  30. await Promise.all([removeDir('.output')])
  31. },
  32. },
  33. {
  34. title: 'Generating static build using Nuxt',
  35. task: async (_, task) => {
  36. await runCommand('nuxt', ['generate'], { task })
  37. },
  38. },
  39. ],
  40. { concurrent: false },
  41. ),
  42. },
  43. {
  44. title: 'Configuring Capacitor Platforms',
  45. task: async (_, task) => {
  46. try {
  47. await runCommand('npx', ['cap', 'add', 'android'], { task })
  48. await runCommand('npx', ['cap', 'add', 'ios'], { task })
  49. } catch {
  50. await runCommand('npx', ['cap', 'sync'], { task })
  51. }
  52. },
  53. },
  54. {
  55. title: "Generating assets",
  56. task: async (ctx, subTask) => {
  57. await runCommand('npx', ["@capacitor/assets", "generate", "--iconBackgroundColor", "'#323232'", "--iconBackgroundColorDark", "'#323232'", "--splashBackgroundColor", "'#323232'", "--splashBackgroundColorDark", "'#323232'"], {
  58. task: subTask,
  59. maxOutputLines: 3,
  60. })
  61. }
  62. },
  63. {
  64. title: 'Assembling Android Application',
  65. task: () =>
  66. new Listr(
  67. [
  68. {
  69. title: 'Verifying Java SE 20 Installation',
  70. task: async (ctx, subTask) => {
  71. subTask.title = 'Checking Java installation'
  72. if (!(await fileExistsAsync(config.JAVA_BIN))) {
  73. const systemConfig: Partial<
  74. Record<NodeJS.Platform, Partial<Record<NodeJS.Architecture, string>>>
  75. > = {
  76. linux: {
  77. x64: `https://download.oracle.com/java/${config.JAVA_VERSION.split('.')[0]}/archive/jdk-${config.JAVA_VERSION}_linux-x64_bin.tar.gz`,
  78. arm64: `https://download.oracle.com/java/${config.JAVA_VERSION.split('.')[0]}/archive/jdk-${config.JAVA_VERSION}_linux-aarch64_bin.tar.gz`,
  79. },
  80. // TODO: add support for win32
  81. }
  82. const platform = os.platform() as keyof typeof systemConfig
  83. const arch = os.arch()
  84. const url = systemConfig[platform]?.[arch]
  85. if (!url) {
  86. throw new Error(
  87. 'Your system platform is not supported for Java installation',
  88. )
  89. }
  90. await createDir(config.JAVA_DIR)
  91. await ensureDownloadedAndExtracted(
  92. config.JAVA_BIN,
  93. url,
  94. `jdk-${config.JAVA_VERSION}.tar.gz`,
  95. [
  96. `rm -rf ${config.JAVA_DIR}/* && mkdir -p ${config.JAVA_DIR}`,
  97. `tar -xzf jdk-${config.JAVA_VERSION}.tar.gz -C ${config.JAVA_DIR}`,
  98. `rm jdk-${config.JAVA_VERSION}.tar.gz`,
  99. ],
  100. { task: subTask },
  101. )
  102. subTask.title = 'Java installed'
  103. }
  104. else {
  105. subTask.title = 'Java already installed'
  106. }
  107. },
  108. },
  109. {
  110. title: 'Verifying Android SDK Installation',
  111. task: async (ctx, subTask) => {
  112. const toolsCheck = config.ANDROID_TOOLS.map(tool =>
  113. fileExistsAsync(`${config.SDK_DIR}/${tool.replace(';', '/')}/package.xml`),
  114. )
  115. const sdkInstalled = (await Promise.all(toolsCheck)).every(Boolean)
  116. // Save the result for later tasks.
  117. ctx.isSdkInstalled = sdkInstalled
  118. subTask.title = sdkInstalled
  119. ? 'Android SDK is installed'
  120. : 'Android SDK not found, installation required'
  121. },
  122. },
  123. {
  124. title: 'Installing Android Command Line Tools',
  125. task: async (ctx, subTask) => {
  126. if (ctx.isSdkInstalled) {
  127. subTask.skip('Command Line Tools already installed')
  128. return
  129. }
  130. if (!(await fileExistsAsync(config.CMD_TOOLS_ARCHIVE))) {
  131. await downloadFile(
  132. subTask,
  133. 'https://dl.google.com/android/repository/commandlinetools-linux-11076708_latest.zip',
  134. config.CMD_TOOLS_ARCHIVE,
  135. )
  136. }
  137. subTask.title = 'Unzipping archive'
  138. await runCommand(
  139. 'bash',
  140. ['-c', `"rm -rf ${config.CMD_TOOLS_DIR} && unzip ${config.CMD_TOOLS_ARCHIVE}"`],
  141. { task: subTask },
  142. )
  143. subTask.title = 'Android Command Line Tools installed'
  144. },
  145. },
  146. {
  147. title: 'Installing Additional Android SDK Components',
  148. skip: ctx =>
  149. ctx.isSdkInstalled ? 'Additional tools already installed' : false,
  150. task: async (ctx, subTask) => {
  151. subTask.title = 'Accepting Android licenses'
  152. await createDir(config.SDK_DIR)
  153. await runCommand('bash', ['-c', `"chmod +x ./${config.CMD_TOOLS_DIR}/bin/sdkmanager"`], {
  154. task: subTask,
  155. disableOutput: true,
  156. })
  157. await runCommand(
  158. 'bash',
  159. [
  160. '-c',
  161. `"yes | ./${config.CMD_TOOLS_DIR}/bin/sdkmanager --sdk_root=${currentPath}/${config.SDK_DIR.slice(2)} --licenses"`,
  162. ],
  163. { task: subTask, disableOutput: true },
  164. )
  165. for (const tool of config.ANDROID_TOOLS) {
  166. subTask.title = `Installing: ${tool}`
  167. await runCommand(
  168. 'bash',
  169. [
  170. '-c',
  171. `"./${config.CMD_TOOLS_DIR}/bin/sdkmanager --sdk_root=${currentPath}/${config.SDK_DIR.slice(2)} --install '${tool}'"`,
  172. ],
  173. { task: subTask, disableOutput: true },
  174. )
  175. }
  176. subTask.title = 'Additional Android SDK components installed'
  177. },
  178. },
  179. {
  180. title: 'Compiling Android Project using Gradle',
  181. task: async (ctx, subTask) => {
  182. subTask.title = 'Gradle is building the project, please wait'
  183. await runCommand('bash', ['-c', '"cd android && ./gradlew assembleDebug"'], {
  184. task: subTask,
  185. maxOutputLines: 3,
  186. })
  187. renameWithOverwrite(`${currentPath}/android/app/build/outputs/apk/debug/app-debug.apk`, `${currentPath}/${process.env.APP_VERSION}.apk`)
  188. subTask.title = `APK built at: ${currentPath}/${process.env.APP_VERSION}.apk`
  189. },
  190. },
  191. {
  192. title: `Deploying APK to Connected Android Device ${!process.env.ANDROID_DEVICE_ID ? '[Will be skipped]' : ''}`,
  193. skip: () => !process.env.ANDROID_DEVICE_ID,
  194. task: async (ctx, subTask) => {
  195. const adb = `${config.SDK_DIR}/platform-tools/adb`
  196. const launchAppCommand = 'shell monkey -p app.hapticx.procollege -c android.intent.category.LAUNCHER 1'.split(' ')
  197. subTask.title = 'Checking connected devices'
  198. await runCommand(adb, ['devices'], { task: subTask })
  199. try {
  200. subTask.title = 'Uninstall old package'
  201. await runCommand(
  202. adb,
  203. [
  204. '-s',
  205. process.env.ANDROID_DEVICE_ID,
  206. 'uninstall',
  207. 'app.hapticx.procollege',
  208. ],
  209. { task: subTask },
  210. )
  211. } catch {
  212. subTask.title = "Package not found";
  213. }
  214. subTask.title = 'Installing APK on device'
  215. await runCommand(
  216. adb,
  217. [
  218. '-s',
  219. process.env.ANDROID_DEVICE_ID,
  220. 'install',
  221. `${currentPath}/${process.env.APP_VERSION}.apk`,
  222. ],
  223. { task: subTask },
  224. )
  225. subTask.title = 'Launching the application'
  226. await runCommand(adb, ['-s', process.env.ANDROID_DEVICE_ID, ...launchAppCommand])
  227. subTask.title = `App launched on device: ${process.env.ANDROID_DEVICE_ID}`
  228. },
  229. },
  230. ],
  231. { concurrent: false },
  232. ),
  233. },
  234. ],
  235. {
  236. concurrent: false,
  237. rendererOptions: {
  238. collapseSubtasks: false,
  239. formatOutput: 'wrap',
  240. },
  241. },
  242. ).run()