Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 64 additions & 20 deletions scripts/ci/check_alembic_required.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,18 @@
# Asana 1215151958676195 / [LAUNCH-GATE-B] schema drift 再発防止。
#
# 終了コード:
# 0: OK (models 変更なし / models と migration 両方変更あり)
# 1: FAIL (models 変更ありだが migration 追加なし)
# 0: OK (models 変更なし / スキーマに影響しない変更のみ / models と migration 両方変更あり)
# 1: FAIL (スキーマに影響する models 変更ありだが migration 追加なし)
#
# 補足:
# - GITHUB_BASE_REF が無い場合は HEAD~1 で diff を取る(ローカル実行用)。
# - 例外(コメントのみ変更など)は未実装。まず厳密に。誤検知が問題化したら例外追加。
# - 2026-07-17: **スキーマ影響判定を追加**(当初の「例外は未実装。まず厳密に。誤検知が
# 問題化したら例外追加」という設計メモに従う)。ファイル名だけで判定していたため、
# models.py 内の docstring / 定数 / ヘルパ関数の変更でも FAIL していた(PR #994 で顕在化:
# `PHASE_1_ALLOWED_RISK_MODES` の env 化=列定義ゼロの変更が落ちた)。
# 「赤いのが常態」は本物の schema drift を見逃す土壌になるため、誤検知の側を潰す。
# - **判定は fail-closed**: 差分にスキーマ関連の字句が 1 つでもあれば従来どおり migration 必須。
# コメント内の一致でも FAIL する(安全側)。判定に迷う余地を作らない。
set -euo pipefail

BASE_REF="${GITHUB_BASE_REF:-main}"
Expand All @@ -22,11 +28,13 @@ BASE_REF="${GITHUB_BASE_REF:-main}"
git fetch origin "${BASE_REF}" --depth=50 >/dev/null 2>&1 || true

if git rev-parse --verify "origin/${BASE_REF}" >/dev/null 2>&1; then
CHANGED="$(git diff --name-only "origin/${BASE_REF}...HEAD" 2>/dev/null || true)"
DIFF_RANGE="origin/${BASE_REF}...HEAD"
else
CHANGED="$(git diff --name-only "HEAD~1...HEAD" 2>/dev/null || true)"
DIFF_RANGE="HEAD~1...HEAD"
fi

CHANGED="$(git diff --name-only "${DIFF_RANGE}" 2>/dev/null || true)"

if [[ -z "${CHANGED}" ]]; then
echo "ℹ️ 変更ファイルが検出できませんでした (base=${BASE_REF})。skip 扱い。"
exit 0
Expand All @@ -40,25 +48,61 @@ MODELS_CHANGED="$(echo "${CHANGED}" | grep -E '^backend/app/(models/.*|.*/models
# alembic versions/ への新規/変更追加検出
MIGRATIONS_ADDED="$(echo "${CHANGED}" | grep -E '^backend/alembic/versions/.*\.py$' || true)"

if [[ -n "${MODELS_CHANGED}" && -z "${MIGRATIONS_ADDED}" ]]; then
echo "❌ FAIL: models/ に変更がありますが backend/alembic/versions/ に migration が追加されていません"
if [[ -z "${MODELS_CHANGED}" ]]; then
echo "✅ models 変更なし。skip。"
exit 0
fi

if [[ -n "${MIGRATIONS_ADDED}" ]]; then
echo "✅ models 変更 + migration 追加を検出。OK。"
echo "変更 models: $(echo "${MODELS_CHANGED}" | wc -l | tr -d ' ') file(s)"
echo "追加 migration: $(echo "${MIGRATIONS_ADDED}" | wc -l | tr -d ' ') file(s)"
exit 0
fi

# --- migration が無い場合: スキーマに影響する変更かを判定する ---
#
# SQLAlchemy でテーブル定義に効く字句。1 つでも追加/削除行に現れたら「スキーマ変更の可能性
# あり」として従来どおり FAIL する(fail-closed)。
SCHEMA_PATTERN='mapped_column|Column\(|__tablename__|__table_args__|CheckConstraint|UniqueConstraint|PrimaryKeyConstraint|ForeignKey|Index\(|relationship\(|server_default|nullable=|primary_key|autoincrement|Mapped\[|ALTER TABLE|CREATE TABLE|DROP COLUMN|sa\.'

# shellcheck disable=SC2086 # MODELS_CHANGED は改行区切りのパス列(意図的に分割する)
SCHEMA_HITS="$(git diff "${DIFF_RANGE}" -- ${MODELS_CHANGED} 2>/dev/null \
| grep -E '^[+-]' \
| grep -vE '^(\+\+\+|---)' \
| grep -E "${SCHEMA_PATTERN}" || true)"

if [[ -z "${SCHEMA_HITS}" ]]; then
echo "✅ models 変更を検出しましたが、差分にスキーマ関連の変更はありません。skip。"
echo ""
echo "変更された models:"
echo "${MODELS_CHANGED}" | sed 's/^/ - /'
echo ""
echo "対処:"
echo " cd backend && alembic revision --autogenerate -m 'describe schema change'"
echo " 生成された backend/alembic/versions/<rev>.py を確認しコミットしてください。"
echo "判定根拠: 追加/削除行に以下のいずれも出現しませんでした:"
echo " ${SCHEMA_PATTERN}"
echo ""
echo " どうしても migration 不要な場合 (例: docstring 変更のみ) は PR description に"
echo " '[skip-alembic-check]' を含め、レビュアー承認のもとマージしてください。"
exit 1
echo "※ 誤ってスキーマ変更を見逃していると感じたら、この判定を疑ってください"
echo " (判定は fail-closed 設計: 上記字句が 1 つでもあれば FAIL します)。"
exit 0
fi

if [[ -n "${MODELS_CHANGED}" ]]; then
echo "✅ models 変更 + migration 追加を検出。OK。"
echo "変更 models: $(echo "${MODELS_CHANGED}" | wc -l) file(s)"
echo "追加 migration: $(echo "${MIGRATIONS_ADDED}" | wc -l) file(s)"
else
echo "✅ models 変更なし。skip。"
fi
echo "❌ FAIL: models/ にスキーマ関連の変更がありますが backend/alembic/versions/ に migration が追加されていません"
echo ""
echo "変更された models:"
echo "${MODELS_CHANGED}" | sed 's/^/ - /'
echo ""
echo "スキーマ関連と判定した差分行:"
echo "${SCHEMA_HITS}" | head -20 | sed 's/^/ /'
echo ""
echo "対処:"
echo " backend/alembic/versions/ に migration を **手書きで** 追加してください。"
echo ""
echo " ⚠️ 本リポジトリでは 'alembic revision --autogenerate' は使用禁止です。"
echo " alembic/env.py が全モデルを import しておらず Base.metadata が不完全なため、"
echo " 実在するテーブルへの DROP を誤生成します"
echo " (memory: project_alembic_envpy_incomplete_model_imports)。"
echo ""
echo " スキーマ変更でないのに検出された場合 (定数やコメントに上記字句が含まれる等) は、"
echo " PR description に '[skip-alembic-check]' と根拠を明記し、レビュアー承認のもと"
echo " マージしてください。"
exit 1
Loading