lyrebird-bugit


Namelyrebird-bugit JSON
Version 1.16.1 PyPI version JSON
download
home_pagehttps://github.com/Meituan-Dianping/lyrebird-bugit
SummaryNone
upload_time2024-03-29 03:44:20
maintainerNone
docs_urlNone
authorHBQA
requires_pythonNone
licenseNone
keywords
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            <h1 align="center">Lyrebird - BugIt plugin</h1>

[![Unit Test](https://github.com/Meituan-Dianping/lyrebird-bugit/actions/workflows/unittest.yml/badge.svg)](https://github.com/Meituan-Dianping/lyrebird-bugit/actions/workflows/unittest.yml)
[![Publish to pypi](https://github.com/Meituan-Dianping/lyrebird-bugit/actions/workflows/publish.yml/badge.svg)](https://github.com/Meituan-Dianping/lyrebird-bugit/actions/workflows/publish.yml)
[![CodeQL](https://github.com/Meituan-Dianping/lyrebird-bugit/actions/workflows/codeql.yml/badge.svg)](https://github.com/Meituan-Dianping/lyrebird-bugit/actions/workflows/codeql.yml)

[![PyPI](https://img.shields.io/pypi/v/lyrebird-bugit.svg)](https://pypi.python.org/pypi/lyrebird-bugit)
![PyPI](https://img.shields.io/pypi/pyversions/lyrebird-bugit.svg)
![GitHub](https://img.shields.io/github/license/meituan-dianping/lyrebird-bugit.svg)

**[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字段的 名称、先后顺序、填写样式、默认值。

![Bug表单](./image/bugit_bug.png)

BugIt可以通过配置服务向任意Bug管理系统提交Issue

## API数据获取
BugIt支持自动填充Lyrebird运行过程中抓取到的数据信息。

![BugIt 获取API信息](./image/bugit_api.gif)

## 报警获取

在[检查器](https://meituan-dianping.github.io/lyrebird/guide/checker.html#载入检查器)([Checker](https://meituan-dianping.github.io/lyrebird/guide/checker.html#载入检查器))捕获报警后,可以通过通知中心随时跳转至BugIt。

在右侧数据面板中,可以对历史[消息总线](/advance/eventbus.md)中的信息进行回溯,补充至Bug中。

![BugIt获取报警信息](./image/bugit_alert.gif)

此外,还支持针对指定检查器的报警进行自动开Bug。

通过配置可以指定检查器,BugIt会将这些检查器的报警信息送入配置指定的脚本内,在脚本内自定义对报警信息的处理,即可实现Bug的自动上报功能。

## 缓存功能

按下[Commond]+[s]键,会将Bug相关字段信息进行存储。

缓存信息不受Lyrebird服务开关、浏览器缓存、脚本/界面切换的影响。

![BugIt缓存功能](./image/bugit_cache.gif)

有效的利用缓存功能,是提高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中的应用](./image/bugit_devices.gif)

## BugIt脚本

在 ~/.lyrebird/conf.json 中,BugIt会读取 “bugit.workspace”字段。并将此字段值作为读取模板的根目录。

该目录下所有模板都会加载到BugIt中,并在UI中可选。

刷新BugIt界面即可重新加载所有模板。

### 模板使用说明

BugIt通过模板定义UI以及提交的行为。通过不同的模板支持JIRA以及其他的Bug管理系统。

BugIt模板是一个Python文件,要求使用Python3.7及以上的版本编写。
![BugIt 脚本工作原理示意图](./image/bugit_callback.png)
配置脚本需包含以下三部分
- init配置文件
- form()
- submit()

### init配置文件

BugIt 通过 name 来标识配置文件。

>注意:BugIt 脚本必须含有 'name' 属性


```python
"""
Template name
"""        
name = 'TEST-JIRA'
```
name 用于在 BugIt 前端页面上展示脚本名称。

![name](./image/bugit_name.png)

选中配置文件后,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组件](./image/bugit_input.png)

input 组件会生成一个文本输入框,在声明时如果 value 不为空,则会作为默认值填充在输入框中

- select

```python
form_item_select = {
    'name': '版本',
    'component': 'select',
    'options':[
        {'id':'001','name':'option_1'},
        {'id':'002','name':'option_2'}
    ],
    'value': '001'
}
```

![select组件](./image/bugit_select.png)

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组件](./image/bugit_text.png)

compoundTextarea组件在 BugIt 中还用于支持 Lyrebird 其他信息的扩展。

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

![附加信息](./image/bugit_text_extra.gif)

对应的数据信息将作为 '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 表单如下图所示
![测试脚本表单生成](./image/bugit_form.png)

>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[![Unit Test](https://github.com/Meituan-Dianping/lyrebird-bugit/actions/workflows/unittest.yml/badge.svg)](https://github.com/Meituan-Dianping/lyrebird-bugit/actions/workflows/unittest.yml)\n[![Publish to pypi](https://github.com/Meituan-Dianping/lyrebird-bugit/actions/workflows/publish.yml/badge.svg)](https://github.com/Meituan-Dianping/lyrebird-bugit/actions/workflows/publish.yml)\n[![CodeQL](https://github.com/Meituan-Dianping/lyrebird-bugit/actions/workflows/codeql.yml/badge.svg)](https://github.com/Meituan-Dianping/lyrebird-bugit/actions/workflows/codeql.yml)\n\n[![PyPI](https://img.shields.io/pypi/v/lyrebird-bugit.svg)](https://pypi.python.org/pypi/lyrebird-bugit)\n![PyPI](https://img.shields.io/pypi/pyversions/lyrebird-bugit.svg)\n![GitHub](https://img.shields.io/github/license/meituan-dianping/lyrebird-bugit.svg)\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![\bBug\u8868\u5355](./image/bugit_bug.png)\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![BugIt \u83b7\u53d6API\u4fe1\u606f](./image/bugit_api.gif)\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![BugIt\u83b7\u53d6\u62a5\u8b66\u4fe1\u606f](./image/bugit_alert.gif)\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![BugIt\u7f13\u5b58\u529f\u80fd](./image/bugit_cache.gif)\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![\u5176\u4ed6\u63d2\u4ef6\u5728BugIt\u4e2d\u7684\u5e94\u7528](./image/bugit_devices.gif)\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![BugIt \u811a\u672c\u5de5\u4f5c\u539f\u7406\u793a\u610f\u56fe](./image/bugit_callback.png)\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![name](./image/bugit_name.png)\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![input\u7ec4\u4ef6](./image/bugit_input.png)\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![select\u7ec4\u4ef6](./image/bugit_select.png)\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![compoundTextarea\u7ec4\u4ef6](./image/bugit_text.png)\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![\u9644\u52a0\u4fe1\u606f](./image/bugit_text_extra.gif)\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![\u6d4b\u8bd5\u811a\u672c\u8868\u5355\u751f\u6210](./image/bugit_form.png)\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"
}
        
Elapsed time: 0.19828s