Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 25 additions & 18 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -187,22 +187,23 @@ jobs:
- name: Build
run: cargo build --release -p twcore

# macOS 那边的 smoke 脚本在这里跑不了(`stat -f`、`curl --unix-socket`、
# 一堆 BSD 的写法),移植它是另一件事。但**至少要证明这个二进制真的
# 起得来、控制面真的应答** —— 那两样恰恰是这个平台上和 unix 完全不同的
# 实现:回环端口换掉了 socket,凭据是唯一的门。
# macOS 那边的 smoke 脚本在这里跑不了(`stat -f`、一堆 BSD 的写法),
# 移植它是另一件事。但**至少要证明这个二进制真的起得来、控制面真的应答**
# —— 那两样恰恰是这个平台上和 unix 完全不同的实现:回环端口换掉了 socket,
# 握手是唯一的门。
#
# 不验这个的话,Windows 上的失败模式是「CI 全绿,而用户装上之后界面
# 永远停在连接页」。
- name: It starts, and the control plane answers only with the token
- name: It starts, and the control plane answers only after the handshake
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
$home_ = Join-Path $env:RUNNER_TEMP "tw"
New-Item -ItemType Directory -Force -Path $home_ | Out-Null
$env:THINKWATCH_HOME = $home_
$cfg = Join-Path $home_ "config.yaml"
$proc = Start-Process -FilePath "target\release\twcore.exe" `
$bin = "target\release\twcore.exe"
$proc = Start-Process -FilePath $bin `
-ArgumentList "serve","--config",$cfg,"--port","18999" `
-PassThru -NoNewWindow `
-RedirectStandardOutput (Join-Path $home_ "out.log") `
Expand All @@ -222,26 +223,32 @@ jobs:
}
Start-Sleep -Milliseconds 200
}
$port = (Get-Content $portFile -Raw).Trim()
$token = (Get-Content (Join-Path $home_ "control.token") -Raw).Trim()
$url = "http://127.0.0.1:$port/status"
Start-Sleep -Milliseconds 200
$port = (Get-Content $portFile -Raw).Trim()
Write-Host "control plane on $port"

try {
$no = Invoke-WebRequest -Uri $url -SkipHttpErrorCheck -TimeoutSec 10
if ($no.StatusCode -ne 401) { throw "without a token the control plane answered $($no.StatusCode), not 401" }
# 不握手的 HTTP 进不来:core 回一个拒绝字节就断开
$plainGotIn = $false
try {
$no = Invoke-WebRequest -Uri "http://127.0.0.1:$port/status" -SkipHttpErrorCheck -TimeoutSec 10
if ($no.Content -match "api_version") { $plainGotIn = $true }
} catch {
Write-Host "plain HTTP was turned away: $($_.Exception.Message)"
}
if ($plainGotIn) { throw "plain HTTP without the handshake got a status back" }

$yes = Invoke-WebRequest -Uri $url -TimeoutSec 10 -Headers @{ Authorization = "Bearer $token" }
if ($yes.StatusCode -ne 200) { throw "with the token it answered $($yes.StatusCode)" }
$status = $yes.Content | ConvertFrom-Json
if (-not $status.version) { throw "the status carried no version: $($yes.Content)" }
# 握手之后:`twcore call` 读同一份配置里的钥匙,走和桌面端同一条握手
$out = & $bin --config $cfg call /status
if ($LASTEXITCODE -ne 0) { throw "twcore call /status failed ($LASTEXITCODE)" }
$status = $out | ConvertFrom-Json
if (-not $status.version) { throw "the status carried no version: $out" }
Write-Host "gateway $($status.version), api $($status.api_version)"
# **请它退出,它就该退。**这条路在这个平台上没有替代品:
# 没有 SIGTERM,而桌面端要靠它在改完配置后重启 core、在装更新
# 之前停掉它。只能强杀意味着 WAL 不收尾、在途请求断在半路。
$bye = Invoke-WebRequest -Uri "http://127.0.0.1:$port/shutdown" -Method Post `
-TimeoutSec 10 -Headers @{ Authorization = "Bearer $token" }
if ($bye.StatusCode -ne 202) { throw "asking it to exit answered $($bye.StatusCode)" }
$code = & $bin --config $cfg call -X POST --out (Join-Path $home_ "bye.json") /shutdown
if ("$code".Trim() -ne "202") { throw "asking it to exit answered $code" }
# 等**进程**没了,不是等端口没了 —— 一个卡住的进程也可能丢掉监听
if (-not $proc.WaitForExit(10000)) {
throw "it was asked to exit and is still running after 10s"
Expand Down
7 changes: 6 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ surface lints you cannot reproduce — `rustup update stable` before
blaming CI.

`scripts/smoke.sh` runs the real binary against a real socket and a real
data plane. **It catches what unit tests structurally cannot** — file
data plane, talking to the control plane through `twcore call` (every
control connection starts with a Noise handshake, so curl cannot). **It catches what unit tests structurally cannot** — file
permissions, socket path limits, an endpoint that simply isn't
registered, a config field silently swallowed. This project's first four
real bugs were all in those seams. Tests that hit the live network are
Expand All @@ -81,6 +82,10 @@ clean the diff is:
- **Anything that bypasses the main pipeline re-applies its
protections.** Replay came close to being a legitimate way around
redaction.
- **One door into the control plane.** Every transport (unix socket,
Windows loopback port, and the remote port to come) hands its
connections to the same handshake before HTTP. The control key never
leaves through the control plane and cannot be changed through it.

## The price list

Expand Down
158 changes: 156 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading