こんにちは、テクマトリックスの酒井です。

前回の記事で、 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 ステップを使用します。つまり、パイプラインコード内の batpowershell に置き換えるだけです。

pipeline {
  agent any
  parameters {
    string 'buildBatArgument'
  }
  stages {
    stage('ビルド') {
      steps {
        powershell "dir \"${params.buildBatArgument}\""
      }
    }
  }
}

(今回のスクリプトに関して正確に言うと、コマンドプロンプトにおける dir は内部コマンドですが、 Windows 10 上の PowerShell では通常 dirGet-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でバッチスクリプトがうまく実行できない問題の回避策をいくつかご紹介しました。どの回避策が適しているかはケースバイケースだと思いますが、同じような問題に悩んでいる方の助けになれば幸いです。

By tsakai

Jenkins関連のサービスやCloudBees製品を主に担当しています。 Certified CloudBees Jenkins Engineer (CCJE) および CloudBees CI DevOps Associate です。