サイト内を検索

GitHub Actionsを活用したリポジトリ運用改善:Reusable workflowsとComposite actionの導入事例

こんにちは。
Cocone Engineering で、ウェブフロントエンドを担当している O です。

私たちのチームでは、複数のプロダクトに横断的に関わるため、管理対象のGitリポジトリが100以上存在しています。
昨年、GitLabからGitHubへの移行に伴い、GitLab CI/CDをGitHub Actionsへと切り替えました。
この変化により、既存の運用フローをどのように効率化し、統一するかが課題となりました。

この記事では、増え続けるリポジトリを効率的に管理するため、GitHub ActionsのReusable workflows(再利用可能なワークフロー)とComposite action(複合アクション)を導入した背景や、試行錯誤のプロセス、具体例を交えて共有します。

はじめに

管理リポジトリが100以上存在し、一部にはworkflowが設定されているものの、ほぼコピペ運用である状態でした。  

さらに、リポジトリごとに微妙に異なる設定が混在しており、管理や変更に手間がかかり、変更ミスや運用の非効率化を招くリスクがありました。  

また、チーム全体で統一されたフローを簡単に適用できる仕組みがないことも課題でした。  

そこで、これらの課題を解決するために、GitHub ActionsのReusable workflowsとComposite actionを活用する方法を模索しました。

 

試行した解決策

 Workflowの現状把握と統一ポイントの整理

まず、既存の運用状況を把握するため、チームで管理しているすべてのリポジトリに設定されているworkflowをリストアップしました。  

その結果、以下の特徴がありました。

 

  • リポジトリごとにworkflowの設定が微妙に異なる
  • コピー&ペーストで作成されたworkflowが多く、複数の異なるコピー元が存在
  • ビルドからS3デプロイまでの基本フローはほぼ同じだが、微細な差異が混在

 

 

これらの課題に対応するため、各workflowを詳細に比較し、以下のように整理しました。

 

  • 環境ごとに異なる設定や特定のプロジェクト固有の処理は「独自部分」として分類
  • デプロイやビルドなどの共通処理は「再利用可能部分」として抽出

 

共通化できる部分と独自部分を切り分けた上で、GitHub ActionsのReusable workflowsとComposite actionを活用したWorkflowの作成を進めました。  

共通処理を一元化するReusable workflowsの活用

Reusable workflowsを活用し、次のような課題解決を目指しました。

 

  • 共通化:ビルドやデプロイなど、ほぼすべてのプロジェクトで共通して必要な処理をReusable workflowsとして統一
  • 柔軟性:環境やプロジェクトごとに異なる設定はinputsやSecretsを活用して動的に変更可能

Reusable workflowsの仕組み

Reusable workflowsは、各プロジェクトが共通の処理を「呼び出す」形で利用されます。
この仕組みにより、以下が可能になります

 

  • 再利用性:共通処理を一度定義するだけで、複数のリポジトリやプロジェクトで利用可能
  • メンテナンス効率の向上:共通処理の変更が必要な場合、Reusable workflowsを更新するだけで、関連プロジェクト全体に反映可能
  • セキュリティの向上:Secretsを用いることで、デプロイ先の環境や認証情報を安全に管理

YAMLの例:デプロイ用 Reusable workflow

以下は、実際に利用しているデプロイ用workflowの簡略版です。
このworkflowでは、Node.js環境のセットアップ、プロジェクトのビルド、AWS S3へのデプロイを行います。

name: Deploy

on:
  workflow_call:
    inputs:
      environment:
        description: |
          デプロイ先の環境(例: production, staging)
        type: string
        required: true

      node-version:
        description: |
          利用するNode.jsのバージョン
        type: string
        required: false


      build-command:
        description: |
          プロジェクトのビルドに使用するスクリプト
          `package.json` に定義されたスクリプトに合わせて調整

          例: `build`, `generate`
        type: string
        required: false
        default: "generate"

      s3-sync-command:
        description: |
          AWS S3にデプロイするためのコマンド

          例: `'./dist s3://my-bucket --delete --exclude "logs/*'`
        type: string
        required: true

    secrets:
      aws-role-arn:
        description: |
          AWSリソースへのアクセスを許可するためのロールARN(GitHub Secretsで管理)
        required: true

jobs:
  deploy:
    name: Deploy to ${{ inputs.environment }}
    runs-on: ubuntu-22.04
    timeout-minutes: 20
    permissions:
      contents: write
      id-token: write

    steps:
      # Node.jsのセットアップと依存関係のインストール
      - name: Setup Node.js and Install dependencies
        uses: my-org/composite-actions/.github/actions/node-setup-and-dependencies@v1
        with:
          node-version: ${{ inputs.node-version }}

      # プロジェクトのビルド
      - name: Build ${{ inputs.environment }}
        run: |
          set -xeu
          npm run ${{ inputs.build-command }}

      # AWS S3へのデプロイ
      - name: Deploy to ${{ inputs.environment }} [${{ inputs.s3-sync-command }}]
        uses: my-org/composite-actions/.github/actions/aws-s3-deploy-conditionally@v1
        with:
          environment: ${{ inputs.environment }}
          aws-role-arn: ${{ secrets.aws-role-arn }}
          s3-sync-command: ${{ inputs.s3-sync-command }}

このReusable workflowは以下の流れで動作します。

 

  1. 呼び出し元のworkflowから必要な入力(inputs)を受け取り、環境や設定を動的に変更
  2. Node.js環境をセットアップし、依存関係をインストール
  3. ビルドコマンドを実行して成果物を生成
  4. AWS S3の指定バケットに成果物をデプロイ

 個別処理を効率化するComposite actionの活用

Reusable workflowsの補完として、細かい処理や特定のプロセスをComposite actionとしてモジュール化しました。  

これにより、コードの再利用性を高め、プロジェクトごとの細かな調整が可能になりました。  

 

Composite actionの役割とメリット

  • 再利用性の向上 : 共通の処理を1つのアクションにまとめることで、複数のWorkflowで簡単に利用可能
  • メンテナンス性の向上 : 修正が必要な場合でも、1つのComposite actionを更新するだけで全体に反映可能
  • 柔軟性 : プロジェクトや環境に応じた設定や入力を受け取ることで、特定の要件に対応

 

YAMLの例:Node.js セットアップ用 composite action

Node.jsのセットアップと依存関係のインストールを行うComposite actionの例です。
このアクションは、Reusable workflows内の共通処理として使用されています。

name: Node.js Setup and Dependency Installation
description: |
  指定されたバージョンとオプションに基づいて Node.js と npm を設定し、依存関係をインストールします。

  主な機能:
  1. 指定されたNode.jsバージョンをインストール
  2. npmの依存関係をインストール

inputs:
  skip-checkout:
    description: |
      チェックアウトステップをスキップするかどうか
    required: false
    default: "false"

  node-version:
    description: |
      Node.jsのバージョンを指定
    required: false

  npm-install-command:
    description: |
      npmインストールコマンド
    required: false
    default: "ci"

  npm-install-options:
    description: |
      npmコマンドの追加オプション

      例: `--prefer-offline --no-audit`
    required: false

runs:
  using: composite
  steps:
    - name: Checkout code
      if: ${{ inputs.skip-checkout == 'false' }}
      uses: actions/checkout@v4

    - name: Setup Node.js
      uses: actions/setup-node@v4
      with:
        node-version: ${{ inputs.node-version }}

    - name: Install dependencies
      run: |
        set -xeu

        npm ${{ inputs.npm-install-command}} ${{ inputs.npm-install-options }}
      shell: bash

YAMLの例:AWS S3への条件付きデプロイ composite action

AWS S3バケットへの条件付きデプロイを行うComposite actionの例です。
このアクションでは、環境変数や入力に基づいてデプロイ処理を実行し、結果をSummaryに出力します。

name: AWS S3 Deploy Conditionally
description: |
  条件に応じてAWS S3バケットにファイルをデプロイするためのComposite actionです。

inputs:
  aws-region:
    description: |
      AWSリージョン(例: us-east-1, eu-central-1)
    required: false
    default: "ap-northeast-1"

  aws-role-arn:
    description: |
      AWSリソースアクセス用ロールARN(GitHub Secretsを使用)
    required: true

  environment:
    description: |
      デプロイ先の環境
    required: true

  s3-sync-command:
    description: |
      実行する aws s3 sync コマンド

      例: `./dist s3://your-bucket-name/path --delete --exclude "tmp/*"`
    required: true

runs:
  using: composite
  steps:
    - name: Setup AWS Credentials
      uses: aws-actions/configure-aws-credentials@v4
      with:
        role-to-assume: ${{ inputs.aws-role-arn }}
        aws-region: ${{ inputs.aws-region }}

    - name: Deploy to ${{ inputs.environment }} [aws s3 sync ${{ inputs.s3-sync-command }}] and check return code
      run: |
        set -xeu

        # AWS S3 Sync コマンドを実行し、コマンドの標準出力と標準エラー出力の両方をキャプチャ
        if output=$(aws s3 sync ${{ inputs.s3-sync-command }} 2>&1); then
          {
            echo "## AWS S3 Sync succeeded 🚀"
            echo "Running command \`aws s3 sync ${{ inputs.s3-sync-command }}\`"
          } >> "$GITHUB_STEP_SUMMARY"
        else
          ret_code=$?
          error_message=$(echo "$output" | tr '\n' ' ' | sed 's/"/\\"/g')
          echo "::error title=AWS S3 Sync Failure::Failed with return code $ret_code. Error: $error_message"
          {
            echo "## AWS S3 Sync failed ❌"
            echo "Running command \`aws s3 sync ${{ inputs.s3-sync-command }}\`"
            echo "Error output:"
            echo '```'
            echo "$output"
            echo '```'
          } >> "$GITHUB_STEP_SUMMARY"
          exit $ret_code
        fi
      shell: bash

Reusable workflowsを使った具体例

Reusable workflowsを活用した具体例を以下に示します。
この例では、手動でトリガーされるデプロイワークフローを定義しています。
このワークフローは、Reusable workflowsを呼び出して実行し、環境ごとの設定に応じた柔軟なデプロイを可能にします。

 

YAMLの例:Reusable workflowsを使用したデプロイ

name: CI/CD
run-name: '${{ github.workflow }} for ${{ github.ref_name }} / ${{ inputs.environment }} [${{ github.event_name }}] [${{ github.sha }}]'

on:
  workflow_dispatch:
    inputs:
      environment:
        description: 'Environment to deploy'
        type: environment
        required: true
        default: 'prod'

      s3-dryrun:
        description: 'Dryrun for S3 sync'
        type: boolean
        required: false
        default: false

concurrency:
  group: ${{ github.workflow }}-${{ github.ref }}-${{ inputs.environment }}
  cancel-in-progress: true

jobs:
  deploy:
    uses: my-org/github-reusable-workflows/.github/workflows/deploy.yml@v1
    with:
      environment: ${{ inputs.environment }}
      node-version: 20
      build-command: build
      s3-sync-command: >-
        ./dist s3://your-bucket-name/path --delete
        ${{ inputs.s3-dryrun && '--dryrun' || '' }}
    secrets:
      aws-role-arn: ${{ secrets.YOUR_AWS_S3_DEPLOY_ROLE_ARN }}
    permissions:
      contents: write
      id-token: write

この例でのポイント

  • workflow_dispatch トリガー : 手動でのワークフロー実行を可能にするトリガーを使用しています。 必要な入力値(environments3-dryrun)を指定可能です。
  • concurrency : 同じ環境でのデプロイが重複して実行されないように設定しています。
  • Reusable workflowsの呼び出し : jobs.deploy で定義したReusable workflows(deploy.yml)を呼び出し、必要なパラメータを入力しています。
  • s3-sync-command の動的設定 : inputs.s3-dryrun に応じて、--dryrun オプションを追加。これにより、安全にデプロイ内容を確認することが可能です。

 

検証結果と今後へのヒント

導入初期の成果と可能性

Reusable workflowsとComposite actionを導入し始めたことで、いくつかの方向性や可能性が見えてきました。

 

  • 統一性の向上 : デプロイやビルドといった共通処理をReusable workflowsに統一したことで、各リポジトリに散在していたコピーペーストのコードを削減できる可能性が浮上
  • モジュール化による柔軟性の強化 : Composite action を活用することで、Node.jsセットアップやAWSデプロイといった個別処理を簡潔にしつつ、プロジェクト特有の要件にも対応可能
  • 適用範囲の拡大余地 : テンプレートリポジトリを用意することで、導入をより簡略化できる手応えを確認

今後の課題と改善への道筋

導入段階で明らかになった課題と、その解決に向けた方向性について触れます。

 

  • デバッグ作業の煩雑さ : Reusable workflowsやComposite action内部で発生するエラーの調査には、時間と知識が必要。ログの可視化やエラー情報の収集を効率化する仕組みが課題
  • カスタム要件対応の難しさ : 特殊なリポジトリでは、Reusable workflowsやComposite actionをそのまま適用できないケースがあり、柔軟なカスタマイズの仕組みが求められる
  • チーム全体への浸透 : チーム全員が新しいフローを活用できるよう、トレーニングやドキュメントの整備が引き続き重要

 

自動化と運用改善への取り組み

Reusable workflowsやComposite actionの導入に加え、以下の取り組みを進めています。

 

  • GitHubテンプレートリポジトリの活用 : よく使うworkflowを含むテンプレートを準備し、新規プロジェクトへの導入を効率化
  • 社内向けドキュメント公開: Reusable workflowsやComposite actionの仕様や使用例をGitHub Pagesで公開中。これにより、開発者がいつでも参照可能な状態を維持
  • リリースフローの自動化: Reusable workflowsやComposite actionの更新を一元管理し、自動リリースプロセスを整備
  • 静的解析の導入: YAMLファイルの記述ミスを防ぐため、yaml-lintやaction-lintを導入
  • テスト導入の拡充: 各workflowやactionの動作確認用テストを追加し、運用の安定性を向上

 

まとめ

GitHub ActionsのReusable workflowsとComposite actionを活用することで、効率的な管理フローの構築に向けた第一歩を踏み出すことができました。
本記事で紹介したYAML例は簡略化されていますが、実際の運用ではさらに複雑な要件を考慮しています。
現在はテストや静的解析の導入、テンプレートリポジトリの活用を進めており、運用を改善し続けています。
今後は、さらに多くのリポジトリに適用範囲を広げ、より高い効率化を実現していく予定です。

この記事が、同じような課題を抱える方々のヒントや参考になれば幸いです。

Tag

Category

Tag