github.com/jaylevin/jenkins-library@v1.230.4/vars/checksPublishResults.groovy (about)

     1  import static com.sap.piper.Prerequisites.checkScript
     2  
     3  import com.cloudbees.groovy.cps.NonCPS
     4  
     5  import com.sap.piper.GenerateDocumentation
     6  import com.sap.piper.ConfigurationHelper
     7  import com.sap.piper.MapUtils
     8  import com.sap.piper.Utils
     9  
    10  import groovy.transform.Field
    11  
    12  @Field def STEP_NAME = getClass().getName()
    13  
    14  @Field Set TOOLS = [
    15      /**
    16       * Allows to publish the check results.
    17       * @possibleValues `true`, `false`, `Map`
    18       */
    19      'aggregation',
    20      /**
    21       * Searches and publishes TODOs in files with the [Task Scanner Plugin](https://wiki.jenkins-ci.org/display/JENKINS/Task+Scanner+Plugin).
    22       * @possibleValues `true`, `false`, `Map`
    23       */
    24      'tasks',
    25      /**
    26       * Publishes PMD findings with the [PMD plugin](https://plugins.jenkins.io/pmd).
    27       * @possibleValues `true`, `false`, `Map`
    28       */
    29      'pmd',
    30      /**
    31       * Publishes CPD findings with the [DRY plugin](https://plugins.jenkins.io/dry).
    32       * @possibleValues `true`, `false`, `Map`
    33       */
    34      'cpd',
    35      /**
    36       * Publishes Findbugs findings with the [Findbugs plugin](https://plugins.jenkins.io/findbugs).
    37       * @possibleValues `true`, `false`, `Map`
    38       */
    39      'findbugs',
    40      /**
    41       * Publishes Checkstyle findings with the [Checkstyle plugin](https://plugins.jenkins.io/checkstyle).
    42       * @possibleValues `true`, `false`, `Map`
    43       */
    44      'checkstyle',
    45      /**
    46       * Publishes ESLint findings (in [JSLint format](https://eslint.org/docs/user-guide/formatters/)) with the [Warnings plugin](https://plugins.jenkins.io/warnings).
    47       * @possibleValues `true`, `false`, `Map`
    48       */
    49      'eslint',
    50      /**
    51       * Publishes PyLint findings with the [Warnings plugin](https://plugins.jenkins.io/warnings), pylint needs to run with `--output-format=parseable` option.
    52       * @possibleValues `true`, `false`, `Map`
    53       */
    54      'pylint'
    55  ]
    56  
    57  @Field Set GENERAL_CONFIG_KEYS = []
    58  @Field Set STEP_CONFIG_KEYS = TOOLS.plus([
    59      /**
    60       * If it is set to `true` the step will archive reports matching the tool specific pattern.
    61       * @possibleValues `true`, `false`
    62       */
    63      'archive',
    64      /**
    65       * If it is set to `true` the step will fail the build if JUnit detected any failing tests.
    66       * @possibleValues `true`, `false`
    67       */
    68      'failOnError'
    69  ])
    70  @Field Set PARAMETER_KEYS = STEP_CONFIG_KEYS
    71  
    72  /**
    73   * This step can publish static check results from various sources.
    74   */
    75  @GenerateDocumentation
    76  void call(Map parameters = [:]) {
    77      handlePipelineStepErrors (stepName: STEP_NAME, stepParameters: parameters) {
    78  
    79          def script = checkScript(this, parameters) ?: this
    80  
    81          prepare(parameters)
    82          String stageName = parameters.stageName ?: env.STAGE_NAME
    83          // load default & individual configuration
    84          Map configuration = ConfigurationHelper.newInstance(this)
    85              .loadStepDefaults([:], stageName)
    86              .mixinGeneralConfig(script.commonPipelineEnvironment, GENERAL_CONFIG_KEYS)
    87              .mixinStepConfig(script.commonPipelineEnvironment, STEP_CONFIG_KEYS)
    88              .mixinStageConfig(script.commonPipelineEnvironment, stageName, STEP_CONFIG_KEYS)
    89              .mixin(parameters, PARAMETER_KEYS)
    90              .use()
    91  
    92          if (configuration.aggregation && configuration.aggregation.active != false){
    93              error "[ERROR] Configuration of the aggregation view is no longer possible. Migrate any thresholds defined here to tool specific quality gates. (piper-lib/${STEP_NAME})"
    94          }
    95  
    96          new Utils().pushToSWA([
    97              step: STEP_NAME,
    98              stepParamKey1: 'scriptMissing',
    99              stepParam1: parameters?.script == null
   100          ], configuration)
   101  
   102          // JAVA
   103          if(configuration.pmd.active) {
   104            report(pmdParser(createToolOptions(configuration.pmd)), configuration.pmd, configuration.archive)
   105          }
   106          if(configuration.cpd.active) {
   107            report(cpd(createToolOptions(configuration.cpd)), configuration.cpd, configuration.archive)
   108          }
   109          if(configuration.findbugs.active) {
   110            report(findBugs(createToolOptions(configuration.findbugs, [useRankAsPriority: true])), configuration.findbugs, configuration.archive)
   111          }
   112          if(configuration.checkstyle.active) {
   113            report(checkStyle(createToolOptions(configuration.checkstyle)), configuration.checkstyle, configuration.archive)
   114          }
   115          // JAVA SCRIPT
   116          if(configuration.eslint.active) {
   117            report(esLint(createToolOptions(configuration.eslint)), configuration.eslint, configuration.archive)
   118          }
   119          // PYTHON
   120          if(configuration.pylint.active) {
   121            report(pyLint(createToolOptions(configuration.pylint)), configuration.pylint, configuration.archive)
   122          }
   123          // GENERAL
   124          if(configuration.tasks.active) {
   125            report(taskScanner(createToolOptions(configuration.tasks, [
   126                includePattern: configuration.tasks.get('pattern'),
   127                highTags: configuration.tasks.get('high'),
   128                normalTags: configuration.tasks.get('normal'),
   129                lowTags: configuration.tasks.get('low'),
   130            ]).minus([pattern: configuration.tasks.get('pattern')])), configuration.tasks, configuration.archive)
   131          }
   132          if (configuration.failOnError && 'FAILURE' == script.currentBuild?.result){
   133              script.currentBuild.result = 'FAILURE'
   134              error "[${STEP_NAME}] Some checks failed!"
   135          }
   136      }
   137  }
   138  
   139  def report(tool, settings, doArchive){
   140      def options = createOptions(settings).plus([tools: [tool]])
   141      echo "recordIssues OPTIONS: ${options}"
   142      // publish
   143      recordIssues(options)
   144      // archive check results
   145      archiveResults(doArchive && settings.get('archive'), settings.get('pattern'), true)
   146  }
   147  
   148  def archiveResults(archive, pattern, allowEmpty){
   149      if(archive){
   150          echo "[${STEP_NAME}] archive ${pattern}"
   151          archiveArtifacts artifacts: pattern, allowEmptyArchive: allowEmpty
   152      }
   153  }
   154  
   155  @NonCPS
   156  def createOptions(settings){
   157      Map result = [:]
   158      result.put('blameDisabled', true)
   159      result.put('enabledForFailure', true)
   160      result.put('aggregatingResults', false)
   161  
   162      def qualityGates = []
   163      if (settings.qualityGates)
   164          qualityGates = qualityGates.plus(settings.qualityGates)
   165  
   166      // handle legacy thresholds
   167      // https://github.com/jenkinsci/warnings-ng-plugin/blob/6602c3a999b971405adda15be03979ce21cb3cbf/plugin/src/main/java/io/jenkins/plugins/analysis/core/util/QualityGate.java#L186
   168      def thresholdsList = settings.get('thresholds', [:])
   169      if (thresholdsList) {
   170          for (String status : ['fail', 'unstable']) {
   171              def thresholdsListPerStatus = thresholdsList.get(status, [:])
   172              if (thresholdsListPerStatus) {
   173                  for (String severity : ['all', 'high', 'normal', 'low']) {
   174                      def threshold = thresholdsListPerStatus.get(severity)
   175                      if (threshold == null)
   176                          continue
   177                      threshold = threshold.toInteger() + 1
   178                      def type = "TOTAL"
   179                      if(severity != 'all')
   180                          type += "_" + severity.toUpperCase()
   181                      def gate = [threshold: threshold, type: type, unstable: status == 'unstable']
   182                      echo "[WARNING] legacy threshold found, please migrate to quality gate (piper-lib/checksPublishResults)"
   183                      echo "legacy threshold transformed to quality gate: ${gate}"
   184                      qualityGates = qualityGates.plus([gate])
   185                  }
   186              }
   187          }
   188      }
   189  
   190      result.put('qualityGates', qualityGates)
   191      // filter empty values
   192      result = result.findAll {
   193          return it.value != null && it.value != ''
   194      }
   195      return result
   196  }
   197  
   198  @NonCPS
   199  def createToolOptions(settings, additionalOptions = [:]){
   200      Map result = [pattern: settings.get('pattern')]
   201      if (settings.id)
   202          result.put('id', settings.id)
   203      if (settings.name)
   204          result.put('name', settings.name)
   205      result = result.plus(additionalOptions)
   206      // filter empty values
   207      result = result.findAll {
   208          return it.value != null && it.value != ''
   209      }
   210      return result
   211  }
   212  
   213  def prepare(parameters){
   214      // ensure tool maps are initialized correctly
   215      for(String tool : TOOLS){
   216          parameters[tool] = toMap(parameters[tool])
   217      }
   218      return parameters
   219  }
   220  
   221  def toMap(parameter){
   222      if(MapUtils.isMap(parameter))
   223          parameter.put('active', parameter.active == null?true:parameter.active)
   224      else if(Boolean.TRUE.equals(parameter))
   225          parameter = [active: true]
   226      else if(Boolean.FALSE.equals(parameter))
   227          parameter = [active: false]
   228      else
   229          parameter = [:]
   230      return parameter
   231  }