Repository navigation
Fix unstable GitHub access and add mirror-to-direct fallback - #23
Conversation
审阅者指南本 PR 通过禁止 GitHub 核心域静态 hosts 固定,降低自适应 IP 钉造成的系统级故障;同时重构下载和克隆失败路径,采用有限预算的镜像竞速并在镜像不可用时清除代理后直连官方 GitHub,辅以目标目录保护、并行清单刷新和 Windows 离线 CI 验证。 镜像下载失败后回退到直连的时序图sequenceDiagram
participant User
participant gh as gh.mjs
participant Manifest as MirrorManifest
participant Mirrors
participant GitHub
participant File as OutputFile
User->>gh: race(url, name)
gh->>Manifest: loadManifest()
Manifest-->>gh: manifest sources in parallel
gh->>Mirrors: checkAll()
Mirrors-->>gh: healthy entries
gh->>Mirrors: startCurl() concurrently
alt Mirror succeeds
Mirrors-->>gh: complete file
gh->>File: commit successful output
else Mirrors unavailable or exhausted
gh->>gh: proxyFreeEnv()
gh->>GitHub: curl with --noproxy *
alt Direct download succeeds
GitHub-->>gh: complete temporary file
gh->>File: rename temporary file
else Direct download fails
gh-->>User: exit with failure
end
end
镜像克隆失败后回退到直连的时序图sequenceDiagram
participant User
participant gh as gh.mjs
participant Mirrors
participant GitHub
participant Target as CloneTarget
User->>gh: clone(args)
gh->>gh: cloneOutputDir(repo, rest)
alt Target already exists
gh-->>User: reject without overwrite
else Target is absent
gh->>Mirrors: probeRefs(repo)
alt Mirror clone succeeds
Mirrors-->>gh: clone complete
gh->>Target: retain cloned directory
else Mirror probing or retries fail
gh->>gh: proxyFreeEnv()
gh->>GitHub: git clone with cleared proxy and no insteadOf
alt Direct clone succeeds
GitHub-->>gh: clone complete
gh->>Target: retain cloned directory
else Direct clone fails
gh->>Target: cleanupFailedClone(targetDir)
gh-->>User: exit with failure
end
end
end
有限镜像尝试的状态图stateDiagram-v2
[*] --> Probing
Probing --> Racing: healthy entries found
Probing --> DirectFallback: no healthy entries or health check fails
Racing --> Racing: retry within entry budget
Racing --> Exhausted: first-byte or speed budget exhausted
Racing --> Success: complete non-empty file
Exhausted --> Racing: another non-exhausted entry
Exhausted --> DirectFallback: all entries exhausted
DirectFallback --> Success: official direct retry succeeds
DirectFallback --> Failed: retry budget exhausted
Success --> [*]
Failed --> [*]
文件级变更
提示和命令与 Sourcery 互动
自定义使用体验访问你的控制面板以:
获取帮助Original review guide in EnglishReviewer's Guide本 PR 通过禁止 GitHub 核心域静态 hosts 固定,降低自适应 IP 钉造成的系统级故障;同时重构下载和克隆失败路径,采用有限预算的镜像竞速并在镜像不可用时清除代理后直连官方 GitHub,辅以目标目录保护、并行清单刷新和 Windows 离线 CI 验证。 Sequence diagram for mirror-to-direct download fallbacksequenceDiagram
participant User
participant gh as gh.mjs
participant Manifest as MirrorManifest
participant Mirrors
participant GitHub
participant File as OutputFile
User->>gh: race(url, name)
gh->>Manifest: loadManifest()
Manifest-->>gh: manifest sources in parallel
gh->>Mirrors: checkAll()
Mirrors-->>gh: healthy entries
gh->>Mirrors: startCurl() concurrently
alt Mirror succeeds
Mirrors-->>gh: complete file
gh->>File: commit successful output
else Mirrors unavailable or exhausted
gh->>gh: proxyFreeEnv()
gh->>GitHub: curl with --noproxy *
alt Direct download succeeds
GitHub-->>gh: complete temporary file
gh->>File: rename temporary file
else Direct download fails
gh-->>User: exit with failure
end
end
Sequence diagram for mirror-to-direct clone fallbacksequenceDiagram
participant User
participant gh as gh.mjs
participant Mirrors
participant GitHub
participant Target as CloneTarget
User->>gh: clone(args)
gh->>gh: cloneOutputDir(repo, rest)
alt Target already exists
gh-->>User: reject without overwrite
else Target is absent
gh->>Mirrors: probeRefs(repo)
alt Mirror clone succeeds
Mirrors-->>gh: clone complete
gh->>Target: retain cloned directory
else Mirror probing or retries fail
gh->>gh: proxyFreeEnv()
gh->>GitHub: git clone with cleared proxy and no insteadOf
alt Direct clone succeeds
GitHub-->>gh: clone complete
gh->>Target: retain cloned directory
else Direct clone fails
gh->>Target: cleanupFailedClone(targetDir)
gh-->>User: exit with failure
end
end
end
State diagram for bounded mirror attemptsstateDiagram-v2
[*] --> Probing
Probing --> Racing: healthy entries found
Probing --> DirectFallback: no healthy entries or health check fails
Racing --> Racing: retry within entry budget
Racing --> Exhausted: first-byte or speed budget exhausted
Racing --> Success: complete non-empty file
Exhausted --> Racing: another non-exhausted entry
Exhausted --> DirectFallback: all entries exhausted
DirectFallback --> Success: official direct retry succeeds
DirectFallback --> Failed: retry budget exhausted
Success --> [*]
Failed --> [*]
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
嗨——我发现了 1 个问题
AI 代理提示
请处理本次代码审查中的评论:
## 单条评论
### 评论 1
<location path="gh.mjs" line_range="485" />
<code_context>
+ reason: 'safety-no-pin:GitHub 核心域保留动态 DNS,避免间歇性 IP 固定影响主站/资源' }
+ log('保护性跳过 ', dom.padEnd(28), '不写入 hosts 固定(使用动态 DNS)')
+ continue
+ }
+ if (CFG.deny.indexOf(dom) >= 0) continue
const conf = CFG.domains[dom] || {}
</code_context>
<issue_to_address>
**其他克隆会被删除**
如果其他进程在初次检查确认目标不存在之后创建了目标目录或完成了克隆,而当前克隆尝试随后失败,`cleanupFailedClone` 只根据 `targetDir` 当前是否存在就递归删除它,因此会删除其他进程的数据或已完成的仓库。
跟踪本次调用创建的目标目录,并且只删除本次调用自身未完成的克隆。
`gh.mjs:519`、`gh.mjs:692` 处也有此问题。
</issue_to_address>Sourcery 评估
需要人工审查。 有 1 个问题需要优先处理;此外,当镜像尝试失败时,下载和克隆会有意在清除代理环境变量和 Git 代理设置后直接发往 GitHub,从而绕过这些请求原本配置的代理。回滚可以阻止今后再直接回退,但无法撤销已经绕过代理发出的请求。
阻塞性问题:gh.mjs:485
Original comment in English
Hey - I've found 1 issue
Prompt for AI Agents
Please address the comments from this code review:
## Individual Comments
### Comment 1
<location path="gh.mjs" line_range="485" />
<code_context>
+ reason: 'safety-no-pin:GitHub 核心域保留动态 DNS,避免间歇性 IP 固定影响主站/资源' }
+ log('保护性跳过 ', dom.padEnd(28), '不写入 hosts 固定(使用动态 DNS)')
+ continue
+ }
+ if (CFG.deny.indexOf(dom) >= 0) continue
const conf = CFG.domains[dom] || {}
</code_context>
<issue_to_address>
**Other clones are deleted**
When another process creates or completes the destination after the initial absence check and this clone attempt fails, `cleanupFailedClone` recursively removes `targetDir` based only on its current existence, so it deletes the other process's data or completed repository.
Track which destination this invocation created and only remove that invocation's own partial clone.
Also at `gh.mjs:519`, `gh.mjs:692`.
</issue_to_address>Sourcery assessment
Needs a human reviewer. 1 finding to address first, and when mirror attempts fail, downloads and clones are deliberately sent to GitHub with proxy environment variables and Git proxy settings cleared, bypassing any configured proxy for those requests. Reverting stops future direct fallbacks, but cannot undo requests already made outside that proxy.
Blocking findings: gh.mjs:485
| function cleanupFailedClone(targetDir) { | ||
| // Called only after the caller verified the destination did not exist before this run. | ||
| try { if (fs.existsSync(targetDir)) fs.rmSync(targetDir, { recursive: true, force: true }) } catch (e) {} | ||
| } |
There was a problem hiding this comment.
🟠 高 · 其他克隆会被删除
如果其他进程在初次检查确认目标不存在之后创建了目标目录或完成了克隆,而当前克隆尝试随后失败,cleanupFailedClone 只根据 targetDir 当前是否存在就递归删除它,因此会删除其他进程的数据或已完成的仓库。
跟踪本次调用创建的目标目录,并且只删除本次调用自身未完成的克隆。
gh.mjs:519、gh.mjs:692 处也有此问题。
给 AI 代理的提示
在 `gh.mjs` 第 485 行:
**其他克隆会被删除**
如果其他进程在初次检查确认目标不存在之后创建了目标目录或完成了克隆,而当前克隆尝试随后失败,`cleanupFailedClone` 只根据 `targetDir` 当前是否存在就递归删除它,因此会删除其他进程的数据或已完成的仓库。
跟踪本次调用创建的目标目录,并且只删除本次调用自身未完成的克隆。
`gh.mjs:519`、`gh.mjs:692` 处也有此问题。Original comment in English
🟠 High · Other clones are deleted
When another process creates or completes the destination after the initial absence check and this clone attempt fails, cleanupFailedClone recursively removes targetDir based only on its current existence, so it deletes the other process's data or completed repository.
Track which destination this invocation created and only remove that invocation's own partial clone.
Also at gh.mjs:519, gh.mjs:692.
Prompt for AI agents
In `gh.mjs` at line 485:
**Other clones are deleted**
When another process creates or completes the destination after the initial absence check and this clone attempt fails, `cleanupFailedClone` recursively removes `targetDir` based only on its current existence, so it deletes the other process's data or completed repository.
Track which destination this invocation created and only remove that invocation's own partial clone.
Also at `gh.mjs:519`, `gh.mjs:692`.
审计结论
gh.mjs虽然并发竞速镜像,但镜像全部失败时原先直接退出;克隆也缺少官方源直连的最终回退。accel.mjs会跳过测速;若旧版本留下 GitHub 静态钉,该早退路径可能一直保留坏钉。改动
--noproxy '*';成功前不提交输出文件。验证
范围边界
此方案能移除本工具制造的坏静态钉,并为本工具执行的下载/克隆补齐镜像到官方直连的回退。但在保持 Clash 不变、浏览器 GitHub 路由仍为 DIRECT 的前提下,浏览器页面无法被本仓库脚本透明切换到镜像;任何代码也无法保证运营商链路、DNS 或 GitHub 上游永不故障。