よ〜くわかる GitHub Actions の仕組み【iOS アプリ】


本記事は GitHub Actions 入門者向けです。

大事なポイントだけ押さえたい方は、「📚」マークが付いた章をお読みください。



# はじめに

「自動化は大切だ──でも、さらっと学ぶだけで良いんじゃない？」

最初はそう考えていましたが、使ってみると、これが本当に面白い。

何と言っても、複数のタスクが自動処理されるのは、見ていて気持ちが良いものです。

本記事では、図解や具体例を用いて、GitHub Actions の仕組みを丁寧に解説していきます。

:::note info
この記事で取り上げる内容
- CI/CD の概要 〜 GitHub Actions の使い方まで
- GitHub Actions を使って、シェルコマンドを自動実行させる。
:::



# 1. GitHub Actions とは

[GitHub Actions](https://github.co.jp/features/actions) は、GitHub が提供している CI/CD サービスです。

これまで手動実行していた複数の処理を、１つの YAMLファイルにまとめて自動化できます。

身近な例として、コーヒーマシンを思い浮かべてみてください。

ピッと押すだけで、「コーヒー豆 → 挽く → 抽出 → カップに注ぐ」といった処理が自動実行される感じによく似ています。



### CI/CD サービス とは

──それで CI/CD サービスって……何なの？

簡潔に説明すると、**特定のイベントを検知して、「ビルド → テスト → 公開」を自動実行してくれる**サービスです。

（例）[SwiftLint](https://github.com/realm/SwiftLint) や [fastlane](https://github.com/fastlane/fastlane) を自動化することで、次のような CI/CD を実現できます。

<img alt="ios-pipeline-image" width="695" height="341" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366605/dc0bf44e-2a61-b6a8-4336-21b89b9cfa15.png">



# 📚 2. GitHub Actions の仕組み

GitHub Actions の特徴は、GitHub 上で直接操作できる点です。

リポジトリ内の **"Actions"** タブにて、CI/CD の管理画面を表示できます。

<img alt="actions-tab-image" width="618" height="62" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366605/e183f67d-4b8e-f989-3487-4854703786ce.png">

例えば、`pull_request` をトリガーに、テストを自動実行するとしましょう。

エラーを検出すると、プルリクエストが自動的に中断されるので、バグの取り込みを防ぐことができます。

<img alt="merge-pr-image" width="463" height="310" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366605/d5ff4e4b-c795-4332-641d-cf51c3a67b84.png">



### GitHub Actions のプロセスを分解する

もう少し詳しく見てみると、以下のような構造になっています。

1. まずは、リポジトリ内にて、特定の **Event** がトリガーになる。
1. 次に、**Workflow** が上から順次実行されていく。（なお、`job_1` と `job_2` は並列実行）
1. 最後に、**Runner** から実行結果と出力ログが返ってくる。

<img alt="github-actions-image" width="772" height="283" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366605/62ec958a-44cc-0871-3a0e-d203c0c1f886.png">

注目すべきは、**Runner と呼ばれる仮想マシンを介して、実行結果を取得している**点です。

極端な話をすると、仮にローカルの Mac 環境でビルドに成功しても、他の PC 上に同じ環境がなければ、エラーが出てしまいますよね。

それと同じで、Runner には、各 Job に適した仮想環境を用意しなければなりません。



### GitHub-hosted Runner を使う

そこで使えるのが、**"GitHub-hosted Runner"** です。

|タイプ|特徴|
|:-----|:---|
| **GitHub-hosted Runner** <br> （GitHub サーバー上の Virtual Machine） | ◎ 無料で使える（一部条件あり） <br> ◎ ３種類の OS 環境を選べる <br> ◎ OS 環境も CLI ツールも常に最新！ |

この Runner を使えば、自分でサーバーを用意しなくても、仮想環境を生成できます。

使用方法も簡単で、**OS 環境（macOS / Windows / Ubuntu）を指定するだけ**です。

しかも、各 OS 環境には **「開発に必要なパッケージ」** がプリインストールされています。  
（内部的には、Job を実行するための [actions/runner](https://github.com/actions/runner) も搭載されています。）

<img alt="github-hosted-runners-image" width="540" height="356" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366605/d87f9340-7cb1-4bfc-0b5b-ff4bdb85c97a.png">

例えば、[macOS-14 image](https://github.com/actions/runner-images/blob/main/images/macos/macos-14-Readme.md) の仕様を見てみましょう。

下記のように、主要な開発ツールがすべて揃っているので、環境構築にかかる時間と手間を大幅に削減できます。

```markdown:macOS-14 image（一部抜粋）
- OS Version: macOS 14.4.1 (23E224)

### Package Management
- Carthage 0.39.1
- CocoaPods 1.15.2
- Homebrew 4.2.21

### Tools
- AWS CLI 2.15.45
- AWS SAM CLI 1.116.0
- AWS Session Manager CLI 1.2.553.0
- Azure CLI 2.60.0
- Azure CLI (azure-devops) 1.0.0
- Bicep CLI 0.27.1
- Cmake 3.29.2
- CodeQL Action Bundle 2.17.1
- Fastlane 2.220.0
- SwiftFormat 0.53.8
- Xcbeautify 2.3.0
- Xcode Command Line Tools 15.3.0.0.1.1708646388
- Xcodes 1.4.1

### Linters
- SwiftLint 0.53.0
```



### 利用料金に注意！

さて、気になるのが、GitHub-hosted Runner の利用料金です。

| Public リポジトリ | Private リポジトリ |
|:-----------------:|:------------------:|
| 完全無料！ | 無料枠の上限あり <br> （Free Plan は 2,000分/月） |

大前提として、Public リポジトリなら、何も問題ありません。

どんなに時間のかかる処理でも、**1 回につき、最大 6 時間まで**は実行できます。  
（稼働し続けると、[`timeout-minutes`](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idtimeout-minutes) の設定で強制ストップする仕様です。）

ですが、**Private リポジトリの場合、指定した OS 環境の種類によって、利用料金が大きく変動してしまう**ので注意してください。

:::note warn
**特に macOS image は、実行コストが 10 倍高く設定されています。**

例えば、３種類の Runner を稼働させた場合、以下のように算出されます。
- Ubuntu image では、**$0.008/min**（等倍）
- Windows image では、**$0.016/min**（2倍）
- macOS image では、**$0.08/min**（10倍）で計算されてしまう。

※なお、毎月の無料枠も同様の倍率で消費されます。

より詳しい利用料金は、公式の [GitHub Actions の課金について](https://docs.github.com/ja/billing/managing-billing-for-github-actions/about-billing-for-github-actions) をご覧ください。
:::

（今回は割愛しますが、最安の Ubuntu image をベースに、Docker で環境構築した方が断然お得です。）



# 3. GitHub Actions の実装

GiHub Actions の実装は、３ステップです。
1. 特定のディレクトリ（`.github/workflows`）を作成する。
1. その中に、YAML形式の設定ファイル（`.yml`）を配置する。
1. リポジトリにアップロードする。

要するに、**`.github/workflows/sample.yml` をコミットすれば完了**です。

また、用途に応じて、複数の YAMLファイルを使い分けることができます。

```console:ディレクトリ構造の一例
├── .github
│    └── workflows
│           ├── sample.yml
│           ├── lint.yml
│           └── unit-tests.yml
│
├── SampleProject.xcodeproj
└── README.md
```

今回は、簡単なシェルコマンドを自動実行する `hello-world.yml` を作成します。

サクっと、ターミナルから以下のコマンドを実行して、YAMLファイルを準備しましょう。

```shell
# 1. ディレクトリの作成
$ mkdir -p .github/workflows

# 2. YAMLファイルの作成
$ touch .github/workflows/hello-world.yml
```



### YAMLファイルを準備する

YAMLファイルを開いて、`name` を記述したら、Event と Workflow を定義していきます。

```yaml:hello-world.yml
name: Hello and Goodbye Workflow

# 1. Event
on:

  # (...)

# 2. Workflow
jobs:

  # (...)
```



### Event を定義する

まず、`on` キーワード内にて、トリガーとなる Event を定義します。

```yaml:hello-world.yml
# 1. Event
on:
  pull_request:
    branches: ["main"]
  push:
    branches: ["main"]

  workflow_dispatch: # 任意のタイミングで手動実行できる
```

ここでは、大きく２種類のトリガーを設定しています。
1. 特定のイベントで自動実行されるトリガー（`pull_request` と `push`）
1. 任意のタイミングで手動実行できるトリガー（`workflow_dispatch`）

**`workflow_dispatch` とは、すなわち「手動実行を許可する」ためのキーワードです。**

これにより、Actions タブ内で **"Run workflow"** ボタンがポチッと押せるようになります。

<img alt="run-workflow-button-image" width="695" height="355" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366605/6e800c48-52c2-8e2a-855d-790f43067452.png">

例えば、仮にサーバー障害等の理由で CI/CD に失敗した場合でも、手動で再実行できるようになるので、非常に便利なトリガーです。

それ以外にも、[GitHub CLI](https://github.com/cli/cli) を導入した際には、ターミナルから [`gh workflow run`](https://cli.github.com/manual/gh_workflow_run) コマンドを実行できるようになります。



### Workflow を定義する

次に、`jobs` キーワード内にて、複数の Job を定義します。

今回は、２つの `job_id` を記述して、`runs-on` で Runner の実行環境を指定しましょう。

```yaml:hello-world.yml
# 2. Workflow
jobs:
  hello_world: # job_1
    runs-on: macos-latest

    # (...)

  goodbye_world: # job_2
    runs-on: windows-latest

    # (...)
```

:::note info
`runs-on` では、以下のような OS image を指定できます。（2024年時点）
- `macos-latest` / `macos-14` / `macos-13`
- `windows-latest` / `windows-2022` / `windows-2019`
- `ubuntu-latest` / `ubuntu-24.04` / `ubuntu-22.04`

最新の仕様については、公式の [actions/runner-images](https://github.com/actions/runner-images) をご確認ください。
:::

そして、`steps` キーワードを追加して、一連の Step を記述していきます。

今回は、簡単なシェルコマンド（`echo` コマンド と `date` コマンド）を実行させます。

```yaml:hello-world.yml
# 2. Workflow
jobs:
  hello_world:
    runs-on: macos-latest

    # 👉 steps for job_1
    steps:
      - name: Hello World!
        run: echo "hello world."

      - name: Today's Date!
        run: date

  goodbye_world:
    runs-on: windows-latest

    # 👉 steps for job_2
    steps:
      - name: Goodbye World!
        run: echo "goodbye world."
```

:::note info
以上で、YAMLファイルの記述は終了です。最終的なコードを掲載しておきます。

<details>
<summary><b>hello-world.yml の全体コード</b></summary>

```yaml:hello-world.yml
name: Hello and Goodbye Workflow

# 1. Event
on:
  pull_request:
    branches: ["main"]
  push:
    branches: ["main"]

  workflow_dispatch: # 任意のタイミングで手動実行できる

# 2. Workflow
jobs:
  hello_world: # job_1
    runs-on: macos-latest

    # 👉 steps for job_1
    steps:
      - name: Hello World!
        run: echo "hello world."

      - name: Today's Date!
        run: date

  goodbye_world: # job_2
    runs-on: windows-latest

    # 👉 steps for job_2
    steps:
      - name: Goodbye World!
        run: echo "goodbye world."
```
</details>

:::



### Runner の実行画面を見る

最後にコミットして、`git push origin main` を実行すれば、セットアップ完了です。

Actions タブ内で確認すると、定義した２つの Job が並列で動き出します。

<img alt="parallel-processing-image" width="502" height="344" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366605/8fe16046-f464-624b-37ca-8f4c07152a61.png">

全てのプロセスに成功すると、緑のチェックマークが付きます。

実行結果の詳細は、各 Step ごとの出力ログで確認できます。

<img alt="log-results-image" width="734" height="472" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366605/452a2074-457e-d41d-56c1-80e83c771fdc.png">



# おわりに

GitHub Actions を使って、シェルコマンドの自動実行に成功しました！

今回の内容を総括すると、次のように感じるのではないでしょうか。

- 「GitHub Actions って思ったよりも簡単！実装も難しくない」
- 「全自動化すれば、何より開発に集中できそう」
- 「仕組みは理解できたので、次はもっと実践的な CI/CD に挑戦したい」

以上、最後までお読みいただき、ありがとうございました！🙌  
この記事が少しでもお役に立てれば嬉しいです。

▋次回未定：GitHub Actions 実践  
￣￣￣￣￣￣￣￣￣￣￣￣￣￣￣￣￣￣



### 参考資料

- [GitHub Actions を理解する](https://docs.github.com/ja/actions/learn-github-actions/understanding-github-actions) ― GitHub Docs
- [GitHub ホステッド ランナーの概要](https://docs.github.com/ja/actions/using-github-hosted-runners/about-github-hosted-runners/about-github-hosted-runners) ― GitHub Docs
- [Events that trigger workflows](https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows) ― GitHub Docs
- [Workflow syntax for GitHub Actions](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions) ― GitHub Docs