You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

256 lines
7.3 KiB

4 weeks ago
param(
[ValidateSet("start", "stop", "restart", "status", "logs")]
[string]$Action = "status",
[int]$Port = 8080
)
$ScriptRoot = Split-Path -Parent $MyInvocation.MyCommand.Path
$ProjectRoot = Split-Path -Parent $ScriptRoot
$RuntimeRoot = Join-Path $ProjectRoot "runtime"
$LogRoot = Join-Path $RuntimeRoot "logs"
$RunRoot = Join-Path $RuntimeRoot "run"
$BinRoot = Join-Path $RuntimeRoot "bin"
$CacheRoot = Join-Path $RuntimeRoot "cache"
$PidFile = Join-Path $RunRoot "teraclone.pid"
$StdOutLogFile = Join-Path $LogRoot "server.out.log"
$StdErrLogFile = Join-Path $LogRoot "server.err.log"
$DevExe = Join-Path $BinRoot "teraclone-dev.exe"
$GoCache = Join-Path $CacheRoot "go-build"
$GoTmp = Join-Path $CacheRoot "go-tmp"
$Port = [string]$Port
$StdOutEventSource = "teraclone.stdout.$Port"
$StdErrEventSource = "teraclone.stderr.$Port"
function Ensure-RuntimeLayout {
New-Item -ItemType Directory -Force -Path $LogRoot | Out-Null
New-Item -ItemType Directory -Force -Path $RunRoot | Out-Null
New-Item -ItemType Directory -Force -Path $BinRoot | Out-Null
New-Item -ItemType Directory -Force -Path $GoCache | Out-Null
New-Item -ItemType Directory -Force -Path $GoTmp | Out-Null
}
function Get-PortOwnerPid {
$netstatLines = netstat -ano -p TCP | Select-String ":$Port\s+.*LISTENING\s+(\d+)$"
if ($null -eq $netstatLines) {
return $null
}
foreach ($line in $netstatLines) {
if ($line.Matches.Count -gt 0) {
return [int]$line.Matches[0].Groups[1].Value
}
}
return $null
}
function Get-PortOwnerProcess {
$ownerPid = Get-PortOwnerPid
if ($null -eq $ownerPid) {
return $null
}
return Get-Process -Id $ownerPid -ErrorAction SilentlyContinue
}
function Get-ServerProcess {
if (-not (Test-Path $PidFile)) {
return $null
}
$pidLine = Get-Content $PidFile -ErrorAction SilentlyContinue | Select-Object -First 1
if ($null -eq $pidLine) {
Remove-Item $PidFile -ErrorAction SilentlyContinue
return $null
}
$pidValue = $pidLine.Trim()
if ([string]::IsNullOrWhiteSpace($pidValue)) {
Remove-Item $PidFile -ErrorAction SilentlyContinue
return $null
}
$process = Get-Process -Id $pidValue -ErrorAction SilentlyContinue
if ($null -eq $process) {
Remove-Item $PidFile -ErrorAction SilentlyContinue
return $null
}
return $process
}
function Clear-LogEventSubscriptions {
foreach ($source in @($StdOutEventSource, $StdErrEventSource)) {
Unregister-Event -SourceIdentifier $source -ErrorAction SilentlyContinue
Get-Job -ErrorAction SilentlyContinue |
Where-Object { $_.Name -eq $source } |
Remove-Job -Force -ErrorAction SilentlyContinue
}
}
function Start-Server {
Ensure-RuntimeLayout
Clear-LogEventSubscriptions
$existing = Get-ServerProcess
if ($null -ne $existing) {
Write-Host "Server already running. PID: $($existing.Id)"
return
}
$portOwner = Get-PortOwnerProcess
if ($null -ne $portOwner) {
Write-Host "Port $Port is already in use by PID $($portOwner.Id) ($($portOwner.ProcessName))."
Write-Host "Run '.\\scripts\\dev.ps1 stop' first, or free the port manually."
return
}
if (Test-Path $StdOutLogFile) {
Remove-Item $StdOutLogFile -ErrorAction SilentlyContinue
}
if (Test-Path $StdErrLogFile) {
Remove-Item $StdErrLogFile -ErrorAction SilentlyContinue
}
if (Test-Path $DevExe) {
Remove-Item $DevExe -ErrorAction SilentlyContinue
}
$env:GOCACHE = $GoCache
$env:GOTMPDIR = $GoTmp
& go build -buildvcs=false -o $DevExe ./cmd/server
if ($LASTEXITCODE -ne 0 -or -not (Test-Path $DevExe)) {
Write-Host "Build failed."
return
}
$startInfo = New-Object System.Diagnostics.ProcessStartInfo
$startInfo.FileName = $DevExe
$startInfo.WorkingDirectory = $ProjectRoot
$startInfo.UseShellExecute = $false
$startInfo.CreateNoWindow = $true
$startInfo.RedirectStandardOutput = $true
$startInfo.RedirectStandardError = $true
$startInfo.EnvironmentVariables["PORT"] = $Port
$process = New-Object System.Diagnostics.Process
$process.StartInfo = $startInfo
if (-not $process.Start()) {
Write-Host "Server failed to start."
return
}
Register-ObjectEvent -InputObject $process -EventName OutputDataReceived -SourceIdentifier $StdOutEventSource -Action {
if ($EventArgs.Data -ne $null) {
[System.IO.File]::AppendAllText($Event.MessageData.Path, $EventArgs.Data + [Environment]::NewLine)
}
} -MessageData @{ Path = $StdOutLogFile } | Out-Null
Register-ObjectEvent -InputObject $process -EventName ErrorDataReceived -SourceIdentifier $StdErrEventSource -Action {
if ($EventArgs.Data -ne $null) {
[System.IO.File]::AppendAllText($Event.MessageData.Path, $EventArgs.Data + [Environment]::NewLine)
}
} -MessageData @{ Path = $StdErrLogFile } | Out-Null
$process.BeginOutputReadLine()
$process.BeginErrorReadLine()
Set-Content -Path $PidFile -Value $process.Id
Start-Sleep -Seconds 2
$running = Get-ServerProcess
$portOwnerAfterStart = Get-PortOwnerProcess
if ($null -eq $running -or $null -eq $portOwnerAfterStart) {
Write-Host "Server failed to start."
if (Test-Path $StdOutLogFile) {
Get-Content $StdOutLogFile
}
if (Test-Path $StdErrLogFile) {
Get-Content $StdErrLogFile
}
return
}
Write-Host "Server started."
Write-Host "PID: $($portOwnerAfterStart.Id)"
Write-Host "URL: http://localhost:$Port"
}
function Stop-Server {
Clear-LogEventSubscriptions
$existing = Get-ServerProcess
if ($null -ne $existing) {
Stop-Process -Id $existing.Id -Force -ErrorAction SilentlyContinue
Remove-Item $PidFile -ErrorAction SilentlyContinue
Start-Sleep -Milliseconds 500
}
$portOwner = Get-PortOwnerProcess
if ($null -ne $portOwner) {
Stop-Process -Id $portOwner.Id -Force -ErrorAction SilentlyContinue
if (Test-Path $DevExe) {
Remove-Item $DevExe -ErrorAction SilentlyContinue
}
Write-Host "Server stopped. PID: $($portOwner.Id)"
return
}
if (Test-Path $DevExe) {
Remove-Item $DevExe -ErrorAction SilentlyContinue
}
Write-Host "Server is not running."
}
function Restart-Server {
Stop-Server
Start-Server
}
function Show-Status {
$portOwner = Get-PortOwnerProcess
if ($null -eq $portOwner) {
Write-Host "Server status: stopped"
return
}
Write-Host "Server status: running"
Write-Host "PID: $($portOwner.Id)"
Write-Host "Process: $($portOwner.ProcessName)"
Write-Host "URL: http://localhost:$Port"
}
function Show-Logs {
$hasStdOut = Test-Path $StdOutLogFile
$hasStdErr = Test-Path $StdErrLogFile
if (-not $hasStdOut -and -not $hasStdErr) {
Write-Host "No log file yet."
return
}
if ($hasStdOut) {
Write-Host "[stdout]"
Get-Content $StdOutLogFile
}
if ($hasStdErr) {
Write-Host "[stderr]"
Get-Content $StdErrLogFile
}
}
switch ($Action) {
"start" { Start-Server }
"stop" { Stop-Server }
"restart" { Restart-Server }
"status" { Show-Status }
"logs" { Show-Logs }
}