为什么stash / unstash在这个Jenkins文件中不起作用?

时间:2017-11-29 12:57:22

标签: java git jenkins groovy jacoco

我有一台Jenkins服务器在现场运行,它使用Jenkins文件管理一个管道,该管道使用并行测试执行器插件在几个代理上运行所有JUnit测试以加快测试速度。我们有一个刀片服务器(比买一个便宜!)并且我们的测试从接近2小时到22分钟加速。 JUnit插件适用于并行测试。

然而,Jacoco Plugin却没有。所以我试图将覆盖文件合并到一个文件,以便Jacoco插件可以发布覆盖结果。 Stash / unstash正在存储源代码,但当我尝试存储不同的Jacoco输出文件以在主服务器上取消它们时,它无法正常工作。

任何想法为什么?

这是我的Jenkinsfile:

#!/usr/bin/env groovy

def branch
def hash

node('remote') {
  sh 'echo starting'

  branch = env.gitlabBranch ?: '**'
  echo "Branch: $branch"

  checkout([$class: 'GitSCM',
        branches: [[name: "$branch"]],
        extensions: [
          [$class: 'PruneStaleBranch'],
          [$class: 'CheckoutOption', timeout: 120],
          [$class: 'CloneOption', depth: 0, noTags: true, shallow: true, timeout: 180]
        ],
        doGenerateSubmoduleConfigurations: false,
        submoduleCfg: [],
        userRemoteConfigs: [[credentialsId: 'gitlabLabptop', url: 'git@gitlab.com:protocase/my_project_url.git']]
       ]
      )

  hash = sh (script: 'git rev-parse HEAD', returnStdout: true).trim()

  ### - this stash works fine -###
  stash name: 'sources', includes: '**', excludes: '**/.git,**/.git/**'
}

def numBranches = 9
def splits = splitTests count(numBranches)
def branches = [:]

for (int i = 0; i < splits.size(); i++) {
  def index = i // fresh variable per iteration; i will be mutated

  branches["split${i}"] = {
    timeout(time: 125, unit: 'MINUTES') {
      node('remote') {
    sh 'echo starting a node'
    deleteDir()

    ### - this unstash works fine - ###
    unstash 'sources'

    def exclusions = splits.get(index);
    writeFile file: 'test/exclusions.txt', text: exclusions.join("\n")

    sh 'ant clean'

    sh 'rm -rf build'

    sh 'ant jar'

    sh 'ant -buildfile build-test.xml buildTests'

    sh 'ant -buildfile build-test.xml jenkinsBatch'

    junit 'build/test/results/*.xml'

    sh "mv build/test/jacoco/jacoco.exec build/test/jacoco/jacoco${index}.exec"
    echo "name: coverage$index, unclude jacoco${index}"

       ### - this stash appears to work - ### 
       stash name: "coverage$index", includes: "build/test/jacoco/jacoco${index}.exec"
       echo "stashed"

      }
    }
  }
}

parallel branches


def branchIndecis = 0..numBranches

node('master') {
  if (currentBuild.result != "ABORTED") {

    echo "collecting exec files"

    branchIndecis.each {
      echo "unstash coverage${it}"

      ### !!! this unstash causes an error !!! ###
      unstash name: "coverage${it}"



      echo "make file name"
      def coverageFileName = "build/test/jacoco/jacoco${it}.exec"
      echo "merge file"
      sh "ant -buildfile build-test.xml -Dfile=${coverageFileName} coverageMerge"
    }

    echo "collected exec files"

    step([$class: 'JacocoPublisher',
      execPattern:'build/test/jacoco/jacoco.exec',
      classPattern: 'build/classes',
      sourcePattern: 'src'])

    echo "finishing ${branch} - ${hash}"

  }
}

我得到的输出是:

[split7] [jdesigner] Running shell script
[split7] + mv build/test/jacoco/jacoco.exec build/test/jacoco/jacoco7.exec
[Pipeline] [split7] echo
[split7] name: coverage7, unclude jacoco7
[Pipeline] [split7] stash
[split7] Stashed 1 file(s)
[Pipeline] [split7] echo
[split7] stashed
[Pipeline] [split7] }
[Pipeline] [split7] // node
[Pipeline] [split7] }
[Pipeline] [split7] // timeout
[Pipeline] [split7] }
[Pipeline] // parallel
[Pipeline] node
Running on eightyeight in /var/jenkins/workspace/jdesigner
[Pipeline] {
[Pipeline] echo
collecting exec files
[Pipeline] echo
unstash coverage0
[Pipeline] unstash
[Pipeline] }
[Pipeline] End of Pipeline
Finished: FAILURE

[编辑] coverage0的存储是

[split0] Recording test results
[Pipeline] [split0] sh
[split0] [jdesigner] Running shell script
[split0] + mv build/test/jacoco/jacoco.exec build/test/jacoco/jacoco0.exec
[Pipeline] [split0] echo
[split0] name: coverage0, include jacoco0
[Pipeline] [split0] stash
[split0] Stashed 1 file(s)
[Pipeline] [split0] echo
[split0] stashed
[Pipeline] [split0] }
[Pipeline] [split0] // node
[Pipeline] [split0] }
[Pipeline] [split0] // timeout
[Pipeline] [split0] }
[split3]     [junit] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 18.737 sec
[split3]     [junit] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 18.737 sec

注意行

[split0] name: coverage0, include jacoco0

只是我的echo语句,我从脚本的这一部分回显了这个名字:

    sh "mv build/test/jacoco/jacoco.exec build/test/jacoco/jacoco${index}.exec"
    echo "name: coverage$index, include jacoco${index}"

    stash name: "coverage$index", includes: "build/test/jacoco/jacoco${index}.exec"
    echo "stashed"

请注意,节点上没有进行实际存储,即使在远程节点上完成,也会将其列为管道。我看到的东西表明存储是在主服务器上完成的,但实际上并不存在于该目录所在的位置。

[[进一步编辑]] - 感谢eis的建议。

master上的jobs / jdesigner / builds / 1639 / stashes /目录包含#.tar.gz文件,其中包含相应的jacoco#.exec文件。当我试着抓住unstash:

try {
    unstash name: "coverage${it}"
} catch (error) {
    echo "error unstashing: ${error}"
}

我得到的输出是:

collecting exec files
[Pipeline] echo
unstash coverage0
[Pipeline] unstash
[Pipeline] echo
error unstashing: java.io.NotSerializableException: groovy.lang.IntRange
[Pipeline] echo
make file name

2 个答案:

答案 0 :(得分:6)

TLDR:这是一个问题,迭代样式导致了这个问题,因为使用的密钥it不是Serializable

让调试变得困难的是,错误消息未正确报告,可能是由于this issue。在代码和“手动”报告中捕获异常修复了该问题。

使用Serializable键修复了实际问题。

更长的版本:

因为在你的例子中这是有用的:

node('remote') {
    ### - this stash works fine -###
    stash name: 'sources', includes: '**', excludes: '**/.git,**/.git/**'
}
node('remote') {    
    ### - this unstash works fine - ###
    unstash 'sources'
}

但这不是:

node('remote') {

   ### - this stash appears to work - ### 
   stash name: "coverage$index", includes: "build/test/jacoco/jacoco${index}.exec"
   echo "stashed"

}
node('master') {
   echo "unstash coverage${it}"

   ### !!! this unstash causes an error !!! ###
   unstash name: "coverage${it}"
}

我最初认为工作的是在远程节点上存储和取消,而非工作的存储在远程节点上,但是您尝试在主节点上取消它(当然不会找到它)

但事实并非如此。根据{{​​3}},

  

当您将文件存储在从属文件上时,文件将发送给主文件。   这些文件将存储在相关版本的Job文件夹中   存储文件夹下的文件夹。每个存储将作为tar存储   文件。这些文件将在构建结束时删除。

所以主远程分离不应该有所作为。此外,如果找不到存储,则可this它将失败"No such saved stash ‘" + name + "’,因为根据see from the sources“捕获此异常时,将报告指定的消息“。这显然没有发生。

相反,应该使用try-catch块进行调试,以找出破坏构建的真正异常。

至于默认情况下没有正确报告的原因,有AbortException javadoc:“在流程结束时序列化错误未在构建日志中正确报告,只有Jenkins日志”。错误报告声称它“固定”但显然只是因为在新版本中,某些测试此行为并未触发问题,因此它可能仍然存在。

如果收到错误消息,可以看到问题是this issue - 当我们传递它时,我们试图序列化一个不可序列化的密钥。

答案 1 :(得分:0)

在Jenkins中使用并行进程时有两种可能性:

  1. 您可能尝试在foo$L <- replicate(nrow(foo), sample(foo, 1:foo$K,1)) 节点上stash name: "coverage$index", includes: "build/test/jacoco/jacoco${index}.exec"完成{/ 1}}之前在一个(或多个)进程中解除阻塞

  2. 您可能在进程之间存在名称争用。

  3. 解释(2):

    进程1创建一个名为master

    的存储

    以相同的名称stashed_files处理2个stashes,然后成功释放。 stashed_files已删除。

    进程1尝试释放stashed_files。在卸载过程中出现错误,因为进程2删除了stashed_files

    可以找到解决此问题的一些有用的Groovy代码in this question