GitHub Actionsで制約を突破！サードパーティActionsなしで自前アクションを作った話

この記事は[ミライトアドベントカレンダー](https://qiita.com/advent-calendar/2024/miraito-inc) 19日目の記事です。
昨日は [ほげさん](https://zenn.dev/suzuki_hoge) の [概観 トランザクションとロック - 用語整理と最初の一歩 -](https://zenn.dev/suzuki_hoge/books/2024-12-mysql-tx-a6ea4d00e8bd70) でした。

トランザクション分離レベルについては学生時代に勉強したっきりで、これまであまり気にしてませんでした...
この機会に勉強させていただこうと思います。

ZennのBookって章ごとにページ分けられて良いですね、Qiitaだと長い1ページが長くなっちゃうので...

さて、本日の記事は `Pest アーキテクチャテスト` についての記事を書く予定でしたが、時間が足りず別のネタに急遽変更しました。
コード書いたり、検証が必要な内容だと完成させるの大変でした...

仕切り直して本日の記事は **GitHub Actionsで制約を突破！サードパーティActionsなしで自前アクションを作った話** です。

## 何が起きたのか

このワークフローはPHPUnitを実行して自動テストを行なってくれるワークフローです。

```yaml:.github/workflows/phpunit.yml
name: PHPUnit
on:
  pull_request:
jobs:
  phpunit:
    runs-on: ubuntu-20.04
    timeout-minutes: 20
    steps:
      - uses: actions/checkout@v4
      - name: Setup PHP with composer v2
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.2'
          tools: composer:v2
      - name: PHP Version
        run: php -v
      - name: Composer Version
        run: composer -V
      - name: Install Dependencies
        run: composer install --quiet --prefer-dist --no-progress --no-interaction --no-scripts --no-ansi
      - name: PHPUnit Testing
        run: ./vendor/bin/phpunit
```

こんな感じのPHPとComposerをインストールして、PHPUnitを実行するシンプルなワークフローです。

えいや！とgit pushしたらGitHub Actions上で `Startup failure` のエラーが表示されました。詳細なエラーは次の内容でした。

:::note alert
Error
shivammathur/setup-php@v2 is not allowed to be used in company-name/project-name. Actions in this workflow must be: within a repository that belongs to your Enterprise account, created by GitHub, or matching the following: aws-actions/*, gradle/*, hashicorp/*, redhat-actions/*, google-github-actions/*.
:::

なんとPHPの環境構築をしてくれる [shivammathur/setup-php](https://github.com/shivammathur/setup-php) ライブラリが許可されていませんでした...

客先と交渉しましたが、結論はセキュリティ上サードパーティのアクションズは許可できないとのことでした。

自前で実装するのはいいですよと許可貰えたので、せっかくなので自分で書いてみることにしました。
## 方針

複数のワークフローでPHPの環境を用意したかったため、[複合アクション(Composite Action)](https://docs.github.com/ja/actions/sharing-automations/creating-actions/creating-a-composite-action)を使ってPHP環境構築の部分を切り出すことにしました。

## 複合アクションの実装

### ディレクトリ構成

```
.github
├── actions
│   └── php-setup
│       └── action.yml
└── workflows
    └── phpunit.yml
```

- `.github/actions/php-setup/action.yml`
  - 複合アクションファイル名は `action.yml` or `action.yaml` 固定なので注意
- `.github/workflows/ci.yml`
  - ワークフローファイル

### PHP Setup複合アクション

```yaml:.github/actions/php-setup/action.yml
name: PHP Setup
description: PHP, Composer をインストールするアクション
inputs:
  php-version:
    description: 'PHP version to install'
    required: true
  composer-version:
    description: 'Composer version to install'
    required: true
runs:
  using: composite
  steps:
    - name: Add PHP repository
      shell: bash
      run: |
        sudo apt-get update
        sudo apt-get install -y software-properties-common
        sudo add-apt-repository -y ppa:ondrej/php
        sudo apt-get update
    - name: Install PHP
      shell: bash
      run: |
        sudo apt-get install -y \
        php${{ inputs.php-version }} \
        php${{ inputs.php-version }}-cli \
        php${{ inputs.php-version }}-mbstring \
        php${{ inputs.php-version }}-xml \
        php${{ inputs.php-version }}-curl \
        php${{ inputs.php-version }}-zip \
        php${{ inputs.php-version }}-bcmath \
        php${{ inputs.php-version }}-bz2 \
        php${{ inputs.php-version }}-calendar \
        php${{ inputs.php-version }}-pdo-sqlite \
        php${{ inputs.php-version }}-sqlite3
    - name: Set PHP default version
      shell: bash
      run: sudo update-alternatives --set php /usr/bin/php${{ inputs.php-version }}
    - name: Install Composer
      shell: bash
      run: |
        curl -sS https://getcomposer.org/installer | php -- --version=${{ inputs.composer-version }}
        sudo mv composer.phar /usr/local/bin/composer
```

#### 補足: inputs

```
inputs:
  php-version:
    description: 'PHP version to install'
    required: true
```

`inputs` で受け取る値を取得
`${{ inputs.php-version }}` で取得できる

```
runs:
  using: composite
```

ワークフローと違って、 `jobs` ではなく `runs` なことと `using: composite` と複合アクションと明示する必要があるようです。
自分はずっと勘違いして `jobs` と書いてました笑

#### 補足: Add PHP repository ステップ

```
    - name: Add PHP repository
      shell: bash
      run: |
        sudo apt-get update
        sudo apt-get install -y software-properties-common
        sudo add-apt-repository -y ppa:ondrej/php
        sudo apt-get update
```

[ppa:ondrej/php](https://launchpad.net/~ondrej/+archive/ubuntu/php) は
Ondřej Surý 氏が管理する Ubuntu 用のサードパーティーリポジトリ(PPA: Personal Package Archive)です。

`ppa:ondrej/php` を追加するために `add-apt-repository` コマンドを利用します。
`add-apt-repository` コマンドを利用するために `software-properties-common` パッケージをインストールしています。

注意点なんですが、 `shell: bash` 各ステップに shell の指定が必須です。

#### 補足: Install PHP ステップ

```
    - name: Install PHP
      shell: bash
      run: |
        sudo apt-get install -y \
        php${{ inputs.php-version }} \
        php${{ inputs.php-version }}-cli \
        php${{ inputs.php-version }}-mbstring \
        php${{ inputs.php-version }}-xml \
        php${{ inputs.php-version }}-curl \
        php${{ inputs.php-version }}-zip \
        php${{ inputs.php-version }}-bcmath \
        php${{ inputs.php-version }}-bz2 \
        php${{ inputs.php-version }}-pdo-sqlite \
        php${{ inputs.php-version }}-sqlite3
```

PHPをインストールしています。
プロジェクトで使っている拡張機能をインストールしています。

#### 補足: Set PHP default version ステップ

```
    - name: Set PHP default version
      shell: bash
      run: sudo update-alternatives --set php /usr/bin/php${{ inputs.php-version }}
```

`update-alternatives` コマンドはDebianベースのLinuxディストリビューションで用意されているシステムコマンドです。
このコマンドは、システム内で複数のバージョンが存在するコマンドやツールを管理するために使用します。

#### 補足: Install Composer ステップ

```
    - name: Install Composer
      shell: bash
      run: |
        curl -sS https://getcomposer.org/installer | php -- --version=${{ inputs.composer-version }}
        sudo mv composer.phar /usr/local/bin/composer
```

ここでは [Composer](https://getcomposer.org/download) をインストールしています。

### PHPUnitを実行するワークフロー

```yaml:.github/workflows/phpunit.yml
name: PHPUnit
on:
  pull_request:
jobs:
  phpunit:
    runs-on: ubuntu-20.04
    timeout-minutes: 20
    steps:
      - uses: actions/checkout@v4
      - name: Setup PHP
        uses: ./.github/actions/php-setup
        with:
          php-version: '8.2'
          composer-version: '2.7.7'
      - name: PHP Version
        run: php -v
      - name: Composer Version
        run: composer -V
      - name: Install Dependencies
        run: composer install --quiet --prefer-dist --no-progress --no-interaction --no-scripts --no-ansi
      - name: PHPUnit Testing
        run: ./vendor/bin/phpunit
```

#### 補足: Setup PHP ステップ

複合アクションを呼び出してる箇所です。

```
      - name: Setup PHP
        uses: ./.github/actions/php-setup
        with:
          php-version: '8.2'
          composer-version: '2.7.7'
```

- `uses: ./.github/actions/php-setup`
  - ファイル名ではなくディレクトリ名を指定するところが注意です

## さいごに

今回はセキュリティでやむなく複合アクションに切り出しましたが、思ったより便利でした。
ハマりどころも知れたので、共通アクションがある場合は切り出してみようかなと思いました。

## 明日のアドベントカレンダー

明日のミライトデザインのアドカレは [ブッチ](https://zenn.dev/t_kitamura) さんの「データ活用から始めるエンジニアの視野の広げ方」という記事です。お楽しみに！