GitHub ActionsとPull Requestを活用した、同期の自動化

あけまして、おめでとうございます。神社のおみくじで、人生はじめて大吉を引きました、silverbirder です。

普段の業務で、FigmaのデザイントークンやAPIのスキーマファイル、i18nのメッセージファイルなどを、フロントエンドへ同期するコミュニケーションが不毛に感じています。そこで、GitHub ActionsとPull Requestを活用して、同期コミュニケーションを削減する仕組みを紹介します。

目新しい情報はないかもしれませんが、同じお困りごとを持つ人へ助けになれば、幸いです。

GitHub Actionsで使用するもの

今回紹介する仕組みの核となるのが GitHub Actionsのrepository-dispatch トリガーです。

https://docs.github.com/ja/rest/repos/repos?apiVersion=2022-11-28#create-a-repository-dispatch-event

このトリガーは、GitHub APIを経由して、GitHub Actionsのワークフローを起動することができます。そのため、次のように 異なるリポジトリでのGitHub Actionsワークフローを連携できます。

repository-dispatchとcreate-pull-requestは、次のGitHub Actionsです。

https://github.com/peter-evans/repository-dispatch https://github.com/peter-evans/create-pull-request

  • respository-dispatch
    • repository-dispatch-eventをdispatchするAction
  • create-pull-request
    • Pull Requestを作成するAction

これらのGitHub Actionsを使わずに gh などを使って代替できますが、便利なモノを使って楽をします。

GitHubリポジトリ以外からのトリガー

GitHubのリポジトリ(username/other)からトリガーだけでなく、他のサービスからでもトリガーできます。例えば、Google Sheets からだと、Google Apps ScriptからGitHub APIを呼べばよいです。

他にも、Kibelaのoutgoing webhookを、Serverが受けて、ServerがGitHub APIを呼び出す方法があります。

Serverは、IFTTTやZapierのようなサービスでも良いですし、自前のサーバーでも良いでしょう。

自動commit

schemaファイルから、型を生成したい(yarn codegen)こともあると思います。そういうときは、次のフローを追加します。

git-auto-commit-actionは、変更したファイルをgit commitするだけのActionです。

https://github.com/stefanzweifel/git-auto-commit-action

create-pull-requestだけでも、自動commitすることができます。私は、次のケースで使用しました。

  • FigmaのDesign Tokensで、Figma上からPull Requestを作成する。
    • GitHub Actionsで、style dictionaryのbuildしたものをcommitしたい
on:
  pull_request:
    types: [opened]
jobs:
  update:
    if: startsWith(github.head_ref, 'figma/')
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
      - run: npm ci
      - run: npx style-dictionary build
      - uses: stefanzweifel/git-auto-commit-action@v4

Preview

Figmaのデザイントークンや、i18nのメッセージファイルを更新したとき、Previewできる仕組みがあると、画面の確認ができて、良いです。

例えば、vercelやchromaticのpreviewです。

https://vercel.com/docs/concepts/deployments/preview-deployments https://www.chromatic.com/docs/review

サンプルコード

i18nのメッセージファイルをフロントエンドへ同期するGitHub Actionsを、紹介します。

repository やること
username/frontend i18nのメッセージファイルを利用
username/message i18nのメッセージファイルを管理

# <username/message>/.github/workflows/main.yml
on:
  push:
    branches:
      - main
    paths:
      - 'i18n/**'
jobs:
  dispath:
    name: Setup
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: peter-evans/repository-dispatch@v1
        with:
          repository: username/frontend
          token: ${{ secrets.PAT }}
          event-type: create-pull-request-message
          client-payload: '{"ref": "${{ github.ref }}"}'
# <username/frontend>/.github/workflows/main.yml
on:
  repository_dispatch:
    types: [create-pull-request-message]
jobs:
  createPullRequest:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/checkout@v3
        with:
          repository: username/message
          ref: ${{ github.event.client_payload.ref }}
          path: "tmp/"
      - run: |
          mv tmp/message.json src/message.json
          rm -rf tmp
      - uses: actions/setup-node@v3
      - run: npm ci
      - run: yarn generate:message
      - uses: peter-evans/create-pull-request@v4

受け入れテストをマークダウンで管理

安心してマージできるように、受け入れテストを整備しておきましょう。

具体的には、cucumberで仕様書をMarkdown(MARKDOWN_WITH_GHERKIN)で管理します。

例えば、次のような仕様書です。

# Feature: Staying alive

This is about actually staying alive,
not the [Bee Gees song](https://www.youtube.com/watch?v=I_izvAbhExY).

## Rule: If you don't eat you die

![xkcd](https://imgs.xkcd.com/comics/lunch_2x.png)

`@important` `@essential`
### Scenario Outline: eating

* Given there are <start> cucumbers
* When I eat <eat> cucumbers
* Then I should have <left> cucumbers

#### Examples:

  | start | eat | left |
  | ----- | --- | ---- |
  |    12 |   5 |    7 |
  |    20 |   5 |   15 |

このMarkdownも、GitHub ActionsでPull Requestするフローに載せましょう。新しいシナリオが追加された場合、(cucumberのライブラリ上) テストコードが存在しないとエラーとなります。

機能で担保したいシナリオをMarkdownで管理していくことで、次のメリットがあります。

  • 仕様が明確になる
  • CIで受け入れテスト(cucumber)を動かし成功すると、仕様を満たす状態 となる

ハマったこと

GitHub Actions Botのcommitで、他のワークフローをトリガーできない

https://github.com/orgs/community/discussions/27028

tokenに、PATを渡すように変更すれば解決します。

他の解決策としては、workflow_run のトリガーを使えます。

https://docs.github.com/ja/actions/using-workflows/events-that-trigger-workflows#workflow_run

ただし、デフォルトブランチでのみ動作します。

repository-dispatchのPOSTは、JSONで制限がある

https://github.com/peter-evans/repository-dispatch#client-payload

同期したいファイルをjsonに変換して、dispatchするeventペイロードに含めようと、当初考えていました。ただ、次の懸念があったため、却下しました。

  • jsonにしてしまうとコメントが消える
  • JSONのバイトサイズに上限がある

そこで、同期したいリポジトリのgithub.refをeventペイロードに含めて、eventを受けた側がソースコードをチェックアウトして使う方針に切り替えました。

終わりに

GitHub ActionsとPull Requestを活用することで、自動的にアプリケーションのソースコードを更新する仕組みを簡単に組み立てられます。 このようなOpsがあれば、Slackでのメッセージラリーをする回数が減らせられます。ぜひ、ご活用ください。

2022年の振り返り。転職と妊活

南紀白浜とれとれヴィレッジへ行ってきました

2022年の終わりに近づきました。今年の振り返りをしようと思います。 これは、自分のために書いていくので、文章の構造化とか不適当にやります。

仕事

今年の1月から、新しい職場で働き始めました。人生2回目の転職です。 2回目となると、転職関連の手続きは随分と慣れました。

新しい職場では、SlackやDiscordでコミュニケーションを取るのですが、ボケるコメントが多く、とても賑やかで楽しい職場です。クスっと笑えるので、居心地良い環境だなと思います。

私はフルスタックなエンジニアリング力を経験してきたのですが、改めてフロントエンドエンジニア力を高めたいと思い(もともとIT業界に入ったきっかけはWebフロントエンド)、転職してみました。

ただ、入社して半年ぐらいはドメインキャッチアップやバックエンド開発などをしていました。

特別な領域のドメインのため、それの勉強や、バックエンドのプログラミング言語も馴染みがないものだったので、色々と一杯一杯でした。

半年が経過したあたりから、フロントエンド開発に投資できるようになり、業務で初めてReactやTypeScriptを試すことができました。

1つの画面をReactに置き換えるプロジェクトを終え、すごいインターン生のフロントエンドレビューをひたすらしたり、フロントエンド刷新プロジェクトにチャレンジして、フロントキャリアが長い方と数ヶ月一緒に仕事をして、ドンドンと右肩上がりに吸収できていると満足しています。

また、私は本来、Reactなどのフレームワークよりもブラウザの仕組みや、HTMLやWebAPIの仕様を読むという方が好きなので、それは継続していこうと思います。

今年の11月から、名古屋拠点のメンバーと仕事をすることになり、私が住む大阪から新幹線通勤することになりました。そこでは、エッジなアーキテクチャを採用していて、それを楽しみに、今全力投球しています。

プライベート

わたしは、妻と二人で暮らしています。僕ら二人の気持ちとしては、そろそろ子供がほしくなってきました。祖父母や両親に、僕らの子を見させてあげたいと思っています。

妊活って、まったくわかりませんでした。 自然妊娠がなかなかできずに、とても悩みました。 職場の人から、不妊治療というのを教えてもらい、妻と相談し紆余曲折あって、不妊治療の検査に進むことができました。妻が保育士で多忙のため、なかなか検査にいく時間がないため、まだ検査は終わっていません。

妊活を進めていく中で、妻に精神的な負荷がかかってしまい、つらいときがありました。最近は、『子供は授かりもの』と運だと割り切り、できたら良いよねと思い始めるようになりました。

お金と時間

来年の4月ぐらいに、祖父母が所有する空き家を貰い住むことなりました。理由としては、空き家が実家の近くのため、両親の近くだと何かと安心できるから (子供の件で) です。 祖父母としても、他界する前に相続でバタバタして貰いたくないので、空き家を誰かに貰って欲しかったとのことでした。僕らとしても、家賃を払わなくて済む (固定資産税は年2,3万程度 だけで良い) ので、引き受ける決意をしました。

そうすると、支出がめちゃくちゃ抑えることができ、貯金も四捨五入したら1000万はあるため、妻には来年4月には働かず自由になって貰うつもりです。

お金はいくらでも欲しくなるのですが、正直私としてはお金よりも家族との時間を優先したいと常々思っています。

そのため、週5日の8時間勤務の正社員を継続するのか、もしくは契約社員やちょっとした副業でもやろうかと悩んでいます。

副業だと、以前やっていたプログラミングを教えるサービスをもう一度やろうかなと思ったり。

もしくは、人の写真を撮るのが好きで、写真って思い出にも残るし、いつか見返したときの温かい気持ちになれる素敵なアイテムだと思ってて、そういうのをちょっとした副業とかにもつなげてみるのも、面白そうだなと思います。

来年の抱負

仕事よりも、家族の時間を大切にしたいと思います。

と言いつつも、モノづくりは結局楽しいので、趣味の範囲でワチャワチャしつつ、仕事でスキルアップしていこうと思います。

過去の振り返り

過去の振り返りを読んでみると、なんだかエモい気持ちになりました。

結局、車の購入は見送りました。交通機関が充実してる地域に住んでいるため、まだデメリットが大きく感じました。

「マイクロフロントエンド」を読みました

DAZN の Luca Mezzalira さんが書かれたマイクロフロントエンド を読みました。簡単な書籍レビューを残しておこうかなと思います。

なぜマイクロフロントエンドを使うのか

従来のモノリスなフロントエンドから、マイクロフロントエンドに置き換えることで、どういう価値があるのでしょうか。 書籍に書いてある内容と、自身の意見を混ぜて以下に列挙します。

  • 機能開発のイテレーションが短くなる
    • 1 チームに 1 サブドメインという小さなスコープのため
  • チーム内の意思決定がしやすい
    • コードベースが小さいため
  • リリース速度が早い
    • 各チームが独立しているため

1 チームが小さなサブドメインで独立することで、開発やコミュニケーション、リリースなどのコスト低減ができます。 これは、最終的には顧客への価値提供するサイクルを短くすることに繋がります。

マイクロフロントエンドの導入方法

書籍には、実例として既存アプリケーションをマイクロフロントエンドへマイグレーションする話があります。 マイクロフロントエンドは、その設計の性質上、基本的に(既存アプリケーションからの)マイグレーションとセットです。 そのため、マイグレーションをどのように進めるかというのは、とても重要です。

その中で、マイクロフロントエンドへの最初の一歩として、次のパターンがあります。

  • 共有コンポーネントをマイクロフロントエンドとしてリリース
  • 新機能をマイクロフロントエンドとしてリリース
  • 既存アプリケーションの一部をマイクロフロントエンドとしてリリース

要は、段階的に導入しましょうという話です。 また、マイクロフロントエンドには、従来の SPA 開発に似ている垂直分割という方法が最初の一歩としてお勧めのようです。

  • 垂直分割
    • 1 つの画面に 1 つのマイクロフロントエンド
  • 水平分割
    • 1 つの画面に複数のマイクロフロンエンド

ちなみに、マイクロフロントエンドとコンポーネントは、次のように区別します。

  • マイクロフロントエンド
    • サブドメインのビジネス表現
    • ロジックをカプセル化し、イベント通信
  • コンポーネント
    • 再利用性の目的で使用される技術的ソリューション
    • 拡張しやすく複数のプロパティを公開

書籍にあった好きな言葉

付録にある、New Relic で働かれている Erik Grijzen さんのインタビュー記事にて、

    1. マイクロフロントエンドを 3 語で表現すると
    1. Scaling UI Development

という回答がありました。 Scaling UI Development という言葉、めちゃくちゃ好きになりました。

マイクロフロントエンドは、フロントエンドをサブドメインで分割し、小さく独立した開発が可能となります。 大規模な 1 つのアプリケーション開発や、1 つのブランド内の様々なプロダクトを提供するアプリケーション開発に対しては、マイクロフロントエンドは効果的だと思っています。

終わりに

余談ですが、ブログを書く時間が、徐々に減っているな〜って感じています。1 年前とかは、1 日とか使っていたんですが、今日は 30 分とかです。 効率化できている訳じゃなく、単純に時間がないなと思います...。時間を捻出しなきゃ...!

ObsidianでiPhoneからGit Commitする

WikiWikiWeb というコンセプトが好きで、そのコンセプトが含まれている Obsidian や Scrapbox が好きです。Obsidian には、obsidian-git という Git 連携のプラグインがあります。こちらには、デスクトップだけでなく、モバイルからでも Git Commit できます。 そこで、私が持ってる iPhone を使って、Obsidian で Git Commit する手順を紹介します。

手順

  1. iPhone から https://obsidian.md/ にアクセスし、アプリをダウンロード

  2. アプリを開いて、Create new vault をタップ

obsidian_1

  1. Vault name に、適当な名前を入力 (後で変更可能)し、Create をタップ

obsidian_2

  1. 左上のサイドバーアイコン → 設定アイコン → コミュニティプラグイン → コミュニティプラグインを有効化 の順でタップ

obsidian_3

  1. コミュニティプラグインを閲覧 → Git を入力 → Obsidian Git をタップし、インストール → 有効化をタップ

obsidian_4

  1. オプションをタップ

obsidian_5

  1. Github のアカウント、Personal Access Token (repo の権限があれば良い) を入力. バツボタンをタップし、4 の画面に戻る

obsidian_6

  1. 下へスクロールして、コマンドパレットを表示 → Clone と入力し、表示された選択肢をタップ

obsidian_7

  1. Clone したいリポジトリ URL を入力

obsidian_8

  1. Vault Root をタップ

obsidian_9

  1. NO をタップ (.obsidian フォルダが repository にあるなら YES)

obsidian_10

  1. Clone が成功すると、リポジトリのファイルが閲覧できる

  2. Obisidian Git の Advanced に、Author name と Authr email を入力し、バツをタップ

obsidian_12

  1. ファイルを適当に変更する

  2. 下へスクロールし、コマンドパレットを開く → Git Open source と入力し、表示された選択肢をタップ

obsidian_16

  1. +ボタンで Stage、チェックボタンで Commit、アップロードボタンで Push できる

obsidian_15

困ったこと

  • スマホから、ブランチを作成できるが Push できない
  • スマホから、ローカルには存在しないリモートブランチを(pull しても)Switch できない

基本的には、スマホからの操作は、main ブランチでコミットプッシュするしかできなさそうです。

終わりに

これから、スマホからいろいろメモを書いていこうかなと思います!

GraphQL Guildのエコシステムって便利だね

GraphQL Guild ってご存知ですか? GraphQL 界隈だと、Code Generator が有名と思いますが、GraphQL Guild は、それら GraphQL 関連の OSS を開発しているグループです。(詳しくは、こちら)

GraphQL Guild のエコシステムって便利だな〜って感じたことがあったので、紹介します。 試したソースコードは、こちらにあります。

GraphQL Schema をダウンロードできる

スキーマ駆動開発をすると、GraphQL のクライアントとサーバーのリポジトリ(GraphQL Schema が置いてある)が分かれることがあります。そうすると、クライアントのリポジトリに、サーバーのリポジトリにある GraphQL の Schema が欲しくなると思います。その状況では、次の解決手段が想像できると思います。

git を扱うと、CI/CD のプロセスやいくつかの場面で、面倒なことがあります。 そこで、GraphQL の SchemaURL を指定するだけで、Schema をダウンロードする機能が、GraphQL CLI にあります。

具体的には、GraphQL Config を作成し、graphql codegen と実行します。

Schema をダウンロードするために、GraphQL のエンドポイント URL を指定します。

Config ファイルの形式は、yml、json、js、ts のどれかを選べます。 私の場合、(supabase を使っている関係で)GraphQL のエンドポイントへアクセスする認証情報を環境変数から読み込みたかったため、Typescript(ts)を選びました。

具体的には、次の Config ファイルを生成します。

// graphql.config.ts
import type { IGraphQLConfig } from "graphql-config";
import { config } from "dotenv";

config();

/** @type {import('graphql-config').IGraphQLConfig} */
const graphqlConfig: IGraphQLConfig = {
  schema: [
    {
      [`${process.env.SUPABASE_URL}/graphql/v1`]: {
        headers: {
          apikey: process.env.SUPABASE_ANON_KEY || "",
          authorization: `Bearer ${process.env.SUPABASE_ANON_KEY}`,
        },
      },
    },
  ],
  extensions: {
    codegen: {
      generates: {
        "generated/schema.graphql": {
          plugins: ["schema-ast"],
        },
      },
    },
  },
};

export default graphqlConfig;

必要なパッケージをインストールした状態で、graphql codegen と実行すると generated/schema.graphql が生成されます!

GraphiQL が Config だけで動く

GraphQL を利用する側としては、どのようなクエリが書けるか試せす場所が欲しくなります。 サーバー側から GraphiQL を用意頂くでも全然良いのですが、GraphQL Yoga というものを使えば簡単にできます。

yarn add graphql-yoga したあとに、先程の graphql.config.ts が存在すれば、yarn yoga するだけで GraphiQL が手に入ります!一切、サーブするコードを書いていません。最高でした。

GraphQL CLI には色々便利な機能がある

GraphQL CLI には、GraphQL 関連で便利な機能があります。

具体的には、次の 3 つです。

  • @graphql-cli/coverage
    • Document のオペレーションを元に、Schema がどれくらい使われているかわかる
  • @graphql-cli/diff
    • ローカルとリモートの GraphQL Schema の違いを教えてくれる
  • @graphql-cli/validate
    • Document のオペレーション が、GraphQL Schema 定義に反していないかチェックしてくれる

終わりに

GraphQL Guild は、GraphQL Config が中心になっている印象を受けました。 Config があれば、他のエコシステムはそれを見て機能が動くため、準備するものが少なくて済みます。

関係ないですが、supabase で GraphQL を使うのもすごく簡単で、ありがたいです。