ドット絵の枚数をREADME.mdに自動反映させたい

こんにちは。なっしゅです。
今取り組んでいる作品でドット絵をGitHub上にあげていますが、その枚数を計算して`README.md`に自動的に表示する機構を作る機会があったので備忘録として残します。

### したいこと
1. ドット絵をdot-worksディレクトリにpushする。
2. GitHub Actionsのworkflowが実行される。
3. C++をコンパイル、実行し`dot-works`内の`.png`の枚数を`${COUNT}`として取得する。
4. `README.md`に`${COUNT}`を書き込む。

### 実装
まず1つ目の目標として「push時にC++をコンパイル、実行する」ところまで実装します。
`.github/workflows/.yml`を作成します。
```yml:.yml
name: Write the number of dot works down on README.md

on: [push]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2
    - name: Install g++
      run: sudo apt-get install g++ -y
    - name: Compile C++ program
      run: g++ counter.cpp -o counter
    - name: Run C++ program
      run: ./counter
      
```
`dot-works`ディレクトリ内のドット絵の枚数(=`.png`の枚数)、を数えるC++のコードは以下の通りです。
```cpp:counter.cpp
#include <iostream>
#include <filesystem>

int main() {
    const std::string path = "dot-works";
    int count = 0;

    for (const auto& entry : std::filesystem::recursive_directory_iterator(path)) {
        if (entry.path().extension() == ".png") {
            ++count;
        }
    }

    std::cout << count << std::endl;
    return 0;
}

```
実行したところ、どうやら`<filesystem>`はC++17の環境で動作するようです。
よって、`.yml`を以下のように書き換えました。
```yml:.yml
name: Write the number of dot works down on README.md

on: [push]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2
    - name: Install g++
      run: sudo apt-get install g++ -y
    - name: Compile C++ program
      run: g++ counter.cpp -o counter -std=c++17
    - name: Run C++ program
      run: ./counter
      
```
pushしたら上手くいきました。
では次に`README.md`に書き込むように`.yml`をいじっていきます。
```yml:.yml
name: Write the number of dot works down on README.md

on: [push]

jobs:
  update-readme:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
      with:
        ref: 'main'
    
    - name: Install g++
      run: sudo apt-get install g++ -y
    
    - name: Compile and Run C++ Program
      run: |
        g++ counter.cpp -o counter -std=c++17
        ./counter > output.txt
    
    - name: Update README
      run: |
        COUNT=$(cat output.txt)
        README_CONTENT=$(cat README.md)
        echo -e "${README_CONTENT}\n今までで**${COUNT}**つのドット絵作品が描かれています。" > README.md
    
    - name: Commit and Push if Changes
      run: |
        git config --local user.email "action@github.com"
        git config --local user.name "GitHub Action"
        git add README.md
        git commit -m "Update README with .png file count" -a || echo "No changes to commit"
        git push

```
これはファイルの末尾に文字列を追加する場合のコードです。
`echo`では`-e`オプションをつけていますが、これは`\n`を含めたエスケープシーケンスを有効にするためのオプションです。また、`echo`のあとの文字列をシングルクォーテーションで囲ってしまうと`${COUNT}`が変数ではなく文字列として認識されてしまうので注意してください。

最後に、READMEの好きな場所に文字列を挿入することを考えましょう。
`sed`コマンドで文字列を挿入する位置を定めるため、`README.md`の文字列を挿入したい場所に次のようにコメントを書きます。
```md:README.md
<!-- PNG_COUNT_START -->
<!-- PNG_COUNT_END -->
```
このように書くことによって文字列を追加、ではなく更新することができます。
`sed`コマンドを使って`.yml`を次のように書き換えます。
```yml:.yml
name: Write the number of dot works down on README.md

on: [push]

jobs:
  update-readme:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
      with:
        ref: 'main'
    
    - name: Install g++
      run: sudo apt-get install g++ -y
    
    - name: Compile and Run C++ Program
      run: |
        g++ counter.cpp -o counter -std=c++17
        ./counter > output.txt
    
    - name: Update README
      run: |
        COUNT=$(cat output.txt)
        sed -i "/<!-- PNG_COUNT_START -->/,/<!-- PNG_COUNT_END -->/c\<!-- PNG_COUNT_START -->\n今までで**${COUNT}**つのドット絵作品が描かれています。\n<!-- PNG_COUNT_END -->" README.md
    
    - name: Commit and Push if Changes
      run: |
        git config --local user.email "action@github.com"
        git config --local user.name "GitHub Action"
        git diff --quiet && git diff --staged --quiet || (git commit -am "Update README with .png file count" && git push)

```
これで文字列を任意の場所に挿入することができました。
以上です。

ファイル階層
```
.
├───.github/
│   └───workflows/
│       └───.yml
├───dot-works/
└───counter.cpp
```