mise + pre-commit + Renovate で作るメンテしやすいガードレール
1. はじめに
株式会社GENDA データエンジニア / MLOps エンジニアの uma-chan です。
この記事は GENDA Advent Calendar 2025 シリーズ3 Day 6 の記事です。
私の業務上クラウド IDE からコミットしたり AI コーディングエージェントが直接 PR を作成したりするケースが増えています。
以前は例えば .editorconfig でローカルのエディタ設定を統一するだけで統制は取れていたと思いますが、今は編集環境が多様化してしまっているのでガードレールの重要性が増しています。
本記事では設定ファイルを一元管理することでメンテしやすく、かつローカルでも GitHub Actions でも同じチェックが走るガードレールの構成を紹介します。
2. この記事で紹介する構成
簡単に解説
- mise: ツールのバージョンを設定ファイルで一元管理
- pre-commit: ガードレールを定義。ローカルでも GitHub Actions でも同じチェックを実行
- Renovate: mise.toml、pre-commit、GitHub Actions で使用するアクションを継続的に更新
- ブランチ保護ルール: 最後の砦として直接 push や force push を防止
3. mise
3.1. mise とは
mise は開発ツールのバージョンを設定ファイルで管理するツールです。
asdf の後継として開発されています。
主な利点
- mise.toml による宣言的なバージョン管理
- クロスプラットフォーム
- 多様なバックエンド (aqua, cargo, go, npm 等からもインストール可能)
注意点
- 任意の環境で任意のツールを動かせるわけではなく、各ツールのドキュメントに従う必要があります
3.2. インストール
このコマンドを実行するだけです。
curl https://mise.run | sh
3.3. mise.toml の書き方
[tools]
# パッケージマネージャ
uv = "0.9.15"
# 開発ツール
pre-commit = "4.5.0"
shellcheck = "0.11.0"
# Aqua 経由でインストールするツール
"aqua:rhysd/actionlint" = "1.7.9"
"aqua:suzuki-shunsuke/pinact" = "3.4.5"
"aqua:zizmorcore/zizmor" = "1.18.0"
"aqua:gitleaks/gitleaks" = "8.30.0"
uv は Python のパッケージマネージャですがそれ自体も mise で管理できます。
mise で uv 自体のバージョンを管理し、uv で Python のバージョンとライブラリを管理するという組み合わせ、オススメです。
参考のために以下に雑に運用している私の mise.toml を載せておきます。
開発に必要なツールは全て mise.toml に書けるのではないかと思っています。
盛り盛り mise.toml (参考)
[settings]
experimental = true
[tools]
# Language Runtimes
deno = "2.5.6"
go = "latest"
node = "24"
rust = "latest"
uv = "latest"
# CLI Tools
act = "latest"
awscli = "latest"
databricks-cli = "latest"
fd = "latest"
fzf = "latest"
gcloud = "latest"
gh = "latest"
hadolint = "latest"
jq = "latest"
pre-commit = "latest"
ripgrep = "latest"
shellcheck = "latest"
shfmt = "latest"
terraform = "latest"
tflint = "latest"
# Cargo Tools
"cargo:bat" = "latest"
"cargo:rumdl" = "latest"
"cargo:tokei" = "latest"
# Aqua Tools
# "aqua:evilmartians/lefthook" = "latest"
# "aqua:mikefarah/yq" = "latest"
"aqua:rhysd/actionlint" = "latest"
"aqua:suzuki-shunsuke/pinact" = "latest"
"aqua:x-motemen/ghq" = "latest"
"aqua:zizmorcore/zizmor" = "latest"
"aqua:gitleaks/gitleaks" = "latest"
# Go Tools
# "go:github.com/jameswoolfenden/pike" = { version = "latest" }
"go:github.com/mattn/efm-langserver" = { version = "latest" }
"go:github.com/rhysd/vim-startuptime" = { version = "latest" }
詳細はドキュメントを参照してください。
4. pre-commit
4.1. pre-commit とは
pre-commit は Git hook (Git 操作前後に実行する処理) を設定ファイルで管理するツールです。
ローカルでも GitHub Actions でも同じチェックを実行できます。
4.2. repo: local + mise exec -- パターン
pre-commit の設定で repo: local を使うと、ローカルにインストールされたツールを hook として使えます。
ここに mise exec -- を組み合わせると、mise で管理されたツールを使えます。
この組み合わせにしておくと mise.toml の設定が反映されて、かつキャッシュを活用しやすいので GitHub Actions 上でも高速にチェックを実行できます。
とりあえずローカルで動くように書いてみて後から GitHub Actions でキャッシュが効くように調整する、という流れがスムーズです。
default_stages: [pre-commit]
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
hooks:
- id: check-json
- id: check-yaml
- id: detect-private-key
- id: end-of-file-fixer
- id: trailing-whitespace
args: [--markdown-linebreak-ext=md]
- repo: local
hooks:
- id: gitleaks
name: Detect hardcoded secrets
entry: mise exec -- gitleaks protect --verbose --redact --staged
language: system
pass_filenames: false
- id: actionlint
name: actionlint
entry: mise exec -- actionlint
language: system
files: ^\.github/workflows/.*\.(yml|yaml)$
- id: pinact
name: pinact
entry: mise exec -- pinact run
language: system
files: ^\.github/workflows/.*\.(yml|yaml)$
- id: zizmor
name: zizmor
entry: mise exec -- zizmor
language: system
files: ^\.github/workflows/.*\.(yml|yaml)$
- id: shellcheck
name: shellcheck
entry: mise exec -- shellcheck
language: system
types: [shell]
- id: ruff-check
name: ruff check
entry: mise exec -- uv run --no-sync ruff check --fix
language: system
types: [python]
- id: ruff-format
name: ruff format
entry: mise exec -- uv run --no-sync ruff format
language: system
types: [python]
4.3. ローカル環境での活用 (オプション)
ローカル環境で pre-commit hook を活用できます。
どの道 GitHub Actions でチェックされるので必須ではありませんが、ローカルで事前に問題を検出できるため便利です。
# pre-commit hook をインストール
mise exec -- pre-commit install
# これ以降、git commit 時に自動でチェックが走る
git add .
git commit -m "feat: add new feature"
# → 自動でチェックが実行される
GitHub Actions でチェックされる前にローカルで問題を検出できるため、開発効率が向上します。
AI が pre-commit hook を無視することがたまにあるのですが GitHub Actions で必ずチェックされるので安心です。
5. GitHub Actions
5.1. PR に対してチェックを実行
どの環境から PR が作られても GitHub Actions でチェックしてくれます。
name: pre-commit
run-name: ${{ github.event_name }} on ${{ github.ref_name }} by @${{ github.actor }}
on:
workflow_dispatch:
push:
branches:
- main
pull_request:
branches:
- main
types:
- opened
- synchronize
- reopened
permissions: {}
defaults:
run:
shell: bash
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
pre-commit:
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
persist-credentials: false
# mise.toml で定義したツールをキャッシュ
# mise.toml が変わったらキャッシュを破棄
- name: Install mise
uses: jdx/mise-action@146a28175021df8ca24f8ee1828cc2a60f980bd5 # v3.5.1
with:
install_args: --yes # 確認プロンプトをスキップ
cache: true
# pre-commit が hook ごとに作る仮想環境をキャッシュ
# .pre-commit-config.yaml が変わったらキャッシュを破棄
# restore-keys を設定すると古いキャッシュから差分更新できるが、
# ゴミが溜まりやすいのでここではクリーンインストールを優先
- name: Cache pre-commit
uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: ~/.cache/pre-commit
key: |
pre-commit-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('.pre-commit-config.yaml') }}
- name: Run pre-commit
run: |
mise exec -- pre-commit run --all-files
5.2. ポイント
-
jdx/mise-actionがmise.tomlを読み取り、必要なツールを自動でインストール - PR の作成元環境に関係なく、同じチェックが実行される
- キャッシュを活用して高速化
5.3. テストもガードレールに含める
pre-commit による lint に加えて、テストもガードレールとして追加できます。
別ジョブとして定義し、集約ジョブでまとめてブランチ保護ルールで必須にします。
test:
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
steps:
- name: Checkout
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
with:
persist-credentials: false
- name: Install mise
uses: jdx/mise-action@146a28175021df8ca24f8ee1828cc2a60f980bd5 # v3.5.1
with:
install_args: --yes
cache: true
- name: Run tests
run: |
mise exec -- uv run pytest
# 集約ジョブ: ブランチ保護ルールではこのジョブを必須に設定
# matrix でジョブが増減しても設定変更が不要になる
ci:
needs: [pre-commit, test]
if: always()
runs-on: ubuntu-latest
timeout-minutes: 5
permissions: {}
steps:
- name: Check results
run: |
if [[ "${{ contains(needs.*.result, 'failure') }}" == "true" ]]; then
echo "One or more jobs failed"
exit 1
fi
if [[ "${{ contains(needs.*.result, 'cancelled') }}" == "true" ]]; then
echo "One or more jobs were cancelled"
exit 1
fi
echo "All checks passed"
6. Renovate
6.1. Renovate とは
Renovate は依存関係を自動更新するボットです。
automerge 機能を使えば、Renovate が作成した PR を CI が通ったら自動でマージすることも可能です。
6.2. 設定ファイル
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended",
":enablePreCommit"
],
"lockFileMaintenance": {
"enabled": true
}
}
ポイント
-
config:recommended: 推奨設定を適用。mise.toml や GitHub Actions で使用するアクション (SHA ピン留め含む) など色々と更新対象としてくれる。 -
:enablePreCommit: pre-commit のバージョンも自動更新 -
lockFileMaintenance: 推移的依存関係を定期的にリフレッシュ (オプション)
7. ガードレールの実例
この構成で使用しているチェックツールを紹介します。
これらのガードレールはサプライチェーン攻撃対策としても有効です。詳しくは以下の記事が参考になります。
7.1. gitleaks
コード内にハードコードされたシークレット (API キー、パスワード、トークンなど) を検出します。
防げる事故の例
- AI がサンプルコードを生成したとき、API キーっぽい文字列 (
API_KEY=xxx) が含まれていた - 環境変数をコピペしたとき、うっかり本番の認証情報が混入
- AWS 認証情報、GitHub トークン、データベース接続文字列などの漏洩
7.2. actionlint / pinact / zizmor
- actionlint: GitHub Actions ワークフローの構文チェック
- pinact: アクションのバージョンを SHA でピン留め (Renovate が SHA ごと更新してくれるので、初回のピン留めに使う)
- zizmor: GitHub Actions のセキュリティ脆弱性を検出
防げる事故の例
- actionlint:
branchesをbranchとタイポ、存在しないランナーラベルの指定 - pinact: アクションを
v1のようなミュータブルタグで指定 → サプライチェーン攻撃のリスク (tj-actions 事件など) - zizmor: テンプレートインジェクション脆弱性、過剰な権限設定、
pull_request_targetの危険な使用
7.3. shellcheck
シェルスクリプトの一般的な問題を検出します。
7.4. ruff
Python コードの lint と format を高速に実行します。
uv で管理されるライブラリを使った場合の設定の書き味の紹介のために記載してます。
設定しがいがあるツールで私は今学習中になります。
8. ブランチ保護ルール ~最後の砦~
ここまでのガードレールを設定しても、ブランチ保護ルールがなければ force push で全て無効化できてしまいます。
GitHub のリポジトリ設定でブランチ保護ルールを設定しましょう。
8.1. 推奨設定
Settings → Rules → Rulesets から設定できます。
- Restrict deletions: ブランチ削除を禁止
- Require a pull request before merging: PR 必須 (直接 push 禁止)
- Require status checks to pass: ステータスチェック必須。5.3 で紹介した集約ジョブ (
ci) を指定すると、lint とテスト両方の成功が必須になる - Block force pushes: force push 禁止
8.2. 防げる事故の例
- AI エージェントが main ブランチに直接 push してしまう
- レビューなしでマージされてしまう
- force push で履歴が書き換えられる
- CI をスキップしてマージされる
9. まとめ
設定ファイルの一元管理ができていることによって、ローカルで動くように書いた後に GitHub Actions で動くように調整するのが楽なガードレールの構成を紹介しました。参考になれば幸いです。
Discussion