web-dev-qa-db-ja.com

このJenkinsfileでstash / unstashが機能しないのはなぜですか?

オンサイトで実行しているJenkinsサーバーがあり、Jenkinsfileを使用して、並列テストエグゼキュータープラグインを使用するパイプラインを管理し、複数のエージェントですべてのJUnitテストを実行してテストを高速化します。ブレードサーバーを作成し(購入するよりもずっと安い!)、テストを2時間近くから22分に短縮しました。 JUnitプラグインは、並列テストに最適です。

ただし、Jacocoプラグインはサポートしていません。そのため、カバレッジファイルを1つのファイルにマージして、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: '[email protected]: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

スクリプトのこの部分から名前をエコーするだけのエコーステートメントです。

    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"

実際のスタッシングはノードでは実行されず、リモートノードで実行されてもパイプラインとしてリストされます。マスターでスタッシュが行われることを示すものを見てきましたが、実際にはそのディレクトリが存在する場所ではありません。

[[今後の編集]]-勧告に感謝します。

Masterのjobs/jdesigner/builds/1639/stashes /ディレクトリには、適切なjacoco#.execファイルを含むcoverage#.tar.gzファイルがあります。 unstashの周りにtry catchを配置すると:

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
6
vextorspace

TLDR:これは、使用されたキーitSerializableではなかったため、反復スタイルが問題を引き起こしたこの問題のケースでした。

これをデバッグするのが難しいのは、おそらく この問題 が原因で、エラーメッセージが適切に報告されなかったことです。コードで例外をキャッチし、「手動」レポートで修正しました。

実際の問題は、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}"
}

最初は、稼働中のノードはリモートノードに格納され、格納されていないのに対し、稼働していないノードはリモートノードに格納されているが、マスターノードでそれを格納しようとします(自然に見つかりません)。

しかし、そうではありませんでした。 this によると、

スレーブにファイルを格納すると、ファイルはマスターに送信されます。ファイルは、stashフォルダーの下の関連するビルドフォルダーのJobフォルダーに保存されます。各スタッシュはtarファイルとして保存されます。これらのファイルは、ビルドの最後に削除されます。

そのため、マスターとリモートの分離は違いを生じないはずです。さらに、スタッシュが見つからない場合は、 ソースから参照"No such saved stash ‘" + name + "’AbortException javadoc "この例外がキャッチされると、指定されたメッセージが報告されます。"それは明らかに起きていません。

代わりに、try-catchブロックを使用してデバッグし、ビルドを壊している実際の例外を見つけてください。

デフォルトで適切に報告されない理由については、 この問題 :「ビルドログで適切に報告されないフロー終了時のシリアル化エラー、Jenkinsログのみ」があります。バグレポートは「修正済み」と主張していますが、明らかに新しいバージョンでは、この動作のsomeテストが問題を引き起こさなかったため、存在します。

エラーメッセージがキャッチされると、問題は this であることがわかります。それを渡すときに、シリアル化できないキーをシリアル化しようとしていました。

7
eis

Jenkinsで並列プロセスを使用する場合、2つの可能性があります。

  1. stash name: "coverage$index", includes: "build/test/jacoco/jacoco${index}.exec"masterノードで完了する前に、1つ(または複数)のプロセスでアンスタッシュしようとしている可能性があります

  2. プロセス間で名前の競合が発生する場合があります。

説明するには(2):

プロセス1は、stashed_filesというスタッシュを作成します

同じ名前stashed_filesで2つのスタッシュを処理してから、正常にアンスタッシュします。 stashed_filesが削除されました。

プロセス1はstashed_filesのアンスタッシュを試みます。 stashed_filesがプロセス2によって削除されたため、アンスタッシング中にエラーが発生します。

この問題を回避するための便利なGroovyコードがいくつかあります この質問

0
Nick