Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: improve Windows support #1645

Merged
merged 4 commits into from
May 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 137 additions & 25 deletions scripts/start.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -2,41 +2,153 @@
# It also installs frontend dependencies.

# For more details on setting-up a development environment, check the docs:
# https://github.com/usememos/memos/blob/main/docs/development.md
# * https://usememos.com/docs/contribution/development
# * https://github.com/usememos/memos/blob/main/docs/development.md

# Usage: ./scripts/start.ps1
$LastExitCode = 0

$projectRoot = (Resolve-Path "$MyInvocation.MyCommand.Path/..").Path
Write-Host "Project root: $projectRoot"
foreach ($dir in @(".", "../")) {
if (Test-Path (Join-Path $dir ".gitignore")) {
$repoRoot = (Resolve-Path $dir).Path
break
}
}

##
$frontendPort = 3001
# Tasks to run, in order
$runTasks = @(
@{
Desc = "start backend with live reload";
Exe = "air.exe";
Args = "-c .\scripts\.air-windows.toml";
Dir = "$repoRoot";
Wait = $false;
},
@{
Desc = "install frontend dependencies";
Exe = "pnpm.exe";
Args = "i";
Dir = "$repoRoot/web"
Wait = $true;
}
@{
Desc = "start frontend with live reload";
Exe = "pnpm.exe";
Args = "dev";
Dir = "$repoRoot/web";
Wait = $false;
}
)
##

Write-Host "Starting backend..." -f Magenta
Start-Process -WorkingDirectory "$projectRoot" -FilePath "air" "-c ./scripts/.air-windows.toml"
if ($LastExitCode -ne 0) {
Write-Host "Failed to start backend!" -f Red
exit $LastExitCode
if (!$repoRoot) {
Write-Host "Could not find repository root!" -f Red
Write-Host "cd into the repository root and run the script again."
Exit 1
}
else {
Write-Host "Backend started!" -f Green

Write-Host "Repository root is $repoRoot"
Write-Host "Starting development environment...`n"
Write-Host @"
███╗ ███╗███████╗███╗ ███╗ ██████╗ ███████╗
████╗ ████║██╔════╝████╗ ████║██╔═══██╗██╔════╝
██╔████╔██║█████╗ ██╔████╔██║██║ ██║███████╗
██║╚██╔╝██║██╔══╝ ██║╚██╔╝██║██║ ██║╚════██║
██║ ╚═╝ ██║███████╗██║ ╚═╝ ██║╚██████╔╝███████║
╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚══════╝
"@

function Stop-ProcessTree {
Param([int]$ParentProcessId)
if (!$ParentProcessId) {
Write-Host "Stop-ProcessTree: unspecified ParentProcessId!" -f Red
return
}
Write-Host "Terminating pid $($ParentProcessId) with all its child processes" -f DarkGray
Get-CimInstance Win32_Process | Where-Object {
$_.ParentProcessId -eq $ParentProcessId
} | ForEach-Object {
Stop-ProcessTree $_.ProcessId
}
Stop-Process -Id $ParentProcessId -ErrorAction SilentlyContinue
}

Write-Host "Installing frontend dependencies..." -f Magenta
Start-Process -Wait -WorkingDirectory "$projectRoot/web" -FilePath "powershell" -ArgumentList "pnpm i"
if ($LastExitCode -ne 0) {
Write-Host "Failed to install frontend dependencies!" -f Red
exit $LastExitCode
$maxDescLength = ( $runTasks | ForEach-Object { $_.Desc.Length } | Measure-Object -Maximum).Maximum
$spawnedPids = @()
foreach ($task in $runTasks) {
Write-Host ("Running task ""$($task.Desc)""...").PadRight($maxDescLength + 20) -f Blue -NoNewline
$task.Dir = (Resolve-Path $task.Dir).Path
try {
$process = Start-Process -PassThru -WorkingDirectory $task.Dir -FilePath $task.Exe -ArgumentList $task.Args -Wait:$task.Wait

if ($process.ExitCode -and $process.ExitCode -ne 0) {
# ExitCode only works for processes started with -Wait:$true
throw "Process exited with code $($process.ExitCode)"
}

Write-Host "[OK]" -f Green
$spawnedPids += $process.Id
}
catch {
Write-Host "[FAILED]" -f Red
Write-Host "Error: $_" -f Red
Write-Host "Unable to execute: $($task.Exe) $($task.Args)" -f Red
Write-Host "Process working directory: $($task.Dir)" -f Red

foreach ($spawnedPid in $spawnedPids) {
Stop-ProcessTree -ParentProcessId $spawnedPid
}
Exit $process.ExitCode
}
}
else {
Write-Host "Frontend dependencies installed!" -f Green

Write-Host "Front-end should be accessible at:" -f Green
$ipAddresses = (Get-NetIPAddress -AddressFamily IPv4) | Select-Object -ExpandProperty IPAddress | Sort-Object
$ipAddresses += "localhost"
foreach ($ip in $ipAddresses) {
Write-Host "· http://$($ip):$($frontendPort)" -f Cyan
}

Write-Host "Starting frontend..." -f Magenta
Start-Process -WorkingDirectory "$projectRoot/web" -FilePath "powershell" -ArgumentList "pnpm dev"
if ($LastExitCode -ne 0) {
Write-Host "Failed to start frontend!" -f Red
exit $LastExitCode
Write-Host "`nPress" -NoNewline
Write-Host " Ctrl + C" -f DarkYellow -NoNewline
Write-Host " or" -NoNewline
Write-Host " Esc" -f DarkYellow -NoNewline
Write-Host " to terminate running servers." -f DarkYellow
[Console]::TreatControlCAsInput = $true

$lastPoll = 0
$noWaitTasks = $runTasks | Where-Object { $_.Wait -eq $false }
while ($true) {
if ([Console]::KeyAvailable) {
$readkey = [Console]::ReadKey("AllowCtrlC,IncludeKeyUp,NoEcho")
if ($readkey.Modifiers -eq "Control" -and $readkey.Key -eq "C") {
break
}
if ($readkey.Key -eq "Escape") {
Break
}
}

# Poll for processes that exited unexpectedly
# Do this every 5 seconds to avoid excessive CPU usage
if (([DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds() - $lastPoll) -ge 5000) {
$noWaitTasks | ForEach-Object {
$name = $_.Exe.TrimEnd(".exe")
if (!(Get-Process -Name $name -ErrorAction SilentlyContinue)) {
Write-Host "Process " -f Red -NoNewline
Write-Host $name -NoNewline -f DarkYellow
Write-Host " is not running anymore!" -f Red
break
}
}
$lastPoll = [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds()
}
Start-Sleep -Milliseconds 500
}
else {
Write-Host "Frontend started!" -f Green

foreach ($spawnedPid in $spawnedPids) {
Stop-ProcessTree -ParentProcessId $spawnedPid
}

Write-Host "Exiting..."
9 changes: 6 additions & 3 deletions server/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,9 @@ func (s *Server) registerResourceRoutes(g *echo.Group) {
Blob: fileBytes,
}
} else if storageServiceID == api.LocalStorage {
// filepath.Join() should be used for local file paths,
// as it handles the os-specific path separator automatically.
// path.Join() always uses '/' as path separator.
systemSettingLocalStoragePath, err := s.Store.FindSystemSetting(ctx, &api.SystemSettingFind{Name: api.SystemSettingLocalStoragePathName})
if err != nil && common.ErrorCode(err) != common.NotFound {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to find local storage path setting").SetInternal(err)
Expand All @@ -123,11 +126,11 @@ func (s *Server) registerResourceRoutes(g *echo.Group) {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to unmarshal local storage path setting").SetInternal(err)
}
}
filePath := localStoragePath
filePath := filepath.FromSlash(localStoragePath)
if !strings.Contains(filePath, "{filename}") {
filePath = path.Join(filePath, "{filename}")
filePath = filepath.Join(filePath, "{filename}")
}
filePath = path.Join(s.Profile.Data, replacePathTemplate(filePath, file.Filename))
filePath = filepath.Join(s.Profile.Data, replacePathTemplate(filePath, file.Filename))
dir, filename := filepath.Split(filePath)
if err = os.MkdirAll(dir, os.ModePerm); err != nil {
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to create directory").SetInternal(err)
Expand Down