OCLint + Infer +Jenkins + SonarQube 搭建iOS代碼靜態(tài)分析系統(tǒng)

背景

隨著代碼量的日益增加锦积,以及團(tuán)隊(duì)的擴(kuò)大表谊,我們往往因?yàn)樾枨笮枰M快上線以及快速迭代,導(dǎo)致代碼并不是很規(guī)范,時(shí)間長(zhǎng)了就留下了一堆技術(shù)債凿渊,代碼的質(zhì)量也沒(méi)有了保證。所以開(kāi)始嘗試一些代碼質(zhì)量相關(guān)建設(shè)册舞,希望能夠通過(guò)代碼靜態(tài)掃描的方式播演,幫助我們掃描出一些代碼漏洞,然后嘗試去修復(fù)漏洞和bug贯底,以此來(lái)保證代碼質(zhì)量丰辣。

工具與平臺(tái)

本文涉及的工具及平臺(tái):

開(kāi)源方案介紹及配置

大部分iOS平臺(tái)代碼靜態(tài)分析基本是基于開(kāi)源的oclint或者infer進(jìn)行的,本文雖然使用sonarqube禽捆,但免費(fèi)的分析方案核心仍然是oclintinfer

sonarqube是一個(gè)開(kāi)源的靜態(tài)代碼分析平臺(tái)笙什,提供免費(fèi)的社區(qū)版,免費(fèi)的社區(qū)版不支持Objective-C胚想,但github有提供開(kāi)源的插件琐凭,Objective-C,付費(fèi)的社區(qū)版plus有支持Objective-C的分析插件SonarCFamily for C

無(wú)論免費(fèi)方案還是付費(fèi)方案浊服,首先都是基于xcodebuid過(guò)程中的日志來(lái)進(jìn)行的统屈,我們這次主要針對(duì)開(kāi)源方案來(lái)講相關(guān)配置流程。

對(duì)于使用開(kāi)源方案來(lái)說(shuō)牙躺,本質(zhì)流程如下:


image

xcodebuild

iOS核心工具愁憔,安裝好Xcode就會(huì)自帶此工具,因?yàn)?code>oclint分析的核心是xcodebuild在編譯app過(guò)程中的log孽拷,所以需要xcodebuild(build失敗也會(huì)對(duì)已經(jīng)build的日志進(jìn)行分析吨掌,但盡量保證可以build成功)

如果項(xiàng)目是在workspace中需要指定-workspace和對(duì)應(yīng)的scheme,可以通過(guò)xcodebuild -list查看

//執(zhí)行
$ xcodebuild -list
image
//執(zhí)行
$ xcodebuild -workspace CsdnPlus.xcworkspace -scheme CsdnPlus
image

我們并不需要打出的IPA包可以安裝到手機(jī)上脓恕,只是需要build過(guò)程中的日志而已膜宋,所以我們只需要打出模擬器下Debug包就可以了。

//執(zhí)行
$ xcodebuild -showsdks
image

因?yàn)?code>xcodebuild會(huì)有緩存炼幔,所以我們每次執(zhí)行前需要clean

$ xcodebuild -workspace CsdnPlus.xcworkspace -scheme CsdnPlus clean
image

執(zhí)行build編譯

$ xcodebuild -scheme CsdnPlus -workspace CsdnPlus.xcworkspace -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 12 Pro Max' -configuration Debug
image

image

能編譯成功的話(huà)就可以進(jìn)入下一步了

xcpretty

xcpretty is a fast and flexible formatter for xcodebuild.
It does one thing, and it should do it well.

xcpretty是一個(gè)格式化xcodebuild輸出的工具激蹲。安裝:

$ gem install xcpretty
image

-r, --report指定生成的報(bào)告格式可選為junit, html, json-compilation-database

-o, --output指定生成的文件名稱(chēng)江掩。
這里我們使用json-compilation-database格式学辱,輸出文件名為compile_commands.json
(注意輸出名稱(chēng)不能更改,否則后面oclint會(huì)報(bào)錯(cuò)环形,因?yàn)閛clint源碼中內(nèi)置了校驗(yàn)名稱(chēng)策泣,具體可查看源碼

用法:

緊跟在xcodebuild相關(guān)語(yǔ)句后面,比如:

$ xcodebuild [flags] | xcpretty

可以結(jié)合tee進(jìn)行日志收集

$ xcodebuild [flags] | tee xcodebuild.log | xcpretty

執(zhí)行完整命令生成編譯數(shù)據(jù)compile_commands.json文件:

首先需要用xcodebuild cleanbuild項(xiàng)目抬吟,并且添加COMPILER_INDEX_STORE_ENABLE=NO參數(shù)萨咕,不然可能會(huì)出現(xiàn)報(bào)錯(cuò):oclint: error: one compiler command contains multiple jobs報(bào)錯(cuò)

$ xcodebuild -scheme CsdnPlus -workspace CsdnPlus.xcworkspace -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 12 Pro Max' -configuration Debug GCC_PRECOMPILE_PREFIX_HEADER=YES CLANG_ENABLE_MODULE_DEBUGGING=NO COMPILER_INDEX_STORE_ENABLE=NO OTHER_CFLAGS="-DNS_FORMAT_ARGUMENT(A)= -D_Nullable_result=_Nullable" | tee xcodebuild.log | xcpretty -r json-compilation-database -o compile_commands.json

OCLint

OCLint is a static code analysis tool for improving quality and reducing defects by inspecting C, C++ and Objective-C code

OCLint 是基于 Clang Tooling 開(kāi)發(fā)的靜態(tài)分析工具,主要用來(lái)發(fā)現(xiàn)編譯器檢查不到的那些潛在的關(guān)鍵技術(shù)問(wèn)題。是進(jìn)行OC代碼分析的核心工具火本,主要對(duì)上一步生成的compile_commands.json進(jìn)行分析危队,生成報(bào)告

命令安裝:

$ brew tap oclint/formulae
$ brew install oclint

我建議使用安裝包來(lái)安裝OCLint,Homebrew 安裝只能安裝到20.11版本聪建,最新Xcode 版本對(duì)應(yīng)的是22.02。如果安裝版本不符合茫陆,OClint分析出來(lái)只有一堆compiler error金麸。

image

下載安裝包安裝:

https://github.com/oclint/oclint/releases

配置環(huán)境變量:

export PATH="/Users/csdn/oclint-22.02/bin:$PATH"
source ~/.zshrc

在終端輸入 oclint --version,驗(yàn)證是否安裝成功簿盅。

image

在終端輸入oclint --help 查看命令介紹
image

其中我們主要使用oclint-json-compilation-database命令挥下,Github源碼

oclint-json-compilation-database命令支持指定校驗(yàn)文件夾和過(guò)濾指定文件夾,本質(zhì)上最終執(zhí)行oclint -p命令桨醋,可以通過(guò)附加-v查看棚瘟,同時(shí)還支持使用--后面跟上oclint執(zhí)行參數(shù)。
例如:

// 此處--符號(hào)后的參數(shù)是傳遞給oclint的
$ oclint-json-compilation-database -v -e Pods -e xxxx -- -report-type html -o report.html

oclint-rc選項(xiàng)可以自定義校驗(yàn)的參數(shù)值喜最,例如:

$ oclint-json-compilation-database -v -e Pods -e xxxx -- -rc LONG_METHOD=60 -rc LONG_LINE=100

另外當(dāng)我們需要自定義多個(gè)oclint參數(shù)時(shí)偎蘸,我們可以將配置寫(xiě)在.oclint文件中

disable-rules:                      // 不使用的規(guī)則
   - LongLine
rulePaths:                          // oclint校驗(yàn)規(guī)則所在的路徑,Mac端默認(rèn)在/usr/local/lib/oclint/rules瞬内,如果不需要自定義規(guī)則的話(huà)可以不配置此項(xiàng)
  - /etc/rules
rule-configurations:                // 自定義配置參數(shù)
  - key: CYCLOMATIC_COMPLEXITY
    value: 15
  - key: NPATH_COMPLEXITY
    value: 300
output: oclint.xml                  // 生成的報(bào)告
report-type: xml                    // 生成的報(bào)告格式支持html禀苦、xml、json等
max-priority-1: 20                  // 級(jí)別1的問(wèn)題最大個(gè)數(shù)遂鹊,如果檢測(cè)出的問(wèn)題超過(guò)這個(gè)個(gè)數(shù)就會(huì)自動(dòng)終止
max-priority-2: 40                  // 級(jí)別2的問(wèn)題最大個(gè)數(shù)
max-priority-3: 60                  // 級(jí)別3的問(wèn)題最大個(gè)數(shù)
enable-clang-static-analyzer: false //

以下是OCLint內(nèi)置支持的72條Rule振乏,可以通過(guò) --list-enabled-rules x查看

$ oclint --list-enabled-rules x
enabled rules:
- TooManyMethods
- DestructorOfVirtualClass
- DeadCode
- EmptyForStatement
- AvoidDefaultArgumentsOnVirtualMethods
- ProblematicBaseClassDestructor
- MisplacedDefaultLabel
- EmptyFinallyStatement
- CallingProhibitedMethod
- RedundantIfStatement
- CollapsibleIfStatements
- UnnecessaryElseStatement
- ConstantConditionalOperator
- DeepNestedBlock
- AssignIvarOutsideAccessors
- UnnecessaryNullCheckForDealloc
- RedundantNilCheck
- RedundantLocalVariable
- EmptyDoWhileStatement
- UnusedMethodParameter
- BitwiseOperatorInConditional
- ReturnFromFinallyBlock
- MultipleUnaryOperator
- DoubleNegative
- MissingCallToBaseMethod
- EmptyWhileStatement
- ShortVariableName
- ParameterReassignment
- UselessParentheses
- ThrowExceptionFromFinallyBlock
- UnnecessaryDefaultStatement
- HighNcssMethod
- PreferEarlyExit
- MissingBreakInSwitchStatement
- TooManyParameters
- CallingProtectedMethod
- AvoidBranchingStatementAsLastInLoop
- MissingAbstractMethodImplementation
- MissingHashMethod
- MisplacedNullCheck
- MisplacedNilCheck
- UseContainerLiteral
- LongLine
- ForLoopShouldBeWhileLoop
- HighNPathComplexity
- LongMethod
- EmptySwitchStatement
- RedundantConditionalOperator
- EmptyTryStatement
- EmptyCatchStatement
- UseObjectSubscripting
- AvoidPrivateStaticMembers
- EmptyElseBlock
- InvertedLogic
- LongClass
- LongVariableName
- GotoStatement
- BrokenOddnessCheck
- UseNumberLiteral
- TooFewBranchesInSwitchStatement
- UseBoxedExpression
- JumbledIncrementer
- EmptyIfStatement
- BranchDivergence
- MissingDefaultStatement
- HighCyclomaticComplexity
- NonCaseLabelInSwitchStatement
- ConstantIfExpression
- BrokenNullCheck
- BrokenNilCheck
- TooManyFields
- UnusedLocalVariable

如果我們使用.oclint最終我們將.oclint放在與compile_commands.json相同的路徑下,并在該路徑下執(zhí)行命令:

$ oclint-json-compilation-database -v -e Pods

或者直接執(zhí)行命令:

$ oclint-json-compilation-database -e Pods -- -report-type pmd \
    -rc=LONG_CLASS=1500 \
    -rc=NESTED_BLOCK_DEPTH=5 \
    -rc=LONG_VARIABLE_NAME=80 \
    -rc=LONG_METHOD=200 \
    -rc=LONG_LINE=300 \
    -disable-rule ShortVariableName \
    -disable-rule ObjCAssignIvarOutsideAccessors \
    -disable-rule AssignIvarOutsideAccessors \
    -allow-duplicated-violations=false\
    -max-priority-1=100000 \
    -max-priority-2=100000 \
    -max-priority-3=100000 >> oclint.xml

最終會(huì)生成oclint.xml(也可以自己生成html格式秉扑,直接查看效果)

Infer

InferFacebook開(kāi)源的一款代碼掃描軟件,可以分析Objective-C慧邮,Java或者C代碼,報(bào)告潛在的問(wèn)題。任何人都可以使用Infer檢測(cè)應(yīng)用舟陆,這可以將那些嚴(yán)重的 bug 扼殺在發(fā)布之前误澳,同時(shí)防止應(yīng)用崩潰和性能低下。

  • 命令安裝
$ brew install infer

在終端輸入 infer --version秦躯,驗(yàn)證是否安裝成功忆谓。

在這里插入圖片描述

InferOCLint一樣,都是分析compile_commands.json文件踱承。在compile_commands.json文件相同路徑下執(zhí)行命令:

# --skip-analysis-in-path 是忽略?huà)呙枘夸?$ infer run --skip-analysis-in-path Pods --keep-going --compilation-database compile_commands.json

注意:命令中一定要有--keep-going 不然會(huì)有錯(cuò)誤導(dǎo)致無(wú)法進(jìn)行分析倡缠。
另外需要在xcodebuild命令中添加OTHER_CFLAGS="-DNS_FORMAT_ARGUMENT(A)= -D_Nullable_result=_Nullable"

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

掃描出的結(jié)果會(huì)在工程目錄下的 infer-out 文件中茎活,其中具體的代碼會(huì)以 csv昙沦,txt,json 的格式分別存在對(duì)應(yīng)的文件中载荔。

總結(jié):OCLint基本上分析都是一些代碼規(guī)范的問(wèn)題盾饮,Infer可以檢查出空指針訪問(wèn)、資源泄露以及內(nèi)存泄露。

sonarqube

sonarqube是一個(gè)提供代碼靜態(tài)分析的平臺(tái)丘损,提供了一套完整的靜態(tài)分析方案普办,包括后端及前端頁(yè)面,可以結(jié)合jenkins徘钥、gitlab等平臺(tái)來(lái)進(jìn)行代碼分析衔蹲。sonarqube分社區(qū)版本和商業(yè)化版本,能掃描多種語(yǔ)言并且開(kāi)源。官網(wǎng)地址吏饿。
因?yàn)槠涞讓釉创a為java開(kāi)發(fā)的,所以對(duì)java代碼支持比較完善蔬浙,但是免費(fèi)的社區(qū)版并不支持OC猪落,所以我們?nèi)绻柚似脚_(tái)的話(huà),有以下兩種方式:

  • 開(kāi)源插件sonar-swift
    畴博,支持Objective-CSwift / Java笨忌,支持導(dǎo)入 SwiftLintInfer俱病、OCLint官疲、LizardFauxpas 工具的掃描分析結(jié)果亮隙。最新v1.6版本途凫,兼容SonarQube 8.9LTS版本。該插件是好未來(lái)研發(fā)團(tuán)隊(duì)研發(fā)并且開(kāi)源溢吻。
  • 付費(fèi)使用社區(qū)版plus维费,提供了SonarCFamily for C插件
    有官方提供技術(shù)支持,250+的rules可供選擇促王,不可自定義規(guī)則犀盟。

我們將使用開(kāi)源插件方案。

sonar-services(sonarqube)安裝

sonarqube 安裝有兩種方式

  • docker 安裝
$ docker pull sonarqube:8.9.7-community
  • 下載二進(jìn)制安裝包
    選中 8.9.7LTS 社區(qū)版本蝇狼,下載
    image

下載完安裝包后阅畴,進(jìn)入bin/macosx-universal-64 目錄。執(zhí)行命令:

$ sh sonar.sh start
image

控制臺(tái)輸出Started SonarQube說(shuō)明啟動(dòng)成功迅耘。

在瀏覽器訪問(wèn)http://localhost:9000/,能打開(kāi)頁(yè)面說(shuō)明啟動(dòng)成功贱枣。
默認(rèn)賬號(hào)為admin,密碼為admin颤专。

image

注意:如果啟動(dòng)失敗可以去sonarqube/logs下查看日志冯事。

image

安裝時(shí),發(fā)現(xiàn)sonarqube 8.9.7版本需要Java 11環(huán)境血公。所以需要先安裝Java 11環(huán)境昵仅。
安裝Java 11:
image

image

啟動(dòng)成功后我們?cè)谧钕旅鏁?huì)看到warning

image

警告建議我們自己配置數(shù)據(jù)庫(kù),需要說(shuō)明的是SonarQube如果想持久化保存數(shù)據(jù),是需要依賴(lài)數(shù)據(jù)庫(kù)的摔笤。

SonarQube默認(rèn)提供H2存儲(chǔ)够滑,只能暫時(shí)存儲(chǔ)一些小項(xiàng)目結(jié)果,僅為了演示使用吕世。

conf/sonar.properties下配置數(shù)據(jù)庫(kù)地址即可彰触。可選 MySQL命辖、Oracle况毅、PostgreSQL

下面我們就來(lái)配置數(shù)據(jù)庫(kù)(mysql后續(xù)將不再支持):這里我們使用的是PostgreSQL尔艇,配置參考

PostgreSQL

用Homebrew 執(zhí)行命令安裝PostgreSQL:

$ brew install postgresql //安裝
image

安裝完數(shù)據(jù)庫(kù)后尔许,啟動(dòng)數(shù)據(jù)庫(kù),執(zhí)行命令:

$ pg_ctl -D /usr/local/var/postgres start //啟動(dòng)
$ createdb                                //創(chuàng)建數(shù)據(jù)庫(kù)
$ psql                                    //登錄控制臺(tái)

數(shù)據(jù)庫(kù)安裝創(chuàng)建好后终娃,我們需要提供一個(gè)數(shù)據(jù)庫(kù)和賬號(hào)sonarqube使用味廊。執(zhí)行命令:

CREATE USER sonarqube WITH PASSWORD 'sonarqube';//創(chuàng)建用戶(hù)
CREATE DATABASE sonar OWNER sonarqube;//創(chuàng)建屬于該用戶(hù)的數(shù)據(jù)庫(kù)

創(chuàng)建完成后執(zhí)行命令退出:

\q
image

然后我們?nèi)?code>sonarqube/conf目錄下編輯sonar.properties,將數(shù)據(jù)庫(kù)相應(yīng)配置配置完成

sonar.jdbc.username=sonarqube
sonar.jdbc.password=sonarqube
sonar.jdbc.url=jdbc:postgresql://localhost/sonar

編輯完成后在sonarqube/bin/macosx-universal-64/目錄下執(zhí)行: sh sonar.sh restart棠耕,此時(shí)警告已經(jīng)消除了余佛。

image

至此我們的sonarqube的前端服務(wù)已經(jīng)配置完成了。

漢化包安裝

通過(guò)Github 下載對(duì)應(yīng)版本漢化包jar包插件窍荧。

image

下載插件放到/extensions/plugins 目錄下辉巡。重啟sonarqube服務(wù),就可以看到漢化后的界面蕊退。
image

sonar-swift

通過(guò)GitHub 下載對(duì)應(yīng)插件

image

下載插件放到/extensions/plugins 目錄下红氯。重啟sonarqube服務(wù),就可以了咕痛。

sonar-scanner

sonar-scanner 用來(lái)掃描本地代碼,并且上傳到SonarQube平臺(tái)中痢甘。

  • 下載安裝地址;按照不同的操作系統(tǒng)選擇不同安裝包即可茉贡。
    image
  • 配置環(huán)境變量:
$ vim ~/.bash_profile

#sonar-scanner for cli
export PATH=$PATH:/Users/csdn/scanner/bin:$PATH

$ source ~/.bash_profile

sonar-scanner分為兩種使用方式:

  • 配置文件方式:

在需要掃描項(xiàng)目根目錄下新建sonar-project.properties 文件塞栅,內(nèi)容如下:

sonar.projectKey=CsdnPlus
sonar.projectName=CsdnPlus
sonar.language=objc
sonar.sources=/Users/csdn/.jenkins/workspace/csdn_build_ios/
sonar.objectivec.workspace=CsdnPlus.xcworkspace
sonar.objectivec.appScheme=CsdnPlus
sonar.sourceEncoding=UTF-8
sonar.junit.reportsPath=sonar-reports/
sonar.objectivec.oclint.report=sonar-reports/oclint.xml
sonar.swift.infer.report=infer-out/report.json

進(jìn)入項(xiàng)目根目錄下,然后輸入sonar-scanner命令腔丧,執(zhí)行代碼分析放椰。

  • 命令行方式:

在命令中設(shè)置了參數(shù):

sonar-scanner -Dsonar.projectKey=CsdnPlus -Dsonar.projectName=CsdnPlus -Dsonar.projectName=CsdnPlus -Dsonar.projectVersion=5.1.0 

如果我們要SonarQube忽略一些指定目錄或者文件的掃描,可以在配置中添加sonar.exclusions 例如:

//忽略指定文件目錄或者文件
sonar.exclusions=**/Resource/**,**/*.py

或者可以在命令中設(shè)置參數(shù)愉粤,例如:

sonar-scanner -Dsonar.exclusions=**/Resource/**,**/*.py  -Dsonar.projectKey=CsdnPlus -Dsonar.projectName=CsdnPlus -Dsonar.projectName=CsdnPlus -Dsonar.projectVersion=5.1.0 

這里的核心便是在上面步驟中由OCLint生成的oclint.xml文件與Infer生成的report.json砾医,另外注意oclint.xml必須放至sonar-reports文件下。report.jsonInfer生成的infer-out目錄下衣厘。

image

在這里插入圖片描述

命令執(zhí)行成功后如蚜,便可在sonarQube的前端頁(yè)面看到對(duì)應(yīng)的檢測(cè)效果了压恒。


image

在這里插入圖片描述

在這里插入圖片描述

檢測(cè)效果圖:


image

image

集成進(jìn)Jenkins

我們項(xiàng)目本身已經(jīng)有自動(dòng)化構(gòu)建服務(wù),所以比較方便错邦。

  • Jenkins項(xiàng)目配置探赫,選項(xiàng)中增加OCLint(可以自己命名)選項(xiàng)


    image
  • 構(gòu)建Shell命令中,增加OCLint相關(guān)命令

if [ "$MODE"x = "OCLint"x ]
then
    sh /Users/csdn/.jenkins/workspace/csdn_build_ios/fastlane/oclint.sh "$GIT_BRANCH"
fi

image
  • 企業(yè)微信通知, Shell中增加企業(yè)微信機(jī)器人URL
//獲取本機(jī)IP
local_ip=$(ifconfig | grep '\<inet\>'| grep -v '127.0.0.1' | awk '{ print $2}' | awk 'NR==1')
curl 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=31ef53a9-1e97-42c6-9cc7-fb4432bd41f9' \
       -H 'Content-Type: application/json' \
       -d '
                {
                "msgtype":"news",
                "news":{
                "articles":[
                {
                "title":"SonarQube 靜態(tài)代碼掃描完成",
                "url":"http://'${local_ip}':9000/",
                "description":"APP名稱(chēng):CSDNAPP\n掃描代碼分支:'$1'",
                "picurl":"https://img-bss.csdnimg.cn/202103251639445966.png"
            }
        ]
    }
}'
在這里插入圖片描述

一些有用的shell命令

獲取APP名稱(chēng):

product_name=`sed -n '/PRODUCT_NAME/{s/PRODUCT_NAME = //;s/;//;s/^[[:space:]]*//;s/\"http://g;p;q;}' ./$myscheme.xcodeproj/project.pbxproj`

獲取APP版本:

version_number=`sed -n '/MARKETING_VERSION/{s/MARKETING_VERSION = //;s/;//;s/^[[:space:]]*//;s/\"http://g;p;q;}' ./$myscheme.xcodeproj/project.pbxproj`

獲取本機(jī)IP地址:

local_ip = $(ifconfig | grep '\<inet\>'| grep -v '127.0.0.1' | awk '{ print $2}' | awk 'NR==1')
  • 附上完整oclint.sh命令:
#!/bin/bash

COLOR_ERR="\033[1;31m"    #出錯(cuò)提示
COLOR_SUCC="\033[0;32m"  #成功提示
COLOR_QS="\033[1;37m"  #問(wèn)題顏色
COLOR_AW="\033[0;37m"  #答案提示
COLOR_END="\033[1;34m"     #顏色結(jié)束符



# 尋找項(xiàng)目的 ProjectName
function searchProjectName () {
    # maxdepth 查找文件夾的深度
    find . -maxdepth 1 -name "*.xcodeproj"
}

function oclintForProject () {
    # 預(yù)先檢測(cè)所需的安裝包是否存在
    if which xcodebuild 2>/dev/null; then
    echo 'xcodebuild exist'
    else
    echo 'xcodebuild 未安裝撬呢,請(qǐng)安裝Xcode'
    fi
    
    if which oclint 2>/dev/null; then
    echo 'oclint exist'
    else
    echo 'oclint 未安裝伦吠,請(qǐng)安裝OCLint'
    fi
    if which xcpretty 2>/dev/null; then
    echo 'xcpretty exist'
    else
    gem install xcpretty
    fi
    
    
    # 指定編碼
    export LANG="zh_CN.UTF-8"
    export LC_COLLATE="zh_CN.UTF-8"
    export LC_CTYPE="zh_CN.UTF-8"
    export LC_MESSAGES="zh_CN.UTF-8"
    export LC_MONETARY="zh_CN.UTF-8"
    export LC_NUMERIC="zh_CN.UTF-8"
    export LC_TIME="zh_CN.UTF-8"
    export xcpretty=/usr/local/bin/xcpretty # xcpretty 的安裝位置可以在終端用 which xcpretty找到
    
    searchFunctionName=`searchProjectName`
    path=${searchFunctionName}
    # 字符串替換函數(shù)。//表示全局替換 /表示匹配到的第一個(gè)結(jié)果替換魂拦。
    path=${path//.\//}  # ./BridgeLabiPhone.xcodeproj -> BridgeLabiPhone.xcodeproj
    path=${path//.xcodeproj/} # BridgeLabiPhone.xcodeproj -> BridgeLabiPhone
    
    myworkspace=$path".xcworkspace" # workspace名字
    myscheme=$path  # scheme名字
    
    # 清除上次編譯數(shù)據(jù)
    if [ -d ./derivedData ]; then
    echo -e $COLOR_SUCC'-----清除上次編譯數(shù)據(jù)derivedData-----'$COLOR_SUCC
    rm -rf ./derivedData
    fi
    
    # xcodebuild clean
    xcodebuild -scheme $myscheme -workspace $myworkspace clean


    # # 生成編譯數(shù)據(jù)
    xcodebuild -scheme $myscheme -workspace $myworkspace -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 12 Pro Max' -configuration Debug GCC_PRECOMPILE_PREFIX_HEADER=YES CLANG_ENABLE_MODULE_DEBUGGING=NO COMPILER_INDEX_STORE_ENABLE=NO OTHER_CFLAGS="-DNS_FORMAT_ARGUMENT(A)= -D_Nullable_result=_Nullable" | tee xcodebuild.log | xcpretty -r json-compilation-database -o compile_commands.json

    if [ -f ./compile_commands.json ]; then
    echo -e $COLOR_SUCC'編譯數(shù)據(jù)生成完畢'$COLOR_SUCC
    else
    echo -e $COLOR_ERR'編譯數(shù)據(jù)生成失敗'$COLOR_ERR
    return -1
    fi

    echo -e $COLOR_SUCC'OCLint代碼分析開(kāi)始'$COLOR_SUCC
    # 生成報(bào)表
    oclint-json-compilation-database -e Pods -- -report-type pmd \
    -rc=LONG_CLASS=1500 \
    -rc=NESTED_BLOCK_DEPTH=5 \
    -rc=LONG_VARIABLE_NAME=80 \
    -rc=LONG_METHOD=200 \
    -rc=LONG_LINE=300 \
    -disable-rule ShortVariableName \
    -disable-rule ObjCAssignIvarOutsideAccessors \
    -disable-rule AssignIvarOutsideAccessors \
    -allow-duplicated-violations=false\
    -max-priority-1=100000 \
    -max-priority-2=100000 \
    -max-priority-3=100000 >> oclint.xml
    
    echo -e $COLOR_SUCC'Infer代碼分析開(kāi)始'$COLOR_SUCC
    # --skip-analysis-in-path 是忽略?huà)呙枘夸?    infer run --skip-analysis-in-path Pods --keep-going --compilation-database compile_commands.json
    
    product_name=`sed -n '/PRODUCT_NAME/{s/PRODUCT_NAME = //;s/;//;s/^[[:space:]]*//;s/\"http://g;p;q;}' ./$myscheme.xcodeproj/project.pbxproj`
    version_number=`sed -n '/MARKETING_VERSION/{s/MARKETING_VERSION = //;s/;//;s/^[[:space:]]*//;s/\"http://g;p;q;}' ./$myscheme.xcodeproj/project.pbxproj`
    
    if [ -f ./oclint.xml -a -f ./infer-out/report.json ]; then
    rm compile_commands.json
    echo -e $COLOR_SUCC'代碼分析完畢'$COLOR_SUCC
    
    mv oclint.xml sonar-reports/
    
    echo -e $COLOR_SUCC'移動(dòng)至sonar-reports完畢'$COLOR_SUCC
    echo -e $COLOR_SUCC'開(kāi)始執(zhí)行sonar-scanner掃描文件'$COLOR_SUCC
    
    
    
    echo -e $COLOR_SUCC'版本號(hào):'${version_number}''$COLOR_SUCC
    
    sonar-scanner  -Dsonar.projectVersion=$version_number
    
    else
    
    echo -e $COLOR_ERR'分析失敗'$COLOR_ERR
    
    curl 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=31ef53a9-1e97-42c6-9cc7-fb4432bd41f9' \
    -H 'Content-Type: application/json' \
    -d '
           {
           "msgtype": "text",
           "text": {
              "content": "APP名稱(chēng):'${product_name}'\nAPP版本:'${version_number}'\nOCLint分析失敗"
           }
       }'
       
       return -1
       
       fi
       
       local_ip = $(ifconfig | grep '\<inet\>'| grep -v '127.0.0.1' | awk '{ print $2}' | awk 'NR==1')
       
       curl 'https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=31ef53a9-1e97-42c6-9cc7-fb4432bd41f9' \
       -H 'Content-Type: application/json' \
       -d '
                {
                "msgtype":"news",
                "news":{
                "articles":[
                {
                "title":"SonarQube 靜態(tài)代碼掃描完成",
                "url":"http://'${local_ip}':9000/",
                "description":"APP名稱(chēng):'${product_name}'\nAPP版本:'${version_number}'掃描代碼分支:'$1'",
                "picurl":"https://csdn-app.csdn.net/1024store_1024pt.png"
            }
        ]
    }
}'
}

oclintForProject $1

  • Jenkins 配置PMD(當(dāng)然有了SonarQube毛仪,PMD就很雞肋了)


    image

    image

    image
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市芯勘,隨后出現(xiàn)的幾起案子箱靴,更是在濱河造成了極大的恐慌,老刑警劉巖借尿,帶你破解...
    沈念sama閱讀 222,807評(píng)論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件刨晴,死亡現(xiàn)場(chǎng)離奇詭異屉来,居然都是意外死亡路翻,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,284評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)茄靠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)茂契,“玉大人,你說(shuō)我怎么就攤上這事慨绳〉粢保” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,589評(píng)論 0 363
  • 文/不壞的土叔 我叫張陵脐雪,是天一觀的道長(zhǎng)厌小。 經(jīng)常有香客問(wèn)我,道長(zhǎng)战秋,這世上最難降的妖魔是什么璧亚? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,188評(píng)論 1 300
  • 正文 為了忘掉前任,我火速辦了婚禮脂信,結(jié)果婚禮上癣蟋,老公的妹妹穿的比我還像新娘。我一直安慰自己狰闪,他們只是感情好疯搅,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,185評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著埋泵,像睡著了一般幔欧。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,785評(píng)論 1 314
  • 那天琐馆,我揣著相機(jī)與錄音规阀,去河邊找鬼。 笑死瘦麸,一個(gè)胖子當(dāng)著我的面吹牛谁撼,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播滋饲,決...
    沈念sama閱讀 41,220評(píng)論 3 423
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼厉碟,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了屠缭?” 一聲冷哼從身側(cè)響起箍鼓,我...
    開(kāi)封第一講書(shū)人閱讀 40,167評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎呵曹,沒(méi)想到半個(gè)月后款咖,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,698評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡奄喂,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,767評(píng)論 3 343
  • 正文 我和宋清朗相戀三年铐殃,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片跨新。...
    茶點(diǎn)故事閱讀 40,912評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡富腊,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出域帐,到底是詐尸還是另有隱情赘被,我是刑警寧澤,帶...
    沈念sama閱讀 36,572評(píng)論 5 351
  • 正文 年R本政府宣布肖揣,位于F島的核電站民假,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏龙优。R本人自食惡果不足惜羊异,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,254評(píng)論 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望陋率。 院中可真熱鬧球化,春花似錦、人聲如沸瓦糟。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,746評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)菩浙。三九已至巢掺,卻和暖如春句伶,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背陆淀。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,859評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工考余, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人轧苫。 一個(gè)月前我還...
    沈念sama閱讀 49,359評(píng)論 3 379
  • 正文 我出身青樓楚堤,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親含懊。 傳聞我的和親對(duì)象是個(gè)殘疾皇子身冬,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,922評(píng)論 2 361

推薦閱讀更多精彩內(nèi)容