GitHub Actions の JavaScript Action
以下を毎回書くのも面倒なので Action の作成を試すことにした。
Container と JavaScript が選べるが軽く調べた感じだと JavaScript Action で大丈夫そうだったので JavaScript で試す。
だめだったら Container にする。
リポジトリ内でコードのファイルがそろっていれば良さそうなのとTypeScript でコードを記述したかったので rollup.js を使ってみたらエラーになる。
エラー
./src/index.ts → ./dist/index.js...
(!) Circular dependencies
node_modules/@actions/core/lib/core.js -> node_modules/@actions/core/lib/oidc-utils.js -> node_modules/@actions/core/lib/core.js
node_modules/@actions/core/lib/core.js -> node_modules/@actions/core/lib/oidc-utils.js -> /home/node/tmp/collect-labels-from-release-note/node_modules/@actions/core/lib/core.js?commonjs-proxy -> node_modules/@actions/core/lib/core.js
[!] Error: 'default' is not exported by node_modules/node-fetch/node_modules/tr46/lib/mappingTable.json, imported by /home/node/tmp/collect-labels-from-release-note/node_modules/node-fetch/node_modules/tr46/lib/mappingTable.json?commonjs-proxy
https://rollupjs.org/guide/en/#error-name-is-not-exported-by-module
/home/node/tmp/collect-labels-from-release-note/node_modules/node-fetch/node_modules/tr46/lib/mappingTable.json?commonjs-proxy (1:8)
ドキュメントでは '@vercel/ncc` を使っていたので今回はそれに従う。
node_modules ディレクトリをチェックインすると、問題が発生する可能性があります。 別の方法として、@vercel/ncc というツールを使用して、コードとモジュールを配布に使用する 1 つのファイルにコンパイルできます。
いちおう動くようになった。
ビルドしたものもリポジトリに含める必要がある。
できれば含めたくないのだけど無理そうかな。
Action は ESM でも動いた。
Node.js の 16.x を指定して動かすからそうだと言われればそうかと。
ncc でのビルド時に 以下の定義のみのdist/package.json
が作成される。
{
"type": "module"
}
テストの書き方。
モジュールに切り出している部分は普通にユニットテストでできる。
action.yml
の main
に設定している部分はビルドされたファイルを Node.js のスクリプトとして実行する。
入力(with
)については環境変数(process.env
)で INPUT_入力名を大文字
を設定することでわたせる。
この場合の問題はモックが使えない(と思う)。よって octokit での通信などはテストできない(こともないが、後述のワークフローでのテストにまかせた方が環境に左右されにくい)
メイン部分の処理をモジュールにしておくことで対応できるが、
サンプルを見た感じでは「実際に Node.js のスクリプトとして動作している状況」でテストすることが主題に感じる。
よって、自分用に作るものはモックを必要としない部分のみのテストに限定する。
通信などが必要な部分は実際にワークフローを使ってのテストを記述する。
上記のテストで __dirname
が必要になるが以下で対応。
上記対応や top-level await などで tsconfig.json
は以下のような感じなっている。
この辺は、おそらく Action としての要件はなく、using
に指定している Node.js に依存すると思う。
{
"compilerOptions": {
"target": "es2020",
"module": "es2022",
"lib": ["ESNext"],
"moduleResolution": "node",
"strict": true,
"skipLibCheck": true,
"declaration": true,
"pretty": true,
"newLine": "lf",
"outDir": "build",
"esModuleInterop": true,
"allowJs": true
},
"exclude": ["node_modules", "src/**/*.test.ts"],
"include": ["src/**/*.ts", "test/**/*.ts"]
}
input
に required
を付けているものをフローで指定しない場合でもとくにエラーにならない。
値としてはブランクを渡されているもよう。
default
の値が影響しているのかと思い default
を外してみたけど変わらず。
コード側でチェックしていからよいけど、使い方間違えている?
inputs:
repository:
description: 'Repository name(owner/name)'
required: true
default: ''
作った。
他のリポジトリからも実行してみた。
その後、Google Drive 用の Action などを作ってみたところ、いくつか追記したいところが出てきたので reopen。
ビルドとタグ
リポジトリにビルドしたコードを含むのは避けたいと考えていた。また、Action を特定するためのタグはメジャーバージョン、マイナーバージョンのものが欲しくなった。
その辺の対応は公式のドキュメントに記述があった。
具体的にはこのワークフロー を参考にJasonEtco/build-and-tag-action を使うと解決できる。
この Action では以下のことを実施してくれる。
- バンドルされた
dist/index.js
(package.json
のmain
で指定したファイル)とaction.yml
のみのコミットを作成 - 上記コミットに対して
v1
v1.1
v1.1.1
のようなタグを付ける
便利なのだが、Action の作り方によっては少し対応が必要。
nativeESM
モジュールとして packge.json を設定していた場合、ncc は最低限必要な dist/package.json
を作成する。しかし JasonEtco/build-and-tag-action では dist/package.json
をコミットに含めない。
これは dist/index.js
を dist/index.mjs
へ mv
することで対応。
licenses.txt
JavaScript Action 作成の手順では licenses.txt
を作成してリポジトリに含めるようになっている。
しかし、このファイルもタグ付けされたコミットには含まれない。
バンドルしたファイルのライセンス表示は含めたいので暫定的に以下のように対応。
-
licenses.txt
をリリースの assets へアップロード -
dist/index.mjs
にライセンスバナーを追加しその中にリンクを含める
- name: Make licenses.zip file
run: zip "./licenses.zip" dist/licenses.txt
- name: Upload licenses.zip file to release Asset
id: upload-release-asset
uses: shogo82148/actions-upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ github.event.release.upload_url }}
asset_path: ./licenses.zip
asset_name: licenses.zip
asset_content_type: application/zip
- name: Prepend license banner into dist/index.mjs
run: |
bash scripts/license-banner.sh "${DOWNLOAD_URL}" | cat - dist/index.mjs > tmp.txt
mv tmp.txt dist/index.mjs
env:
DOWNLOAD_URL: ${{ steps.upload-release-asset.outputs.browser_download_url }}
構文を考慮しないで実行コードを書き換えているのでできれば避けたいところだけど。。。
あとは直接関係ないけれど、actions/upload-release-asset が Archived になっていたので、shogo82148/actions-upload-release-asset を利用させていただく。
とはいっても inputs はそのままで動作したので uses
を変更しただけ。
README.md の自動生成
Action の README は大まかにフォーマットが提示されていて、内容的には action.yml
からコピペでだいたい記述できる。
が、inputs が増えてくると非常に手間がかかるので、以下を利用させていただく。
Action 版もあるのだけど、手元で確認しながら実行したかったので通常版(?) を利用。
また、README.md
全体生成するようになっているのだけど、action.yml
にはない情報(利用する環境変数など)を手動で追記したかったので、別途テンプレートを作成して合成するようにしてある。
「README.md
をビルドしてリポジトリに含める」ことになるので「ビルド漏れ」が生じる可能性がある(というか、実際に結構忘れて頭をかかえた)。
テスト用のワークフローに以下を追加した。
- name: Diff built files
run: |
git diff --exit-code --quiet README.md
ビルドを忘れているとテストが落ちる。
Marketplace への公開
基本的には以下の手順通り。
少しハマったところなど。
同意
Select Publish this action to the GitHub Marketplace. If you can't select the Publish this action to the GitHub Marketplace checkbox, you'll need to read and accept the GitHub Marketplace agreement first.
手順通りの流れで全文を表示させると小さいポップアップで表示されるので、
事前に「GitHubマーケットプレイス契約」などで検索して見ておいた方がよい。
branding
action.yml
の branding
も事前に記述してコミットしておいた方が楽。
tag を push するだけ
あらかじめリリースを作成してしまうとその tag は使えないので、公開するときは tag を push するだけにする。
バージョンを上げるときの手順は見当たらない。実際に上げるときに要確認。
Markplace で更新
公開してある Action を更新してみた。
- 更新時はリポジトリで普通にリリースを作成
- ウェブ UI でリリースの編集画面を開く
- Publish this Action to the GitHub Marketplace にチェック付いていることを確認し
- Update Release をクリック
これで Markerplace の方に新しいリリースが反映される(リポジトリのリリースにも Marketplace のラベルのようなものが表示される)。