たまには環境面にも目を向けてGitHub Actionsのキャッシュ機能でビルド処理を短縮してみよう

こんにちは。Kaneyasuです。

GitHub Actionsのキャッシュ機能でビルド処理を短縮してみました。
ついでにこれをする中で、こういうみんなが手を出したがらない領域に手を出せるようになると、エンジニアとしての仕事の幅が広がる可能性あるよねと感じたので、この話をしてみます。


# GitHub Actionsによるビルド処理を短縮する

GitHub Actionsは、GitHub上でパイプラインを構築するための機能です。
ビルドやテスト、デプロイなどの処理を自動化したパイプラインを構築することができます。
GitHubへのプッシュやプルリクエストの作成など、イベントに応じてパイプラインを実行することができます。
この機能を用いて、ビルド処理、そして自動テストを行うことができます。

例えば、以下の例はmainブランチにプッシュされた際に、ビルド処理を行うパイプラインを構築しています。
ライブラリのインストールと、自動テストの実行まで行っています。
この例のプロジェクトでは、パッケージ管理に`pipenv`を利用しており、必要なライブラリは`Pipfile`と`Pipfile.lock`に記述されているとします。


```yaml
name: Python build test
on: [push]
jobs:
  build:
    name: Build
    runs-on: ubuntu-24.04
    steps:
    - uses: actions/checkout@v4
    - name: Set up Python
      uses: actions/setup-python@v5
      with:
        python-version: '3.11.6'
    - name: Check Python version
      run: python --version
    - name: Install pipenv
      run: |
        pip install pipenv
    - name: Install dependencies
      run: |
        pipenv install --dev
    - name: Run Unit Tests
      run: |
        pipenv run pytest
```

このビルド処理の中で、意外と時間がかかるのが、`pipenv install --dev`で行っているライブラリのインストールです。
この部分は、以下のようにキャッシュを利用することで、ビルド処理の時間を数十秒から数秒に短縮することができます。

```yaml
name: Python build test pip cache
on: [push]
jobs:
  build:
    name: Build
    runs-on: ubuntu-24.04
    steps:
    - uses: actions/checkout@v4
    - name: Set up Python
      uses: actions/setup-python@v5
      with:
        python-version: '3.11.6'
    - name: Check Python version
      run: python --version
    - name: install pipenv
      run: |
        python -m pip install --upgrade pip
        pip install pipenv
    - name: Cache Dependencies
      uses: actions/cache@v4
      id: cache-dependencies
      with:
        path: ./.venv
        key: ${{ runner.os }}-pip-${{ hashFiles('**/Pipfile.lock') }}
    - name: Install dependencies
      if: steps.cache-dependencies.outputs.cache-hit != 'true'
      env:
        PIPENV_VENV_IN_PROJECT: "true"  # 仮想環境をプロジェクト内に作成
      run: |
        pipenv install --dev
    - name: Run Unit Tests
      run: |
        pipenv run pytest
```

# キャッシュ機能でビルド処理を短縮できる理由

上述の例では、`pipenv install --dev`で行っているライブラリのインストールをキャッシュしています。

```yaml
- name: Cache Dependencies
    uses: actions/cache@v4
    id: cache-dependencies
    with:
    path: ./.venv
    key: ${{ runner.os }}-pip-${{ hashFiles('**/Pipfile.lock') }}
```

GitHub Actionsのキャッシュ機能は、指定したパスのファイルをキャッシュすることができます。
`PIPENV_VENV_IN_PROJECT`を`true`に設定した上で、`pipenv install`でインストールしたライブラリは、プロジェクト内の`.venv`ディレクトリに保存されます。
この`.venv`ディレクトリをキャッシュすることで、ライブラリのインストールを再度行う必要がなくなります。
これをactions/cache@v4を用いて実現しています。

https://github.com/actions/cache/tree/v4.0.0

一方で、何も考えずにキャッシュを利用すると、プロジェクトに必要なライブラリが変わった時にも、古いキャッシュが使われてしまい、ビルド処理が失敗する可能性が出てきます。
この例に挙げているプロジェクトでは、`Pipfile`と`Pipfile.lock`が使われているため、必要なライブラリが変わる＝`Pipfile.lock`が変わる、ということになります。
これを利用し、`Pipfile.lock`をハッシュ化してキャッシュのキーとすることで、必要なライブラリが変わったら、キャッシュがヒットしないようにしています。

キャッシュがヒットしない場合、`steps.cache-dependencies.outputs.cache-hit`が`true`にならないため、`pipenv install --dev`が再実行される次第です。

```yaml
- name: Install dependencies
    if: steps.cache-dependencies.outputs.cache-hit != 'true'
    env:
    PIPENV_VENV_IN_PROJECT: "true"  # 仮想環境をプロジェクト内に作成
    run: |
    pipenv install --dev
```

# 今回キャッシュの利用の仕方にたどりつくために必要な知識

ここから技術的な話じゃなくて、若干ポエム入った考え方の話になります。

前項でキャッシュ機能でビルド処理を短縮できる理由を述べましたが、この方法にたどり着くにはどのような知識が必要だったのでしょうか。
GitHub Actionsのキャッシュ機能はさておき、pipenv installしたらどこにどのようなファイルが保存されるのかと、パッケージ管理ツールの特に`.lock`ファイルの役割を理解している必要があります。

CI/CDのパイプラインを構築する人が、プロジェクトチームの外の人である場合、これらの知識を持ってくれというのは、少し難しいかもしれません。
ライブラリ周りの話はプロジェクト固有の話もあるでしょうから、プロジェクト内の人に任せるのがベストだと思います。
そうではありますが、現実にはプロジェクト内の人でも、知識差があるのが現実でしょう。
何らかの事情で、環境面は他の人に任せて、機能面の開発に専念している人もいると思います。

# 仕事の幅を広げる勇気の源泉となる知識

私はパイプラインのような人があまり手を出したがらない領域に手を出せるようになると、エンジニアとして仕事の幅が広がっていくと考えています。
自分たちの専門領域の外のこともできますよ、または少なくとも歩み寄りますよという姿を見せることで、今までとは違う仕事が舞い込んでくる可能性があります。
とはいえ、それは誰しもができるわけではなく、そこに踏み出すにはある程度の勇気が必要です。
じゃあその勇気ってどこからくるの？と言われれば、それは目の前のものがどう動いているのかをしっかり理解しているかが関係すると思います。
キャッシュの話で言えば、GitHub Actionsのキャッシュ機能自体の経験はないけれど、ライブラリ管理の知識はあるので、もしかしたらこうすればいけるのでは？みたいな感覚が勇気を生んでいきます。

プロジェクトを進めていく中で、どうしても機能面の開発に集中してしまいがちですが、たまには自分たちのコードが何がどうなって動いているのかなどの環境面にも目を向けてみてください。
それで得た知識が仕事の幅を広げる一歩になる気がします。