فهرست منبع

ci(wine): add verified archive mirror fallback

Tianyi Cui 1 ماه پیش
والد
کامیت
548726eede

+ 2 - 2
.agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.i18n.yaml

@@ -2,5 +2,5 @@
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # side as of the last confirmed-consistent state. Both languages carry equal authority;
 # after editing either side, bring the other along and re-record with:
 # after editing either side, bring the other along and re-record with:
 #   pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.md
 #   pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.md
-2026-08-08-native-windows-pull-request-ci.md: a1f294b2b9e73a5d14a00780c7b0adfaf58e4702
-2026-08-08-native-windows-pull-request-ci.zh.md: 2c91c21118f5ebbd9f0e341f7d31ec71481db4dc
+2026-08-08-native-windows-pull-request-ci.md: 258bca96db04c44ca2a3509c297028e31ef5375e
+2026-08-08-native-windows-pull-request-ci.zh.md: dd0031630eea9bb09a4a4db0125933ba0c7bac2f

+ 1 - 1
.agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.md

@@ -10,7 +10,7 @@ The required pull-request Windows verdict needs a fast win32 toolchain signal wi
 
 
 ## Decision
 ## Decision
 
 
-The required `windows` job in [ci.yml](../../../../.github/workflows/ci.yml) remains `windows node 24 / wine blocking` on `ubuntu-latest`. It retains the checksum-verified Windows Node, Wine apt and pnpm caches, a hoisted install confined to a workspace snapshot, and the [shared Wine gate script](../../../../scripts/wine-windows-gates.sh) that runs the workspace build and production site. Node distribution transfers use bounded retries so a stalled download fails or recovers before consuming the job timeout. The stable `windows` job id remains a dependency of `all checks passed`. The [archived Wine experiment](../../archived/process/2026-07-27-wine-windows-gates-experiment.md) preserves its measured trade-offs, while this note owns the current dual topology.
+The required `windows` job in [ci.yml](../../../../.github/workflows/ci.yml) remains `windows node 24 / wine blocking` on `ubuntu-latest`. It retains the checksum-verified Windows Node, Wine apt and pnpm caches, a hoisted install confined to a workspace snapshot, and the [shared Wine gate script](../../../../scripts/wine-windows-gates.sh) that runs the workspace build and production site. Node distribution transfers use bounded retries; when nodejs.org stalls on the large archive, a range-capable transport mirror resumes the same bytes, but nodejs.org remains the version and SHA-256 authority and the archive is never promoted before that checksum passes. The stable `windows` job id remains a dependency of `all checks passed`. The [archived Wine experiment](../../archived/process/2026-07-27-wine-windows-gates-experiment.md) preserves its measured trade-offs, while this note owns the current dual topology.
 
 
 Every pull request also starts an independent `windows-native` job named `windows node 24 / native complete` on GitHub's standard `windows-2025` image. It enables Developer Mode for workspace symlinks, provisions the repository-pinned pnpm through `pnpm/action-setup`, performs an immutable install without a transferred store archive, and runs `pnpm run check:ci:windows-complete` under native PowerShell. The job is deliberately absent from `all-checks-passed.needs`: the aggregate neither waits for it nor changes conclusion because of it, while the native job retains its own unmasked success or failure result.
 Every pull request also starts an independent `windows-native` job named `windows node 24 / native complete` on GitHub's standard `windows-2025` image. It enables Developer Mode for workspace symlinks, provisions the repository-pinned pnpm through `pnpm/action-setup`, performs an immutable install without a transferred store archive, and runs `pnpm run check:ci:windows-complete` under native PowerShell. The job is deliberately absent from `all-checks-passed.needs`: the aggregate neither waits for it nor changes conclusion because of it, while the native job retains its own unmasked success or failure result.
 
 

+ 1 - 1
.agents/notes/implemented/process/2026-08-08-native-windows-pull-request-ci.zh.md

@@ -10,7 +10,7 @@ Status: implemented
 
 
 ## 决策
 ## 决策
 
 
-[ci.yml](../../../../.github/workflows/ci.yml) 中必需的 `windows` 作业仍是在 `ubuntu-latest` 上运行的 `windows node 24 / wine blocking`。它保留经过校验和验证的 Windows Node、Wine apt 与 pnpm 缓存、仅限工作区快照的 hoisted 安装,以及运行工作区构建与生产网站的[共享 Wine 门禁脚本](../../../../scripts/wine-windows-gates.sh)。Node 分发文件传输采用有界重试,使停滞的下载在耗尽作业超时前失败或恢复。稳定的 `windows` 作业 ID 仍是 `all checks passed` 的依赖项。[已归档的 Wine 实验](../../archived/process/2026-07-27-wine-windows-gates-experiment.md)保留其实测取舍,而本文负责当前双通道拓扑。
+[ci.yml](../../../../.github/workflows/ci.yml) 中必需的 `windows` 作业仍是在 `ubuntu-latest` 上运行的 `windows node 24 / wine blocking`。它保留经过校验和验证的 Windows Node、Wine apt 与 pnpm 缓存、仅限工作区快照的 hoisted 安装,以及运行工作区构建与生产网站的[共享 Wine 门禁脚本](../../../../scripts/wine-windows-gates.sh)。Node 分发文件传输采用有界重试;nodejs.org 的大文件传输停滞时,由支持范围请求的传输镜像续传相同字节,但版本和 SHA-256 权威仍属于 nodejs.org,归档通过该校验前绝不会投入使用。稳定的 `windows` 作业 ID 仍是 `all checks passed` 的依赖项。[已归档的 Wine 实验](../../archived/process/2026-07-27-wine-windows-gates-experiment.md)保留其实测取舍,而本文负责当前双通道拓扑。
 
 
 每个拉取请求还会在 GitHub 标准 `windows-2025` 镜像上启动一个独立的 `windows-native` 作业,名称为 `windows node 24 / native complete`。该作业为工作区符号链接启用开发人员模式,通过 `pnpm/action-setup` 提供仓库固定版本的 pnpm,在不传输 store 归档的情况下执行不可变安装,并在原生 PowerShell 下运行 `pnpm run check:ci:windows-complete`。该作业被刻意排除在 `all-checks-passed.needs` 之外:聚合流程既不等待它,也不会因它改变结论;原生作业则保留自身未被掩盖的成功或失败结果。
 每个拉取请求还会在 GitHub 标准 `windows-2025` 镜像上启动一个独立的 `windows-native` 作业,名称为 `windows node 24 / native complete`。该作业为工作区符号链接启用开发人员模式,通过 `pnpm/action-setup` 提供仓库固定版本的 pnpm,在不传输 store 归档的情况下执行不可变安装,并在原生 PowerShell 下运行 `pnpm run check:ci:windows-complete`。该作业被刻意排除在 `all-checks-passed.needs` 之外:聚合流程既不等待它,也不会因它改变结论;原生作业则保留自身未被掩盖的成功或失败结果。
 
 

+ 16 - 6
scripts/wine-windows-gates.sh

@@ -77,22 +77,32 @@ mkdir -p "$cache_dir" "$scratch/logs"
 curl_metadata_args=(
 curl_metadata_args=(
   --fail --silent --show-error --location
   --fail --silent --show-error --location
   --retry 3 --retry-all-errors --retry-delay 2
   --retry 3 --retry-all-errors --retry-delay 2
-  --connect-timeout 10 --max-time 30 --retry-max-time 120
+  --http1.1 --connect-timeout 10 --max-time 30 --retry-max-time 120
 )
 )
 
 
 download_node_archive() {
 download_node_archive() {
-  local url="$1" output="$2" attempt status=0
+  local version="$1" output="$2" attempt status=0
+  local archive="node-$version-win-x64.zip"
+  local primary_url="https://nodejs.org/dist/$version/$archive"
+  local mirror_url="https://npmmirror.com/mirrors/node/$version/$archive"
+
+  if curl --fail --silent --show-error --location --http1.1 \
+    --connect-timeout 10 --max-time 300 --speed-limit 1024 --speed-time 30 \
+    -o "$output" "$primary_url"; then
+    return 0
+  fi
+  echo 'wine-windows-gates: nodejs.org archive transfer stalled; resuming from the checksum-untrusted transport mirror' >&2
   for attempt in 1 2 3; do
   for attempt in 1 2 3; do
-    if curl --fail --silent --show-error --location \
+    if curl --fail --silent --show-error --location --http1.1 \
       --continue-at - --connect-timeout 10 --max-time 300 \
       --continue-at - --connect-timeout 10 --max-time 300 \
       --speed-limit 1024 --speed-time 30 \
       --speed-limit 1024 --speed-time 30 \
-      -o "$output" "$url"; then
+      -o "$output" "$mirror_url"; then
       return 0
       return 0
     else
     else
       status=$?
       status=$?
     fi
     fi
     (( attempt < 3 )) || break
     (( attempt < 3 )) || break
-    echo "wine-windows-gates: Windows Node archive transfer failed (exit $status) on attempt $attempt; resuming partial download" >&2
+    echo "wine-windows-gates: mirror transfer failed (exit $status) on attempt $attempt; resuming partial download" >&2
   done
   done
   return "$status"
   return "$status"
 }
 }
@@ -109,7 +119,7 @@ provision_node() {
   if [ -n "$version" ]; then
   if [ -n "$version" ]; then
     zip="$cache_dir/node-$version-win-x64.zip"
     zip="$cache_dir/node-$version-win-x64.zip"
     if [ ! -f "$zip" ]; then
     if [ ! -f "$zip" ]; then
-      download_node_archive "https://nodejs.org/dist/$version/node-$version-win-x64.zip" "$zip.tmp"
+      download_node_archive "$version" "$zip.tmp"
       local expected
       local expected
       expected="$(curl "${curl_metadata_args[@]}" "https://nodejs.org/dist/$version/SHASUMS256.txt" \
       expected="$(curl "${curl_metadata_args[@]}" "https://nodejs.org/dist/$version/SHASUMS256.txt" \
         | awk -v a="node-$version-win-x64.zip" '$2 == a { print $1; exit }')"
         | awk -v a="node-$version-win-x64.zip" '$2 == a { print $1; exit }')"