tech.guitarrapc.cóm

Technical updates

GitHub Actions で README にリポジトリのコードを埋め込みたい

README に、リポジトリにおいているコードを埋め込みたい時があります。 そんな時に便利なのが、embedme です。

github.com

今回は、GitHub Actions を使って README にリポジトリのコードを埋め込むことをしたので紹介します。

tl;dr;

  • GitHub Actions で embedme を実行して、README.md にリポジトリのコードを埋め込み自動化ができる
  • リポジトリと README.md で同じコードを示す時の二重管理が解消するのでうれしい
  • 軽量シンプルなので、pull_request、push、workflow_dispatch など任意のトリガーで組めて便利

embedme の基本的な利用例

例として、README.md に src/example.ts の中身を埋め込むことを考えてみましょう。

まずはembedme をインストールします。

npm install -g embedme

README.md に次のようにコードブロックと言語を示しておいてリポジトリのファイルパスを示します。

gist.github.com

TIPS: OS問わずファイルパスは / 区切りがいいです。

あとは、インストールした embedme をREADME.md に実行します。

embedme README.md

これでexample.ts のファイルがコードブロックに差し込まれます。便利。

gist.github.com

行指定も直感的にできます。

// src/embedme.lib.ts#L44-L82

embedme のいいところ

個人的に気に入ってるところは3つあります。

埋め込むときに使ったコメントがコードブロックに残る

埋め込み後もコメントが残ることで、埋め込むソースコードを更新してembedmeを実行すると埋め込みが更新されるので、勝手にメンテが維持します。 どことリンクしていたかも一目瞭然で、README.md を見る人が探すこともできるので好きです。

実行が軽い

インストールも実行も早いのはいいこととです。 npm install -g embedme で入るので、GitHub Actions などの CI との相性もいいです。

実行も即座に終了するのでカジュアルに走らせられる安心感があります。

実行履歴

README.md の何行目のコードブロックを実行した、実行できなかったを理由を添えて表示してくれます。 例えばファイルパスが見つからず埋め込みができなかったら次のように教えてくれます。

   README.md#L381-L383 Found filename .github\workflows\concurrency_control_cancel_in_progress.yaml in comment in first line, but file does not exist at /home/runner/work/githubactions-lab/githubactions-lab/.github\workflows\concurrency_control_cancel_in_progress.yaml!

十分です。

embedme で困るであろうこと

コードブロックのコメント

コードブロック内のコメントを検知するので、埋め込む前提になってないのにコメントを適当にいれたコードブロックを作ると警告は出ます。 ファイルパスが示されていないければ問題ないですが注意です。

言語ごとのコメントが違う

言語ごとに対応しているので当然ですが注意しましょう。 yaml や bash なら # でコメント認識ですし、js や ts なら // でコメント認識です。

GitHub Actions で自動化する

embedme は GitHub Actions としては公開されていないので適当に組みます。 今回は、GitHub Actions をいろいろ試しているリポジトリで組んだ例を示します。

github.com

大量に yaml を埋め込んでいるので本当に助かっています。

https://github.com/guitarrapc/githubactions-lab での自動埋め込み例

GitHub Actions workflow を用意する

普段は pull_request で実行して、変更があれば PR にコミットを積むようにします。 必要に応じて workflow_dispatch で手動で更新もできるようにします。

gist.github.com

PRで リポジトリを checkout するときは、Merge コミットではなく PR のコミットを checkout します。 refを指定せず checkout すると、後段で git push したときにマージコミットが PR に入ってしまいます。

      - if: ${{ github.event_name == 'pull_request' }}
        uses: actions/checkout@v3
        with:
          ref: ${{ github.event.pull_request.head.sha }} # checkout PR HEAD commit instead of merge commit

19-22行目で embedded を実行します。

      - name: Embedding Code into README
        run: |
          npm install -g embedme
          embedme README.md

あとは変更があれば git push します。

      - name: git diff
        id: diff
        run: |
          git add -N .
          git diff --name-only --exit-code
        continue-on-error: true
      - if: steps.diff.outcome == 'failure'
        name: git-commit
        run: |
          git config user.name github-actions[bot]
          git config user.email 41898282+github-actions[bot]@users.noreply.github.com
          git add .
          git commit -m "[auto commit] Embed code"
      - if: steps.diff.outcome == 'failure'
        name: Push changes
        uses: ad-m/github-push-action@master
        with:
          github_token: ${{ secrets.GITHUB_TOKEN }}
          branch: ${{ github.head_ref }}

push で実行してもいいですが、私のリポジトリではREADMEの目次作成 (toc) を push でやっているので pull_request にしています。 コード生成は重なると直列実行 と checkout 時の ref 伝搬を気にしないといけないので注意です。

まとめ

embedme 便利です。 あんまり README にリポジトリのコードを埋め込むのを考えてこなかったのですが、いざやってみると便利でいろいろ使えそうです。(実際コピペではることありますし)

補足情報として、類似したものに tokusumi/markdown-embed-codeがありますが、これはそもそも実行できなかったのと docker のpullが走って重すぎるので好みじゃなかったです。

github.com