azu / azu/english-notes

Cloudflare Workers + GitHub Issues + GitHub Actionsでブログを作る

Open
#3 0 comments 2 reactions 0 assignees View on GitHub
public
Dominant language
TypeScript
Stars
8
Forks
0
PR merge metrics
No merged PRs in 30d

Description

英語の勉強ブログみたいの復帰させようとして、GitHubでやるのがいいと思ってたタイミングでGitHub IssueをCMSとして使う話を見たので作った。

## 元ネタ:

- [2020-11-08 このブログの実装 2020年版 - waka.dev](https://waka.dev/entry/2020-11-08%20%E3%81%93%E3%81%AE%E3%83%96%E3%83%AD%E3%82%B0%E3%81%AE%E5%AE%9F%E8%A3%85%202020%E5%B9%B4%E7%89%88)
- [waka/waka.dev: Just a logging.](https://github.com/waka/waka.dev)

## 元ネタからの変更点

- URLを issueの `number` (連番) ベースにした
- [Cloudflare Workers](https://workers.cloudflare.com/)の無料枠で動くようにするため、[KV](https://developers.cloudflare.com/workers/runtime-apis/kv)ではなく[Cache API](https://developers.cloudflare.com/workers/runtime-apis/cache)に変更

URLを `number` のIDベースにしたかったので、GraphQLのクエリも特定のIssue Idを元に取得するものへ変更している。

https://github.com/azu/english-notes/blob/07cd24857af20b55533eee9fe89f2789ddc3d92b/src/api_client.ts#L47-L68

このクエリをもっと頑張ると、特定のissueやprのURLからその情報をまとめて取得するクエリとかも作れる。
これをREST APIでやるとn回のAPIコールする必要があるけど、GraphQLだと1回でできる(ただし、クエリもそれなりに遅い。数十コだと2-3秒かかることある)

- [azu/my-board-for-github: Project Board to handle across all GitHub repositories!](https://github.com/azu/my-board-for-github)

元のコードだと、Cloudflareの$5/monthで使える[Cloudflare Workers KV](https://www.cloudflare.com/products/workers-kv/)という機能を使っていた。

- https://github.com/waka/waka.dev/blob/07c752bfd482c84237c64f9a218acd9e15bccde9/src/proxy.ts#L11

ただ、元データはIssueでブログ自体はIssueの別Viewでしかないので、ただのキャッシュで問題なさそうと思って[Cache API](https://developers.cloudflare.com/workers/runtime-apis/cache)ベースに変更した。
[Cloudflare Workers KV](https://www.cloudflare.com/products/workers-kv/)はKey-Value Storageなので、もっとインタラクティブな機能とか、デプロイせずに状態を変更したい場合は必要になってくる感じ。
ブログみたいなやつでは、リクエストに対するレスポンスがキャッシュできて、それが配信できればよかったので[Cache API](https://developers.cloudflare.com/workers/runtime-apis/cache)でほぼカバーできていた。

[Cache API](https://developers.cloudflare.com/workers/runtime-apis/cache)の制限として、キャッシュの[Delete](https://developers.cloudflare.com/workers/runtime-apis/cache#delete)や中身を見ての処理などは制限されていた。
Cache APIはService Workerででてくる[Cache インターフェイス](https://developer.mozilla.org/ja/docs/Web/API/Cache)の実装になっているけど、`caches.delete(namespace)` や `cache.keys()`、`cache.has()` などは実装されてなくて、キャシュを列挙したりキャッシュの中身を見ることはできないようになっていた。

なので、Cache APIでは[opaque response](https://developers.google.com/web/updates/2015/03/introduction-to-fetch)を扱う感じになって、明示的にキャッシュを中身を見て処理を分ける - つまりCache APIをKey-Value Storageみたいに使うようにはできてなかった。

デプロイしたときに、Cache APIのキャッシュをすべてクリアしたい場合は`caches.open(namespace)`で、デプロイ時に新しいキーを渡してそれを元にキャッシュを扱うと擬似的にキャッシュをクリアできる。

```js
const currentCaches = await caches.open(新しいネームスペース);
```

- https://github.com/azu/english-notes/blob/07cd24857af20b55533eee9fe89f2789ddc3d92b/src/proxy.ts#L8
- [How the Cache works · Cloudflare Workers docs](https://developers.cloudflare.com/workers/learning/how-the-cache-works#cache-api)

CloudflareのAPIに[Purge All Files](https://api.cloudflare.com/#zone-purge-all-files)もあるけど、`workers_dev`(`worker.dev`ドメイン)を使っている場合は、そもそもzone idがないため、このAPIの使い方が謎な状態になっていた。(よくわからない)

- https://api.cloudflare.com/#zone-purge-all-files

[Cloudflare Workers KV](https://www.cloudflare.com/products/workers-kv/)の場合は、ちゃんと削除コマンドが用意されているので、Issueを変更したらキャッシュだけ消すみたいのも実装できる。
GitHub Actionでcurl叩くのに10秒ぐらいなので、大体20秒ぐらいで反映されるブログが作れると思う。

今のCache APIを使った仕組みだと、Issueやコードに変更があるたびにGitHub Actionsでデプロイしているので、反映まで1-2分かかる感じになっている。

- https://github.com/azu/english-notes/tree/main/.github/workflows

## デバッグ

Cache APIは `wrangler preview` では無効化されているので、基本的にデプロイしないとデバッグできない。
また、Cloudflare Workerにはデバッグ用のダッシュボードとかがまだないので(管理画面はログすら見れない)、`wrangler tail`を使ってデプロイしたもの標準出力/標準エラーをコンソールに出す感じでデバッグした。

`wrangler tail`の使い方がめっちゃわかりにくいので使い方のメモ。

デフォルトのCloudflare Worker用のトークンには"Workers Tails"のパーミッションがついてないので、トークンにパーミッションをつけるところから始める必要がある(最小権限の原則らしい)

次のようなエラーがでた場合は"Workers Tails"のパーミッションがついてない。

```
🦚 Setting up log streaming from Worker script "xxx". Using ports 8080 and 8081.
Closing tail session...
Error: ⚠️ Code 10000: Authentication error
🕵️ Make sure your API token has permission to both edit and read workers on your account
````

`wrangler tail` の使い方

1. https://dash.cloudflare.com/profile/api-tokens からログインしてるトークンに"アカウント > Workers Tails > 読み取り"の権限を追加する
2. `wrangler tail --env production` で待機する(たまに失敗する)
3. 実際にアクセスしてエラーを発生させる

![image](https://user-images.githubusercontent.com/19714/98470151-3d278080-2227-11eb-9ce5-8e0763d6a9a7.png)

- [Debugging Workers · Cloudflare Workers docs](https://developers.cloudflare.com/workers/learning/debugging-workers)

## パフォーマンス

元ネタとほぼ同じ。

![Lighthouse](https://user-images.githubusercontent.com/19714/98470336-4a913a80-2228-11eb-8280-1ddd67825816.png)

![Network](https://user-images.githubusercontent.com/19714/98470365-714f7100-2228-11eb-8061-9fe26f5d8ef8.png)

GitHub Issueを直で見たときも最速は30msでDocumentは返ってくる。
ただ他のJavaScriptをまったり、たまにつっかかるときがある感じ。

![image](https://user-images.githubusercontent.com/19714/98470405-afe52b80-2228-11eb-9ce8-b28237132c62.png)

ブログの方は余計なものがまだ何もないので、常にその最速のパターンを引いている感じ。

GitHubリポジトリを別の見せ方したいことは結構あるので、そういう用途には[Cloudflare Workers](https://workers.cloudflare.com/)は結構向いているかもしれない。

## エディタ

Issue画面でそのまま入力している。

[textlint editor](https://azu.github.io/slide/2020/textlint-editor/textlint-editor.html)とか[Grammarly](https://app.grammarly.com/)の拡張とか動くし、画像もそのままアップロードできるし、リロードで保存されてるからまあ普通によさそう。

もっといいエディタ使いたかったらコピペするとか、[korefile](https://github.com/azu/korefile)みたいのを使ってリポジトリをCMS的に使うとかもできるけどあんまり気軽じゃなくなるので、ウェブで完結する形であることに意味がありそう。

![image](https://user-images.githubusercontent.com/19714/98470514-677a3d80-2229-11eb-85ee-ee89d13073e5.png)

## おもしろいポイント

GitHub Issueなので、 #1 や @azu みたいなリファレンスリンクやメンションがもそのままブログにリンクとして反映される。
これは、レンダリングはGitHub側がやっているHTMLをそのまま表示しているからだと思う。

GitHubのCSSを使うと表示も似た感じになる。(コードリンクの埋め込みやシンタックスハイライトも機能する)

- [sindresorhus/github-markdown-css: The minimal amount of CSS to replicate the GitHub Markdown style](https://github.com/sindresorhus/github-markdown-css)

この発想自体は、昔書いてたものと似ているので、結構馴染みある。

- [azu on Twitter: "GItHub Isssueをそのままマイクロブログにするのを思いついたけど、あんまり嬉しいユースケースが思いつかなかった。 データからHTMLを生成すれば静的サイトになる、WebHookを使えばコメントがあるたびに更新できる、画像が投稿できる、Issueへのコメントがそのままブログのコメントとなる。" / Twitter](https://twitter.com/azu_re/status/969903462199713794)

これを書いてたときと違ってGitHub Actionsがあるので、自分のブログ(GitHub Issues)に対するbotみたいなものが簡単につくれるというのがおもしろいポイントになるかもしれない。

特定のワードがあったらIssueに反映する仕組みとか、そういうのもGitHub Actionsでイベントで書けるし、GraphQLがかなり柔軟なのでREST APIでは面倒なアグリゲーションとかもできてしまうので、いろいろ遊び方がありそう。

その他

- Jekyllとかのブログと違って、Pull Requestで修正みたいなことができない
- 権限的にCollaboratorにやってもらうしかできない
- レビューみたいなのはやりにくい(PRだとコメント書けるのでセルフレビューしやすいし、CIもつなぎ込みやすい)
- ファイルベースではないので、まとめて編集みたいのは仕組みが必要になる
- ローカルで編集するときにgit pullしなくてもいいので楽
- コンテンツとコメント欄が一体的になっているので遊びの余地がありそう
- GitHub Issueはmp4のアップロードに対応していない
- デプロイ時に記事やサイトを静的サイトとしてビルドするわけじゃないので、反映が比較的早い
- リクエスト受けたタイミングでSSRして、それをCDNにキャッシュというのをCDN Edgeで動かしている
- キャッシュがない場合はSSRを含め表示されるまで400~500msぐらいかかってる

Contributor guide

No contributing guide indexed for this repository

Research direction

This issue is a detailed project write-up rather than a concrete change request. Start by reading src/api_client.ts, src/proxy.ts, and the files under .github/workflows, then review the described Cloudflare Workers deployment and debugging flow. Done is not defined by the issue, so a maintainer would need to specify a documentation goal or actionable follow-up first.

Written by the indexing model from the issue text.

Assessment

Tech stack
github-actions, graphql, typescript
Domain
cloud, documentation, web-dev
Issue type
Documentation
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
20/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.