mcpField
M
messages

messages

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

关于 messages

这是一份为 safe-output 消息系统添加消息类型的开发指南,供贡献者使用。它涵盖 Schema、Go 解析、JavaScript 模块、测试、打包和文档的更新步骤。

主要功能

  1. 01更新 JSON Schema 和 Go 结构体
  2. 02解析消息配置
  3. 03创建 JavaScript 消息模块
  4. 04添加测试和导出
  5. 05注册嵌入脚本并更新文档

适用场景

  1. 01添加可自定义的 safe-output 消息
  2. 02贯通 Go 与 JavaScript 中的消息类型
  3. 03验证并打包新的消息实现

安装

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

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

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

详细介绍

添加新消息类型指南

使用本指南向安全输出消息系统添加新的消息类型,使其能够在 frontmatter、编译器解析、JavaScript 和打包过程中正常工作。

概述

消息系统允许工作流作者自定义安全输出消息。消息流如下:

  1. 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:

  1. 在“Message Categories”部分添加新消息
  2. 记录占位符和用法
  3. 添加示例

更新 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.jsonJSON Schema添加字段定义
pkg/workflow/compiler.goGo 结构体添加结构体字段
pkg/workflow/safe_outputs.go解析器添加解析逻辑(如有需要)
pkg/workflow/js/messages_my_new.cjsJavaScript 模块创建新文件
pkg/workflow/js/messages_my_new.test.cjs测试创建新文件
pkg/workflow/js/messages_core.cjs核心工具更新 typedef
pkg/workflow/js/messages.cjs汇总文件添加重新导出
pkg/workflow/js.goGo 嵌入资源添加 embed 指令
scratchpad/safe-output-messages.md文档记录新消息

示例:添加 close-older-discussion 消息

此消息类型按照以下流程添加:

  1. Schema:添加 close-older-discussion 字段,包含占位符 {new_discussion_number}、{new_discussion_url}、{workflow_name}、{run_url}
  2. Go 结构体:添加 CloseOlderDiscussion string 字段
  3. JavaScript:创建 messages_close_discussion.cjs,其中包含 getCloseOlderDiscussionMessage()
  4. 测试:添加对应的测试文件
  5. 打包器:在 GetJavaScriptSources() 中注册
  6. 调用方:通过直接导入,在 close_older_discussions.cjs 中使用

请参阅这些文件,了解可正常运行的实现示例。

评论

支持 Markdown