fix(ci): 去掉 actions/checkout 外网依赖,MSVC 探测改 vswhere 并钉住工具集世代
两个 job 的第 1 步原先是 `uses: actions/checkout@v4`。`uses:` 的解析发生在 Prepare all required actions 阶段——先于任何步骤、无条件联网(默认 github.com), 且每个 job 各自解析进自己的 .cache/act/<hash>、跨 job 不复用。这条链在本机实测 不稳:run 36(10-21)/ 41(10-25)/ 42(10-26)三次都是 ~45 s 后整 job 死亡、 零钩子输出,而同一分钟手工 curl 同一地址返回 200——手工可达 ≠ CI 可用(解析器 跑在 SYSTEM 下)。Gitea 1.25.4 又去掉了自定义 DEFAULT_ACTIONS_URL,服务端没有 可拧的键,唯一归本仓库管的修法就是不再依赖它:两个 job 第 1 步换成原生 git 全量检出。 关键选择(依据与实测表见 docs/CI/远程CI完整指南.md 同文 §5.3,此处只记结论): - 完整 refspec + 全量,不是 --depth 1:提交信息门禁要解 base..head 的 range; 且目标 sha 不在历史里就 ::error:: 硬失败,绝不默默构建一棵错的树; - clean -ffd 而非 -ffdx:-x 会删被忽略文件,而 runner 工作区跨运行复用它们; - GIT_TERMINAL_PROMPT=0 + 空 credential.helper:否则认证挑战会等人类输入, 把 30 分钟 job 超时白烧掉,表现得像 CI 挂了; - 令牌只走 per-invocation -c http.extraheader(Basic + x-access-token,与 checkout@v4 同拼法),不落 .git/config、不回显;仓库公开时匿名即可; - 不抓 tag:本仓库 tag 数 0、无脚本用 describe --tags(实测)。 另修 scripts/ci_runner_bootstrap.ps1 的 Find-MsvcCl。旧版只扫 %ProgramFiles% 派生根且取首个命中:本机 C 盘 VS2019 已于今日 01:00 卸载(C 盘 x64 cl.exe 实测 0 个),旧版因此是「找不到 → 只 Warn → 不写」,机器级变量一直指着那条失效路径, 而 harness 对「显式设置但无效」是硬失败、不做自动回退。现在改为 vswhere 取实例 (能看见非默认盘)+ 工具集版本降序 + -MsvcToolset 前缀钉住世代(默认 14.51., 与覆盖率基线同版本;钉不上连 WARN 再回落最新,绝不静默)+ -MsvcLatest; -SetEnv 与 -Status 同步纳入 DTU_MS_COVERAGE。 验证(内层一律 powershell.exe 5.1,忠实复现 runner 执行方式):临时工作区六项 A/B/C/D/E/F——E 是执行**从 ci.yml 里抽出的正文**,测的就是文件那份字节。 A rc=0、commits=665、refspec 完整、分离 HEAD;B 复用工作区后 dirty_files=0、 被忽略文件保留、17ece86..HEAD 解出 18 个提交;C rc=1 且 HEAD 不动;D 坏令牌 fatal: unable to get password 秒失败;令牌改造后再跑 ANON/BOGUS/ANON2=0/1/0。 ci_local 全量:L0.5 = 40 runner、3188/3188 断言、fail=0(MSVC 14.51); test-coverage 与 ui-golden 均 Passed。.ps1 非 ASCII 字节 0、语法错误 0、无 BOM、 LF;ci.yml 无 BOM 无 CR、13 步、0 个 run 块含非 ASCII、uses: 计数 0。
This commit is contained in:
+198
-7
@@ -125,11 +125,105 @@ jobs:
|
||||
name: L0 静态门禁 (pre-commit)
|
||||
runs-on: windows
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
# 钩子要对比基线与历史(doc-links 等读工作区即可,但 gitlint/cubemx 需完整历史)
|
||||
fetch-depth: 0
|
||||
|
||||
- name: 检出源码(原生 git;不依赖 github.com 上的 action)
|
||||
shell: powershell
|
||||
env:
|
||||
DTU_REPO_URL: ${{ github.server_url }}/${{ github.repository }}.git
|
||||
DTU_REPO_REF: ${{ github.ref }}
|
||||
DTU_REPO_SHA: ${{ github.sha }}
|
||||
DTU_REPO_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
# Pure ASCII only (see file header).
|
||||
#
|
||||
# WHY NOT `uses: actions/checkout`: the runner resolves every `uses:` by
|
||||
# contacting the actions URL during "Prepare all required actions", BEFORE
|
||||
# the first step runs. On this intranet runner that call intermittently dies
|
||||
# with a connect timeout -- runs 36 (10-21) / 41 (10-25) / 42 (10-26) each
|
||||
# burned ~45 s and produced ZERO hook output, while a manual curl to the same
|
||||
# URL minutes later returned 200. Manual reachability is not CI health: the
|
||||
# resolver runs as SYSTEM and the runner caches nothing across jobs (each job
|
||||
# re-resolves into its own .cache/act/<hash>). Gitea 1.25.4 also removed the
|
||||
# custom DEFAULT_ACTIONS_URL knob, so the only fix this repo owns is to stop
|
||||
# depending on the public resolver at all.
|
||||
#
|
||||
# KEEP IN SYNC with the same step in the other job. This logic deliberately
|
||||
# does NOT live in scripts/*.ps1 (unlike the Python / MSVC probes): the file
|
||||
# is not checked out yet at the moment it would be needed -- chicken and egg.
|
||||
#
|
||||
# NOTE on $ErrorActionPreference: left at Continue on purpose. Under Windows
|
||||
# PowerShell 5.1 a native command that writes to stderr while EAP is Stop
|
||||
# raises NativeCommandError -- git legitimately writes progress to stderr.
|
||||
if (-not $env:DTU_REPO_SHA) { Write-Host '::error::DTU_REPO_SHA is empty'; exit 1 }
|
||||
if (-not $env:DTU_REPO_URL) { Write-Host '::error::DTU_REPO_URL is empty'; exit 1 }
|
||||
$ws = (Get-Location).Path
|
||||
Write-Host "workspace: $ws"
|
||||
if (-not (Test-Path -LiteralPath (Join-Path $ws '.git'))) {
|
||||
git init -q .
|
||||
if ($LASTEXITCODE -ne 0) { Write-Host '::error::git init failed'; exit 1 }
|
||||
}
|
||||
$rem = git remote get-url origin 2>$null
|
||||
if ($LASTEXITCODE -eq 0 -and $rem) {
|
||||
git remote set-url origin $env:DTU_REPO_URL
|
||||
} else {
|
||||
git remote add origin $env:DTU_REPO_URL
|
||||
}
|
||||
if ($LASTEXITCODE -ne 0) { Write-Host '::error::configuring remote origin failed'; exit 1 }
|
||||
# Full refspec, NOT --depth 1: the commit-message gate resolves base..head
|
||||
# ranges and several gates diff against history, so a shallow clone turns
|
||||
# them into "unknown revision" failures. Tags are skipped on purpose: this
|
||||
# repository has zero tags and no script uses `git describe --tags`
|
||||
# (measured 2026-10-27).
|
||||
git config remote.origin.fetch '+refs/heads/*:refs/remotes/origin/*'
|
||||
# Never let a fetch wait for a human: the runner has no console and a
|
||||
# credential prompt would burn the whole 30 m job timeout while looking like
|
||||
# a hung CI. Terminal prompts off + no credential helper means an auth
|
||||
# problem fails immediately with a message instead of blocking. Anonymous
|
||||
# fetch works here (the repository is public; measured HTTP 200 on
|
||||
# /info/refs?service=git-upload-pack), so CI must not start depending on a
|
||||
# credential store it cannot answer.
|
||||
$env:GIT_TERMINAL_PROMPT = '0'
|
||||
# The token, when the instance provides one, travels only as a per-invocation
|
||||
# -c override: it never lands in .git/config and is never echoed in logs.
|
||||
# Anonymous fetch works too (the repository is public), so CI does not start
|
||||
# depending on token availability.
|
||||
$auth = @('-c', 'credential.helper=', '-c', 'credential.interactive=false')
|
||||
if ($env:DTU_REPO_TOKEN) {
|
||||
# Same scheme actions/checkout uses against Gitea git-http: Basic with a
|
||||
# placeholder user and the token as the password. (Bearer is the GitHub
|
||||
# *API* style, not the git-http style.) Honest caveat: this branch CANNOT
|
||||
# be verified here -- the repository is public, so an anonymous fetch
|
||||
# succeeds and any token we fabricate locally only proves a bad one is
|
||||
# rejected. If the repository ever goes private this is the first line to
|
||||
# check; the failure mode is a loud rc=1, never a silent fallback.
|
||||
$pair = 'x-access-token:' + $env:DTU_REPO_TOKEN
|
||||
$b64 = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($pair))
|
||||
$auth += @('-c', ('http.extraheader=AUTHORIZATION: Basic ' + $b64))
|
||||
}
|
||||
# PS 5.1 wraps each native stderr line into an ErrorRecord, turning two real
|
||||
# log lines into eight of CategoryInfo/FullyQualifiedErrorId noise. Flattening
|
||||
# with "$_" keeps the log readable; $LASTEXITCODE is unaffected.
|
||||
$fetchArgs = $auth + @('fetch', '--prune', '--no-tags', 'origin')
|
||||
& git @fetchArgs 2>&1 | ForEach-Object { "$_" }
|
||||
if ($LASTEXITCODE -ne 0) { Write-Host "::error::git fetch origin failed (exit $LASTEXITCODE)"; exit 1 }
|
||||
# Some refs (PR merge commits, refs outside refs/heads) are not covered by the
|
||||
# branch refspec -- fall back to an explicit ref fetch, then INSIST the wanted
|
||||
# SHA is really present, rather than silently building the wrong tree.
|
||||
git rev-parse --verify -q ($env:DTU_REPO_SHA + '^{commit}') 2>$null | Out-Null
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host 'sha not covered by the branch refspec -- fetching the event ref explicitly'
|
||||
$refArgs = $auth + @('fetch', '--prune', '--no-tags', 'origin', $env:DTU_REPO_REF)
|
||||
& git @refArgs 2>&1 | ForEach-Object { "$_" }
|
||||
}
|
||||
git rev-parse --verify -q ($env:DTU_REPO_SHA + '^{commit}') 2>$null | Out-Null
|
||||
if ($LASTEXITCODE -ne 0) { Write-Host ('::error::' + $env:DTU_REPO_SHA + ' is not present in the fetched history'); exit 1 }
|
||||
& git checkout -q --detach $env:DTU_REPO_SHA 2>&1 | ForEach-Object { "$_" }
|
||||
if ($LASTEXITCODE -ne 0) { Write-Host '::error::detached checkout failed'; exit 1 }
|
||||
git reset --hard -q $env:DTU_REPO_SHA
|
||||
if ($LASTEXITCODE -ne 0) { Write-Host '::error::reset --hard failed'; exit 1 }
|
||||
# -ffd, deliberately not -ffdx: -x would delete IGNORED files, and the runner
|
||||
# reuses those across runs (pip / pre-commit caches live in the workspace).
|
||||
git clean -ffd -q
|
||||
Write-Host ('checkout ok: ' + (git rev-parse --short HEAD) + ' | commits=' + (git rev-list --count HEAD) + ' | dirty_files=' + ((git status --porcelain | Measure-Object).Count))
|
||||
- name: 环境自检(Python / cppcheck / pre-commit)
|
||||
shell: powershell
|
||||
run: |
|
||||
@@ -399,8 +493,105 @@ jobs:
|
||||
name: L0.5 主机侧单测 (MSVC)
|
||||
runs-on: windows
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: 检出源码(原生 git;与 l0-gates 同一实现口径)
|
||||
shell: powershell
|
||||
env:
|
||||
DTU_REPO_URL: ${{ github.server_url }}/${{ github.repository }}.git
|
||||
DTU_REPO_REF: ${{ github.ref }}
|
||||
DTU_REPO_SHA: ${{ github.sha }}
|
||||
DTU_REPO_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
# Pure ASCII only (see file header).
|
||||
#
|
||||
# WHY NOT `uses: actions/checkout`: the runner resolves every `uses:` by
|
||||
# contacting the actions URL during "Prepare all required actions", BEFORE
|
||||
# the first step runs. On this intranet runner that call intermittently dies
|
||||
# with a connect timeout -- runs 36 (10-21) / 41 (10-25) / 42 (10-26) each
|
||||
# burned ~45 s and produced ZERO hook output, while a manual curl to the same
|
||||
# URL minutes later returned 200. Manual reachability is not CI health: the
|
||||
# resolver runs as SYSTEM and the runner caches nothing across jobs (each job
|
||||
# re-resolves into its own .cache/act/<hash>). Gitea 1.25.4 also removed the
|
||||
# custom DEFAULT_ACTIONS_URL knob, so the only fix this repo owns is to stop
|
||||
# depending on the public resolver at all.
|
||||
#
|
||||
# KEEP IN SYNC with the same step in the other job. This logic deliberately
|
||||
# does NOT live in scripts/*.ps1 (unlike the Python / MSVC probes): the file
|
||||
# is not checked out yet at the moment it would be needed -- chicken and egg.
|
||||
#
|
||||
# NOTE on $ErrorActionPreference: left at Continue on purpose. Under Windows
|
||||
# PowerShell 5.1 a native command that writes to stderr while EAP is Stop
|
||||
# raises NativeCommandError -- git legitimately writes progress to stderr.
|
||||
if (-not $env:DTU_REPO_SHA) { Write-Host '::error::DTU_REPO_SHA is empty'; exit 1 }
|
||||
if (-not $env:DTU_REPO_URL) { Write-Host '::error::DTU_REPO_URL is empty'; exit 1 }
|
||||
$ws = (Get-Location).Path
|
||||
Write-Host "workspace: $ws"
|
||||
if (-not (Test-Path -LiteralPath (Join-Path $ws '.git'))) {
|
||||
git init -q .
|
||||
if ($LASTEXITCODE -ne 0) { Write-Host '::error::git init failed'; exit 1 }
|
||||
}
|
||||
$rem = git remote get-url origin 2>$null
|
||||
if ($LASTEXITCODE -eq 0 -and $rem) {
|
||||
git remote set-url origin $env:DTU_REPO_URL
|
||||
} else {
|
||||
git remote add origin $env:DTU_REPO_URL
|
||||
}
|
||||
if ($LASTEXITCODE -ne 0) { Write-Host '::error::configuring remote origin failed'; exit 1 }
|
||||
# Full refspec, NOT --depth 1: the commit-message gate resolves base..head
|
||||
# ranges and several gates diff against history, so a shallow clone turns
|
||||
# them into "unknown revision" failures. Tags are skipped on purpose: this
|
||||
# repository has zero tags and no script uses `git describe --tags`
|
||||
# (measured 2026-10-27).
|
||||
git config remote.origin.fetch '+refs/heads/*:refs/remotes/origin/*'
|
||||
# Never let a fetch wait for a human: the runner has no console and a
|
||||
# credential prompt would burn the whole 30 m job timeout while looking like
|
||||
# a hung CI. Terminal prompts off + no credential helper means an auth
|
||||
# problem fails immediately with a message instead of blocking. Anonymous
|
||||
# fetch works here (the repository is public; measured HTTP 200 on
|
||||
# /info/refs?service=git-upload-pack), so CI must not start depending on a
|
||||
# credential store it cannot answer.
|
||||
$env:GIT_TERMINAL_PROMPT = '0'
|
||||
# The token, when the instance provides one, travels only as a per-invocation
|
||||
# -c override: it never lands in .git/config and is never echoed in logs.
|
||||
# Anonymous fetch works too (the repository is public), so CI does not start
|
||||
# depending on token availability.
|
||||
$auth = @('-c', 'credential.helper=', '-c', 'credential.interactive=false')
|
||||
if ($env:DTU_REPO_TOKEN) {
|
||||
# Same scheme actions/checkout uses against Gitea git-http: Basic with a
|
||||
# placeholder user and the token as the password. (Bearer is the GitHub
|
||||
# *API* style, not the git-http style.) Honest caveat: this branch CANNOT
|
||||
# be verified here -- the repository is public, so an anonymous fetch
|
||||
# succeeds and any token we fabricate locally only proves a bad one is
|
||||
# rejected. If the repository ever goes private this is the first line to
|
||||
# check; the failure mode is a loud rc=1, never a silent fallback.
|
||||
$pair = 'x-access-token:' + $env:DTU_REPO_TOKEN
|
||||
$b64 = [System.Convert]::ToBase64String([System.Text.Encoding]::ASCII.GetBytes($pair))
|
||||
$auth += @('-c', ('http.extraheader=AUTHORIZATION: Basic ' + $b64))
|
||||
}
|
||||
# PS 5.1 wraps each native stderr line into an ErrorRecord, turning two real
|
||||
# log lines into eight of CategoryInfo/FullyQualifiedErrorId noise. Flattening
|
||||
# with "$_" keeps the log readable; $LASTEXITCODE is unaffected.
|
||||
$fetchArgs = $auth + @('fetch', '--prune', '--no-tags', 'origin')
|
||||
& git @fetchArgs 2>&1 | ForEach-Object { "$_" }
|
||||
if ($LASTEXITCODE -ne 0) { Write-Host "::error::git fetch origin failed (exit $LASTEXITCODE)"; exit 1 }
|
||||
# Some refs (PR merge commits, refs outside refs/heads) are not covered by the
|
||||
# branch refspec -- fall back to an explicit ref fetch, then INSIST the wanted
|
||||
# SHA is really present, rather than silently building the wrong tree.
|
||||
git rev-parse --verify -q ($env:DTU_REPO_SHA + '^{commit}') 2>$null | Out-Null
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
Write-Host 'sha not covered by the branch refspec -- fetching the event ref explicitly'
|
||||
$refArgs = $auth + @('fetch', '--prune', '--no-tags', 'origin', $env:DTU_REPO_REF)
|
||||
& git @refArgs 2>&1 | ForEach-Object { "$_" }
|
||||
}
|
||||
git rev-parse --verify -q ($env:DTU_REPO_SHA + '^{commit}') 2>$null | Out-Null
|
||||
if ($LASTEXITCODE -ne 0) { Write-Host ('::error::' + $env:DTU_REPO_SHA + ' is not present in the fetched history'); exit 1 }
|
||||
& git checkout -q --detach $env:DTU_REPO_SHA 2>&1 | ForEach-Object { "$_" }
|
||||
if ($LASTEXITCODE -ne 0) { Write-Host '::error::detached checkout failed'; exit 1 }
|
||||
git reset --hard -q $env:DTU_REPO_SHA
|
||||
if ($LASTEXITCODE -ne 0) { Write-Host '::error::reset --hard failed'; exit 1 }
|
||||
# -ffd, deliberately not -ffdx: -x would delete IGNORED files, and the runner
|
||||
# reuses those across runs (pip / pre-commit caches live in the workspace).
|
||||
git clean -ffd -q
|
||||
Write-Host ('checkout ok: ' + (git rev-parse --short HEAD) + ' | commits=' + (git rev-list --count HEAD) + ' | dirty_files=' + ((git status --porcelain | Measure-Object).Count))
|
||||
- name: 探测 MSVC(VS BuildTools / Community 等)
|
||||
id: msvc
|
||||
shell: powershell
|
||||
|
||||
Reference in New Issue
Block a user