From 49aad7311c68459d11de30cb4d1c90b4c6201a5e Mon Sep 17 00:00:00 2001 From: kdletters Date: Sun, 3 May 2026 14:18:27 +0800 Subject: [PATCH] Add local Jenkins controller watchdog --- .../PRODUCTION_DEPLOYMENT_PLAN_2026-05-02.md | 3 + scripts/build-production-release.sh | 1 + .../jenkins-local-controller-watchdog.ps1 | 95 +++++++++++++++++++ 3 files changed, 99 insertions(+) create mode 100644 scripts/deploy/jenkins-local-controller-watchdog.ps1 diff --git a/docs/technical/PRODUCTION_DEPLOYMENT_PLAN_2026-05-02.md b/docs/technical/PRODUCTION_DEPLOYMENT_PLAN_2026-05-02.md index ae8e6639..d4c6ad3e 100644 --- a/docs/technical/PRODUCTION_DEPLOYMENT_PLAN_2026-05-02.md +++ b/docs/technical/PRODUCTION_DEPLOYMENT_PLAN_2026-05-02.md @@ -223,6 +223,8 @@ Jenkins 可运行在 Windows 或其他机器上,本机 Windows 只作为人工 本地反向隧道脚本不内置目标机地址;注册 Windows 计划任务时必须显式传入 `-RemoteHost `,真实 IP 或主机名只保存在本地计划任务配置中,不提交到 Git。 +当 Jenkins controller 以本地 Windows `java -jar jenkins.war` 方式运行时,使用 `scripts/deploy/jenkins-local-controller-watchdog.ps1` 作为本地守护脚本。该脚本只保存本机 Java、`jenkins.war`、`JENKINS_HOME` 和端口路径,不保存 Jenkins 账号、密码、token 或 agent secret;注册 Windows 计划任务后,脚本会在登录后检查 `8080` 是否已有 Jenkins 监听,若已有则监控现有 PID,若进程退出或端口空闲则重新启动 Jenkins,并固定 `--agentPort=50000` 供远端 inbound agent 连接。 + 首次迁移示例: ```bash @@ -579,6 +581,7 @@ WASM_SOURCE="${CARGO_TARGET_DIR}/wasm32-unknown-unknown/release/spacetime_module - [x] `scripts/deploy/maintenance-on.sh` - [x] `scripts/deploy/maintenance-off.sh` - [x] `scripts/deploy/maintenance-status.sh` +- [x] `scripts/deploy/jenkins-local-controller-watchdog.ps1` - [x] `scripts/deploy/jenkins-agent-reverse-tunnel.ps1` - [x] `scripts/deploy/jenkins-inbound-agent-start.sh` - [x] `scripts/deploy/install-jenkins-inbound-agent.sh` diff --git a/scripts/build-production-release.sh b/scripts/build-production-release.sh index adf46c33..03cd25cb 100644 --- a/scripts/build-production-release.sh +++ b/scripts/build-production-release.sh @@ -373,6 +373,7 @@ cp "${SCRIPT_DIR}/deploy/maintenance-status.sh" "${TARGET_DIR}/scripts/maintenan cp "${SCRIPT_DIR}/deploy/jenkins-inbound-agent-start.sh" "${TARGET_DIR}/scripts/jenkins-inbound-agent-start.sh" cp "${SCRIPT_DIR}/deploy/install-jenkins-inbound-agent.sh" "${TARGET_DIR}/scripts/install-jenkins-inbound-agent.sh" cp "${SCRIPT_DIR}/deploy/jenkins-agent-reverse-tunnel.ps1" "${TARGET_DIR}/scripts/jenkins-agent-reverse-tunnel.ps1" +cp "${SCRIPT_DIR}/deploy/jenkins-local-controller-watchdog.ps1" "${TARGET_DIR}/scripts/jenkins-local-controller-watchdog.ps1" chmod +x \ "${TARGET_DIR}/scripts/maintenance-on.sh" \ "${TARGET_DIR}/scripts/maintenance-off.sh" \ diff --git a/scripts/deploy/jenkins-local-controller-watchdog.ps1 b/scripts/deploy/jenkins-local-controller-watchdog.ps1 new file mode 100644 index 00000000..7b9ff16b --- /dev/null +++ b/scripts/deploy/jenkins-local-controller-watchdog.ps1 @@ -0,0 +1,95 @@ +param( + [string]$JavaPath = "$env:USERPROFILE\jenkins-local\jdk-21\jdk-21.0.11+10\bin\java.exe", + [string]$JenkinsWar = "$env:USERPROFILE\jenkins-local\jenkins.war", + [string]$JenkinsHome = "$env:USERPROFILE\.jenkins", + [int]$HttpPort = 8080, + [int]$AgentPort = 50000, + [int]$RestartDelaySeconds = 10 +) + +$ErrorActionPreference = "Stop" + +function Write-Log { + param([string]$Message) + $timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss" + Write-Output "[$timestamp] $Message" +} + +function Get-ListeningProcessId { + param([int]$Port) + $line = netstat -ano | Select-String -Pattern "LISTENING\s+(\d+)$" | Where-Object { + $_.Line -match "[:.]$Port\s+" + } | Select-Object -First 1 + + if (-not $line) { + return $null + } + + if ($line.Line -match "LISTENING\s+(\d+)$") { + return [int]$Matches[1] + } + + return $null +} + +function Test-JenkinsProcess { + param([int]$ProcessId) + if (-not $ProcessId) { + return $false + } + + $process = Get-CimInstance Win32_Process -Filter "ProcessId = $ProcessId" -ErrorAction SilentlyContinue + if (-not $process) { + return $false + } + + return ($process.CommandLine -like "*jenkins.war*") +} + +if (-not (Test-Path -LiteralPath $JavaPath)) { + throw "Java path not found: $JavaPath" +} + +if (-not (Test-Path -LiteralPath $JenkinsWar)) { + throw "Jenkins war not found: $JenkinsWar" +} + +New-Item -ItemType Directory -Force -Path $JenkinsHome | Out-Null + +while ($true) { + $listeningPid = Get-ListeningProcessId -Port $HttpPort + + if ($listeningPid -and (Test-JenkinsProcess -ProcessId $listeningPid)) { + Write-Log "Jenkins is already listening on port $HttpPort with pid $listeningPid; monitoring it." + try { + Wait-Process -Id $listeningPid + } catch { + Write-Log "Existing Jenkins process wait failed: $($_.Exception.Message)" + } + } elseif ($listeningPid) { + Write-Log "Port $HttpPort is occupied by pid $listeningPid, but it is not Jenkins. Retrying in ${RestartDelaySeconds}s." + Start-Sleep -Seconds $RestartDelaySeconds + continue + } else { + $arguments = @( + "-Djenkins.install.runSetupWizard=false", + "-jar", $JenkinsWar, + "--httpPort=$HttpPort", + "--agentPort=$AgentPort" + ) + + Write-Log "Starting local Jenkins controller on port $HttpPort." + $previousJenkinsHome = $env:JENKINS_HOME + $env:JENKINS_HOME = $JenkinsHome + $process = Start-Process -FilePath $JavaPath -ArgumentList $arguments -WorkingDirectory (Split-Path -Parent $JenkinsWar) -NoNewWindow -PassThru + $env:JENKINS_HOME = $previousJenkinsHome + try { + Wait-Process -Id $process.Id + } catch { + Write-Log "Started Jenkins process wait failed: $($_.Exception.Message)" + } + } + + Write-Log "Jenkins controller stopped; retrying in ${RestartDelaySeconds}s." + Start-Sleep -Seconds $RestartDelaySeconds +}