🛠️

mise導入したらIaC開発が快適すぎた

に公開

この記事は Qiita GitHub Actions Advent Calendar 2025 22日目の記事です。

はじめに

Terraform/Terragruntでインフラを管理していると、周辺ツールがどんどん増えていきませんか?

  • tflint(静的解析)
  • trivy(セキュリティスキャン)
  • terraform-docs(ドキュメント生成)
  • gitleaks(シークレット検出)
  • actionlint(GitHub Actions Lint)
  • ...etc

私たちのチームでも以下のような問題に悩まされていました。

  • チームメンバーごとにツールバージョンがバラバラ
  • 「ローカルでは通ったのにCIで落ちた」問題
  • 新メンバーの環境構築に時間がかかる
  • コードレビューでフォーマットやLintエラーを指摘する不毛さ

そこで mise + pre-commit + GitHub Actions の構成を導入したところ、これらの悩みが一気に解消されました。本記事では、その具体的な設定と運用方法を紹介します。

全体構成

.
├── .mise.toml              # ツールバージョン管理
├── .pre-commit-config.yaml # コミット前の自動チェック
├── .tflint.hcl             # tflint設定
└── .github/
    ├── actions/
    │   └── setup-mise/     # mise セットアップ用 Composite Action
    └── workflows/
        ├── ci.yml          # PRトリガーのCI
        └── _pre-commit.yml # 再利用可能ワークフロー

miseとは

mise(ミーズ)は、複数の開発ツールのバージョンを一元管理できるツールです。

特徴:

  • asdf互換のプラグインシステム
  • Rust製で高速
  • .mise.toml でプロジェクトごとにバージョンを固定
  • mise exec -- <command> で指定バージョンのツールを実行

asdfからの移行も容易で、既存の .tool-versions もそのまま読み込めます。

1. miseによるツールバージョン管理

.mise.toml の設定

.mise.toml
[tools]
actionlint = "1.7.9"
gitleaks = "8.30.0"
opentofu = "1.11.2"
pinact = "3.6.0"
pre-commit = "4.5.1"
terraform-docs = "0.21.0"
terragrunt = "0.96.1"
tflint = "0.60.0"
trivy = "0.68.2"
zizmor = "1.18.0"

各ツールの役割

ツール 用途
opentofu Terraform互換のIaCエンジン(Terraformのオープンソースフォーク)
terragrunt Terraformのラッパー(DRY化)
tflint Terraform静的解析
trivy IaCセキュリティスキャン
gitleaks シークレット検出
terraform-docs READMEの自動生成
actionlint GitHub Actions Lint
zizmor GitHub Actionsセキュリティ分析
pinact GitHub Actionsのバージョンピン留め
pre-commit Git hookマネージャー

インストール方法

# miseのインストール(macOS)
brew install mise

# ツール一式をインストール
cd your-project
mise install

これだけで、10個以上のツールが正しいバージョンでインストールされます。

2. pre-commitによる自動チェック

なぜpre-commitを使うのか

コミット前に自動でチェックを走らせることで:

  • レビュー前に機械的なエラーを排除
  • CI待ち時間の削減
  • コードレビューで本質的な議論に集中できる

設定ファイル

.pre-commit-config.yaml
repos:
  # 標準的なチェック
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v5.0.0
    hooks:
      - id: check-added-large-files
      - id: check-json
      - id: check-merge-conflict
      - id: check-yaml
      - id: detect-private-key
      - id: end-of-file-fixer
      - id: trailing-whitespace
        args: [--markdown-linebreak-ext=md]

  # ローカルフック(miseで管理するツール)
  - repo: local
    hooks:
      # GitHub Actions Lint
      - id: actionlint
        name: actionlint
        entry: mise exec -- actionlint
        language: system
        files: ^\.github/workflows/.*\.(yml|yaml)$

      # シークレット検出
      - id: gitleaks
        name: Detect hardcoded secrets
        entry: mise exec -- gitleaks protect --verbose --redact --staged
        language: system
        pass_filenames: false

      # GitHub Actionsバージョンピン留め
      - id: pinact
        name: pinact
        entry: mise exec -- pinact run
        language: system
        files: ^\.github/.*\.(yml|yaml)$

      # Terraformドキュメント自動生成
      - id: terraform-docs
        name: terraform-docs
        entry: bash -c 'shopt -s nullglob; for dir in modules/*/; do mise exec -- terraform-docs markdown table --lockfile=false --output-file README.md "$dir"; done'
        language: system
        files: ^modules/.*\.tf$
        pass_filenames: false

      # Terragrunt HCLフォーマット
      - id: terragrunt-hcl-fmt
        name: terragrunt hcl fmt
        entry: mise exec -- terragrunt hcl fmt
        language: system
        files: \.hcl$
        pass_filenames: false

      # tflint
      - id: tflint
        name: tflint
        entry: bash -c 'CONFIG="$(pwd)/.tflint.hcl"; mise exec -- tflint --init > /dev/null 2>&1; mise exec -- tflint --config="$CONFIG" --chdir=modules --recursive && mise exec -- tflint --config="$CONFIG" --chdir=environments --recursive'
        language: system
        files: \.tf$
        pass_filenames: false

      # OpenTofu フォーマット
      - id: tofu-fmt
        name: tofu fmt
        entry: mise exec -- tofu fmt -recursive
        language: system
        files: \.tf$
        pass_filenames: false

      # OpenTofu バリデーション
      - id: tofu-validate
        name: tofu validate
        entry: bash -c 'for dir in modules/*/; do (cd "$dir" && mise exec -- tofu init -backend=false -input=false > /dev/null 2>&1 && mise exec -- tofu validate) || exit 1; done'
        language: system
        files: \.tf$
        pass_filenames: false

      # Trivy セキュリティスキャン
      - id: trivy
        name: trivy IaC scan
        entry: bash -c 'mise exec -- trivy config --exit-code 1 --severity HIGH,CRITICAL --skip-dirs .terragrunt-cache .'
        language: system
        files: \.(tf|hcl)$
        pass_filenames: false

      # GitHub Actionsセキュリティ分析
      - id: zizmor
        name: zizmor
        entry: mise exec -- zizmor
        language: system
        files: ^\.github/.*\.(yml|yaml)$

ポイント:mise exec の活用

すべてのローカルフックで mise exec -- を使っているのがポイントです。

entry: mise exec -- tflint --recursive

これにより:

  • .mise.toml で指定したバージョンのツールが確実に使われる
  • グローバルにインストールしたツールと混同しない
  • チーム全員が同じバージョンで実行

tflint設定のカスタマイズ

.tflint.hcl
config {
  call_module_type = "local"
}

plugin "terraform" {
  enabled = true
  preset  = "recommended"
}

# Terragruntで管理するためモジュールでは不要
rule "terraform_required_version" {
  enabled = false
}

rule "terraform_required_providers" {
  enabled = false
}

セットアップと実行

# pre-commitのインストール(初回のみ)
mise exec -- pre-commit install

# 手動で全ファイルチェック
mise exec -- pre-commit run --all-files

3. GitHub Actionsとの統合

Composite Actionでmiseをセットアップ

.github/actions/setup-mise/action.yml
name: Setup mise
description: Setup mise with caching

inputs:
  cache:
    description: Enable mise and pre-commit cache
    required: false
    default: "true"

runs:
  using: composite
  steps:
    - uses: jdx/mise-action@c37c93293d6b742fc901e1406b8f764f6fb19dac # v2
      with:
        install_args: "--yes"
        cache: ${{ inputs.cache }}

    # pre-commitのキャッシュも設定
    - uses: actions/cache@v4
      if: inputs.cache == 'true'
      with:
        path: ~/.cache/pre-commit
        key: pre-commit-${{ runner.os }}-${{ hashFiles('.pre-commit-config.yaml') }}

CIワークフロー

.github/workflows/ci.yml
name: CI
on: pull_request

permissions:
  contents: read

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

jobs:
  pre-commit:
    runs-on: ubuntu-latest
    timeout-minutes: 5
    steps:
      - uses: actions/checkout@v4
        with:
          persist-credentials: false

      - uses: ./.github/actions/setup-mise

      - name: Run pre-commit
        run: mise exec -- pre-commit run --all-files

ポイント

  1. キャッシュの活用: mise-actionは自動でツールをキャッシュ。pre-commitの環境も別途キャッシュすることで高速化。

  2. 同じチェックの実行: ローカルの pre-commit run --all-files と同じコマンドをCIでも実行。環境差異がなくなる。

  3. Composite Actionで共通化: setup-miseを再利用可能なActionにすることで、複数ワークフローで一貫した設定を維持。

運用してみて

導入効果

Before After
「tflintのバージョンいくつ?」 .mise.tomlを見ればわかる
CIで初めてエラーに気づく コミット時点で検出
新メンバーの環境構築に半日 mise installで数分
レビューでフォーマット指摘 自動修正されてコミット

Tips

新しいツールの追加

# .mise.tomlの[tools]セクションに追加
# 例: new-tool = "1.0.0"
mise install

# pre-commitにフックを追加
# .pre-commit-config.yamlを編集

特定のフックだけ実行

mise exec -- pre-commit run tflint --all-files
mise exec -- pre-commit run trivy --all-files

フックをスキップ(緊急時のみ)

git commit --no-verify -m "hotfix"

まとめ

mise + pre-commit + GitHub Actionsの組み合わせで、IaC開発の品質管理を大幅に改善できました。

得られたメリット:

  • 環境統一: ローカルとCIで完全に同じツール・バージョン
  • 自動化: コミット前に17種類のチェックが自動実行
  • 簡単導入: 新ツール追加は.mise.tomlに1行書くだけ
  • 高速CI: キャッシュ活用で待ち時間を短縮
  • セキュリティ強化: trivy、gitleaks、zizmorで多層防御

特にmiseの mise exec -- による統一実行は、pre-commitとの相性が抜群です。チーム開発でツールバージョンの不一致に悩んでいる方は、ぜひ試してみてください。

参考リンク

GitHubで編集を提案
Atrae Tech

Discussion