相對現代化的把控前端程式碼質量
本站使用「署名 4.0 國際 (CC BY 4.0)」許可協議,歡迎轉載、或重新修改使用,但需要註明來源。 ofollow,noindex">署名 4.0 國際 (CC BY 4.0)
本文作者: 蘇洋
建立時間: 2018年09月06日 統計字數: 10766字 閱讀時間: 22分鐘閱讀 本文連結: soulteary.com/2018/09/06/…
相對現代化的把控前端程式碼質量
最近幾天聊天,常常聊到 持續整合
輔助把控 程式碼質量
,以前端團隊為例,我們來簡單聊聊。
本篇很可能是你在網上能找到的使用容器應用最新版本 SonarQube
相對詳細的一篇,或者是唯一一篇,所以如果遇到問題,歡迎和我進行討論溝通。
如何把控質量
個人認為把控質量的核心是 形成標準 、 避免錯誤引入 ,從而提高應用的 健壯性 ,降低 維護成本 和不應存在的 複雜度 ,這裡在不討論 測試
的情況下,通常的做法無非:
- 在程式碼提交前或後進行 Code Review,定期走讀程式碼。
- 使用
eslint
、jshint
、jslint
等工具在開發環境通過Git Hook
觸發,進行風格和簡單的質量評估,避免Bug
、漏洞
、壞味道
的程式碼提交到平臺。 - 配合
pipeline
使用,定義一些過程指令碼,在程式碼提交後,或者分支合併前去呼叫以上工具,避免質量不佳的程式碼合併到開發主幹,被“傳承”下去。 - 使用
istanbul
之類的工具簡單檢查覆蓋率,避免有一定複雜度的業務程式碼潛在風險過高。 - 人員培訓,技術氛圍建設。
策略的差異
一個良好的開發氛圍,第一條是必不可少的,不論是否使用輔助審查系統,使用審查機制,可以把很多不合理的設計和明顯使用錯誤的程式碼在萌芽期殺死。
如果輔助手段使用本地執行驗證,在配套前端開發技術設施不完善的情況下,則可能出現為了省事解除安裝 Hook
跳過審查的事件;或者需要額外維護規則更新機制的成本,將團隊程式碼規則儲存到倉庫或者私有包倉庫的維護成本,甚至是儲存倉庫的建設成本。如果雲端執行資源不足的話,本地執行則會是一個很好的補充手段。
如果使用雲端審查,需要配套設施都相對完善:私有包倉庫、CVS 系統、甚至容器倉庫、CI Runner 執行環境...(後續或許可以聊聊如何建設這些)
雲端審查可以將規則和標準統一,配合 Docker
容器技術可以輕鬆進行應用資源擴容,遠超本地執行效率,並且可以進行掃描存檔,客觀的反饋程式碼質量和功能加入、人員培訓前後的變化。
而技術團隊的氛圍建設、人員的技術培訓是一件長期持久的事情,打造“有人願意教”、“有人願意學”的機會可遇不可求。
相比較之下,如果你的公司已經將 GitLab
升級到了 9.x
以上,並且有資源去做 CI/CD
的事情,那麼直接在 Pipeline
過程中新增程式碼審查是一個相對靠譜的事情。
使用 SonarQube 進行程式碼分析
接下來我會演示如何使用 SonarQube
最新版本輔助進行程式碼分支把控質量。
我將最新版本的 SonarQube
和 scanner-runner
封裝成了映象,避免直接在 GitLab Runner
宿主環境直接執行,汙染執行環境。
然後在 CI
階段定義一個新的 Stage
,這裡假定叫 sonar
好了。
sonar: image: docker.lab.com/sonar-scanner-runner:3.2.0.1227 script: - cd .. && BASE_DIR=$(pwd) && rm -rf /data && ln -s $BASE_DIR/${CI_PROJECT_NAME} /data - ls /data - echo ${CI_PROJECT_NAME}-${CI_PROJECT_ID} - sonar ${CI_PROJECT_NAME}-${CI_PROJECT_ID} 複製程式碼
這裡使用 Publish
模式將掃描結果回傳 SonarQube Web Server
,形成持續的報告。
如果你需要將掃描階段定義為部署前的一個“質量把控關口”,那麼需要再簡單寫一個指令碼,在掃描器執行完畢後,抓取當前專案的狀態,是否超過 設定的閾值
,從而中斷後續的流程,指令碼參考我上一篇 如果不用 Node.js 寫業務 中的使用 Node.js
作為持續整合的粘合劑,獲取掃描器返回的介面地址中的 status
狀態值即可,比較簡單,就略了。

當你提交程式碼之後, GitLab Runner
呼叫映象中的掃描器指令碼(這裡使用的 Runner
為 docker
型別的 Runner
)進行掃描,如果成功,大概是這個樣子。

執行器日誌會返回這個任務的具體執行結果,上文有提到該如何處理。
{ task: { id: "AWWtMWX1Frzdillyw-1a", type: "REPORT", componentId: "AWWtLY29Frzdillyw-1T", componentKey: "leetcode-547", componentName: "leetcode-547", componentQualifier: "TRK", analysisId: "AWWtMWsbbu6HJjdFR-kA", status: "SUCCESS", submittedAt: "2018-09-06T04:43:40+0000", startedAt: "2018-09-06T04:43:41+0000", executedAt: "2018-09-06T04:43:42+0000", executionTimeMs: 1260, logs: false, hasScannerContext: true, organization: "default-organization" } } 複製程式碼
訪問 Web 平臺,可以看到我們剛剛提交觸發的測試報告已經生成,如果有缺陷的話,會展示因為什麼,又該如何修正。

相關功能還是比較多的,參考本文自行搭建後,可以自行了解。

如果上面的規則看不太清楚,可以嘗試瀏覽官方規則列表的線上版本:
- JavaScript 的規則列表
當然,如果你願意的話,可以參考下面的連結進行自定義配置,讓掃描報告中包含覆蓋率,以及使用自定義的 eslint
規則進行補充或者替換預設的規則:
當然,還有一個定製化更強的套路,使用專門的 eslint
外掛,目前外掛只能跑在老平臺上,感興趣的你如果對 Java 很熟悉,可以參考官方開源社群的示例和開源的三方 eslint
外掛進行升級改造,這裡就不展開了。
官方提供了示例、也有三方外掛,你甚至可以定義如果包含了哪些詞彙、或者檔案超出指定大小等就拒絕這次的審查通過,避免程式碼合併到主幹分支造成更大的損失。
說了這麼多,那麼如何快速搭建一個相對高可用的 SonarQube
呢。
構建你的平臺映象
例子中可以看到,我個人使用私有域名,並簽署了私有證書,如果你的公司團隊狀況類似,可以直接參考,不然的話,去掉相關程式碼即可。(不去掉也無所謂)
下面是我的映象,會自動將私有證書進行授信,一般來說未來版本升級,只要修改這個版本號即可使用(7.1、7.2、7.3 驗證通過)。
FROM openjdk:8-alpine ENV JAVA_CERTS_DIR "$JAVA_HOME/jre/lib/security/" WORKDIR $JAVA_CERTS_DIR ADD ssl/*.crt $JAVA_CERTS_DIR # https://stackoverflow.com/questions/41497871/importing-self-signed-cert-into-dockers-jre-cacert-is-not-recognized-by-the-ser RUN ls *.crt | xargs -I {} keytool -importcert -storepass changeit -noprompt -trustcacerts -alias {} -file {} -keystore "$JAVA_CERTS_DIR/cacerts" # https://github.com/SonarSource/docker-sonarqube/blob/4d76dfe9fb4c5d41ba1852cd5072dbff15ca5a20/7.1-alpine/Dockerfile ENV SONAR_VERSION=7.3 \ SONARQUBE_HOME=/opt/sonarqube \ # Database configuration # Defaults to using H2 SONARQUBE_JDBC_USERNAME=sonar \ SONARQUBE_JDBC_PASSWORD=sonar \ SONARQUBE_JDBC_URL= # Http port EXPOSE 9000 RUN addgroup -S sonarqube && adduser -S -G sonarqube sonarqube RUN set -x \ && apk add --no-cache gnupg unzip \ && apk add --no-cache libressl wget \ && apk add --no-cache su-exec \ && apk add --no-cache bash \ # pub2048R/D26468DE 2015-05-25 #Key fingerprint = F118 2E81 C792 9289 21DBCAB4 CFCA 4A29 D264 68DE # uidsonarsource_deployer (Sonarsource Deployer) <[email protected]> # sub2048R/06855C1D 2015-05-25 && gpg --keyserver ha.pool.sks-keyservers.net --recv-keys F1182E81C792928921DBCAB4CFCA4A29D26468DE \ && mkdir /opt \ && cd /opt \ && wget -O sonarqube.zip --no-verbose https://sonarsource.bintray.com/Distribution/sonarqube/sonarqube-$SONAR_VERSION.zip \ && wget -O sonarqube.zip.asc --no-verbose https://sonarsource.bintray.com/Distribution/sonarqube/sonarqube-$SONAR_VERSION.zip.asc \ && gpg --batch --verify sonarqube.zip.asc sonarqube.zip \ && unzip sonarqube.zip \ && mv sonarqube-$SONAR_VERSION sonarqube \ && chown -R sonarqube:sonarqube sonarqube \ && rm sonarqube.zip* \ && rm -rf $SONARQUBE_HOME/bin/* VOLUME "$SONARQUBE_HOME/data" WORKDIR $SONARQUBE_HOME COPY run.sh $SONARQUBE_HOME/bin/ ENTRYPOINT ["./bin/run.sh"] 複製程式碼
配合的執行指令碼:
#!/bin/sh # https://raw.githubusercontent.com/SonarSource/docker-sonarqube/4d76dfe9fb4c5d41ba1852cd5072dbff15ca5a20/7.1-alpine/run.sh set -e if [ "${1:0:1}" != '-' ]; then exec "$@" fi chown -R sonarqube:sonarqube $SONARQUBE_HOME exec su-exec sonarqube \ java -jar lib/sonar-application-$SONAR_VERSION.jar \ -Dsonar.log.console=true \ -Dsonar.jdbc.username="$SONARQUBE_JDBC_USERNAME" \ -Dsonar.jdbc.password="$SONARQUBE_JDBC_PASSWORD" \ -Dsonar.jdbc.url="$SONARQUBE_JDBC_URL" \ -Dsonar.web.javaAdditionalOpts="$SONARQUBE_WEB_JVM_OPTS -Djava.security.egd=file:/dev/./urandom" \ "$@" 複製程式碼
最後是編排工具使用的配置:
# https://github.com/SonarSource/docker-sonarqube/blob/master/recipes.md version: '3' services: sonarqube: image: docker.lab.com/sonar.lab.com:7.3 expose: - 9000 - 9001 - 9092 environment: SONARQUBE_JDBC_USERNAME: sonar SONARQUBE_JDBC_PASSWORD: sonar SONARQUBE_JDBC_URL: 'jdbc:postgresql://db:5432/sonar' JAVA_OPTS: '-Duser.timezone=Asia/Shanghai' depends_on: - db volumes: - ./data/conf:/opt/sonarqube/conf - ./data/data:/opt/sonarqube/data - ./data/extensions:/opt/sonarqube/extensions - ./data/bundled-plugins:/opt/sonarqube/lib/bundled-plugins networks: - traefik labels: - "traefik.enable=true" - "traefik.port=9000" - "traefik.frontend.rule=Host:sonar.lab.com" - "traefik.frontend.entryPoints=http,https" db: image: postgres:10.4-alpine expose: - 5432 environment: - POSTGRES_USER=sonar - POSTGRES_PASSWORD=sonar volumes: - ./data/postgresql:/var/lib/postgresql # This needs explicit mapping due to https://github.com/docker-library/postgres/blob/4e48e3228a30763913ece952c611e5e9b95c8759/Dockerfile.template#L52 - ./data/postgresql_data:/var/lib/postgresql/data networks: - traefik networks: traefik: external: true 複製程式碼
這裡依舊是使用 Traefik
的服務發現進行自動註冊,擴容我已經講過好多次了,翻翻前面的文章吧:D。
這裡為了能夠達到使用者和 GitLab
互通等,我還做了一個簡單的預設配置,儲存在 data/conf/sonar.properties
,並使用編排工具對映到容器內,你也可以選擇直接把配置打到容器裡,或者走配置中心進行遠端獲取(最佳方案)。
# https://github.com/SonarSource/sonarqube/blob/master/sonar-application/src/main/assembly/conf/sonar.properties sonar.telemetry.enable=false sonar.updatecenter.activate=false sonar.exclusions=bower_components/**/*, node_modules/**/* sonar.core.serverBaseURL=https://sonar.lab.com # GitLab Authentication sonar.auth.gitlab.enabled=true sonar.auth.gitlab.url=https://gitlab.lab.com sonar.auth.gitlab.applicationId=YOUR sonar.auth.gitlab.secret=YOUR sonar.auth.gitlab.allowUsersToSignUp=true sonar.auth.gitlab.ignore_certificate=true # GitLab Reporting sonar.gitlab.url=https://gitlab.lab.com sonar.gitlab.ignore_certificate=true sonar.gitlab.user_token=YOUR 複製程式碼
如果你有想定製修改的地方,可以參考官方文件,限於篇幅和時間,這裡也先略過,後面還有好多內容。
構建你的掃描器映象
掃描器我這裡分為了兩部分,一個映象是給使用者使用的,也就是跨平臺的 掃描工具
,另外一個是專屬於 GitLab Runner
的掃描器。
兩者差異主要是是否要進行檔案掛載、以及是否包含預設執行入口,為了方便維護,我將跨平臺的映象設計為基礎映象,使用指令碼基於基礎映象進行修改。
下面是基礎映象:
FROM java:openjdk-8u45-jre MAINTAINER soulteary <[email protected]> ENV NODE_VERSION 10.8.0 ENV SONAR_RUNNER_VERSION 3.2.0.1227 ENV SONAR_RUNNER_DIR sonar-scanner ENV SONAR_RUNNER_HOME /opt/${SONAR_RUNNER_DIR} ENV SONAR_RUNNER_PACKAGE sonar-scanner-cli-${SONAR_RUNNER_VERSION}-linux.zip ENV HOME ${SONAR_RUNNER_HOME} WORKDIR /opt RUN wget https://sonarsource.bintray.com/Distribution/sonar-scanner-cli/${SONAR_RUNNER_PACKAGE} && \ unzip ${SONAR_RUNNER_PACKAGE} && \ mv ${SONAR_RUNNER_DIR}-${SONAR_RUNNER_VERSION}-linux ${SONAR_RUNNER_DIR} && \ rm ${SONAR_RUNNER_PACKAGE} RUN groupadd -r sonar && \ useradd -r -s /usr/sbin/nologin -d ${SONAR_RUNNER_HOME} -c "Sonar service user" -g sonar sonar && \ chown -R sonar:sonar ${SONAR_RUNNER_HOME} && \ mkdir -p /data && \ chown -R sonar:sonar /data RUN curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash && \ export NVM_DIR="$HOME/.nvm" && \. "$NVM_DIR/nvm.sh" && \ nvm install ${NODE_VERSION} && nvm alias default ${NODE_VERSION} ENV JAVA_CERTS_DIR "$JAVA_HOME/jre/lib/security" ADD /ssl/*.crt /tmp/ ADD run.sh ${SONAR_RUNNER_HOME}/bin/ # https://stackoverflow.com/questions/41497871/importing-self-signed-cert-into-dockers-jre-cacert-is-not-recognized-by-the-ser RUN ls /tmp/*.crt | xargs -I {} keytool -importcert -storepass changeit -noprompt -trustcacerts -alias {} -file {} -keystore "$SONAR_RUNNER_HOME$JAVA_CERTS_DIR/cacerts" && \ cp /tmp/*.crt /usr/share/ca-certificates && update-ca-certificates && \ rm -rf /tmp/*.crt && \ ln ${SONAR_RUNNER_HOME}/bin/run.sh /bin/sonar # https://stackoverflow.com/questions/28740785/error-in-sonarqube-when-launching-svn-blame RUN echo "sonar.host.url=https://sonar.lab.com" >> ${SONAR_RUNNER_HOME}/conf/sonar-scanner.properties && \ echo "sonar.projectBaseDir=/data">> ${SONAR_RUNNER_HOME}/conf/sonar-scanner.properties && \ echo "sonar.sources=/data">> ${SONAR_RUNNER_HOME}/conf/sonar-scanner.properties && \ echo "sonar.scm.disabled=true">> ${SONAR_RUNNER_HOME}/conf/sonar-scanner.properties USER sonar VOLUME /data ENTRYPOINT ["/bin/sonar"] 複製程式碼
可以看到,基礎映象包含可配置化的 Node
版本,以及可配置的掃描器,當然,也支援私有證書的授信。
在本地執行的話,到專案程式碼根目錄,需要輸入下面的命令執行即可,結果等同使用 CI Runner 執行。
docker run --rm -it -v `pwd`:/data docker.lab.com/sonar-scanner:3.2.0.1227 YOUR_PROJECT_ID 複製程式碼
交付給 CI 使用的映象大致相同,前文提到的指令碼內容如下:
#!/usr/bin/env bash cat ../normal/Dockerfile | \ sed 's/"\/bin\/sonar"/"\/bin\/bash"/g' | \ sed 's/ENTRYPOINT/CMD/g' | \ sed 's/VOLUME \/data//g' | \ sed 's/USER sonar//g' | tee Dockerfile 複製程式碼
在執行完畢你會獲得另外一個去除了部分內容的 Dockerfile
。
如何使用,在上一小節定義 GitLab Runner
有提過,定義 image
欄位後,新增一行執行指令碼 sonar YOUR_PROJECT_ID
即可。
關於 CI Runner
至於文章中一直提到的 CI Runner,如果公司為你準備好了,直接使用就是了。
如果還沒有,參考下面命令,根據自己需求進行調整,註冊一個 docker 型別的執行器即可。
gitlab-runner register -n \ --url https://gitlab.lab.com/ \ --registration-token $YOUR_TOKEN \ --executor docker \ --description "docker runner" \ --docker-image "docker.lab.com/docker-with-certs:stable" \ --docker-privileged --tls-ca-file=/lab.com.crt 複製程式碼