From bb4b205aa08358760a684431bc11a003332a97d0 Mon Sep 17 00:00:00 2001 From: youfak Date: Fri, 7 Aug 2026 17:39:23 +0800 Subject: [PATCH] docs: package versioned delivery artifacts --- README.md | 7 +- docs/development/implementation-plan.md | 6 +- docs/package_test.go | 81 +++++++++++++++++++++ docs/requirements/completion-audit.md | 2 + scripts/package-docs.ps1 | 94 +++++++++++++++++++++++++ task_plan.md | 3 +- 6 files changed, 190 insertions(+), 3 deletions(-) create mode 100644 docs/package_test.go create mode 100644 scripts/package-docs.ps1 diff --git a/README.md b/README.md index 4c11d4e..12da9cd 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/docs/development/implementation-plan.md b/docs/development/implementation-plan.md index e169b14..1e6ee2e 100644 --- a/docs/development/implementation-plan.md +++ b/docs/development/implementation-plan.md @@ -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 diff --git a/docs/package_test.go b/docs/package_test.go new file mode 100644 index 0000000..d239784 --- /dev/null +++ b/docs/package_test.go @@ -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) + } + } +} diff --git a/docs/requirements/completion-audit.md b/docs/requirements/completion-audit.md index 5002a95..ae1340d 100644 --- a/docs/requirements/completion-audit.md +++ b/docs/requirements/completion-audit.md @@ -11,6 +11,8 @@ - 产品设计、总体架构、项目结构、四项 ADR。 - 开发、配置、Distribution/Admin API、控制面协议、安全、测试、运维文档。 - 20 个配置场景和 35 张 Mermaid 架构/流程/状态/故障图。 +- `scripts/package-docs.ps1` 可生成版本化 ZIP,包含 README、文档、图表、OpenAPI、Proto + 契约,以及带 Git revision 和 SHA-256 的文件清单。 ### 机器契约 diff --git a/scripts/package-docs.ps1 b/scripts/package-docs.ps1 new file mode 100644 index 0000000..db4fd29 --- /dev/null +++ b/scripts/package-docs.ps1 @@ -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 + } +} diff --git a/task_plan.md b/task_plan.md index 47c9ed9..79f2897 100644 --- a/task_plan.md +++ b/task_plan.md @@ -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、公用行为契约和