354 lines
12 KiB
PowerShell
354 lines
12 KiB
PowerShell
# Vendor a trimmed Playbook snapshot into a target project (offline copy),
|
||
# then run sync_standards to materialize rulesets\<lang>\ and .gitattributes in
|
||
# the target project root.
|
||
#
|
||
# Usage:
|
||
# powershell -File scripts/vendor_playbook.ps1 -ProjectRoot <path>
|
||
# powershell -File scripts/vendor_playbook.ps1 -ProjectRoot <path> -Langs tsl,cpp
|
||
# powershell -File scripts/vendor_playbook.ps1 -ProjectRoot <path> -Langs @("tsl","cpp") -ApplyTemplates
|
||
#
|
||
# Options:
|
||
# -ApplyTemplates Apply CI/lang templates to project root (skip if exists)
|
||
#
|
||
# Notes:
|
||
# - Snapshot is written to: <project-root>\docs\standards\playbook\
|
||
# - Existing snapshot is backed up before overwrite.
|
||
# - With -ApplyTemplates, CI and lang templates are copied to project root.
|
||
|
||
[CmdletBinding()]
|
||
param(
|
||
[Parameter(Mandatory = $false)]
|
||
[Alias('h', 'help', '?')]
|
||
[switch]$Help,
|
||
|
||
[Parameter(Mandatory = $false)]
|
||
[string]$ProjectRoot,
|
||
|
||
[Parameter(Mandatory = $false)]
|
||
[string[]]$Langs,
|
||
|
||
[Parameter(Mandatory = $false)]
|
||
[switch]$ApplyTemplates
|
||
)
|
||
|
||
$ErrorActionPreference = "Stop"
|
||
|
||
if ($Help) {
|
||
Write-Host "Usage:"
|
||
Write-Host " powershell -File scripts/vendor_playbook.ps1 -ProjectRoot <path>"
|
||
Write-Host " powershell -File scripts/vendor_playbook.ps1 -ProjectRoot <path> -Langs tsl,cpp"
|
||
Write-Host " powershell -File scripts/vendor_playbook.ps1 -ProjectRoot <path> -Langs @('tsl','cpp') -ApplyTemplates"
|
||
Write-Host ""
|
||
Write-Host "Options:"
|
||
Write-Host " -ProjectRoot Target project root (required)."
|
||
Write-Host " -Langs Comma/space-separated list or array (default: tsl)."
|
||
Write-Host " -ApplyTemplates Apply CI/lang templates to project root."
|
||
Write-Host " -Help Show this help."
|
||
exit 0
|
||
}
|
||
|
||
if (-not $ProjectRoot) {
|
||
throw "ProjectRoot is required. Use -Help for usage."
|
||
}
|
||
|
||
function Normalize-Langs([string[]]$InputLangs) {
|
||
if (-not $InputLangs -or $InputLangs.Count -eq 0) { return @("tsl") }
|
||
|
||
$result = New-Object System.Collections.Generic.List[string]
|
||
foreach ($item in $InputLangs) {
|
||
if (-not $item) { continue }
|
||
foreach ($part in $item.Split(@(',', ' '), [System.StringSplitOptions]::RemoveEmptyEntries)) {
|
||
if (-not $part) { continue }
|
||
$result.Add($part)
|
||
}
|
||
}
|
||
if ($result.Count -eq 0) { return @("tsl") }
|
||
return $result.ToArray()
|
||
}
|
||
|
||
$Langs = Normalize-Langs $Langs
|
||
|
||
foreach ($lang in $Langs) {
|
||
if ($lang -match '[\\/]' -or $lang -match '\.\.') {
|
||
throw "Invalid lang=$lang"
|
||
}
|
||
}
|
||
|
||
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||
$Src = (Resolve-Path (Join-Path $ScriptDir "..")).Path
|
||
|
||
New-Item -ItemType Directory -Path $ProjectRoot -Force | Out-Null
|
||
$DestRootAbs = (Resolve-Path $ProjectRoot).Path
|
||
|
||
$StandardsDir = Join-Path $DestRootAbs "docs/standards"
|
||
$DestPrefix = Join-Path $StandardsDir "playbook"
|
||
|
||
New-Item -ItemType Directory -Path $StandardsDir -Force | Out-Null
|
||
|
||
$timestamp = Get-Date -Format "yyyyMMddHHmmss"
|
||
if (Test-Path $DestPrefix) {
|
||
$bak = Join-Path $StandardsDir "playbook.bak.$timestamp"
|
||
Move-Item $DestPrefix $bak
|
||
Write-Host "Backed up existing snapshot -> docs\\standards\\$(Split-Path -Leaf $bak)"
|
||
}
|
||
|
||
New-Item -ItemType Directory -Path $DestPrefix -Force | Out-Null
|
||
|
||
Copy-Item (Join-Path $Src ".gitattributes") (Join-Path $DestPrefix ".gitattributes") -Force
|
||
Copy-Item (Join-Path $Src "scripts") $DestPrefix -Recurse -Force
|
||
Copy-Item (Join-Path $Src "codex") $DestPrefix -Recurse -Force
|
||
Copy-Item (Join-Path $Src "SKILLS.md") (Join-Path $DestPrefix "SKILLS.md") -Force
|
||
|
||
$DocsDir = Join-Path $DestPrefix "docs"
|
||
New-Item -ItemType Directory -Path $DocsDir -Force | Out-Null
|
||
Copy-Item (Join-Path $Src "docs/common") $DocsDir -Recurse -Force
|
||
|
||
$AgentsDir = Join-Path $DestPrefix "rulesets"
|
||
New-Item -ItemType Directory -Path $AgentsDir -Force | Out-Null
|
||
Copy-Item (Join-Path $Src "rulesets/index.md") (Join-Path $AgentsDir "index.md") -Force
|
||
|
||
$TemplatesDir = Join-Path $DestPrefix "templates"
|
||
New-Item -ItemType Directory -Path $TemplatesDir -Force | Out-Null
|
||
|
||
$ciTplSrc = Join-Path (Join-Path $Src "templates") "ci"
|
||
if (Test-Path $ciTplSrc) {
|
||
Copy-Item $ciTplSrc $TemplatesDir -Recurse -Force
|
||
}
|
||
|
||
foreach ($lang in $Langs) {
|
||
$docsSrc = Join-Path (Join-Path $Src "docs") $lang
|
||
if (-not (Test-Path $docsSrc)) { throw "Docs not found for lang=$lang ($docsSrc)" }
|
||
Copy-Item $docsSrc $DocsDir -Recurse -Force
|
||
|
||
$agentsSrc = Join-Path (Join-Path $Src "rulesets") $lang
|
||
if (-not (Test-Path $agentsSrc)) { throw "Agents ruleset not found for lang=$lang ($agentsSrc)" }
|
||
Copy-Item $agentsSrc $AgentsDir -Recurse -Force
|
||
|
||
$tplSrc = Join-Path (Join-Path $Src "templates") $lang
|
||
if (Test-Path $tplSrc) {
|
||
Copy-Item $tplSrc $TemplatesDir -Recurse -Force
|
||
}
|
||
}
|
||
|
||
$langsCsv = ($Langs -join ",")
|
||
|
||
$docLines = New-Object System.Collections.Generic.List[string]
|
||
$docLines.Add("# 文档导航(Docs Index)")
|
||
$docLines.Add("")
|
||
$docLines.Add("本快照为裁剪版 Playbook(langs: $langsCsv)。")
|
||
$docLines.Add("")
|
||
$docLines.Add("## 跨语言(common)")
|
||
$docLines.Add("")
|
||
$docLines.Add('- 提交信息与版本号:`common/commit_message.md`')
|
||
|
||
function Append-DocsSection([string]$Lang) {
|
||
switch ($Lang) {
|
||
"tsl" {
|
||
$docLines.Add("")
|
||
$docLines.Add("## TSL(tsl)")
|
||
$docLines.Add("")
|
||
$docLines.Add('- 代码风格:`tsl/code_style.md`')
|
||
$docLines.Add('- 命名规范:`tsl/naming.md`')
|
||
$docLines.Add('- 语法手册:`tsl/syntax_book/index.md`')
|
||
$docLines.Add('- 工具链与验证命令(模板):`tsl/toolchain.md`')
|
||
break
|
||
}
|
||
"cpp" {
|
||
$docLines.Add("")
|
||
$docLines.Add("## C++(cpp)")
|
||
$docLines.Add("")
|
||
$docLines.Add('- 代码风格:`cpp/code_style.md`')
|
||
$docLines.Add('- 命名规范:`cpp/naming.md`')
|
||
$docLines.Add('- 工具链与验证命令(模板):`cpp/toolchain.md`')
|
||
$docLines.Add('- 第三方依赖(Conan):`cpp/dependencies_conan.md`')
|
||
$docLines.Add('- clangd 配置:`cpp/clangd.md`')
|
||
break
|
||
}
|
||
"python" {
|
||
$docLines.Add("")
|
||
$docLines.Add("## Python(python)")
|
||
$docLines.Add("")
|
||
$docLines.Add('- 代码风格:`python/style_guide.md`')
|
||
$docLines.Add('- 工具链:`python/tooling.md`')
|
||
$docLines.Add('- 配置清单:`python/configuration.md`')
|
||
break
|
||
}
|
||
"markdown" {
|
||
$docLines.Add("")
|
||
$docLines.Add("## Markdown(markdown)")
|
||
$docLines.Add("")
|
||
$docLines.Add('- 代码块与行内代码格式:`markdown/index.md`')
|
||
break
|
||
}
|
||
}
|
||
}
|
||
|
||
foreach ($lang in $Langs) {
|
||
Append-DocsSection $lang
|
||
}
|
||
|
||
($docLines -join "`n") | Set-Content -Path (Join-Path $DocsDir "index.md") -Encoding UTF8
|
||
|
||
$commit = ""
|
||
try {
|
||
$commit = (git -C $Src rev-parse HEAD 2>$null)
|
||
} catch {
|
||
$commit = ""
|
||
}
|
||
if (-not $commit) { $commit = "N/A" }
|
||
|
||
@"
|
||
# Playbook(裁剪快照)
|
||
|
||
本目录为从 Playbook vendoring 的裁剪快照(langs: $langsCsv)。
|
||
|
||
## 使用
|
||
|
||
在目标项目根目录执行(多语言一次同步):
|
||
|
||
```sh
|
||
sh docs/standards/playbook/scripts/sync_standards.sh -langs $langsCsv
|
||
```
|
||
|
||
查看规范入口:
|
||
|
||
- `docs/standards/playbook/docs/index.md`
|
||
- `.agents/index.md`
|
||
|
||
## Codex skills(可选)
|
||
|
||
安装到本机(需要先在 `~/.codex/config.toml` 启用 skills;见 `docs/standards/playbook/SKILLS.md`):
|
||
|
||
```sh
|
||
sh docs/standards/playbook/scripts/install_codex_skills.sh -all
|
||
```
|
||
|
||
## CI templates(可选)
|
||
|
||
目标项目可复制启用的 CI 示例模板(如 Gitea Actions):`templates/ci/`。
|
||
"@ | Set-Content -Path (Join-Path $DestPrefix "README.md") -Encoding UTF8
|
||
|
||
@"
|
||
# SOURCE
|
||
|
||
- Source: $Src
|
||
- Commit: $commit
|
||
- Date: $(Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")
|
||
- Langs: $langsCsv
|
||
- Generated-by: scripts/vendor_playbook.ps1
|
||
"@ | Set-Content -Path (Join-Path $DestPrefix "SOURCE.md") -Encoding UTF8
|
||
|
||
Write-Host "Vendored snapshot -> $DestPrefix"
|
||
|
||
$ProjectAgentsRoot = Join-Path $DestRootAbs ".agents"
|
||
$ProjectAgentsIndex = Join-Path $ProjectAgentsRoot "index.md"
|
||
New-Item -ItemType Directory -Path $ProjectAgentsRoot -Force | Out-Null
|
||
if (-not (Test-Path $ProjectAgentsIndex)) {
|
||
$agentLines = New-Object System.Collections.Generic.List[string]
|
||
$agentLines.Add("# .agents(多语言)")
|
||
$agentLines.Add("")
|
||
$agentLines.Add("本目录用于存放仓库级/语言级的代理规则集。")
|
||
$agentLines.Add("")
|
||
$agentLines.Add("本项目已启用的规则集:")
|
||
foreach ($lang in $Langs) {
|
||
switch ($lang) {
|
||
"tsl" { $agentLines.Add("- .agents/tsl/:TSL 相关规则集(适用于 .tsl/.tsf)"); break }
|
||
"cpp" { $agentLines.Add("- .agents/cpp/:C++ 相关规则集(C++23,含 Modules)"); break }
|
||
"python" { $agentLines.Add("- .agents/python/:Python 相关规则集"); break }
|
||
"markdown" { $agentLines.Add("- .agents/markdown/:Markdown 相关规则集(仅代码格式化)"); break }
|
||
}
|
||
}
|
||
$agentLines.Add("")
|
||
$agentLines.Add("入口建议从:")
|
||
foreach ($lang in $Langs) { $agentLines.Add("- .agents/$lang/index.md") }
|
||
$agentLines.Add("")
|
||
$agentLines.Add("标准快照文档入口:")
|
||
$agentLines.Add("")
|
||
$agentLines.Add("- docs/standards/playbook/docs/index.md")
|
||
($agentLines -join "`n") | Set-Content -Path $ProjectAgentsIndex -Encoding UTF8
|
||
}
|
||
|
||
$oldSyncRoot = $env:SYNC_ROOT
|
||
$env:SYNC_ROOT = $DestRootAbs
|
||
try {
|
||
& (Join-Path $DestPrefix "scripts/sync_standards.ps1") -Langs $Langs
|
||
} finally {
|
||
$env:SYNC_ROOT = $oldSyncRoot
|
||
}
|
||
|
||
# Apply templates to project root if requested
|
||
if ($ApplyTemplates) {
|
||
Write-Host ""
|
||
Write-Host "Applying templates to project root..."
|
||
|
||
# Helper function: copy file if not exists
|
||
function Copy-IfNotExists {
|
||
param([string]$SrcFile, [string]$DstFile)
|
||
if (Test-Path $SrcFile) {
|
||
if (Test-Path $DstFile) {
|
||
Write-Host " Skip (exists): $(Split-Path -Leaf $DstFile)"
|
||
} else {
|
||
Copy-Item $SrcFile $DstFile -Force
|
||
Write-Host " Applied: $(Split-Path -Leaf $DstFile)"
|
||
}
|
||
}
|
||
}
|
||
|
||
# Apply CI templates (Gitea workflows)
|
||
$ciSrc = Join-Path $DestPrefix "templates/ci/gitea/.gitea"
|
||
if (Test-Path $ciSrc) {
|
||
$ciDst = Join-Path $DestRootAbs ".gitea"
|
||
if (Test-Path $ciDst) {
|
||
Write-Host " Skip (exists): .gitea/"
|
||
} else {
|
||
Copy-Item $ciSrc $ciDst -Recurse -Force
|
||
Write-Host " Applied: .gitea/"
|
||
}
|
||
}
|
||
|
||
# Apply lang-specific templates
|
||
foreach ($lang in $Langs) {
|
||
$langSrc = Join-Path $DestPrefix "templates/$lang"
|
||
if (-not (Test-Path $langSrc)) { continue }
|
||
|
||
switch ($lang) {
|
||
"cpp" {
|
||
Copy-IfNotExists (Join-Path $langSrc ".clang-format") (Join-Path $DestRootAbs ".clang-format")
|
||
Copy-IfNotExists (Join-Path $langSrc ".clangd") (Join-Path $DestRootAbs ".clangd")
|
||
Copy-IfNotExists (Join-Path $langSrc "CMakeLists.txt") (Join-Path $DestRootAbs "CMakeLists.txt")
|
||
Copy-IfNotExists (Join-Path $langSrc "CMakeUserPresets.json") (Join-Path $DestRootAbs "CMakeUserPresets.json")
|
||
Copy-IfNotExists (Join-Path $langSrc "conanfile.txt") (Join-Path $DestRootAbs "conanfile.txt")
|
||
$conanSrc = Join-Path $langSrc "conan"
|
||
$conanDst = Join-Path $DestRootAbs "conan"
|
||
if ((Test-Path $conanSrc) -and -not (Test-Path $conanDst)) {
|
||
Copy-Item $conanSrc $conanDst -Recurse -Force
|
||
Write-Host " Applied: conan/"
|
||
} elseif (Test-Path $conanDst) {
|
||
Write-Host " Skip (exists): conan/"
|
||
}
|
||
break
|
||
}
|
||
"python" {
|
||
Copy-IfNotExists (Join-Path $langSrc ".editorconfig") (Join-Path $DestRootAbs ".editorconfig")
|
||
Copy-IfNotExists (Join-Path $langSrc ".flake8") (Join-Path $DestRootAbs ".flake8")
|
||
Copy-IfNotExists (Join-Path $langSrc ".pre-commit-config.yaml") (Join-Path $DestRootAbs ".pre-commit-config.yaml")
|
||
Copy-IfNotExists (Join-Path $langSrc ".pylintrc") (Join-Path $DestRootAbs ".pylintrc")
|
||
Copy-IfNotExists (Join-Path $langSrc "pyproject.toml") (Join-Path $DestRootAbs "pyproject.toml")
|
||
$vscodeSrc = Join-Path $langSrc ".vscode"
|
||
$vscodeDst = Join-Path $DestRootAbs ".vscode"
|
||
if ((Test-Path $vscodeSrc) -and -not (Test-Path $vscodeDst)) {
|
||
Copy-Item $vscodeSrc $vscodeDst -Recurse -Force
|
||
Write-Host " Applied: .vscode/"
|
||
} elseif (Test-Path $vscodeDst) {
|
||
Write-Host " Skip (exists): .vscode/"
|
||
}
|
||
break
|
||
}
|
||
}
|
||
}
|
||
|
||
Write-Host "Templates applied."
|
||
}
|
||
|
||
Write-Host "Done."
|