forked from akai/readest
d4136c53c7
# Conflicts: # apps/readest-app/src/app/library/components/BookshelfItem.tsx # apps/readest-app/src/app/review-editor/ReviewEditorLauncher.tsx # apps/readest-app/src/services/reviewEditorService.ts # apps/readest-app/tools/epub-review-editor/MAINTENANCE.md # apps/readest-app/tools/epub-review-editor/README.md # apps/readest-app/tools/epub-review-editor/static/index.html # apps/readest-app/tools/epub-review-editor/version.py # scripts/open-readest-latest.ps1
266 lines
8.8 KiB
PowerShell
266 lines
8.8 KiB
PowerShell
param(
|
|
[string]$Remote = "akai-tools",
|
|
[string]$Branch = "codex/readest-web-translation-base",
|
|
[switch]$Web,
|
|
[switch]$Desktop,
|
|
[switch]$SkipPull,
|
|
[switch]$CheckOnly,
|
|
[switch]$KeepRunning
|
|
)
|
|
|
|
if ($Web -and $Desktop) {
|
|
throw "-Web and -Desktop cannot be used together. Web is the default mode."
|
|
}
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
|
|
|
|
$RepoRoot = (Resolve-Path (Join-Path $PSScriptRoot "..")).Path
|
|
|
|
function Write-Step {
|
|
param([string]$Message)
|
|
Write-Host ""
|
|
Write-Host "==> $Message" -ForegroundColor Cyan
|
|
}
|
|
|
|
function Invoke-Checked {
|
|
param(
|
|
[string]$Command,
|
|
[string[]]$Arguments,
|
|
[string]$WorkingDirectory = $RepoRoot
|
|
)
|
|
|
|
Push-Location $WorkingDirectory
|
|
try {
|
|
& $Command @Arguments
|
|
if ($LASTEXITCODE -ne 0) {
|
|
throw "$Command $($Arguments -join ' ') failed with exit code $LASTEXITCODE."
|
|
}
|
|
} finally {
|
|
Pop-Location
|
|
}
|
|
}
|
|
|
|
function Add-PathIfExists {
|
|
param([string]$PathToAdd)
|
|
if ($PathToAdd -and (Test-Path -LiteralPath $PathToAdd)) {
|
|
$script:PathParts += $PathToAdd
|
|
}
|
|
}
|
|
|
|
function Test-SubmoduleCheckout {
|
|
param([string]$RelativePath)
|
|
|
|
$fullPath = Join-Path $RepoRoot $RelativePath
|
|
if (-not (Test-Path -LiteralPath (Join-Path $fullPath ".git"))) {
|
|
return $false
|
|
}
|
|
& git -C $fullPath rev-parse --is-inside-work-tree 2>$null | Out-Null
|
|
return $LASTEXITCODE -eq 0
|
|
}
|
|
|
|
function Stop-OldReadestDev {
|
|
if ($KeepRunning) {
|
|
return
|
|
}
|
|
|
|
Write-Step "Stopping old Readest desktop/dev processes"
|
|
Get-Process -Name "Readest" -ErrorAction SilentlyContinue | Stop-Process -Force -ErrorAction SilentlyContinue
|
|
|
|
$repoLower = $RepoRoot.ToLowerInvariant()
|
|
$currentPid = $PID
|
|
Get-CimInstance Win32_Process |
|
|
Where-Object {
|
|
$_.ProcessId -ne $currentPid -and
|
|
$_.CommandLine -and
|
|
$_.CommandLine.ToLowerInvariant().Contains($repoLower) -and
|
|
($_.CommandLine -match "tauri\s+dev|next\s+dev|@readest/readest-app")
|
|
} |
|
|
ForEach-Object {
|
|
Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue
|
|
}
|
|
|
|
$portConnection = Get-NetTCPConnection -State Listen -LocalPort 3000 -ErrorAction SilentlyContinue |
|
|
Select-Object -First 1
|
|
if (-not $portConnection) {
|
|
return
|
|
}
|
|
|
|
$ownerPid = $portConnection.OwningProcess
|
|
$processLineage = @()
|
|
$lineagePid = $ownerPid
|
|
while ($lineagePid -and $processLineage.Count -lt 8) {
|
|
$lineageProcess = Get-CimInstance Win32_Process -Filter "ProcessId = $lineagePid" -ErrorAction SilentlyContinue
|
|
if (-not $lineageProcess) {
|
|
break
|
|
}
|
|
$processLineage += $lineageProcess
|
|
$lineagePid = $lineageProcess.ParentProcessId
|
|
}
|
|
|
|
$isReadestDev = $processLineage | Where-Object {
|
|
$_.Name -ieq "Readest.exe" -or
|
|
($_.CommandLine -and
|
|
$_.CommandLine -match "(?i)readest" -and
|
|
$_.CommandLine -match "(?i)next(.+?)dev|start-server\.js|@readest/readest-app|tauri(.+?)dev")
|
|
}
|
|
if (-not $isReadestDev) {
|
|
$owner = $processLineage | Select-Object -First 1
|
|
$ownerDescription = if ($owner.CommandLine) { $owner.CommandLine } else { "$($owner.Name) (PID $ownerPid)" }
|
|
throw "Port 3000 is occupied by an unrelated process: $ownerDescription. Stop it or launch Readest after freeing port 3000."
|
|
}
|
|
|
|
Write-Host "Stopping old Readest/Next listener on port 3000 (PID $ownerPid)." -ForegroundColor Yellow
|
|
Stop-Process -Id $ownerPid -Force -ErrorAction SilentlyContinue
|
|
for ($attempt = 0; $attempt -lt 20; $attempt++) {
|
|
Start-Sleep -Milliseconds 250
|
|
if (-not (Get-NetTCPConnection -State Listen -LocalPort 3000 -ErrorAction SilentlyContinue)) {
|
|
return
|
|
}
|
|
}
|
|
throw "Port 3000 is still occupied after stopping the old Readest/Next process. Close the old development terminal and try again."
|
|
}
|
|
|
|
function Assert-CleanWorktree {
|
|
$status = @(& git -C $RepoRoot status --porcelain --untracked-files=no --ignore-submodules=dirty)
|
|
if ($status) {
|
|
Write-Host ""
|
|
Write-Host "Local changes were found. The launcher will not pull or overwrite them:" -ForegroundColor Yellow
|
|
$status | ForEach-Object { Write-Host $_ }
|
|
throw "Commit, stash, or discard local changes before launching the latest organization version."
|
|
}
|
|
}
|
|
|
|
try {
|
|
$PathParts = @()
|
|
$runtimeRoot = Join-Path $env:USERPROFILE ".cache\codex-runtimes\codex-primary-runtime\dependencies"
|
|
Add-PathIfExists "C:\w64devkit\bin"
|
|
Add-PathIfExists (Join-Path $runtimeRoot "bin")
|
|
Add-PathIfExists (Join-Path $runtimeRoot "node\bin")
|
|
Add-PathIfExists (Join-Path $env:USERPROFILE ".cargo\bin")
|
|
$env:Path = (($PathParts + ($env:Path -split ";" | Where-Object { $_ })) -join ";")
|
|
$env:RUSTUP_TOOLCHAIN = "stable-x86_64-pc-windows-gnu"
|
|
|
|
$requiredCommands = @("git", "pnpm")
|
|
if ($Desktop) {
|
|
$requiredCommands += "cargo"
|
|
}
|
|
foreach ($command in $requiredCommands) {
|
|
if (-not (Get-Command $command -ErrorAction SilentlyContinue)) {
|
|
throw "Required command not found: $command"
|
|
}
|
|
}
|
|
|
|
Write-Step "Readest latest launcher"
|
|
Write-Host "Repository: $RepoRoot"
|
|
Write-Host "Remote: $Remote"
|
|
Write-Host "Branch: $Branch"
|
|
Write-Host "Mode: $(if ($Desktop) { 'Desktop (Tauri)' } else { 'Web (Fast Refresh)' })"
|
|
|
|
$beforeHead = (& git -C $RepoRoot rev-parse HEAD).Trim()
|
|
|
|
if (-not $SkipPull) {
|
|
Assert-CleanWorktree
|
|
|
|
Write-Step "Fetching latest code from organization remote"
|
|
Invoke-Checked "git" @("-C", $RepoRoot, "fetch", "--prune", $Remote)
|
|
|
|
$currentBranch = (& git -C $RepoRoot branch --show-current).Trim()
|
|
if ($currentBranch -ne $Branch) {
|
|
Write-Step "Switching to $Branch"
|
|
& git -C $RepoRoot show-ref --verify --quiet "refs/heads/$Branch"
|
|
if ($LASTEXITCODE -eq 0) {
|
|
Invoke-Checked "git" @("-C", $RepoRoot, "switch", $Branch)
|
|
} else {
|
|
Invoke-Checked "git" @("-C", $RepoRoot, "switch", "--track", "-c", $Branch, "$Remote/$Branch")
|
|
}
|
|
}
|
|
|
|
Write-Step "Fast-forwarding local branch"
|
|
Invoke-Checked "git" @("-C", $RepoRoot, "pull", "--ff-only", $Remote, $Branch)
|
|
} else {
|
|
Write-Step "Skipping git pull by request"
|
|
}
|
|
|
|
$afterHead = (& git -C $RepoRoot rev-parse HEAD).Trim()
|
|
|
|
Write-Step "Updating submodules"
|
|
$requiredSubmodules = @(
|
|
"packages/foliate-js",
|
|
"packages/simplecc-wasm",
|
|
"packages/js-mdict"
|
|
)
|
|
if ($Desktop) {
|
|
$requiredSubmodules += "apps/readest-app/src-tauri/plugins/tauri-plugin-turso"
|
|
}
|
|
$missingSubmodules = @($requiredSubmodules | Where-Object { -not (Test-SubmoduleCheckout $_) })
|
|
if ($missingSubmodules.Count -gt 0) {
|
|
$submoduleArgs = @(
|
|
"-C", $RepoRoot,
|
|
"-c", "http.version=HTTP/1.1",
|
|
"-c", "submodule.fetchJobs=1",
|
|
"submodule", "update", "--init", "--depth", "1", "--"
|
|
) + $missingSubmodules
|
|
Invoke-Checked "git" $submoduleArgs
|
|
} else {
|
|
Write-Host "Required submodules are already initialized."
|
|
}
|
|
|
|
$changedFiles = @()
|
|
if ($beforeHead -ne $afterHead) {
|
|
$changedFiles = (& git -C $RepoRoot diff --name-only $beforeHead $afterHead)
|
|
}
|
|
|
|
$needsInstall =
|
|
-not (Test-Path -LiteralPath (Join-Path $RepoRoot "node_modules")) -or
|
|
($changedFiles -contains "pnpm-lock.yaml") -or
|
|
($changedFiles -contains "package.json") -or
|
|
($changedFiles -contains "apps/readest-app/package.json")
|
|
|
|
if ($needsInstall) {
|
|
Write-Step "Installing/updating dependencies"
|
|
Invoke-Checked "pnpm" @("install", "--frozen-lockfile")
|
|
}
|
|
|
|
$vendorSentinel = Join-Path $RepoRoot "apps\readest-app\public\vendor\pdfjs\pdf.min.mjs"
|
|
if ($needsInstall -or -not (Test-Path -LiteralPath $vendorSentinel)) {
|
|
Write-Step "Preparing browser runtime assets"
|
|
Invoke-Checked "pnpm" @("--filter", "@readest/readest-app", "setup-vendors")
|
|
}
|
|
|
|
if ($Desktop) {
|
|
$tursoRoot = Join-Path $RepoRoot "apps\readest-app\src-tauri\plugins\tauri-plugin-turso"
|
|
$tursoOutput = Join-Path $tursoRoot "dist\index.js"
|
|
if (-not (Test-Path -LiteralPath $tursoOutput)) {
|
|
Write-Step "Building Tauri plugin dependencies"
|
|
Invoke-Checked "pnpm" @("--dir", $tursoRoot, "install", "--ignore-workspace", "--frozen-lockfile")
|
|
Invoke-Checked "pnpm" @("--dir", $tursoRoot, "build")
|
|
}
|
|
}
|
|
|
|
if ($CheckOnly) {
|
|
Write-Step "Launcher check complete"
|
|
exit 0
|
|
}
|
|
|
|
Stop-OldReadestDev
|
|
|
|
if ($Desktop) {
|
|
Write-Step "Starting Readest desktop with the latest code"
|
|
Write-Host "Close the Readest window or press Ctrl+C here to stop the dev server."
|
|
Invoke-Checked "pnpm" @("--filter", "@readest/readest-app", "tauri", "dev")
|
|
} else {
|
|
Write-Step "Starting Readest web development server"
|
|
Write-Host "Open http://localhost:3000. Saved React and CSS changes appear through Fast Refresh."
|
|
Invoke-Checked "pnpm" @("--filter", "@readest/readest-app", "dev-web")
|
|
}
|
|
} catch {
|
|
Write-Host ""
|
|
Write-Host "Readest launcher failed:" -ForegroundColor Red
|
|
Write-Host $_.Exception.Message -ForegroundColor Red
|
|
Write-Host ""
|
|
Write-Host "Press Enter to close this window."
|
|
[void][Console]::ReadLine()
|
|
exit 1
|
|
}
|