{"id":49772794,"url":"https://github.com/opensourceways/cora","last_synced_at":"2026-05-11T13:57:49.241Z","repository":{"id":351238343,"uuid":"1209954106","full_name":"opensourceways/cora","owner":"opensourceways","description":"Command collection for all kinds of Services in Open Source Community","archived":true,"fork":false,"pushed_at":"2026-05-05T02:59:27.000Z","size":9928,"stargazers_count":0,"open_issues_count":3,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-11T13:57:37.216Z","etag":null,"topics":["command-line-tool","openapi"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/opensourceways.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-14T00:26:28.000Z","updated_at":"2026-05-09T18:00:34.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/opensourceways/cora","commit_stats":null,"previous_names":["tommylike/cora","opensourceways/cora"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/opensourceways/cora","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/opensourceways%2Fcora","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/opensourceways%2Fcora/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/opensourceways%2Fcora/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/opensourceways%2Fcora/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/opensourceways","download_url":"https://codeload.github.com/opensourceways/cora/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/opensourceways%2Fcora/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32897941,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-10T13:40:02.631Z","status":"online","status_checked_at":"2026-05-11T02:00:05.975Z","response_time":120,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["command-line-tool","openapi"],"created_at":"2026-05-11T13:57:48.460Z","updated_at":"2026-05-11T13:57:49.226Z","avatar_url":"https://github.com/opensourceways.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Cora\n[English Version](readme_en.md)\n\n**Cora**（Community Collaboration）是统一的开源社区服务命令行工具。通过单一二进制文件访问论坛、邮件列表、会议、Issue CICD等社区服务，命令由各后端服务发布的 OpenAPI Spec 动态驱动生成。\n\n![Cora](assets/img/cora.png)\n\n## 项目简介\n\n`cora` 面向每天需要与多个社区服务交互的开源开发者。无需在各种工具和 Web 页面之间来回切换，所有服务统一使用 `cora \u003c服务\u003e \u003c资源\u003e \u003c操作\u003e` 的命令结构。\n\n**核心特点：**\n\n- **零代码扩展** — 接入新的后端服务只需在配置文件中添加一条记录，无需修改 CLI 代码。\n- **OpenAPI 驱动** — 命令在运行时根据各服务的 OpenAPI 3.0 Spec 动态生成。\n- **Spec 本地缓存** — Spec 缓存到本地（默认 24 小时有效），冷启动无需网络请求，延迟 \u003c 200ms。\n- **输出结果可定制** — 通过声明式配置定制每个操作的输出字段和展示方式；`--format json/yaml` 可直接输出完整原始数据，适合脚本和 Agent 使用。\n- **脚本友好** — stdout/stderr 分离、语义化退出码、`--format json` 输出可直接 pipe 给 `jq`。\n\n## 已支持服务\n\n| 服务                                   | 命令名        | Spec 来源  | 鉴权方式                              |\n|--------------------------------------|------------|----------|-----------------------------------|\n| [ GitCode ](https://gitcode.com)     | `gitcode`  | 内置嵌入     | 个人访问令牌（`?access_token=`), 统一认证待补充 |\n| [ GitHub ](https://github.com)       | `github`   | 内置嵌入     | PAT / Fine-grained Token（`Authorization: Bearer …`） |\n| [ Etherpad ](https://etherpad.org)   | `etherpad` | 内置嵌入     | API Key（`?apikey=`）, 统一认证待补充      |\n| [ Jenkins ](https://www.jenkins.io)  | `jenkins`  | 内置嵌入     | HTTP Basic Auth（`base64(username:api_token)`） |\n| [ Forum ](https://www.discourse.org) | `forum`    | spec_url | API Key + 用户名（请求头）,  统一认证待补充      |\n\n## 命令结构\n\n```\ncora \u003c服务\u003e \u003c资源\u003e \u003c操作\u003e [参数]\n```\n\n| 层级     | 示例                             | 来源                    |\n|--------|--------------------------------|-----------------------|\n| `cora` | —                              | 二进制入口                 |\n| `\u003c服务\u003e` | `gitcode`、`forum`、`etherpad`   | OpenAPI               |\n| `\u003c资源\u003e` | `issues`、`posts`、`topics`      | OpenAPI `tags[0]`     |\n| `\u003c操作\u003e` | `list`、`get`、`create`、`delete` | OpenAPI `operationId` |\n\n## 使用示例\n\n### GitCode\n\n```bash\n# 列出仓库列表\ncora gitcode repos list --owner my-org\n\n# 获取仓库信息\ncora gitcode repos get --owner my-org --repo my-repo\n\n# 列出 Issue\ncora gitcode issues list --owner my-org --repo my-repo --state open\n\n# 获取单个 Issue（表格展示）\ncora gitcode issues get --owner my-org --repo my-repo --number 1367\n\n# 以 JSON 格式输出（原始完整数据，可 pipe 给 jq）\ncora gitcode issues get --owner my-org --repo my-repo --number 1367 --format json | jq '.title'\n\n# 以 YAML 格式输出\ncora gitcode issues list --owner my-org --repo my-repo --format yaml\n\n# 预览请求内容（不实际发送）\ncora gitcode issues create --owner my-org --repo my-repo --title \"test\" --dry-run\n```\n\n### GitHub\n\n```bash\n# 获取仓库信息\ncora github repos get --owner cncf --repo cora\n\n# 列出仓库 Issue\ncora github issues list --owner cncf --repo cora --state open\n\n# 获取单个 Issue\ncora github issues get --owner cncf --repo cora --issue-number 1\n\n# 列出 Pull Request\ncora github pulls list --owner cncf --repo cora --state open\n\n# JSON 输出 + jq 提取\ncora github issues get --owner cncf --repo cora --issue-number 1 --format json | jq '.title'\n```\n\n### Forum（Discourse）\n\n```bash\n# 列出论坛最新帖子\ncora forum posts list\n\n# 获取指定帖子\ncora forum posts get --id 42\n\n# 创建帖子\ncora forum posts create --title \"Release v1.2.0\" --raw \"正文内容\"\n\n# 以 JSON 格式输出并通过 jq 过滤\ncora forum posts list --format json | jq '.[].username'\n\n# 强制刷新 OpenAPI Spec 缓存\ncora forum posts list --refresh-spec\n```\n\n### Etherpad\n\n```bash\n# 列出所有 pad\ncora etherpad pads list\n\n# 获取 pad 内容\ncora etherpad pads get-text --pad-id my-pad\n\n# 创建新 pad\ncora etherpad pads create-pad --pad-id new-pad\n```\n\n### Jenkins\n\n```bash\n# 列出所有 Job\ncora jenkins jobs list\n\n# 获取单个 Job 详情\ncora jenkins jobs get --name my-job\n\n# 触发构建\ncora jenkins jobs build --name my-job\n\n# 启用 / 禁用 Job\ncora jenkins jobs enable-job --name my-job\ncora jenkins jobs disable-job --name my-job\n\n# 删除 Job\ncora jenkins jobs delete --name my-job\n\n# 获取构建详情\ncora jenkins builds get --name my-job --number 1\n\n# 查看队列\ncora jenkins queue list\n\n# JSON 格式输出\ncora jenkins jobs list --format json | jq '.jobs[].name'\n```\n\n### 全局参数\n\n| 参数               | 默认值     | 说明                           |\n|------------------|---------|------------------------------|\n| `--format`       | `table` | 输出格式：`table`、`json` 或 `yaml` |\n| `--dry-run`      | `false` | 打印 HTTP 请求详情，不实际发送           |\n| `--refresh-spec` | `false` | 跳过缓存，重新拉取服务 Spec             |\n| `--verbose`      | `false` | 输出详细调试日志（INFO + DEBUG 级别）    |\n\n## 输出结果定制\n\n### 格式说明\n\n`--format` 控制全局输出格式，对所有子命令生效：\n\n| 值           | 行为                                    |\n|-------------|---------------------------------------|\n| `table`（默认） | 应用 View 定义展示格式化表格；无 View 时自动 fallback |\n| `json`      | 跳过所有 View，完整响应体 pretty-print 为 JSON   |\n| `yaml`      | 跳过所有 View，完整响应体转换为 YAML               |\n\n**`--format json/yaml` 永远输出完整、未经过滤的原始响应**，适合脚本和 Agent 使用。View 系统只在 `--format table` 时生效。\n\n### View 系统\n\ncora 内置了常用操作的 View 定义（字段选取、格式化），同时支持通过 `~/.config/cora/views.yaml` 用户自定义覆盖或新增。\n\n**内置 View 覆盖的操作：**\n\n| 服务       | 操作            | 展示模式         |\n|----------|---------------|--------------|\n| gitcode  | `issues get`  | 竖式 KV 表（单对象） |\n| gitcode  | `issues list` | 横向表格（列表）     |\n| gitcode  | `repos get`   | 竖式 KV 表      |\n| gitcode  | `repos list`  | 横向表格         |\n| gitcode  | `pulls get`   | 竖式 KV 表      |\n| gitcode  | `pulls list`  | 横向表格         |\n| github   | `issues get`  | 竖式 KV 表      |\n| github   | `issues list` | 横向表格         |\n| github   | `repos get`   | 竖式 KV 表      |\n| github   | `repos list`  | 横向表格         |\n| github   | `pulls get`   | 竖式 KV 表      |\n| github   | `pulls list`  | 横向表格         |\n| github   | `users get`   | 竖式 KV 表      |\n| forum    | `topics list` | 横向表格         |\n| forum    | `topics get`  | 竖式 KV 表      |\n| forum    | `posts list`  | 横向表格         |\n| etherpad | `pads list`   | 横向表格         |\n\n### 配置 views.yaml\n\n将 `views.example.yaml` 复制到 `~/.config/cora/views.yaml` 并按需修改：\n\n```bash\nmkdir -p ~/.config/cora\ncp views.example.yaml ~/.config/cora/views.yaml\n```\n\n也可通过环境变量指定其他路径：\n\n```bash\nexport CORA_VIEWS=/path/to/my-views.yaml\n```\n\n或在 `config.yaml` 中配置：\n\n```yaml\nviews_file: /path/to/my-views.yaml\n```\n\n### views.yaml 格式\n\n```yaml\n\u003c服务名\u003e:\n  \u003c资源\u003e/\u003c操作\u003e:\n    root_field: \"\"        # 可选：响应中包含列表的字段名（空=自动探测）\n    columns:\n      - field: \u003cdot.path\u003e # 必填：JSON 字段路径，支持点号嵌套（如 user.login）\n        label: \u003cstring\u003e   # 可选：表头（默认自动 title-case）\n        format: \u003ctype\u003e    # 可选：text（默认）| json | date | multiline\n        truncate: \u003cint\u003e   # 可选：最大字符数，0=不截断\n        width: \u003cint\u003e      # 可选：固定列宽（仅列表横向表格有效）\n        date_fmt: \u003cstring\u003e# 可选：Go 时间格式，format=date 时有效\n        indent: \u003cbool\u003e    # 可选：format=json 时是否缩进展示\n```\n\n**format 类型说明：**\n\n| 值           | 适用场景                  | 渲染逻辑                           |\n|-------------|-----------------------|--------------------------------|\n| `text`（默认）  | 字符串、数字、布尔             | 转为字符串，按 `truncate` 截断          |\n| `json`      | 嵌套对象、数组               | 保留原始 JSON；`indent: true` 时缩进展示 |\n| `date`      | ISO 8601 时间戳          | 解析后按 `date_fmt` 重新格式化          |\n| `multiline` | 长文本（body、description） | 保留换行符，按 `truncate` 字符数截断       |\n\n### 示例：覆盖内置 View，只显示部分字段\n\n```yaml\n# ~/.config/cora/views.yaml\ngitcode:\n  issues/get:\n    columns:\n      - field: number\n        label: \"No.\"\n      - field: title\n        label: Title\n        truncate: 80\n      - field: state\n      - field: html_url\n        label: URL\n      - field: user.login\n        label: Author\n      - field: created_at\n        label: Created\n        format: date\n```\n\n### 示例：为尚无内置 View 的操作自定义\n\n```yaml\ngitcode:\n  commits/list:\n    columns:\n      - field: sha\n        label: SHA\n        truncate: 8\n        width: 10\n      - field: commit.message\n        label: Message\n        truncate: 60\n        width: 62\n      - field: commit.author.name\n        label: Author\n        width: 18\n      - field: commit.author.date\n        label: Date\n        format: date\n        width: 12\n```\n\n用户 View 完全覆盖同键的内置 View（整体替换，不合并列）。\n\n## 安装\n\n### 从源码构建\n\n**环境要求：** Go 1.22+、make\n\n```bash\ngit clone https://github.com/cncf/cora.git\ncd cora\nmake build\nmv bin/cora /usr/local/bin/\n```\n\n### 使用 Docker\n\n```bash\n# 构建镜像\nmake docker-build\n\n# 运行（挂载本地配置目录）\ndocker run --rm \\\n  -v ~/.config/cora:/root/.config/cora:ro \\\n  cora:latest forum posts list\n```\n\n或使用 `make docker-run`：\n\n```bash\nmake docker-run ARGS=\"forum posts list\"\n```\n\n## 配置\n\n默认读取 `~/.config/cora/config.yaml`。可通过环境变量 `CORA_CONFIG` 指定其他路径。\n\n### 初始化配置\n\n```bash\nmkdir -p ~/.config/cora\ncp config/config.example.yaml ~/.config/cora/config.yaml\n# 编辑文件，填写实际值\n```\n\n### 配置文件说明\n\n```yaml\nservices:\n  # ── GitCode（内置 Spec，无需 spec_url）──\n  gitcode:\n    base_url: https://api.gitcode.com   # 必填，无默认值\n    auth:\n      gitcode:\n        access_token: \"你的个人访问令牌\"  # GitCode 用户设置 → 个人访问令牌\n\n  # ── GitHub（内置 Spec，无需 spec_url）──\n  github:\n    base_url: https://api.github.com    # 必填；GHE Server 改为 https://\u003chost\u003e/api/v3\n    auth:\n      github:\n        token: \"你的 GitHub PAT\"          # https://github.com/settings/tokens\n\n  # ── Etherpad（内置 Spec，无需 spec_url）──\n  etherpad:\n    base_url: https://your-etherpad-host/api/1.3.0  # 必填，无默认值\n    auth:\n      etherpad:\n        api_key: \"你的 Etherpad API Key\"\n\n  # ── Jenkins（内置 Spec，无需 spec_url）──\n  jenkins:\n    base_url: https://jenkins.example.com          # 必填，无默认值\n    auth:\n      jenkins:\n        username: \"你的 Jenkins 用户名\"\n        api_token: \"你的 Jenkins API Token\"          # JENKINS_URL/user/\u003cyou\u003e/configure\n\n  # ── Forum / Discourse（需要 spec_url）──\n  forum:\n    spec_url: assets/openapi/forum/openapi.json   # 支持 http://、https://、file:// 或裸路径\n    base_url: https://forum.example.org            # 必填\n    auth:\n      discourse:\n        api_key: \"你的 API Key\"\n        api_username: \"你的用户名\"\n\n  # 按需添加更多服务，无需修改 CLI 代码。\n  # myservice:\n  #   spec_url: https://myservice.example.org/openapi.yaml\n  #   base_url: https://myservice.example.org\n\n# 全局 Spec 缓存配置（可选，括号内为默认值）\nspec_cache:\n  ttl: 24h                      # 缓存有效期\n  dir: ~/.config/cora/cache     # 缓存存储目录\n\n# 自定义 views.yaml 路径（可选，默认 ~/.config/cora/views.yaml）\nviews_file: ~/.config/cora/views.yaml\n```\n\n\u003e **注意**：内置服务（gitcode、github、etherpad、jenkins）的 `base_url` 没有硬编码默认值，必须在配置文件中显式声明。\n\n### 环境变量\n\n所有配置项均可通过 `CORA_` 前缀的环境变量覆盖，优先级高于配置文件。\n\n| 环境变量                                               | 对应配置项                                         | 说明                |\n|----------------------------------------------------|-----------------------------------------------|-------------------|\n| `CORA_CONFIG`                                      | —                                             | 覆盖配置文件路径          |\n| `CORA_VIEWS`                                       | —                                             | 覆盖 views.yaml 路径  |\n| `CORA_SPEC_CACHE_TTL`                              | `spec_cache.ttl`                              | 缓存有效期（如 `12h`）    |\n| `CORA_SPEC_CACHE_DIR`                              | `spec_cache.dir`                              | 缓存目录路径            |\n| `CORA_SERVICES_\u003cNAME\u003e_BASE_URL`                    | `services.\u003cname\u003e.base_url`                    | 覆盖指定服务的 API 根地址   |\n| `CORA_SERVICES_\u003cNAME\u003e_SPEC_URL`                    | `services.\u003cname\u003e.spec_url`                    | 覆盖指定服务的 Spec 地址   |\n| `CORA_SERVICES_GITCODE_AUTH_GITCODE_ACCESS_TOKEN`  | `services.gitcode.auth.gitcode.access_token`  | GitCode 个人访问令牌    |\n| `CORA_SERVICES_GITHUB_AUTH_GITHUB_TOKEN`           | `services.github.auth.github.token`           | GitHub PAT / 细粒度令牌 |\n| `CORA_SERVICES_ETHERPAD_AUTH_ETHERPAD_API_KEY`     | `services.etherpad.auth.etherpad.api_key`     | Etherpad API Key  |\n| `CORA_SERVICES_JENKINS_AUTH_JENKINS_USERNAME`     | `services.jenkins.auth.jenkins.username`     | Jenkins 用户名       |\n| `CORA_SERVICES_JENKINS_AUTH_JENKINS_API_TOKEN`    | `services.jenkins.auth.jenkins.api_token`    | Jenkins API Token |\n| `CORA_SERVICES_\u003cNAME\u003e_AUTH_DISCOURSE_API_KEY`      | `services.\u003cname\u003e.auth.discourse.api_key`      | Discourse API Key |\n| `CORA_SERVICES_\u003cNAME\u003e_AUTH_DISCOURSE_API_USERNAME` | `services.\u003cname\u003e.auth.discourse.api_username` | Discourse 用户名     |\n\n\u003e **注意**：环境变量只能覆盖配置文件中**已存在**的服务条目，无法通过环境变量新增服务。\n\n#### 本地开发：使用 .env 文件\n\n在项目根目录创建 `.env` 文件，cora 启动时会自动加载其中的变量（已存在的系统环境变量不会被覆盖）。`.env` 文件仅供本地开发使用，**不要提交到版本库**。\n\n```bash\ncp .env.example .env\n# 编辑 .env，填入本地开发的实际值\n```\n\n`.env` 示例：\n\n```bash\nCORA_SERVICES_FORUM_BASE_URL=http://localhost:3000\nCORA_SERVICES_FORUM_AUTH_DISCOURSE_API_KEY=dev-api-key\nCORA_SERVICES_FORUM_AUTH_DISCOURSE_API_USERNAME=system\n```\n\n### Spec 加载策略\n\n| 优先级   | 条件         | 行为                   |\n|-------|------------|----------------------|\n| 1（最快） | 缓存存在且未过期   | 直接读本地文件，不发起网络请求      |\n| 2     | 缓存不存在或已过期  | 从 `spec_url` 拉取，写入缓存 |\n| 3     | 拉取失败，存在旧缓存 | 使用旧缓存，stderr 输出警告    |\n| 4（报错） | 拉取失败且无缓存   | 退出码 4，提示检查网络和配置      |\n\n使用 `--refresh-spec` 可强制跳过缓存重新拉取。\n\n## 本地开发\n\n### 前置依赖\n\n| 工具            | 版本要求    | 安装                             |\n|---------------|---------|--------------------------------|\n| Go            | \u003e= 1.22 | `brew install go`              |\n| make          | 任意      | macOS 预装                       |\n| golangci-lint | \u003e= 1.57 | `brew install golangci-lint`   |\n| Docker        | \u003e= 24.0 | [官网下载](https://www.docker.com) |\n\n### 常用命令\n\n```bash\nmake build          # 编译二进制（输出：./bin/cora）\nmake build-prod     # 生产构建（CGO 禁用，去除调试信息）\nmake test           # 运行全量测试（含竞态检测）\nmake test-unit      # 仅运行短测试（跳过集成测试）\nmake test-cover     # 生成 HTML 覆盖率报告（coverage.html）\nmake lint           # 运行 golangci-lint\nmake fmt            # 格式化代码（gofmt + goimports）\nmake tidy           # 整理依赖（go mod tidy）\nmake clean          # 清理构建产物\n```\n\n### 从源码运行\n\n```bash\n# 直接运行（无需先构建）\ngo run ./cmd/cora -- forum posts list\n\n# 构建后运行\nmake build \u0026\u0026 ./bin/cora forum posts list\n```\n\n### 测试\n\n```bash\n# 全量测试（含竞态检测器）\nmake test\n\n# 查看覆盖率\nmake test-cover-text\n```\n\n各包测试位置：\n\n| 测试文件                                | 覆盖范围                            |\n|-------------------------------------|---------------------------------|\n| `pkg/errs/errors_test.go`           | 错误类型、退出码、构造函数                   |\n| `internal/spec/cache_test.go`       | 缓存读写、原子写入、TTL                   |\n| `internal/spec/loader_test.go`      | 三段式加载、HTTP、本地文件、降级策略            |\n| `internal/builder/command_test.go`  | 资源名推导、动词解析、Flag 名转换             |\n| `internal/log/mask_test.go`         | URL 脱敏、请求头脱敏、响应体格式化             |\n| `internal/output/formatter_test.go` | JSON/YAML/Table 输出、View 渲染、终端安全 |\n| `internal/smoke/loader_test.go`     | YAML 场景加载、默认值、空文件跳过           |\n| `internal/smoke/assertion_test.go`  | 10 种断言逻辑验证                         |\n| `internal/smoke/runner_test.go`     | 子进程调用、环境变量注入                   |\n| `internal/smoke/report_test.go`     | HTML 报告生成                            |\n\n### 项目目录结构\n\n```\ncora/\n├── cmd/cora/main.go                  # 入口，两阶段命令加载\n├── internal/\n│   ├── auth/resolver.go              # 鉴权凭证注入\n│   ├── builder/\n│   │   ├── command.go                # OpenAPI Spec → Cobra 命令树\n│   │   └── command_test.go\n│   ├── config/config.go              # 配置加载与结构体定义\n│   ├── executor/executor.go          # HTTP 请求执行\n│   ├── log/\n│   │   ├── log.go                    # 分级日志（Error/Warn/Info/Debug）\n│   │   └── mask.go                   # URL/Header 脱敏，响应体格式化\n│   ├── output/\n│   │   ├── formatter.go              # Table / JSON / YAML 输出格式化\n│   │   └── formatter_test.go\n│   ├── registry/\n│   │   ├── registry.go               # 服务注册表\n│   │   └── builtin.go                # 内置服务注册（gitcode、github、etherpad、jenkins）\n│   ├── spec/\n│   │   ├── loader.go                 # 三段式 Spec 加载\n│   │   ├── cache.go                  # 缓存读写（原子写入）\n│   │   └── *_test.go\n│   └── view/\n│       ├── view.go                   # ViewColumn / ViewConfig / Registry 类型定义\n│       ├── extract.go                # 字段路径提取与值格式化\n│       ├── builtin.go                # 内置 View 定义\n│       └── loader.go                 # 加载 views.yaml，合并内置 View\n├── pkg/errs/\n│   ├── errors.go                     # 错误类型与退出码定义\n│   └── errors_test.go\n├── assets/\n│   ├── openapi/                      # 内置服务 OpenAPI Spec 文件\n│   └── assets.go                     # go:embed 声明\n├── config/\n│   ├── config.example.yaml           # 配置文件示例\n│   └── views.example.yaml            # views.yaml 示例\n├── cmd/\n│   ├── cora/main.go                  # cora 主入口\n│   └── smoke/main.go                 # Smoke Runner 入口\n├── scenarios/                        # Smoke 测试场景 YAML 文件\n├── spec/                             # 架构设计文档\n├── Makefile\n└── Dockerfile\n```\n\n## 接入新服务\n\n1. 确保后端服务在固定地址发布了 OpenAPI 3.0 Spec。\n2. 在 `~/.config/cora/config.yaml` 中添加配置：\n\n   ```yaml\n   services:\n     myservice:\n       spec_url: https://myservice.example.org/openapi.yaml\n       base_url: https://myservice.example.org\n   ```\n\n3. 执行任意命令，Spec 会自动拉取并缓存：\n\n   ```bash\n   cora myservice --help\n   ```\n\n4. （可选）在 `~/.config/cora/views.yaml` 中为常用操作定义自定义 View。\n\n### 后端服务接入要求\n\n- 使用 **OpenAPI 3.0** 规范（不支持 Swagger 2.0）\n- 为每个操作声明 `tags`，第一个 tag 即为 CLI 的 `\u003c资源\u003e` 层命令名\n- `operationId` 使用已知动词前缀：`list`、`get`、`create`、`update`、`delete`、`patch`\n- 通过 `security` 字段按操作声明鉴权需求（缺失或为空表示无需鉴权）\n\n## 退出码\n\n| 退出码 | 含义                   |\n|-----|----------------------|\n| 0   | 成功                   |\n| 1   | API 错误（后端返回 4xx/5xx） |\n| 2   | 鉴权错误（未登录或凭证无效）       |\n| 3   | 参数校验错误               |\n| 4   | Spec 加载失败            |\n| 5   | 配置错误（服务未配置或配置文件损坏）   |\n| 127 | 未分类错误                |\n\n## Smoke 测试\n\ncora 内置了一套端对端 Smoke 测试框架，用于持续看护各服务子命令的可用性，防止接口不可用或输出异常被遗漏。\n\n### 工作原理\n\nSmoke Runner（`cmd/smoke`）读取 `scenarios/` 目录下的 YAML 场景文件，依次调用真实的 `cora` 二进制，检查退出码、stdout/stderr 内容、响应时间及 JSON 字段等多个维度，最终生成一份 HTML 报告。空文件或纯注释文件会被静默跳过。\n\n### 场景文件格式\n\n```yaml\nname: \"GitCode · issues list\"\nservice: gitcode\nargs:\n  - issues\n  - list\n  - --owner\n  - openeuler\n  - --repo\n  - infrastructure\n  - --state\n  - open\nformat: table\ntimeout_ms: 8000\nassertions:\n  - type: exit_code\n    value: 0\n  - type: response_time_lt\n    value: 5000\n  - type: stdout_not_empty\n  - type: stderr_not_contains\n    value: \"ERROR\"\n  - type: json_has_keys          # 仅 format: json 时有意义\n    values: [\"title\", \"state\"]\n```\n\n**支持的断言类型：**\n\n| 类型 | 说明 |\n|------|------|\n| `exit_code` | 退出码等于指定值 |\n| `stdout_not_empty` | stdout 非空 |\n| `stderr_not_contains` | stderr 不包含指定字符串 |\n| `response_time_lt` | 响应时间（毫秒）低于指定值 |\n| `json_has_keys` | JSON 输出包含所有指定顶层键 |\n| `json_key_not_empty` | JSON 指定键值非空 |\n| `table_has_columns` | 表格输出包含所有指定列名 |\n| `stdout_contains` | stdout 包含指定字符串 |\n| `stderr_empty` | stderr 为空 |\n| `exit_code_not` | 退出码不等于指定值 |\n\n### 配置\n\n复制示例配置并填写凭证：\n\n```bash\ncp config/smoke-config.example.yaml config/smoke-config.yaml\n# 编辑 smoke-config.yaml，填入实际 token 和 URL\n```\n\n各服务的凭证也可通过环境变量注入，场景文件的 `args` 中可使用 `${VAR}` 占位符：\n\n```bash\nexport SMOKE_GITCODE_TOKEN=glpat-xxxx\n```\n\n### 运行\n\n```bash\n# 构建 smoke-runner 并运行全部场景\nmake smoke\n\n# 只运行包含关键字的场景（按 name 过滤）\nmake smoke-filter FILTER=gitcode\n\n# 手动运行，指定各参数\n./bin/smoke-runner \\\n  --cora-bin ./bin/cora \\\n  --config ./config/smoke-config.yaml \\\n  --scenarios-dir ./scenarios \\\n  --report-dir ./smoke-report\n```\n\n报告默认输出到 `smoke-report/\u003cYYYY-MM-DD\u003e/report.html`，按日期归档。\n\n### CI 集成\n\nGitHub Actions 配置（`.github/workflows/smoke.yml`）每晚 UTC 02:00 自动运行 Smoke 测试，也支持手动触发。HTML 报告作为 Artifact 上传，保留 90 天，名称格式为 `smoke-report-\u003cYYYY-MM-DD\u003e`。\n\n### 目录结构\n\n```\nscenarios/\n├── gitcode/\n│   ├── repos-list.yaml\n│   ├── issues-list.yaml\n│   └── issues-get.yaml\n├── forum/\n│   └── posts-list.yaml      # 可注释掉暂时跳过\n└── etherpad/\n    └── pad-list.yaml        # 可注释掉暂时跳过\n\ninternal/smoke/\n├── types.go                 # Scenario、Assertion、Result 类型定义\n├── loader.go                # YAML 场景加载，空文件自动跳过\n├── assertion.go             # 10 种断言逻辑\n├── runner.go                # 调用 cora 二进制，注入环境变量\n└── report.go                # 控制台输出 + HTML 报告生成\n```\n\n## 架构文档\n\n完整架构设计（含框架选型、OpenAPI 驱动命令生成、鉴权策略、Spec 缓存、View 系统等）请参阅 [`spec/`](spec/) 目录。\n\n## 贡献\n\n欢迎贡献代码。提交较大改动前请先开 Issue 说明意图。\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopensourceways%2Fcora","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fopensourceways%2Fcora","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopensourceways%2Fcora/lists"}