📁

GitHub Actionsのworking-directoryの罠:runステップごとにディレクトリがリセットされる仕組み

に公開

はじめに

GitHub Actionsでモノレポや複数プロジェクトを扱う際、working-directoryを使ってディレクトリを指定することがよくあります。しかし、このworking-directoryの挙動には注意が必要な罠があります。

今回は、GitHub Actionsのworking-directoryがrunステップごとにリセットされるという重要な仕様について、実例を交えて解説します。

発見したきっかけ

モノレポ構成のプロジェクトで、以下のような構造のリポジトリを扱っていました:

ProjectA/
├── web/
│   ├── productA/
│   │   └── package.json
│   └── package.json
└── api/

このプロジェクトで、webディレクトリ配下の処理を実行するためにGitHub Actionsのワークフローを書いていたところ、予期しない挙動に遭遇しました。

問題のコード

最初、以下のようなワークフローを書いていました:

name: Build Web Application

on:
  push:
    branches: [ main ]

defaults:
  run:
    shell: bash
    working-directory: ./web

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - uses: actions/setup-node@v3
        with:
          node-version: '18'
      
      - run: pwd
        # 出力: /home/runner/work/ProjectA/web
      
      - run: | 
          cd productA
          pwd
          npm install
        # 出力: /home/runner/work/ProjectA/web/productA
      
      - run: pwd
        # 期待: /home/runner/work/ProjectA/web/productA
        # 実際: /home/runner/work/ProjectA/web 😱
      
      - run: npm run build
        # エラー!productAディレクトリではなくwebディレクトリで実行される

何が起きているのか?

上記のコードで、3番目のrunステップでcd productAを実行しているにも関わらず、4番目のrunステップでは元のwebディレクトリに戻ってしまっています。

これは、GitHub Actionsの各runステップが独立したシェルセッションで実行されるためです。つまり:

  1. runステップは新しいシェルプロセスで開始される
  2. defaults.run.working-directoryで指定したディレクトリから毎回開始される
  3. 前のステップでのcdコマンドやその他の環境変更は、次のステップには引き継がれない

正しい解決方法

この問題を解決するには、いくつかの方法があります:

方法1: 各runステップでディレクトリを指定

steps:
  - uses: actions/checkout@v3
  
  - run: npm install
    working-directory: ./web/productA
  
  - run: npm run build
    working-directory: ./web/productA
  
  - run: npm test
    working-directory: ./web/productA

方法2: 1つのrunステップ内で複数のコマンドを実行

steps:
  - uses: actions/checkout@v3
  
  - run: |
      cd productA
      npm install
      npm run build
      npm test

方法3: defaultsを適切に設定

プロジェクト全体で特定のディレクトリを基準にする場合:

defaults:
  run:
    shell: bash
    working-directory: ./web/productA

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      
      - run: npm install
      
      - run: npm run build
      
      - run: npm test

方法4: 環境変数を使った動的なパス管理

env:
  PRODUCT_A_DIR: ./web/productA

steps:
  - uses: actions/checkout@v3
  
  - run: npm install
    working-directory: ${{ env.PRODUCT_A_DIR }}
  
  - run: npm run build
    working-directory: ${{ env.PRODUCT_A_DIR }}

ベストプラクティス

  1. 明示的なディレクトリ指定: 各ステップで必要なディレクトリを明示的に指定する
  2. 関連するコマンドはまとめる: 同じディレクトリで実行する一連のコマンドは、1つのrunステップにまとめる
  3. 環境変数の活用: 頻繁に使用するパスは環境変数として定義する
  4. コメントの追加: 特にディレクトリ操作がある場合は、期待される動作をコメントで明記する

まとめ

GitHub Actionsのworking-directoryは、各runステップごとにリセットされるという重要な仕様があります。これは一見すると直感に反する動作かもしれませんが、各ステップの独立性を保つという観点では理にかなっています。

この仕様を理解し、適切に対処することで、より堅牢なCI/CDパイプラインを構築できます。特にモノレポや複雑なディレクトリ構造を持つプロジェクトでは、この挙動を意識したワークフローの設計が重要です。

参考リンク

Discussion