基本思路:
每一行代码都有作者, 错误的代码更应该有作者。
本文的思路是, 通过火线静态代码扫描程序扫描后的 json 报告, 直接定位到人,也就是错误代码的引入者。
责任到人,可以更好地做到错误代码发现及时, 修复更及时。
准备条件:
火线静态代码扫描程序
1). 下载火线 jar 包 http://magic.360.cn/index.html
2). 编写配置文件 RedLineConfigurationNoLog.xml http://magic.360.cn/user.html#args
代码扫描服务器 - Linux CentOS 6 or 7
1). 安装 JDK
2). 准备预扫描项目的 git 目录,例如:/data/git/test
3). 发布 WEB 服务的文件夹, 用于发布火线报告 HTML 版, 例如: /data/webtest/fireline
3). jq 用于解析火线 json 报告的内容,
CentOS 7:yum install -y jq,
CentOS 6:需要自行下载安装,https://stedolan.github.io/jq/
脚本执行步骤:
一、火线静态代码扫描
二、定位错误代码作者
脚本内容(/data/fireline/fireline.sh):
#! /bin/sh
# Author : binwei.liu
# Usage: 2017-09-02 火线静态代码扫描以及定位错误代码作者
#########################################
# 一、火线静态代码扫描
#########################################
# 1. git pull 更新扫描服务器上的代码
# 2. 执行火线静态代码扫描命令
# 3. 发布火线扫描报告html
#########################################
# 二、定位错误代码作者
#########################################
# 4. 检查火线扫描报告json文件内block和error条目的数量
# 5. 根据错误代码的文件名和出错行数查找错误代码作者
#########################################
PS4='+[$LINENO]'
RUN_TIME=$( date +'%Y%m%dT%H%M%S' )
# 设置扫描项目名称
# FIRELINE_PROJ_NAME=${1}
FIRELINE_PROJ_NAME='test'
# 设置已经准备好的项目git目录
# FIRELINE_SCAN_SRC_DIR=${2}
FIRELINE_SCAN_SRC_DIR='/data/git/test'
# 火线报告临时存放文件夹
FIRELINE_REPORT_SAVE_DIR="/tmp/report_${FIRELINE_PROJ_NAME}_${RUN_TIME}"
# 火线报告文件名
FIRELINE_REPORT_FILE_NAME="index_${FIRELINE_PROJ_NAME}"
# 火线报告提交人名称
FIRELINE_USER='binwei.liu'
# 火线程序jar包
FIRELINE_JAR='/data/fireline/fireline.jar'
# 火线程序配置文件路径
FIRELINE_CONFIG='/data/fireline/RedLineConfigurationNoLog.xml'
# 扫描服务器上,web服务发布的文件夹
PUBLIC_REPORT_DIR='/data/webtest/fireline'
# 火线报告内的json文件
FIRELINE_REPORT_JSON_FILE="/data/webtest/fireline/${FIRELINE_REPORT_FILE_NAME}.json"
# web报告发布后的查看地址
PUBLIC_REPORT_URL="http://www.17test.net/fireline/${FIRELINE_REPORT_FILE_NAME}.html"
# json报告里包含block条目的数量
JSON_BLOCK_ITEM_COUNT=0
# json报告里包含error条目的数量
JSON_ERROR_ITEM_COUNT=0
# 日志屏幕显示或者记录到指定文件
function log () {
# echo -e `date +'%Y-%m-%d %H:%M:%S'` "${*}" | tee -a ${LOG_FILE}
# echo -e "${*}" | tee -a ${LOG_FILE}
echo -e "${*}"
}
function init () {
log "##################################################"
log "报告链接: ${PUBLIC_REPORT_URL}"
log "规则文档: http://magic.360.cn/document.html"
log "##################################################"
# 建立报告临时存放文件夹
log "info: create new tmp report folder ${FIRELINE_REPORT_SAVE_DIR}"
mkdir -p ${FIRELINE_REPORT_SAVE_DIR}
}
function finalize () {
log "${2}"
exit ${1}
}
function update_local_code () {
log "info: update the local code to the latest."
cd ${FIRELINE_SCAN_SRC_DIR}
git reset --hard origin/master && \
git gc && \
git pull && \
git submodule init && \
git submodule update && \
git submodule foreach git checkout master && \
git submodule foreach git pull
if [[ $? -ne 0 ]]; then
finalize 1 "error: update the local code failed."
fi
}
function fireline_scan () {
log "info: fireline starts to scan the project of <${FIRELINE_PROJ_NAME}>"
java -jar ${FIRELINE_JAR} \
-Df ile.encoding=UTF-8 \
scanSrcDir=${FIRELINE_SCAN_SRC_DIR} \
reportSaveDir=${FIRELINE_REPORT_SAVE_DIR} \
reportFileName=${FIRELINE_REPORT_FILE_NAME} \
proj_name=${FIRELINE_PROJ_NAME} \
user=${FIRELINE_USER} \
config=${FIRELINE_CONFIG} > /dev/null 2&>1
if [[ $? -ne 0 ]]; then
finalize 1 "error: fireline scans project <${FIRELINE_PROJ_NAME}> failed."
else
log "info: fireline scans completely."
fi
}
function public_report_html () {
log "info: public the fireline report."
cp -rf ${FIRELINE_REPORT_SAVE_DIR}/* ${PUBLIC_REPORT_DIR}/
if [[ $? -ne 0 ]]; then
log "error: public the fireline report failed."
fi
}
function find_author_of_error_code () {
local _beginline=${1}
local _endline=${2}
local _filepath=${3}
log "author:"
cd ${FIRELINE_SCAN_SRC_DIR}
git blame -L ${_beginline},${_endline} ${_filepath}
if [[ $? -ne 0 ]]; then
finalize 1 "error: git blame -L ${_beginline},${_endline} ${_filepath} failed."
fi
}
function get_item_count_by_level () {
local _level=${1}
jq ".${_level} | length" ${FIRELINE_REPORT_JSON_FILE}
if [[ $? -ne 0 ]]; then
finalize 1 "error: get the count of <${_level}> level failed."
fi
}
function get_element_value_from_item () {
local _level=${1}
local _no=${2}
local _key=${3}
jq -r ".${_level}[] | select(.no == \"${_no}\") | .${_key}" ${FIRELINE_REPORT_JSON_FILE}
if [[ $? -ne 0 ]]; then
finalize 1 "error: get the key <level:${_level}, no:${2}, key:${_key}> failed."
fi
}
function get_json_item_count () {
JSON_BLOCK_ITEM_COUNT=$( get_item_count_by_level 'block' )
JSON_ERROR_ITEM_COUNT=$( get_item_count_by_level 'error' )
}
function print_items_details () {
local _level=${1}
local _count=${2}
log "info: show the items of <${_level}>"
for ((i=1; i<=${_count}; i++)); do
_no=${i}
_classname=$( get_element_value_from_item ${_level} ${_no} 'classname' )
_filepath=$( get_element_value_from_item ${_level} ${_no} 'filepath' )
_rulename=$( get_element_value_from_item ${_level} ${_no} 'rulename' )
_rule_description=$( get_element_value_from_item ${_level} ${_no} 'ruleDescription' )
_docurl=$( get_element_value_from_item ${_level} ${_no} 'docurl' )
_beginline=$( get_element_value_from_item ${_level} ${_no} 'beginline' )
_endline=$( get_element_value_from_item ${_level} ${_no} 'endline' )
log "---- ${_no} ----"
log "classname: ${_classname}"
log "filepath: ${_filepath}"
log "rulename: ${_rulename}"
log "ruleDescription: ${_rule_description}"
log "docurl: ${_docurl}"
log "beginline: ${_beginline}"
log "endline: ${_endline}"
find_author_of_error_code ${_beginline} ${_endline} ${_filepath}
done
}
function main () {
# 初始化
init
# 1. git pull 更新扫描服务器上的代码
update_local_code
# 2. 执行火线静态代码扫描命令
fireline_scan
# 3. 发布火线扫描报告html
public_report_html
# 4. 检查火线扫描报告json文件内block和error条目的数量
get_json_item_count
# 5. 根据错误代码的文件名和出错行数查找错误代码作者
if [[ ${JSON_BLOCK_ITEM_COUNT}>0 ]]; then
log "fail: there are some code issues about <block> level."
print_items_details 'block'${JSON_BLOCK_ITEM_COUNT}
else
log "info: not found the code issues about <block> level."
fi
if [[ ${JSON_ERROR_ITEM_COUNT}>0 ]]; then
log "fail: there are some code issues about <error> level."
print_items_details 'error' ${JSON_ERROR_ITEM_COUNT}
else
log "info: not found the code issues about <error> level."
fi
}
main
脚本执行结果:
[root@VM-TEST-96 fireline]# bash /data/fireline/fireline.sh
##################################################
报告链接: http://www.17test.net/fireline/index_test.html
规则文档: http://magic.360.cn/document.html
##################################################
info: create new tmp report folder /tmp/report_test_20170904T201126
info: update the local code to the latest.
HEAD is now at e096a44 edit errot01 02 03
Counting objects: 15, done.
Compressing objects: 100% (6/6), done.
Writing objects: 100% (15/15), done.
Total 15 (delta 2), reused 15 (delta 2)
Already up-to-date.
info: fireline starts to scan the project of <test>
info: fireline scans completely.
info: public the fireline report.
info: not found the code issues about <block> level.
fail: there are some code issues about <error> level.
info: show the items of <error>
---- 1 ----
classname: TestFireline
filepath: /data/git/test/src/test/TestFireline.java
rulename: 错位的空判断
ruleDescription: 这里的空检查是放错位置的。如果变量为空你将得到一个空指针异常。可能因为检查是无用的或者是不正确的。如:if (!string.equals("") && string!=null){}。
docurl: http://magic.360.cn/document.html#CodeStyle-11
beginline: 53
endline: 53
author:
013d7a2d (binwei.liu 2017-09-04 15:39:29 +0800 53) if (a.equals(baz) && a != null) {}
---- 2 ----
classname: TestFireline
filepath: /data/git/test/src/test/TestFireline.java
rulename: 破坏空判断
ruleDescription: 如果自身抛出空指针异常空检查就会遭到破坏,比如你使用 代替 &&,反之亦然,如(应是&&):if (string!=null !string.equals("")){}。
docurl: http://magic.360.cn/document.html#CodeStyle-13
beginline: 60
endline: 62
author:
e096a440 (binwei.liu 2017-09-04 16:16:25 +0800 60) if (string!=null || !string.equals("")) {
e096a440 (binwei.liu 2017-09-04 16:16:25 +0800 61) return string;
e096a440 (binwei.liu 2017-09-04 16:16:25 +0800 62) }
---- 3 ----
classname: TestFireline
filepath: /data/git/test/src/test/TestFireline.java
rulename: 破坏空判断
ruleDescription: 如果自身抛出空指针异常空检查就会遭到破坏,比如你使用 代替 &&,反之亦然,如(应是&&):if (string!=null !string.equals("")){}。
docurl: http://magic.360.cn/document.html#CodeStyle-13
beginline: 65
endline: 67
author:
e096a440 (binwei.liu 2017-09-04 16:16:25 +0800 65) if (string==null && string.equals("")) {
e096a440 (binwei.liu 2017-09-04 16:16:25 +0800 66) return string;
e096a440 (binwei.liu 2017-09-04 16:16:25 +0800 67) }
[root@VM-TEST-96 fireline]#
火线 json 报告内容(/data/webtest/fireline/index_test.json):
{
"block": [],
"error": [
{
"no": "1",
"classname": "TestFireline",
"filepath": "/data/git/test/src/test/TestFireline.java",
"rulename": "错位的空判断",
"ruleDescription": "这里的空检查是放错位置的。如果变量为空你将得到一个空指针异常。可能因为检查是无用的或者是不正确的。如:if (!string.equals(\"\") && string!=null){}。",
"docurl": "http://magic.360.cn/document.html#CodeStyle-11",
"beginline": "53",
"endline": "53"
},
{
"no": "2",
"classname": "TestFireline",
"filepath": "/data/git/test/src/test/TestFireline.java",
"rulename": "破坏空判断",
"ruleDescription": "如果自身抛出空指针异常空检查就会遭到破坏,比如你使用 代替 &&,反之亦然,如(应是&&):if (string!=null !string.equals(\"\")){}。",
"docurl": "http://magic.360.cn/document.html#CodeStyle-13",
"beginline": "60",
"endline": "62"
},
{
"no": "3",
"classname": "TestFireline",
"filepath": "/data/git/test/src/test/TestFireline.java",
"rulename": "破坏空判断",
"ruleDescription": "如果自身抛出空指针异常空检查就会遭到破坏,比如你使用 代替 &&,反之亦然,如(应是&&):if (string!=null !string.equals(\"\")){}。",
"docurl": "http://magic.360.cn/document.html#CodeStyle-13",
"beginline": "65",
"endline": "67"
}
]
}
鸣谢: @oggboy (丁老九), 完善了火线 json 报告,使代码扫描和定位责任人两部分结合起来, 实现完整的自动化。