Diary of a Perpetual Student

Perpetual Student: A person who remains at university far beyond the normal period

GitHub Actions の ::set-output 脱出 上級編 便利ツールでカバーできないケース

::set-output 脱出 進んでますか?

github.blog

エンジニアで GitHub Actions を利用している人はだいたい知っているであろう、 ::set-output::save-state の非推奨化問題。このコマンドを利用していると2023年5月末にはエラーになってしまう予定となっています。

面倒がっているエンジニアのために、巷には便利なツールがあります。

一つは rhysd/actionlint です。::set-output のような非推奨機能を使っていると actionlint の検査に引っかかります。こちらについては過去記事で紹介したのでご覧ください。

blog.arthur1.dev

もう一つは azu/set-env-to-github_env です。このコマンドをインストールして実行すると、workflow ファイルを新しい書き方に書き換えてくれます。便利。

今日は、残念ながらこれらのツールでは引っ掛からない例とその対応策をご紹介します。

スクリプトファイルを呼び出しているところに注意

mackerelio/mackerel-agentGitHub Actions の結果を見ていると、Annotation で warning が大量に表示されていることに気づきました。利用している action の更新をしたり、actionlint を実行して非推奨な記述を見つけたりして修正したのですが、まだ ::set-output 使っているよ、という warning が表示されていました。

actionlint に引っ掛からなかったのに出るのはおかしいな、と思って、::set-output で文字列検索を掛けたところ、こんなコードが引っ掛かりました

(async function () {
  const version = "1.2.3"; // ここは本当は parse してアレコレしている
  console.log("::set-output name=VERSION::" + version);
})();

そして、この Node.js のスクリプトを Workflow 中で呼び出していました。

- run: node _tools/parse_version.js

なるほど!workflow から呼び出した外部のスクリプトが標準出力に ::set-output と書き込んでいたのに actionlint だと気づけなかった、というわけです。これは先ほど紹介した set-env-to-github_env でも無理だと思います。

解決策: actions/github-script を咬ます

先程のスクリプトを直すのには工夫がいります。新しいやり方では標準出力は利用せず専用のファイルに書き込まなければならないので、 console.log() を用いることはできません。例えば、以下のような方法で新しい方法に対応することができます。

const fs = require('fs/promises')

(async function () {
  const version = "1.2.3";
  const file = process.env.GITHUB_OUTPUT;
  if (file === undefined) {
    process.exit(1);
  }
  await fs.appendFile(file, `VERSION=${version}`);
})();

これは美しくないし、今後さらに変更があったときに見つけづらくなるなあと悩んでいたところ、 id:stefafafan さんが @actions/coresetOutput() という関数があるよ、と教えてくれました。これなら、仮にさらに新たな仕組みになったとしてもライブラリの更新に追従すればよいので良さそうです。

しかし、この npm ライブラリを利用するためだけにちょっとした js ファイルを bundle して生成するのは大袈裟です。少し調べていたところ、actions/github-script という公式の action に行き着きました。この action を経由して Node.js のスクリプトを動かすと、GitHub API や Workflow context を利用するためのライブラリを利用することができるようになります。

Workflow の yml と呼び出しているスクリプトを以下のように書き換えました。

- use: actions/github-script@v6
  with:
    script: |
      const script = require('./_tools/parse_version.js')
      await script({ core })
module.exports = async ({ core }) => {
  const version = "1.2.3";
  core.setOutput("VERSION", version);
};

すると、無事 warning が消え、かつ workflow の output を設定することができました!

まとめ

GitHub Actions の ::set-output 脱出の際には、以下の事項に気をつけましょう。

  • Workflow 定義外のスクリプトファイルを呼び出して ::set-output と標準出力に書き出しているところに気をつける
  • それが Node.js のスクリプトの場合は、actions/github-script 経由で呼び出すようにすると、setOutput() 関数が用意されたライブラリを利用可能