[CmdletBinding()] param( [Parameter(Mandatory = $true)] [ValidatePattern('^v\d+\.\d+\.\d+$')] [string]$Version, [string]$OutputDirectory = (Join-Path (Get-Location) 'dist') ) $ErrorActionPreference = 'Stop' $sourceRoot = Split-Path -Parent $PSScriptRoot $outputRoot = [System.IO.Path]::GetFullPath($OutputDirectory) $packageName = "开发治理框架-项目使用包-$Version" $packageDirectory = Join-Path $outputRoot $packageName $archivePath = Join-Path $outputRoot "$packageName.zip" $requiredPaths = @( 'AGENTS.md', 'README.md', '01-项目文档_docs', '02-AI协作_ai-workflow', '03-项目输入_project-inputs', '04-新项目源码_source', 'openspec/project.md', 'openspec/README.md' ) foreach ($relativePath in $requiredPaths) { if (-not (Test-Path -LiteralPath (Join-Path $sourceRoot $relativePath))) { throw "缺少使用包必含内容:$relativePath" } } foreach ($placeholderDirectory in @('03-项目输入_project-inputs', '04-新项目源码_source')) { $unexpectedFiles = Get-ChildItem -LiteralPath (Join-Path $sourceRoot $placeholderDirectory) -File -Recurse | Where-Object { $_.Name -ne 'README.md' } if ($unexpectedFiles) { $paths = ($unexpectedFiles | ForEach-Object { $_.FullName }) -join '; ' throw "使用包禁止包含项目输入或业务源码实际文件:$paths" } } if (Test-Path -LiteralPath $packageDirectory) { throw "输出目录已存在,拒绝覆盖:$packageDirectory" } if (Test-Path -LiteralPath $archivePath) { throw "输出压缩包已存在,拒绝覆盖:$archivePath" } New-Item -ItemType Directory -Path $packageDirectory -Force | Out-Null foreach ($file in @('AGENTS.md', 'README.md')) { Copy-Item -LiteralPath (Join-Path $sourceRoot $file) -Destination (Join-Path $packageDirectory $file) } foreach ($directory in @('01-项目文档_docs', '02-AI协作_ai-workflow', '03-项目输入_project-inputs', '04-新项目源码_source')) { Copy-Item -LiteralPath (Join-Path $sourceRoot $directory) -Destination (Join-Path $packageDirectory $directory) -Recurse } $openSpecDestination = Join-Path $packageDirectory 'openspec' New-Item -ItemType Directory -Path $openSpecDestination | Out-Null Copy-Item -LiteralPath (Join-Path $sourceRoot 'openspec/project.md') -Destination $openSpecDestination Copy-Item -LiteralPath (Join-Path $sourceRoot 'openspec/README.md') -Destination $openSpecDestination New-Item -ItemType Directory -Path (Join-Path $openSpecDestination 'changes') | Out-Null New-Item -ItemType Directory -Path (Join-Path $openSpecDestination 'specs') | Out-Null $versionContent = @" # 框架版本 framework_version: $Version package_type: 项目使用包 source_repository: 框架维护仓库 release_date: $(Get-Date -Format 'yyyy-MM-dd') "@ Set-Content -LiteralPath (Join-Path $packageDirectory 'FRAMEWORK_VERSION.md') -Value $versionContent -Encoding UTF8 $manifestContent = @" # 项目使用包清单 版本:$Version ## 包含 - AGENTS.md - README.md - 01-项目文档_docs/ - 02-AI协作_ai-workflow/ - 03-项目输入_project-inputs/(仅 README/目录占位) - 04-新项目源码_source/(仅 README/目录占位) - openspec/(仅 project.md、README.md 与空目录) - FRAMEWORK_VERSION.md ## 已排除 - 00-框架治理_maintainers-only/ - .git/、dist/、维护端台账和收件快照 - 项目输入、业务源码、OpenSpec 历史实例、项目事实与凭据 "@ Set-Content -LiteralPath (Join-Path $packageDirectory 'PACKAGE_MANIFEST.md') -Value $manifestContent -Encoding UTF8 foreach ($directory in @('00-框架治理_maintainers-only', '.git', 'dist')) { if (Test-Path -LiteralPath (Join-Path $packageDirectory $directory)) { throw "产物包含禁止目录:$directory" } } $forbiddenFiles = Get-ChildItem -LiteralPath $packageDirectory -File -Recurse | Where-Object { $_.Name -eq '.env' -or $_.Name -like '.env.*' -or $_.Extension -in @('.pem', '.key', '.pfx', '.p12', '.crt', '.cer') -or $_.Name -like '*secret*' -or $_.Name -like '*token*' -or $_.Name -like '*config.enc*' } if ($forbiddenFiles) { $paths = ($forbiddenFiles | ForEach-Object { $_.FullName }) -join '; ' throw "产物包含禁止文件:$paths" } foreach ($relativePath in @( 'AGENTS.md', '01-项目文档_docs/00-项目管理/项目总控入口.md', '02-AI协作_ai-workflow/00-协作入口与总则_guides/框架反馈提交说明.md', '02-AI协作_ai-workflow/04-文档模板_templates/开发治理框架反馈记录模板.md', 'FRAMEWORK_VERSION.md', 'PACKAGE_MANIFEST.md' )) { if (-not (Test-Path -LiteralPath (Join-Path $packageDirectory $relativePath))) { throw "产物缺少发布门禁文件:$relativePath" } } Compress-Archive -LiteralPath $packageDirectory -DestinationPath $archivePath -CompressionLevel Optimal Write-Output "项目使用包目录:$packageDirectory" Write-Output "项目使用包压缩包:$archivePath"