<h1 align="center">Lyrebird - BugIt plugin</h1>
[](https://github.com/Meituan-Dianping/lyrebird-bugit/actions/workflows/unittest.yml)
[](https://github.com/Meituan-Dianping/lyrebird-bugit/actions/workflows/publish.yml)
[](https://github.com/Meituan-Dianping/lyrebird-bugit/actions/workflows/codeql.yml)
[](https://pypi.python.org/pypi/lyrebird-bugit)


**[Lyrebird](https://github.com/Meituan-Dianping/lyrebird)**
是一个基于拦截以及模拟HTTP/HTTPS网络请求的面向移动应用的插件化测试平台。
**BugIt plugin是一个Lyrebird的插件,用于汇总Lyrebird各插件信息,提交Bug。**
----
# 简介
BugIt是一个[Lyrebird](https://github.com/Meituan-Dianping/lyrebird)插件。
通过BugIt可以轻松的将Lyrebird中收集的人机交互数据作为描述或者附件提交到Bug管理系统中(如JIRA)。
与[检查器](https://meituan-dianping.github.io/lyrebird/guide/checker.html#%E8%BD%BD%E5%85%A5%E6%A3%80%E6%9F%A5%E5%99%A8)(Checker)结合,即可实现一键提交bug的功能。
# 快速开始
## 环境要求
- macOS OR Linux
- Python3.7及以上
- Lyrebird 1.6及以上
## 安装
```bash
pip3 install lyrebird-bugit
```
## 启动
```bash
lyrebird
```
# 功能介绍
## Bug提交
读取脚本文件后,BugIt会得到一个Bug信息填充的界面,称之为Bug表单。
用户可以自定义Bug字段的 名称、先后顺序、填写样式、默认值。

BugIt可以通过配置服务向任意Bug管理系统提交Issue
## API数据获取
BugIt支持自动填充Lyrebird运行过程中抓取到的数据信息。

## 报警获取
在[检查器](https://meituan-dianping.github.io/lyrebird/guide/checker.html#载入检查器)([Checker](https://meituan-dianping.github.io/lyrebird/guide/checker.html#载入检查器))捕获报警后,可以通过通知中心随时跳转至BugIt。
在右侧数据面板中,可以对历史[消息总线](/advance/eventbus.md)中的信息进行回溯,补充至Bug中。

此外,还支持针对指定检查器的报警进行自动开Bug。
通过配置可以指定检查器,BugIt会将这些检查器的报警信息送入配置指定的脚本内,在脚本内自定义对报警信息的处理,即可实现Bug的自动上报功能。
## 缓存功能
按下[Commond]+[s]键,会将Bug相关字段信息进行存储。
缓存信息不受Lyrebird服务开关、浏览器缓存、脚本/界面切换的影响。

有效的利用缓存功能,是提高Bug上报效率的关键。
## 插件在Bugit中的应用
### Android iOS插件
安装[Android插件](https://meituan-dianping.github.io/lyrebird/guide/plugin.html#android插件)、[iOS插件](https://meituan-dianping.github.io/lyrebird/guide/plugin.html#ios插件)后,BugIt支持设备信息扩展服务。
- 设备信息获取
- BugIt 支持将设备名称、设备系统版本、被测 App 信息(Bundle ID(iOS)/PackageName(Android))填充到Issue内容中。
- 实时设备截图
- BugIt 可以实时获取设备截图,并支持在截图上进行涂鸦、文本标记。
- 提交 Bug 时,截图将作为附件一并提交给脚本配置服务。
- Crash Log 获取
- 在Lyrebird运行过程中,如发生 被测 App 发生 Crash,BugIt 会捕获到 Crash Log。
- 提交 Bug 时,Crash Log 将作为附件一并提交给脚本配置服务。
- Crash 获取 暂不支持 iOS 设备

## BugIt脚本
在 ~/.lyrebird/conf.json 中,BugIt会读取 “bugit.workspace”字段。并将此字段值作为读取模板的根目录。
该目录下所有模板都会加载到BugIt中,并在UI中可选。
刷新BugIt界面即可重新加载所有模板。
### 模板使用说明
BugIt通过模板定义UI以及提交的行为。通过不同的模板支持JIRA以及其他的Bug管理系统。
BugIt模板是一个Python文件,要求使用Python3.7及以上的版本编写。

配置脚本需包含以下三部分
- init配置文件
- form()
- submit()
### init配置文件
BugIt 通过 name 来标识配置文件。
>注意:BugIt 脚本必须含有 'name' 属性
```python
"""
Template name
"""
name = 'TEST-JIRA'
```
name 用于在 BugIt 前端页面上展示脚本名称。

选中配置文件后,BugIt init 脚本文件,获取脚本文件中回调方法form()、submit()。
### form()
form() 方法用于自定义Bug表单的字段与填写样式。
Bug 中的每一字段由一dict定义(通常称之为FormItem),表单配置时,通过dict的以下关键字去控制字段的名称、默认值、样式等内容。
>form()方法应返回一由FormItem组成的list。
#### FormItem dict说明
key|说明|枚举值|是否必填
:--:|:--|:--:|:--:
name|展示在页面上的字段名称|--|Y
value|对应字段填充的值|--|N
component|字段展示的组件形式|'input'、'select'、'compoundTextarea'|Y
options|配合 select component使用,用于存放select component的选项内容|--|N
custom keys|自定义属性,不影响UI组件展示,可以帮住更好地处理数据,详细用法请参考[脚本高级](https://meituan-dianping.github.io/lyrebird/plugins/bugit.html)|--|N
#### BugIt 支持的 component
- input
```python
form_item_input = {
'name': '主题',
'component': 'input',
'value': 'defalut value'
}
```

input 组件会生成一个文本输入框,在声明时如果 value 不为空,则会作为默认值填充在输入框中
- select
```python
form_item_select = {
'name': '版本',
'component': 'select',
'options':[
{'id':'001','name':'option_1'},
{'id':'002','name':'option_2'}
],
'value': '001'
}
```

select组件会生成一个筛选框,筛选项由 options 定义,options 为一个 list,其中每一元素为 dict 类型。
option dict说明
key|说明|是否必填
:--:|:--|:--:
id|筛选项唯一标识|Y
name|筛选项在前端展示文本内容|Y
select 组件中,value应为option dict中对应 id 的值
- compoundTextarea
compoundTextarea组件会生成一个文本框,在声明时如果 value 不为空,则会作为默认值填充在文本框中
```python
form_item_text = {
'name': '描述',
'component': 'compoundTextarea',
'value': 'defalut value'
}
```

compoundTextarea组件在 BugIt 中还用于支持 Lyrebird 其他信息的扩展。
选择[消息总线](https://meituan-dianping.github.io/lyrebird/advance/eventbus.html)或其他[插件](https://meituan-dianping.github.io/lyrebird/plugins/)的数据时,该数据将作为附加信息展示在 compoundTextarea 中。

对应的数据信息将作为 'extraMsg' 存放在 FormItem中。
此时From Item变成
```python
form_item_text = {
'name': '描述',
'component': 'compoundTextarea',
'value': 'defalut value',
'extraMsg':[
{'message':'Flow Info'},
{'message':'Notice Info'},
{'message':'Devices Info'}
]
}
```
#### form()方法示例代码
```python
def form(context):
"""
BugIt callback function
BugIt will call this function when user select this template from UI.
This function should return a array, list all FormItem dict.
"""
form_item_input = {
'name': '主题',
'component': 'input',
'value': 'defalut value'
}
form_item_select = {
'name': '版本',
'component': 'select',
'options':[
{'id':'001','name':'option_1'},
{'id':'002','name':'option_2'}
],
'value': '001'
}
form_item_text = {
'name': '描述',
'component': 'compoundTextarea',
'value': 'defalut value'
}
form = [form_item_input,form_item_select,form_item_text]
return form
```
示例代码生成的 Bug 表单如下图所示

>form()方法入参 context ,可用于传递缓存数据,相关用法详见[脚本高级](https://meituan-dianping.github.io/lyrebird/plugins/bugit.html)
### submit()
提交函数返回一个数组,数组中包含若干提交步骤需要回调的函数。
>submit()方法返回一组用于处理 submit 行为的方法。
方法说明
方法名|入参|说明
:--:|:--:|:--
issue|context|处理 Bug表单中的信息,通过 API 向 Bug 管理系统提交 Issue
attachments|context|处理附件信息(如 [Android插件](https://meituan-dianping.github.io/lyrebird/guide/plugin.html#android插件)、[iOS插件](https://meituan-dianping.github.io/lyrebird/guide/plugin.html#ios插件)提供的截图或 Log),通过 API 向 创建的 Issue 中添加附件
#### submit()方法示例代码(以JIRA服务为例)
```python
def submit():
"""
BugIt callback function
BugIt will call this function when user tap submit button.
This function should return a array, contains all submit actions.
Each submit-action function have a argument. It contains form data and attachments info.
"""
return [issue, attachments]
def issue(context):
"""
This function is used to submit Issue
"""
form_data = context['issue']
jira_fields = {}
# transform from_data to payload submited to API of JIRA
for form_item in form_data:
if form_item['name'] == '主题' :
jira_fields['summary'] = form_item['value']
elif form_item['name'] == '版本':
jira_fields['version'] = {
id:form_item['value']
}
elif form_item['name'] == '描述':
jira_fields['description'] = form_item['value']
# add extraMsg to description
if form_item['extraMsg']:
for add_des in form_item['extraMsg']:
jira_fields['description'] +='\n'
jira_fields['description'] +='------------------------------\n'
jira_fields['description'] +=add_des['message']
url = 'http://www.example.com/jira/rest/api/2/issue'
header = {
'Content-Type': 'application/json;charset=utf-8'
}
resp = requests.post(url, auth=('YOUR JIRA_USER_NAME', 'YOUR_JIRA_PASSWD'),json={"fields": jira_fields},headers=header)
if resp.status_code >= 200 and resp.status_code < 300:
body = json.loads(response.text)
if body.get('key') :
context['key'] = body['key']
else:
raise Exception(f'Submit failed {response.text}')
else:
raise Exception(f'Create issue failed with code {response.status_code}\n{response.text}')
def attachments(context):
"""
This function is used to upload attachments to the Issue which has been created
"""
key = context['key']
attachments = context['attachments']
if len(attachments) == 0:
# No attachments
return
url = f'http://www.example.com/jira/rest/api/2/issue/{key}/attachments'
headers = {
'X-Atlassian-Token': 'nocheck'
}
multiple_files = []
# transform attachment data to payload submited to API of JIRA
for attachment in attachments:
attachment_path = Path(attachment['path'])
multiple_files.append(
('file', (attachment_path.name, open(str(attachment_path), 'rb')))
)
response = requests.post(url, files=multiple_files, headers=headers)
if response.status_code == 200 and response.json()['code'] == 0:
print('Submit attachments success')
else:
raise Exception(f'Submit failed {response.text}')
```
完整脚本请参考: [示例脚本](example/TEST.py)
脚本高级功能请参考:[脚本高级](https://meituan-dianping.github.io/lyrebird/plugins/bugit.html)
----
# 开发者指南
## 开发环境
- macOS OR Linux
- Python3
- NodeJS
- vscode(推荐)
- Chrome(推荐)
## 调试代码
### Vscode debug配置
```json
{
"version": "0.2.0",
"configurations": [
{
"name": "Python: Terminal (integrated)",
"type": "python",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal"
},
{
"name": "Server",
"type": "python",
"request": "launch",
"module": "lyrebird",
"args": [
"-b",
"-vvv",
"--plugin",
"${workspaceFolder}"
],
"console": "integratedTerminal"
},
{
"name": "Client",
"type": "chrome",
"request": "launch",
"url": "http://localhost:8080/",
"webRoot": "${workspaceFolder}/frontend/src/",
"sourceMapPathOverrides": {
"webpack:///src/*": "${webRoot}/*"
},
"timeout": 30000
}
]
}
```
### 后端代码
1. 激活python虚拟环境
通过 source venv/bin/activate 来激活该环境
2. 通过Debug功能启动
按照上面 debug配置中 python:Lyrebrid配置启动即可
### 前端代码
1. 启动node server
```
# 进入前端目录
cd frontend
# 启动前端node serve
npm run serve
```
2. 通过Debug功能启动浏览器
按照上面 debug配置中 vuejs: chrome 配置启动即可
>注意: vscode 需要安装chrome debug插件
Raw data
{
"_id": null,
"home_page": "https://github.com/Meituan-Dianping/lyrebird-bugit",
"name": "lyrebird-bugit",
"maintainer": null,
"docs_url": null,
"requires_python": null,
"maintainer_email": null,
"keywords": null,
"author": "HBQA",
"author_email": null,
"download_url": "https://files.pythonhosted.org/packages/4b/c6/e2b04de22244e78b372bc82d4ddeb69189185569f499fd35cca12f3b2be3/lyrebird-bugit-1.16.1.tar.gz",
"platform": null,
"description": "<h1 align=\"center\">Lyrebird - BugIt plugin</h1>\n\n[](https://github.com/Meituan-Dianping/lyrebird-bugit/actions/workflows/unittest.yml)\n[](https://github.com/Meituan-Dianping/lyrebird-bugit/actions/workflows/publish.yml)\n[](https://github.com/Meituan-Dianping/lyrebird-bugit/actions/workflows/codeql.yml)\n\n[](https://pypi.python.org/pypi/lyrebird-bugit)\n\n\n\n**[Lyrebird](https://github.com/Meituan-Dianping/lyrebird)**\n\u662f\u4e00\u4e2a\u57fa\u4e8e\u62e6\u622a\u4ee5\u53ca\u6a21\u62dfHTTP/HTTPS\u7f51\u7edc\u8bf7\u6c42\u7684\u9762\u5411\u79fb\u52a8\u5e94\u7528\u7684\u63d2\u4ef6\u5316\u6d4b\u8bd5\u5e73\u53f0\u3002\n\n**BugIt plugin\u662f\u4e00\u4e2aLyrebird\u7684\u63d2\u4ef6\uff0c\u7528\u4e8e\u6c47\u603bLyrebird\u5404\u63d2\u4ef6\u4fe1\u606f\uff0c\u63d0\u4ea4Bug\u3002**\n\n----\n\n# \u7b80\u4ecb\nBugIt\u662f\u4e00\u4e2a[Lyrebird](https://github.com/Meituan-Dianping/lyrebird)\u63d2\u4ef6\u3002\n\n\u901a\u8fc7BugIt\u53ef\u4ee5\u8f7b\u677e\u7684\u5c06Lyrebird\u4e2d\u6536\u96c6\u7684\u4eba\u673a\u4ea4\u4e92\u6570\u636e\u4f5c\u4e3a\u63cf\u8ff0\u6216\u8005\u9644\u4ef6\u63d0\u4ea4\u5230Bug\u7ba1\u7406\u7cfb\u7edf\u4e2d(\u5982JIRA)\u3002\n\n\u4e0e[\u68c0\u67e5\u5668](https://meituan-dianping.github.io/lyrebird/guide/checker.html#%E8%BD%BD%E5%85%A5%E6%A3%80%E6%9F%A5%E5%99%A8)(Checker)\u7ed3\u5408\uff0c\u5373\u53ef\u5b9e\u73b0\u4e00\u952e\u63d0\u4ea4bug\u7684\u529f\u80fd\u3002\n\n# \u5feb\u901f\u5f00\u59cb\n## \u73af\u5883\u8981\u6c42\n\n- macOS OR Linux\n\n- Python3.7\u53ca\u4ee5\u4e0a\n\n- Lyrebird 1.6\u53ca\u4ee5\u4e0a\n## \u5b89\u88c5\n\n```bash\npip3 install lyrebird-bugit\n```\n\n## \u542f\u52a8\n\n```bash\nlyrebird\n```\n\n# \u529f\u80fd\u4ecb\u7ecd\n\n## Bug\u63d0\u4ea4\n\u8bfb\u53d6\u811a\u672c\u6587\u4ef6\u540e\uff0cBugIt\u4f1a\u5f97\u5230\u4e00\u4e2aBug\u4fe1\u606f\u586b\u5145\u7684\u754c\u9762\uff0c\u79f0\u4e4b\u4e3aBug\u8868\u5355\u3002\n\n\u7528\u6237\u53ef\u4ee5\u81ea\u5b9a\u4e49Bug\u5b57\u6bb5\u7684 \u540d\u79f0\u3001\u5148\u540e\u987a\u5e8f\u3001\u586b\u5199\u6837\u5f0f\u3001\u9ed8\u8ba4\u503c\u3002\n\n\n\nBugIt\u53ef\u4ee5\u901a\u8fc7\u914d\u7f6e\u670d\u52a1\u5411\u4efb\u610fBug\u7ba1\u7406\u7cfb\u7edf\u63d0\u4ea4Issue\n\n## API\u6570\u636e\u83b7\u53d6\nBugIt\u652f\u6301\u81ea\u52a8\u586b\u5145Lyrebird\u8fd0\u884c\u8fc7\u7a0b\u4e2d\u6293\u53d6\u5230\u7684\u6570\u636e\u4fe1\u606f\u3002\n\n\n\n## \u62a5\u8b66\u83b7\u53d6\n\n\u5728[\u68c0\u67e5\u5668](https://meituan-dianping.github.io/lyrebird/guide/checker.html#\u8f7d\u5165\u68c0\u67e5\u5668)([Checker](https://meituan-dianping.github.io/lyrebird/guide/checker.html#\u8f7d\u5165\u68c0\u67e5\u5668))\u6355\u83b7\u62a5\u8b66\u540e\uff0c\u53ef\u4ee5\u901a\u8fc7\u901a\u77e5\u4e2d\u5fc3\u968f\u65f6\u8df3\u8f6c\u81f3BugIt\u3002\n\n\u5728\u53f3\u4fa7\u6570\u636e\u9762\u677f\u4e2d\uff0c\u53ef\u4ee5\u5bf9\u5386\u53f2[\u6d88\u606f\u603b\u7ebf](/advance/eventbus.md)\u4e2d\u7684\u4fe1\u606f\u8fdb\u884c\u56de\u6eaf\uff0c\u8865\u5145\u81f3Bug\u4e2d\u3002\n\n\n\n\u6b64\u5916\uff0c\u8fd8\u652f\u6301\u9488\u5bf9\u6307\u5b9a\u68c0\u67e5\u5668\u7684\u62a5\u8b66\u8fdb\u884c\u81ea\u52a8\u5f00Bug\u3002\n\n\u901a\u8fc7\u914d\u7f6e\u53ef\u4ee5\u6307\u5b9a\u68c0\u67e5\u5668\uff0cBugIt\u4f1a\u5c06\u8fd9\u4e9b\u68c0\u67e5\u5668\u7684\u62a5\u8b66\u4fe1\u606f\u9001\u5165\u914d\u7f6e\u6307\u5b9a\u7684\u811a\u672c\u5185\uff0c\u5728\u811a\u672c\u5185\u81ea\u5b9a\u4e49\u5bf9\u62a5\u8b66\u4fe1\u606f\u7684\u5904\u7406\uff0c\u5373\u53ef\u5b9e\u73b0Bug\u7684\u81ea\u52a8\u4e0a\u62a5\u529f\u80fd\u3002\n\n## \u7f13\u5b58\u529f\u80fd\n\n\u6309\u4e0b[Commond]+[s]\u952e\uff0c\u4f1a\u5c06Bug\u76f8\u5173\u5b57\u6bb5\u4fe1\u606f\u8fdb\u884c\u5b58\u50a8\u3002\n\n\u7f13\u5b58\u4fe1\u606f\u4e0d\u53d7Lyrebird\u670d\u52a1\u5f00\u5173\u3001\u6d4f\u89c8\u5668\u7f13\u5b58\u3001\u811a\u672c/\u754c\u9762\u5207\u6362\u7684\u5f71\u54cd\u3002\n\n\n\n\u6709\u6548\u7684\u5229\u7528\u7f13\u5b58\u529f\u80fd\uff0c\u662f\u63d0\u9ad8Bug\u4e0a\u62a5\u6548\u7387\u7684\u5173\u952e\u3002\n\n## \u63d2\u4ef6\u5728Bugit\u4e2d\u7684\u5e94\u7528\n\n\n### Android iOS\u63d2\u4ef6\n\n\u5b89\u88c5[Android\u63d2\u4ef6](https://meituan-dianping.github.io/lyrebird/guide/plugin.html#android\u63d2\u4ef6)\u3001[iOS\u63d2\u4ef6](https://meituan-dianping.github.io/lyrebird/guide/plugin.html#ios\u63d2\u4ef6)\u540e\uff0cBugIt\u652f\u6301\u8bbe\u5907\u4fe1\u606f\u6269\u5c55\u670d\u52a1\u3002\n\n- \u8bbe\u5907\u4fe1\u606f\u83b7\u53d6\n - BugIt \u652f\u6301\u5c06\u8bbe\u5907\u540d\u79f0\u3001\u8bbe\u5907\u7cfb\u7edf\u7248\u672c\u3001\u88ab\u6d4b App \u4fe1\u606f\uff08Bundle ID(iOS)/PackageName(Android)\uff09\u586b\u5145\u5230Issue\u5185\u5bb9\u4e2d\u3002\n- \u5b9e\u65f6\u8bbe\u5907\u622a\u56fe\n - BugIt \u53ef\u4ee5\u5b9e\u65f6\u83b7\u53d6\u8bbe\u5907\u622a\u56fe\uff0c\u5e76\u652f\u6301\u5728\u622a\u56fe\u4e0a\u8fdb\u884c\u6d82\u9e26\u3001\u6587\u672c\u6807\u8bb0\u3002\n - \u63d0\u4ea4 Bug \u65f6\uff0c\u622a\u56fe\u5c06\u4f5c\u4e3a\u9644\u4ef6\u4e00\u5e76\u63d0\u4ea4\u7ed9\u811a\u672c\u914d\u7f6e\u670d\u52a1\u3002\n- Crash Log \u83b7\u53d6\n - \u5728Lyrebird\u8fd0\u884c\u8fc7\u7a0b\u4e2d\uff0c\u5982\u53d1\u751f \u88ab\u6d4b App \u53d1\u751f Crash\uff0cBugIt \u4f1a\u6355\u83b7\u5230 Crash Log\u3002\n - \u63d0\u4ea4 Bug \u65f6\uff0cCrash Log \u5c06\u4f5c\u4e3a\u9644\u4ef6\u4e00\u5e76\u63d0\u4ea4\u7ed9\u811a\u672c\u914d\u7f6e\u670d\u52a1\u3002\n - Crash \u83b7\u53d6 \u6682\u4e0d\u652f\u6301 iOS \u8bbe\u5907\n\n\n\n## BugIt\u811a\u672c\n\n\u5728 ~/.lyrebird/conf.json \u4e2d\uff0cBugIt\u4f1a\u8bfb\u53d6 \u201cbugit.workspace\u201d\u5b57\u6bb5\u3002\u5e76\u5c06\u6b64\u5b57\u6bb5\u503c\u4f5c\u4e3a\u8bfb\u53d6\u6a21\u677f\u7684\u6839\u76ee\u5f55\u3002\n\n\u8be5\u76ee\u5f55\u4e0b\u6240\u6709\u6a21\u677f\u90fd\u4f1a\u52a0\u8f7d\u5230BugIt\u4e2d\uff0c\u5e76\u5728UI\u4e2d\u53ef\u9009\u3002\n\n\u5237\u65b0BugIt\u754c\u9762\u5373\u53ef\u91cd\u65b0\u52a0\u8f7d\u6240\u6709\u6a21\u677f\u3002\n\n### \u6a21\u677f\u4f7f\u7528\u8bf4\u660e\n\nBugIt\u901a\u8fc7\u6a21\u677f\u5b9a\u4e49UI\u4ee5\u53ca\u63d0\u4ea4\u7684\u884c\u4e3a\u3002\u901a\u8fc7\u4e0d\u540c\u7684\u6a21\u677f\u652f\u6301JIRA\u4ee5\u53ca\u5176\u4ed6\u7684Bug\u7ba1\u7406\u7cfb\u7edf\u3002\n\nBugIt\u6a21\u677f\u662f\u4e00\u4e2aPython\u6587\u4ef6\uff0c\u8981\u6c42\u4f7f\u7528Python3.7\u53ca\u4ee5\u4e0a\u7684\u7248\u672c\u7f16\u5199\u3002\n\n\u914d\u7f6e\u811a\u672c\u9700\u5305\u542b\u4ee5\u4e0b\u4e09\u90e8\u5206\n- init\u914d\u7f6e\u6587\u4ef6\n- form()\n- submit()\n\n### init\u914d\u7f6e\u6587\u4ef6\n\nBugIt \u901a\u8fc7 name \u6765\u6807\u8bc6\u914d\u7f6e\u6587\u4ef6\u3002\n\n>\u6ce8\u610f\uff1aBugIt \u811a\u672c\u5fc5\u987b\u542b\u6709 'name' \u5c5e\u6027\n\n\n```python\n\"\"\"\nTemplate name\n\"\"\" \nname = 'TEST-JIRA'\n```\nname \u7528\u4e8e\u5728 BugIt \u524d\u7aef\u9875\u9762\u4e0a\u5c55\u793a\u811a\u672c\u540d\u79f0\u3002\n\n\n\n\u9009\u4e2d\u914d\u7f6e\u6587\u4ef6\u540e\uff0cBugIt init \u811a\u672c\u6587\u4ef6\uff0c\u83b7\u53d6\u811a\u672c\u6587\u4ef6\u4e2d\u56de\u8c03\u65b9\u6cd5form()\u3001submit()\u3002\n\n### form()\n\nform() \u65b9\u6cd5\u7528\u4e8e\u81ea\u5b9a\u4e49Bug\u8868\u5355\u7684\u5b57\u6bb5\u4e0e\u586b\u5199\u6837\u5f0f\u3002\n\nBug \u4e2d\u7684\u6bcf\u4e00\u5b57\u6bb5\u7531\u4e00dict\u5b9a\u4e49\uff08\u901a\u5e38\u79f0\u4e4b\u4e3aFormItem\uff09\uff0c\u8868\u5355\u914d\u7f6e\u65f6\uff0c\u901a\u8fc7dict\u7684\u4ee5\u4e0b\u5173\u952e\u5b57\u53bb\u63a7\u5236\u5b57\u6bb5\u7684\u540d\u79f0\u3001\u9ed8\u8ba4\u503c\u3001\u6837\u5f0f\u7b49\u5185\u5bb9\u3002\n\n>form()\u65b9\u6cd5\u5e94\u8fd4\u56de\u4e00\u7531FormItem\u7ec4\u6210\u7684list\u3002\n\n#### FormItem dict\u8bf4\u660e\nkey|\u8bf4\u660e|\u679a\u4e3e\u503c|\u662f\u5426\u5fc5\u586b\n:--:|:--|:--:|:--:\nname|\u5c55\u793a\u5728\u9875\u9762\u4e0a\u7684\u5b57\u6bb5\u540d\u79f0|--|Y\nvalue|\u5bf9\u5e94\u5b57\u6bb5\u586b\u5145\u7684\u503c|--|N\ncomponent|\u5b57\u6bb5\u5c55\u793a\u7684\u7ec4\u4ef6\u5f62\u5f0f|'input'\u3001'select'\u3001'compoundTextarea'|Y\noptions|\u914d\u5408 select component\u4f7f\u7528\uff0c\u7528\u4e8e\u5b58\u653eselect component\u7684\u9009\u9879\u5185\u5bb9|--|N\ncustom keys|\u81ea\u5b9a\u4e49\u5c5e\u6027\uff0c\u4e0d\u5f71\u54cdUI\u7ec4\u4ef6\u5c55\u793a\uff0c\u53ef\u4ee5\u5e2e\u4f4f\u66f4\u597d\u5730\u5904\u7406\u6570\u636e\uff0c\u8be6\u7ec6\u7528\u6cd5\u8bf7\u53c2\u8003[\u811a\u672c\u9ad8\u7ea7](https://meituan-dianping.github.io/lyrebird/plugins/bugit.html)|--|N\n\n#### BugIt \u652f\u6301\u7684 component\n- input\n\n```python\nform_item_input = {\n 'name': '\u4e3b\u9898',\n 'component': 'input',\n 'value': 'defalut value'\n}\n```\n\n\n\ninput \u7ec4\u4ef6\u4f1a\u751f\u6210\u4e00\u4e2a\u6587\u672c\u8f93\u5165\u6846\uff0c\u5728\u58f0\u660e\u65f6\u5982\u679c value \u4e0d\u4e3a\u7a7a\uff0c\u5219\u4f1a\u4f5c\u4e3a\u9ed8\u8ba4\u503c\u586b\u5145\u5728\u8f93\u5165\u6846\u4e2d\n\n- select\n\n```python\nform_item_select = {\n 'name': '\u7248\u672c',\n 'component': 'select',\n 'options':[\n {'id':'001','name':'option_1'},\n {'id':'002','name':'option_2'}\n ],\n 'value': '001'\n}\n```\n\n\n\nselect\u7ec4\u4ef6\u4f1a\u751f\u6210\u4e00\u4e2a\u7b5b\u9009\u6846\uff0c\u7b5b\u9009\u9879\u7531 options \u5b9a\u4e49\uff0coptions \u4e3a\u4e00\u4e2a list\uff0c\u5176\u4e2d\u6bcf\u4e00\u5143\u7d20\u4e3a dict \u7c7b\u578b\u3002\n\noption dict\u8bf4\u660e\nkey|\u8bf4\u660e|\u662f\u5426\u5fc5\u586b\n:--:|:--|:--:\nid|\u7b5b\u9009\u9879\u552f\u4e00\u6807\u8bc6|Y\nname|\u7b5b\u9009\u9879\u5728\u524d\u7aef\u5c55\u793a\u6587\u672c\u5185\u5bb9|Y\n\nselect \u7ec4\u4ef6\u4e2d\uff0cvalue\u5e94\u4e3aoption dict\u4e2d\u5bf9\u5e94 id \u7684\u503c\n\n- compoundTextarea\n\ncompoundTextarea\u7ec4\u4ef6\u4f1a\u751f\u6210\u4e00\u4e2a\u6587\u672c\u6846\uff0c\u5728\u58f0\u660e\u65f6\u5982\u679c value \u4e0d\u4e3a\u7a7a\uff0c\u5219\u4f1a\u4f5c\u4e3a\u9ed8\u8ba4\u503c\u586b\u5145\u5728\u6587\u672c\u6846\u4e2d\n\n```python\nform_item_text = {\n 'name': '\u63cf\u8ff0',\n 'component': 'compoundTextarea',\n 'value': 'defalut value'\n}\n```\n\n\ncompoundTextarea\u7ec4\u4ef6\u5728 BugIt \u4e2d\u8fd8\u7528\u4e8e\u652f\u6301 Lyrebird \u5176\u4ed6\u4fe1\u606f\u7684\u6269\u5c55\u3002\n\n\u9009\u62e9[\u6d88\u606f\u603b\u7ebf](https://meituan-dianping.github.io/lyrebird/advance/eventbus.html)\u6216\u5176\u4ed6[\u63d2\u4ef6](https://meituan-dianping.github.io/lyrebird/plugins/)\u7684\u6570\u636e\u65f6\uff0c\u8be5\u6570\u636e\u5c06\u4f5c\u4e3a\u9644\u52a0\u4fe1\u606f\u5c55\u793a\u5728 compoundTextarea \u4e2d\u3002\n\n\n\n\u5bf9\u5e94\u7684\u6570\u636e\u4fe1\u606f\u5c06\u4f5c\u4e3a 'extraMsg' \u5b58\u653e\u5728 FormItem\u4e2d\u3002\n\u6b64\u65f6From Item\u53d8\u6210\n\n```python\nform_item_text = {\n 'name': '\u63cf\u8ff0',\n 'component': 'compoundTextarea',\n 'value': 'defalut value',\n 'extraMsg':[\n {'message':'Flow Info'},\n {'message':'Notice Info'},\n {'message':'Devices Info'}\n ]\n}\n```\n\n#### form()\u65b9\u6cd5\u793a\u4f8b\u4ee3\u7801\n\n```python\ndef form(context):\n \"\"\"\n BugIt callback function\n\n BugIt will call this function when user select this template from UI.\n This function should return a array, list all FormItem dict.\n\n \"\"\"\n form_item_input = {\n 'name': '\u4e3b\u9898',\n 'component': 'input',\n 'value': 'defalut value'\n }\n form_item_select = {\n 'name': '\u7248\u672c',\n 'component': 'select',\n 'options':[\n {'id':'001','name':'option_1'},\n {'id':'002','name':'option_2'}\n ],\n 'value': '001'\n }\n form_item_text = {\n 'name': '\u63cf\u8ff0',\n 'component': 'compoundTextarea',\n 'value': 'defalut value'\n }\n form = [form_item_input,form_item_select,form_item_text]\n return form\n```\n\u793a\u4f8b\u4ee3\u7801\u751f\u6210\u7684 Bug \u8868\u5355\u5982\u4e0b\u56fe\u6240\u793a\n\n\n>form()\u65b9\u6cd5\u5165\u53c2 context \uff0c\u53ef\u7528\u4e8e\u4f20\u9012\u7f13\u5b58\u6570\u636e\uff0c\u76f8\u5173\u7528\u6cd5\u8be6\u89c1[\u811a\u672c\u9ad8\u7ea7](https://meituan-dianping.github.io/lyrebird/plugins/bugit.html)\n\n\n### submit()\n\n\u63d0\u4ea4\u51fd\u6570\u8fd4\u56de\u4e00\u4e2a\u6570\u7ec4\uff0c\u6570\u7ec4\u4e2d\u5305\u542b\u82e5\u5e72\u63d0\u4ea4\u6b65\u9aa4\u9700\u8981\u56de\u8c03\u7684\u51fd\u6570\u3002\n\n>submit()\u65b9\u6cd5\u8fd4\u56de\u4e00\u7ec4\u7528\u4e8e\u5904\u7406 submit \u884c\u4e3a\u7684\u65b9\u6cd5\u3002\n\n\u65b9\u6cd5\u8bf4\u660e\n\u65b9\u6cd5\u540d|\u5165\u53c2|\u8bf4\u660e\n:--:|:--:|:--\nissue|context|\u5904\u7406 Bug\u8868\u5355\u4e2d\u7684\u4fe1\u606f\uff0c\u901a\u8fc7 API \u5411 Bug \u7ba1\u7406\u7cfb\u7edf\u63d0\u4ea4 Issue\nattachments|context|\u5904\u7406\u9644\u4ef6\u4fe1\u606f\uff08\u5982 [Android\u63d2\u4ef6](https://meituan-dianping.github.io/lyrebird/guide/plugin.html#android\u63d2\u4ef6)\u3001[iOS\u63d2\u4ef6](https://meituan-dianping.github.io/lyrebird/guide/plugin.html#ios\u63d2\u4ef6)\u63d0\u4f9b\u7684\u622a\u56fe\u6216 Log\uff09\uff0c\u901a\u8fc7 API \u5411 \u521b\u5efa\u7684 Issue \u4e2d\u6dfb\u52a0\u9644\u4ef6\n\n#### submit()\u65b9\u6cd5\u793a\u4f8b\u4ee3\u7801\uff08\u4ee5JIRA\u670d\u52a1\u4e3a\u4f8b\uff09\n```python\ndef submit():\n \"\"\"\n BugIt callback function\n\n BugIt will call this function when user tap submit button.\n This function should return a array, contains all submit actions.\n\n Each submit-action function have a argument. It contains form data and attachments info.\n \"\"\"\n return [issue, attachments]\n\ndef issue(context):\n \"\"\"\n This function is used to submit Issue \n \"\"\"\n form_data = context['issue']\n jira_fields = {}\n\n # transform from_data to payload submited to API of JIRA\n for form_item in form_data:\n if form_item['name'] == '\u4e3b\u9898' :\n jira_fields['summary'] = form_item['value']\n elif form_item['name'] == '\u7248\u672c':\n jira_fields['version'] = {\n id:form_item['value']\n }\n elif form_item['name'] == '\u63cf\u8ff0':\n jira_fields['description'] = form_item['value']\n # add extraMsg to description \n if form_item['extraMsg']:\n for add_des in form_item['extraMsg']:\n jira_fields['description'] +='\\n'\n jira_fields['description'] +='------------------------------\\n'\n jira_fields['description'] +=add_des['message']\n\n url = 'http://www.example.com/jira/rest/api/2/issue'\n header = {\n 'Content-Type': 'application/json;charset=utf-8'\n }\n resp = requests.post(url, auth=('YOUR JIRA_USER_NAME', 'YOUR_JIRA_PASSWD'),json={\"fields\": jira_fields},headers=header)\n \n if resp.status_code >= 200 and resp.status_code < 300:\n body = json.loads(response.text)\n if body.get('key') :\n context['key'] = body['key']\n else:\n raise Exception(f'Submit failed {response.text}')\n else:\n raise Exception(f'Create issue failed with code {response.status_code}\\n{response.text}')\n\n\ndef attachments(context):\n \"\"\"\n This function is used to upload attachments to the Issue which has been created\n \"\"\"\n key = context['key']\n attachments = context['attachments']\n\n if len(attachments) == 0:\n # No attachments\n return\n\n url = f'http://www.example.com/jira/rest/api/2/issue/{key}/attachments'\n headers = {\n 'X-Atlassian-Token': 'nocheck'\n }\n multiple_files = []\n\n # transform attachment data to payload submited to API of JIRA\n for attachment in attachments:\n attachment_path = Path(attachment['path'])\n multiple_files.append(\n ('file', (attachment_path.name, open(str(attachment_path), 'rb')))\n )\n response = requests.post(url, files=multiple_files, headers=headers)\n\n if response.status_code == 200 and response.json()['code'] == 0:\n print('Submit attachments success')\n else:\n raise Exception(f'Submit failed {response.text}')\n```\n\n\u5b8c\u6574\u811a\u672c\u8bf7\u53c2\u8003: [\u793a\u4f8b\u811a\u672c](example/TEST.py)\n\n\u811a\u672c\u9ad8\u7ea7\u529f\u80fd\u8bf7\u53c2\u8003:[\u811a\u672c\u9ad8\u7ea7](https://meituan-dianping.github.io/lyrebird/plugins/bugit.html)\n\n----\n# \u5f00\u53d1\u8005\u6307\u5357\n\n## \u5f00\u53d1\u73af\u5883\n- macOS OR Linux\n- Python3\n- NodeJS\n- vscode(\u63a8\u8350)\n- Chrome(\u63a8\u8350)\n\n## \u8c03\u8bd5\u4ee3\u7801\n\n### Vscode debug\u914d\u7f6e\n```json\n{\n \"version\": \"0.2.0\",\n \"configurations\": [\n {\n \"name\": \"Python: Terminal (integrated)\",\n \"type\": \"python\",\n \"request\": \"launch\",\n \"program\": \"${file}\",\n \"console\": \"integratedTerminal\"\n },\n {\n \"name\": \"Server\",\n \"type\": \"python\",\n \"request\": \"launch\",\n \"module\": \"lyrebird\",\n \"args\": [\n \"-b\",\n \"-vvv\",\n \"--plugin\",\n \"${workspaceFolder}\"\n ],\n \"console\": \"integratedTerminal\"\n },\n {\n \"name\": \"Client\",\n \"type\": \"chrome\",\n \"request\": \"launch\",\n \"url\": \"http://localhost:8080/\",\n \"webRoot\": \"${workspaceFolder}/frontend/src/\",\n \"sourceMapPathOverrides\": {\n \"webpack:///src/*\": \"${webRoot}/*\"\n },\n \"timeout\": 30000\n }\n ]\n}\n```\n### \u540e\u7aef\u4ee3\u7801\n1. \u6fc0\u6d3bpython\u865a\u62df\u73af\u5883\n\n \u901a\u8fc7 source venv/bin/activate \u6765\u6fc0\u6d3b\u8be5\u73af\u5883\n2. \u901a\u8fc7Debug\u529f\u80fd\u542f\u52a8\n \n \u6309\u7167\u4e0a\u9762 debug\u914d\u7f6e\u4e2d python:Lyrebrid\u914d\u7f6e\u542f\u52a8\u5373\u53ef\n### \u524d\u7aef\u4ee3\u7801\n1. \u542f\u52a8node server\n\n```\n# \u8fdb\u5165\u524d\u7aef\u76ee\u5f55\ncd frontend\n\n# \u542f\u52a8\u524d\u7aefnode serve\nnpm run serve\n```\n\n2. \u901a\u8fc7Debug\u529f\u80fd\u542f\u52a8\u6d4f\u89c8\u5668\n\n \u6309\u7167\u4e0a\u9762 debug\u914d\u7f6e\u4e2d vuejs: chrome \u914d\u7f6e\u542f\u52a8\u5373\u53ef\n >\u6ce8\u610f: vscode \u9700\u8981\u5b89\u88c5chrome debug\u63d2\u4ef6",
"bugtrack_url": null,
"license": null,
"summary": null,
"version": "1.16.1",
"project_urls": {
"Homepage": "https://github.com/Meituan-Dianping/lyrebird-bugit"
},
"split_keywords": [],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "4bc6e2b04de22244e78b372bc82d4ddeb69189185569f499fd35cca12f3b2be3",
"md5": "85aeae429f96c6b6a187fc77a4535e2d",
"sha256": "702227bce222230879379a38d025b33b7d3ce8c21afd530f4e74863e7938a78e"
},
"downloads": -1,
"filename": "lyrebird-bugit-1.16.1.tar.gz",
"has_sig": false,
"md5_digest": "85aeae429f96c6b6a187fc77a4535e2d",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 3377884,
"upload_time": "2024-03-29T03:44:20",
"upload_time_iso_8601": "2024-03-29T03:44:20.881960Z",
"url": "https://files.pythonhosted.org/packages/4b/c6/e2b04de22244e78b372bc82d4ddeb69189185569f499fd35cca12f3b2be3/lyrebird-bugit-1.16.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-03-29 03:44:20",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "Meituan-Dianping",
"github_project": "lyrebird-bugit",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"requirements": [],
"lcname": "lyrebird-bugit"
}