こんにちは、テクマトリックスの酒井です。
前回の記事で、 Jenkins のパイプラインでbatが実行できないというトラブルとその回避策をご紹介しました。今回はそのトラブルの原因と回避策を深掘りしてみたいと思います。なお本記事の内容は Windows 10 にインストールした Jenkins 2.375.1 にて確認しています。
目次
問題 1 :パイプラインが bat ステップで止まる
前回の記事では、原因を調べる過程で、 Jenkins が生成するラッパースクリプトから、 bat
ステップで指定したスクリプトからなるバッチファイルを見つけることができなかったことが分かりました。そしてその原因は、日本語のジョブ名がバッチファイルのパス名に含まれているためでした。
回避策: Jenkins ジョブ名に日本語を使わない
同じく前回の記事では、 Jenkins のジョブ名を ASCII 文字のみにすることで、バッチファイルのパス名に日本語を含めないようにして回避できました。一方で副作用として、 Jenkins の UI でジョブ名からジョブを識別することが難しくなることが考えられます。
ジョブを識別しやすくする
ジョブ名でジョブを識別することが難しい場合、ジョブの設定で「プロジェクトの高度な設定>表示プロジェクト名」を設定することで改善できます。
表示プロジェクト名は表示のためだけのものですので、ジョブの実行には何の影響も与えません。表示プロジェクト名に分かりやすい名前を指定することで、ジョブを識別しやすくすることができます。
問題 2 :バッチスクリプトに日本語を含めるとコマンドが失敗する
ところで、バッチファイルに日本語が含まれる原因となるものはジョブ名だけではありません。例えば bat
ステップで指定するスクリプトに直接日本語を使用したり、ビルドパラメーターをスクリプトで利用したりしている場合も当てはまります。例えば、次のようなパイプラインを作成します。
pipeline {
agent any
parameters {
string 'buildBatArgument'
}
stages {
stage('ビルド') {
steps {
bat "dir \"${params.buildBatArgument}\""
}
}
}
}
このパイプラインで実行したいことは、ビルドパラメーターで指定したフォルダ内のファイルの一覧を出力することです。パラメーターに日本語を含むフォルダ名を入力して実行してみます。
ビルドボタンをクリックしてパイプラインを実行し、コンソールの出力を確認します。
bat
ステップの出力を見ると、 dir
コマンドの引数のフォルダ名が文字化けしてしまい、指定したフォルダを見つけることができませんでした。この原因はラッパースクリプトと同様で、 bat
ステップで指定したバッチスクリプトも UTF-8 でエンコーディングされるためです。今回は日本語を含むフォルダ名を指定したいので、パラメーターに日本語を含めないという回避策はとれません。
回避策 1 :バッチファイルをパイプラインで作成してから呼び出す
bat
ステップに直接スクリプトを記述する代わりに、 writeFile
ステップを使用していわゆるシフト JIS (java.io API 用の正準名では MS932
)でエンコーディングされたバッチファイルを作成し、次に bat
ステップからそのバッチファイルを呼び出すという 2 つのステップに置き換えることで問題を回避できます。パイプラインコードは次のようになります。
pipeline {
agent any
parameters {
string 'buildBatArgument'
}
stages {
stage('ビルド') {
steps {
writeFile encoding: 'MS932', file: 'build.bat', text: "dir \"${params.buildBatArgument}\""
bat "build.bat"
}
}
}
}
同じパラメーターでパイプラインを実行してコンソールの出力を確認します。
バッチスクリプトが期待どおりに実行されたことが確認できました。ただし副作用として、パイプラインに writeFile
ステップが増えるのと、実行時にワークスペースに余計なバッチファイルが作成されます。
回避策 2 : PowerShell のスクリプトとして実行する
最近の Windows には PowerShell が付属しており、追加のインストールをすることなく PowerShell スクリプトを実行できます。さらに外部コマンドを呼び出すだけなら、スクリプトはほぼ変更せずに済みます。 Jenkins で Powershell スクリプトを実行するには、 bat
ステップの代わりに powershell
ステップを使用します。つまり、パイプラインコード内の bat
を powershell
に置き換えるだけです。
pipeline {
agent any
parameters {
string 'buildBatArgument'
}
stages {
stage('ビルド') {
steps {
powershell "dir \"${params.buildBatArgument}\""
}
}
}
}
(今回のスクリプトに関して正確に言うと、コマンドプロンプトにおける dir
は内部コマンドですが、 Windows 10 上の PowerShell では通常 dir
が Get-ChildItem
コマンドレットのエイリアスとして作成済みのため、スクリプトを変更することなく実行できます)
同じパラメーターでパイプラインを実行してコンソールの出力を確認します。
実行は問題なくできたように見えますが、スクリプトの出力が文字化けしてしまいました。
スクリプトの出力の文字化けを回避する
PowerShell ではコンソール出力の文字エンコーディングを指定できます。これを利用して、実行したいスクリプトの出力を一旦変数に保存し、コンソール出力のエンコーディングを UTF-8
に指定した上で変数の内容を出力することで文字化けを回避できます。
pipeline {
agent any
parameters {
string 'buildBatArgument'
}
stages {
stage('ビルド') {
steps {
powershell encoding: 'UTF-8', script: """
[Console]::OutputEncoding = [Text.Encoding]::Default
\$_ = dir "${params.buildBatArgument}"
[Console]::OutputEncoding = [Text.Encoding]::UTF8
Write-Output \$_
"""
}
}
}
}
同じパラメーターでパイプラインを実行してコンソールの出力を確認します。
文字化けすることなくファイルの一覧が出力されました。
どの回避策をとるべきか?
ジョブ名には日本語を使わないのが無難だと思います。ジョブ名を日本語で表示したい場合は、ジョブの表示プロジェクト名を日本語で設定しましょう。
バッチスクリプト内に日本語を含める必要がある場合は、ビルドパラメーターで日本語が入力される場合を除いて、日本語を含むバッチファイルを作成してパイプラインコードからそのバッチファイルを呼び出すようにし、バッチファイルとパイプラインコード(Jenkinsfile)をソースコード管理に登録することをお勧めします。
まとめ
Jenkinsでバッチスクリプトがうまく実行できない問題の回避策をいくつかご紹介しました。どの回避策が適しているかはケースバイケースだと思いますが、同じような問題に悩んでいる方の助けになれば幸いです。