ニッチな条件のバグ踏み抜いたのでメモ
TL;DR
- 同じものを踏んだ場合は、以下のどれかで対処する。
-
Write-Host
、Write-Information
を利用しない、Write-Warning
等で代替する。 -
returnStdOut
の場合はログを諦める。 - PowerShellが直るのを待つ。
- Jenkinsを64bit版に変更する。
-
環境
OS/MW | バージョン |
---|---|
Windows Server | 6.3.9600.0(2012 R2) |
Jenkins | 2.73.3(32bit) |
PowerShell | 5.0.10586.117 |
何が起きたか
JenkinsからPowerShellに連携して、PowerShellの途中でOutputとは別にログを表示しようとした時に
Format-List
されて出力されていた。
node {
def out = powershell returnStdout: true, script: 'Write-Host "Hello World."; "some output"'
echo "PSResult = ${out}"
}
Hello World.
PSResult = some output
WriteInformationStream : True
MessageData : Hello World.
Source : Write-Host
TimeGenerated : 2017/11/29 20:43:20
Tags : {PSHOST}
User : ****\****
Computer : ****
ProcessId : 131196
NativeThreadId : 21572
ManagedThreadId : 14
PSResult = some output
なんか Format-List
かかってるらしい!?
原因
簡単に上げると以下の感じで。
- JVM + Jenkinsを32ビットで構築していたため、PowerShellも32ビットで動作していた
- 出力内容を返すオプションを付けるとInformationのストリームリダイレクトが使われていた
PowerShell側の原因
32ビット版PowerShellにリダイレクトでフォーマットが変わるバグがあるらしい。
https://github.com/PowerShell/PowerShell/issues/2147
まさにこの挙動だった。
試したところ 6(Information) だとダメ、3~5(Warning,Verbose,Debug)だと大丈夫だったので
まさにピンポイントで踏んだらしい。
64ビット版の PowerShell ではこれが発生しないので開発チームでも優先度は低そうで、
github上でも post-6.0.0
としてタグが付いているので、直るのはまだ来年まではかかりそう。
Jenkins側の原因
アーキテクチャによる問題
構築当初にあまりちゃんと調査できてなかったこともあり、JVMとJenkinsを32ビットバージョンで構築していた。
そのためと思われるがPowerShellの実行も32ビット版が起動されているらしい。
(情報ソースはないので想像含む)
プラグインによる問題
Jenkins の PowerShell Plugin は returnStdOut
を付けると標準出力の結果を戻り値として保持できるが、
その際にstdout以外の各ストリームをOutputにリダイレクトして、ファイルに保存してから表示するらしい。
該当ソースコード
ここで 6>&1
が付いてることによって、32ビットの問題と掛け合わせて見事にバグを踏み抜いている。
対応方法
64ビット環境で構築
一から環境を作るなら、JVMの64ビット版をインストールするとJenkinsが64ビット環境として稼働するらしい(?)
stack overflow
64ビットのJenkinsであればPowerShellも64ビット側が使われるので、一番きれいな方法はこれ。
returnStdOut
を使わない
スクリプト実行をしたいだけで出力結果を変数などに拾う必要がなければ、returnStdOut
を付けないだけでも解決する。
node {
powershell 'Write-Host "Hello World."'
}
これなら渡した内容がそのまま出力される。
information以外のストリームに流す
元々PowerShellからのコンフィグを読みたくて returnStdOut
は必須だったので、今回採用した方法がこれで。
Information以外ならなんでも大丈夫だけど、ErrorはStopかけている、VerboseとDebugは表示すると量が多いので
Warningがまだマシな感じで。
node {
def out = powershell returnStdOut: true, script: '''\
$WarningPreference = "Continue"
Write-Warning "Hello World."
"some output"
'''
echo "PSResult = ${out}"
}
Hello World.
PSResult = some output
気にしない
そもそも余計な表示が増えるだけで他への動作影響はないので、気にしないのもありで。