GitHub Actions の run ではパイプの終端の結果がジョブのステータスになる

GitHub Actions でテスト実行を tee で標準出力しつつファイルに出力するようにしました。

jobs:
  build:
    name: Build
    runs-on: ubuntu-latest
    steps:
      - name: Test & report
        run: |
          go test . -v 2>&1 | tee result.txt
           :

出力されたファイルをレポーティング用のツールで加工してテスト結果レポートなどの成果物を作成したいためです。

しかし、テストが失敗してもワークフローが success() になってしまいます。

        run: |
          go test . -v 2>&1; test $? -ne 0 && exit 1 | tee result.txt

とかやるとテスト失敗したらfailure() にはなりますが、意図した内容がファイルに出力されません。

次のようにリダイレクトではちゃんとエラーになってくれます。

        run: |
          go test . -v 2>&1 > result.txt
           :

GitHub Actions の run ではコマンドパイプの途中の処理でエラーになっても終端の処理が成功しているとジョブステータスも success() になってしまうようです。ワンライナーで色々やりたい場合、ちょっと不便ですね。

テストの失敗はジョブのステータスとして検出したいのでテスト実行のみの step とテスト&レポート作成の step に分割しました。

jobs:
  build:
    name: Build
    runs-on: ubuntu-latest
    steps:
      - name: Test
        run: |
          go test . -v

      - name: Test & report
        run: |
          go test . -v 2>&1 | tee result.txt
          :

この場合 test が失敗するとワークフロー自体は終了するので後続のテスト&レポート作成 step は実行されません。失敗成功にかかわらず後続の step を実行したい場合は、実行条件に always() を指定することで実行を強制可能です。

jobs:
  build:
    name: Build
    runs-on: ubuntu-latest
    steps:
      - name: Test
        run: |
          go test . -v

      - name: Test & report
        if: always()
        run: |
          go test | tee result.txt
          :

後続の step が成功しても、それ以前の step がエラーになっていればジョブ自体は failure() になります。

help.github.com

テストを2回実行するコストが許容できない場合、テスト結果をファイルにリダイレクトして後続の step でログ出力するというやり方も考えられます。

jobs:
  build:
    name: Build
    runs-on: ubuntu-latest
    steps:
      - name: Test & report
        run: |
          go test . -v 2>&1 > result.txt
          :

      - name: Print test result
        if: always()
        run: cat result.txt

この方法だとエラーが発生してもログ出力の step は成功するためログがどこに出ているのかわかりづらいという問題はあります。

f:id:kondoumh:20200119163222p:plain