Build-ProjectUsagePackage.ps1 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. [CmdletBinding()]
  2. param(
  3. [Parameter(Mandatory = $true)]
  4. [ValidatePattern('^v\d+\.\d+\.\d+$')]
  5. [string]$Version,
  6. [string]$OutputDirectory = (Join-Path (Get-Location) 'dist')
  7. )
  8. $ErrorActionPreference = 'Stop'
  9. $sourceRoot = Split-Path -Parent $PSScriptRoot
  10. $outputRoot = [System.IO.Path]::GetFullPath($OutputDirectory)
  11. $packageName = "开发治理框架-项目使用包-$Version"
  12. $packageDirectory = Join-Path $outputRoot $packageName
  13. $archivePath = Join-Path $outputRoot "$packageName.zip"
  14. $requiredPaths = @(
  15. 'AGENTS.md', 'README.md', '01-项目文档_docs', '02-AI协作_ai-workflow',
  16. '03-项目输入_project-inputs', '04-新项目源码_source',
  17. 'openspec/project.md', 'openspec/README.md'
  18. )
  19. foreach ($relativePath in $requiredPaths) {
  20. if (-not (Test-Path -LiteralPath (Join-Path $sourceRoot $relativePath))) {
  21. throw "缺少使用包必含内容:$relativePath"
  22. }
  23. }
  24. foreach ($placeholderDirectory in @('03-项目输入_project-inputs', '04-新项目源码_source')) {
  25. $unexpectedFiles = Get-ChildItem -LiteralPath (Join-Path $sourceRoot $placeholderDirectory) -File -Recurse |
  26. Where-Object { $_.Name -ne 'README.md' }
  27. if ($unexpectedFiles) {
  28. $paths = ($unexpectedFiles | ForEach-Object { $_.FullName }) -join '; '
  29. throw "使用包禁止包含项目输入或业务源码实际文件:$paths"
  30. }
  31. }
  32. if (Test-Path -LiteralPath $packageDirectory) { throw "输出目录已存在,拒绝覆盖:$packageDirectory" }
  33. if (Test-Path -LiteralPath $archivePath) { throw "输出压缩包已存在,拒绝覆盖:$archivePath" }
  34. New-Item -ItemType Directory -Path $packageDirectory -Force | Out-Null
  35. foreach ($file in @('AGENTS.md', 'README.md')) {
  36. Copy-Item -LiteralPath (Join-Path $sourceRoot $file) -Destination (Join-Path $packageDirectory $file)
  37. }
  38. foreach ($directory in @('01-项目文档_docs', '02-AI协作_ai-workflow', '03-项目输入_project-inputs', '04-新项目源码_source')) {
  39. Copy-Item -LiteralPath (Join-Path $sourceRoot $directory) -Destination (Join-Path $packageDirectory $directory) -Recurse
  40. }
  41. $openSpecDestination = Join-Path $packageDirectory 'openspec'
  42. New-Item -ItemType Directory -Path $openSpecDestination | Out-Null
  43. Copy-Item -LiteralPath (Join-Path $sourceRoot 'openspec/project.md') -Destination $openSpecDestination
  44. Copy-Item -LiteralPath (Join-Path $sourceRoot 'openspec/README.md') -Destination $openSpecDestination
  45. New-Item -ItemType Directory -Path (Join-Path $openSpecDestination 'changes') | Out-Null
  46. New-Item -ItemType Directory -Path (Join-Path $openSpecDestination 'specs') | Out-Null
  47. $versionContent = @"
  48. # 框架版本
  49. framework_version: $Version
  50. package_type: 项目使用包
  51. source_repository: 框架维护仓库
  52. release_date: $(Get-Date -Format 'yyyy-MM-dd')
  53. "@
  54. Set-Content -LiteralPath (Join-Path $packageDirectory 'FRAMEWORK_VERSION.md') -Value $versionContent -Encoding UTF8
  55. $manifestContent = @"
  56. # 项目使用包清单
  57. 版本:$Version
  58. ## 包含
  59. - AGENTS.md
  60. - README.md
  61. - 01-项目文档_docs/
  62. - 02-AI协作_ai-workflow/
  63. - 03-项目输入_project-inputs/(仅 README/目录占位)
  64. - 04-新项目源码_source/(仅 README/目录占位)
  65. - openspec/(仅 project.md、README.md 与空目录)
  66. - FRAMEWORK_VERSION.md
  67. ## 已排除
  68. - 00-框架治理_maintainers-only/
  69. - .git/、dist/、维护端台账和收件快照
  70. - 项目输入、业务源码、OpenSpec 历史实例、项目事实与凭据
  71. "@
  72. Set-Content -LiteralPath (Join-Path $packageDirectory 'PACKAGE_MANIFEST.md') -Value $manifestContent -Encoding UTF8
  73. foreach ($directory in @('00-框架治理_maintainers-only', '.git', 'dist')) {
  74. if (Test-Path -LiteralPath (Join-Path $packageDirectory $directory)) { throw "产物包含禁止目录:$directory" }
  75. }
  76. $forbiddenFiles = Get-ChildItem -LiteralPath $packageDirectory -File -Recurse | Where-Object {
  77. $_.Name -eq '.env' -or $_.Name -like '.env.*' -or
  78. $_.Extension -in @('.pem', '.key', '.pfx', '.p12', '.crt', '.cer') -or
  79. $_.Name -like '*secret*' -or $_.Name -like '*token*' -or $_.Name -like '*config.enc*'
  80. }
  81. if ($forbiddenFiles) {
  82. $paths = ($forbiddenFiles | ForEach-Object { $_.FullName }) -join '; '
  83. throw "产物包含禁止文件:$paths"
  84. }
  85. foreach ($relativePath in @(
  86. 'AGENTS.md',
  87. '01-项目文档_docs/00-项目管理/项目总控入口.md',
  88. '02-AI协作_ai-workflow/00-协作入口与总则_guides/框架反馈提交说明.md',
  89. '02-AI协作_ai-workflow/04-文档模板_templates/开发治理框架反馈记录模板.md',
  90. 'FRAMEWORK_VERSION.md', 'PACKAGE_MANIFEST.md'
  91. )) {
  92. if (-not (Test-Path -LiteralPath (Join-Path $packageDirectory $relativePath))) {
  93. throw "产物缺少发布门禁文件:$relativePath"
  94. }
  95. }
  96. Compress-Archive -LiteralPath $packageDirectory -DestinationPath $archivePath -CompressionLevel Optimal
  97. Write-Output "项目使用包目录:$packageDirectory"
  98. Write-Output "项目使用包压缩包:$archivePath"