GitHubプロフィールをカッコよくするGitHubActionsをつくってみた（つもり）

お勉強を兼ねて GitHubActions を作ってみました。

GitHub の各種情報を取得して、SVG に出力する GitHubActions です。
出力イメージは４種類あって、green 版は普通の芝生を立体にした感じ。season 版は芝生の色を季節ごとに変えてみたもの。さらにそれぞれにアニメーションの有版/無版があります。

green 版：

![svg](https://raw.githubusercontent.com/yoshi389111/github-profile-3d-contrib/main/docs/demo/profile-green-animate.svg)

season 版：

![svg](https://raw.githubusercontent.com/yoshi389111/github-profile-3d-contrib/main/docs/demo/profile-season-animate.svg)

season 版は、春夏秋冬をそれぞれ桜花→夏草→紅葉→積雪をイメージした色にしています。

（もともとの芝生は少し寂しかったので、自作の [git-turf](https://github.com/yoshi389111/git-turf) コマンドで文字を書いてみました）

![svg](https://raw.githubusercontent.com/yoshi389111/github-profile-3d-contrib/7c68191fb22f6d26dda444ff3f53da3005f22758/docs/profile-green-animate.svg)

GitHubActionsに毎日１回起動するように登録しておけば、上記のような画像(SVG)が自動で生成されます。profile にこの自動生成される画像を最初に一回貼り付けておけばOK。ブログなどにも貼り付けられます。

また、勢い余ってマーケットプレイスにも公開してしまいました。

https://github.com/marketplace/actions/github-profile-3d-contrib

作ってみてわかったのは、自分にカッコよくするセンスがないということでした。

【追記】

ver3.0.0で以下のようなバージョンも追加しました

![night-view](https://raw.githubusercontent.com/yoshi389111/github-profile-3d-contrib/main/docs/demo/profile-night-view.svg)

![night-rainbow](https://raw.githubusercontent.com/yoshi389111/github-profile-3d-contrib/main/docs/demo/profile-night-rainbow.svg)

（これ👆は、お時間があれば10秒ぐらい眺めていてください）

こちらも参考に

https://qiita.com/yoshi389111/items/46b2b3d7aa4b3022e697

ver0.5.0 や ver0.6.0 でも別バージョンを追加しています。

【追記ここまで】

## ちなみに

これは、GW に書いた駄文３本のうちの１本です。気が向いたらほかの物も読んでみてください。

https://qiita.com/yoshi389111/items/b83ba301f08b2167739d

https://qiita.com/yoshi389111/items/29ade2f62483e9c095d9

## きっかけ

偶然見かけた以下の Qiita 記事がきっかけになりました。

https://qiita.com/e99h2121/items/59ce92f5c2215ecb9f89

* GitHub のプロフィール用のリポジトリの話
* GitHubActions で GitHub のプロフィール用リポジトリに自動で画像を生成するツール
* GitHub のコントリビュートカレンダー（いわゆる芝生）の立体化するブラウザ拡張

https://github.com/vn7n24fzkq/github-profile-summary-cards

https://github.com/jasonlong/isometric-contributions

なんだか、自分でも作ってみたい！　とおもったのです。

## 使い方

使い方を簡単に記載しておきます。

### 手順 1. プロフィール用のリポジトリを作る

ユーザー名と同じ名前で GitHub にリポジトリを作成してください。
（既に作成済みなら、そこを使ってください）

例えば、ユーザー名が `octocat`の場合は、`octocat/octocat` という名前のリポジトリを作成します。

私の場合は `yoshi389111` なので、new repository を選んで `yoshi38911` という名前でリポジトリを作れば `yoshi389111/yoshi389111` になります。

https://docs.github.com/ja/github/setting-up-and-managing-your-github-profile/managing-your-profile-readme

新しく作成したリポジトリで、以降の手順を実行します。

### 手順 2. ワークフローファイルを作る

リポジトリで `Add file` -> `Create new file` を選んで、以下のようなファイル名でワークフローファイルを作成します。

* ファイル名： `.github/workflows/profile-3d.yml`

スケジュールは1日1回開始するように設定されています。
起動時間を都合の良い時間に修正してください。

cron の指定は UTC なので、そこだけ注意してください。
以下のサンプルは日本時間の深夜（早朝）の３時に動くようになっています。

```yaml:.github/workflows/profile-3d.yml
name: GitHub-Profile-3D-Contrib

on:
  schedule: # 03:00 JST == 18:00 UTC
    - cron: "0 18 * * *"
  workflow_dispatch:

jobs:
  build:
    runs-on: ubuntu-latest
    name: generate-github-profile-3d-contrib
    steps:
      - uses: actions/checkout@v3
      - uses: yoshi389111/github-profile-3d-contrib@0.7.1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          USERNAME: ${{ github.repository_owner }}
      - name: Commit & Push
        run: |
          git config user.name github-actions
          git config user.email github-actions@github.com
          git add -A .
          git commit -m "generated"
          git push
```

（上記の定義は古いことがあります。実際に使う時には [GitHub のリポジトリ](https://github.com/yoshi389111/github-profile-3d-contrib) の説明を見てください。また public リポジトリのみを対象する場合には、何も書き換えずにそのまま使用できます。お試しで動かしてみたい人は、サンプルのまま何も変えない `.github/workflows/profile-3d.yml` を自分のリポジトリに登録してみてください）

これにより、アクションがリポジトリに追加されます。

### 手順 3. アクションを手動起動する

追加したアクションを手動で起動してください。

* `Actions` -> `GitHub-Profile-3D-Contrib` -> `Run workflow`

プロフィール画像は以下のパスで生成されます。

* `profile-3d-contrib/profile-green-animate.svg`
* `profile-3d-contrib/profile-green.svg`

* `profile-3d-contrib/profile-season-animate.svg`
* `profile-3d-contrib/profile-season.svg`

green版のサンプル

![svg](https://raw.githubusercontent.com/yoshi389111/github-profile-3d-contrib/main/docs/demo/profile-green-animate.svg)

season版のサンプル

![svg](https://raw.githubusercontent.com/yoshi389111/github-profile-3d-contrib/main/docs/demo/profile-season-animate.svg)


### 手順 4. README.md を追加

生成された画像のパスを readme ファイルに追加します。
画像は４つあるので、内容を見てお好みの物を選んでください。

例：

```md
![](./profile-3d-contrib/profile-green-animate.svg)
```

### 補足１：コミットするメールアドレスについて

workflowファイルの `git config` には GitHubActions のサンプルに書いてあったメールアドレスと名前を書いています。
お分かりだとは思いますが、この状態では自動コミットで芝は生えません。

ズルしたくない、という人は上記のままで問題ないと思います。
どうしても他人のメールアドレスでは嫌だという人は、GitHub に登録されていないご自分のメールアドレス（例えば gmail のエイリアスなど）を登録してください）

逆に自動で芝生を生やしたいという人は、GitHub に登録しているメールアドレスにすることで自動で１日１回のコミットをする、自動芝植えマシンになると思います。

### 補足２：プライベートリポジトリも集計対象としたい場合

上記の `secrets.GITHUB_TOKEN` は自動で作られる特別なトークンです（詳細は以下を参照）。

https://docs.github.com/ja/actions/reference/authentication-in-a-workflow

`secrets.GITHUB_TOKEN` を使う場合、プライベートリポジトリの情報は権限的に取得できないため、集計対象はパブリックリポジトリだけになります。

詳しく言えば、コントリビュートカレンダー（芝生）と、それ以外ではプライベートリポジトリを含めるかどうかの設定方法が異なります。

コントリビュートカレンダーについては、プロファイル画面にある設定 `Contribution settings` から `Private contributions` を選ぶと、SVG 画像にもプライベートリポジトリの情報が反映されます。

コントリビュート数や、星の数、フォーク数などは、`secrets.GITHUB_TOKEN` を使っている場合にはプライベートリポジトリ分は集計されません。

プライベートリポジトリも集計対象とする場合は、「personal access token」を新しく作って（あるいは既存の物を）リポジトリに登録し、ワークフローファイルで指定されている GITHUB_TOKEN に設定します。

多分、以下のような手順になると思います（トークン名は例です）。

* ユーザーの settings -> Developer settings -> Personal access tokens -> Token でトークン作成(repo/userのread権限)
* 自分の名前のリポジトリの settings -> Secrets and variables -> Actions -> Secrets タブ -> 「New repository secret」 に PERSONAL_ACCESS_TOKEN を登録
* secrets.PERSONAL_ACCESS_TOKEN を環境変数に登録する

```diff_yaml
        env:
-         GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+         GITHUB_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
          USERNAME: ${{ github.repository_owner }}
```

ちなみに「ユーザーの settings」と「リポジトリの settings」は別物なのでご注意ください。

## 作成した時の話

以下は、作成したときに考えたことや、詰まったことなどをメモしておきます。

### GitHub GraphQL

まず、芝生の立体化はやってみたい。

芝生情報を取得する方法は、大きく分けて２つありそう。

１つは、GitHub の GraphQL で問い合わせて情報を取得する方法。

もう１つは、GitHub のプロフィールページに含まれる SVG を解析する方法。

今回はいろいろ考えて、GraphQL にしました。
というか、その他の情報（言語情報とか、全リポジトリの星の合計数とか）も取得して載せたかったので、ほぼ一択だったとおもう。

とりあえず、欲しい情報をまとめてみました。

* コントリビュートカレンダー（芝生）の情報
* コントリビュートの種類と数（commit, issue, PullRequest, Review, Repository）
* コントリビュートの言語種類
  * 厳密にいえば、コントリビュートの言語ではなく、コントリビュートしたリポジトリの主要言語をつかう
* 全リポジトリの星の数と、フォークされた数

GraphQL を使うのは始めてだったのですが、GraphQL は以下のサイトで実際に試せるので、まずはクエリを作って試してみました（最初に使うときに認可が求められるので、確認してOKする）

https://docs.github.com/en/graphql/overview/explorer

クエリーはこんな感じかな（ソースは直しても記事の方は直さないので少し古い版かもしれません。参考にするときには注意してください）

```
query ($login: String!) {
  user(login: $login) {
    contributionsCollection {
      contributionCalendar {
        totalContributions
        weeks {
          contributionDays {
            contributionCount
            contributionLevel
            date
          }
        }
      }
      commitContributionsByRepository(maxRepositories: 100) {
        repository {
          primaryLanguage {
            name
            color
          }
        }
        contributions {
          totalCount
        }
      }
      totalCommitContributions
      totalIssueContributions
      totalPullRequestContributions
      totalPullRequestReviewContributions
      totalRepositoryContributions
    }
    repositories(first: 100) {
      nodes {
        forkCount
        stargazerCount
      }
    }
  }
}
```

現状は、見ての通り集計対象のリポジトリ上限は100個にしています。
（……でも100個以上のリポジトリ持っている人もいるよな、どうしよう）

### SVG の組み立て

情報が取得できたら、次は SVG の組み立てです。

SVG の中身は、単なる XML なので、全部自力で造ることも可能だけど、せっかくなので d3.js を活用してみました。

以前、古い版の d3.js を少しだけ使ったことがあるので、作ること自体はそこまで困りませんでした。
（TypeScript で作っていたので、型で少し悩んだところがあるぐらい）

また、SVG にはアニメーションができる機能があるというのを以前見かけたので、今回はそれも取り込もうと考えました。
SVG のアニメーションというと、JavaScript から SVG を加工してアニメーションをするか、CSSでアニメーションするかのイメージですが、SVG のタグでもある程度のアニメーションはつくれるようでした。
具体的には `<animate>` タグを登録して、時間に応じて値を変化させることができるようです。

https://developer.mozilla.org/ja/docs/Web/SVG/Element/animate

今回は、立体化した芝生（緑のビルみたいな感じ）が表示した直後は短くなっていて、そこからググーっと伸びるようなアニメーションを設定してみました。

この時点では GraphQL は実際にはたたかず、上記エクスプローラで返ってきたデータをダミーデータとしてファイルに保存して、それをもとにSVGを作成するプログラムを作りました。

SVG の組み立てる作業自体は特に問題なかったのですが、一番の問題は（冒頭でも言った通り）色や配置、見せ方のセンスが全くないということでした。絶望。

### データーの見せ方について

前述の通り、今回は以下の情報をまとめて表示しようと思っています。

* コントリビュートカレンダー（芝生）の情報
* コントリビュートの種類と数（commit, issue, PullRequest, Review, Repository）
* コントリビュートの言語種類
  * 厳密にいえば、コントリビュートの言語ではなく、コントリビュートしたリポジトリの主要言語をつかう
* 全リポジトリの星の数と、フォークされた数

芝生情報は立体化して表示するのが決まっていますが、それ以外をどう表現するかを考えました。

コントリビュートの種類については、レーダーチャートっぽく見せようと思いました。
ただ、通常の GitHub プロフィールも設定を変えるとレーダーチャートっぽいものが表示されますよね。
でも、あのチャートだと件数の多いものと少ないものの差が大きくて、よくわからない気がしています。

GitHub 純正のレーダーチャート：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97350/e91dc115-c952-3c99-b13d-a7b38f30e9e3.png)


そこで、コントリビュート数の対数をとって、レーダーチャートにしたらどうかと考えました。
つまり、以下のようにします。

* コントリビュート 0 件：対数が取れないので 0 とする
* コントリビュート 1 件：$log_{10}(1) = 0$ なので 0 とする
* コントリビュート 10 件：$log_{10}(10) = 1$ なので 1 とする
* コントリビュート 100 件：$log_{10}(100) = 2$ なので 2 とする
* コントリビュート 1000 件：$log_{10}(1000) = 3$ なので 3 とする
* コントリビュート 10000 件：$log_{10}(10000) = 4$ なので 4 とする
* コントリビュート 10001 件以上：レーダーチャートの範囲外になるので 4 に丸める

これならレーダーチャートは 0～4の範囲で表せます。

こんなイメージです（上のレーダーチャートと同じ値での表示です）：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97350/892d8e5a-7a79-4b07-8256-52c392ab8fe6.png)

次にコントリビュートの言語種類です。

言語情報はいくつかの情報が取得できるようです。

* リポジトリに含まれる、言語別のサイズ（バイト数のようです）
* リポジトリの主要言語（ `primaryLanguage` ）

今回は、コントリビュートしている先のリポジトリの主要言語を、コントリビュートした言語だと判断しています。
（つまり、主要言語が ruby となっているリポジトリのドキュメントに typo があって、プルリクを送ってマージされたとすると、ドキュメントしか直してないけど ruby にコントリビュートしている、と判断されます）

星の数とフォークされた数は、単に本人の全リポジトリの合計を表示するだけです。
（Organizationsにも自分が作ったパブリックリポジトリがあれば、それも加算されると思います）

### GitHubActions の種類

GitHubActions を作るのも、今回が始めてです。

GitHubActions には大きく分けて３種類あるみたい。

https://docs.github.com/ja/actions/creating-actions/about-actions

* Docker コンテナ・アクション
* JavaScriptアクション
* 複合実行ステップアクション

今回は、この中の JavaScript アクションで作ってみました。

### GitHubActions の実装

とりあえずローカルで SVG ができるようになったら、GitHubActions 化を進めます。

プロジェクトのルートに `action.yml` を作って、push して tag を作って、別のリポジトリで動かしてみると、エラー。
直してみて、push して tag を作り直して、動かしてみて、エラー。
と、手順がなかなか面倒ですね。

後から思ったのは、まずは小さな GitHubActions を作ってみて、それが動くところは事前に確認しておいた方が良かっただろうな、ということです。

また、大きく躓いたところが一つあります。

最初、github にある gitignore ファイルのリポジトリをもとに `.gitignore` ファイルを作っていました。

https://github.com/github/gitignore

そうすると、アクション実行時に `dist/index.js` がないと怒られました。

「あー、自動でコンパイルしてくれるわけじゃないから、`dist/` も push しないといけないのか」と思い push すると今度は `node_modules` がないと怒られました。

JavaScript アクションの作り方は以下のページに説明がありますが、そのなかに

https://docs.github.com/ja/actions/creating-actions/creating-a-javascript-action

> ターミナルから、action.yml、index.js、node_modules、package.json、package-lock.json、および README.md ファイルをコミットします。 node_modules を一覧表示する .gitignore ファイルを追加した場合、node_modules ディレクトリをコミットするため、その行を削除する必要があります。

という記述がありました。

非常に大量のファイルのある（噂ではブラックホール以上に重たいという） `node_modules` を commit しろとのこと（もちろん push もするのでしょう）。

「マジか！」と思いながら、push したらさらに変な動きになってしまいました（実行時には使うはずのない `ts-node` がないとか怒られた。それも実行時ではなく、setup 時に）。

悩んだ挙句、上記ページの続きに書いてある以下の記述

> `node_modules` ディレクトリをチェックインすると、問題が発生する可能性があります。 別の方法として、`@vercel/ncc` というツールを使用して、コードとモジュールを配布に使用する 1 つのファイルにコンパイルできます。

を見つけて、github から `node_modules` を削除して、`@vercel/ncc` で関連ライブラリも含めて `index.js` にして実行するようにしました。

### その他

今回のアクションで作成できる SVG は前述の通りアニメーションするのですが、アニメーションしない方が良いという人もいるかなと思い、アニメーションあり版となし版を生成するようにしています。

また、芝生の色を季節ごとに変えたらきれいかとおもって試しに実装しています。

ちなみに、例えば春に見たときと、夏に見た時で色が違うという意味ではありません。
春頃のコントリビュートは桜色（梅・桃・桜をイメージ）で、夏頃のコントリビュートは緑色（いつもな感じ）で、秋頃のコントリビュートは黄色や紅色（紅葉をイメージ）みたいな感じを考えていました（冬は積雪をイメージ）。

ですが、現状の実装だと季節の切れ目で、スパっと色が変わるのでちょっと今一つでした。
季節の変わり目はグラデーションのように色を変えた方がよさそうですね。今後の課題とします（バージョン0.2.0で実装しました）。

もし気になる人がいたら試してみてください。以下のリポジトリ、あるいは前述したマーケットプレイスのページに手順が書いてあります。

https://github.com/yoshi389111/github-profile-3d-contrib

まだ、異例なケースでの不良があるかもしれないで、うまく動かなかったら教えてください。よろしくお願いします。

もう一つ、GitHubActions のマーケットプレイスの Social preview (OGP画像)は、GitHub のリポジトリの Social preview がそのまま使われます。公開する時には、事前に設定したほうが良いと思います。

## ついでに

あわせて GW に書いた駄文です。気が向いたら読んでください。

https://qiita.com/yoshi389111/items/b83ba301f08b2167739d

https://qiita.com/yoshi389111/items/29ade2f62483e9c095d9