Bashプログラミング
 Computer >> コンピューター >  >> プログラミング >> Bashプログラミング

GitHub ActionsとPagesを使ってイベントデータを公開・活用する方法

GitHub上で開発を行うチームにとって、イベントデータは円滑なコラボレーションに欠かせない存在です。Issue、プルリクエスト、コメントとして記録されたデータは、プロジェクトを理解するうえで重要な情報源となります。

GitHub Actionsが一般提供されたことで、リポジトリ内のGitHubイベントデータにプログラムからアクセスし、保存できるようになりました。データをリポジトリ自体の一部にすることで、GitHubの外部にもデータを保全できます。さらに、そのデータをGitHub PagesのようなフロントエンドのWebサイトで表示することも可能になります。

そして、私のように90年代風のゲストブックページを作ることだってできるのです。

用途が何であれ、基本的な考え方は同じです。たった1つのワークフローファイルだけで、Actionsを使ってGitHubイベントデータへのアクセス、保存、表示が実現できます。ここでは、私のゲストブックを動かしているワークフローコードを通じて、その流れをご紹介します。

GitHub Actionsの入門的な内容(ワークフローのトリガー方法など)については、「A lightweight, tool-agnostic CI/CD flow with GitHub Actions」を参照してください。

GitHubイベントデータへのアクセス

Actionワークフローは、いくつかのデフォルト環境変数が設定された環境で実行されます。ここには便利な情報が多数含まれており、イベントデータもその一つです。イベントデータに最も完全な形でアクセスするには、$GITHUB_EVENT_PATH変数を使用します。これは、完全なJSONイベントペイロードが格納されたファイルのパスです。

展開されたパスは/home/runner/work/_temp/_github_workflow/event.jsonのようになり、そのデータは対応するWebhookイベントと一致します。Webhookイベントデータの詳細は「GitHub REST API Event Types and Payloads」のドキュメントで確認できます。ワークフロー環境でJSONデータを利用可能にするには、jqなどのツールでイベントデータをパースし、環境変数に格納します。

以下は、IssueコメントイベントからコメントIDを取得する例です:

ID="$(jq '.comment.id' $GITHUB_EVENT_PATH)"

ほとんどのイベントデータは、JSONをパースしなくてもgithub.eventコンテキスト変数から取得できます。フィールドにはドット記法でアクセスします。以下は同じコメントIDを取得する例です:

ID=${{ github.event.comment.id }}

ゲストブックでは、ユーザーのハンドル名と日時を含むエントリを表示したいので、次のようにイベントデータを取得します:

AUTHOR=${{ github.event.comment.user.login }}
DATE=${{ github.event.comment.created_at }}

シェル変数はデータへのアクセスに便利ですが、存続期間が短いという弱点があります。ワークフロー環境は実行ごとに新しく作成され、あるステップで設定したシェル変数は他のステップに引き継がれません。取得したデータを永続化するには、「アーティファクトを使う」か「リポジトリにコミットする」の2つの選択肢があります。

イベントデータの保存: アーティファクトを使う

アーティファクトを使えば、リポジトリにコミットすることなく、ワークフロージョブ間でデータを引き継げます。これは、より恒久的な場所に置く前にデータを変換したり統合したりしたい場合などに便利です。ジョブ間でデータを永続化する必要があるのは、次の理由によるものです:

ワークフロー内の各ジョブは、仮想環境の新しいインスタンスで実行されます。ジョブが完了すると、ランナーは終了し、仮想環境のインスタンスは削除されます。(Persisting workflow data using artifacts)

アーティファクトの利用を支援する2つのActionがあります:upload-artifactdownload-artifactです。これらのActionを使うと、同じワークフロー内の他のジョブに対してファイルを提供できます。完全な例については「passing data between jobs in a workflow」を参照してください。

upload-artifact Actionのaction.ymlには、各キーワードの説明が記載されています。アップロードされたファイルは.zip形式で保存され、同じワークフロー実行内の別のジョブがdownload-artifact Actionを使ってそのデータを利用できます。

また、リポジトリのActionsタブにあるワークフロー実行ページから、アーカイブを手動でダウンロードすることもできます。

ジョブ間でワークフローデータを永続化しても、リポジトリのファイルには一切変更が加えられません。生成されたアーティファクトはワークフロー環境内にのみ存在するためです。

個人的にはシェル環境での作業に慣れているため、アーティファクトの用途は限定的だと感じていますが、触れないままにするのは失礼なので紹介しました。ジョブ間のデータ受け渡し以外にも、テスト出力データなどを.zip形式でアーカイブする用途に役立ちます。私のゲストブックの例では、必要なすべてのステップを1つのジョブで実行したため、ジョブ間でのデータ受け渡しは不要でした。

イベントデータの保存: ワークフローファイルをリポジトリにプッシュする

ワークフローで取得したデータをリポジトリ自体に保存するには、そのデータをGitリポジトリに追加してプッシュする必要があります。ワークフロー内では、シェルコマンドを使ってデータから新しいファイルを作成するか、既存のファイルにデータを追記します。

ワークフロー内でファイルを作成する

ワークフロー内でリポジトリのファイルを操作するには、まずcheckout Actionで作業用のコピーを取得します:

- uses: actions/checkout@master
  with:
    fetch-depth: 1

ゲストブックにコメントを追加するために、シェル変数に格納したイベントデータをファイルに変換します。シェルのパラメータ展開における置換を使って、ユーザー入力をサニタイズし、改行を段落タグに変換しています。ユーザー入力を慎重に扱うべき理由については、以前記事にまとめています。

- name: Turn comment into file
  run: |
    ID=${{ github.event.comment.id }}
    AUTHOR=${{ github.event.comment.user.login }}
    DATE=${{ github.event.comment.created_at }}
    COMMENT=$(echo "${{ github.event.comment.body }}")
    NO_TAGS=${COMMENT//[<>]/\`}
    FOLDER=comments

    printf '%b\n' "<div class=\"comment\"><p>${AUTHOR} says:</p><p>${NO_TAGS//$'\n'/\<\/p\>\<p\>}</p><p>${DATE}</p></div>\r\n" > ${FOLDER}/${ID}.html

printfの出力を>で新規ファイルにリダイレクトすることで、イベントデータはコメントID番号をファイル名としたHTMLファイルに変換され、取得したイベントデータが格納されます。整形すると次のようになります:

<div class="comment">
  <p>victoriadrake says:</p>
  <p>This is a comment!</p>
  <p>2019-11-04T00:28:36Z</p>
</div>

コメントを扱う際、コメントIDでファイル名を付ける効果の一つは、同じIDを持つ新しいファイルが古いファイルを上書きすることです。これはゲストブックにとって都合がよく、コメントの編集があれば元のコメントファイルを置き換えられます。

Hugoのような静的サイトジェネレーターを使っている場合は、Markdown形式のファイルを作成してcontent/フォルダに入れておけば、通常のサイトビルドが残りを自動的に処理してくれます。

私のシンプルなゲストブックの場合は、個々のコメントファイルを1つのページにまとめる追加ステップがあります。実行されるたびに、header.html部分で既存のindex.htmlを上書きし(>)、次にすべてのコメントファイルの内容を降順で検索して追記し(>>)、最後にfooter.html部分を追記してページを完成させます。

- name: Assemble page
  run: |
    cat header.html > index.html
    find comments/ -name "*.html" | sort -r | xargs -I % cat % >> index.html
    cat footer.html >> index.html

変更をリポジトリにコミットする

checkout Actionはリポジトリのクローンと完全に同じではないため、執筆時点では回避すべき問題がいくつかあります。pullcheckout、そして変更をmasterブランチに正常にpushするには、いくつかの追加ステップが必要ですが、いずれもシェルで簡単に行えます。

以下は、ワークフローが行った変更を追加・コミットし、リポジトリのmasterブランチにプッシュするステップです。

- name: Push changes to repo
  run: |
    REMOTE=https://${{ secrets.GITHUB_TOKEN }}@github.com/${{ github.repository }}
    git config user.email "${{ github.actor }}@users.noreply.github.com"
    git config user.name "${{ github.actor }}"

    git pull ${REMOTE}
    git checkout master
    git add .
    git status
    git commit -am "Add new comment"
    git push ${REMOTE} master

リモート、つまり私たちのリポジトリは、github.repositoryコンテキスト変数を使って指定します。ワークフローがmasterへのプッシュを許可されるようにするため、secrets.GITHUB_TOKEN変数を使用します。

ワークフロー環境は毎回真新しく生成されるため、Gitの設定が必要です。上記の例では、github.actorコンテキスト変数を使って、ワークフローを開始したアカウントのユーザー名を設定しています。メールアドレスも同様に、GitHubのデフォルトのnoreplyメールアドレスを使って設定します。

イベントデータの表示

2019年11月6日訂正: GitHub ActionsでPagesサイトのビルドをトリガーするには、Personal Access Tokenが必要です。

デフォルトのsecrets.GITHUB_TOKEN変数を使ってGitHub Pagesを利用しており、サイトジェネレーターを使用していない場合、ワークフローでリポジトリにプッシュしてもリポジトリのファイルが更新されるだけです。GitHub Pagesのビルドは失敗し、「Your site is having problems building: Page build failed.」というエラーが発生します。

ActionsがPagesサイトのビルドをトリガーできるようにするには、Personal Access Tokenを作成する必要があります。このトークンはリポジトリの設定でシークレットとして保存し、デフォルトのsecrets.GITHUB_TOKEN変数の代わりにワークフローへ渡せます。Actionsの環境と変数については、別の記事で詳しく解説しています。

Personal Access Tokenを使えば、Actionsワークフローが開始したプッシュでもPagesサイトが更新されます。私のゲストブックにコメントを残して、ぜひ試してみてください!コメント作成イベントがワークフローをトリガーし、約30秒から1分ほどでゲストブックページが更新されます。

Hugoを使う場合のように、変更を公開するためにサイトビルドが必要なケースでも、Actionで対応できます。ただし、意図しないループを避けるため、あるActionワークフローが別のワークフローをトリガーすることはありません。代わりに、Makefileでサイトビルドの処理を担うのが非常に便利です。どのワークフローからでもMakefileを実行できます。ワークフロージョブの最後のステップとしてMakefileの実行を追加し、必要に応じてリポジトリトークンを渡すだけです:

- name: Run Makefile
  env:
    TOKEN: ${{ secrets.GITHUB_TOKEN }}
  run: make all

これにより、ワークフローの最終ステップで更新済みサイトのビルドとデプロイが確実に行われます。

イベントデータの可能性は無限大

GitHub Actionsは、イベントデータを取得して活用する洗練された手段を提供し、データをGitHubの中だけにとどめません。その可能性は想像力次第です!この仕組みで実現できるアイデアをいくつか挙げてみましょう:

  1. GitHubアカウントを持たない顧客でもプロジェクトのIssueを閲覧し、フィードバックを送れる公開型のIssueボード。
  2. 任意のリポジトリの新しいIssue、コメント、PRを自動更新するRSSフィード。
  3. GitHubのIssueコメントを入力手段として使う、静的サイト向けコメントシステム。
  4. 最高にクールな90年代風ゲストブックページ。

90年代風のゲストブックページを作ったと言いましたっけ?内心のGeocitiesオタクが少し興奮しています。

  1. 【保存版】Googleドライブと暗号化でWhatsAppのデータを安全に守る方法

    WhatsAppのデータをGoogleドライブで安全に保護しよう WhatsAppは今や、友人や家族とのコミュニケーションにおいて最も人気のある手段のひとつです。動画や画像、面白い話題、連絡先など、さまざまなデータを日常的にやり取りしていますよね。 しかし、端末の内部ストレージに保存されている大切な写真や動画を、うっかり失ってしまったらどうなるか——考えただけでもゾッとします。実は、その心配はもう無用です。WhatsAppはGoogleと提携しており、チャットデータを暗号化したうえで、Googleアカウント経由でGoogleドライブにバックアップできるようになっているからです。 設定方法はとて

  2. Windows 10の「ファイル履歴」でデータを自動バックアップ・復元する完全ガイド

    ハードディスクの故障、停電、その他の予期せぬトラブルから、大切なファイルやドキュメントを守ることは非常に重要です。本来なら定期的なバックアップを行うべきですが、Windowsでは重要なファイルをUSBメモリ(少量のデータ向け)や外付けハードディスク(大量のデータ向け)などの外部ストレージに手動でコピーする方法が一般的です。さらに、クラウドストレージへ毎日自動的にバックアップしてくれる有料ソフトを利用する選択肢もあります。実は、Windows 10には「ファイル履歴(File History)」という優れた機能が標準搭載されており、これを使えばPCのデータを自動的にバックアップできます。 Win