Isolate modern and Win7 workspaces (T-604)

This commit is contained in:
ila
2026-07-16 21:27:52 +08:00
parent e4a9295cbf
commit 7efcab5dfe
11 changed files with 234 additions and 43 deletions
+10 -10
View File
@@ -45,21 +45,21 @@ SoftBox 软件盒子是一个使用 Go + Gio 开发的 Windows 桌面客户端,
## 当前阶段
当前项目处于:**M2 已完成(清单验签、ZIP 安全解压、原子切换/回滚三项高风险原型已验证),准备进入 Phase 2 清单与软件列表**。
当前项目已完成 Phase 0~2、T-301 与首个交叉审核整改 `T-604`。下一步按审核定稿落成状态模型收口任务,随后处理依赖可复现性和 Runtime 并发契约,再恢复 T-302。
优先路径:
1. 已完成 Phase 0:monorepo 骨架 + 双目标编译 + 空 Gio 窗口。
2. 已完成 Phase 1:清单验签、ZIP 安全解压、原子切换回滚原型。
3. 下一步 Phase 2-3:清单/列表 → 下载/安装。
4. Phase 4-5:启动/更新/自更新 → 授权。
5. Phase 6:Win7 加固与双通道发布。
3. 已完成 Phase 2 与 T-301:清单/列表/详情/图标缓存 + 可恢复下载队列。
4. 已完成 T-604:modern/Win7 workspace 与 Gio 版本解析彻底隔离。
5. 继续串行完成其余审核整改,再恢复 T-302/T-303 和 Phase 4-6。
## 领取任务规则
任务以「一任务一文件」存放在 `docs/tasks/`(约定见 [`tasks/README.md`](tasks/README.md)):
- 每个 agent 只领取一个 frontmatter `status: TODO` 且依赖均 `DONE` 的任务文件,取编号最靠前的;项目可并行多个 `write_paths` 互不重叠的任务。
- 单 Agent 只领取一个 frontmatter `status: TODO` 且依赖均 `DONE` 的任务文件,取编号最靠前的;项目同一时间只保留一个活跃任务。
- 若 `docs/tasks/` 暂无可领任务,先按 [`06-tasks.md`](06-tasks.md) 路线图把下一个建议任务落成任务文件,再领取。
- 开始前在独立分支 / worktree 把该文件 frontmatter 的 `status` 改为 `DOING`。
- 本轮只完成这一个任务;验收通过后改为 `DONE`。
@@ -133,17 +133,17 @@ MVP 不做:
## 验证命令
统一启动与验证入口收敛到根目录 `./init.sh` / `./init.ps1`;脚本会同步工作区依赖并运行完整 Phase 0 闸门。也可直接运行 `bash scripts/verify_phase0.sh` / `./scripts/verify_phase0.ps1`。标准分项命令:
统一启动与验证入口收敛到根目录 `./init.sh` / `./init.ps1`;脚本会分别同步根 modern/core workspace 与嵌套 win7/core workspace,再运行完整 Phase 0 闸门。也可直接运行 `bash scripts/verify_phase0.sh` / `./scripts/verify_phase0.ps1`。标准分项命令:
```bash
# core 测试(Linux 无头可跑)
cd core && go vet ./... && go test -count=1 ./...
# 现代版构建
cd app-modern && GOTOOLCHAIN=go1.25.0 CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -trimpath -ldflags="-H=windowsgui" ./cmd/softbox
# 现代版构建(从仓库根执行;GOWORK 必须是绝对路径)
GOTOOLCHAIN=go1.25.0 GOWORK="$(pwd)/go.work" CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go -C app-modern build -trimpath -ldflags="-H=windowsgui" ./cmd/softbox
# Win7 版构建(强制 Go 1.20)
cd app-win7 && GOTOOLCHAIN=go1.20.14 CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -trimpath -ldflags="-H=windowsgui" ./cmd/softbox
# Win7 版构建(从仓库根执行;强制 Go 1.20)
GOTOOLCHAIN=go1.20.14 GOWORK="$(pwd)/app-win7/go.work" CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go -C app-win7 build -trimpath -ldflags="-H=windowsgui" ./cmd/softbox
```
说明:
+8 -5
View File
@@ -11,7 +11,7 @@
| UI 框架 | Gio(纯 Go 立即模式 GUI) | 已定 | 无 CGO、单 EXE、跨版本可锁定;仅限 UI 层 |
| 现代版工具链 | Go 1.25(锁定已验证版本) + Gio v0.10.1 | 已定 | 当前受支持工具链;支持 Win10/Win11 x64 |
| Win7 版工具链 | Go 1.20.14(最终补丁) + Gio v0.6.0 | 已定 | Go 1.20 是最后支持 Win7 的版本;Gio v0.6.0 可由 Go 1.20 构建 |
| 模块结构 | go.work + 三个 go.mod(core / app-modern / app-win7) | 已定 | 共享核心,双构建互不污染工具链要求 |
| 模块结构 | 两个 go.work + 三个 go.mod(core / app-modern / app-win7) | 已定 | 根 workspace 只含 modern/core;嵌套 Win7 workspace 只含 win7/core |
| 依赖管理 | 版本锁定 + vendor(Gio 及相关依赖) | 已定 | 防止 Win7 构建被隐式升级到要求 Go 1.21+ 的依赖 |
| 存储 | JSON 文件 + 临时文件原子替换 | 已定 | 数百款软件规模足够;SQLite 仅在历史/搜索/事务需求出现后评估 |
| 软件包格式 | ZIP(标准软件包协议 v1) | 已定 | 避免 UnRAR.dll;协议见 [api.md](api.md) |
@@ -41,14 +41,17 @@
| --- | --- |
| Phase 0 完整闸门(Unix) | `bash scripts/verify_phase0.sh` |
| Phase 0 完整闸门(Windows) | `./scripts/verify_phase0.ps1` |
| 同步工作区依赖 | `go work sync` |
| 同步根 workspace | `GOTOOLCHAIN=go1.25.0 GOWORK=<repo>/go.work go work sync` |
| 同步 Win7 workspace | `GOTOOLCHAIN=go1.25.0 GOWORK=<repo>/app-win7/go.work go work sync`(同步元数据后仍由 Go 1.20.14 校验/构建) |
| core 测试 | `cd core && go test -count=1 ./...` |
| core 静态检查 | `cd core && go vet ./...` |
| 现代版构建 | `cd app-modern && GOTOOLCHAIN=go1.25.0 CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -trimpath -ldflags="-H=windowsgui" -o ../dist/SoftBox.exe ./cmd/softbox` |
| Win7 版构建 | `cd app-win7 && GOTOOLCHAIN=go1.20.14 CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -trimpath -ldflags="-H=windowsgui" -o ../dist/SoftBox-win7.exe ./cmd/softbox` |
| 现代版构建 | `GOTOOLCHAIN=go1.25.0 GOWORK=<repo>/go.work CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go -C app-modern build -trimpath -ldflags="-H=windowsgui" -o ../dist/SoftBox.exe ./cmd/softbox` |
| Win7 版构建 | `GOTOOLCHAIN=go1.20.14 GOWORK=<repo>/app-win7/go.work CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go -C app-win7 build -trimpath -ldflags="-H=windowsgui" -o ../dist/SoftBox-win7.exe ./cmd/softbox` |
| 本地运行(开发) | Windows 下直接 `go run ./cmd/softbox`(WSL 中构建 EXE 后到 Windows 侧运行) |
根 `go.work` 纳入三个模块并使用 modern 所需的 Go 1.25 workspace 版本;`app-win7/go.work` 是就近的 Go 1.20 workspace,只纳入 `app-win7` 与 `core`,确保 Go 1.20.14 不需要解析 modern 模块。
根 `go.work` 使用 Go 1.25.0,只纳入 `app-modern` 与 `core`;`app-win7/go.work` 使用 Go 1.20,只纳入 `app-win7` 与 `core`。同步、测试和构建始终显式指定对应 `GOWORK`,任何 workspace 都不会同时解析 Gio v0.10.1 与 v0.6.0。
两个 workspace 的元数据同步统一使用 Go 1.25.0。原因是 Go 1.20.14 的 `go work sync` 会忽略 workspace 级本地 replace 并尝试联网解析占位模块 `softbox.local/core@v0.0.0`;不为规避该工具缺陷把本机相对路径写进 go.mod。Win7 的依赖图、测试和构建仍全部强制使用 Go 1.20.14,并由版本检查拒绝 Go 1.21+ 依赖。
工具链矩阵:
+6 -6
View File
@@ -13,22 +13,22 @@
## 当前快照
- 日期:2026-07-16
- 阶段:Phase 2 已完成(T-201~T-204);Phase 3 的 T-301 可恢复下载队列已完成;Phase 0 交叉审核整改从 T-604 开始,T-302 暂后置
- 技术栈:根 Go workspace 纳入 core/app-modern/app-win7 三模块;`app-win7/go.work` 隔离 Go 1.20.14 构建;modern Gio v0.10.1 与 win7 Gio v0.6.0 已实际接入
- 阶段:Phase 2 已完成(T-201~T-204);Phase 3 的 T-301 可恢复下载队列已完成;Phase 0 首个交叉审核整改 T-604 已完成,T-302 继续暂后置
- 技术栈:根 Go 1.25 workspace 只纳入 core/app-modern,`app-win7/go.work` 独立纳入 core/app-win7;版本闸门证明 modern Gio v0.10.1 与 win7 Gio v0.6.0 不交叉解析
- 生产代码:core 已有 Catalog/本地状态/存储、无 IO 软件列表模型、可信图标缓存和默认并发 2 的持久可恢复下载队列;modern/win7 AppShell 已实现搜索/分类/视图、惰性列表、详情右栏与内存图标
- 测试:core 覆盖 Catalog、SemVer/12 状态、本地安装记录、列表/图标、下载并发/暂停/取消/重试/Range/断连/恢复/事件失败与文件身份替换;两个 app 覆盖 500 项虚拟列表、ID 控件稳定性、详情/ApplyIcon 与平台 stub;ZIP/安装恢复矩阵保持通过
- 数据:`schemas/` 已有 manifest/app.json/installed-app.json/download-task.json v1 Schema;`testdata/catalog/` 有公开虚构清单样例;`testdata/zip/` 与 `testdata/download/` 记录运行时生成的攻击/传输矩阵
- 标准启动路径:`./init.sh` / `./init.ps1`(同步依赖、执行完整 Phase 0 闸门、打印双目标构建命令)
- 标准验证路径:`bash scripts/verify_phase0.sh` / `./scripts/verify_phase0.ps1`
- 版本管理:git 已初始化,main 分支,远端 origin 为 Gitea `opc/soft_quay`;harness 文档已提交
- 当前 blocker:无;下一个任务是 T-604 workspace 隔离加固,完成审核整改后再恢复 T-302 安装流程整合
- 当前 blocker:无;下一步按审核定稿落成状态模型收口任务,其余审核整改完成后再恢复 T-302 安装流程整合
## 当前目录要点
| 路径 | 状态 | 说明 |
| --- | --- | --- |
| `docs/` | 已有 | harness coding 文档集(本次初始化完成) |
| `docs/tasks/` | 已有 | Phase 0~2 与 T-301 已完成;T-604 审核整改已落成并待领取;T-302 暂后置 |
| `docs/tasks/` | 已有 | Phase 0~2、T-301 与 T-604 已完成;下一审核整改任务尚未落成;T-302 暂后置 |
| `scripts/` | 已有 | harness 治理、core 边界、Go 版本检查与 Phase 0 双平台验证入口 |
| `core/` | 已建 | Go 1.20 兼容;已有正式 Catalog、本地状态/存储、列表模型、图标缓存、可恢复下载队列与 Phase 1 安装安全原型 |
| `app-modern/` | 已建 | Go 1.25.0 + Gio v0.10.1;Modern AppShell 已接入虚拟列表、详情和内存图标 |
@@ -40,9 +40,9 @@
任务状态以 `docs/tasks/` 各任务文件 frontmatter 的 `status` 为准。本节只写项目级摘要:
- 已完成:Phase 0 的 `T-001`~`T-004`;Phase 1 的 `T-101`、`T-102`、`T-103`;Phase 2 的 `T-201`~`T-204`;Phase 3 的 `T-301`。
- 已完成:Phase 0 的 `T-001`~`T-004`;Phase 1 的 `T-101`、`T-102`、`T-103`;Phase 2 的 `T-201`~`T-204`;Phase 3 的 `T-301`;审核整改 `T-604`。
- 正在进行:无。
- 下一个可领取任务:`T-604 隔离 modern 与 Win7 workspace`;完成并提交后继续按审核定稿拆分下一整改任务。
- 下一个可领取任务:无;先按 `docs/review/phase0-skeleton-review.md` 落成下一状态模型收口任务。
## 当前可运行内容
+10 -5
View File
@@ -3,12 +3,12 @@ id: T-604
title: 隔离 modern 与 Win7 workspace
phase: 0
deps: [T-004]
status: TODO
status: DONE
created: 2026-07-16
issue: null
context_ref: null
context_ref: e4a9295cbf94d645370d2583866b7ca91c0c5271
claim_branch: null
work_branch: null
work_branch: agent/codex/T-604
write_paths:
- docs/tasks/T-604.md
- go.work
@@ -36,7 +36,7 @@ Phase 0 交叉审核确认根 `go.work` 同时纳入 `app-modern`、`app-win7`
1. 根 `go.work` 只纳入 `app-modern` 与 `core`;`app-win7` 只由 `app-win7/go.work` 纳入并继续与 `core` 共享本地替换。
2. `init.sh`、`init.ps1`、`verify_phase0.sh` 与 `verify_phase0.ps1` 分别同步两个 workspace:
- Go 1.25.0 同步根 workspace。
- Go 1.20.14 + 显式 `GOWORK=app-win7/go.work` 同步 Win7 workspace。
- Go 1.25.0 + 显式 `GOWORK=app-win7/go.work` 同步 Win7 workspace 元数据;随后必须用 Go 1.20.14 校验实际依赖图、测试和构建。
3. 加固 `scripts/check_go_versions.py`:
- 校验根 workspace 只包含 modern/core。
- 校验 Win7 workspace 只包含 win7/core。
@@ -50,7 +50,7 @@ Phase 0 交叉审核确认根 `go.work` 同时纳入 `app-modern`、`app-win7`
- `go work edit -json` 显示根 workspace 只有 `./app-modern`、`./core`;`app-win7/go.work` 只有 `.`、`../core`。
- 根 workspace 执行 `go list -m gioui.org` 得到 v0.10.1;Win7 workspace 使用 Go 1.20.14 执行同命令得到 v0.6.0。
- 根 workspace 不再把 `./app-win7/...` 当作自身 package pattern 构建;Win7 test/build 必须显式使用嵌套 workspace。
- 两个 workspace 的 sync 都由 `init` 与 `verify_phase0` 执行;sync 后 `core/go.mod`、`app-modern/go.mod`、`app-win7/go.mod` 无意外漂移。
- 两个 workspace 的 sync 都由 `init` 与 `verify_phase0` 执行;Win7 sync 使用 Go 1.25.0 规避 Go 1.20.14 对 workspace replace 的已验证限制,但 Win7 依赖图/test/build 仍强制 Go 1.20.14;sync 后三个 go.mod 无意外漂移。
- `python scripts/check_go_versions.py` 能在 workspace 成员或 Gio 解析版本错误时失败。
- gopls/IDE 打开仓库后,Win7 文件使用 Gio v0.6.0 语义;若当前环境没有 gopls,执行记录必须写明限制并至少提供嵌套 workspace 的 `go list/test` 证据。
- `./scripts/verify_phase0.ps1` 与 `bash scripts/verify_phase0.sh` 全绿,modern/Win7 EXE 均重新产出。
@@ -74,3 +74,8 @@ Phase 0 交叉审核确认根 `go.work` 同时纳入 `app-modern`、`app-win7`
## 执行记录
- 2026-07-16:根据 `docs/review/phase0-skeleton-review.md` 的交叉复核定稿落成首个整改任务;编号按路线图与现有任务最大编号 `T-603 + 1` 取 `T-604`。
- 2026-07-16:在 `agent/codex/T-604` 分支领取任务;基线为 `e4a9295cbf94d645370d2583866b7ca91c0c5271`。当前环境未安装 gopls,后续以嵌套 workspace 的实际 `go list/test/build` 作为自动化证据,并在收尾记录该环境限制。
- 2026-07-16:真实执行发现 Go 1.20.14 `go work sync` 虽能用 workspace replace 完成 `go list`,却会在 sync 时联网解析 `softbox.local/core@v0.0.0` 并失败;Go 1.25.0 对同一 Win7 workspace sync 成功且三个 go.mod 哈希不变。决定用 Go 1.25.0 同步两套 workspace 元数据,继续用 Go 1.20.14 强制验证和构建 Win7,不向 go.mod 写本地 replace。
- 2026-07-16:根 `go.work` 移除 `app-win7`,清理根 `go.work.sum` 中 Win7 专属旧校验和;`init`/`verify_phase0` 分别同步两套 workspace并显式传递绝对 `GOWORK`。`check_go_versions.py` 现校验 workspace 成员、实际 Gio 解析版本、Go 1.20.14 工具链与 core/win7 完整模块图,并带错误分支 self-check。
- 2026-07-16:隔离证据通过:根 `go work edit -json` 仅含 `app-modern/core`,Gio 为 v0.10.1;Win7 workspace 仅含 `app-win7/core`,Go 1.20.14 解析 Gio v0.6.0;根 workspace 的 `go list ./app-win7/...` 按预期拒绝;三个 go.mod 前后哈希完全一致。当前环境未安装 gopls,已按验收规则记录限制并用实际 list/test/build 替代自动化证据。
- 2026-07-16:验证通过:`python scripts/check_go_versions.py`;`./scripts/verify_phase0.ps1`;`C:\Program Files\Git\bin\bash.exe scripts/verify_phase0.sh`;`./init.ps1`(含 `RUN_START_COMMAND=1` 的显式双 workspace build);`C:\Program Files\Git\bin\bash.exe ./init.sh`;治理/边界检查、core Go 1.20.14 vet/test、modern Go 1.25.0 与 Win7 Go 1.20.14 adapter test/GUI build 全绿。
-1
View File
@@ -2,7 +2,6 @@ go 1.25.0
use (
./app-modern
./app-win7
./core
)
-2
View File
@@ -1,8 +1,6 @@
eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d/go.mod h1:OYVuxibdk9OSLX8vAqydtRPP87PyTFcT9uH3MlEGBQA=
gioui.org v0.6.0/go.mod h1:eUvGo6FAzA7jUqeSu5a+M1W03yc9r1nanIBS8A5+Nng=
gioui.org v0.10.1 h1:Dvp6iDk9RKuZk19jxhOmb4p673CLVvb656LyMxQ+uO0=
gioui.org v0.10.1/go.mod h1:MZJZsdEPkTBzChdqeE8CiiQhreUQBj43qusDxQNDf7k=
gioui.org/cpu v0.0.0-20210808092351-bfe733dd3334/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ=
gioui.org/shader v1.0.8 h1:6ks0o/A+b0ne7RzEqRZK5f4Gboz2CfG+mVliciy6+qA=
gioui.org/shader v1.0.8/go.mod h1:mWdiME581d/kV7/iEhLmUgUK5iZ09XR5XpduXzbePVM=
github.com/go-text/typesetting v0.3.4 h1:YYurUOtEb9kGSOz4uE3k4OpBGsp1dDL8+fjCeaFamAU=
+11 -2
View File
@@ -12,17 +12,23 @@ $Utf8 = [System.Text.UTF8Encoding]::new($false)
$OutputEncoding = $Utf8
Set-Location -Path $PSScriptRoot
$InstallCmd = "go work sync"
$InstallCmd = '$env:GOTOOLCHAIN="go1.25.0"; $env:GOWORK=(Resolve-Path "go.work").Path; go work sync; if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }; $env:GOWORK=(Resolve-Path "app-win7/go.work").Path; go work sync'
$VerifyCmd = "./scripts/verify_phase0.ps1"
$StartCmd = '$env:CGO_ENABLED="0"; $env:GOOS="windows"; $env:GOARCH="amd64"; $env:GOTOOLCHAIN="go1.25.0"; go build -C app-modern -trimpath "-ldflags=-H=windowsgui" -o ../dist/SoftBox.exe ./cmd/softbox; if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }; $env:GOTOOLCHAIN="go1.20.14"; go build -C app-win7 -trimpath "-ldflags=-H=windowsgui" -o ../dist/SoftBox-win7.exe ./cmd/softbox'
$StartCmd = '$env:CGO_ENABLED="0"; $env:GOOS="windows"; $env:GOARCH="amd64"; $env:GOTOOLCHAIN="go1.25.0"; $env:GOWORK=(Resolve-Path "go.work").Path; go build -C app-modern -trimpath "-ldflags=-H=windowsgui" -o ../dist/SoftBox.exe ./cmd/softbox; if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }; $env:GOTOOLCHAIN="go1.20.14"; $env:GOWORK=(Resolve-Path "app-win7/go.work").Path; go build -C app-win7 -trimpath "-ldflags=-H=windowsgui" -o ../dist/SoftBox-win7.exe ./cmd/softbox'
Write-Host "==> 当前目录: $($PWD.Path)"
Write-Host "==> 同步依赖"
Invoke-Expression $InstallCmd
if ($LASTEXITCODE -ne 0) {
exit $LASTEXITCODE
}
Write-Host "==> 运行基础验证"
Invoke-Expression $VerifyCmd
if ($LASTEXITCODE -ne 0) {
exit $LASTEXITCODE
}
Write-Host "==> 启动命令"
Write-Host " $StartCmd"
@@ -30,6 +36,9 @@ Write-Host " $StartCmd"
if ($env:RUN_START_COMMAND -eq "1") {
Write-Host "==> 启动应用"
Invoke-Expression $StartCmd
if ($LASTEXITCODE -ne 0) {
exit $LASTEXITCODE
}
} else {
Write-Host "如果希望 init.ps1 直接启动应用,请设置环境变量 RUN_START_COMMAND=1。"
Write-Host "如果基础验证失败,先修复基线状态,不要在坏的起点上继续叠新功能。"
+2 -2
View File
@@ -11,9 +11,9 @@ set -euo pipefail
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$ROOT_DIR"
INSTALL_CMD="go work sync"
INSTALL_CMD="GOTOOLCHAIN=go1.25.0 GOWORK=\"$ROOT_DIR/go.work\" go work sync && GOTOOLCHAIN=go1.25.0 GOWORK=\"$ROOT_DIR/app-win7/go.work\" go work sync"
VERIFY_CMD="bash scripts/verify_phase0.sh"
START_CMD="GOTOOLCHAIN=go1.25.0 CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go -C app-modern build -trimpath -ldflags=-H=windowsgui -o ../dist/SoftBox.exe ./cmd/softbox && GOTOOLCHAIN=go1.20.14 CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go -C app-win7 build -trimpath -ldflags=-H=windowsgui -o ../dist/SoftBox-win7.exe ./cmd/softbox"
START_CMD="GOTOOLCHAIN=go1.25.0 GOWORK=\"$ROOT_DIR/go.work\" CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go -C app-modern build -trimpath -ldflags=-H=windowsgui -o ../dist/SoftBox.exe ./cmd/softbox && GOTOOLCHAIN=go1.20.14 GOWORK=\"$ROOT_DIR/app-win7/go.work\" CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go -C app-win7 build -trimpath -ldflags=-H=windowsgui -o ../dist/SoftBox-win7.exe ./cmd/softbox"
echo "==> 当前目录: $PWD"
+173 -6
View File
@@ -10,6 +10,8 @@ import sys
MAX_LEGACY_GO_VERSION = (1, 20)
MODERN_TOOLCHAIN = "go1.25.0"
LEGACY_TOOLCHAIN = "go1.20.14"
def version_tuple(value):
@@ -52,9 +54,10 @@ def required_version(path, module_path):
return match.group(1)
def run_go(repo_root, cwd, arguments, go_work):
def run_go(cwd, arguments, go_work, toolchain):
environment = os.environ.copy()
environment["GOWORK"] = str(go_work) if go_work else "off"
environment["GOTOOLCHAIN"] = toolchain
result = subprocess.run(
["go"] + arguments,
cwd=str(cwd),
@@ -70,6 +73,130 @@ def run_go(repo_root, cwd, arguments, go_work):
return result.stdout
def workspace_modules(workspace, toolchain):
document = json.loads(
run_go(
workspace.parent,
["work", "edit", "-json"],
workspace,
toolchain,
)
)
return {
(workspace.parent / entry["DiskPath"]).resolve()
for entry in document.get("Use", [])
}
def display_path(path, repo_root):
try:
return str(path.relative_to(repo_root))
except ValueError:
return str(path)
def workspace_layout_violation(workspace, actual, expected, repo_root):
if actual == expected:
return None
actual_names = sorted(display_path(path, repo_root) for path in actual)
expected_names = sorted(display_path(path, repo_root) for path in expected)
return "{} contains {}, want {}".format(
display_path(workspace, repo_root),
actual_names,
expected_names,
)
def validate_workspace_layout(repo_root):
root_workspace = repo_root / "go.work"
win7_workspace = repo_root / "app-win7" / "go.work"
expected = {
root_workspace: {
(repo_root / "app-modern").resolve(),
(repo_root / "core").resolve(),
},
win7_workspace: {
(repo_root / "app-win7").resolve(),
(repo_root / "core").resolve(),
},
}
toolchains = {
root_workspace: MODERN_TOOLCHAIN,
win7_workspace: LEGACY_TOOLCHAIN,
}
violations = []
for workspace, expected_modules in expected.items():
actual_modules = workspace_modules(workspace, toolchains[workspace])
violation = workspace_layout_violation(
workspace,
actual_modules,
expected_modules,
repo_root,
)
if violation:
violations.append(violation)
return violations
def resolved_module_version(cwd, workspace, toolchain, module_path):
document = json.loads(
run_go(
cwd,
["list", "-m", "-json", module_path],
workspace,
toolchain,
)
)
return document.get("Version", "")
def module_version_violation(label, module_path, actual, expected):
if actual == expected:
return None
return "{} workspace resolves {} {}, want {}".format(
label,
module_path,
actual,
expected,
)
def validate_workspace_gio_versions(repo_root):
checks = (
(
"modern",
repo_root / "app-modern",
repo_root / "go.work",
MODERN_TOOLCHAIN,
"v0.10.1",
),
(
"win7",
repo_root / "app-win7",
repo_root / "app-win7" / "go.work",
LEGACY_TOOLCHAIN,
"v0.6.0",
),
)
violations = []
for label, cwd, workspace, toolchain, expected in checks:
actual = resolved_module_version(
cwd,
workspace,
toolchain,
"gioui.org",
)
violation = module_version_violation(
label,
"gioui.org",
actual,
expected,
)
if violation:
violations.append(violation)
return violations
def validate_pins(repo_root):
expected_go_directives = {
repo_root / "go.work": "1.25.0",
@@ -106,7 +233,12 @@ def validate_pins(repo_root):
def validate_go20_toolchain(repo_root):
output = run_go(repo_root, repo_root, ["version"], None).strip()
output = run_go(
repo_root,
["version"],
None,
LEGACY_TOOLCHAIN,
).strip()
if "go1.20.14" not in output:
return ["compatibility scan uses {!r}, want go1.20.14".format(output)]
return []
@@ -114,22 +246,23 @@ def validate_go20_toolchain(repo_root):
def validate_module_versions(repo_root):
module_sets = (
("core", repo_root / "core", None),
("core", repo_root / "core", None, LEGACY_TOOLCHAIN),
(
"win7",
repo_root / "app-win7",
repo_root / "app-win7" / "go.work",
LEGACY_TOOLCHAIN,
),
)
violations = []
checked = 0
for label, cwd, go_work in module_sets:
for label, cwd, go_work, toolchain in module_sets:
output = run_go(
repo_root,
cwd,
["list", "-m", "-json", "all"],
go_work,
toolchain,
)
for module in decode_json_stream(output):
checked += 1
@@ -153,12 +286,45 @@ def self_check():
raise AssertionError("Go 1.20.14 should be accepted")
if not version_tuple("1.21")[:2] > MAX_LEGACY_GO_VERSION:
raise AssertionError("Go 1.21 should be rejected")
fake_root = pathlib.Path("/repo")
fake_workspace = fake_root / "go.work"
fake_expected = {fake_root / "core", fake_root / "app-modern"}
if workspace_layout_violation(
fake_workspace,
fake_expected,
fake_expected,
fake_root,
):
raise AssertionError("matching workspace layout should be accepted")
if not workspace_layout_violation(
fake_workspace,
fake_expected | {fake_root / "app-win7"},
fake_expected,
fake_root,
):
raise AssertionError("unexpected workspace module should be rejected")
if module_version_violation(
"win7",
"gioui.org",
"v0.6.0",
"v0.6.0",
):
raise AssertionError("matching Gio version should be accepted")
if not module_version_violation(
"win7",
"gioui.org",
"v0.10.1",
"v0.6.0",
):
raise AssertionError("mismatched Gio version should be rejected")
def main():
self_check()
repo_root = pathlib.Path(__file__).resolve().parents[1]
violations = validate_pins(repo_root)
violations.extend(validate_workspace_layout(repo_root))
violations.extend(validate_workspace_gio_versions(repo_root))
violations.extend(validate_go20_toolchain(repo_root))
module_violations, checked = validate_module_versions(repo_root)
violations.extend(module_violations)
@@ -169,7 +335,8 @@ def main():
raise SystemExit(1)
print(
"Go version check passed: pins valid; {} module records are Go 1.20-compatible.".format(
"Go version check passed: workspace isolation and pins valid; "
"{} module records are Go 1.20-compatible.".format(
checked
)
)
+9 -2
View File
@@ -35,7 +35,13 @@ New-Item -ItemType Directory -Force -Path (Join-Path $Root "dist") | Out-Null
Invoke-Step "Sync root workspace" {
$env:GOTOOLCHAIN = "go1.25.0"
Remove-Item Env:GOWORK -ErrorAction SilentlyContinue
$env:GOWORK = (Resolve-Path "go.work").Path
go work sync
}
Invoke-Step "Sync Win7 workspace" {
$env:GOTOOLCHAIN = "go1.25.0"
$env:GOWORK = (Resolve-Path "app-win7/go.work").Path
go work sync
}
@@ -54,7 +60,8 @@ Invoke-Step "Validate core architecture boundary" {
}
Invoke-Step "Validate Go and dependency pins" {
$env:GOTOOLCHAIN = "go1.20.14"
Remove-Item Env:GOTOOLCHAIN -ErrorAction SilentlyContinue
Remove-Item Env:GOWORK -ErrorAction SilentlyContinue
python scripts/check_go_versions.py
}
+5 -2
View File
@@ -26,7 +26,10 @@ fi
mkdir -p dist
echo "==> Sync root workspace"
GOTOOLCHAIN=go1.25.0 go work sync
GOTOOLCHAIN=go1.25.0 GOWORK="$ROOT_DIR/go.work" go work sync
echo "==> Sync Win7 workspace"
GOTOOLCHAIN=go1.25.0 GOWORK="$ROOT_DIR/app-win7/go.work" go work sync
echo "==> Validate harness governance"
"$PYTHON" scripts/validate_agent_context.py
@@ -38,7 +41,7 @@ echo "==> Validate core architecture boundary"
"$PYTHON" scripts/check_core_boundaries.py
echo "==> Validate Go and dependency pins"
GOTOOLCHAIN=go1.20.14 "$PYTHON" scripts/check_go_versions.py
"$PYTHON" scripts/check_go_versions.py
echo "==> Vet and test core with Go 1.20.14"
GOTOOLCHAIN=go1.20.14 GOWORK=off go -C core vet ./...