GitHub Actionsの中で生成したファイルをブランチに登録する

2023/03/19に公開

この記事について

この会話画像、生成してコミット・プッシュするのだるいなぁ…
それは面倒くさがり過ぎじゃ…でもできるものは極力自動化したいのは同意する

というわけで、前回の記事で作成したツールをGitHub Actionsに組み込んで自動化したいと思います。

今回の目標

やりたいのは、インプットとなるMarkdownの記事ファイルを作業用ブランチにプッシュした際、
GitHub Actionsのワークフローが動いて、同ブランチに生成した画像がプッシュされる仕組みを作ることです。

いちいちツール実行しなくてもよくなるね!

最初に成果物

GitHub Actionsは、以下の手順を踏めば簡単に有効化できます。

  1. リポジトリに.github/workflowsディレクトリを作成する
  2. ワークフローの処理をyaml形式のファイルに定義したものを、.github/workflowsディレクトリに登録する

https://docs.github.com/ja/actions/learn-github-actions/understanding-github-actions#workflows

今回はこんな感じのyamlファイルを作りました。

main.yaml
name: Main CI
on:
  push:
    branches:
      - 'article/**'
    paths:
      - 'articles/**'
      - '.github/**'
jobs:
  parse-markdown:
    runs-on: ubuntu-latest
    container:
      image: mcr.microsoft.com/playwright/python:v1.30.0-focal
    steps:
      - name: setting playwright container
        run: |
          pip install markdown
          git clone https://github.com/googlefonts/zen-marugothic.git
          cp ./zen-marugothic/fonts/ttf/ZenMaruGothic-Regular.ttf /usr/local/share/fonts/

      - name: Checkout
        uses: actions/checkout@v3
        with:
          fetch-depth: 0 
          token: ${{ secrets.PERSONAL_ACCESS_TOKEN }}

      - name: safe dirctory
        run: git config --global --add safe.directory $(pwd)

      - name: generate conversation image
        run: |
          ARTICLE_LIST=$(git diff --name-only origin/main...HEAD | grep articles/article_)
          
          for ARTICLE in ${ARTICLE_LIST}; do
            python3 playwright/markdown/markdown_parser.py ${ARTICLE}
          done

      - name: push generated image
        run: |
          git config user.name auto-push
          git config user.email auto-push@example.com
          git add .
          git commit -m "auto push from ci" || echo no commits
          git push

各設定について

ワークフローの起動設定

on:
  push:
    branches:
      - 'article/**'
    paths:
      - 'articles/**'
      - '.github/**'  

記事を作成する際は、article/xxxxといった命名ルールにしようと決めたので、そのブランチに対してpushを実施した際、起動するようにしました。
push以外にも色々なイベントを起因に起動設定が可能です!
https://docs.github.com/ja/actions/using-workflows/events-that-trigger-workflows

また、最終的にはimages/ディレクトリ配下に画像ファイルをpushするので、再び起動条件に一致してしまうのを防ぐためpathsを使用しています。

Markdownだけをインプットにしてワークフローを起動したいからね

たしかに、全然関係無いファイルのpushでも動いちゃうのはビミョーかも…

また、yamlのメンテナンス時にも動作確認したいので、.github/ディレクトリも追加しています。

ジョブ実行環境の設定

ワークフローで実行する処理の単位をジョブと呼びます。
https://docs.github.com/ja/actions/using-jobs/using-jobs-in-a-workflow

そのジョブを実行する環境の定義を以下のように記述。

jobs:
  parse-markdown:
    runs-on: ubuntu-latest
    container:
      image: mcr.microsoft.com/playwright/python:v1.30.0-focal

ジョブを実行するサーバであるランナーのOSをこれらの中から選択して指定します。
とりあえずLinuxであれば良いので、Ubuntuの最新版を選択。

またcontainerを設定することで、ランナー上にコンテナが起動され、そのコンテナ上でジョブを実行するという設定も可能です。
今回はPlaywrightの公式イメージをそのまま利用することで、Playwright環境を構築手間を省きました。

開発環境にもコンテナを使っていれば全く同じ環境でCIできるから、品質アップにも繋がるよ!

各ジョブの詳細

stepsの配下にジョブをリストで記述していきます。

追加のインストール

- name: setting playwright container
  run: |
    pip install markdown
    git clone https://github.com/googlefonts/zen-marugothic.git
    cp ./zen-marugothic/fonts/ttf/ZenMaruGothic-Regular.ttf /usr/local/share/fonts/

runに記述したコマンドがジョブで実行されます。
ここでは、Playwrightのコンテナに足りないライブラリとGoogleフォントを追加。

リポジトリのソースを取得

GitHub Actionsの利点として、別リポジトリのワークフローを利用することで、自分で処理を記述しなくても良いという、ライブラリ的な使い方ができます。
https://docs.github.com/ja/actions/using-workflows/reusing-workflows#calling-a-reusable-workflow

- name: Checkout
  uses: actions/checkout@v3
  with:
    fetch-depth: 0 
    token: ${{ secrets.PERSONAL_ACCESS_TOKEN }}

ここでは、公式で準備されているcheckoutを利用して、リポジトリのソースコードをランナーにダウンロードします。

リポジトリに登録されたMarkdownファイルをインプットにしないとだもんねー

また最終的に、画像生成したものをpushするので、リポジトリに対してpushする権限が必要となります。そのため、予めPersonal Access Tokenを作成します。それをシークレットに登録しておきtokenに設定。

https://docs.github.com/ja/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token

https://docs.github.com/ja/actions/security-guides/encrypted-secrets

safe directoryの設定

checkoutしたランナー上のローカルリポジトリの中でgit操作をしようとすると、以下エラーがでます。

warning: Not a git repository

Gitのセキュリティ脆弱性対策っぽいです。そのため、次のコマンドを実行しておく必要があります。

- name: safe dirctory
  run: git config --global --add safe.directory $(pwd)

参考:https://git-scm.com/docs/git-config/2.40.0

これらの設定項目は、Git で追跡されたディレクトリが現在のユーザー以外によって所有されていても安全であるとみなされるように指定します。

メイン処理

- name: generate conversation image
  run: |
    ARTICLE_LIST=$(git diff --name-only origin/main...HEAD | grep articles/article_)
    
    for ARTICLE in ${ARTICLE_LIST}; do
      python3 playwright/markdown/markdown_parser.py ${ARTICLE}
    done

- name: push generated image
  run: |
    git config user.name auto-push
    git config user.email auto-push@example.com
    git add .
    git commit -m "auto push from ci" || echo no commits
    git push

前置きが長かった…やっとメイン処理ね!!
…とは言っても、あとはツール実行してGitにプッシュしてるってだけなんだよね

工夫ポイントとしては

  • git diffコマンドを使って、更新がかかったMarkdownファイルを抽出してツール実行している
  • ツールで画像生成しなかった場合、git commitが失敗するので||echoを繋げてジョブがエラーにならないようにする

となります。

まとめ

GitHub Actionsでツール実行を自動化しました。
yamlファイル作成するだけなので簡単ですよね!

ただ、完成するまではトライ&エラーでワークフローを動かしまくる必要があるので、ワークフロー自体のデバッグはもっと効率化したいところです。

失敗ワークフローが増えていくと心が折れそうになる…

そこで、ローカル端末上でワークフローを実行できるactというツールがあったり、そもそもGitHub Actionsに依存しない自動化処理を作成できるdaggerというツールが流行ってるので、今後使い勝手を見ていけたらなと思ってます!

Discussion