修复CI门禁三因:runner掉线致12个job零日志、L0探测回归全灭、gitlint在CI从不执行
1) runner 掉线:run 16~21 共 12 个 job 零日志。gitea-runner v4 的
daemon 无服务支持、首次 Declare 失败即退出进程,Gitea/机器重启
一次 CI 就静默停摆;叠加 cancel-in-progress 后症状是"任务凭空
消失"。新增 scripts/ci_runner_bootstrap.ps1:等 Gitea 就绪 +
崩溃自拉起看门狗 + 开机计划任务 + -SetEnv 写 MACHINE 级 DTU_*。
2) L0 自 33a4aa4 起在 runner 上必挂:Python 候选被收窄成 PATH python
(msys64,无 pre-commit) + 仓库 .venv(gitignore),cppcheck 目录兜底
亦被删,而机器级 DTU_* 从未设置。探测收进 ci_find_python.ps1
(两 job 共用、可离线自测、恢复机器级安装兜底)。
3) gitlint 声明 stages:[commit-msg],而 --all-files 只跑 pre-commit
stage → 17 个钩子定义 CI 只报 16 个且全绿。新增
ci_gitlint_range.ps1 逐条校验本次范围,ci_local.py 增 L0b 段。
另:GITHUB_ENV/PATH/OUTPUT 写入显式 -Encoding ascii(PS 5.1 的
Out-File -Append 默认 UTF-16LE,会把带 NUL 的行追加进 UTF-8 文件)
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Pick a Python interpreter that actually works, for the CI jobs (and for
|
||||
local use). Prints diagnostics with Write-Host and emits ONLY the chosen
|
||||
interpreter path on the success stream, so a caller can capture it:
|
||||
|
||||
$py = & './scripts/ci_find_python.ps1' -NeedPreCommit -WriteEnv
|
||||
|
||||
.DESCRIPTION
|
||||
Why this file exists (2026-10-11 CI incident):
|
||||
|
||||
The DTU_* env-var refactor moved the interpreter probe inline into
|
||||
.gitea/workflows/ci.yml and narrowed it to two candidates
|
||||
(PATH python + repo .venv). On the Windows runner BOTH are dead ends:
|
||||
|
||||
* PATH python for the SYSTEM account is C:\msys64\ucrt64\bin\python.exe,
|
||||
which has no pre-commit module;
|
||||
* the repo .venv is gitignored, so it does not exist in a fresh runner
|
||||
workspace at all.
|
||||
|
||||
The interpreter that does work (a machine-wide install such as
|
||||
C:\ProgramData\anaconda3\python.exe) was no longer probed, and no
|
||||
machine-level DTU_PRECOMMIT_PYTHON had ever been set -> every L0 job died
|
||||
in "environment check" before running a single hook.
|
||||
|
||||
So discovery lives here now: one implementation, shared by both CI jobs and
|
||||
runnable offline, and it keeps the well-known machine-wide install
|
||||
locations as fallbacks.
|
||||
|
||||
Probe order (see the DTU_* local-tool-path plan under docs/ -- this file is
|
||||
kept pure ASCII on purpose: PS 5.1 decodes BOM-less files as CP936):
|
||||
1. DTU_PRECOMMIT_PYTHON -- explicit override. If it is set but the file
|
||||
is missing, or it cannot satisfy the requirement, this script FAILS
|
||||
with the variable name and does NOT silently fall back.
|
||||
2. python on PATH
|
||||
3. <repo>\.venv\Scripts\python.exe
|
||||
4. well-known machine-wide installs (Program Files / ProgramData /
|
||||
%LOCALAPPDATA%\Programs\Python), newest version first
|
||||
|
||||
.PARAMETER NeedPreCommit
|
||||
Require the interpreter to expose the pre-commit module (L0 needs it).
|
||||
Probed with `-s` (no user site-packages) on purpose: the runner service
|
||||
runs as SYSTEM, which cannot see a USER-scope site install at all, so only
|
||||
an interpreter that works without user-site is accepted.
|
||||
|
||||
.PARAMETER WriteEnv
|
||||
Append PYTHON=<path> to $env:GITHUB_ENV so later steps can use $env:PYTHON.
|
||||
Written as ASCII: Out-File's PS 5.1 default is UTF-16LE, which appends
|
||||
foreign-encoded lines to a file other steps also append to.
|
||||
|
||||
.OUTPUTS
|
||||
The chosen python.exe path (single line, success stream).
|
||||
|
||||
.NOTES
|
||||
Keep this file pure ASCII -- Windows PowerShell 5.1 decodes BOM-less files
|
||||
as CP936, and non-ASCII bytes break string quoting (same trap as ci.yml).
|
||||
Exit codes: 0 = found, 2 = no usable interpreter / bad DTU_* override.
|
||||
#>
|
||||
param(
|
||||
[switch]$NeedPreCommit,
|
||||
[switch]$WriteEnv
|
||||
)
|
||||
|
||||
# Native probes write to stderr; the runner injects $ErrorActionPreference
|
||||
# ='Stop', which would turn that into a terminating NativeCommandError and
|
||||
# abort the probe loop before the next candidate is tried.
|
||||
$ErrorActionPreference = 'Continue'
|
||||
|
||||
function Test-Interpreter {
|
||||
param([string]$Exe, [bool]$RequirePreCommit)
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($Exe)) { return $false }
|
||||
if (-not (Test-Path -LiteralPath $Exe -PathType Leaf)) { return $false }
|
||||
|
||||
$saved = $ErrorActionPreference
|
||||
$ErrorActionPreference = 'Continue'
|
||||
try {
|
||||
if ($RequirePreCommit) {
|
||||
& $Exe -s -m pre_commit --version 2>&1 | Out-Null
|
||||
}
|
||||
else {
|
||||
& $Exe --version 2>&1 | Out-Null
|
||||
}
|
||||
return ($LASTEXITCODE -eq 0)
|
||||
}
|
||||
catch {
|
||||
return $false
|
||||
}
|
||||
finally {
|
||||
$ErrorActionPreference = $saved
|
||||
}
|
||||
}
|
||||
|
||||
$repoRoot = Split-Path -Parent $PSScriptRoot
|
||||
$require = [bool]$NeedPreCommit
|
||||
$what = if ($require) { 'pre-commit' } else { 'a runnable interpreter' }
|
||||
|
||||
# --- 1. explicit override: honour it or fail loudly -------------------------
|
||||
$explicit = $env:DTU_PRECOMMIT_PYTHON
|
||||
if (-not [string]::IsNullOrWhiteSpace($explicit)) {
|
||||
$explicit = $explicit.Trim()
|
||||
if (-not (Test-Path -LiteralPath $explicit -PathType Leaf)) {
|
||||
Write-Host "::error::DTU_PRECOMMIT_PYTHON is set but does not exist: $explicit"
|
||||
Write-Host '::error::fix the variable (full path to a python.exe) or unset it to use auto-discovery'
|
||||
exit 2
|
||||
}
|
||||
if (-not (Test-Interpreter -Exe $explicit -RequirePreCommit $require)) {
|
||||
Write-Host "::error::DTU_PRECOMMIT_PYTHON does not provide $what (probed without user-site, i.e. SYSTEM-visible): $explicit"
|
||||
Write-Host '::error::fix the variable, or unset it to use auto-discovery'
|
||||
exit 2
|
||||
}
|
||||
Write-Host "using python (DTU_PRECOMMIT_PYTHON): $explicit"
|
||||
if ($WriteEnv -and $env:GITHUB_ENV) {
|
||||
"PYTHON=$explicit" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding ascii
|
||||
}
|
||||
Write-Output $explicit
|
||||
exit 0
|
||||
}
|
||||
|
||||
# --- 2..4 auto-discovery ----------------------------------------------------
|
||||
$programFiles = $env:ProgramFiles
|
||||
$programFilesX86 = ${env:ProgramFiles(x86)}
|
||||
if ([string]::IsNullOrWhiteSpace($programFiles)) { $programFiles = 'C:\Program Files' }
|
||||
if ([string]::IsNullOrWhiteSpace($programFilesX86)) { $programFilesX86 = 'C:\Program Files (x86)' }
|
||||
|
||||
$candidates = @()
|
||||
|
||||
$onPath = Get-Command python -ErrorAction SilentlyContinue
|
||||
if ($onPath) { $candidates += $onPath.Source }
|
||||
|
||||
$candidates += (Join-Path $repoRoot '.venv\Scripts\python.exe')
|
||||
|
||||
# Newest first: a fresh install is the usual intent.
|
||||
foreach ($ver in @('Python313', 'Python312', 'Python311', 'Python310')) {
|
||||
$candidates += (Join-Path $programFiles "$ver\python.exe")
|
||||
$candidates += (Join-Path $programFilesX86 "$ver\python.exe")
|
||||
if ($env:LOCALAPPDATA) {
|
||||
$candidates += (Join-Path $env:LOCALAPPDATA "Programs\Python\$ver\python.exe")
|
||||
}
|
||||
}
|
||||
|
||||
# Machine-wide distributions (these are what the runner actually had).
|
||||
if ($env:ProgramData) {
|
||||
foreach ($dist in @('anaconda3', 'miniconda3')) {
|
||||
$candidates += (Join-Path $env:ProgramData "$dist\python.exe")
|
||||
}
|
||||
}
|
||||
$candidates += 'C:\ProgramData\anaconda3\python.exe'
|
||||
$candidates += 'C:\ProgramData\miniconda3\python.exe'
|
||||
|
||||
$tried = New-Object System.Collections.Generic.List[string]
|
||||
$chosen = $null
|
||||
foreach ($c in $candidates) {
|
||||
if ([string]::IsNullOrWhiteSpace($c)) { continue }
|
||||
if ($tried.Contains($c)) { continue }
|
||||
$tried.Add($c) | Out-Null
|
||||
if (Test-Interpreter -Exe $c -RequirePreCommit $require) { $chosen = $c; break }
|
||||
}
|
||||
|
||||
if (-not $chosen) {
|
||||
Write-Host "::error::no Python providing $what found (tried `$DTU_PRECOMMIT_PYTHON`, PATH python, repo .venv, and the well-known machine-wide installs)"
|
||||
foreach ($t in $tried) { Write-Host " tried: $t" }
|
||||
Write-Host '::error::note: the runner service runs as SYSTEM, so a USER-scope `pip install --user pre-commit` is invisible to it'
|
||||
Write-Host '::error::fix: set a MACHINE-level DTU_PRECOMMIT_PYTHON (see scripts/ci_runner_bootstrap.ps1 -SetEnv), or install pre-commit into a machine-wide interpreter'
|
||||
exit 2
|
||||
}
|
||||
|
||||
Write-Host "using python (auto-discovered): $chosen"
|
||||
# Out-Host, NOT the pipeline: a native command's stdout joins the success
|
||||
# stream, which would pollute the single-line path the caller captures.
|
||||
& $chosen --version 2>&1 | Out-Host
|
||||
if ($require) { & $chosen -s -m pre_commit --version 2>&1 | Out-Host }
|
||||
|
||||
if ($WriteEnv -and $env:GITHUB_ENV) {
|
||||
"PYTHON=$chosen" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding ascii
|
||||
}
|
||||
|
||||
Write-Output $chosen
|
||||
exit 0
|
||||
Reference in New Issue
Block a user