🐧

READMEに載せるアプリのスクショを自動更新するGitHubカスタムアクションを作ってみた

2024/08/19に公開

アプリのイメージを伝えるためにREADMEにスクショを載せときたい。。。
でも画面の変更のたびにスクショを手作業で更新するのはめんどくさい。。。

せや!自動で更新できたらええねん!

ということで作りました。誰でも使えます。

想定読者

  • GitHub Actions を使ったことはあるけどカスタムアクションは作ったことがないよ〜くらいの方
  • READMEのスクショの更新がめんどい!という方

概要

README.md に載せるWebアプリのスクショを自動更新するためのカスタムアクション。
たとえば以下のような README.md のスクショ部分を自動で最新に保ってくれます。
GitHub Actions ワークフロー内で使えるので、よかったら使ってみてください。

リポジトリ

https://github.com/otaly/readme-screenshot-action

使い方

以下はNode.jsアプリの例です。
urls にスクリーンショットを撮りたいURLを指定します。複数画面があるアプリの場合は例のように複数のURLを指定することもできます。
server_command でアプリの起動コマンドを渡すことで、スクリーンショット撮影後に自動でプロセスを終了させます。

.github/workflows/node-sample.yml
jobs:
  update_readme:
    runs-on: ubuntu-latest
        # コミットするための権限
    permissions:
      contents: write

    steps:
      - uses: actions/checkout@v4

      # Node.jsのセットアップ
      - uses: actions/setup-node@v4
        with:
          node-version: 20

      # 依存関係のインストール
      - run: npm install

      # スクリーンショットを撮影してREADMEを更新する
      - uses: otaly/readme-screenshot-action@v1
        with:
          urls: |
            https://localhost:5173/
            https://localhost:5173/hello
          server_command: npm run dev  # アプリの起動コマンド

      # 更新したスクショとREADMEをコミット
      - uses: stefanzweifel/git-auto-commit-action@v5
        with:
          commit_message: Update screenshots

詳しい説明はリポジトリに譲りますが、他にもたとえば以下のようなパラメータを指定することができます。

パラメータ 説明 デフォルト
delay スクリーンショットを撮る前の遅延時間(ミリ秒) 0 3000
width ビューポートの横幅(CSSピクセル) 1920 1280
height ビューポートの高さ(CSSピクセル) 1080 720

README.mdに以下のようなコメントを追加しておくと、その間にスクリーンショットが自動で挿入・更新されます。

README.md
<!-- :README-SCREENSHOT-BEGIN: -->
<!-- :README-SCREENSHOT-END: -->

実装

初めてカスタムアクションを作成したので、実装する上でのポイントをまとめておきます。

バンドル

カスタムアクションとして動作するには依存関係にあるコードも含めてpushする必要があります。
ただ、できれば node_modules をpushしたくはないので、基本的には @vercel/ncc などを用いて一つのファイルにバンドルすることになります。
@vercel/ncc はTypeScriptに対応しているため、TypeScriptでもそのまま問題なく使うことができます。

npm install -D @vercel/ncc

package.json に以下を追加します。

package.json
{
  ....
  "scripts": {
    // 追加
    "package": "npx ncc build src/index.ts -o dist --source-map --license licenses.txt",

action.ymlの作成

action.yml にメタデータを記載します。ここには inputs や outputs を定義します。

action.yml
name: "readme-screenshot-action"
description: "READMEのスクリーンショットを自動更新します"
inputs:
  urls:
    description: "スクリーンショットを撮る対象のURL (eg. http://localhost:3000/)"
    required: true
    default: ""
  server_command:
    description: "サーバーを起動するためのコマンド (eg. npm start)"
    required: false
    default: ""
  delay:
    description: "スクリーンショットを撮る前の遅延時間(ミリ秒)"
    required: false
    default: "0"
  width:
    description: "ビューポートの横幅(CSSピクセル)"
    required: false
    default: "1920"
  height:
    description: "ビューポートの高さ(CSSピクセル)"
    required: false
    default: "1080"
runs:
  using: "node20"
  main: "dist/index.js"

ツールキットパッケージの追加

with で渡されるパラメータを受け取ったり、 outputs や終了ステータスを設定するために @actions/core パッケージを追加します。
また、 @actions/github はアクションをトリガーしたワークフローに関する情報や Octokit REST クライアントを使用することができます。今回はコミットハッシュを入手する目的で入れていますが、リポジトリに対する操作をしたいようなケースでも使えるでしょう。

npm install @actions/core @actions/github

他にも色々と便利そうなパッケージがあるので、アクション作成の際は以下のページに一度目を通すことをおすすめします。
https://github.com/actions/toolkit

実装

with で渡されるパラメータには getInputgetBooleanInputgetMultilineInputを使ってアクセスできます。

// 例
import * as core from '@actions/core';

// required: true にするとパラメータが渡されなかったときにエラーを投げる
const message: string = core.getInput('message', { required: true });

// booleanの場合
const hasMessage: boolean = core.getBooleanInput('has_message');

// 複数行の入力を受け取る場合
const messages: string[] = core.getMultilineInput('messages');

エラー時は setFailed を呼んでワークフローにエラーを通知します。

try {
  JSON.parse(json);
} catch (error) {
  core.setFailed(error);
  return;
}

まとめ

今回初めてカスタムアクションを作ってみましたが、案外サクッと作ることができました。
公式の typescript-action テンプレートなどを使えば簡単に作り始めることができるので、ぜひ作ってみてください。

今回私が作った readme-screenshot-action はGitHub Actionsのワークフロー内で簡単に利用できるので、ぜひお試しください。

参考

https://github.com/actions/typescript-action

https://docs.github.com/ja/actions/sharing-automations/creating-actions/creating-a-javascript-action

Discussion