3年ほど前にGitHubのREADMEをサクッと高品質で書けるツール「LEADYOU」を開発し記事を公開したところ、800を超えるいいねをいただき良い感じのスタートを切れたのですが、公開から半年ほど経つとアクティブユーザーがほぼいなくなり、サーバー代金を払い続けるのが厄介になったため人知れずサービスを終了していました。
しかし、せっかくのアイデアや実装が勿体無いと思い、なんとかランニングコストとメンテナンスコストを抑えて配信できないかを考えました。その結果、3連休を生贄にして最高の配信構成を組むことができたので方法をまとめます。
TL;DR
- Webサイトホスティングは
GitHub Pages
を使用- webpackによるReact × TypeScript構成のビルドは
GitHub Actions
で行う
- webpackによるReact × TypeScript構成のビルドは
- サーバーサイド(データベース)はJSONファイルを読み込むように置き換え、
GitHub Actions
で直接リポジトリのファイルを定期的に書き換える - サーバーサイド(API)はツールの特性上
GitHub REST API
で事足りた
※ LEADYOUは商用ではなく、あくまで開発者を支援するツールなのでこの構成が成立している側面はあります。
LEADYOUの機能要件
- トップページ
- 入力されたURLがPublicなGitHubリポジトリのURLであるかを判別する
- GitHubリポジトリのURLであった場合、オーナー名とリポジトリ名を抽出する
- 実際にツールを利用して作成され掲載されているREADMEの一覧を表示する
- フッターでこれまでに作成されたREADMEの累計数を表示する
- フォーム入力ページ
- 定義されている構成に従ってフォームを自動形成する
- オーナー名とリポジトリ名の情報を用いてフォームに入力すべき情報を取得し自動穴埋めをする
- フォームの入力内容からREADMEのMarkdownを構成し、プレビュー表示したりダウンロード用のZipを生成する
- ドキュメントページ
- Markdownで書かれたドキュメントをHTML化して表示する
上記の機能要件のうち、READMEの一覧表示、累計数の表示、フォームの自動穴埋めはREST APIサーバーを用意して行なっていました。また、デプロイも同じAPIサーバーで行なっていたためファイルサーバーも兼任させていました。
また、READMEの一覧表示と累計数の表示に関しては、過去にREADMEを生成したリポジトリの情報をデータベースで持っておき、フロントに要求されたら最新N件のURLリストと累計数を返すようにしていました。
その他はフロントエンドで処理していました。
機能要件実現の課題
GitHub REST APIを認証なしで叩く場合、1時間あたり60回という制限があります。そのためユーザーが認証なしで使えるツールとして提供するには、なるべくサーバーサイドで認証ありでAPIを叩く必要がありました。
GitHub APIは、入力されたリポジトリの存在チェック、フォームの自動穴埋め用データ取得、過去にREADMEを生成したリポジトリ一覧の更新で必要です。一つ目に関しては、当初APIを叩かずに実際のリポジトリページへアクセス可能かどうかで判断しようとしていたのですが、CORSに阻まれて敢えなく断念しました。二つ目と三つ目に関してはAPIを用いないと取得できない情報ですが、大量にAPIを叩く必要があるため認証ありにせざるを得ませんでした。
元々の技術構成
- フロントエンド
- Pure JavaScript
- Web Components
- Marked
- バックエンド
フロントエンドについての補足
当時2人で開発していたのですが、そのメンバーが学習コストなしでフロントとバック両方書けるようにメインで書く言語はJavaScriptだけで済むようにしていました。
また、READMEに書く項目を手軽に変更可能なようにしようというアイデア実現するために、各項目の入力フォームの特徴をJSONで定義しておき、それを元にフォームを自動形成する仕組みを作りました。具体的な実装は@AkiraKashiharaのこだわりによりWeb Componentsを用いました(詳しくはこちらの記事を読んでください)。
さらに、Markdownに関するツールを作るというテーマからとことんMarkdownを使ってやろうという思いで、ドキュメントは全てMarkdownで書いておき、そのリソースを読み込んでHTMLに変換して表示することにしました。
バックエンドについての補足
なるべく安くor無料で使えるものを利用しようということでデータベースにはMariaDBを用いました。
また、開発環境やデプロイ環境の差分を吸収するためにDockerでNode.jsが動く環境を用意しました(実際に私の開発機はMacで相方の開発機はWindowsの時もありました)。
デプロイサーバーに関しては、最初は相方が得意なGCPを使っていたのですが、途中から私が得意なAWSに切り替えて運用していました。ランニングコストは月額1500円くらいでした。
本題 技術構成の見直し
冒頭にも書いたように、ランニングコストとメンテナンスコストの削減を目的として技術構成を見直しました。
ランニングコスト面
普通サーバーを運用するにはお金がかかります。WebサイトのホスティングとAPIサーバーの可動の両方を実現するためにはVPSやレンタルサーバーの利用が避けられないように思えますが(自前サーバー過激勢は除外)、我々にはGitHub Pages
とGitHub Actions
という強い味方がいます。
GitHub Pages
は個人ユーザーやOrganizationがWebサイトをGitHub上のリポジトリから直接ホスティングできる仕組みです。しかもPublicなリポジトリであれば無料です。もちろん商用利用禁止などの制限事項はありますが、簡単にWebサイトをホスティングしてくれるのですから非常にありがたいです。
GitHub Actions
はビルド、テスト、デプロイのパイプラインを自動化できるCI/CDの仕組みです。リポジトリに対するPushやPRへのイベントをトリガーにして事前に定義した処理を実行するワークフローを作成できます。嬉しいことに、ワークフローの中ではGitHub APIを叩くのに必要なトークンがデフォルトで用意されていたり、ワークフローを定期実行することができたり、リポジトリに直接差分をCommitできたり、GitHub Pages
のデプロイ方法を細かく調整できたりします。しかもPublicなリポジトリなら無料です。つまり無敵です。
GitHub Actions
を使って定期的にGitHub APIを叩き、LEADYOU製のREADMEが掲載されているリポジトリの検索を行い、そのリストをJSONファイルとして書き出してリポジトリに上書きすることができます。そのため、定期的なデータ更新が行われるデータベースとして活用することができます。リアルタイム性には欠けますが、本ツールの要件は十分に満たせます。なお、APIサーバーとして活用するにはGitHub Actions
は向いていません。ので、別のサービスでその要件がある場合、GASを使えばなんかできるんじゃないかと邪推しました。
メンテナンスコスト面
まず、JavaScriptは静的型言語ではないので、黒魔術とか錬金術みたいな感じでメンテナンス性の悪い実装が容易にできてしまうのがよろしくないと思っていました。なのでTypeScriptを選択しました。
また、Web ComponentsはWebの標準技術であるため特別な環境導入が不要であるというメリットはあるのですが、おそらくReactやVue.jsなどと比較すると普及しておらず、筆者も不慣れに感じていました。今回は宣言的UIでメンテナンス性の高いデータフローを高速に実装したかったので、筆者が比較的手慣れているReactを採用しました。(普段はSwiftUIを書いているので、Reactは馴染みやすいです。なお、Web Componentsで宣言的UIを実装できるLitなるものもあるそうです。)Pure JavaScriptでのDOM操作をしなくて良いので、パッとコードを眺めただけでもどんな画面が構成されるのかがわかります。これは素晴らしいことです。
React × TypeScript構成をビルドするのにwebpackとNode.jsを使います。(もっとモダンなやつもありそうですが不勉強です。)
バックエンドのメンテナンスコスト性に関してもGitHub Pages
とGitHub Actions
は最高のパフォーマンスを与えてくれます。VPSにSSHで入って複雑な操作を行う必要もありませんし、GitHub Pagesは勝手にHTTPS対応してくれますし、GitHub ActionsでのデプロイもチョチョイとYAMLを書くだけ(webpackビルドのためのNode.js環境セットアップならactions/setup-nodeを使うだけ)です。なんといっても、アクセスするプラットフォームがGitHubだけですから、スイッチングコストが全くありません。
唯一の問題点は、GitHubが落ちたらサービスも完全に止まることだけです笑
一蓮托生ってやつですよ。
新しい技術構成
ということで以下のような構成になりました。
- フロントエンド
- TypeScript
- React
- Marked & react-markdown
- webpack
- Node.js
- バックエンド
- GitHub Pages
- GitHub Actions
- Node.js
いかがでしょうか?結構いけてると思いませんか?
元となる実装があったとはいえ、3日でリプレイス作業ができたのもGitHubの力が大きかったです。本記事をきっかけに、ぜひ皆さんもGitHub Pages
とGitHub Actions
を活用してみてください!
あと、LEADYOUを使ってみたら感想やコメントをぜひください!