前言

因为我们公司前端主用的是vue,在进行sonar代码检测的时候默认使用的是sonar检测规则,经过会议讨论要使用eslint自定义规则来进行检测vue代码;
注:
eslint自定义规则需要修改前端项目下的.eslintrc.js文件,这里就不赘述了,本篇使用默认的eslint规则,主要是和sonarqube、jenkins进行集成 实现使用eslint自动化分析前端代码;


一、sonar新建"JavaScript"质量配置

 sonar在前端的JavaScript校验不支持自行编写规则,只能使用sonar提供的内置规则;
 需要注意,把eslint的检查报告作为导入到sonar并不意味着sonar只维护导入的问题,在导入时sonarQube也会根据根据不同语言使用其默认的sonar规则进行扫描检查,最后sonar界面上显示的问题情况是两个数据的合并。
 若是两个方式都使用,则会导致开发者本地使用eslint风格校验没问题,但是有些代码却不符合sonar的内置规则,因此需要把sonar的内置规则禁用,只保留eslint检查报告导入的方式。
 但是,sonar不允许禁用内置规则,只能通过创建一个空规则,然后把规则作为项目默认规则即可。
 基本流程:sonar对内置所有的前端语言都创建一个空规则,然后把eslint检测报告导入到sonar进行问题管理和统计。

在这里插入图片描述

#将新建的质量规则设为默认,不使用sonar自带的检测规则
在这里插入图片描述



二、前端项目引入eslint插件导出检测报告

需要在前端项目中的package.json文件"scripts"处添加以下脚本

{
    "scripts": {
        "lint:report": "eslint ./src -f json -o report.json --ext .js,.vue,.css,.scss || exit 0"
    }
}
  • .src:表示eslint校验的文件在src目录;
  • -f json:表示校验的内容结构以json形式导出报告;
  • -o report.json:表示导出的报告将写入到根目录的report.json文件中;
  • –ext .js,.vue,.css,.scss:表示校验的文件扩展名为js、vue、css和scss文件,如果有其他文件可以继续添加;

配置如下:
在这里插入图片描述



三、导入eslint报告到sonarqube系统中

默认的sonar代码检测配置为如下:
在这里插入图片描述

默认的sonar代码检测配置集成到jenkins如下:
 集成到jenkins可参考SonarQube 9.x与Jenkins进行集成并扫描后端java以及前端vue代码;
在这里插入图片描述


需要将上面默认的配置修改为

npm install
npm run lint:report
    
sonar-scanner \
-Dsonar.projectKey=${Gitee_Code_Project} \
-Dsonar.sources=. \
-Dsonar.host.url=${SonarQube_URL} \
-Dsonar.login=${SonarQube_Secret} \
-Dsonar.eslint.reportPaths=report.json

首先执行npm install用于安装eslint等包,接着执行npm run lint:report导出eslint的报告内容,最后添加一个reportPaths参数,把eslint错误报告文件导入到SonarQube系统中。

 jenkins配置如下:

在这里插入图片描述

修改好后再次构建项目查看日志会包含以下内容

...
INFO: ------------- Run sensors on module gotone-admin
INFO: Load metrics repository
INFO: Load metrics repository (done) | time=24ms
INFO: Sensor CSS Metrics [cssfamily]
INFO: Sensor CSS Metrics [cssfamily] (done) | time=111ms
INFO: Sensor CSS Rules [cssfamily]
INFO: 32 source files to be analyzed
INFO: 32/32 source files have been analyzed
INFO: Sensor CSS Rules [cssfamily] (done) | time=1473ms
INFO: Sensor JaCoCo XML Report Importer [jacoco]
INFO: 'sonar.coverage.jacoco.xmlReportPaths' is not defined. Using default locations: target/site/jacoco/jacoco.xml,target/site/jacoco-it/jacoco.xml,build/reports/jacoco/test/jacocoTestReport.xml
INFO: No report imported, no coverage information will be imported by JaCoCo XML Report Importer
INFO: Sensor JaCoCo XML Report Importer [jacoco] (done) | time=3ms
INFO: Sensor JavaScript analysis [javascript]
INFO: 8 source files to be analyzed
INFO: 8/8 source files have been analyzed
INFO: Sensor JavaScript analysis [javascript] (done) | time=6364ms
INFO: Sensor TypeScript analysis [javascript]
...
INFO: ------------- Run sensors on project
INFO: Sensor Zero Coverage Sensor
INFO: Sensor Zero Coverage Sensor (done) | time=29ms
INFO: SCM Publisher SCM provider for this project is: git
INFO: SCM Publisher 8 source files to be analyzed
INFO: SCM Publisher 8/8 source files have been analyzed (done) | time=280ms
INFO: CPD Executor 9 files had no CPD blocks
INFO: CPD Executor Calculating CPD for 102 files
INFO: CPD Executor CPD calculation finished (done) | time=62ms
INFO: Analysis report generated in 75ms, dir size=835.9 kB
INFO: Analysis report compressed in 261ms, zip size=463.9 kB
INFO: Analysis report uploaded in 72ms
INFO: ANALYSIS SUCCESSFUL, you can browse http://internal.sonarqube.wonderlink.cc/dashboard?id=gotone-admin
INFO: Note that you will be able to access the updated dashboard once the server has processed the submitted analysis report

去sonarqube系统上查看检测详情
在这里插入图片描述

在这里插入图片描述




完整的jenkinsfile文件内容如下:

pipeline {
        agent any
        tools{
            nodejs 'node-14.19.2'
        }
        
        //全局变量
        environment { 
            //Harbor仓库地址以及以及镜像所在Harbor项目
         Harbor_Registry_URL= 'rexxxx.net'
         Harbor_Registry_Cert= 'HQT_Harbor_Cert'
         Fat_Harbor_Registry_Project= 'hqt-registry-fat'   //-开发仓库
         Pro_Harbor_Registry_Project= 'hqt-registry-pro'
         
         
            //gitee代码仓库地址以及项目名称
         Gitee_Registry_URL= 'https://xxxxdmin.git'
         Gitee_Code_Project= 'gotxxxxin'
         
         //-SonqrQube信息
         SonarQube_URL= 'http://intxxxxnk.cc'
         SonarQube_Secret= 'bb2fb7c8ad5exxxxxxxbb9653'
         
            //项目的Dockerfile所在目录
         Dockerfile_Directory_path= '/hqtxxxx/Dockerfile'
         
         
           //项目所在kubernetes命名空间(环境)
         Kubernetes_Project_Namespace_fat= 'fat'  //本地环境
         Kubernetes_Project_Namespace_fat1= 'fat1'
         
        }
    parameters {
      gitParameter branch: '', branchFilter: 'origin/(.*)', defaultValue: 'master', description: '选择拉取代码的分支', name: 'Branch', quickFilterEnabled: false, selectedValue: 'NONE', sortMode: 'NONE', tagFilter: '*', type: 'GitParameterDefinition'
      booleanParam  description: '是否进行代码质量检测;', name: 'IS_CODE_DETECTION'
      choice choices: ['faxxxxone.com', 'guxxxxne.com', 'zhangxxxxne.com', 'wuxxxxne.com', 'shaxxxxne.com', 'liuxxxxone.com', 'zouxxxxne.com'], description: '''********************************************
*        请在下方选择接收检测报告的邮箱      *
*        请在下方选择接收检测报告的邮箱      *                        
********************************************''', name: 'RECEIVE_MAILBOX'
      choice(choices: ['fat1','fat','prod'], description: '请选择需要部署的环境', name: 'deploy_env')
      booleanParam  description: '是否部署到fat环境;', name: 'IS_DEPLOY_FAT'
      booleanParam  description: '是否部署到fat1环境;', name: 'IS_DEPLOY_FAT1'
      booleanParam description: '是否将本次构建镜像推送到pro线上仓库;', name: 'Push_Image_To_Pro_Registry'
      choice(name:'Replicase', choices:'1\n3\n5', description:'请选择副本数(如果对此参数不清楚的话,默认即可);' )
      
    }

    stages {
        stage('拉取代码') {
            steps {
                checkout([$class: 'GitSCM', branches: [[name: "${params.Branch}"]], extensions: [], userRemoteConfigs: [[credentialsId: 'Pull-Code_Gitee_fandaoshuai', url: "${Gitee_Registry_URL}"]]])
            }
        }
        
        stage('git merge') {
            steps {
              sh """
                 git remote set-url origin https://fanxxxx:fdsxxxxx@gitee.com/ciecinfo/${env.Gitee_Code_Project}.git
                 git checkout  ${params.Branch}
				 git fetch --all && git reset --hard origin/${params.Branch}
                 git pull
                 git checkout master
                 git pull
                 git checkout  ${params.Branch}
                 git merge master
                 """
                 //git checkout  refs/remotes/origin/${params.Branch}
            }
        }
        
        
        
        stage('init vars '){
            agent any
            steps {
                script{
                    echo "======================"
                    echo "$deploy_env"
                    switch("$deploy_env"){
                    
                    case "fat":
                        println "This is fat"
                        //OPERATION_URL = "http://internal.fat-1.operation-server.wonderlink.cc"
                        env_vue = "fat"
                    break
                    
                    case "prod":
                        println "This is prod"
                        //OPERATION_URL = "http://merchant-server.cngotone.com"
                        env_vue = "prod"
                    break
                    
                    case "fat1":
                        println "This is fat"
                        //OPERATION_URL = "http://internal.fat-1.operation-server.wonderlink.cc"
                        env_vue = "fat1"
                    break
                    
                    default:
                        echo "############ fat Job name ############"
                        println "This is fat"
                        //OPERATION_URL = "http://merchant-server.ciecdev.com"
                        env_vue = "fat"
                    break
                }
                }
            }
        }
        
        stage('质量检测') {
            when { expression { return params.IS_CODE_DETECTION } }
            steps {
                script{
                    scannerHome = tool 'sonar-scanner'
                }
                    withSonarQubeEnv('SonarQube') {
                        sh"""
                          npm install
                          npm run lint:report
    
                          ${scannerHome}/bin/sonar-scanner \
                          -Dsonar.projectKey=${Gitee_Code_Project} \
                          -Dsonar.sources=. \
                          -Dsonar.host.url=${SonarQube_URL} \
                          -Dsonar.login=${SonarQube_Secret} \
                          -Dsonar.eslint.reportPaths=report.json
                          
                          cd .scannerwork
                          ls 
                          echo "${JOB_NAME}-第${BUILD_NUMBER}次代码扫描报告" > mail.txt
                          echo "项目名称 : ${JOB_NAME}" >> mail.txt
                          echo "构建编号 : 第${BUILD_NUMBER}次构建" >> mail.txt
                          echo "代码路径 : ${Gitee_Registry_URL}" >> mail.txt
                          echo "构建日志 : ${BUILD_URL}consoleText" >> mail.txt
                          echo "构建Url : ${JOB_URL}" >> mail.txt
                          echo "工作目录 : ${WORKSPACE}" >> mail.txt
                          ls
                          cat mail.txt  | mail -s "SonarQube 检测报告" -a *.pdf ${RECEIVE_MAILBOX}
                        """
                }
            }
        }
        
        stage('代码编译') {
            steps {
                sh """
                npm cache clear --force
                npm config set registry https://registry.npm.taobao.org
                npm run build:${env_vue}
                """
                //npm run build:${env_vue}
                }
            }
        
                
        
        stage('构建并上传到测试仓库') {
            steps {
                withCredentials([usernamePassword(credentialsId: "${Harbor_Registry_Cert}", passwordVariable: 'password', usernameVariable: 'username')]) {
                sh """
                   cp -pr /hqtbj/hqtwww/Dockerfile/${JOB_NAME}/Dockerfile ${WORKSPACE}/Dockerfile
                   docker login -u ${username} -p ${password} ${env.Harbor_Registry_URL}
                   docker build -t ${Harbor_Registry_URL}/${Fat_Harbor_Registry_Project}/${Gitee_Code_Project}:F-${BUILD_NUMBER}-${BUILD_TIMESTAMP} -f ${Dockerfile_Directory_path} .
                   docker push ${Harbor_Registry_URL}/${Fat_Harbor_Registry_Project}/${Gitee_Code_Project}:F-${BUILD_NUMBER}-${BUILD_TIMESTAMP}
                   """
                }
            }
        }
        
        //-部署到fat环境
        stage('部署到测试环境') {
            when { expression { return params.IS_DEPLOY_FAT } }
            steps {
                sh "kubectl set image deployment.apps/${Gitee_Code_Project} ${Gitee_Code_Project}=${Harbor_Registry_URL}/${Fat_Harbor_Registry_Project}/${Gitee_Code_Project}:F-${BUILD_NUMBER}-${BUILD_TIMESTAMP} -n ${Kubernetes_Project_Namespace_fat} --kubeconfig=/root/.kube/aliyun-fat.config"
                sh "kubectl scale deployment.apps/${Gitee_Code_Project} --replicas=${params.Replicase} -n ${Kubernetes_Project_Namespace_fat} --kubeconfig=/root/.kube/aliyun-fat.config"
            }
        }
		
		//-部署到fat1环境
        stage('部署到测试-1环境') {
            when { expression { return params.IS_DEPLOY_FAT1 } }
            steps {
                sh "kubectl set image deployment.apps/${Gitee_Code_Project} ${Gitee_Code_Project}=${Harbor_Registry_URL}/${Fat_Harbor_Registry_Project}/${Gitee_Code_Project}:F-${BUILD_NUMBER}-${BUILD_TIMESTAMP} -n ${Kubernetes_Project_Namespace_fat1} --kubeconfig=/root/.kube/aliyun-fat.config"
                sh "kubectl scale deployment.apps/${Gitee_Code_Project} --replicas=${params.Replicase} -n ${Kubernetes_Project_Namespace_fat1} --kubeconfig=/root/.kube/aliyun-fat.config"
            }
        }
        
        stage('将本次构建的镜像传到pro仓库') {
              when { expression { return params.Push_Image_To_Pro_Registry } }
            steps {
                withCredentials([usernamePassword(credentialsId: "${Harbor_Registry_Cert}", passwordVariable: 'password', usernameVariable: 'username')]) {
				sh """
                   docker login -u ${username} -p ${password} ${env.Harbor_Registry_URL}
				   docker tag  ${Harbor_Registry_URL}/${Fat_Harbor_Registry_Project}/${Gitee_Code_Project}:F-${BUILD_NUMBER}-${BUILD_TIMESTAMP} ${Harbor_Registry_URL}/${Pro_Harbor_Registry_Project}/${Gitee_Code_Project}:P-${BUILD_NUMBER}-${BUILD_TIMESTAMP}
				   docker push ${Harbor_Registry_URL}/${Pro_Harbor_Registry_Project}/${Gitee_Code_Project}:P-${BUILD_NUMBER}-${BUILD_TIMESTAMP}
				   """  
                }     
            }
        }
        
        stage('清除本地镜像') {
            steps {
                sh "docker rmi -f ${Harbor_Registry_URL}/${Fat_Harbor_Registry_Project}/${Gitee_Code_Project}:F-${BUILD_NUMBER}-${BUILD_TIMESTAMP}"
				sh "docker rmi -f ${Harbor_Registry_URL}/${Pro_Harbor_Registry_Project}/${Gitee_Code_Project}:P-${BUILD_NUMBER}-${BUILD_TIMESTAMP}"
                
            }
        }
        
   }
}

Logo

前往低代码交流专区

更多推荐