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
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 }
|
||
|
|
}
|