目录
- 一、前言
- 二、系统环境
- 三、工作目标
- 四、流水线设置
- 五、开发工具
- 5.1 教程地址
- 5.2 开发工具程序结构
- 5.3 qciplugin.yml文件
- 5.4 main.py文件
- 六、插件的安装
- 6.1 打包成zip
- 6.2 上传zip包
- 6.3 构建新插件
- 6.4 质量门禁
- 7、流水线设置
- 7.1 添加质量管理阶段节点
- 7.2 添加其它动作
- 八、流水线运行效果
- 8.1 质量门禁信息面板
- 8.2 用户workspace
- 8.3 查看通用报告
- 8.4 result.json的生成路径问题
一、前言
本文主要内容是在腾讯Coding平台内使用自定义团队插件方法验证用户自己搭建代码扫描工具的可行性,作为Coding平台内置扫描能力的补充。该课题的起因是Coding平台的C#语言代码扫描能力较弱。因为目前没有找到合适的c#扫描工具,暂时用阿里的p3c做一下验证。
二、系统环境
操作系统:Windows10
Coding:高级版
测试项目为:maven项目
代码扫描插件:ali-p3c(下载地址)
开发环境:python3.7
三、工作目标
- 在Coding平台接入第三方代码扫描插件
- 在CI流水线部署该工具
- 生成可用的测试结果报告
四、流水线设置
五、开发工具
5.1 教程地址
Coding教程地址:https://coding.net/help/docs/ci/plugins/customize/overview.html
示例插件:https://e.coding.net/coding-public/cci/ci-plugin-demo.git
5.2 开发工具程序结构
- my-plugin-project // 您的项目目录
- my-script.xx // 构建插件执行脚本或入口文件,支持任意语言组织(需执行环境具备,如需特殊环境可使用容器)
- qciplugin.yml // 构建插件声明文件,定义您的构建插件名称、版本、参数等信息
这里的my-script.xx文件就是你自己的入口文件,比如python你可以写成run.py
5.3 qciplugin.yml文件
插件文件需读取声明文件 qciplugin.yml,这个文件特别重要
- version版本号不能空必须填写。
- id不能重名,而且如果你之前导入过又删除了,这个名字也不能再用了。
- name是以后在coding平台里看到的名字,请按便于使用的原则命名。
- metrics是腾讯指导老师帮忙写的格式,直接套用。
# 插件版本, 用于定义插件包的版本, 做版本管理使用
version: '2.0'
# 插件ID
id: java_quality_p3c_rule_test8
# 插件中文名称
name: Java阿里开发规范插件
# 插件描述
description: Java阿里开发规范插件
# 插件分类
category: test
# 声明插件使用的参数
variables:
[]
# 执行入口配置, 声明如何运行插件脚本
entry:
# 插件启动入口
start: $QCI_PLUGIN_EXECUTABLE $QCI_PLUGIN_RUNTIME/code_check_0223/main.py
# 插件状态文件
status: $QCI_PLUGIN_RUNTIME/status.json
metrics: # 声明将会上报的元数据
- key: 'total_warn' # 元数据 key 值
label: '警告数量' # 元数据 label 值, 必填,需有可读性
desc: '' # 元数据描述信息, 非必填
value_type: NUMBER # 元数据的数据类型,必填,质量门禁暂只支持 NUMBER
group: "当前分支问题量" # 指标分组,非必填
indicator:
available_op: 'LT,LE,EQ,GE,GT' # 指标支持的操作符,必填,可选操作LT,LE,EQ,GE,GT,多个操作以英文逗号分隔
default_op: 'LE' # 指标默认操作符, 非必填
default_threshold: 50 # 指标默认阈值,非必填
- key: 'total_error' # 元数据 key 值
label: '错误数量' # 元数据 label 值, 必填,需有可读性
desc: '' # 元数据描述信息, 非必填
value_type: NUMBER # 元数据的数据类型,必填,质量门禁暂只支持 NUMBER
group: "当前分支问题量" # 指标分组,非必填
indicator:
available_op: 'LT,LE,EQ,GE,GT' # 指标支持的操作符,必填,可选操作LT,LE,EQ,GE,GT,多个操作以英文逗号分隔
default_op: 'LE' # 指标默认操作符, 非必填
default_threshold: 10 # 指标默认阈值,非必填
- key: 'count_warn' # 元数据 key 值
label: '提示' # 元数据 label 值, 必填,需有可读性
desc: '' # 元数据描述信息, 非必填
value_type: NUMBER # 元数据的数据类型,必填,质量门禁暂只支持 NUMBER
group: "本次扫描问题量" # 指标分组,非必填
indicator:
available_op: 'LT,LE,EQ,GE,GT' # 指标支持的操作符,必填,可选操作LT,LE,EQ,GE,GT,多个操作以英文逗号分隔
default_op: 'LE' # 指标默认操作符, 非必填
default_threshold: 40 # 指标默认阈值,非必填
- key: 'count_error' # 元数据 key 值
label: '致命错误' # 元数据 label 值, 必填,需有可读性
desc: '' # 元数据描述信息, 非必填
value_type: NUMBER # 元数据的数据类型,必填,质量门禁暂只支持 NUMBER
group: "本次扫描问题量" # 指标分组,非必填
indicator:
available_op: 'LT,LE,EQ,GE,GT' # 指标支持的操作符,必填,可选操作LT,LE,EQ,GE,GT,多个操作以英文逗号分隔
default_op: 'LE' # 指标默认操作符, 非必填
default_threshold: 3 # 指标默认阈值,非必填
5.4 main.py文件
主程序入口文件,该文件在qciplugin.yml文件里有过声明,启动的时候就会来这里找入口开始运行
test_c.sh就是程序执行脚本,扫描结果收集到result对象里。
# 规则1: 对文件就行判断是否 符合阿里p3c - 缺少包含@author的注释信息
print("当前执行的目录是:",file_path)
scan_files_result = os.popen("cd {} && cd code_check_0223/pmd_p3c_resource && sh test_c.sh {}".format(path, file_path)).readlines()
print("当前的结果是:",scan_files_result)
# 收集规则1扫描结果:
temp_author_data_list = check_java_obj.class_must_have_author_rule(listvar=scan_files_result, file_path=file_path)
author_data_list += temp_author_data_list
# .... ... ...
# 规则n, 其他的规则
result = [
# 2, 获取符合阿里p3c - 缺少包含@author的注释信息 处理结果
*author_data_list
]
print("当前执行阿里p3c result.json中的值:", result)
# 备份详细的扫描结构
with open(os.path.join(path, 'result.json'), "w") as fp:
json.dump(result, fp, indent=2, ensure_ascii=False)
六、插件的安装
6.1 打包成zip
6.2 上传zip包
6.3 构建新插件
6.4 质量门禁
在质量门禁里添加刚才上传的插件,管控流水线必须要设置,不然后面看不到质量门禁输出信息。
7、流水线设置
7.1 添加质量管理阶段节点
7.2 添加其它动作
添加shell脚本只是为了查看一下路径和文件是否存在(非必须)。
添加收集通用报告比较有必要,因为如果不收集报告的话检查结果只显示在日志中,不便使用。
八、流水线运行效果
8.1 质量门禁信息面板
8.2 用户workspace
result.json放在固定目录下便于形成通用报告
8.3 查看通用报告
8.4 result.json的生成路径问题
result.json默认是根据main.py里面的环境变量"QCI_PLUGIN_RUNTIME"生成路径,生成的路径是在tmp下的一个随机路径(机制不明),我们每次生成的时候将该文件copy一份到固定目录,这样就便于获取并生成通用报告了。
在python里添加下面这段代码,将result复制到workspace路径。
# 复制详细扫描结果作为通用报告
src_dir = path + '/result.json'
dst_dir = '/root/workspace/'
print("这里是result文件地址:"+src_dir)
shutil.copy(src_dir, dst_dir + 'result.json') # 复制文件
print ("copy %s -> %s"%(src_dir, dst_dir + 'result.json'))