【GitHub Actions】CI/CDパイプライン構築の際にNo such file or directoryでデプロイしたいデータが送れないときの対処法

## はじめに
GitHub ActionsでCI/CDパイプラインを構築する際に、デプロイ時のデータ転送がうまくいかずにハマったので、解消方法を残しておきます。

### 備考
おそらくあまり環境に依存しない内容だと思いますが、以下を使用しています
- Java21とSpringBoot3を使用したアプリケーションを開発
- AWSのEC2にデプロイ

## やりたいこと

CI/CDパイプラインとして以下の挙動を実現したい

- プルリクエスト作成時にはCIとして自動テストを走らせる
- マージ後にはCDとしてデプロイする

### 変更前の状態

以下のような設定をしていたため、プルリクエスト作成時にデプロイされてしまっていました

```yaml
name: Java CI with Gradle

on:
  push:
    branches: [ "main" ]
  pull_request:
    branches: [ "main" ]

env:
  EC2_USER: 'ec2-user'
  EC2_HOST: 'hogehoge'
  SRC_PATH: 'build/libs/*.jar'
  DEST_PATH: '/home/ec2-user/portfolio-student-management.jar'

jobs:
  build:

    runs-on: ubuntu-latest
    permissions:
      contents: read

    steps:
      - uses: actions/checkout@v4
      - name: Set up JDK 21
        uses: actions/setup-java@v4
        with:
          java-version: '21'
          distribution: 'temurin'

      - name: Setup Gradle
        uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0

      - name: Make gradlew executable
        run: chmod +x ./gradlew

      - name: Test with Gradle Wrapper
        run: ./gradlew test

      - name: Build with Gradle Wrapper
        run: ./gradlew bootJar
        
        
    # 以下がそのまま動くため、プルリク時にデプロイされてしまう
      - name: SCP Copy Application to EC2
        env:
          PRIVATE_KEY: ${{ secrets.AWS_EC2_PRIVATE_KEY }}
        run: |
          echo "$PRIVATE_KEY" > private_key && chmod 600 private_key
          scp -o StrictHostKeyChecking=no -i private_key $SRC_PATH $EC2_USER@$EC2_HOST:$DEST_PATH
      - name: SSH Application Deploy
        uses: appleboy/ssh-action@master
        with:
          host: ${{ env.EC2_HOST }}
          username: ${{ env.EC2_USER }}
          key: ${{ secrets.AWS_EC2_PRIVATE_KEY }}
          envs: DEST_PATH
          script: |
            sudo yum update -y
            if sudo systemctl status portfolio-student-management 2>&1 | grep "Active: active (running)" ; then
            sudo systemctl restart portfolio-student-management
          else
            sudo systemctl start portfolio-student-management

```

### 変更後の状態（エラーあり）

以下のようにjobを`build-and-test`と`deploy`に分けたところ、エラーが発生するようになりました

```yaml
.
.
.

# ジョブを分ける
jobs:
  build-and-test:
    runs-on: ubuntu-latest
    permissions:
      contents: read

    steps:
    - uses: actions/checkout@v4
    - name: Set up JDK 21
      uses: actions/setup-java@v4
      with:
        java-version: '21'
        distribution: 'temurin'

    - name: Setup Gradle
      uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0

    - name: Setup Gradle
      uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0
      
    - name: Make gradlew executable
      run: chmod +x ./gradlew

    - name: Make gradlew executable
      run: chmod +x ./gradlew

    - name: Test with Gradle Wrapper
      run: ./gradlew test
    
    - name: Build with Gradle Wrapper
      run: ./gradlew bootJar
        
  # ここまでは動く

  deploy:
    if: github.event_name == 'push'
    runs-on: ubuntu-latest
    needs: build-and-test
    steps:
    
    # 以下で落ちる
      - name: SCP Copy Application to EC2
        env:
          PRIVATE_KEY: ${{ secrets.AWS_EC2_PRIVATE_KEY }}
        run: |
          echo "$PRIVATE_KEY" > private_key && chmod 600 private_key
          scp -o StrictHostKeyChecking=no -i private_key $SRC_PATH $EC2_USER@$EC2_HOST:$DEST_PATH
      .
      .
      .         	   

```

## エラー内容

```bash
scp: stat local "build/libs/*.jar": No such file or directory
Error: Process completed with exit code 255.
```

## 原因

- GitHub Actionsの仕様上、異なるjobは独立した仮想環境で実行される
    - `build-and-test`と`deploy`は別のマシンで動いているということ
    - つまり、`build-and-test`で作成したファイルは、`deploy`に引き継がれない

## 解決策

- `actions/upload-artifact` と`actions/download-artifact`を使用する
- `actions/download-artifact`の保存先はディレクトリは`$GITHUB_WORKSPACE`で取得可能なため、`$GITHUB_WORKSPACE`を利用して送信元ファイルを特定する

```yaml
.
.
.
  build-and-test:
      runs-on: ubuntu-latest
    permissions:
      contents: read

    steps:
    .
    .
    .
      - name: Build with Gradle Wrapper
        run: ./gradlew bootJar
        
    # 作成したjarファイルをアップロード
    
      - name: Upload Build Artifact
        uses: actions/upload-artifact@v3
        with:
          name: built-jar
          path: build/libs/*.jar

  deploy:
    if: github.event_name == 'push'
    runs-on: ubuntu-latest
    needs: build-and-test
    steps:

    # アップロードしたファイルをダウンロード
      - name: Download Build Artifact
        uses: actions/download-artifact@v3
        with:
          name: built-jar

    # $GITHUB_WORKSPACEを使用して送り元ファイルを特定
      - name: SCP Copy Application to EC2
        env:
          PRIVATE_KEY: ${{ secrets.AWS_EC2_PRIVATE_KEY }}
        run: |
          echo "$PRIVATE_KEY" > private_key && chmod 600 private_key
          scp -o StrictHostKeyChecking=no -i private_key $GITHUB_WORKSPACE/*.jar $EC2_USER@$EC2_HOST:$DEST_PATH
```

## おわりに
自身の使っているchatGPTでは初動でうまく解消できませんでしたが、最初の状態でデプロイができていたことをヒントにGitHub Actionsの仕様を疑うことで比較的早めに解消できました。

CDのテストのために何回かGitHub上でPR->マージを繰り返してしまったので、GitHubが汚れてしまったのが少し残念…
マージしなくても挙動のテストをする方法を調べてみようと思います…