GitHub Actions 基本:ワークフロー、ジョブ、ステップ
1. 概要
GitHub Actions は GitHub に統合された CI/CD ツールで、専用の CI サーバーなしに、ビルド、テスト、デプロイを自動化できます。このガイドでは、3 つの主要な概念(ワークフロー、ジョブ、ステップ)について説明し、Maven を使用した Spring Boot プロジェクトの具体的な例を提供します。
このガイドを読み終わったら、コードをプッシュするたびにテストを自動実行するワークフローファイルを作成でき、ジョブが並列または順序通りに実行される方法を理解し、ワークフローが失敗したときのデバッグ方法を知ることができます。
2. ワークフロー(Workflow)とは?
ワークフロー(Workflow)は、リポジトリの .github/workflows/ ディレクトリにある YAML ファイルです。各ワークフローは、いつ実行するか(トリガー)と何を実行するか(ジョブ)を定義します。
# .github/workflows/ci.yml
name: CI Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
- name: Build with Maven
run: mvn -B package
説明:
- ファイルの先頭の
nameは、GitHub の Actions タブに表示される名前です。 onはトリガーを宣言します。ここでは、pushまたはpull_requestがmainブランチに対して実行されたときにワークフローが実行されます。jobs.buildは、buildという名前の単一のジョブで、ubuntu-latestの仮想マシン上で実行されます。
注意: ファイルは正しいパス
.github/workflows/*.ymlに配置する必要があります。ディレクトリが異なると、GitHub がワークフローを認識できません。
3. ジョブ(Job):独立した実行単位
1 つのワークフローには複数のジョブを含めることができます。各ジョブは独立した runner(仮想マシン)で実行され、デフォルトではジョブは 並列実行 されます。ただし、needs を宣言した場合は例外です。
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: mvn -B test
deploy:
runs-on: ubuntu-latest
needs: test
steps:
- run: echo "Deploying application..."
説明:
testとdeployは 2 つの独立したジョブで、各ジョブはクリーンな runner 環境を持ち、ファイルシステムを共有しません。needs: testにより、deployはtestが正常に完了するのを待ってから実行されます。これは順序付き関係を作成します。needsがない場合、GitHub Actions はtestとdeployを同時に実行します。
3.1. ジョブ間でのデータ共有
各ジョブが独立した runner を持つため、ジョブ間でファイルを転送するには artifacts を使用する必要があります。
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: mvn -B package
- uses: actions/upload-artifact@v4
with:
name: app-jar
path: target/*.jar
deploy:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/download-artifact@v4
with:
name: app-jar
説明:
upload-artifactは、ジョブbuildからの.jarファイルを GitHub の一時ストレージにアップロードします。- ジョブ
deployのdownload-artifactは、新しい runner にそのファイルをダウンロードし直します。 - これはジョブ間でデータを転送する唯一の方法です(通常の環境変数は使用できません)。
4. ステップ(Step):最小の操作単位
ステップはジョブ内の特定の操作で、上から下へ順序通りに実行されます。ステップには 2 つの主な種類があります:uses を使用する(事前に作成された action を呼び出す)か、run を使用する(shell コマンドを実行する)です。
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Cache Maven dependencies
uses: actions/cache@v4
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
- name: Run tests
run: mvn -B test
- name: Print result
run: echo "Test step finished with status ${{ job.status }}"
説明:
- ステップ
Checkout codeは、事前に作成された actionactions/checkoutを使用して、ソースコードを runner にチェックアウトします。 - ステップ
Cache Maven dependenciesは、~/.m2/repositoryディレクトリをキャッシュし、以降のビルドを高速化します。依存関係を再度ダウンロードする必要がないためです。 - キャッシュの
keyはpom.xmlのハッシュに基づいています。pom.xmlが変更されると、古いキャッシュは破棄され、新しいキャッシュが作成されます。 - 最後のステップは、
runを使用して通常の shell コマンドを実行し、${{ job.status }}などの context を参照できます。
5. 環境変数とシークレット
通常、機密設定(API キー、トークン)をワークフローに渡す必要がありますが、YAML ファイルにハードコードしたくない場合があります。
jobs:
deploy:
runs-on: ubuntu-latest
env:
SPRING_PROFILES_ACTIVE: prod
steps:
- uses: actions/checkout@v4
- name: Deploy to server
env:
DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}
run: |
echo "Deploying with profile $SPRING_PROFILES_ACTIVE"
curl -H "Authorization: Bearer $DEPLOY_TOKEN" https://api.example.com/deploy
説明:
- ジョブレベルの
envは、そのジョブ内のすべてのステップに適用されます(SPRING_PROFILES_ACTIVE)。 - ステップレベルの
envは、そのステップにのみ適用されます(DEPLOY_TOKEN)。 secrets.DEPLOY_TOKENはリポジトリの Settings → Secrets and variables → Actions から取得され、ログに実際の値が表示されることはありません。
注意:
echo $DEPLOY_TOKENでシークレット値をログに直接出力しないでください。GitHub は自動的にマスキングしますが、シークレットを出力しないことが最善です。
6. マトリックス(Matrix):複数のバージョンを同時に実行
複数の Java バージョンまたは OS でテストする必要がある場合、matrix を使用するとジョブの繰り返し記述を避けられます。
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
java: [17, 21]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
java-version: ${{ matrix.java }}
distribution: 'temurin'
- run: mvn -B test
説明:
strategy.matrix.javaは値のリスト[17, 21]を宣言します。- GitHub Actions は自動的に 2 つの並列ジョブ を作成し、各ジョブは対応する Java バージョンで実行されます。
- この方法により、YAML の繰り返し記述を節約でき、複数の環境でコードが正常に実行されることを確認する必要がある場合に適しています。
7. シンプルなテストでワークフローを検証
ワークフローは JUnit で直接テストされませんが、ワークフロー内の mvn test ステップがプロジェクトの正しいテストスイートを実行していることを確認する必要があります。以下は、CI が実行する OrderService の簡単なテスト例です:
class OrderServiceTest {
private final OrderService orderService = new OrderService();
@Test
void shouldCalculateTotalPriceCorrectly() {
Order order = new Order("ORD-001", List.of(
new OrderItem("Keyboard", 2, BigDecimal.valueOf(50)),
new OrderItem("Mouse", 1, BigDecimal.valueOf(20))
));
BigDecimal total = orderService.calculateTotal(order);
assertThat(total).isEqualByComparingTo(BigDecimal.valueOf(120));
}
}
説明:
- このテストは
src/test/javaにあり、ワークフローのステップでmvn -B testによって自動実行されます。 - テストが失敗した場合、ステップ
Run testsはエラーを報告し(終了コードが 0 以外)、ジョブは停止し、依存するジョブ(needs)は実行されません。 - これが CI が有用な理由です。エラーのあるコードは
mainにマージされる前にブロックされます。
GitHub Actions UI(Actions タブ)で実行する結果:
✓ Checkout code
✓ Set up JDK 21
✓ Build with Maven
✓ Run tests (shouldCalculateTotalPriceCorrectly PASSED)
8. 一般的なエラー
- YAML のインデント エラー:GitHub Actions は YAML を使用しており、インデントが 1 スペース間違うとジョブまたはステップが認識されず、通常「Invalid workflow file」エラーが報告されます。
runs-onの忘却:各ジョブは必ずruns-onを持つ必要があります。欠落するとワークフローが解析時に失敗します。- ジョブ間での環境変数の使用:
envと出力変数は通常、ジョブ間で自動的に転送されません。outputsまたはartifactを使用する必要があります。 - 依存関係が変更されてもキャッシュ キーが変わらない:キャッシュ
keyが一般的すぎる場合(例えばmaven-cacheのみ)、古い依存関係を含む古いキャッシュを誤って使用する可能性があります。 - すべてのプッシュで重いワークフローを実行:
pathsまたはbranchesを制限しない場合、小さなコミット(README の編集など)でも完全なパイプラインのビルド + テストがトリガーされ、Actions 実行分数を浪費します(無料の制限があります)。
注意: プロジェクトがシンプルなビルドのみを必要とし、多くの環境がない場合、
matrixやartifactを使用する必要はありません。不要な複雑さを追加するとワークフローの保守が困難になります。
9. まとめ
ワークフローはパイプライン全体を定義する YAML ファイルで、ジョブは独立した runner で実行される独立した実行ブロックであり、ステップはジョブ内の特定の操作です。この 3 つのレイヤーを理解することで、CI/CD パイプラインを明確に整理できます。どのジョブが並列実行され、どのジョブが needs で相互に待機し、データ共有が必要な場合は artifact を使用するかを理解できます。
初めの段階では、ワークフローをシンプルに保ちましょう(1 つのジョブ、いくつかの基本的なステップ:チェックアウト、Java セットアップ、テスト)。プロジェクトが大きくなったら、matrix、cache を追加し、build/test/deploy を別々のジョブに分割して、管理やデバッグを容易にしてください。