feat: 添加g3fo-commit-jira skill

This commit is contained in:
2026-03-16 18:11:34 +08:00
parent 2768d58aa3
commit dc2d6697af
13 changed files with 960 additions and 24 deletions
+2
View File
@@ -0,0 +1,2 @@
# 勿提交含 API Token 的配置文件
jira_upload.env
+164
View File
@@ -0,0 +1,164 @@
---
name: g3fo-commit-jira
description: G3FO 项目 Git 提交规范自动化工具。强制要求提供 git 修订号(hash),支持用户直接提供 Jira 编号或自动查找/创建 Jira 任务。按任务类型(漏洞修复/新功能/重构等)判定需附加的文档(开发计划、影响分析、任务摘要),生成对应 MD 并按公司规范命名(<JIRA-ID>_Dev_Plan.md / _Impact_Analysis.md / _Task_Summary.md)上传到 Jira 附件,将影响分析写入评论,最后将 Jira 编号追加到 commit message 末尾后提交(不 push)。适用于 G3FO/G3SF 项目的所有 git 提交场景。
---
# G3FO Commit Jira 技能
本技能通过 git 修订号自动分析改动,绑定/创建 Jira 任务,按**公司 Jira 工单附件文档政策**生成并上传所需文档(开发计划、影响分析、任务摘要),并更新 commit message。
> **公司文档政策**(自 2026-03-09 起):何时附加哪些文件见 `references/jira_commit_docs_policy.md`。
> 报告模板、任务类型判定与 JQL 参考 `references/workflow.md`。
---
## 前置要求
**1. 强制提供 Git 修订号 (hash)**
用户必须提供一个有效的 git 修订号(如 `HEAD` 或具体的 `hash`)。
- **校验逻辑**:使用 `git rev-parse <hash>` 检查修订号是否存在。
- **错误处理**:如果修订号无效或找不到,**必须** 停止操作并提示用户:“找不到修订号 `<hash>`,请提供正确的 git 修订号(例如 HEAD 或 7 位以上的 commit hash)。”
- **路径要求**:必须在相关的项目根目录下执行 git 命令。如果当前目录不是 git 仓库或不是目标项目,请提示用户切换到正确的项目路径。
**2. Jira 编号 (可选)**
用户可以主动提供 Jira 编号(如 `G3SF-123`)。
- 如果提供了 Jira 编号,直接引用该编号,跳过“查找/选择 Jira 任务”的步骤。
- 如果未提供,则按流程自动查找或提示用户新建。
**3. Atlassian MCP 授权**
- 查看 `mcps/plugin-atlassian-atlassian/STATUS.md` 确认已在 Cursor 中授权 MCP 插件。
- 如果未授权,调用 `mcp_auth`,server: `plugin-atlassian-atlassian`,参数 `{}`。
- 若 MCP 插件未安装,引导用户按以下步骤操作:
1. 打开 Cursor Settings -> MCP
2. 添加名为 `Atlassian` 的插件
3. 按照提示完成授权并启用 skill
- 确保可用工具在 `mcps/plugin-atlassian-atlassian/tools/` 目录下已列出。
**4. 上传附件配置(用于将影响分析报告 MD 上传到 Jira)**
- Atlassian MCP 插件不支持上传文件,本技能使用自带脚本 `scripts/upload_attachment.py` 将报告 MD 上传为 Issue 附件。
- 在技能目录或脚本目录放置 `jira_upload.env`(或使用 `--config` 指定路径),包含:`JIRA_BASE_URL`、`JIRA_EMAIL`、`JIRA_API_TOKEN`。格式见 `jira_upload.env.example`,详细说明见 `references/env_config.md`。
- 若未配置,上传附件步骤跳过,仅通过 `jira_add_comment` 写入报告;已配置则先上传 MD 再写评论。
---
## 执行步骤
### Step 1 - 获取提交内容
```powershell
git show <hash> # 获取 diff + 元数据
git log -1 --format="%B" <hash> # 获取原始 commit message
```
分析要点:
- 涉及的服务模块/包名
- 新增/修改/删除的方法与接口
- 逻辑改动的核心目的
### Step 2 - 判定任务类型并确定需生成的文档
根据改动内容与 `references/jira_commit_docs_policy.md` 中的**详细条件表**,先判定任务类型,再决定需要生成并上传的文档:
| 任务类型 | 开发计划 | 影响分析 | 任务摘要 |
|----------|:--------:|:--------:|:--------:|
| 漏洞修复——简单(如拼写、界面对齐) | ❌ | ❌ | ❌ |
| 漏洞修复——中等(范围有限的逻辑错误) | ❌ | ✅ | ❌ |
| 漏洞修复——复杂/高风险(并发、核心模块) | ✅ | ✅ | ✅ |
| 小幅度增强(如小效用方法) | ❌ | ✅ | ❌ |
| 新功能/模块 | ✅ | ✅ | ✅ |
| 主要重构 | ✅ | ✅ | ✅ |
| 配置/基础设施变更 | ❌ | ✅ | ❌ |
- **无需任何文档**时:仅执行 Step 3(确定 Jira)、Step 5(amend),不生成报告、不写评论、不上传附件;仍将 Jira 编号追加到 commit message。
- **需要文档**时:继续 Step 2 下半部分,生成对应内容。
### Step 2(续)- 生成所需报告内容
对上述判定为“需要”的文档,按 `references/workflow.md` 中的模板生成内容(可先存于内存或临时文件):
- **开发计划**:步骤、设计方法、技术决策。
- **影响分析**:改动概览、方法级分析、影响范围、风险与回滚、验证与测试(模板见 workflow.md)。
- **任务摘要**:所完成工作的简要回顾,以及任何偏离计划之处。
生成后先不按“公司附件名”落盘,等取得 Jira 编号后再以 `<JIRA-ID>_*.md` 命名保存并上传。
### Step 3 - 确定 Jira 任务
**1. 如果用户已提供 Jira 编号:**
- 直接使用该编号(如 `G3SF-123`),进入 Step 4。
**2. 如果用户未提供 Jira 编号:**
- 使用 JQL 查找当前用户在 G3SF 项目下的进行中任务(limit 5):
```
project = G3SF AND assignee = currentUser() AND statusCategory != Done ORDER BY updated DESC
```
- 展示列表供用户选择,或允许用户输入 `0` 新建。
- 如果搜索结果为空,自动进入新建流程。
### Step 4 - 写入 Jira(附件 + 评论)
**附件命名规范(公司要求)**:上传到 Jira 的 MD 文件名必须为
`<JIRA-ID>_Dev_Plan.md`、`<JIRA-ID>_Impact_Analysis.md`、`<JIRA-ID>_Task_Summary.md`。
根据 Step 2 判定结果,仅上传“需要”的文档;每个文件先按该命名写入本地再上传(脚本以本地文件名为 Jira 附件名)。
**如果选择/新建的任务 ID > 0:**
1. **保存并上传附件**(已配置 `jira_upload.env` 且本任务需要文档时):
将 Step 2 生成的各文档内容,按公司规范命名写入当前项目目录(或 `docs/` 等),再调用上传脚本。PowerShell 中不要用 `&&`,改用 `;` 或换行。
```powershell
# 示例:需要三份文档时,先写入 G3SF-123_Dev_Plan.md、G3SF-123_Impact_Analysis.md、G3SF-123_Task_Summary.md,再:
python skills/g3fo-commit-jira/scripts/upload_attachment.py --config "skills/g3fo-commit-jira/jira_upload.env" --issue "G3SF-123" --file "G3SF-123_Impact_Analysis.md" --file "G3SF-123_Dev_Plan.md" --file "G3SF-123_Task_Summary.md"
```
若本任务仅需影响分析,则只写入并上传 `G3SF-123_Impact_Analysis.md`。未配置 `jira_upload.env` 时跳过上传,仅写评论。
2. **写评论**:若有影响分析内容,调用 `jira_add_comment` 将影响分析报告正文作为评论写入 issue。
3. 记录该任务的 issue key。
**如果用户选择 0 或搜索无结果:**
- 调用 `jira_create_issue` 创建新任务,详情见 `references/workflow.md`
- 设置 project key: `G3SF`, issue type: `Task`
- **Summary**:必须在标题前添加 `[G3SF]` 前缀,格式为 `[G3SF] 提炼后的改动摘要`
- **Description**:完整影响分析报告(Markdown 格式);若本任务需开发计划/任务摘要,可在创建后通过附件上传
- **Assignee**:必须设置为当前登录账号(由 MCP 传入当前用户或等效参数)
- 获取返回的新 issue key(如 `G3SF-123`)
- 若已配置 `jira_upload.env` 且本任务需要文档,创建成功后按公司命名写入并上传对应 MD 文件到该新 Issue
### Step 5 - 更新 Git 提交信息
使用获取到的 Jira 编号,通过 `git commit --amend` 更新 commit message。**严禁 push**。
**注意**:在 Windows PowerShell 环境下,需确保 UTF8 编码以防止中文乱码。
```powershell
# 设置 UTF8 编码
$OutputEncoding = [System.Text.Encoding]::UTF8
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
$origMsg = (git log -1 --format="%B").TrimEnd()
$jiraKey = "G3SF-123" # 实际获取到的 key
# 使用 --cleanup=verbatim 确保原始消息格式不被 git 自动修剪导致编码转换问题
git commit --amend -m "$origMsg`n`n**** Jira $jiraKey" --cleanup=verbatim
```
完成后告知用户:
```
绑定成功:
Jira: G3SF-123 (https://your-jira/browse/G3SF-123)
Commit: <git log --oneline -1 的结果>
```
---
## 相关参考
- **公司 Jira 附件文档政策**(何时附加哪些文件):`references/jira_commit_docs_policy.md`
- **报告模板、三文档说明与错误处理**:`references/workflow.md`
- **上传附件配置**:`references/env_config.md`;脚本 `scripts/upload_attachment.py`,依赖见 `scripts/requirements.txt`
@@ -0,0 +1,6 @@
# Jira 上传附件配置示例
# 复制为 jira_upload.env 并填入真实值,勿提交 jira_upload.env 到 Git
JIRA_BASE_URL=https://你的站点.atlassian.net
JIRA_EMAIL=你的邮箱@company.com
JIRA_API_TOKEN=你的 API Token
@@ -0,0 +1,71 @@
# Jira 上传附件 — 配置文件说明
本技能通过脚本 `scripts/upload_attachment.py` 将影响分析报告 MD 文件上传到 Jira Issue 附件(Atlassian MCP 插件不支持上传文件)。脚本从**配置文件**或环境变量读取 Jira 认证与站点信息,环境变量可覆盖配置文件中的同名项。
---
## 配置文件方式(推荐)
### 1. 创建配置文件
在技能目录 `skills/g3fo-commit-jira/` 下放置 `jira_upload.env`,或复制示例后改名并填写真实值:
```powershell
# 在技能目录下
copy jira_upload.env.example jira_upload.env
# 编辑 jira_upload.env,填入 JIRA_BASE_URL、JIRA_EMAIL、JIRA_API_TOKEN
```
### 2. 文件格式
`jira_upload.env` 为 KEY=VALUE 格式,支持 `#` 注释:
```ini
# Jira 站点 URL,不要带末尾 /
JIRA_BASE_URL=https://你的站点.atlassian.net
# 登录邮箱
JIRA_EMAIL=你的邮箱@company.com
# API Token(在 Atlassian 账户设置中创建)
JIRA_API_TOKEN=你的 API Token
```
### 3. 指定配置文件路径
从仓库根目录执行时,建议用 `--config` 指定技能目录下的配置文件:
```powershell
python skills/g3fo-commit-jira/scripts/upload_attachment.py --config "skills/g3fo-commit-jira/jira_upload.env" --issue G3SF-123 --file "G3SF-123_Impact_Analysis.md"
```
---
## 配置项说明
| 配置项 | 说明 | 示例 |
|--------|------|------|
| `JIRA_BASE_URL` | Jira 站点 URL,不要带末尾 `/` | `https://your-domain.atlassian.net` |
| `JIRA_EMAIL` | 登录 Jira 的邮箱 | `you@company.com` |
| `JIRA_API_TOKEN` | Jira 账户的 API Token | 在 [Atlassian API Tokens](https://id.atlassian.com/manage-profile/security/api-tokens) 创建 |
---
## 环境变量覆盖
若同时存在配置文件和环境变量,**环境变量优先**。
---
## API Token 获取
1. 打开 https://id.atlassian.com/manage-profile/security/api-tokens
2. 使用 Jira 登录邮箱登录
3. 点击 “Create API token”,命名后复制
4. Token 只显示一次,请妥善保存
---
## 安全注意
- **不要将 `jira_upload.env` 提交到 Git**。技能目录下已通过 `.gitignore` 忽略。
- 不要将 `JIRA_API_TOKEN` 写入代码或提交到仓库。
- 定期在 Atlassian 账户中轮换 API Token。
@@ -0,0 +1,48 @@
# Jira 工单附件文档政策(公司强制性指南)
自 **2026年3月9日** 起,以下为将 **开发计划**、**影响分析**、**任务摘要** 附加到 Jira 工单的强制性指南,用于跟踪设计决策、评估风险、为未来参考提供背景及项目间协作。
---
## 一般规则(经验法则)
| 场景 | 需要附加的文档 |
|------|----------------|
| **漏洞修复** | 仅包括 **影响分析**(若修复有非简单副作用) |
| **新功能 / 重大核心变更** | **全部三个文件**:开发计划、影响分析、任务摘要 |
| **琐碎 / 小改动** | 无需文件 |
---
## 详细条件(按任务类型)
| 任务类型 | 开发计划 | 影响分析 | 任务摘要 |
|----------|:--------:|:--------:|:--------:|
| 漏洞修复——简单(如拼写错误、界面对齐) | ❌ | ❌ | ❌ |
| 漏洞修复——中等(如范围有限的逻辑错误) | ❌ | ✅ | ❌ |
| 漏洞修复——复杂/高风险(如并发、核心模块) | ✅ | ✅ | ✅ |
| 小幅度增强(如添加小效用方法) | ❌ | ✅ | ❌ |
| 新功能/模块 | ✅ | ✅ | ✅ |
| 主要重构 | ✅ | ✅ | ✅ |
| 配置/基础设施变更 | ❌ | ✅ | ❌ |
---
## 文档含义说明
- **开发计划**:概述步骤、设计方法和技术决策。
- **影响分析**:列出受影响区域、风险、性能考虑因素及向后兼容性。
- **任务摘要**:简要回顾所完成的工作,包括任何偏离计划的地方。
---
## 提交方式与命名规范
- 将文件(Markdown)**直接放在工单的附件部分**。
- 使用一致的命名规范:
- `<JIRA-ID>_Dev_Plan.md`
- `<JIRA-ID>_Impact_Analysis.md`
- `<JIRA-ID>_Task_Summary.md`
示例:工单 `G3SF-123` 的三个附件命名为
`G3SF-123_Dev_Plan.md`、`G3SF-123_Impact_Analysis.md`、`G3SF-123_Task_Summary.md`。
@@ -0,0 +1,333 @@
# G3FO Commit Jira — 详细工作流参考
## 目录
1. [公司 Jira 附件文档政策](#公司-jira-附件文档政策)
2. [Step 1: 获取并解析提交内容](#step-1-获取并解析提交内容)
3. [Step 2: 任务类型判定与三文档模板](#step-2-任务类型判定与三文档模板)
4. [Step 3: Jira 任务查找与确认](#step-3-jira-任务查找与确认)
5. [Step 4: Jira 字段映射与写入](#step-4-jira-字段映射与写入)
6. [Step 5: Git Amend 命令详解](#step-5-git-amend-命令详解)
7. [错误处理表](#错误处理表)
8. [Atlassian MCP 工具速查](#atlassian-mcp-工具速查)
---
## 公司 Jira 附件文档政策
自 **2026年3月9日** 起,何时附加「开发计划」「影响分析」「任务摘要」以公司强制性指南为准。
完整政策与详细条件表见 **`references/jira_commit_docs_policy.md`**。
- **附件命名规范**:上传到 Jira 的 MD 必须命名为
`<JIRA-ID>_Dev_Plan.md`、`<JIRA-ID>_Impact_Analysis.md`、`<JIRA-ID>_Task_Summary.md`
例如:`G3SF-123_Impact_Analysis.md`。
- 根据任务类型仅生成并上传“需要”的文档;无需文档的琐碎改动仍可绑定 Jira 并 amend,但不生成报告、不写评论、不上传附件。
---
## Step 1: 获取并解析提交内容
### 命令
```powershell
# 检查 hash 是否为 HEAD
$head = git rev-parse HEAD
$provided = git rev-parse <hash>
if ($head -ne $provided) { Write-Error "提交不是 HEAD,无法 amend" }
# 获取完整 diff 和元数据
git show <hash>
# 仅获取原始 commit message(用于后续 amend 保留)
git log -1 --format="%B" <hash>
# 列出修改的文件(不含 diff 内容,快速预览)
git diff-tree --no-commit-id -r --name-status <hash>
```
### 解析要点
从 `git show` 输出中提取:
| 信息 | 说明 |
|------|------|
| `--- a/path` / `+++ b/path` | 修改的文件路径 |
| `@@ ... @@` 上下文 | 修改的类名、方法名 |
| `+` 行 | 新增代码(方法签名、接口、配置) |
| `-` 行 | 删除/替换代码 |
| 文件路径中的包名 | 推断所属服务(`g3fo-trade-service`, `g3fo-margin-service` 等) |
G3FO 服务包路径规律:
- `com.afe.g3fo.<service-short-name>.*` → 对应服务
- `controller` / `api` → REST 接口层
- `facade` / `facade.impl` → Dubbo 接口层
- `service` / `service.impl` → 业务逻辑层
---
## Step 2: 任务类型判定与三文档模板
### 2.1 按任务类型确定需生成的文档
| 任务类型 | 开发计划 | 影响分析 | 任务摘要 |
|----------|:--------:|:--------:|:--------:|
| 漏洞修复——简单(如拼写、界面对齐) | ❌ | ❌ | ❌ |
| 漏洞修复——中等(范围有限的逻辑错误) | ❌ | ✅ | ❌ |
| 漏洞修复——复杂/高风险(并发、核心模块) | ✅ | ✅ | ✅ |
| 小幅度增强(如小效用方法) | ❌ | ✅ | ❌ |
| 新功能/模块 | ✅ | ✅ | ✅ |
| 主要重构 | ✅ | ✅ | ✅ |
| 配置/基础设施变更 | ❌ | ✅ | ❌ |
生成内容后,在**取得 Jira 编号后**按公司规范保存为 `<JIRA-ID>_Dev_Plan.md` / `_Impact_Analysis.md` / `_Task_Summary.md` 再上传。无需文档时跳过生成与上传,仅绑定 Jira 并 amend。
### 2.2 影响分析报告模板
生成影响分析时严格按以下结构输出(Markdown 格式):
```markdown
## 影响分析报告
### 1. 改动概览
- **背景与目标**:[说明为什么改、解决什么问题]
- **涉及模块**:[列举受影响的服务/模块,如 g3fo-trade-service、g3fo-margin-service]
- **改动类型**:功能新增 / 缺陷修复 / 重构 / 配置变更(选一或多个)
### 2. 方法级改动分析
| 文件/类 | 方法/接口 | 改动说明 | 行为是否变更 |
|---------|----------|---------|------------|
| XxxService.java | `calculateMargin()` | 新增空值校验 | 否(边界补强) |
### 3. 调用方与影响范围
| 调用方 | 文件路径 | 影响说明 |
|--------|---------|---------|
| XxxController | controller/XxxController.java | 入参校验增强,现有调用方兼容 |
- **破坏性变更**:是 / 否
- 如为"是",具体变更点:[列出方法签名、返回值、异常类型变化]
### 4. 风险与回滚
- **风险级别**:低 / 中 / 高
- **风险说明**:[说明原因]
- **回滚方式**:回退版本 / 关闭特性开关 / 回滚配置
- **回滚方式是否简单**:是 / 否
### 5. 验证与测试
- **已执行测试**:单测 / 集成测试 / 冒烟测试(选已执行的)
- **关键用例**:[简述关键场景及预期结果]
- **需线上观察**:[列出需关注的日志关键字、监控指标或告警项]
```
> 如果 diff 信息不足以确认调用方,在报告中注明:「建议在代码库中搜索 `<方法名>` 确认所有调用方」。
### 2.3 开发计划模板(仅当任务类型要求时生成)
开发计划用于概述步骤、设计方法和技术决策。结构示例:
```markdown
## 开发计划
### 1. 目标与范围
- **目标**:[本任务要达成的结果]
- **范围**:[涉及模块、接口、配置等]
### 2. 步骤与设计方法
1. [步骤一:如需求分析、接口设计]
2. [步骤二:如实现方案、关键类/方法]
3. [步骤三:如测试与验证]
### 3. 技术决策
- [关键技术选型或实现方式及理由]
- [与现有架构的衔接方式]
```
### 2.4 任务摘要模板(仅当任务类型要求时生成)
任务摘要用于简要回顾所完成的工作,包括任何偏离计划的地方。结构示例:
```markdown
## 任务摘要
### 1. 完成内容
- [已完成的主要改动与交付物]
### 2. 与计划差异(如有)
- [若实际实现与开发计划有偏差,在此说明原因与结果]
### 3. 后续建议(可选)
- [遗留事项、后续优化或文档更新建议]
```
---
## Step 3: Jira 任务查找与确认
### JQL 查询
```
project = G3SF AND assignee = currentUser() AND statusCategory != Done ORDER BY updated DESC
```
- 限制返回 5 条,避免信息过载
- 若返回 0 条,直接告知用户并自动进入新建流程
### 展示格式
```
找到以下 G3SF 进行中任务,请输入编号绑定,或输入 0 新建:
1. G3SF-101 [修复保证金计算逻辑] 状态: In Progress
2. G3SF-98 [交易服务批量撤单性能优化] 状态: To Do
3. G3SF-95 [用户服务登录接口重构] 状态: In Progress
0. 新建一个 G3SF Task
请输入 (1/2/3/0):
```
### 确认规则
| 用户输入 | 行为 |
|---------|------|
| `1`~`N` | 使用对应任务,写入评论 |
| `0` | 新建 Jira Task |
| `n` / `no` | 等同于 `0`,新建任务 |
| 其他 | 重新提问一次 |
---
## Step 4: Jira 字段映射与写入
### 新建 Issue 字段
调用 `jira_create_issue`(或鉴权后发现的等效工具),传入:
| 字段 | 值 |
|------|----|
| `project` | `G3SF` |
| `issuetype` | `Task` |
| `summary` | **必须带 `[G3SF]` 前缀**,格式:`[G3SF] 提炼后的一句话描述`(≤100 字符) |
| `description` | 完整影响分析报告(Markdown / ADF 格式) |
| `assignee` | **必须为当前登录账号**(传入当前用户 accountId/name,确保新建任务经办人为自己) |
Summary 生成规则:
- **标题必须统一加前缀**:`[G3SF]` + 正文。例如:`[G3SF] [g3fo-trade-service] 修复批量撤单时的并发锁问题`
- 正文优先使用原始 commit message 的第一行(若描述清晰),否则根据修改内容生成:`[服务名] 功能描述`
新建 Issue 成功后,若已配置 `jira_upload.env` 且本任务需要文档,按公司命名将对应 MD 写入本地后上传到该新 Issue。
### 保存报告为 MD 并上传附件
1. **按公司规范命名并保存**:取得 Jira 编号(如 `G3SF-123`)后,将 Step 2 生成的各文档内容分别写入本地文件,文件名必须为:
- `<JIRA-ID>_Dev_Plan.md`
- `<JIRA-ID>_Impact_Analysis.md`
- `<JIRA-ID>_Task_Summary.md`
仅保存本任务类型要求的那几份(见 [2.1 按任务类型确定需生成的文档](#21-按任务类型确定需生成的文档))。保存位置:当前 Git 项目根目录或项目约定目录(如 `docs/`)。
2. **上传到 Jira 附件**:Atlassian MCP 不支持上传文件,使用本技能自带脚本上传上述 MD 到当前 Issue:
- 配置文件:在技能目录下配置 `jira_upload.env`(`JIRA_BASE_URL`、`JIRA_EMAIL`、`JIRA_API_TOKEN`),详见 `references/env_config.md`。
- 命令示例(PowerShell 用 `;` 连接,勿用 `&&`),按需上传多文件:
```powershell
python skills/g3fo-commit-jira/scripts/upload_attachment.py --config "skills/g3fo-commit-jira/jira_upload.env" --issue "G3SF-101" --file "G3SF-101_Impact_Analysis.md" --file "G3SF-101_Dev_Plan.md" --file "G3SF-101_Task_Summary.md"
```
- 未配置 `jira_upload.env` 时跳过上传,仅写评论。
### 写入已有 Issue(评论)
调用 `jira_add_comment`(或等效工具),字段:
| 字段 | 值 |
|------|----|
| `issue_key` | 用户选择的 issue key,如 `G3SF-101` |
| `body` | 完整影响分析报告(与已保存的 MD 内容一致) |
### Jira 描述格式兼容
部分 Jira 实例使用 Atlassian Document Format (ADF) 而非纯 Markdown。若 MCP 工具提示格式错误,则:
- 将 Markdown 内容作为纯文本写入
- 或使用 `{code}` 块包裹(Jira Wiki 格式兼容)
---
## Step 5: Git Amend 命令详解
### PowerShell(Windows 环境)
在 Windows PowerShell 中,直接使用 `$origMsg` 可能会导致编码问题(乱码)。必须显式指定输出编码为 UTF8。
```powershell
# 设置控制台和输出编码为 UTF8,防止中文乱码
$OutputEncoding = [System.Text.Encoding]::UTF8
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
# 获取原始 commit message(保留完整内容)
$origMsg = (git log -1 --format="%B").TrimEnd()
# Jira key(从上一步获取)
$jiraKey = "G3SF-123"
# 追加 Jira 行并 amend(不 push)
# 使用 --cleanup=verbatim 确保消息内容不被 git 自动修剪导致编码转换问题
git commit --amend -m "$origMsg`n`n**** Jira $jiraKey" --cleanup=verbatim
```
### Bash/Linux(供参考)
```bash
origMsg=$(git log -1 --format="%B" | sed 's/[[:space:]]*$//')
jiraKey="G3SF-123"
git commit --amend -m "$origMsg
**** Jira $jiraKey"
```
### Amend 注意事项
- `--amend` 只修改 HEAD,不改变工作区内容
- 如果有 GPG 签名要求,告知用户需确认签名
- amend 后 commit hash 会变更,这是正常现象
- **禁止 push**:本 skill 不执行 `git push`,提交操作到 `amend` 为止
### 结果验证
```powershell
git log -1 --format="%B" # 确认 Jira 行已追加
git log --oneline -1 # 查看 commit 摘要
```
---
## 错误处理表
| 错误场景 | 检测方式 | 处理方式 |
|---------|---------|---------|
| 修订号不存在 | `git rev-parse <hash>` 报错 | **停止**,提示用户提供正确的修订号。 |
| 提交 hash 不是 HEAD | `git rev-parse HEAD` ≠ `git rev-parse <hash>` | 停止,提示用户确认 hash(只有 HEAD 才能 amend)。 |
| 项目路径不正确 | `git rev-parse --is-inside-work-tree` 失败 | 提示用户在正确的 git 项目目录下操作。 |
| Atlassian MCP 未鉴权 | `STATUS.md` 有提示 / 工具调用返回 401 | 调用 `mcp_auth`,等待用户完成授权。 |
| Atlassian 插件未安装 | `tools/` 目录为空或 MCP 调用失败 | 引导用户在 Cursor Settings → MCP 安装 Atlassian 插件 |
| Jira 项目 G3SF 不存在 | `jira_create_issue` 返回 404/400 | 提示用户确认 Jira 实例 URL 和项目 key |
| 工作区有未提交变更 | `git status --porcelain` 有输出 | 警告:amend 会将未暂存变更排除在外,建议先 `git add` 或 stash |
| commit message 中已有 Jira 行 | message 中包含 `**** Jira` | 跳过追加,提示用户该提交已绑定 |
| Jira 搜索无结果 | 工具返回空列表 | 直接进入新建流程,无需用户确认 |
| 未配置 jira_upload.env | 上传前检查配置不存在 | 跳过上传附件,仅通过 jira_add_comment 写入报告;提示用户可配置后使用脚本上传 |
| 任务类型判定为「无需文档」 | 按政策表属简单漏洞/琐碎改动 | 不生成报告、不写评论、不上传附件;仍执行 Jira 绑定与 commit --amend,将 Jira 编号追加到 commit message |
---
## Atlassian MCP 工具速查
鉴权成功后,通过列举 `mcps/plugin-atlassian-atlassian/tools/` 确认可用工具。常见工具名:
| 功能 | 可能的工具名 |
|------|------------|
| 搜索 Issue | `jira_search_issues`, `jira_jql_search` |
| 创建 Issue | `jira_create_issue` |
| 获取 Issue 详情 | `jira_get_issue` |
| 添加评论 | `jira_add_comment` |
| 更新 Issue | `jira_update_issue` |
| 获取当前用户 | `jira_get_current_user` |
| 获取项目列表 | `jira_get_projects` |
| 上传附件 | MCP 不支持,使用 `scripts/upload_attachment.py` + `jira_upload.env` |
> 实际工具名以 `tools/` 目录下的 JSON 文件名为准,鉴权后务必先列目录确认。上传附件需使用本技能自带的 Python 脚本,见 `references/env_config.md`。
@@ -0,0 +1 @@
requests>=2.28.0
@@ -0,0 +1,122 @@
#!/usr/bin/env python3
"""
Upload one or more files as attachments to a Jira Cloud issue via REST API v3.
Config: from jira_upload.env (current dir or script dir) or env vars.
Usage:
python upload_attachment.py --issue ISSUE_KEY --file path1 [--file path2 ...]
python upload_attachment.py --config /path/to/jira_upload.env --issue KEY --file path1
"""
import argparse
import base64
import os
import sys
from pathlib import Path
from typing import Dict, Optional
try:
import requests
except ImportError:
print("ERROR: 'requests' is required. Run: pip install requests", file=sys.stderr)
sys.exit(1)
CONFIG_FILENAME = "jira_upload.env"
def _find_config_file(explicit_path: Optional[str], script_dir: Path) -> Optional[Path]:
if explicit_path:
p = Path(explicit_path)
return p if p.exists() and p.is_file() else None
cwd_file = Path.cwd() / CONFIG_FILENAME
if cwd_file.exists():
return cwd_file
script_dir_file = script_dir / CONFIG_FILENAME
if script_dir_file.exists():
return script_dir_file
return None
def load_config(config_path: Optional[str]) -> Dict[str, str]:
"""Load KEY=VALUE from jira_upload.env. Env vars override file values."""
script_dir = Path(__file__).resolve().parent
path = _find_config_file(config_path, script_dir)
out = {}
if path:
with open(path, "r", encoding="utf-8") as f:
for line in f:
line = line.strip()
if not line or line.startswith("#"):
continue
if "=" in line:
k, _, v = line.partition("=")
out[k.strip()] = v.strip().strip('"').strip("'")
for key in ("JIRA_BASE_URL", "JIRA_EMAIL", "JIRA_API_TOKEN"):
if key in os.environ:
out[key] = os.environ[key]
return out
def main() -> int:
parser = argparse.ArgumentParser(description="Upload files as attachments to a Jira issue")
parser.add_argument("--issue", required=True, help="Jira issue key (e.g. G3SF-123)")
parser.add_argument("--file", action="append", required=True, dest="files", help="Path to file to upload (can be repeated)")
parser.add_argument("--config", default=None, help=f"Path to config file (default: {CONFIG_FILENAME} in cwd or script dir)")
args = parser.parse_args()
cfg = load_config(args.config)
base_url = (cfg.get("JIRA_BASE_URL") or "").rstrip("/")
email = cfg.get("JIRA_EMAIL")
token = cfg.get("JIRA_API_TOKEN")
if not base_url or not email or not token:
print("ERROR: Set JIRA_BASE_URL, JIRA_EMAIL, JIRA_API_TOKEN in config file or environment.", file=sys.stderr)
print(f" Config file: {CONFIG_FILENAME} (in current dir or script dir), or use --config PATH", file=sys.stderr)
return 1
issue_key = args.issue.strip()
files_to_upload = []
for p in args.files:
path = Path(p)
if not path.exists():
print(f"ERROR: File not found: {path}", file=sys.stderr)
return 1
if not path.is_file():
print(f"ERROR: Not a file: {path}", file=sys.stderr)
return 1
files_to_upload.append(path)
url = f"{base_url}/rest/api/3/issue/{issue_key}/attachments"
auth_str = base64.b64encode(f"{email}:{token}".encode()).decode()
headers = {
"Authorization": f"Basic {auth_str}",
"X-Atlassian-Token": "no-check",
}
for path in files_to_upload:
with open(path, "rb") as f:
files = {"file": (path.name, f)}
try:
resp = requests.post(url, headers=headers, files=files, timeout=60)
except requests.RequestException as e:
print(f"ERROR: Request failed for {path.name}: {e}", file=sys.stderr)
return 1
if resp.status_code not in (200, 201):
print(f"ERROR: Upload failed for {path.name}: HTTP {resp.status_code}", file=sys.stderr)
if resp.text:
print(resp.text[:500], file=sys.stderr)
return 1
data = resp.json()
if isinstance(data, list) and data:
for att in data:
print(f"OK: {att.get('filename', path.name)} (id={att.get('id')}, size={att.get('size', '?')})")
else:
print(f"OK: {path.name}")
return 0
if __name__ == "__main__":
sys.exit(main())