spawn.test.mjs 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. // Copyright 2019-2021 Tauri Programme within The Commons Conservancy
  2. // SPDX-License-Identifier: Apache-2.0
  3. // SPDX-License-Identifier: MIT
  4. import { main, spawn, sleep } from 'effection'
  5. import { exec } from '@effection/process'
  6. import fs from 'fs/promises'
  7. import path from 'path'
  8. import crypto from 'crypto'
  9. import tempDirectory from 'temp-dir'
  10. let assert
  11. const nodeVersion = process.versions.node.split('.')[0]
  12. if (nodeVersion === '14') {
  13. assert = await import('assert')
  14. } else {
  15. assert = await import('assert/strict')
  16. }
  17. const ctaBinary = path.resolve('./bin/create-tauri-app.js')
  18. const clijs = path.resolve('../cli.js/')
  19. const api = path.resolve('../api/')
  20. const manager = process.env.TAURI_RUN_MANAGER || 'yarn'
  21. const recipes = process.env.TAURI_RECIPE
  22. ? process.env.TAURI_RECIPE.split(',')
  23. : ['vanillajs', 'cra', 'vite', 'ngcli', 'solid', 'cljs']
  24. const parallelize = process.env.TAURI_RECIPE_PARALLELIZE || false
  25. main(function* start() {
  26. const tauriTemp = path.join(
  27. tempDirectory,
  28. `tauri_${crypto.randomBytes(16).toString('hex')}`
  29. )
  30. try {
  31. const appName = 'tauri-app'
  32. let output = {}
  33. for (let i = 0; i < recipes.length; i++) {
  34. const recipe = recipes[i]
  35. console.log(`::group::recipe ${recipe}`)
  36. console.log(`------------------ ${recipe} started -------------------`)
  37. const recipeFolder = path.join(tauriTemp, recipe)
  38. const appFolder = path.join(recipeFolder, appName)
  39. yield fs.mkdir(recipeFolder, { recursive: true })
  40. console.log(`${recipeFolder} created.`)
  41. // runs CTA with all args set to avoid any prompts
  42. const run = `node '${ctaBinary}' --manager ${manager} --recipe ${recipe} --ci --dev`
  43. console.log(`[running] ${run}`)
  44. let opts = []
  45. if (manager === 'npm') {
  46. opts =
  47. recipe == 'vuecli'
  48. ? ['run', 'tauri:build']
  49. : ['run', 'tauri', '--', 'build']
  50. } else if (manager === 'yarn') {
  51. opts = recipe == 'vuecli' ? ['tauri:build'] : ['tauri', 'build']
  52. }
  53. if (!parallelize) {
  54. const cta = yield exec(run, { cwd: recipeFolder })
  55. yield streamLogs(cta)
  56. // now it is finished, assert on some things
  57. yield assertCTAState({ appFolder, appName })
  58. const tauriBuild = yield exec(manager, {
  59. arguments: opts,
  60. cwd: appFolder
  61. })
  62. yield streamLogs(tauriBuild)
  63. // build is complete, assert on some things
  64. yield assertTauriBuildState({ appFolder, appName })
  65. } else {
  66. console.log('running CTA recipe in parallel')
  67. output[recipe] = yield spawn(function* () {
  68. const childCTA = yield exec(run, { cwd: recipeFolder })
  69. const cta = yield captureLogs(childCTA)
  70. const childTauriBuild = yield exec(manager, {
  71. arguments: opts,
  72. cwd: appFolder
  73. })
  74. const tauriBuild = yield captureLogs(childTauriBuild)
  75. return { cta, tauriBuild }
  76. })
  77. }
  78. console.log(`------------------ ${recipe} complete -------------------`)
  79. console.log('::endgroup::')
  80. // sometimes it takes a moment to flush all of the logs
  81. // to the console, let things catch up here
  82. yield sleep(1000)
  83. }
  84. if (parallelize) {
  85. for (let i = 0; i < recipes.length; i++) {
  86. const recipe = recipes[i]
  87. console.log(`::group::recipe ${recipe} logs`)
  88. console.log(
  89. `------------------ ${recipe} output start -------------------`
  90. )
  91. const out = yield output[recipe]
  92. console.log(out.cta)
  93. console.log(out.tauriBuild)
  94. console.log(
  95. `------------------ ${recipe} output end -------------------`
  96. )
  97. console.log('::endgroup::')
  98. }
  99. }
  100. } catch (e) {
  101. console.error(e)
  102. throw Error(e)
  103. } finally {
  104. console.log('\nstopping process...')
  105. // wait a tick for file locks to be release
  106. yield sleep(5000)
  107. yield fs.rm(tauriTemp, { recursive: true, force: true })
  108. console.log(`${tauriTemp} deleted.`)
  109. }
  110. })
  111. function* streamLogs(child) {
  112. yield child.stdout.forEach((data) => {
  113. process.stdout.write(data)
  114. })
  115. yield child.stderr.forEach((data) => {
  116. process.stderr.write(data)
  117. })
  118. }
  119. function* captureLogs(child) {
  120. let log = ''
  121. yield child.stdout.forEach((data) => {
  122. log += data
  123. })
  124. yield child.stderr.forEach((data) => {
  125. log += data
  126. })
  127. return log
  128. }
  129. function* assertCTAState({ appFolder, appName }) {
  130. const packageFileInitial = JSON.parse(
  131. yield fs.readFile(path.join(appFolder, 'package.json'), 'utf-8')
  132. )
  133. assert.strictEqual(
  134. packageFileInitial.name,
  135. appName,
  136. `The package.json did not have the name "${appName}".`
  137. )
  138. assert.strictEqual(
  139. packageFileInitial.scripts.tauri,
  140. 'tauri',
  141. `The package.json did not have the tauri script.`
  142. )
  143. }
  144. function* assertTauriBuildState({ appFolder, appName }) {
  145. const packageFileOutput = JSON.parse(
  146. yield fs.readFile(path.join(appFolder, 'package.json'), 'utf-8')
  147. )
  148. assert.strictEqual(
  149. packageFileOutput.name,
  150. appName,
  151. `The package.json did not have the name "${appName}".`
  152. )
  153. assert.strictEqual(
  154. packageFileOutput.scripts.tauri,
  155. 'tauri',
  156. `The package.json did not have the tauri script.`
  157. )
  158. const cargoFileOutput = yield fs.readFile(
  159. path.join(appFolder, 'src-tauri', 'Cargo.toml'),
  160. 'utf-8'
  161. )
  162. assert.strictEqual(
  163. cargoFileOutput.startsWith(`[package]\nname = "app"`),
  164. true,
  165. `The Cargo.toml did not have the name "app".`
  166. )
  167. const tauriTarget = yield fs.readdir(
  168. path.join(appFolder, 'src-tauri', 'target')
  169. )
  170. assert.strictEqual(
  171. tauriTarget.includes('release'),
  172. true,
  173. `The Tauri build does not have a target/release directory.`
  174. )
  175. }