start.ps1 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. # This script starts the backend and frontend in development mode, with live reload.
  2. # It also installs frontend dependencies.
  3. # For more details on setting-up a development environment, check the docs:
  4. # * https://usememos.com/docs/contribution/development
  5. # * https://github.com/usememos/memos/blob/main/docs/development.md
  6. # Usage: ./scripts/start.ps1
  7. foreach ($dir in @(".", "../")) {
  8. if (Test-Path (Join-Path $dir ".gitignore")) {
  9. $repoRoot = (Resolve-Path $dir).Path
  10. break
  11. }
  12. }
  13. ##
  14. $frontendPort = 3001
  15. # Tasks to run, in order
  16. $runTasks = @(
  17. @{
  18. Desc = "install frontend dependencies";
  19. Exe = "powershell.exe";
  20. Args = (
  21. "-Command",
  22. "pnpm i"
  23. );
  24. Dir = "$repoRoot/web"
  25. Wait = $true;
  26. },
  27. @{
  28. Desc = "generate buf types";
  29. Exe = "powershell.exe";
  30. Args = (
  31. "-Command",
  32. "pnpm type-gen"
  33. );
  34. Dir = "$repoRoot/web"
  35. Wait = $true;
  36. },
  37. @{
  38. Desc = "start backend with live reload";
  39. Exe = "air.exe";
  40. Args = (
  41. "-c",
  42. ".\scripts\.air-windows.toml"
  43. );
  44. Dir = "$repoRoot";
  45. Wait = $false;
  46. },
  47. @{
  48. Desc = "start frontend with live reload";
  49. Exe = "powershell.exe";
  50. Args = (
  51. "-Command",
  52. "pnpm dev"
  53. );
  54. Dir = "$repoRoot/web";
  55. Wait = $false;
  56. }
  57. )
  58. ##
  59. if (!$repoRoot) {
  60. Write-Host "Could not find repository root!" -f Red
  61. Write-Host "cd into the repository root and run the script again."
  62. Exit 1
  63. }
  64. Write-Host "Repository root is $repoRoot"
  65. Write-Host "Starting development environment...`n"
  66. Write-Host @"
  67. ███╗ ███╗███████╗███╗ ███╗ ██████╗ ███████╗
  68. ████╗ ████║██╔════╝████╗ ████║██╔═══██╗██╔════╝
  69. ██╔████╔██║█████╗ ██╔████╔██║██║ ██║███████╗
  70. ██║╚██╔╝██║██╔══╝ ██║╚██╔╝██║██║ ██║╚════██║
  71. ██║ ╚═╝ ██║███████╗██║ ╚═╝ ██║╚██████╔╝███████║
  72. ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚══════╝
  73. "@
  74. function Stop-ProcessTree {
  75. Param([int]$ParentProcessId)
  76. if (!$ParentProcessId) {
  77. Write-Host "Stop-ProcessTree: unspecified ParentProcessId!" -f Red
  78. return
  79. }
  80. Write-Host "Terminating pid $($ParentProcessId) with all its child processes" -f DarkGray
  81. Get-CimInstance Win32_Process | Where-Object {
  82. $_.ParentProcessId -eq $ParentProcessId
  83. } | ForEach-Object {
  84. Stop-ProcessTree $_.ProcessId
  85. }
  86. Stop-Process -Id $ParentProcessId -ErrorAction SilentlyContinue
  87. }
  88. $maxDescLength = ( $runTasks | ForEach-Object { $_.Desc.Length } | Measure-Object -Maximum).Maximum
  89. $spawnedPids = @()
  90. foreach ($task in $runTasks) {
  91. Write-Host ("Running task ""$($task.Desc)""...").PadRight($maxDescLength + 20) -f Blue -NoNewline
  92. $task.Dir = (Resolve-Path $task.Dir).Path
  93. try {
  94. $process = Start-Process -PassThru -WorkingDirectory $task.Dir -FilePath $task.Exe -ArgumentList $task.Args -Wait:$task.Wait
  95. if ($process.ExitCode -and $process.ExitCode -ne 0) {
  96. # ExitCode only works for processes started with -Wait:$true
  97. throw "Process exited with code $($process.ExitCode)"
  98. }
  99. Write-Host "[OK]" -f Green
  100. $spawnedPids += $process.Id
  101. }
  102. catch {
  103. Write-Host "[FAILED]" -f Red
  104. Write-Host "Error: $_" -f Red
  105. Write-Host "Unable to execute: $($task.Exe) $($task.Args)" -f Red
  106. Write-Host "Process working directory: $($task.Dir)" -f Red
  107. foreach ($spawnedPid in $spawnedPids) {
  108. Stop-ProcessTree -ParentProcessId $spawnedPid
  109. }
  110. Exit $process.ExitCode
  111. }
  112. }
  113. Write-Host "Front-end should be accessible at:" -f Green
  114. $ipAddresses = (Get-NetIPAddress -AddressFamily IPv4) | Select-Object -ExpandProperty IPAddress | Sort-Object
  115. $ipAddresses += "localhost"
  116. foreach ($ip in $ipAddresses) {
  117. Write-Host "· http://$($ip):$($frontendPort)" -f Cyan
  118. }
  119. Write-Host "`nPress" -NoNewline
  120. Write-Host " Ctrl + C" -f DarkYellow -NoNewline
  121. Write-Host " or" -NoNewline
  122. Write-Host " Esc" -f DarkYellow -NoNewline
  123. Write-Host " to terminate running servers." -f DarkYellow
  124. [Console]::TreatControlCAsInput = $true
  125. $lastPoll = 0
  126. $noWaitTasks = $runTasks | Where-Object { $_.Wait -eq $false }
  127. while ($true) {
  128. if ([Console]::KeyAvailable) {
  129. $readkey = [Console]::ReadKey("AllowCtrlC,IncludeKeyUp,NoEcho")
  130. if ($readkey.Modifiers -eq "Control" -and $readkey.Key -eq "C") {
  131. break
  132. }
  133. if ($readkey.Key -eq "Escape") {
  134. Break
  135. }
  136. }
  137. # Poll for processes that exited unexpectedly
  138. # Do this every 5 seconds to avoid excessive CPU usage
  139. if (([DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds() - $lastPoll) -ge 5000) {
  140. $noWaitTasks | ForEach-Object {
  141. $name = $_.Exe.TrimEnd(".exe")
  142. if (!(Get-Process -Name $name -ErrorAction SilentlyContinue)) {
  143. Write-Host "Process " -f Red -NoNewline
  144. Write-Host $name -NoNewline -f DarkYellow
  145. Write-Host " is not running anymore!" -f Red
  146. break
  147. }
  148. }
  149. $lastPoll = [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds()
  150. }
  151. Start-Sleep -Milliseconds 500
  152. }
  153. foreach ($spawnedPid in $spawnedPids) {
  154. Stop-ProcessTree -ParentProcessId $spawnedPid
  155. }
  156. Write-Host "Exiting..."