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/development/implementation-plan.md)和
|
||||||
[交付完成度审计](docs/requirements/completion-audit.md)。
|
[交付完成度审计](docs/requirements/completion-audit.md)。
|
||||||
|
|
||||||
@ -159,8 +159,13 @@ Secret 分离管理。
|
|||||||
```powershell
|
```powershell
|
||||||
go run ./deploy/tools/configcheck deploy/config/local.yaml
|
go run ./deploy/tools/configcheck deploy/config/local.yaml
|
||||||
./scripts/verify.ps1
|
./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 启动隔离依赖:
|
Redis、PostgreSQL 和 Controller fixture 脚本会使用 Docker 启动隔离依赖:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
|
|||||||
@ -384,7 +384,11 @@ CI 另有独立 Deployment job,在占位凭据下渲染 Compose,并使用 `k
|
|||||||
reference, API guide, deployment guide, security model, testing guide, and roadmap.
|
reference, API guide, deployment guide, security model, testing guide, and roadmap.
|
||||||
- [x] Provide at least 20 validated configuration examples.
|
- [x] Provide at least 20 validated configuration examples.
|
||||||
- [x] Provide at least 30 Mermaid architecture, flow, sequence, state, and failure diagrams.
|
- [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
|
## 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。
|
- 产品设计、总体架构、项目结构、四项 ADR。
|
||||||
- 开发、配置、Distribution/Admin API、控制面协议、安全、测试、运维文档。
|
- 开发、配置、Distribution/Admin API、控制面协议、安全、测试、运维文档。
|
||||||
- 20 个配置场景和 35 张 Mermaid 架构/流程/状态/故障图。
|
- 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 分类、严格配置、不可变快照和本地调度参考实现
|
Fetch 分类、严格配置、不可变快照和本地调度参考实现
|
||||||
7. [已完成] 执行单元测试、静态检查、构建和静态部署/契约验证;本机因
|
7. [已完成] 执行单元测试、静态检查、构建和静态部署/契约验证;本机因
|
||||||
`CGO_ENABLED=0` 且无 C 编译器未运行 race,保留给 Linux CI
|
`CGO_ENABLED=0` 且无 C 编译器未运行 race,保留给 Linux CI
|
||||||
8. [已完成] 按需求矩阵逐项审计并生成版本化文档包
|
8. [已完成] 按需求矩阵逐项审计;`scripts/package-docs.ps1` 可生成带 revision 与 SHA-256
|
||||||
|
清单的版本化文档包
|
||||||
9. [已完成] 将 Proxy 明细、独占提取、短期幂等和 Worker 所有权统一到
|
9. [已完成] 将 Proxy 明细、独占提取、短期幂等和 Worker 所有权统一到
|
||||||
TTL 活动池契约;PostgreSQL 退出代理数据路径
|
TTL 活动池契约;PostgreSQL 退出代理数据路径
|
||||||
10. [已完成] 实现生产 Redis Activity Adapter、原子 Lua、公用行为契约和
|
10. [已完成] 实现生产 Redis Activity Adapter、原子 Lua、公用行为契约和
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user