Ctrl K
ComunidadGuías…

dev-startall.ps1

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"

$rootDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$apiProject = Join-Path $rootDir "BoardOil.Api/BoardOil.Api.csproj"
$webDir = Join-Path $rootDir "BoardOil.Web"
$devDataDir = Join-Path $rootDir ".data/dev"
$mainDbPath = Join-Path $devDataDir "boardoil.dev.db"

function Require-Command {
    param([string]$Name)

    if (-not (Get-Command $Name -ErrorAction SilentlyContinue)) {
        throw "Required command '$Name' was not found on PATH."
    }
}

function Test-LocalPortInUse {
    param([int]$Port)

    try {
        $listener = Get-NetTCPConnection -LocalPort $Port -State Listen -ErrorAction Stop
        return $null -ne $listener
    }
    catch {
        return $false
    }
}

function Stop-ProcessListeningOnPort {
    param([int]$Port)

    $connections = @()
    try {
        $connections = @(Get-NetTCPConnection -LocalPort $Port -State Listen -ErrorAction Stop)
    }
    catch {
        return
    }

    if (-not $connections -or $connections.Count -eq 0) {
        return
    }

    $pids = @($connections | Select-Object -ExpandProperty OwningProcess -Unique)
    foreach ($pidValue in $pids) {
        if ($pidValue -le 0) {
            continue
        }

        try {
            $proc = Get-Process -Id $pidValue -ErrorAction Stop
            Write-Host "Stopping process on port ${Port}: $($proc.ProcessName) (PID $pidValue)"
            Stop-Process -Id $pidValue -Force -ErrorAction Stop
        }
        catch {
            Write-Host "Warning: failed to stop process PID $pidValue on port $Port."
        }
    }
}

function Stop-ExistingBoardOilApiProcesses {
    $apiProcesses = @()
    try {
        $apiProcesses = @(Get-Process -Name "BoardOil.Api" -ErrorAction Stop)
    }
    catch {
        return
    }

    foreach ($apiProcess in $apiProcesses) {
        try {
            Write-Host "Stopping existing BoardOil.Api process (PID $($apiProcess.Id))"
            Stop-Process -Id $apiProcess.Id -Force -ErrorAction Stop
        }
        catch {
            Write-Host "Warning: failed to stop BoardOil.Api process PID $($apiProcess.Id)."
        }
    }
}

function Get-CurrentBranchName {
    try {
        $branchName = (& git -C $rootDir rev-parse --abbrev-ref HEAD 2>$null).Trim()
    }
    catch {
        $branchName = ""
    }

    if ([string]::IsNullOrWhiteSpace($branchName)) {
        return "unknown"
    }

    if ($branchName -eq "HEAD") {
        try {
            $shortSha = (& git -C $rootDir rev-parse --short HEAD 2>$null).Trim()
        }
        catch {
            $shortSha = ""
        }

        if (-not [string]::IsNullOrWhiteSpace($shortSha)) {
            return "detached-$shortSha"
        }

        return "detached"
    }

    return $branchName
}

function Sanitize-BranchNameForPath {
    param([string]$BranchName)
    $sanitized = [Regex]::Replace($BranchName, "[^A-Za-z0-9._-]+", "_")
    $sanitized = [Regex]::Replace($sanitized, "_+", "_")
    $sanitized = $sanitized.Trim("_")
    if ([string]::IsNullOrWhiteSpace($sanitized)) {
        return "unknown"
    }
    return $sanitized
}

function Resolve-DevDatabasePath {
    $dbMode = if ($env:BOARDOIL_DEV_DB_MODE) { $env:BOARDOIL_DEV_DB_MODE } else { "branch" }
    $mainBranchName = if ($env:BOARDOIL_MAIN_BRANCH_NAME) { $env:BOARDOIL_MAIN_BRANCH_NAME } else { "main" }
    $currentBranchName = Get-CurrentBranchName

    if ($dbMode -eq "shared" -or $currentBranchName -eq $mainBranchName) {
        return $mainDbPath
    }

    $branchDirName = Sanitize-BranchNameForPath -BranchName $currentBranchName
    return (Join-Path $devDataDir "branches/$branchDirName/boardoil.dev.db")
}

function Seed-BranchDatabaseFromMainIfNeeded {
    param([string]$TargetDbPath)

    $seedFromMain = if ($env:BOARDOIL_DEV_DB_SEED_FROM_MAIN) { $env:BOARDOIL_DEV_DB_SEED_FROM_MAIN } else { "1" }

    if ($TargetDbPath -eq $mainDbPath) { return }
    if ($seedFromMain -ne "1") { return }
    if (Test-Path -LiteralPath $TargetDbPath) { return }
    if (-not (Test-Path -LiteralPath $mainDbPath)) { return }

    $targetDir = Split-Path -Parent $TargetDbPath
    New-Item -ItemType Directory -Path $targetDir -Force | Out-Null

    Write-Host "Seeding branch database from main database snapshot..."
    Copy-Item -LiteralPath $mainDbPath -Destination $TargetDbPath -Force
    foreach ($suffix in @("-wal", "-shm")) {
        $source = "$mainDbPath$suffix"
        $dest = "$TargetDbPath$suffix"
        if (Test-Path -LiteralPath $source) {
            Copy-Item -LiteralPath $source -Destination $dest -Force
        }
    }
}

Require-Command -Name "dotnet"
Require-Command -Name "npm"
Require-Command -Name "git"

if (-not (Test-Path -LiteralPath (Join-Path $webDir "node_modules"))) {
    throw "$webDir\node_modules is missing. Run 'cd BoardOil.Web; npm ci' first."
}

New-Item -ItemType Directory -Path $devDataDir -Force | Out-Null
$devDbPath = Resolve-DevDatabasePath
$devDbDir = Split-Path -Parent $devDbPath
New-Item -ItemType Directory -Path $devDbDir -Force | Out-Null
Seed-BranchDatabaseFromMainIfNeeded -TargetDbPath $devDbPath

Write-Host "Using development database: $devDbPath"

# Stop existing dev listeners early so API binaries are not locked during build.
Stop-ExistingBoardOilApiProcesses
Start-Sleep -Milliseconds 300
if (Test-LocalPortInUse -Port 5000) {
    Stop-ProcessListeningOnPort -Port 5000
    Start-Sleep -Milliseconds 500
}
if (Test-LocalPortInUse -Port 5173) {
    Stop-ProcessListeningOnPort -Port 5173
    Start-Sleep -Milliseconds 300
}

Write-Host "Building API ..."
& dotnet build $apiProject -maxcpucount:1 -nodeReuse:false

$logsDir = Join-Path $rootDir ".data/logs"
New-Item -ItemType Directory -Path $logsDir -Force | Out-Null
$apiOutLog = Join-Path $logsDir "api.out.log"
$apiErrLog = Join-Path $logsDir "api.err.log"
$webOutLog = Join-Path $logsDir "web.out.log"
$webErrLog = Join-Path $logsDir "web.err.log"

function Initialize-LogFilePath {
    param([string]$Path)

    if (-not (Test-Path -LiteralPath $Path)) {
        return $Path
    }

    try {
        Clear-Content -LiteralPath $Path -ErrorAction Stop
        return $Path
    }
    catch {
        $timestamp = Get-Date -Format "yyyyMMdd-HHmmss"
        $directory = Split-Path -Parent $Path
        $filename = [System.IO.Path]::GetFileNameWithoutExtension($Path)
        $extension = [System.IO.Path]::GetExtension($Path)
        return (Join-Path $directory "$filename.$timestamp$extension")
    }
}

$apiOutLog = Initialize-LogFilePath -Path $apiOutLog
$apiErrLog = Initialize-LogFilePath -Path $apiErrLog
$webOutLog = Initialize-LogFilePath -Path $webOutLog
$webErrLog = Initialize-LogFilePath -Path $webErrLog

$apiScriptPath = Join-Path $logsDir "run-api.ps1"
$webScriptPath = Join-Path $logsDir "run-web.ps1"

$apiScript = @"
Set-StrictMode -Version Latest
`$ErrorActionPreference = "Stop"
`$env:ASPNETCORE_ENVIRONMENT = "Development"
`$env:DOTNET_ENVIRONMENT = "Development"
`$env:ConnectionStrings__BoardOil = "Data Source=$devDbPath"
dotnet run --no-launch-profile --no-build --project "$apiProject" --urls "http://127.0.0.1:5000"
"@

$webScript = @"
Set-StrictMode -Version Latest
`$ErrorActionPreference = "Stop"
Set-Location -LiteralPath "$webDir"
npm run sync:third-party-licences
if (-not `$env:VITE_BO_VERSION) {
    `$env:VITE_BO_VERSION = node -p "require('./package.json').version"
}
npm exec vite -- --port 5173
"@

Set-Content -LiteralPath $apiScriptPath -Value $apiScript -Encoding UTF8
Set-Content -LiteralPath $webScriptPath -Value $webScript -Encoding UTF8

Write-Host "Starting API on http://127.0.0.1:5000 ..."
if (Test-LocalPortInUse -Port 5000) {
    Stop-ProcessListeningOnPort -Port 5000
    Start-Sleep -Milliseconds 500
}
$apiProcess = Start-Process -FilePath "powershell" -ArgumentList @("-NoProfile", "-ExecutionPolicy", "Bypass", "-File", $apiScriptPath) -RedirectStandardOutput $apiOutLog -RedirectStandardError $apiErrLog -WindowStyle Hidden -PassThru

Write-Host "Starting frontend on http://localhost:5173 ..."
$webProcess = Start-Process -FilePath "powershell" -ArgumentList @("-NoProfile", "-ExecutionPolicy", "Bypass", "-File", $webScriptPath) -RedirectStandardOutput $webOutLog -RedirectStandardError $webErrLog -WindowStyle Hidden -PassThru

Write-Host "Logs:"
Write-Host "  API stdout: $apiOutLog"
Write-Host "  API stderr: $apiErrLog"
Write-Host "  Web stdout: $webOutLog"
Write-Host "  Web stderr: $webErrLog"

try {
    while ($true) {
        Start-Sleep -Seconds 1
        $apiExited = $null -ne $apiProcess -and $apiProcess.HasExited
        $webExited = $null -ne $webProcess -and $webProcess.HasExited

        if ($apiExited -or $webExited) {
            if ($apiExited) {
                Write-Host "API process exited with code $($apiProcess.ExitCode)."
            }
            if ($webExited) {
                Write-Host "Web process exited with code $($webProcess.ExitCode)."
            }
            break
        }
    }
}
finally {
    foreach ($proc in @($apiProcess, $webProcess)) {
        if ($null -ne $proc -and -not $proc.HasExited) {
            Stop-Process -Id $proc.Id -Force -ErrorAction SilentlyContinue
        }
    }
}