统一源码换行并验证跨配置签出一致性

This commit is contained in:
xuans 2026-09-27 18:29:26 +08:00
parent 510a854f66
commit 94237b8e7d
4 changed files with 64 additions and 0 deletions

1
.gitattributes vendored
View File

@ -1,2 +1,3 @@
* text=auto eol=lf
*.sh text eol=lf *.sh text eol=lf
*.service text eol=lf *.service text eol=lf

View File

@ -81,6 +81,66 @@ func TestRepositoryIgnoreBoundaries(t *testing.T) {
} }
} }
func TestRepositoryCheckoutLineEndings(t *testing.T) {
root, sandbox := repositoryIgnoreFixture(t)
attributes, err := os.ReadFile(filepath.Join(root, ".gitattributes"))
if err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(sandbox, ".gitattributes"), attributes, 0o600); err != nil {
t.Fatal(err)
}
text := []byte("first line\nsecond line\n")
files := map[string][]byte{
"backend/go.mod": text,
"backend/go.sum": text,
"frontend/package.json": text,
"frontend/package-lock.json": text,
"frontend/.npmrc": text,
"frontend/index.html": text,
"frontend/src/App.vue": text,
"frontend/src/App.js": text,
"frontend/src/App.css": text,
"build/toolchain.mjs": text,
"build/build-release.ps1": text,
"frontend/logo/fixture.png": {0x89, 0, 13, 10, 0xff},
}
arguments := []string{"add", "--", ".gitattributes"}
for path, data := range files {
fullPath := filepath.Join(sandbox, filepath.FromSlash(path))
if err := os.MkdirAll(filepath.Dir(fullPath), 0o700); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(fullPath, data, 0o600); err != nil {
t.Fatal(err)
}
arguments = append(arguments, path)
}
if output, err := repositoryGitCommand(sandbox, arguments...).CombinedOutput(); err != nil {
t.Fatalf("准备换行测试索引失败:%v\n%s", err, output)
}
// 从相同索引实际签出,证明用户换行设置不会改变文本,也不会转换二进制。
for _, value := range []string{"true", "false", "input"} {
t.Run(value, func(t *testing.T) {
destination := t.TempDir()
command := repositoryGitCommand(sandbox, "-c", "core.autocrlf="+value,
"checkout-index", "--all", "--prefix="+filepath.ToSlash(destination)+"/")
if output, err := command.CombinedOutput(); err != nil {
t.Fatalf("签出换行测试源码失败:%v\n%s", err, output)
}
for path, expected := range files {
actual, err := os.ReadFile(filepath.Join(destination, filepath.FromSlash(path)))
if err != nil {
t.Fatal(err)
}
if !bytes.Equal(actual, expected) {
t.Errorf("签出改变了构建输入或二进制内容:%s", path)
}
}
})
}
}
func repositoryIgnoreFixture(t *testing.T) (string, string) { func repositoryIgnoreFixture(t *testing.T) (string, string) {
t.Helper() t.Helper()
if _, err := exec.LookPath("git"); err != nil { if _, err := exec.LookPath("git"); err != nil {

View File

@ -73,6 +73,8 @@ powershell -ExecutionPolicy Bypass -File .\build\build-release.ps1 -OutputDirect
从 `npm test` 等父进程启动检查器时,npm 可能将用户级 `allow-scripts` 导出到环境变量,而项目级安装拒绝这种传递。本入口仅从子进程环境移除该白名单变量,不改用户配置;`--ignore-scripts` 仍强制生效。正式测试覆盖嵌套调用和主动尝试放行脚本的环境设置,不能通过开启安装脚本解决此兼容问题。 从 `npm test` 等父进程启动检查器时,npm 可能将用户级 `allow-scripts` 导出到环境变量,而项目级安装拒绝这种传递。本入口仅从子进程环境移除该白名单变量,不改用户配置;`--ignore-scripts` 仍强制生效。正式测试覆盖嵌套调用和主动尝试放行脚本的环境设置,不能通过开启安装脚本解决此兼容问题。
仓库 `.gitattributes` 对自动识别的文本固定 LF,避免不同 `core.autocrlf` 设置改变 Vue/CSS/HTML 构建输入及产物哈希,二进制保持原样。已有工作目录不会因此被强制重写;正式验证从干净克隆执行,不用脚本批量替换中文文件或清理既有数据。正式 Go 测试实际签出三种换行设置并检查文本和二进制字节。归档生成时间和源码提交信息仍可能影响包字节,因此不能把前端文件一致等同于所有归档逐字节一致。
## 本机路径保护 ## 本机路径保护
正式服务端和辅助程序统一使用 `-trimpath`。手动构建也必须保留此参数,例如 `go build -trimpath -o scert ./cmd/scert`。前端生产构建明确关闭源码映射。 正式服务端和辅助程序统一使用 `-trimpath`。手动构建也必须保留此参数,例如 `go build -trimpath -o scert ./cmd/scert`。前端生产构建明确关闭源码映射。

View File

@ -47,6 +47,7 @@ npm.cmd run build
| 安装脚本 | `.npmrc` 固定 `ignore-scripts=true`,正式入口再显式传参 | npm 未审查警告不是安全保证,禁止白名单和全量放行 | | 安装脚本 | `.npmrc` 固定 `ignore-scripts=true`,正式入口再显式传参 | npm 未审查警告不是安全保证,禁止白名单和全量放行 |
| 可选平台包 | `npm ci` 显式包含开发和可选依赖 | esbuild 不执行安装脚本,使用锁文件中的平台包;外部二进制覆盖被拒绝 | | 可选平台包 | `npm ci` 显式包含开发和可选依赖 | esbuild 不执行安装脚本,使用锁文件中的平台包;外部二进制覆盖被拒绝 |
| 发布追溯 | `manifest.json` 的 `build_environment` | 包含实际工具链、系统、直接前端依赖、安装策略和五份输入文件 SHA256;Go 驱动及依赖由模块文件哈希追溯 | | 发布追溯 | `manifest.json` 的 `build_environment` | 包含实际工具链、系统、直接前端依赖、安装策略和五份输入文件 SHA256;Go 驱动及依赖由模块文件哈希追溯 |
| 源码换行 | `.gitattributes` 固定文本 LF,二进制自动识别并保留 | 覆盖用户 `core.autocrlf` 差异;原目录不强制重写,正式验证使用干净克隆 |
正式发布入口自动校验工具链、核验 Go 模块缓存、按锁文件干净安装,并在构建结束后复核输入文件。环境不符时尽早失败;构建中版本文件漂移则不生成发布清单。`toolchain.mjs` 和配套测试属于正式构建代码,不作为替代真实部署的辅助验收脚本。具体流程见 `build/README.md`。 正式发布入口自动校验工具链、核验 Go 模块缓存、按锁文件干净安装,并在构建结束后复核输入文件。环境不符时尽早失败;构建中版本文件漂移则不生成发布清单。`toolchain.mjs` 和配套测试属于正式构建代码,不作为替代真实部署的辅助验收脚本。具体流程见 `build/README.md`。