docs: package versioned delivery artifacts
This commit is contained in:
parent
a384fce1b0
commit
bb4b205aa0
@ -116,7 +116,7 @@ flowchart LR
|
||||
|
||||
## 当前完成度
|
||||
|
||||
截至 **2026-08-07**,实施计划中可直接勾选的检查项为 **69 / 75(92.0%)**。详情见
|
||||
截至 **2026-08-07**,实施计划中可直接勾选的检查项为 **70 / 75(93.3%)**。详情见
|
||||
[实施计划](docs/development/implementation-plan.md)和
|
||||
[交付完成度审计](docs/requirements/completion-audit.md)。
|
||||
|
||||
@ -159,8 +159,13 @@ Secret 分离管理。
|
||||
```powershell
|
||||
go run ./deploy/tools/configcheck deploy/config/local.yaml
|
||||
./scripts/verify.ps1
|
||||
./scripts/package-docs.ps1
|
||||
```
|
||||
|
||||
`package-docs.ps1` 默认生成被忽略的 `dist/proxy-pool-docs-v1.0.zip`;包内包含 README、
|
||||
`docs/`、图表、OpenAPI 和 Proto 契约,并以 `manifest.json` 记录 Git revision、文件大小和
|
||||
SHA-256。可使用 `-Version vMAJOR.MINOR[.PATCH]` 与 `-OutputPath OUTPUT.zip` 生成指定交付物。
|
||||
|
||||
Redis、PostgreSQL 和 Controller fixture 脚本会使用 Docker 启动隔离依赖:
|
||||
|
||||
```powershell
|
||||
|
||||
@ -384,7 +384,11 @@ CI 另有独立 Deployment job,在占位凭据下渲染 Compose,并使用 `k
|
||||
reference, API guide, deployment guide, security model, testing guide, and roadmap.
|
||||
- [x] Provide at least 20 validated configuration examples.
|
||||
- [x] Provide at least 30 Mermaid architecture, flow, sequence, state, and failure diagrams.
|
||||
- [ ] Generate `proxy-pool-docs-v1.0.zip` from versioned documentation assets.
|
||||
- [x] Generate `proxy-pool-docs-v1.0.zip` from versioned documentation assets.
|
||||
|
||||
`scripts/package-docs.ps1` 会将 README、`docs/`、图表、OpenAPI 与 Proto 契约复制至临时目录,
|
||||
生成包含 Git revision、文件大小和 SHA-256 的 `manifest.json`,再以临时 ZIP 原子替换目标。
|
||||
默认输出为被 Git 忽略的 `dist/proxy-pool-docs-v1.0.zip`;Go 回归测试实际执行脚本并校验归档内容。
|
||||
|
||||
## Task 15: Completion Audit
|
||||
|
||||
|
||||
81
docs/package_test.go
Normal file
81
docs/package_test.go
Normal file
@ -0,0 +1,81 @@
|
||||
package docs_test
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type docsPackageManifest struct {
|
||||
Version string `json:"version"`
|
||||
Revision string `json:"revision"`
|
||||
Files []struct {
|
||||
Path string `json:"path"`
|
||||
SHA256 string `json:"sha256"`
|
||||
} `json:"files"`
|
||||
}
|
||||
|
||||
func TestPackageDocsCreatesTraceableArchive(t *testing.T) {
|
||||
pwsh, err := exec.LookPath("pwsh")
|
||||
if err != nil {
|
||||
t.Skip("pwsh is required to exercise the documentation packaging script")
|
||||
}
|
||||
repositoryRoot := repositoryRoot(t)
|
||||
outputPath := filepath.Join(t.TempDir(), "proxy-pool-docs-v1.0.zip")
|
||||
if err := os.WriteFile(outputPath, []byte("incomplete"), 0o600); err != nil {
|
||||
t.Fatalf("seed old documentation archive: %v", err)
|
||||
}
|
||||
command := exec.Command(pwsh, "-NoProfile", "-NonInteractive", "-File", filepath.Join(repositoryRoot, "scripts", "package-docs.ps1"), "-OutputPath", outputPath)
|
||||
command.Dir = repositoryRoot
|
||||
if output, err := command.CombinedOutput(); err != nil {
|
||||
t.Fatalf("package docs: %v\n%s", err, output)
|
||||
}
|
||||
|
||||
archive, err := zip.OpenReader(outputPath)
|
||||
if err != nil {
|
||||
t.Fatalf("open documentation archive: %v", err)
|
||||
}
|
||||
defer archive.Close()
|
||||
entries := make(map[string]*zip.File, len(archive.File))
|
||||
for _, entry := range archive.File {
|
||||
entries[entry.Name] = entry
|
||||
}
|
||||
for _, required := range []string{
|
||||
"README.md",
|
||||
"docs/design/architecture.md",
|
||||
"docs/operations/runbook.md",
|
||||
"diagrams/README.md",
|
||||
"api/openapi/proxy-pool.yaml",
|
||||
"api/proto/controlplane/v1/controlplane.proto",
|
||||
"manifest.json",
|
||||
} {
|
||||
if _, exists := entries[required]; !exists {
|
||||
t.Errorf("documentation archive does not contain %s", required)
|
||||
}
|
||||
}
|
||||
|
||||
manifestEntry, exists := entries["manifest.json"]
|
||||
if !exists {
|
||||
return
|
||||
}
|
||||
reader, err := manifestEntry.Open()
|
||||
if err != nil {
|
||||
t.Fatalf("open manifest: %v", err)
|
||||
}
|
||||
defer reader.Close()
|
||||
var manifest docsPackageManifest
|
||||
if err := json.NewDecoder(reader).Decode(&manifest); err != nil {
|
||||
t.Fatalf("decode manifest: %v", err)
|
||||
}
|
||||
if manifest.Version != "v1.0" || manifest.Revision == "" || len(manifest.Files) < 20 {
|
||||
t.Fatalf("manifest = %+v", manifest)
|
||||
}
|
||||
for _, file := range manifest.Files {
|
||||
if file.Path == "" || file.SHA256 == "" {
|
||||
t.Fatalf("invalid manifest file = %+v", file)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -11,6 +11,8 @@
|
||||
- 产品设计、总体架构、项目结构、四项 ADR。
|
||||
- 开发、配置、Distribution/Admin API、控制面协议、安全、测试、运维文档。
|
||||
- 20 个配置场景和 35 张 Mermaid 架构/流程/状态/故障图。
|
||||
- `scripts/package-docs.ps1` 可生成版本化 ZIP,包含 README、文档、图表、OpenAPI、Proto
|
||||
契约,以及带 Git revision 和 SHA-256 的文件清单。
|
||||
|
||||
### 机器契约
|
||||
|
||||
|
||||
94
scripts/package-docs.ps1
Normal file
94
scripts/package-docs.ps1
Normal file
@ -0,0 +1,94 @@
|
||||
param(
|
||||
[string]$Version = "v1.0",
|
||||
[string]$OutputPath = ""
|
||||
)
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($Version) -or $Version -notmatch '^v[0-9]+(?:\.[0-9]+){1,2}$') {
|
||||
throw "Version must use vMAJOR.MINOR or vMAJOR.MINOR.PATCH format"
|
||||
}
|
||||
|
||||
$repositoryRoot = Split-Path -Parent $PSScriptRoot
|
||||
if ([string]::IsNullOrWhiteSpace($OutputPath)) {
|
||||
$OutputPath = Join-Path $repositoryRoot "dist/proxy-pool-docs-$Version.zip"
|
||||
}
|
||||
elseif (-not [System.IO.Path]::IsPathRooted($OutputPath)) {
|
||||
$OutputPath = Join-Path $repositoryRoot $OutputPath
|
||||
}
|
||||
$OutputPath = [System.IO.Path]::GetFullPath($OutputPath)
|
||||
if ([System.IO.Path]::GetExtension($OutputPath) -ne ".zip") {
|
||||
throw "OutputPath must end in .zip"
|
||||
}
|
||||
|
||||
$revision = (& git -C $repositoryRoot rev-parse --verify HEAD 2>$null).Trim()
|
||||
if ($LASTEXITCODE -ne 0 -or [string]::IsNullOrWhiteSpace($revision)) {
|
||||
throw "could not resolve the current Git revision"
|
||||
}
|
||||
|
||||
$sources = @(
|
||||
"README.md",
|
||||
"docs",
|
||||
"diagrams",
|
||||
"api/openapi",
|
||||
"api/proto"
|
||||
)
|
||||
$stagingRoot = Join-Path ([System.IO.Path]::GetTempPath()) ("proxy-pool-docs-" + [Guid]::NewGuid().ToString("N"))
|
||||
$temporaryArchive = ""
|
||||
|
||||
try {
|
||||
New-Item -ItemType Directory -Force -Path $stagingRoot | Out-Null
|
||||
foreach ($relative in $sources) {
|
||||
$source = Join-Path $repositoryRoot $relative
|
||||
if (-not (Test-Path -LiteralPath $source)) {
|
||||
throw "documentation source is missing: $relative"
|
||||
}
|
||||
$destination = Join-Path $stagingRoot $relative
|
||||
$destinationParent = Split-Path -Parent $destination
|
||||
New-Item -ItemType Directory -Force -Path $destinationParent | Out-Null
|
||||
Copy-Item -LiteralPath $source -Destination $destination -Recurse -Force
|
||||
}
|
||||
|
||||
$files = @(Get-ChildItem -LiteralPath $stagingRoot -File -Recurse | Sort-Object FullName)
|
||||
if ($files.Count -eq 0) {
|
||||
throw "documentation package has no files"
|
||||
}
|
||||
$manifestFiles = @(
|
||||
foreach ($file in $files) {
|
||||
[ordered]@{
|
||||
path = [System.IO.Path]::GetRelativePath($stagingRoot, $file.FullName).Replace("\", "/")
|
||||
sha256 = (Get-FileHash -LiteralPath $file.FullName -Algorithm SHA256).Hash.ToLowerInvariant()
|
||||
bytes = $file.Length
|
||||
}
|
||||
}
|
||||
)
|
||||
[ordered]@{
|
||||
package = "proxy-pool-docs"
|
||||
version = $Version
|
||||
revision = $revision
|
||||
generatedAtUtc = [DateTime]::UtcNow.ToString("O")
|
||||
files = $manifestFiles
|
||||
} | ConvertTo-Json -Depth 4 | Set-Content -LiteralPath (Join-Path $stagingRoot "manifest.json") -Encoding utf8
|
||||
|
||||
$outputDirectory = Split-Path -Parent $OutputPath
|
||||
New-Item -ItemType Directory -Force -Path $outputDirectory | Out-Null
|
||||
$temporaryArchive = Join-Path $outputDirectory ("." + [System.IO.Path]::GetFileName($OutputPath) + "." + [Guid]::NewGuid().ToString("N") + ".tmp")
|
||||
Add-Type -AssemblyName System.IO.Compression.FileSystem
|
||||
[System.IO.Compression.ZipFile]::CreateFromDirectory(
|
||||
$stagingRoot,
|
||||
$temporaryArchive,
|
||||
[System.IO.Compression.CompressionLevel]::Optimal,
|
||||
$false
|
||||
)
|
||||
[System.IO.File]::Move($temporaryArchive, $OutputPath, $true)
|
||||
$temporaryArchive = ""
|
||||
Write-Host "documentation package: $OutputPath ($($manifestFiles.Count) files, $revision)"
|
||||
}
|
||||
finally {
|
||||
if (-not [string]::IsNullOrWhiteSpace($temporaryArchive) -and (Test-Path -LiteralPath $temporaryArchive)) {
|
||||
Remove-Item -LiteralPath $temporaryArchive -Force
|
||||
}
|
||||
if (Test-Path -LiteralPath $stagingRoot) {
|
||||
Remove-Item -LiteralPath $stagingRoot -Recurse -Force
|
||||
}
|
||||
}
|
||||
@ -23,7 +23,8 @@
|
||||
Fetch 分类、严格配置、不可变快照和本地调度参考实现
|
||||
7. [已完成] 执行单元测试、静态检查、构建和静态部署/契约验证;本机因
|
||||
`CGO_ENABLED=0` 且无 C 编译器未运行 race,保留给 Linux CI
|
||||
8. [已完成] 按需求矩阵逐项审计并生成版本化文档包
|
||||
8. [已完成] 按需求矩阵逐项审计;`scripts/package-docs.ps1` 可生成带 revision 与 SHA-256
|
||||
清单的版本化文档包
|
||||
9. [已完成] 将 Proxy 明细、独占提取、短期幂等和 Worker 所有权统一到
|
||||
TTL 活动池契约;PostgreSQL 退出代理数据路径
|
||||
10. [已完成] 实现生产 Redis Activity Adapter、原子 Lua、公用行为契约和
|
||||
|
||||
Loading…
Reference in New Issue
Block a user