关于 messages
这是一份为 safe-output 消息系统添加消息类型的开发指南,供贡献者使用。它涵盖 Schema、Go 解析、JavaScript 模块、测试、打包和文档的更新步骤。
主要功能
- 01更新 JSON Schema 和 Go 结构体
- 02解析消息配置
- 03创建 JavaScript 消息模块
- 04添加测试和导出
- 05注册嵌入脚本并更新文档
适用场景
- 01添加可自定义的 safe-output 消息
- 02贯通 Go 与 JavaScript 中的消息类型
- 03验证并打包新的消息实现
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ github/ gh-aw --skill messages安装后在 Claude Code 中输入/messages确认已加载。
详细介绍
添加新消息类型指南
使用本指南向安全输出消息系统添加新的消息类型,使其能够在 frontmatter、编译器解析、JavaScript 和打包过程中正常工作。
概述
消息系统允许工作流作者自定义安全输出消息。消息流如下:
- Frontmatter(YAML)→ 2. JSON Schema → 3. Go 编译器 → 4. JavaScript 模块 → 5. 打包器
步骤 1:更新 JSON Schema
在 pkg/parser/schemas/main_workflow_schema.json 的 messages 对象中添加新的消息字段:
{
"messages": {
"properties": {
"my-new-message": {
"type": "string",
"description": "Description of when this message is used. Available placeholders: {placeholder1}, {placeholder2}.",
"examples": [
"Example message with {placeholder1}"
]
}
}
}
}
要点:
- YAML 字段名使用
kebab-case(例如,my-new-message) - 在描述中记录所有可用的占位符
- 提供有帮助的示例
- 更改后运行
make build(schema 会嵌入二进制文件)
步骤 2:更新 Go 结构体
在 pkg/workflow/compiler.go 的 SafeOutputMessagesConfig 中添加新字段:
type SafeOutputMessagesConfig struct {
// ... existing fields ...
MyNewMessage string `yaml:"my-new-message,omitempty" json:"myNewMessage,omitempty"` // Description of the message
}
要点:
- Go 字段名使用
CamelCase - YAML 标签使用
kebab-case(与 frontmatter 一致) - JSON 标签使用
camelCase(供 JavaScript 使用) - 为两个标签都添加
omitempty
步骤 3:更新 Go 解析器
如有需要,更新 pkg/workflow/safe_outputs.go 中的解析器:
func parseMessagesConfig(messagesMap map[string]any) *SafeOutputMessagesConfig {
config := &SafeOutputMessagesConfig{}
// ... existing parsing ...
if myNewMessage, ok := messagesMap["my-new-message"].(string); ok {
config.MyNewMessage = myNewMessage
}
return config
}
注意: 解析器对大多数字段使用反射,因此简单的字符串字段可能不需要此步骤。
步骤 4:创建 JavaScript 消息模块
创建新文件 pkg/workflow/js/messages_my_new.cjs:
// @ts-check
/// <reference types="@actions/github-script" />
/**
* My New Message Module
*
* This module provides the my-new-message generation
* for [describe when it's used].
*/
const { getMessages, renderTemplate, toSnakeCase } = require("./messages_core.cjs");
/**
* @typedef {Object} MyNewMessageContext
* @property {string} placeholder1 - Description of placeholder1
* @property {string} placeholder2 - Description of placeholder2
*/
/**
* Get the my-new-message, using custom template if configured.
* @param {MyNewMessageContext} ctx - Context for message generation
* @returns {string} The generated message
*/
function getMyNewMessage(ctx) {
const messages = getMessages();
// Create context with both camelCase and snake_case keys
const templateContext = toSnakeCase(ctx);
// Default message template
const defaultMessage = "Default message with {placeholder1} and {placeholder2}";
// Use custom message if configured
return messages?.myNewMessage
? renderTemplate(messages.myNewMessage, templateContext)
: renderTemplate(defaultMessage, templateContext);
}
module.exports = {
getMyNewMessage,
};
要点:
- 文件命名:
messages_<category>.cjs(扁平结构,不使用子文件夹) - 从
./messages_core.cjs导入共享实用工具 - 使用 JSDoc 定义类型
- 提供合理的默认消息
- 同时支持自定义模板和默认模板
步骤 5:添加测试
创建 pkg/workflow/js/messages_my_new.test.cjs:
import { describe, it, expect, beforeEach, vi } from "vitest";
// Mock core global
const mockCore = {
warning: vi.fn(),
};
global.core = mockCore;
describe("getMyNewMessage", () => {
beforeEach(() => {
vi.clearAllMocks();
delete process.env.GH_AW_SAFE_OUTPUT_MESSAGES;
});
it("should return default message when no custom message configured", async () => {
const { getMyNewMessage } = await import("./messages_my_new.cjs");
const result = getMyNewMessage({
placeholder1: "value1",
placeholder2: "value2",
});
expect(result).toBe("Default message with value1 and value2");
});
it("should use custom message when configured", async () => {
process.env.GH_AW_SAFE_OUTPUT_MESSAGES = JSON.stringify({
myNewMessage: "Custom: {placeholder1}",
});
const { getMyNewMessage } = await import("./messages_my_new.cjs");
const result = getMyNewMessage({
placeholder1: "test",
placeholder2: "ignored",
});
expect(result).toContain("Custom: test");
});
});
使用 make test-js 运行测试。
步骤 6:更新 Core 模块的 TypeDef
在 pkg/workflow/js/messages_core.cjs 的 SafeOutputMessages typedef 中添加新属性:
/**
* @typedef {Object} SafeOutputMessages
* @property {string} [footer] - Custom footer message template
* // ... existing properties ...
* @property {string} [myNewMessage] - Custom my-new-message template
*/
同时更新 getMessages() 函数的返回对象:
return {
footer: rawMessages.footer,
// ... existing fields ...
myNewMessage: rawMessages.myNewMessage,
};
步骤 7:更新 Barrel 文件
在 pkg/workflow/js/messages.cjs 中添加重新导出:
// Re-export my new messages
const { getMyNewMessage } = require("./messages_my_new.cjs");
module.exports = {
// ... existing exports ...
getMyNewMessage,
};
步骤 8:在 Go 嵌入中注册
添加到 pkg/workflow/js.go:
//go:embed js/messages_my_new.cjs
var messagesMyNewScript string
添加到 GetJavaScriptSources():
func GetJavaScriptSources() map[string]string {
return map[string]string{
// ... existing entries ...
"messages_my_new.cjs": messagesMyNewScript,
}
}
步骤 9:在消费者脚本中使用
在需要使用它的脚本中直接从特定模块导入:
const { getMyNewMessage } = require("./messages_my_new.cjs");
// Use the message
const message = getMyNewMessage({
placeholder1: actualValue1,
placeholder2: actualValue2,
});
步骤 10:更新文档
更新 scratchpad/safe-output-messages.md:
- 在“Message Categories”部分添加新消息
- 记录占位符和用法
- 添加示例
更新 Message Module Architecture 表:
| Module | Purpose | Exported Functions |
|--------|---------|-------------------|
| `messages_my_new.cjs` | My new message description | `getMyNewMessage` |
验证清单
提交前:
- 已更新
pkg/parser/schemas/main_workflow_schema.json中的 JSON Schema - 已更新
pkg/workflow/compiler.go中的 Go 结构体 - Go 解析器已处理新字段(如有需要),位于
pkg/workflow/safe_outputs.go - 已创建 JavaScript 模块:
pkg/workflow/js/messages_my_new.cjs - 已创建测试:
pkg/workflow/js/messages_my_new.test.cjs - 已更新
messages_core.cjs中的 TypeDef - 已更新 Barrel 文件:
messages.cjs - 已在
js.go中添加 Go embed 指令 - 已添加到
GetJavaScriptSources()映射中 - 已更新消费者脚本,使其使用最精简的导入
- 已更新
scratchpad/safe-output-messages.md中的文档 - 测试通过:
make test-js - 构建成功:
make build - Lint 检查通过:
make lint
文件摘要
| 文件 | 用途 | 所需更改 |
|---|---|---|
pkg/parser/schemas/main_workflow_schema.json | JSON Schema | 添加字段定义 |
pkg/workflow/compiler.go | Go 结构体 | 添加结构体字段 |
pkg/workflow/safe_outputs.go | 解析器 | 添加解析逻辑(如有需要) |
pkg/workflow/js/messages_my_new.cjs | JavaScript 模块 | 创建新文件 |
pkg/workflow/js/messages_my_new.test.cjs | 测试 | 创建新文件 |
pkg/workflow/js/messages_core.cjs | 核心工具 | 更新 typedef |
pkg/workflow/js/messages.cjs | 汇总文件 | 添加重新导出 |
pkg/workflow/js.go | Go 嵌入资源 | 添加 embed 指令 |
scratchpad/safe-output-messages.md | 文档 | 记录新消息 |
示例:添加 close-older-discussion 消息
此消息类型按照以下流程添加:
- Schema:添加
close-older-discussion字段,包含占位符{new_discussion_number}、{new_discussion_url}、{workflow_name}、{run_url} - Go 结构体:添加
CloseOlderDiscussion string字段 - JavaScript:创建
messages_close_discussion.cjs,其中包含getCloseOlderDiscussionMessage() - 测试:添加对应的测试文件
- 打包器:在
GetJavaScriptSources()中注册 - 调用方:通过直接导入,在
close_older_discussions.cjs中使用
请参阅这些文件,了解可正常运行的实现示例。