GitHub Actions で複数行の文字列を output にセットする方法

GitHub

GitHub Actions で改行文字を含む複数行の文字列をコマンドの output パラメータにセットする方法についてです。

追記

2022/11/16

output パラメータをセットする方法が変更されたので記述を更新しました。

参考:

前提

この記事は 2022 年 05 月に書きました。 時間が経つと GitHub Actions の仕様変更などで情報が古くなるので参考にされる際は注意してください。

問題

通常 GitHub Actions では ::set-output name={name}::{value} というフォーマットの文字列を標準出力に出力すると value の部分がコマンドの output パラメータとして認識されます。

たとえば、次のように書くと action_fruit という名前の output パラメータに strawberry という文字列がセットされます。

run: |
  name=action_fruit
  value=strawberry
  echo "${name}=${value}" >> $GITHUB_OUTPUT
shell: bash

しかし、この方法を使うと、 value の中に改行文字が含まれている場合、 output パラメータには最初の行のみがセットされてしまいます。 たとえば、 Hello\nWorld という文字列を value にセットすると、 output パラメータには Hello だけがセットされます:

run: |
  name=greeting
  value="Hello\nWorld"
  echo "${name}=${value}" >> $GITHUB_OUTPUT
shell: bash
id: my_command
# => `steps.my_command.outputs.greeting` には `Hello` だけが含まれる

関連イシューなど:

対応方法

この改行問題を回避して複数行の文字列を output にセットする方法がいくつかあります:

  • 方法 A) エスケープしてから出力する
  • 方法 B) output のかわりに環境変数を使う
  • 方法 C) @actions/corecore.setOutput() を使う

後述しますが、私はシンプルな方法 C) を選びました。

方法 A) エスケープしてから出力する

標準出力に出力する前に value 内に含まれる改行文字などをエスケープします。

Bash スクリプトの場合は次のような処理を行うことになります:

value="${value//'%'/'%25'}"
value="${value//$'\n'/'%0A'}"
value="${value//$'\r'/'%0D'}"

参考:

方法 B) output のかわりに環境変数を使う

output パラメータは使わずかわりに環境変数を使って後のステップに値を渡すというアプローチもあります。 環境変数のセットには $GITHUB_ENV を使用します。 $GITHUB_ENV ではヒアドキュメント風の書き方をすれば複数行の文字列を変数にセットできます。

次のサンプルでは、改行文字を含みうる curl コマンドの出力を JSON_RESPONSE という環境変数にセットしています( GitHub 公式ドキュメントのサンプルです):

steps:
  - name: Set the value in bash
    id: step_one
    run: |
      echo 'JSON_RESPONSE<<EOF' >> $GITHUB_ENV
      curl https://example.lab >> $GITHUB_ENV
      echo 'EOF' >> $GITHUB_ENV

参考:

方法 C) @actions/corecore.setOutput() を使う

NPM パッケージ @actions/corecore.setOutput() を使うというアプローチもあります。 @actions/core のコードとドキュメントは actions/toolkit リポジトリの中にあります:

core.setOutput() はまさに GitHub Actions において output パラメータをセットするための関数です。

core.setOutput(name, value)

core.setOutput() に渡した文字列は内部で自動的に A) と同様のエスケープ処理(↓)が行われた後に標準出力に出力されます。

function escapeData(s: any): string {
  return toCommandValue(s)
    .replace(/%/g, '%25')
    .replace(/\r/g, '%0D')
    .replace(/\n/g, '%0A')
}

core.setOutput() を使う場合、利用側は改行問題を気にして小技を駆使する必要がなくなります。

@actions/core を手軽に利用したいときは公式の @actions/github-script アクションが便利です。

@actions/github-script はたとえば次のような形で使用します:

# output `greeting` に 'Hello\nWorld' をセットする
- uses: actions/github-script@v6
  with:
    script: |
      const name = 'greeting'
      const value = 'Hello\nWorld'
      core.setOutput(name, value)

なお、 actions/github-script を使う場合は、 output パラメータに値をセットする方法として core.setOutput() を使う方法の他にも return でオブジェクトを返す方法も利用可能です。 詳細に興味のある方は actions/github-script の README を覗いてみてください。

A) B) の方法でもできないことはないですが、個人的には C) の方法が最もシンプルでよいのではないかと思います。

以上です。

おまけ

「コマンドを 1 つ実行してその結果を output にセットする」というパターンは頻出な気がするので、そのためだけのかんたんなカスタムアクションを作りました:


アバター
後藤隼人 ( ごとうはやと )GitHubPython

ウェブ制作・開発やマーケティング、プロジェクト支援などをしています。