mcpField
M
makefile

makefile

github.com/mcouthon開発7950 回インストール今日更新
リポジトリ

makefile について

makefileは、AIエージェントのワークフローとサービスのライフサイクル管理を想定したMakefileの作成・保守スキルです。バックグラウンド起動、ログ、プロセスの後始末、状態確認、テストなどを扱います。

主な機能

  1. 01バックグラウンド起動・停止ターゲット
  2. 02PID追跡とプロセスグループの後始末
  3. 03サービス別ログと状態確認
  4. 04一括ライフサイクル操作とテストターゲット
  5. 05ポート設定と自己説明型ヘルプ

活用シーン

  1. 01複数サービス構成のMakefile作成
  2. 02安全な起動・停止・再起動操作の追加
  3. 03テストターゲットとサービスログの整理

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/mcouthon/agents --skill makefile

インストール後、Claude Code で/makefileを入力して読み込まれたか確認します。

詳細

Makefile モード

AI エージェントとのやり取りやプロセスのライフサイクル管理に最適化された Makefile を作成、管理します。

基本理念

「クリーンに開始する。クリーンに停止する。すべてを記録する。状態を把握する。」

原則:

  • AI エージェントを第一に考える:出力はプログラムで読み取り可能にする(対話型プロンプトは使わない)
  • デフォルトでバックグラウンド実行:サービスはデタッチして実行し、ターミナルを起動せずにログを読み取る
  • 包括的なログ記録:すべての出力を .logs/ に保存し、何も失わない
  • プロセスの衛生管理:クリーンに開始・停止し、孤立したプロセスを残さない
  • 適応可能なパターン:あらゆるサービス構成で動作する

実装前の調査

Makefile を作成する前に、以下を確認します。

サービス構成
  • どのようなサービスがありますか?(バックエンド、フロントエンド、ワーカーなど)
  • サービス間に依存関係はありますか?(起動順序)
  • 外部依存関係はありますか?(データベース、エミュレーターなど)
起動要件
  • 各サービスの起動コマンドは何ですか?
  • どの環境変数が必要ですか?
  • 使用するポートは何ですか?(サービスごとに一意である必要があります)
  • 初期化手順はありますか?(マイグレーション、シードなど)
テストと品質
  • どのようなテストコマンドがありますか?(ユニット、統合、E2E)
  • テストの前提条件は何ですか?(Docker、エミュレーターなど)
  • どのようなリンター/フォーマッターを使いますか?(eslint、ruff、mypy など)
プロジェクトのコンテキスト
  • 言語/フレームワークは何ですか?(規約に影響します)
  • 開発環境と本番環境で動作は異なりますか?
  • チームの規約はありますか?(維持すべき既存の慣行)

Makefile の構成

標準的な構成(順序どおり):

# 1. Configuration Variables
# 2. Directory Setup
# 3. Service Lifecycle Targets (run-*, stop-*)
# 4. Combined Operations (run, stop, restart)
# 5. Testing & Quality (test, lint)
# 6. Utility Targets (logs, status, help)
# 7. .PHONY declarations

基本パターンライブラリ

A. サービスの起動(バックグラウンド実行と PID の追跡)
run-backend:
	@mkdir -p .pids .logs
	@if lsof -ti:$(BACKEND_PORT) > /dev/null 2>&1; then \
		echo "❌ Backend already running on port $(BACKEND_PORT)"; \
		exit 1; \
	fi
	@echo "🚀 Starting backend on port $(BACKEND_PORT)..."
	@nohup $(BACKEND_CMD) > .logs/backend.log 2>&1 & echo $$! > .pids/backend.pid
	@echo "✅ Backend started (PID: $$(cat .pids/backend.pid))"
B. サービスの停止(プロセスグループのクリーンアップ)
stop-backend:
	@if [ -f .pids/backend.pid ]; then \
		PID=$$(cat .pids/backend.pid); \
		if ps -p $$PID > /dev/null 2>&1; then \
			echo "🛑 Stopping backend (PID: $$PID)..."; \
			kill -TERM -- -$$PID 2>/dev/null || kill $$PID; \
			rm .pids/backend.pid; \
			echo "✅ Backend stopped"; \
		else \
			echo "⚠️  Backend process not found, cleaning up PID file"; \
			rm .pids/backend.pid; \
		fi \
	else \
		echo "ℹ️  Backend not running"; \
	fi
C. 状態の確認
status:
	@echo "📊 Service Status:"
	@echo ""
	@for service in backend frontend; do \
		if [ -f .pids/$$service.pid ]; then \
			PID=$$(cat .pids/$$service.pid); \
			if ps -p $$PID > /dev/null 2>&1; then \
				echo "✅ $$service: running (PID: $$PID)"; \
			else \
				echo "❌ $$service: stopped (stale PID file)"; \
			fi \
		else \
			echo "⚪ $$service: not running"; \
		fi; \
	done
D. ログの追跡
logs:
	@if [ -f .logs/backend.log ] || [ -f .logs/frontend.log ]; then \
		tail -n 50 .logs/*.log 2>/dev/null; \
	else \
		echo "No logs found"; \
	fi

logs-follow:
	@tail -f .logs/*.log 2>/dev/null
E. 複合操作
run: run-backend run-frontend
stop: stop-frontend stop-backend  # Reverse order for clean shutdown
restart: stop run
F. 前提条件を伴うテスト
test: test-setup
	@echo "🧪 Running tests..."
	@$(TEST_CMD)

test-setup:
	@if [ -n "$(DOCKER_COMPOSE_FILE)" ] && [ -f "$(DOCKER_COMPOSE_FILE)" ]; then \
		docker-compose -f $(DOCKER_COMPOSE_FILE) up -d; \
	fi
G. ヘルプターゲット(自己文書化)
.DEFAULT_GOAL := help

help:
	@echo "Available targets:"
	@echo ""
	@echo "  make run              Start all services"
	@echo "  make stop             Stop all services"
	@echo "  make restart          Restart all services"
	@echo "  make status           Show service status"
	@echo "  make logs             Show recent logs"
	@echo "  make logs-follow      Follow logs in real-time"
	@echo "  make test             Run all tests"
	@echo "  make lint             Run linters and formatters"
	@echo ""
	@echo "Individual services:"
	@echo "  make run-backend      Start backend only"
	@echo "  make run-frontend     Start frontend only"
	@echo "  make stop-backend     Stop backend only"
	@echo "  make stop-frontend    Stop frontend only"

適応パターン

シナリオ適応方法
複数のバックエンド接尾辞で命名します:run-api、run-worker など
データベースのマイグレーションmigrate ターゲットを追加し、run-backend の依存先にします
エミュレーターPID の追跡を行い、他のサービスと同様に扱います
Docker Composedocker-compose コマンドをラップし、コンテナー ID を追跡します
モノレポサブディレクトリー用の変数を使います:cd $(API_DIR) && ...
複数のテスト種別ターゲットを分けます:test-unit、test-integration、test-e2e
ウォッチモード専用のウォッチターゲットを使用し、通常の実行とは混在させません

ベストプラクティスのチェックリスト

Makefile を完成させる前に、以下を確認します。

  • すべてのターゲットが .PHONY に指定されている(または適切に指定されていない)
  • ポート番号を変数で設定できる
  • サービスごとに一意のポートを使っている(競合がない)
  • すべてのログが .logs/ ディレクトリーに保存される
  • すべての PID が .pids/ ディレクトリーに保存される
  • プロセスグループを終了する(子プロセスも処理する)
  • 起動前にポートの競合を検出する
  • 人が読みやすい出力を使う(色/絵文字)
  • help ターゲットがデフォルトになっている(先頭に記載するか .DEFAULT_GOAL を設定する)
  • 変数に := を使う(単純展開)
  • エラーメッセージが明確で、対処方法が分かる
  • 状態コマンドで実際の状態が表示される
  • 停止時にクリーンにシャットダウンする(最初に SIGTERM を送る)
  • 操作が冪等である(2 回実行しても安全)

よくある問題と解決策

問題解決策
PID ファイルは存在するが、プロセスが停止しているPID ファイルを使う前に ps -p $PID を確認する
親プロセスを終了しても子プロセスが残るkill -TERM -- -$PID(プロセスグループ)を使う
ポートがすでに使用されている起動前に lsof -ti:$PORT で確認する
ログが混在して読みづらいサービスごとにログファイルを分ける
サービスが起動後すぐに終了する標準エラー出力をリダイレクトする:2>&1、.logs/ を確認する
Make の変数が評価されない= ではなく := を使い、$$ と $ の違いを確認する
ログに色が表示されないunbuffer を使うか、サービスを TTY 用に設定する
サービスを停止できない(権限)起動したときと同じユーザーで make を実行する

実装ワークフロー

新しい Makefile の作成
  1. 調査:質問する(「調査」セクションを参照)
  2. 設定:変数を設定する(ポート、コマンド、パス)
  3. コアサービス:各サービスの run/stop を実装する
  4. 一括操作:すべてのサービスを対象とする run/stop/restart を追加する
  5. ユーティリティ:status、logs、help を追加する
  6. テスト:前提条件付きのテストターゲットを追加する
  7. 品質:lint/format ターゲットを追加する
  8. 検証:各ターゲットをテストし、冪等性を確認する
  9. ドキュメント:help が完全かつ正確であることを確認する
既存の Makefile の修正
  1. 現在の Makefile を読む:既存の構成を把握する
  2. 不足点を特定する:ベストプラクティスのチェックリストと照合する
  3. 変更を計画する:追加・変更する内容を決める
  4. 規約を維持する:既存の命名規則とスタイルを保つ
  5. 段階的に変更する:機能を一度に 1 つずつ追加する
  6. 変更ごとにテストする:問題が発生しないことを確認する
  7. help を更新する:新しいターゲットを反映する

完全なテンプレート

フルスタックアプリ向けの最小限の実用テンプレート:

# =============================================================================
# Configuration
# =============================================================================
BACKEND_PORT := 3001
FRONTEND_PORT := 3000
BACKEND_CMD := npm run dev --prefix backend
FRONTEND_CMD := npm run dev --prefix frontend
TEST_CMD := npm test

# =============================================================================
# Directory Setup
# =============================================================================
$(shell mkdir -p .pids .logs)

# =============================================================================
# Service Lifecycle
# =============================================================================
run-backend:
	@if lsof -ti:$(BACKEND_PORT) > /dev/null 2>&1; then \
		echo "❌ Backend already running on port $(BACKEND_PORT)"; \
		exit 1; \
	fi
	@echo "🚀 Starting backend on port $(BACKEND_PORT)..."
	@nohup $(BACKEND_CMD) > .logs/backend.log 2>&1 & echo $$! > .pids/backend.pid
	@echo "✅ Backend started (PID: $$(cat .pids/backend.pid))"

run-frontend:
	@if lsof -ti:$(FRONTEND_PORT) > /dev/null 2>&1; then \
		echo "❌ Frontend already running on port $(FRONTEND_PORT)"; \
		exit 1; \
	fi
	@echo "🚀 Starting frontend on port $(FRONTEND_PORT)..."
	@nohup $(FRONTEND_CMD) > .logs/frontend.log 2>&1 & echo $$! > .pids/frontend.pid
	@echo "✅ Frontend started (PID: $$(cat .pids/frontend.pid))"

stop-backend:
	@if [ -f .pids/backend.pid ]; then \
		PID=$$(cat .pids/backend.pid); \
		if ps -p $$PID > /dev/null 2>&1; then \
			echo "🛑 Stopping backend (PID: $$PID)..."; \
			kill -TERM -- -$$PID 2>/dev/null || kill $$PID; \
			rm .pids/backend.pid; \
			echo "✅ Backend stopped"; \
		else \
			echo "⚠️  Backend not found, cleaning up PID file"; \
			rm .pids/backend.pid; \
		fi \
	else \
		echo "ℹ️  Backend not running"; \
	fi

stop-frontend:
	@if [ -f .pids/frontend.pid ]; then \
		PID=$$(cat .pids/frontend.pid); \
		if ps -p $$PID > /dev/null 2>&1; then \
			echo "🛑 Stopping frontend (PID: $$PID)..."; \
			kill -TERM -- -$$PID 2>/dev/null || kill $$PID; \
			rm .pids/frontend.pid; \
			echo "✅ Frontend stopped"; \
		else \
			echo "⚠️  Frontend not found, cleaning up PID file"; \
			rm .pids/frontend.pid; \
		fi \
	else \
		echo "ℹ️  Frontend not running"; \
	fi

# =============================================================================
# Combined Operations
# =============================================================================
run: run-backend run-frontend
stop: stop-frontend stop-backend
restart: stop run

# =============================================================================
# Testing & Quality
# =============================================================================
test:
	@echo "🧪 Running tests..."
	@$(TEST_CMD)

lint:
	@echo "🔍 Running linters..."
	@npm run lint 2>&1 || true

# =============================================================================
# Utilities
# =============================================================================
status:
	@echo "📊 Service Status:"
	@echo ""
	@for service in backend frontend; do \
		if [ -f .pids/$$service.pid ]; then \
			PID=$$(cat .pids/$$service.pid); \
			if ps -p $$PID > /dev/null 2>&1; then \
				echo "✅ $$service: running (PID: $$PID)"; \
			else \
				echo "❌ $$service: stopped (stale PID file)"; \
			fi \
		else \
			echo "⚪ $$service: not running"; \
		fi; \
	done

logs:
	@tail -n 50 .logs/*.log 2>/dev/null || echo "No logs found"

logs-follow:
	@tail -f .logs/*.log 2>/dev/null

clean:
	@rm -rf .pids .logs
	@echo "🧹 Cleaned up PID and log files"

# =============================================================================
# Help
# =============================================================================
.DEFAULT_GOAL := help

help:
	@echo "Available targets:"
	@echo ""
	@echo "  make run           Start all services"
	@echo "  make stop          Stop all services"
	@echo "  make restart       Restart all services"
	@echo "  make status        Show service status"
	@echo "  make logs          Show recent logs (last 50 lines)"
	@echo "  make logs-follow   Follow logs in real-time"
	@echo "  make test          Run tests"
	@echo "  make lint          Run linters"
	@echo "  make clean         Remove PID and log files"
	@echo ""
	@echo "Individual services:"
	@echo "  make run-backend   Start backend only"
	@echo "  make run-frontend  Start frontend only"
	@echo "  make stop-backend  Stop backend only"
	@echo "  make stop-frontend Stop frontend only"

# =============================================================================
# .PHONY
# =============================================================================
.PHONY: run run-backend run-frontend stop stop-backend stop-frontend \
        restart status logs logs-follow test lint clean help

Gitignore への追加

以下を .gitignore に追加するようユーザーに伝えます:

.pids/
.logs/

コメント