📸

deno-puppeteer と GitHub Actions で LeetCode の ストリーク をREADMEに掲載する

2023/02/19に公開

最近、アルゴリズムとデータ構造の勉強のために、LeetCode を 1 日 1 問解いています。今朝ふと LeetCode のストリークを GitHub の README に載せたいなと思い立ちやってみました。

🦾 作ったもの

以下画像のように LeetCode のプロフィールページのストリークと回答問題数を、 GitHub リポジトリの README.md に掲載するスクリプトを作りました。

ダークモードにも対応しています。

Easy しかほぼ解いていないところに実力ががが... 🥹

🛠️ 仕組み

仕組みはとてもシンプルです。
以下一覧の処理を GitHub Actions の cron で定期実行しています。

  • LeetCode のプロフィールページの画像を撮影しファイルとして保存
  • README.md を更新
  • 変更をコミット

コードはすべてこちらにあります。

https://github.com/kawamataryo/leetcode

1. deno-puppeteer でのスクリーンショットの撮影

LeetCode のプロフィールページの撮影には deno-puppeteer を利用しています。

https://github.com/kawamataryo/leetcode/blob/main/scripts/lib/capturePage.ts#L3-L54

xpath で対象要素(ストリークと、回答問題)を指定してスクリーンショットを撮影し、指定のフォルダーに保存しています。

ポイントは、通常用と dark モード用の 2 枚を撮影している点です。

dark モードの切り替えは html タグのdarkクラスの付け替えで行っています。
emulateMediaFeaturesという API でできるという記事もあったのですが、LeetCode の画面の場合、それではうまく動かずこちらの方法にしました。

scripts/lib/capturePage.ts
await page.evaluate((html) => {
  html.classList.add("dark");
}, await page.$("html"));
await streakElement?.screenshot({ path: `images/${name}_dark.png` });

またスクリーンショットの解像度をあげるため大きめの viewport にして、さらにdeviceScaleFactor を調整しています。

scripts/lib/capturePage.ts
await page.setViewport({
  width: 1920,
  height: 1080,
  deviceScaleFactor: 2,
});

2. README の更新

README の更新はこちらの関数で行っています。

https://github.com/kawamataryo/leetcode/blob/main/scripts/lib/updateReadme.ts#L3-L31

あらかじめ README.md に記載しておいた以下のマーカーコメントを、画像タグと更新日時のテキストを使って置き換えることでデータを更新しています。

<!--START_SECTION:leetcode-streak-updated-time-->
<!--END_SECTION:leetcode-streak-updated-time-->

dark モードで画像を切り替えるために、<picture>タグの media 属性にprefers-color-schemeを指定しています。

scripts/lib/capturePage.ts
<a href="https://leetcode.com/${user}/" target="_blank">
<picture>
  <source media="(prefers-color-scheme: dark)" srcset="./images/problems_dark.png" width="500">
  <img alt="" src="./images/problems.png" width="500">
</picture>
</a>

https://github.blog/changelog/2022-05-19-specify-theme-context-for-images-in-markdown-beta/

3. GitHub Actins での定期実行

1, 2 の処理を実行するスクリプトを定期実行し、差分をコミットする GitHub Actions を作っています。

https://github.com/kawamataryo/leetcode/blob/main/.github/workflows/capture.yml#L1-L27

deno-puppeteer の実行のための Chromium のインストールが必要なので注意です。

.github/workflows/capture.yml
- name: Install Puppeteer
  run: PUPPETEER_PRODUCT=chrome deno run -A --unstable https://deno.land/x/puppeteer@16.2.0/install.ts
- name: Capture streak
  run: PUPPETEER_PRODUCT=chrome deno task run
  env:
    LEET_CODE_USER_NAME: kawamataryo

変更差分の Commit は、EndBug/add-and-commitを使っています。
default_author をgithub_actionsとすれば、勝手に bot ユーザーでコミットをしてくれるのはとても便利ですね。

.github/workflows/capture.yml
- uses: EndBug/add-and-commit@v9
  with:
    default_author: github_actions

最後に 1 日 1 回 GitHub Actions の cron で実行されるように設定すれば完成です。

.github/workflows/capture.yml
on:
  push:
    branches:
      - main
  schedule:
    - cron:  '0 0 * * *'

おわりに

deno の開発体験が素晴らしく思いのほかサクサク進みました。
後で時間がとれれば、もっと手軽に利用できるよう GitHub Actions のカスタムアクションとして作り直し、GitHub Marketplace に公開したいです。

Discussion