Jenkins パイプラインの stash/unstash を使ってノード間でファイルを転送する
Jenkins のパイプラインで stash/unstash を使って異なるノード間でファイルを転送する方法を紹介します。 英語の stash には、「しまう」という意味があるようです。一時的にファイルをどこかにしまって(stash)、 必要になったら取り出す(unstash) という操作です。
使いどころとしては、
などが考えられます。 便利なのですが、「しまう」という表現のためか、ちょっと抽象化されすぎていて stash/unstash の中で実際に何をしているのか不安だったので、そこらへんも調べてみました。
stash/unstash を使った例
以下のような簡単なシナリオで stash/unstash の動作を確認しましょう。
パイプラインスクリプトは以下のようになります。(スレーブノードの環境設定とノード追加は実施済みとします)
node('master') { deleteDir() git url: 'https://github.com/arasio/simple-gradle-project-with-tests.git' stash name: 'project' sh 'ls -l' } node('slave1') { deleteDir() unstash 'project' sh 'ls -l' sh 'gradle test' }
まずはワークスペースの掃除
各ノードでまず deleteDir()
をしているのは、前回のビルドでワークスペースに残っているゴミを掃除するためです。
上のジョブを1回実行すると、マスターでは git からチェックアウトしたソースコードがそのまま残りますし、
スレーブでも unstash したソースコードがそのまま残ってしまいます。ワークスペースはまっさらにしておいたほうが精神衛生上良いですね。
マスターでソースコードを stash する
git
ステップでソースコードをチェックアウトしたあと、
stash name: 'project'
で現在のディレクトリにあるファイルやディレクトリを stash します。
後で unstash できるように stash するときは名前をつけます。
ここで ls -l
を実行してワークスペースの状態を確認してみると、下のようになっています。
これらのファイルが stash されたことになります。
+ ls -l total 28 -rw-r--r-- 1 jenkins jenkins 1063 Oct 18 12:47 LICENSE -rw-r--r-- 1 jenkins jenkins 253 Oct 18 12:47 build.gradle drwxr-xr-x 3 jenkins jenkins 4096 Oct 18 12:47 gradle -rwxr-xr-x 1 jenkins jenkins 5242 Oct 18 12:47 gradlew -rw-r--r-- 1 jenkins jenkins 2260 Oct 18 12:47 gradlew.bat drwxr-xr-x 4 jenkins jenkins 4096 Oct 18 12:47 src
スレーブでソースコードを unstash する
スレーブ側でもワークスペースを掃除した後、unstash 'project'
を実行すると、さきほど stash したソースコードをスレーブノード上で取り出すことができます。
unstash 後に ls -l
を実行して確認すると、上のマスターのワークスペースと全く同じ状態になっていることがわかります。
ちなみに、ディレクトリは再帰的に処理されますので、src ディレクトリ以下もすべて転送されています。
そのあとはテストを実行するなり、実際にやりたい処理を書くことになるでしょう。
stash/unstash の中では何が起きている?
stash/unstash が実際にどのような処理になっているか知っておくと安心して使えます。 基本的には
という処理になっています。
stash を実行すると対象ノードのカレントディレクトリを tar.gz で圧縮して、ジョブのビルドディレクトリに保存します。 上の例では、stash された圧縮ファイルは以下にできていました。stash するときに 'project' という名前をつけたので project.tar.gz になっています。
${JENKINS_HOME}/jobs/${JOB_NAME}/builds/${BUILD_NUMBER}/stashes/project.tar.gz
このファイルは一時ファイルなので、ビルドが終了すると消えます。掃除は考える必要がありません。 スレーブで stash を実行してもマスターのビルドディレクトリに圧縮ファイルが生成されます。
unstash は stash で退避した圧縮ファイルを対象ノードにコピーして展開します。 注意したいのが展開したファイルの所有者はすべて jenkins ユーザになることです。 圧縮が jenkins ユーザで実行されるので展開すると元の所有者によらず jenkins ユーザになります。
より詳細に知りたい方は Jenkins のソースコードを直接確認すると良いでしょう。私は以下を参考にしました。
- workflow-basic-steps-plugin/StashStep.java at master · jenkinsci/workflow-basic-steps-plugin · GitHub
- workflow-api-plugin/StashManager.java at master · jenkinsci/workflow-api-plugin · GitHub
オプション
stash は基本的にはカレントディレクトリを圧縮するのですが、 いくつかのオプションが用意されています。
- 特定のディレクトリ/ファイルを除外する (excludes)
stash name: 'test', excludes: '**/*.log'
ワイルドカードの表記は ant スタイルに従います。
- 特定のディレクトリ/ファイルのみ stash する (includes)
stash name: 'test', includes: '**/*.java'
- デフォルトで除外されるファイルも stash する (useDefaultExcludes)
デフォルトでは特定の隠しファイルなどは stash されません。上の例では、.git/ や .gitignore などは実は stash されていません。
これらのファイルも含めて stash/unstash したい場合は useDefaultExcludes
を false
に設定します。
stash name: 'test', useDefaultExcludes: false
なお、デフォルトで除外されるファイル一覧は以下のようになっています。
**/*~ **/#*# **/.#* **/%*% **/._* **/CVS **/CVS/** **/.cvsignore **/SCCS **/SCCS/** **/vssver.scc **/.svn **/.svn/** **/.DS_Store **/.git **/.git/** **/.gitattributes **/.gitignore **/.gitmodules **/.hg **/.hg/** **/.hgignore **/.hgsub **/.hgsubstate **/.hgtags **/.bzr **/.bzr/** **/.bzrignore
出典: https://ant.apache.org/manual/dirtasks.html#defaultexcludes
*1:下の記事で実際のサンプルを紹介していますので興味ある方はご覧くださいarasio.hatenablog.com