references/BEST-PRACTICES.md
# Build Automation Best Practices
Reference guide for generating high-quality build automation files across four tools.
---
## 1. Makefile
### Required Preamble
Every generated Makefile MUST start with this strict preamble:
```makefile
SHELL := bash
.ONESHELL:
.SHELLFLAGS := -eu -o pipefail -c
.DELETE_ON_ERROR:
MAKEFLAGS += --warn-undefined-variables
MAKEFLAGS += --no-builtin-rules
```
**Why each line matters:**
- `SHELL := bash` — Ensures bash features work (arrays, `[[`, `<()`)
- `.ONESHELL` — Runs entire recipe in one shell invocation (variables persist across lines)
- `.SHELLFLAGS` — Fails on errors (`-e`), undefined vars (`-u`), pipe failures (`-o pipefail`)
- `.DELETE_ON_ERROR` — Removes targets if recipe fails (prevents corrupt artifacts)
- `--warn-undefined-variables` — Catches typos in variable names
- `--no-builtin-rules` — Disables implicit rules (faster, less confusing)
### Self-Documenting Pattern
Use `##` comments after targets for auto-generated help:
```makefile
.DEFAULT_GOAL := help
help: ## Show this help message
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \
awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
```
Use `##@` for section headers:
```makefile
##@ Development
dev: ## Start development server
...
##@ Testing
test: ## Run test suite
...
```
Enhanced help target with sections:
```makefile
help: ## Show this help
@awk 'BEGIN {FS = ":.*##"; printf "Usage:\n make \033[36m<target>\033[0m\n"} \
/^[a-zA-Z_-]+:.*?## / {printf " \033[36m%-20s\033[0m %s\n", $$1, $$2} \
/^##@/ {printf "\n\033[1m%s\033[0m\n", substr($$0, 5)}' $(MAKEFILE_LIST)
```
### Variable Conventions
```makefile
# Use ?= for overridable defaults
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")
COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo "unknown")
# Use := for computed-once values
BUILD_TIME := $(shell date -u '+%Y-%m-%dT%H:%M:%SZ')
# Group related variables with comments
# --- Docker ---
DOCKER_REGISTRY ?= ghcr.io
DOCKER_IMAGE ?= $(DOCKER_REGISTRY)/$(PROJECT)
DOCKER_TAG ?= $(VERSION)
```
### .PHONY Rules
Always declare `.PHONY` for non-file targets. Group them near the target:
```makefile
.PHONY: build
build: ## Build the project
...
```
Or declare all at once at the top:
```makefile
.PHONY: all build test lint clean help
```
### Anti-Patterns to Avoid
- **Never use spaces for indentation** — Makefiles require hard tabs
- **Never use `make` recursively** (`$(MAKE) -C subdir`) — Use `include` or target dependencies instead
- **Never suppress errors blindly** (`-rm ...`) — Use conditional checks or `|| true` explicitly
- **Never hardcode paths** — Use variables for tools (`GO ?= go`, `NPM ?= npm`)
- **Never put secrets in Makefiles** — Use environment variables or `.env` files
- **Avoid overly long recipes** — Extract to shell scripts if recipe exceeds ~15 lines
---
## 2. Taskfile (task)
### Required Structure
```yaml
version: '3'
output: prefixed
dotenv: ['.env', '.env.local']
vars:
PROJECT: '{{.ROOT_DIR | base}}'
VERSION:
sh: git describe --tags --always --dirty 2>/dev/null || echo "dev"
```
### Task Definition Conventions
Every task MUST have a `desc` field:
```yaml
tasks:
build:
desc: Build the project
cmds:
- go build -o bin/app ./cmd/app
```
### Sources and Generates (Caching)
Use `sources` and `generates` to skip up-to-date tasks:
```yaml
tasks:
build:
desc: Build the project
sources:
- ./**/*.go
- go.mod
- go.sum
generates:
- ./bin/app
cmds:
- go build -o bin/app ./cmd/app
```
### Parallel Dependencies
Use `deps` for tasks that can run in parallel:
```yaml
tasks:
ci:
desc: Run CI pipeline
deps: [lint, test, build]
```
Use `cmds` with `task:` for sequential execution:
```yaml
tasks:
release:
desc: Create a release
cmds:
- task: test
- task: build
- task: docker:push
```
### Preconditions
Guard tasks with preconditions:
```yaml
tasks:
deploy:
desc: Deploy to production
preconditions:
- sh: '[ "{{.ENV}}" = "production" ]'
msg: "ENV must be set to 'production'"
- sh: git diff --quiet
msg: "Working directory must be clean"
cmds:
- ./deploy.sh
```
### Namespacing with Includes
```yaml
includes:
docker:
taskfile: ./taskfiles/Docker.yml
dir: .
db:
taskfile: ./taskfiles/Database.yml
dir: .
```
Or use colon-separated naming:
```yaml
tasks:
docker:build:
desc: Build Docker image
docker:push:
desc: Push Docker image
```
### Anti-Patterns to Avoid
- **Never omit `desc`** — Tasks without descriptions don't show in `task --list`
- **Never use `silent: true` globally** — Makes debugging impossible
- **Never hardcode OS-specific commands** — Use `{{OS}}` and `{{ARCH}}` variables
- **Never ignore `sources/generates`** — Missing caching leads to slow rebuilds
- **Avoid deeply nested includes** — Keep task graph flat and readable
---
## 3. Justfile (just)
### Required Preamble
```justfile
set shell := ["bash", "-euo", "pipefail", "-c"]
set dotenv-load
set export
set positional-arguments
```
**Why each setting matters:**
- `set shell` — Uses bash with strict mode (like Make preamble)
- `set dotenv-load` — Automatically loads `.env` file
- `set export` — Exports all variables as environment variables
- `set positional-arguments` — Allows `$1`, `$2` in recipes
### Self-Documenting Pattern
Just has built-in `--list` but enhance with groups and docs:
```justfile
# Default recipe - show help
[doc("Show available recipes")]
default:
@just --list --unsorted
```
### Groups and Documentation
Use `[group]` to organize recipes and `[doc]` for descriptions:
```justfile
[group("development")]
[doc("Start development server with hot reload")]
dev:
npm run dev
[group("testing")]
[doc("Run the full test suite")]
test *args:
npm test {{args}}
```
### Variables and Expressions
```justfile
# Backtick variables (evaluated once)
version := `git describe --tags --always --dirty 2>/dev/null || echo "dev"`
commit := `git rev-parse --short HEAD 2>/dev/null || echo "unknown"`
# Built-in functions for cross-platform
os_name := os()
arch_name := arch()
# Conditional expressions
docker_cmd := if os_name == "linux" { "docker" } else { "docker" }
```
### Parameters and Variadic Arguments
```justfile
# Required parameter
build target:
go build -o bin/{{target}} ./cmd/{{target}}
# Optional parameter with default
deploy env="staging":
./deploy.sh {{env}}
# Variadic arguments
test *args:
go test {{args}} ./...
```
### Confirmation for Dangerous Operations
```justfile
[confirm("Are you sure you want to clean all build artifacts?")]
[group("maintenance")]
[doc("Remove all build artifacts and caches")]
clean:
rm -rf bin/ dist/ node_modules/.cache
```
### Cross-Platform Support
```justfile
# OS-specific commands
[linux]
install-deps:
sudo apt-get install -y build-essential
[macos]
install-deps:
brew install gcc
[windows]
install-deps:
choco install mingw
```
### Anti-Patterns to Avoid
- **Never use `#!/usr/bin/env bash` shebang per recipe** — Use `set shell` globally
- **Never hardcode absolute paths** — Use variables and `justfile_directory()`
- **Never ignore the `[confirm]` attribute** — Always guard destructive operations
- **Never use `@` on every line** — Use `[no-exit-message]` attribute instead
- **Avoid complex logic in recipes** — Extract to shell scripts for anything over ~10 lines
---
## 4. Magefile (mage)
### Required Build Tag and Imports
```go
//go:build mage
package main
import (
"fmt"
"os"
"github.com/magefile/mage/mg"
"github.com/magefile/mage/sh"
)
```
The `//go:build mage` constraint ensures the file is only compiled by Mage, not `go build`.
### Function Documentation
Every exported function MUST have a doc comment (shown in `mage -l`):
```go
// Build compiles the project binary.
func Build() error {
return sh.RunV("go", "build", "-o", "bin/app", "./cmd/app")
}
```
### Dependencies
Use `mg.Deps` for parallel and `mg.SerialDeps` for sequential:
```go
// CI runs the full CI pipeline (lint, test, build in parallel).
func CI() {
mg.Deps(Lint, Test, Build)
}
// Release creates a new release (test first, then build, then publish).
func Release() error {
mg.SerialDeps(Test, Build)
return publish()
}
```
### Namespaces
Group related targets using namespace types:
```go
type Docker mg.Namespace
// Build creates the Docker image.
func (Docker) Build() error {
tag := fmt.Sprintf("%s:%s", imageName(), version())
return sh.RunV("docker", "build", "-t", tag, ".")
}
// Push pushes the Docker image to the registry.
func (Docker) Push() error {
tag := fmt.Sprintf("%s:%s", imageName(), version())
return sh.RunV("docker", "push", tag)
}
```
### Shell Helpers
Use `sh` package functions appropriately:
```go
// sh.Run — run, discard output
// sh.RunV — run, stream output to stdout (verbose)
// sh.RunWith — run with env vars
// sh.Output — run, capture output as string
func version() string {
v, _ := sh.Output("git", "describe", "--tags", "--always", "--dirty")
if v == "" {
return "dev"
}
return v
}
```
### Default Target and Aliases
```go
// Default target when `mage` is run without arguments.
var Default = Build
// Aliases maps short names to targets.
var Aliases = map[string]interface{}{
"b": Build,
"t": Test,
"l": Lint,
}
```
### Error Handling
Always return `error` from targets that can fail:
```go
// Clean removes build artifacts.
func Clean() error {
if err := sh.Rm("bin"); err != nil {
return fmt.Errorf("removing bin: %w", err)
}
if err := sh.Rm("dist"); err != nil {
return fmt.Errorf("removing dist: %w", err)
}
return nil
}
```
### Environment Variables
```go
func env(key, fallback string) string {
if v := os.Getenv(key); v != "" {
return v
}
return fallback
}
// Usage
func Deploy() error {
target := env("DEPLOY_ENV", "staging")
return sh.RunV("./deploy.sh", target)
}
```
### Anti-Patterns to Avoid
- **Never forget the build tag** — Without `//go:build mage`, the file breaks `go build`
- **Never use `os/exec` directly** — Always use `sh.Run*` helpers (they handle errors and output)
- **Never use `log.Fatal` or `os.Exit`** — Return errors and let Mage handle exit codes
- **Never skip doc comments** — Undocumented functions don't appear in `mage -l`
- **Never put non-mage code in the magefile** — Keep it focused on build targets
- **Avoid global state** — Use function parameters or environment variables
---
## 5. PHP-Specific Patterns
PHP projects don't have a dedicated build tool like Mage for Go. Use Makefile, Taskfile, or Justfile. The following patterns apply regardless of which tool wraps them.
### Composer as the Foundation
Always use Composer for dependency management. Detect the presence of `composer.json` and `composer.lock`:
```bash
# Install (CI-friendly, reproducible)
composer install --no-interaction --prefer-dist --optimize-autoloader
# Install for production (skip dev deps)
composer install --no-dev --no-interaction --prefer-dist --optimize-autoloader
```
### Laravel Artisan Commands
When Laravel is detected (`artisan` file exists, `laravel/framework` in composer.json), include Artisan-based targets:
```
serve → php artisan serve
migrate → php artisan migrate
seed → php artisan db:seed
fresh → php artisan migrate:fresh --seed (DANGEROUS — guard with confirm)
tinker → php artisan tinker
routes → php artisan route:list
cache:clear → php artisan cache:clear + config:clear + route:clear + view:clear
optimize → php artisan config:cache + route:cache + view:cache
```
### Symfony Console Commands
When Symfony is detected (`bin/console` exists, `symfony/framework-bundle` in composer.json):
```
serve → symfony server:start (or php -S localhost:8000 -t public/)
migrate → php bin/console doctrine:migrations:migrate
cache:clear → php bin/console cache:clear
routes → php bin/console debug:router
```
### Testing Tools
| Tool | Command | Detection |
|------|---------|-----------|
| PHPUnit | `./vendor/bin/phpunit` | `phpunit.xml` or `phpunit.xml.dist` |
| Pest | `./vendor/bin/pest` | `pestphp/pest` in composer.json |
| Paratest | `./vendor/bin/paratest` | `brianium/paratest` in composer.json |
### Linting & Static Analysis
| Tool | Command | Detection |
|------|---------|-----------|
| PHP-CS-Fixer | `./vendor/bin/php-cs-fixer fix` | `.php-cs-fixer.php` or `.php-cs-fixer.dist.php` |
| PHP_CodeSniffer | `./vendor/bin/phpcs` / `phpcbf` | `phpcs.xml` or `phpcs.xml.dist` |
| PHPStan | `./vendor/bin/phpstan analyse` | `phpstan.neon` or `phpstan.neon.dist` |
| Psalm | `./vendor/bin/psalm` | `psalm.xml` or `psalm.xml.dist` |
| Pint (Laravel) | `./vendor/bin/pint` | `laravel/pint` in composer.json |
### Anti-Patterns to Avoid
- **Never run `composer install` without `--no-interaction`** in CI — it hangs on prompts
- **Never use `php artisan migrate:fresh` without a confirmation guard** — it drops all tables
- **Never hardcode `php` path** — use a variable (`PHP ?= php`) for flexibility (e.g., `php8.2`)
- **Never skip `--optimize-autoloader`** in production installs — significant performance impact
- **Never cache config in development** — `config:cache` breaks `.env` loading with `env()` calls
---
## Cross-Cutting Concerns (All Tools)
### Standard Targets
Every build file should include these core targets:
| Target | Purpose |
|--------|---------|
| `help` / `default` | Show available targets |
| `build` | Compile / bundle the project |
| `test` | Run test suite |
| `lint` | Run linters and formatters |
| `clean` | Remove build artifacts |
| `dev` | Start development server/watcher |
| `fmt` / `format` | Format source code |
### Optional Targets (include when relevant)
| Target | When to Include |
|--------|-----------------|
| `docker:build` / `docker:push` | Dockerfile exists |
| `db:migrate` / `db:seed` | Database migrations detected |
| `deploy` | CI/CD or deploy scripts detected |
| `generate` | Code generation tools detected |
| `release` | Tag-based release workflow |
| `install` / `setup` | First-time project setup |
| `ci` | Aggregate target for CI pipelines |
| `cache:clear` / `optimize` | PHP/Laravel framework detected |
| `phpstan` / `typecheck` | Static analysis tool detected |
### Variable Naming
- Use `SCREAMING_SNAKE_CASE` for Makefile and Justfile variables
- Use `PascalCase` for Taskfile vars (Go template convention)
- Use `camelCase` for Magefile variables (Go convention)
### Git Integration
Always include version/commit detection:
```
VERSION = git describe --tags --always --dirty
COMMIT = git rev-parse --short HEAD
BUILD_TIME = date -u +%Y-%m-%dT%H:%M:%SZ
```
### .env Support
- **Makefile**: Include `-include .env` or use `$(shell cat .env | xargs)`
- **Taskfile**: Use `dotenv: ['.env']`
- **Justfile**: Use `set dotenv-load`
- **Magefile**: Use `godotenv` package or manual `os.Getenv`
references/DOC-INTEGRATION.md
# Project Documentation Integration
After writing the build file, integrate the quick commands into the project's documentation. This step ensures that developers can discover commands not just via `make help` / `task --list` / `just` / `mage -l`, but also in the docs they already read.
## Quick Reference Block
Build a `QUICK_REFERENCE` block — a compact markdown table or code block listing the most important commands:
```markdown
## Quick Commands
| Command | Description |
|---------|-------------|
| `make dev` | Start development server |
| `make test` | Run tests |
| `make lint` | Run linters |
| `make build` | Build for production |
| `make docker-dev` | Start dev environment in Docker |
| `make docker-prod-build` | Build production Docker image |
| `make clean` | Remove build artifacts |
```
Adapt the command prefix (`make` / `task` / `just` / `mage`) to match `TARGET_TOOL`.
## 7.1 Update Existing Markdown Files
Scan for markdown files that already contain command/usage sections:
```
Grep in *.md: "## Commands\b|## Quick Start\b|## Usage\b|## Development\b|## Getting Started\b|## How to Run\b|```sh|```bash"
```
For each matching file:
- Read the file and find the relevant section
- Check if it already lists commands for the generated tool — if so, update the command list to match the new/enhanced build file
- If the section exists but doesn't mention our build tool, **append** the quick reference block after the existing commands
- Do NOT delete or rewrite existing content — only add or update the command references
**Be conservative**: only touch files that clearly have a "commands" or "getting started" section. Don't inject commands into unrelated markdown files.
## 7.2 Update Project README
```
Glob: README.md, README.rst, readme.md
```
If a README exists:
- Check if it has a commands/usage/development section (same grep patterns as 7.1)
- If yes → update/append the quick reference there
- If no → add a `## Quick Commands` section before the last section (typically "License" or "Contributing"), or at the end if no such section exists
- Keep it concise — link to the build file for the full list: `Run \`make help\` for all available targets.`
## 7.3 AGENTS.md Integration
```
Glob: AGENTS.md, agents.md, CLAUDE.md, claude.md, .github/copilot-instructions.md, .cursorrules, .cursor/rules/*.md
```
**If an agent instruction file exists** (AGENTS.md, CLAUDE.md, etc.):
- Read it and check if it already has a build/commands section
- If no build section → append a section with quick commands that AI agents should use:
```markdown
## Build & Development Commands
This project uses [Makefile|Taskfile|justfile|Magefile] for build automation.
Common commands:
- `make test` — always run tests before committing
- `make lint` — run linters, fix issues before pushing
- `make build` — verify the project builds cleanly
- `make docker-dev` — start the full dev environment
Run `make help` for all available targets.
```
- If a build section already exists → update it to reflect the current targets
**If NO agent instruction file exists**, suggest creating one:
```
AskUserQuestion: This project doesn't have an AGENTS.md (AI agent instructions). Should I create one with build commands?
Options:
1. Create AGENTS.md — Add build commands and basic project instructions for AI agents
2. Skip — Don't create agent instructions
```
If the user chooses to create it, generate a minimal `AGENTS.md` with:
- Project name and brief description (from `PROJECT_PROFILE` or `.ai-factory/DESCRIPTION.md`)
- Build commands section (as above)
- Key project conventions (language, test framework, linter — so AI agents run the right commands)
## 7.4 Summary of Documentation Changes
After all doc updates, append to the Step 6 summary:
```
### Documentation Updated
- README.md — Added Quick Commands section
- AGENTS.md — Created with build commands
- docs/getting-started.md — Updated command examples
```
references/SUMMARY-FORMAT.md
# Summary Display Formats
## Mode B (Generate New) — show what was created:
```
## Generated: [Filename]
### Targets
| Target | Description |
|--------|-------------|
| build | Compile the project binary |
| test | Run test suite |
| lint | Run golangci-lint |
| ... | ... |
### Project Profile Used
- Language: Go
- Package Manager: go modules
- Framework: Chi
- Docker: yes
- Migrations: goose
- Linters: golangci-lint
### Quick Start
[tool-specific run command, e.g., "make help", "task --list", "just", "mage -l"]
```
## Mode A (Enhance Existing) — show what was changed:
```
## Enhanced: [Filename]
### What Changed
- Added missing preamble: `.SHELLFLAGS`, `.DELETE_ON_ERROR`
- Added `help` target with self-documenting pattern
- Added `docker-build` and `docker-push` targets (Dockerfile detected)
- Added `db-migrate` target (Prisma detected)
- Added `##` descriptions to 3 existing targets
- Added VERSION/COMMIT variables via git
### New Targets Added
| Target | Description |
|--------|-------------|
| help | Show available targets |
| docker-build | Build Docker image |
| db-migrate | Run Prisma migrations |
### Existing Targets (unchanged)
| Target | Description |
|--------|-------------|
| build | Build the project |
| test | Run tests |
| ... | ... |
```
## Installation Hint (both modes)
If the tool requires installation, include a note:
```
### Installation
[install instructions for task/just/mage if not already installed]
```
Installation hints:
- **Task**: `go install github.com/go-task/task/v3/cmd/task@latest` or `brew install go-task`
- **Just**: `cargo install just` or `brew install just`
- **Mage**: `go install github.com/magefile/mage@latest` or `brew install mage`
- **Make**: Usually pre-installed; `brew install make` on macOS for GNU Make 4+
SKILL.md
---
name: aif-build-automation
description: >-
Analyze project and generate or enhance build automation file (Makefile, Taskfile.yml, Justfile, Magefile.go).
If a build file already exists, improves it by adding missing targets and best practices.
Use when user says "generate makefile", "create taskfile", "add justfile", "setup mage", or "build automation".
argument-hint: "[makefile|taskfile|justfile|mage]"
allowed-tools: Read Edit Glob Grep Write Bash(git *) AskUserQuestion Questions
disable-model-invocation: false
metadata:
author: AI Factory
version: "1.0"
category: build-automation
---
# Build Automation Generator
Generate or enhance a build automation file for any project. Supports Makefile, Taskfile.yml, Justfile, and Magefile.go.
**Two modes:**
- **Generate** — No build file exists → create one from scratch using best-practice templates
- **Enhance** — Build file already exists → analyze gaps, add missing targets, fix anti-patterns, preserve existing work
---
## Step 0: Load Project Context
Read the project description if available:
```
Read .ai-factory/DESCRIPTION.md
```
Store the project context (tech stack, framework, architecture) for use in later steps. If the file doesn't exist, that's fine — we'll detect everything in Step 2.
**Read `.ai-factory/skill-context/aif-build-automation/SKILL.md`** — MANDATORY if the file exists.
This file contains project-specific rules accumulated by `/aif-evolve` from patches,
codebase conventions, and tech-stack analysis. These rules are tailored to the current project.
**How to apply skill-context rules:**
- Treat them as **project-level overrides** for this skill's general instructions
- When a skill-context rule conflicts with a general rule written in this SKILL.md,
**the skill-context rule wins** (more specific context takes priority — same principle as nested CLAUDE.md files)
- When there is no conflict, apply both: general rules from SKILL.md + project rules from skill-context
- Do NOT ignore skill-context rules even if they seem to contradict this skill's defaults —
they exist because the project's experience proved the default insufficient
- **CRITICAL:** skill-context rules apply to ALL outputs of this skill — including the generated
build files (Makefile, Taskfile, justfile, magefile). Templates in this skill are **base structures**.
If a skill-context rule says "build file MUST include target X" or "MUST follow convention Y" —
you MUST comply. Generating build automation that violates skill-context rules is a bug.
**Enforcement:** After generating any output artifact, verify it against all skill-context rules.
If any rule is violated — fix the output before presenting it to the user.
---
## Step 1: Detect Existing Build Files & Determine Mode
### 1.1 Scan for Existing Build Files
Before anything else, check if the project already has build automation:
```
Glob: Makefile, makefile, GNUmakefile, Taskfile.yml, Taskfile.yaml, taskfile.yml, justfile, Justfile, .justfile, magefile.go, magefiles/*.go
```
Build a list of `EXISTING_FILES` from the results.
### 1.2 Determine Mode
**Mode A — Enhance Existing** (if `EXISTING_FILES` is not empty):
- Set `MODE = "enhance"`
- Set `TARGET_TOOL` automatically from the detected file (Makefile → `makefile`, Taskfile.yml → `taskfile`, etc.)
- If multiple build files exist AND `$ARGUMENTS` specifies one, use the argument to pick which one to enhance
- If multiple build files exist AND no argument, ask which one to enhance:
```
AskUserQuestion: This project has multiple build files. Which one should I improve?
Options (dynamic, based on what exists):
1. Makefile — Enhance the existing Makefile
2. Taskfile.yml — Enhance the existing Taskfile
...
```
- Read the existing file content — this is the baseline for enhancement
- Store as `EXISTING_CONTENT`
**Mode B — Generate New** (if `EXISTING_FILES` is empty):
- Set `MODE = "generate"`
- Parse `$ARGUMENTS` to determine tool:
| Argument | Tool | Output File |
|----------|------|-------------|
| `makefile` or `make` | GNU Make | `Makefile` |
| `taskfile` or `task` | Taskfile | `Taskfile.yml` |
| `justfile` or `just` | Just | `justfile` |
| `mage` or `magefile` | Mage | `magefile.go` |
- If `$ARGUMENTS` is empty or doesn't match, ask the user interactively:
```
AskUserQuestion: Which build automation tool do you want to generate?
Options:
1. Makefile — GNU Make (universal, no install needed)
2. Taskfile.yml — Task runner (YAML, modern, cross-platform)
3. justfile — Just command runner (simple, fast, ergonomic)
4. magefile.go — Mage (Go-native, type-safe, no shell scripts)
```
Store the chosen tool as `TARGET_TOOL`.
---
## Step 2: Analyze Project
Detect the project profile by scanning the repository with `Glob` and `Grep`. **Use the same flow for every stack:** primary language → package manager / build entrypoints → frameworks → Docker → CI → migrations → tests → linters → monorepo, then the Summary object. JVM projects are handled **inside those steps** (not a separate pipeline).
### 2.1 Primary Language
Check for these files (first match wins in the table order below). For **Java / Kotlin (JVM)**, infer language from build files: default **Java** unless Kotlin plugins / `kotlin("jvm")` / dominant `.kt` layout suggests **Kotlin**.
| File / signal | Language |
|----------------|----------|
| `go.mod` | Go |
| `package.json` | Node.js / JavaScript / TypeScript |
| `pyproject.toml` or `setup.py` or `setup.cfg` | Python |
| `Cargo.toml` | Rust |
| `composer.json` | PHP |
| `Gemfile` | Ruby |
| JVM: Gradle root or wrapper (see §2.2) | Java / Kotlin (JVM) |
| JVM: `pom.xml` | Java / Kotlin (JVM) |
| `*.csproj` or `*.sln` | C# / .NET |
### 2.2 Package manager & build entrypoints
**Lock files and wrappers (same idea as `package-lock.json` → npm):**
| File | Package manager / tool |
|------|-------------------------|
| `bun.lockb` | bun |
| `pnpm-lock.yaml` | pnpm |
| `yarn.lock` | yarn |
| `package-lock.json` | npm |
| `poetry.lock` | poetry |
| `uv.lock` | uv |
| `Pipfile.lock` | pipenv |
| `gradle/wrapper/gradle-wrapper.properties` | `./gradlew` |
| `.mvn/wrapper/maven-wrapper.properties` | `./mvnw` |
**Java / Kotlin (JVM) — Gradle vs Maven:** Detect Gradle with **one batch** of checks (single `Glob` over the paths below, or parallel existence checks — avoid redundant sequential walks):
- `settings.gradle`, `settings.gradle.kts`, `build.gradle`, `build.gradle.kts` (repo root), `gradle/wrapper/gradle-wrapper.properties`
If any Gradle signal matches → Gradle is in play. **`pom.xml`** indicates Maven. Set `PROJECT_PROFILE.java_build.build_tool` from this table:
| Condition | `build_tool` | Notes |
|-----------|--------------|--------|
| Gradle signals present | `gradle` | Wire targets to Gradle commands below. |
| No Gradle, `pom.xml` present | `maven` | Wire targets to Maven commands below. |
| Gradle **and** `pom.xml` | `gradle` | Set `java_build.mixed_maven_gradle: true` and append a **warning** to `PROJECT_PROFILE.warnings` (both builds present; recipes follow Gradle — user confirms authoritative build). |
**Concrete JVM Entrypoint:** Persist the detected entrypoint in `PROJECT_PROFILE.build_entrypoint` based on wrapper presence:
- If `build_tool` is `gradle`: use `./gradlew` if `gradlew` or `gradle/wrapper/gradle-wrapper.properties` exists, else fallback to `gradle`.
- If `build_tool` is `maven`: use `./mvnw` if `mvnw` or `.mvn/wrapper/maven-wrapper.properties` exists, else fallback to `mvn`.
**Single source of truth:** The predicate above is **the same rule** the JVM templates implement in shell (`ENTRYPOINT` / `entrypoint` — test `./gradlew` **or** `gradle/wrapper/gradle-wrapper.properties`; test `./mvnw` **or** `.mvn/wrapper/maven-wrapper.properties`). When generating or enhancing build files, set `PROJECT_PROFILE.build_entrypoint` to the **result** those tests imply (`./gradlew` vs `gradle`, `./mvnw` vs `mvn`). Do not emit a different entrypoint string than that predicate unless the user overrides (e.g. Makefile `ENTRYPOINT=…`). Templates re-resolve at recipe runtime so clones stay correct without editing.
**Version catalog:** If `gradle/libs.versions.toml` exists, set `java_build.has_version_catalog` and document `PROJECT_PROFILE.build_entrypoint` / catalog usage in comments where helpful.
**Commands to wire** into Makefile / Taskfile / Just for JVM (same role as `npm run build` / `pytest` for other stacks; use `gradlew.bat` on Windows):
| Goal | Gradle | Maven |
|------|--------|--------|
| Full compile + checks | `<build_entrypoint> build` | `<build_entrypoint> verify` |
| Unit / integration tests | `<build_entrypoint> test` | `<build_entrypoint> test` |
| Verification (tests + static analysis where configured) | `<build_entrypoint> check` | `<build_entrypoint> verify` |
| Package only | `<build_entrypoint> assemble` (or `jar` / `bootJar`) | `<build_entrypoint> package` |
| Dev server — Spring Boot (see §2.3) | `<build_entrypoint> bootRun` | `<build_entrypoint> spring-boot:run` |
| Dev server — Quarkus | `<build_entrypoint> quarkusDev` | `<build_entrypoint> quarkus:dev` |
| Dev server — Micronaut | `<build_entrypoint> run` | `<build_entrypoint> mn:run` |
| Dev server — Vert.x | `<build_entrypoint> vertxRun` | `<build_entrypoint> vertx:run` |
| Spring Boot — runnable JAR | `<build_entrypoint> bootJar` | `<build_entrypoint> package` (spring-boot repackage) |
| Clean | `<build_entrypoint> clean` | `<build_entrypoint> clean` |
| Multi-module | `<build_entrypoint> :subproject:build` | `<build_entrypoint> -pl module -am package` |
**`dev` target (templates + generated files):** Resolve the **framework dev task/goal** from the same signals as §2.3, **fixed priority** (first match wins): **Quarkus → Micronaut → Vert.x → Spring Boot**. Scan **Gradle:** `build.gradle`, `build.gradle.kts`, `settings.gradle`, `settings.gradle.kts`, `gradle/libs.versions.toml` with the same `grep -E` patterns you use for §2.3 (`quarkus` / `io.quarkus`; `micronaut` / `io.micronaut`; Vert.x Gradle plugin — `vertx-plugin` or `io.vertx.vertx`; Spring Boot — fallback). Scan **Maven:** `pom.xml` only; Vert.x Maven — `vertx-maven-plugin` or `io.reactiverse`. If the repo root is an aggregator and detection misses, override the template’s dev task variable (same idea as **`JVM_MODULE`**).
**Templates:** JVM Makefile/Taskfile/Just ship a **fixed catalog**: **`lint`** → Gradle `check` / Maven `verify`; **`fmt`** → `spotlessApply` / `spotless:apply`; **`lint-checkstyle`**, **`lint-spotbugs`**, **`lint-pmd`**, **`lint-spotless`** (Taskfile `lint:*`); **`db-migrate-liquibase`**, **`db-migrate-flyway`** (Taskfile `db:migrate:*`). Multi-module: **`module-*`** with **`JVM_MODULE`**. Step 5 **removes** catalog entries the repo does not wire (see JVM template rules).
### 2.3 Framework Detection
For Node.js projects, check `package.json` dependencies for:
- `next` → Next.js
- `nuxt` → Nuxt
- `@remix-run/node` → Remix
- `express` → Express
- `fastify` → Fastify
- `hono` → Hono
- `@nestjs/core` → NestJS
For Python projects, check `pyproject.toml` or imports for:
- `fastapi` → FastAPI
- `django` → Django
- `flask` → Flask
For PHP projects, check `composer.json` require for:
- `laravel/framework` → Laravel
- `symfony/framework-bundle` → Symfony
- `slim/slim` → Slim
- `cakephp/cakephp` → CakePHP
For Go projects, check `go.mod` for:
- `gin-gonic/gin` → Gin
- `labstack/echo` → Echo
- `gofiber/fiber` → Fiber
- `go-chi/chi` → Chi
For Rust projects, read `Cargo.toml` (workspace members and `[dependencies]` / `[workspace.dependencies]`) for:
- `axum` → Axum
- `actix-web` → Actix Web
- `rocket` → Rocket
- `warp` → Warp
For Ruby projects, read `Gemfile` for:
- `rails` → Ruby on Rails
- `sinatra` → Sinatra
- `hanami` → Hanami
- `roda` → Roda
For Java / JVM projects, read `pom.xml`, `build.gradle*`, and `gradle/libs.versions.toml` (when present) for dependencies and plugins — same discovery depth as `package.json` for Node:
- `spring-boot`, `spring-boot-starter`, `spring-boot-parent` → Spring Boot
- `grpc`, `protobuf`, `spring-grpc` or `*.proto` in repo → gRPC / protobuf
- `quarkus`, `io.quarkus` → Quarkus
- `micronaut` → Micronaut
- `vertx` / Vert.x stack → Vert.x
- `liquibase` in deps or `db.changelog*` → Liquibase (see §2.6)
- Flyway `org.flywaydb` / `flyway-core` / `flyway-maven-plugin` / Flyway Gradle plugin in `pom.xml`, `build.gradle*`, or `gradle/libs.versions.toml` → Flyway (see §2.6)
- Prefer **Jakarta** (`jakarta.*`) for Java 9+ / Spring Boot 3+; flag legacy `javax.*` migration if both appear
Map findings into `framework` / `java_build` flags (`spring_boot`, `grpc`, `liquibase`, `flyway`) like other ecosystems map Express vs NestJS.
### 2.4 Docker (Deep Scan)
```
Glob: Dockerfile, Dockerfile.*, docker-compose.yml, docker-compose.yaml, compose.yml, compose.yaml, .dockerignore
```
If any exist, set `HAS_DOCKER=true` and perform a deeper analysis:
**Read the Dockerfile(s)** to detect:
- Multi-stage builds (separate `dev` / `prod` stages) → `DOCKER_MULTISTAGE=true`
- Exposed ports → `DOCKER_PORTS` (e.g., `3000`, `8080`)
- Base image → `DOCKER_BASE` (e.g., `node:20-alpine`, `golang:1.22`)
- Entrypoint/CMD → understand how the app is started inside the container
**Read docker-compose / compose file** to detect:
- Service names → `DOCKER_SERVICES` (e.g., `app`, `db`, `redis`, `worker`)
- Volume mounts → understand dev vs prod setup
- Profiles (if any) → `dev`, `production`, `test`
- Dependency services (postgres, redis, rabbitmq, etc.) → `DOCKER_DEPS`
Store as `DOCKER_PROFILE`:
- `has_compose`: boolean
- `has_multistage`: boolean
- `services`: list of service names
- `deps`: list of infrastructure services (db, cache, queue)
- `ports`: exposed ports
- `has_dev_stage`: boolean (Dockerfile has a `dev` or `development` stage)
### 2.5 CI/CD
```
Glob: .github/workflows/*.yml, .gitlab-ci.yml, .circleci/config.yml, Jenkinsfile, .travis.yml
```
Note which CI system is in use.
### 2.6 Database & Migrations
Search for migration tools:
```
Grep: prisma|drizzle|knex|typeorm|sequelize|alembic|django.*migrate|goose|migrate|atlas|sqlx|liquibase|flyway
```
Check for:
- `prisma/schema.prisma` → Prisma
- `drizzle.config.ts` → Drizzle
- `alembic/` directory → Alembic
- `migrations/` directory → Generic migrations
- Liquibase — `db.changelog*`, `liquibase` in Gradle/Maven or resources → Liquibase (JVM and others); set **`java_build.liquibase: true`**
- Flyway — dependency or plugin (`org.flywaydb`, `flyway-core`, `flyway-maven-plugin`, Flyway Gradle plugin) in `pom.xml`, `build.gradle*`, or `gradle/libs.versions.toml`; set **`java_build.flyway: true`**
### 2.7 Test Framework
| Language | Check For |
|----------|-----------|
| Node.js | `jest`, `vitest`, `mocha`, `ava` in package.json |
| Python | `pytest` in pyproject.toml/requirements, `unittest` imports |
| Go | Go has built-in testing; check for `testify` in go.mod |
| Rust | Built-in; check for integration test directory `tests/` |
| Ruby | `rspec` in Gemfile → RSpec; `minitest` / `minitest-` gems → Minitest; else default `rake test` when `Rakefile` exists |
| Java / Kotlin (JVM) | `junit-jupiter`, `junit-jupiter-api`, `JUnitPlatform`, `JUnit5`, `testcontainers`, `mockito`, `rest-assured`, `cucumber` in Gradle/Maven / `libs.versions.toml` |
### 2.8 Linters & Formatters
Scan for formatter/linter configs (EditorConfig, Checkstyle on JVM, ESLint/Prettier/Biome, Python tools, PHP, Go, Rust, Ruby):
```
Glob: .eslintrc*, eslint.config.*, .prettierrc*, biome.json, biome.jsonc, .golangci.yml, .golangci.yaml
Glob: checkstyle.xml, .checkstyle.xml, config/checkstyle/checkstyle.xml, .editorconfig
Glob: ruff.toml, .ruff.toml, .flake8, phpcs.xml, phpcs.xml.dist
Glob: rustfmt.toml, .rustfmt.toml, clippy.toml, .rubocop.yml, .rubocop_todo.yml, .standard.yml
Grep in pyproject.toml: ruff|black|flake8|pylint|isort
Grep in build.gradle*, pom.xml: spotless|spotbugs|pmd|errorprone|checkstyle (when not covered by config files alone)
```
Merge JVM matches into **`PROJECT_PROFILE.linters`** as normalized ids (e.g. `checkstyle`, `spotless`, `spotbugs`, `pmd`, `errorprone`) for use when wiring **`lint`** / **`fmt`** targets (Step 5).
### 2.9 Monorepo Detection
```
Glob: turbo.json, nx.json, lerna.json, pnpm-workspace.yaml
```
### Summary
Build a `PROJECT_PROFILE` object with:
- `language`: primary language
- `package_manager`: detected PM (npm, pnpm, Gradle, Maven, …)
- `build_entrypoint`: the exact entrypoint command detected (e.g. `./gradlew`, `mvn`, `npm`, `cargo`)
- `framework`: detected framework (if any); JVM frameworks map here the same way as NestJS or Django
- `warnings`: optional string array (e.g. mixed Maven+Gradle from §2.2)
- `java_build`: optional — when language is JVM: `{ build_tool: "gradle"|"maven", mixed_maven_gradle?: boolean, has_version_catalog: boolean, spring_boot: boolean, grpc: boolean, liquibase: boolean, flyway: boolean }`
- `has_docker`: boolean
- `docker_profile`: `DOCKER_PROFILE` object (if `has_docker`)
- `ci_system`: detected CI (if any)
- `has_migrations`: boolean + tool name
- `test_framework`: detected test runner
- `linters`: list of detected linters
- `is_monorepo`: boolean
- `has_dev_server`: boolean (framework with dev server)
---
## Step 3: Read Best Practices
Read the best practices reference for the chosen tool:
```
Read skills/aif-build-automation/references/BEST-PRACTICES.md
```
Focus on the section matching `TARGET_TOOL`:
- Makefile → Section 1
- Taskfile → Section 2
- Justfile → Section 3
- Magefile → Section 4
Also read the "Cross-Cutting Concerns" section for standard targets.
---
## Step 4: Select & Read Template
Pick the closest matching template based on `language` + `TARGET_TOOL`:
| Tool | Go | Node.js | Python | PHP | Rust | Ruby | Java / JVM | Other |
|------|----|---------|--------|-----|------|------|------------|------------------------|
| Makefile | `makefile-go.mk` | `makefile-node.mk` | `makefile-python.mk` | `makefile-php.mk` | `makefile-rust.mk` | `makefile-ruby.mk` | `makefile-gradle.mk` or `makefile-maven.mk` | Use closest match |
| Taskfile | `taskfile-go.yml` | `taskfile-node.yml` | `taskfile-python.yml` | `taskfile-php.yml` | `taskfile-rust.yml` | `taskfile-ruby.yml` | `taskfile-gradle.yml` or `taskfile-maven.yml` | Use closest match |
| Justfile | `justfile-go` | `justfile-node` | `justfile-python` | `justfile-php` | `justfile-rust` | `justfile-ruby` | `justfile-gradle` or `justfile-maven` | Use closest match |
| Magefile | `magefile-basic.go` | `magefile-full.go` | `magefile-full.go` | N/A (use Makefile) | N/A (use Makefile) | N/A (use Makefile) | N/A (use Makefile) | N/A (use Makefile) |
For Java / JVM, select the Gradle or Maven template based on `PROJECT_PROFILE.java_build.build_tool`.
If `language` is **not** among Go, Node.js, Python, PHP, Rust, Ruby, or Java / JVM in the table above, use the **Node.js** template as the structural fallback and adapt it to the detected `build_entrypoint` and language conventions (e.g., `dotnet build`).
For Magefile: use `magefile-full.go` if `HAS_DOCKER` or `has_migrations` is true, otherwise `magefile-basic.go`.
For PHP, Rust, Ruby, or Java/JVM + Magefile: Mage is Go-specific and not generally applicable to these stacks. If the user explicitly requested `mage` for such a project, explain this and suggest Makefile as the closest alternative (universal, no install needed). Ask via `AskUserQuestion` whether to proceed with Makefile instead.
Read the selected template:
```
Read skills/aif-build-automation/templates/<selected-template>
```
---
## Step 5: Generate or Enhance File
### Mode B — Generate New File
Using the `PROJECT_PROFILE`, best practices, and template as reference, generate a customized build file from scratch.
#### Generation Rules
1. **Start with the tool's required preamble** (from best practices)
2. **Include all standard targets** from the selected template (help/default, build, test, lint, clean, dev, fmt, `ci`). **JVM:** the template is a **complete catalog**; prune targets in Mode B per Step 5 JVM rules (do not invent one-off `lint` recipes).
3. **Add conditional targets** based on project profile:
- Docker targets → only if `has_docker`
- Database targets → only if `has_migrations` (non-JVM); **JVM:** use the canonical **`db-migrate-liquibase`** / **`db-migrate-flyway`** (or Taskfile `db:migrate:*`) **only when** the matching **`java_build`** flag is true — omit the other
- Deploy targets → only if CI/CD detected
- Generate target → only if code generation detected
- Typecheck target → only if TypeScript or mypy detected
4. **Use correct package manager** — match `PROJECT_PROFILE` (§2.2): JVM → `<build_entrypoint>` (from §2.2); Node → npm/pnpm/yarn/bun; Python → uv/poetry/pip; Go → `go`; Rust → `cargo`; Ruby → Bundler (`bundle`, `bundle exec`); do not substitute the wrong ecosystem (e.g. npm scripts for a Gradle-only repo)
5. **Include CI aggregate target** — default **`ci`** = **clean** + **build** on JVM (already runs `check`/`verify`); add **`lint`** / **`fmt`** to **`ci`** only if those targets remain after pruning
6. **Follow the template's structure** for organization and grouping
7. **Adapt variable names** to match the actual project (module name, binary name, source dirs); **JVM multi-module** repos → set **`JVM_MODULE`** for `module-*` targets (§2.2)
8. **Include version/commit/build-time** detection via git
9. **Docker-aware targets** — if `has_docker`, generate a dedicated Docker section (see below)
**JVM template catalog (fixed names; prune unused tools in Mode B)** — Source of truth is **`skills/aif-build-automation/templates/*gradle*`** and **`*maven*`**. Always use these **exact** Gradle/Maven task names in generated files unless the build files use a different official task name for the same plugin (document in a comment next to the recipe).
| Target (Make/Just) | Taskfile task | Gradle command | Maven command |
|--------------------|---------------|----------------|---------------|
| `lint` | `lint` | `check` | `verify` |
| `fmt` | `fmt` | `spotlessApply` | `spotless:apply` |
| `lint-checkstyle` | `lint:checkstyle` | `checkstyleMain` | `checkstyle:check` |
| `lint-spotbugs` | `lint:spotbugs` | `spotbugsMain` | `spotbugs:check` |
| `lint-pmd` | `lint:pmd` | `pmdMain` | `pmd:check` |
| `lint-spotless` | `lint:spotless` | `spotlessCheck` | `spotless:check` |
| `db-migrate-liquibase` | `db:migrate:liquibase` | `liquibaseUpdate` | `liquibase:update` |
| `db-migrate-flyway` | `db:migrate:flyway` | `flywayMigrate` | `flyway:migrate` |
| `dev` | `dev` | see §2.2 dev tasks + template `DEV_GRADLE_TASK` resolver (§2.3 priority) | see §2.2 dev goals + template `DEV_MAVEN_GOAL` resolver (§2.3 priority) |
- **Mode B (generate):** Copy the catalog from the template, then **delete** targets whose tools are **absent**: e.g. remove **`lint-checkstyle`** if `checkstyle` ∉ **`linters`**; remove **`lint-spotbugs`** / **`lint-pmd`** if those ids are missing; remove **`fmt`** and **`lint-spotless`** if **`spotless`** ∉ **`linters`**; remove **`db-migrate-liquibase`** if not **`java_build.liquibase`**; remove **`db-migrate-flyway`** if not **`java_build.flyway`**. **Always keep** **`lint`** (= `check` / `verify`) unless the project truly has no Java plugin lifecycle (rare). Never substitute **`verify -DskipTests`** or **`check -x test`** as `lint`. For **`dev`**, templates already resolve the task/goal from build files; when enhancing, replace a wrong constant **`bootRun`** / **`spring-boot:run`** with the correct framework command from **`PROJECT_PROFILE`** (same strings as the template resolver).
- **Mode A (enhance):** Prefer missing catalog targets over ad-hoc names; remove recipes that contradict **`java_build`** / **`linters`**.
#### Docker-Aware Target Generation
When `has_docker` is true, generate **two layers** of commands:
**Layer 1 — Container lifecycle** (always when Docker detected):
| Target | Purpose |
|--------|---------|
| `docker-build` or `docker:build` | Build the Docker image |
| `docker-run` or `docker:run` | Run the container |
| `docker-stop` or `docker:stop` | Stop running containers |
| `docker-logs` or `docker:logs` | Tail container logs |
| `docker-push` or `docker:push` | Push image to registry |
| `docker-clean` or `docker:clean` | Remove images and stopped containers |
**Layer 2 — Dev vs Production separation** (when compose or multistage detected):
```
##@ Docker — Development
docker-dev: ## Start all services in dev mode (with hot reload, mounted volumes)
docker-dev-build: ## Rebuild dev containers
docker-dev-down: ## Stop dev environment and remove volumes
##@ Docker — Production
docker-prod-build: ## Build production image (optimized, multi-stage)
docker-prod-run: ## Run production container locally for testing
docker-prod-push: ## Push production image to registry
```
**Generation logic:**
- If `has_compose` → use `docker compose` commands (not `docker-compose`)
- If compose has profiles → use `--profile dev` / `--profile production`
- If `has_multistage` → use `--target dev` for dev builds, no target (or `--target production`) for prod
- If `docker_profile.deps` exist (db, redis, etc.) → add `infra-up` / `infra-down` targets to start/stop only infrastructure services without the app
- If compose detected → `docker-dev` should run `docker compose up` with correct profile/services
- If no compose but Dockerfile → `docker-dev` should run `docker build --target dev` + `docker run` with volume mounts
**Layer 3 — Container-based commands** (mirror host commands via container):
When the project is Docker-based, also generate container-exec variants so that users who run everything in Docker can use the same targets:
```
# Run tests inside the container
docker-test: ## Run tests inside the Docker container
docker compose exec app [test command]
# Run linter inside the container
docker-lint: ## Run linter inside the Docker container
docker compose exec app [lint command]
# Open shell in the container
docker-shell: ## Open a shell inside the running container
docker compose exec app sh
```
Only generate `docker-*` exec variants if the project appears to be Docker-first (compose file mounts source code as volumes, or no local language runtime setup is apparent).
#### Customization from Project Profile
- **JVM (`java_build` / Gradle or Maven)**: Use **`PROJECT_PROFILE.build_entrypoint`** from §2.2 Summary for every tool invocation. **Quality and DB:** use only the **canonical target names and task names** from the JVM template catalog (Step 5 table); when enhancing, add/remove recipes to match **`java_build`** and **`linters`**, not one-off guesses.
- **Binary name**: Use the actual project name from `go.mod`, `package.json`, or directory name
- **Source directory**: Use actual src dir (e.g., `src/`, `app/`, `cmd/`)
- **Dev server command**: Match the framework (e.g., `next dev`, `uvicorn --reload`, `air`; JVM → **`build_entrypoint`** plus the §2.2 dev task for the detected stack — Quarkus `quarkusDev` / `quarkus:dev`, Micronaut `run` / `mn:run`, Vert.x `vertxRun` / `vertx:run`, Spring Boot `bootRun` / `spring-boot:run`)
- **Test command**: Match the detected test runner (§2.7)
- **Lint command (JVM)**: After pruning, **`lint`** must remain **`check`** / **`verify`**; per-tool rows use the Step 5 catalog table
- **Migration commands (JVM)**: Use **`db-migrate-liquibase`** vs **`db-migrate-flyway`** (or Taskfile **`db:migrate:*`**) per **`java_build`**
- **Port numbers**: Use framework defaults (3000 for Node, 8000 for Python, 8080 for Go)
### Mode A — Enhance Existing File
When `MODE = "enhance"`, do NOT replace the file from scratch. Instead, analyze it and improve it surgically.
#### 5A.1 Analyze Existing File
Compare `EXISTING_CONTENT` against the `PROJECT_PROFILE` and best practices. Build a gap analysis:
**Missing preamble/config** — Check if the file has the recommended preamble:
- Makefile: `SHELL := bash`, `.ONESHELL`, `.SHELLFLAGS`, `.DELETE_ON_ERROR`, `MAKEFLAGS`
- Taskfile: `version: '3'`, `output:`, `dotenv:`
- Justfile: `set shell`, `set dotenv-load`, `set export`
- Magefile: `//go:build mage`, proper imports
**Missing standard targets** — Check which of these are absent:
- `help` / `default` (self-documenting)
- `build`, `test`, `lint`, `clean`, `dev`, `fmt`, and JVM catalog targets (`lint-checkstyle`, `db-migrate-flyway`, …) **after** template pruning
- `ci` (aggregate target)
**Missing project-specific targets** — Based on `PROJECT_PROFILE`, check for:
- Docker targets (if `has_docker` but no docker targets in file)
- Database: canonical **`db-migrate-*`** / **`db:migrate:*`** matching **`java_build`**
- Typecheck target (if TypeScript/mypy detected but no typecheck target)
- Generate target (if code generation tools detected)
- Coverage target (if test target exists but no coverage variant)
- JVM: `build` / `test` / `check` delegating to `<build_entrypoint>` when `java_build` is set (not only generic shell or wrong ecosystem)
- JVM multi-module: `module-build` / `module-test` / `module-check` (or Taskfile `module:*`) when the repo is a Gradle multi-project or Maven reactor and per-module commands are useful
**Quality issues** — Check for anti-patterns from best practices:
- **JVM:** recipes that are **not** in the Step 5 catalog table (or wrong tool on a recipe, e.g. Liquibase task on a Flyway-only repo) — replace with catalog names or delete
- Targets without descriptions/documentation
- Missing `.PHONY` declarations (Makefile)
- Hardcoded tool paths that should be variables
- Missing version/commit detection
- No self-documenting help target
#### 5A.2 Plan Changes
Build a list of specific changes to make:
```
CHANGES = [
{ type: "add_preamble", detail: "Add .SHELLFLAGS and .DELETE_ON_ERROR" },
{ type: "add_target", name: "docker-build", detail: "Dockerfile detected but no docker target" },
{ type: "add_target", name: "help", detail: "No self-documenting help target" },
{ type: "fix_quality", detail: "Add ## comments to 3 targets missing descriptions" },
{ type: "add_variable", detail: "Add VERSION/COMMIT detection via git" },
...
]
```
#### 5A.3 Apply Changes
- **Preserve the existing structure** — Keep the user's ordering, naming, and style
- **Preserve existing targets exactly** — Do NOT modify working targets unless fixing a clear bug or adding a missing description
- **Add new targets in the appropriate section** — Follow the existing grouping pattern (if the file uses `##@` sections, add to matching section; if no sections, append logically)
- **Add missing preamble lines** at the top, before existing content
- **Add missing variables** near existing variable declarations
- Use the template as reference for the syntax of new targets, but adapt to match the style already present in the file (e.g., if existing Makefile uses tabs + simple recipes, don't introduce complex multi-line scripts)
### Quality Checks (Both Modes)
Before writing the file, verify:
- [ ] All targets have descriptions/documentation (## comments, desc:, [doc()], doc comments)
- [ ] No hardcoded paths that should be variables
- [ ] Package manager / build entrypoint detection matches the repo (Gradle/Maven wrappers, npm/pnpm, etc.)
- [ ] Self-documenting help target is included
- [ ] `.PHONY` declarations for all non-file targets (Makefile only)
- [ ] Dangerous operations have confirmations (Justfile) or warnings
---
## Step 6: Write File & Report
### 6.1 Write the File
**Mode B (Generate New):**
Write the generated content using the `Write` tool:
| Tool | Output Path |
|------|-------------|
| Makefile | `Makefile` |
| Taskfile | `Taskfile.yml` |
| Justfile | `justfile` |
| Magefile | `magefile.go` |
**Mode A (Enhance Existing):**
Write the enhanced content to the same path where the existing file was found (preserving the original filename casing and location). The file is updated in-place — no need to ask about overwriting since we're improving, not replacing.
### 6.2 Display Summary
Display summary using format from `references/SUMMARY-FORMAT.md`. Shows targets table, project profile used, and quick start command for Mode B (generate), or what changed + new/existing targets for Mode A (enhance). Include installation hints if the tool requires setup.
---
## Step 7: Project Documentation Integration
After writing the build file, integrate quick commands into project docs.
For detailed integration procedures (README, AGENTS.md, existing markdown) → read `references/DOC-INTEGRATION.md`
Brief: scan for existing command sections, update or append quick reference, suggest AGENTS.md creation if missing.
## Artifact Ownership and Config Policy
- Primary ownership: generated or enhanced build automation files (`Makefile`, `Taskfile.yml`, `justfile`, `magefile.go`).
- Allowed companion updates: quick command snippets in existing docs or `AGENTS.md` when directly tied to the generated build workflow.
- Config policy: config-agnostic by design. This skill uses repository detection and fixed AI Factory context files rather than `config.yaml`.
templates/justfile-go
# --- Justfile for Go Projects ---
# Usage: just [recipe]
# Install: https://just.systems/man/en/
set shell := ["bash", "-euo", "pipefail", "-c"]
set dotenv-load
set export
set positional-arguments
# --- Variables ---
project := `basename $(pwd)`
module := `head -1 go.mod | awk '{print $2}'`
version := `git describe --tags --always --dirty 2>/dev/null || echo "dev"`
commit := `git rev-parse --short HEAD 2>/dev/null || echo "unknown"`
build_time := `date -u '+%Y-%m-%dT%H:%M:%SZ'`
bin_dir := "bin"
main_pkg := "./cmd/" + project
ldflags := "-s -w -X " + module + "/internal/version.Version=" + version + " -X " + module + "/internal/version.Commit=" + commit + " -X " + module + "/internal/version.BuildTime=" + build_time
# Docker
docker_registry := env("DOCKER_REGISTRY", "ghcr.io")
docker_image := docker_registry + "/" + project
docker_tag := version
# Default recipe - show help
[doc("Show available recipes")]
default:
@just --list --unsorted
# --- Development ---
[group("development")]
[doc("Build the binary")]
build:
go build -ldflags '{{ ldflags }}' -o {{ bin_dir }}/{{ project }} {{ main_pkg }}
[group("development")]
[doc("Build and run")]
run: build
./{{ bin_dir }}/{{ project }}
[group("development")]
[doc("Run with hot reload (requires air)")]
dev:
air
[group("development")]
[doc("Run go generate")]
generate:
go generate ./...
[group("development")]
[doc("Tidy and verify go.mod")]
tidy:
go mod tidy
go mod verify
# --- Testing ---
[group("testing")]
[doc("Run tests")]
test *args:
go test -race -count=1 {{ args }} ./...
[group("testing")]
[doc("Run tests with coverage report")]
test-cover:
go test -race -count=1 -coverprofile=coverage.out ./...
go tool cover -html=coverage.out -o coverage.html
@echo "Coverage report: coverage.html"
[group("testing")]
[doc("Run integration tests")]
test-integration:
go test -race -count=1 -tags=integration ./...
[group("testing")]
[doc("Run benchmarks")]
bench:
go test -bench=. -benchmem ./...
# --- Code Quality ---
[group("quality")]
[doc("Run linters")]
lint:
golangci-lint run ./...
[group("quality")]
[doc("Format code")]
fmt:
go fmt ./...
goimports -w .
[group("quality")]
[doc("Run go vet")]
vet:
go vet ./...
# --- Docker ---
[group("docker")]
[doc("Build Docker image")]
docker-build:
docker build \
--build-arg VERSION={{ version }} \
--build-arg COMMIT={{ commit }} \
-t {{ docker_image }}:{{ docker_tag }} \
-t {{ docker_image }}:latest \
.
[group("docker")]
[doc("Push Docker image")]
docker-push:
docker push {{ docker_image }}:{{ docker_tag }}
docker push {{ docker_image }}:latest
# --- CI ---
[group("ci")]
[doc("Run full CI pipeline")]
ci: lint test build
# --- Cleanup ---
[confirm("Remove all build artifacts?")]
[group("maintenance")]
[doc("Remove build artifacts")]
clean:
rm -rf {{ bin_dir }} coverage.out coverage.html
templates/justfile-gradle
# --- Justfile for JVM Projects (Gradle) ---
# Canonical targets; delete recipes your build.gradle does not wire (SKILL Step 5).
set shell := ["bash", "-c"]
project := `basename $(pwd)`
version := `git describe --tags --always --dirty 2>/dev/null || echo "dev"`
commit := `git rev-parse --short HEAD 2>/dev/null || echo "unknown"`
entrypoint := `if [ -f ./gradlew ] || [ -f gradle/wrapper/gradle-wrapper.properties ]; then echo "./gradlew"; else echo "gradle"; fi`
dev_gradle_task := `for f in build.gradle build.gradle.kts settings.gradle settings.gradle.kts gradle/libs.versions.toml; do test -f "$f" || continue; if grep -qE "quarkus|io\\.quarkus" "$f" 2>/dev/null; then printf %s quarkusDev; exit 0; fi; done; for f in build.gradle build.gradle.kts settings.gradle settings.gradle.kts gradle/libs.versions.toml; do test -f "$f" || continue; if grep -qE "micronaut|io\\.micronaut" "$f" 2>/dev/null; then printf %s run; exit 0; fi; done; for f in build.gradle build.gradle.kts settings.gradle settings.gradle.kts gradle/libs.versions.toml; do test -f "$f" || continue; if grep -qE "vertx-plugin|io\\.vertx\\.vertx" "$f" 2>/dev/null; then printf %s vertxRun; exit 0; fi; done; printf %s bootRun`
docker-registry := "ghcr.io"
docker-image := docker-registry + "/" + project
# --- Default ---
default:
@just --list
# --- Development ---
clean:
{{entrypoint}} clean
build:
{{entrypoint}} build
assemble:
{{entrypoint}} assemble
dev:
{{entrypoint}} {{dev_gradle_task}}
# --- Testing ---
test:
{{entrypoint}} test
check:
{{entrypoint}} check
# --- Multi-module: export JVM_MODULE=subproject-id ---
module-build:
{{entrypoint}} :{{ env("JVM_MODULE", "change-me-subproject") }}:build
module-test:
{{entrypoint}} :{{ env("JVM_MODULE", "change-me-subproject") }}:test
module-check:
{{entrypoint}} :{{ env("JVM_MODULE", "change-me-subproject") }}:check
# --- Code Quality ---
lint:
{{entrypoint}} check
fmt:
{{entrypoint}} spotlessApply
lint-checkstyle:
{{entrypoint}} checkstyleMain
lint-spotbugs:
{{entrypoint}} spotbugsMain
lint-pmd:
{{entrypoint}} pmdMain
lint-spotless:
{{entrypoint}} spotlessCheck
# --- Docker ---
docker-build:
docker build \
--build-arg VERSION={{version}} \
--build-arg COMMIT={{commit}} \
-t {{docker-image}}:{{version}} \
-t {{docker-image}}:latest \
.
docker-push:
docker push {{docker-image}}:{{version}}
docker push {{docker-image}}:latest
# --- Database ---
db-migrate-liquibase:
{{entrypoint}} liquibaseUpdate
db-migrate-flyway:
{{entrypoint}} flywayMigrate
# --- CI ---
ci: clean build
templates/justfile-maven
# --- Justfile for JVM Projects (Maven) ---
# Canonical targets; delete recipes your pom/plugins do not define (SKILL Step 5).
set shell := ["bash", "-c"]
project := `basename $(pwd)`
version := `git describe --tags --always --dirty 2>/dev/null || echo "dev"`
commit := `git rev-parse --short HEAD 2>/dev/null || echo "unknown"`
entrypoint := `if [ -f ./mvnw ] || [ -f .mvn/wrapper/maven-wrapper.properties ]; then echo "./mvnw"; else echo "mvn"; fi`
dev_maven_goal := `if ! test -f pom.xml; then printf %s spring-boot:run; exit 0; fi; if grep -qE "quarkus|io\\.quarkus" pom.xml 2>/dev/null; then printf %s quarkus:dev; exit 0; fi; if grep -qE "micronaut|io\\.micronaut" pom.xml 2>/dev/null; then printf %s mn:run; exit 0; fi; if grep -qE "vertx-maven-plugin|io\\.reactiverse" pom.xml 2>/dev/null; then printf %s vertx:run; exit 0; fi; printf %s spring-boot:run`
docker-registry := "ghcr.io"
docker-image := docker-registry + "/" + project
# --- Default ---
default:
@just --list
# --- Development ---
clean:
{{entrypoint}} clean
build:
{{entrypoint}} verify
assemble:
{{entrypoint}} package
dev:
{{entrypoint}} {{dev_maven_goal}}
# --- Testing ---
test:
{{entrypoint}} test
check:
{{entrypoint}} verify
# --- Multi-module: export JVM_MODULE=module-id ---
module-build:
{{entrypoint}} -pl {{ env("JVM_MODULE", "change-me-subproject") }} -am package
module-test:
{{entrypoint}} -pl {{ env("JVM_MODULE", "change-me-subproject") }} -am test
module-check:
{{entrypoint}} -pl {{ env("JVM_MODULE", "change-me-subproject") }} -am verify
# --- Code Quality ---
lint:
{{entrypoint}} verify
fmt:
{{entrypoint}} spotless:apply
lint-checkstyle:
{{entrypoint}} checkstyle:check
lint-spotbugs:
{{entrypoint}} spotbugs:check
lint-pmd:
{{entrypoint}} pmd:check
lint-spotless:
{{entrypoint}} spotless:check
# --- Docker ---
docker-build:
docker build \
--build-arg VERSION={{version}} \
--build-arg COMMIT={{commit}} \
-t {{docker-image}}:{{version}} \
-t {{docker-image}}:latest \
.
docker-push:
docker push {{docker-image}}:{{version}}
docker push {{docker-image}}:latest
# --- Database ---
db-migrate-liquibase:
{{entrypoint}} liquibase:update
db-migrate-flyway:
{{entrypoint}} flyway:migrate
# --- CI ---
ci: clean build
templates/justfile-node
# --- Justfile for Node.js Projects ---
# Usage: just [recipe]
# Install: https://just.systems/man/en/
set shell := ["bash", "-euo", "pipefail", "-c"]
set dotenv-load
set export
set positional-arguments
# --- Variables ---
project := `node -p "require('./package.json').name" 2>/dev/null || basename $(pwd)`
version := `git describe --tags --always --dirty 2>/dev/null || echo "dev"`
commit := `git rev-parse --short HEAD 2>/dev/null || echo "unknown"`
# Package manager detection
pm := if path_exists("bun.lockb") == "true" { "bun" } else if path_exists("pnpm-lock.yaml") == "true" { "pnpm" } else if path_exists("yarn.lock") == "true" { "yarn" } else { "npm" }
pmx := if pm == "bun" { "bunx" } else if pm == "pnpm" { "pnpm exec" } else if pm == "yarn" { "yarn" } else { "npx" }
# Docker
docker_registry := env("DOCKER_REGISTRY", "ghcr.io")
docker_image := docker_registry + "/" + project
docker_tag := version
# Default recipe - show help
[doc("Show available recipes")]
default:
@just --list --unsorted
# --- Development ---
[group("development")]
[doc("Install dependencies")]
install:
{{ pm }} install
[group("development")]
[doc("Start development server")]
dev: install
{{ pm }} run dev
[group("development")]
[doc("Build for production")]
build: install
NODE_ENV=production {{ pm }} run build
[group("development")]
[doc("Start production server")]
start:
NODE_ENV=production {{ pm }} run start
[group("development")]
[doc("Run code generation")]
generate:
{{ pm }} run generate
# --- Testing ---
[group("testing")]
[doc("Run tests")]
test *args:
{{ pm }} run test {{ args }}
[group("testing")]
[doc("Run tests in watch mode")]
test-watch:
{{ pm }} run test -- --watch
[group("testing")]
[doc("Run tests with coverage")]
test-cover:
{{ pm }} run test -- --coverage
[group("testing")]
[doc("Run end-to-end tests")]
e2e:
{{ pm }} run test:e2e
# --- Code Quality ---
[group("quality")]
[doc("Run linter")]
lint:
{{ pm }} run lint
[group("quality")]
[doc("Run linter with auto-fix")]
lint-fix:
{{ pm }} run lint -- --fix
[group("quality")]
[doc("Format code with Prettier")]
fmt:
{{ pmx }} prettier --write .
[group("quality")]
[doc("Check code formatting")]
fmt-check:
{{ pmx }} prettier --check .
[group("quality")]
[doc("Run TypeScript type checking")]
typecheck:
{{ pmx }} tsc --noEmit
[group("quality")]
[doc("Run all checks")]
check: lint typecheck test
# --- Docker ---
[group("docker")]
[doc("Build Docker image")]
docker-build:
docker build \
--build-arg VERSION={{ version }} \
--build-arg COMMIT={{ commit }} \
-t {{ docker_image }}:{{ docker_tag }} \
-t {{ docker_image }}:latest \
.
[group("docker")]
[doc("Push Docker image")]
docker-push:
docker push {{ docker_image }}:{{ docker_tag }}
docker push {{ docker_image }}:latest
[group("docker")]
[doc("Run Docker container locally")]
docker-run:
docker run --rm -p 3000:3000 --env-file .env {{ docker_image }}:{{ docker_tag }}
# --- Database ---
[group("database")]
[doc("Run database migrations")]
db-migrate:
{{ pm }} run db:migrate
[group("database")]
[doc("Seed the database")]
db-seed:
{{ pm }} run db:seed
[group("database")]
[doc("Reset database")]
db-reset:
{{ pm }} run db:reset
# --- CI ---
[group("ci")]
[doc("Run full CI pipeline")]
ci: install lint typecheck test build
# --- Cleanup ---
[confirm("Remove all build artifacts and caches?")]
[group("maintenance")]
[doc("Remove build artifacts and caches")]
clean:
rm -rf dist/ build/ .next/ out/ coverage/ .turbo/ node_modules/.cache
[confirm("Remove everything including node_modules?")]
[group("maintenance")]
[doc("Remove everything including node_modules")]
clean-all: clean
rm -rf node_modules/
templates/justfile-php
# --- Justfile for PHP Projects ---
# Usage: just [recipe]
# Install: https://just.systems/man/en/
set shell := ["bash", "-euo", "pipefail", "-c"]
set dotenv-load
set export
set positional-arguments
# --- Variables ---
project := `basename $(pwd)`
php := "php"
composer := "composer"
version := `git describe --tags --always --dirty 2>/dev/null || echo "dev"`
commit := `git rev-parse --short HEAD 2>/dev/null || echo "unknown"`
# Docker
docker_registry := env("DOCKER_REGISTRY", "ghcr.io")
docker_image := docker_registry + "/" + project
docker_tag := version
# Default recipe - show help
[doc("Show available recipes")]
default:
@just --list --unsorted
# --- Development ---
[group("development")]
[doc("Install dependencies")]
install:
{{ composer }} install
[group("development")]
[doc("Update dependencies")]
update:
{{ composer }} update
[group("development")]
[doc("Start development server")]
dev: install
{{ php }} -S localhost:8000 -t public/
[group("development")]
[doc("Start Laravel dev server")]
serve:
{{ php }} artisan serve
[group("development")]
[doc("Open interactive REPL (Laravel)")]
tinker:
{{ php }} artisan tinker
[group("development")]
[doc("List application routes (Laravel)")]
routes:
{{ php }} artisan route:list
# --- Testing ---
[group("testing")]
[doc("Run tests")]
test *args:
./vendor/bin/phpunit {{ args }}
[group("testing")]
[doc("Run tests with coverage report")]
test-cover:
./vendor/bin/phpunit --coverage-html coverage/ --coverage-text
@echo "Coverage report: coverage/index.html"
[group("testing")]
[doc("Run filtered tests")]
test-filter filter:
./vendor/bin/phpunit --filter="{{ filter }}"
[group("testing")]
[doc("Run tests in parallel (requires paratest)")]
test-parallel:
./vendor/bin/paratest
# --- Code Quality ---
[group("quality")]
[doc("Run PHP linter (PHP-CS-Fixer dry-run)")]
lint:
./vendor/bin/php-cs-fixer fix --dry-run --diff
[group("quality")]
[doc("Fix code style issues")]
lint-fix:
./vendor/bin/php-cs-fixer fix
[group("quality")]
[doc("Run static analysis")]
phpstan:
./vendor/bin/phpstan analyse
[group("quality")]
[doc("Format code (alias for lint-fix)")]
fmt: lint-fix
[group("quality")]
[doc("Run all quality checks")]
check: lint phpstan test
# --- Database ---
[group("database")]
[doc("Run database migrations")]
db-migrate:
{{ php }} artisan migrate
[group("database")]
[doc("Rollback last migration")]
db-rollback:
{{ php }} artisan migrate:rollback
[group("database")]
[doc("Seed the database")]
db-seed:
{{ php }} artisan db:seed
[confirm("This will DROP ALL TABLES and re-run migrations. Continue?")]
[group("database")]
[doc("Drop all tables, re-run migrations + seeds")]
db-fresh:
{{ php }} artisan migrate:fresh --seed
# --- Cache & Optimization ---
[group("cache")]
[doc("Clear all caches")]
cache-clear:
{{ php }} artisan cache:clear
{{ php }} artisan config:clear
{{ php }} artisan route:clear
{{ php }} artisan view:clear
[group("cache")]
[doc("Cache config, routes, and views for production")]
optimize:
{{ php }} artisan config:cache
{{ php }} artisan route:cache
{{ php }} artisan view:cache
# --- Docker ---
[group("docker")]
[doc("Build Docker image")]
docker-build:
docker build \
--build-arg VERSION={{ version }} \
--build-arg COMMIT={{ commit }} \
-t {{ docker_image }}:{{ docker_tag }} \
-t {{ docker_image }}:latest \
.
[group("docker")]
[doc("Push Docker image")]
docker-push:
docker push {{ docker_image }}:{{ docker_tag }}
docker push {{ docker_image }}:latest
[group("docker")]
[doc("Run Docker container locally")]
docker-run:
docker run --rm -p 8000:8000 --env-file .env {{ docker_image }}:{{ docker_tag }}
# --- CI ---
[group("ci")]
[doc("Run full CI pipeline")]
ci: install lint phpstan test
# --- Cleanup ---
[confirm("Remove vendor/, coverage/, and framework caches?")]
[group("maintenance")]
[doc("Remove generated files and caches")]
clean:
rm -rf vendor/ coverage/ bootstrap/cache/*.php
rm -rf storage/framework/cache/* storage/framework/sessions/* storage/framework/views/*
templates/justfile-python
# --- Justfile for Python Projects ---
# Usage: just [recipe]
# Install: https://just.systems/man/en/
set shell := ["bash", "-euo", "pipefail", "-c"]
set dotenv-load
set export
set positional-arguments
# --- Variables ---
project := `basename $(pwd)`
python := "python3"
src_dir := "src"
version := `git describe --tags --always --dirty 2>/dev/null || echo "dev"`
commit := `git rev-parse --short HEAD 2>/dev/null || echo "unknown"`
# Package manager detection
pkg := if path_exists("uv.lock") == "true" { "uv" } else if path_exists("poetry.lock") == "true" { "poetry" } else if path_exists("Pipfile.lock") == "true" { "pipenv" } else { "pip" }
pkg_run := if pkg == "uv" { "uv run" } else if pkg == "poetry" { "poetry run" } else if pkg == "pipenv" { "pipenv run" } else { "" }
# Docker
docker_registry := env("DOCKER_REGISTRY", "ghcr.io")
docker_image := docker_registry + "/" + project
docker_tag := version
# Default recipe - show help
[doc("Show available recipes")]
default:
@just --list --unsorted
# --- Development ---
[group("development")]
[doc("Install dependencies")]
install:
#!/usr/bin/env bash
set -euo pipefail
case "{{ pkg }}" in
uv) uv sync ;;
poetry) poetry install ;;
pipenv) pipenv install --dev ;;
*) {{ python }} -m pip install -e ".[dev]" ;;
esac
[group("development")]
[doc("Start development server")]
dev: install
{{ pkg_run }} {{ python }} -m uvicorn main:app --reload --host 0.0.0.0 --port 8000
[group("development")]
[doc("Run the application")]
run:
{{ pkg_run }} {{ python }} -m {{ project }}
[group("development")]
[doc("Open interactive Python shell")]
shell:
{{ pkg_run }} {{ python }}
# --- Testing ---
[group("testing")]
[doc("Run tests")]
test *args:
{{ pkg_run }} pytest {{ args }}
[group("testing")]
[doc("Run tests in watch mode")]
test-watch:
{{ pkg_run }} pytest-watch
[group("testing")]
[doc("Run tests with coverage")]
test-cover:
{{ pkg_run }} pytest --cov={{ src_dir }} --cov-report=html --cov-report=term-missing
@echo "Coverage report: htmlcov/index.html"
[group("testing")]
[doc("Run integration tests")]
test-integration:
{{ pkg_run }} pytest tests/integration/ -v
# --- Code Quality ---
[group("quality")]
[doc("Run linters")]
lint:
{{ pkg_run }} ruff check {{ src_dir }} tests/
[group("quality")]
[doc("Run linters with auto-fix")]
lint-fix:
{{ pkg_run }} ruff check --fix {{ src_dir }} tests/
[group("quality")]
[doc("Format code")]
fmt:
{{ pkg_run }} ruff format {{ src_dir }} tests/
[group("quality")]
[doc("Check code formatting")]
fmt-check:
{{ pkg_run }} ruff format --check {{ src_dir }} tests/
[group("quality")]
[doc("Run type checker")]
typecheck:
{{ pkg_run }} mypy {{ src_dir }}
[group("quality")]
[doc("Run all checks")]
check: lint fmt-check typecheck test
# --- Docker ---
[group("docker")]
[doc("Build Docker image")]
docker-build:
docker build \
--build-arg VERSION={{ version }} \
--build-arg COMMIT={{ commit }} \
-t {{ docker_image }}:{{ docker_tag }} \
-t {{ docker_image }}:latest \
.
[group("docker")]
[doc("Push Docker image")]
docker-push:
docker push {{ docker_image }}:{{ docker_tag }}
docker push {{ docker_image }}:latest
# --- Database ---
[group("database")]
[doc("Run database migrations")]
db-migrate:
{{ pkg_run }} alembic upgrade head
[group("database")]
[doc("Rollback last migration")]
db-rollback:
{{ pkg_run }} alembic downgrade -1
[group("database")]
[doc("Create new migration")]
db-migration msg:
{{ pkg_run }} alembic revision --autogenerate -m "{{ msg }}"
# --- CI ---
[group("ci")]
[doc("Run full CI pipeline")]
ci: install lint fmt-check typecheck test
# --- Cleanup ---
[confirm("Remove all build artifacts and caches?")]
[group("maintenance")]
[doc("Remove build artifacts and caches")]
clean:
rm -rf dist/ build/ *.egg-info .pytest_cache .mypy_cache .ruff_cache htmlcov/ coverage.xml
find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
find . -type f -name "*.pyc" -delete 2>/dev/null || true
templates/justfile-ruby
# --- Justfile for Ruby (Bundler) Projects ---
# Usage: just [recipe]
# Install: https://just.systems/man/en/
set shell := ["bash", "-euo", "pipefail", "-c"]
set dotenv-load
set export
set positional-arguments
project := `basename $(pwd)`
version := `git describe --tags --always --dirty 2>/dev/null || echo "dev"`
commit := `git rev-parse --short HEAD 2>/dev/null || echo "unknown"`
docker_registry := env("DOCKER_REGISTRY", "ghcr.io")
docker_image := docker_registry + "/" + project
docker_tag := version
[doc("Show available recipes")]
default:
@just --list --unsorted
# --- Development ---
[group("development")]
[doc("Install gems")]
install:
bundle install
[group("development")]
[doc("Update gems")]
update:
bundle update
[group("development")]
[doc("Start app — override per project (rails server, etc.)")]
dev: install
bundle exec ruby -S rackup -o 0.0.0.0 -p 9292
[group("development")]
[doc("Rails console")]
console:
bundle exec rails console
# --- Testing ---
[group("testing")]
[doc("Run RSpec")]
test *args:
bundle exec rspec {{ args }}
[group("testing")]
[doc("Run tests via Rake")]
test-rake:
bundle exec rake test
# --- Code Quality ---
[group("quality")]
[doc("Rubocop (no auto-correct)")]
lint:
bundle exec rubocop
[group("quality")]
[doc("Rubocop auto-correct")]
lint-fix:
bundle exec rubocop -A
[group("quality")]
[doc("Alias for RuboCop auto-correct")]
fmt: lint-fix
[group("quality")]
[doc("Static checks + tests")]
check: lint test
# --- Docker ---
[group("docker")]
[doc("Build Docker image")]
docker-build:
docker build \
--build-arg VERSION={{ version }} \
--build-arg COMMIT={{ commit }} \
-t {{ docker_image }}:{{ docker_tag }} \
-t {{ docker_image }}:latest \
.
[group("docker")]
[doc("Push Docker image")]
docker-push:
docker push {{ docker_image }}:{{ docker_tag }}
docker push {{ docker_image }}:latest
# --- CI ---
[group("ci")]
[doc("Run full CI pipeline")]
ci: install lint test
# --- Cleanup ---
[group("maintenance")]
[doc("Remove tmp logs (adjust per app)")]
clean:
rm -rf tmp/ log/*.log
templates/justfile-rust
# --- Justfile for Rust Projects ---
# Usage: just [recipe]
# Install: https://just.systems/man/en/
set shell := ["bash", "-euo", "pipefail", "-c"]
set dotenv-load
set export
set positional-arguments
project := `basename $(pwd)`
version := `git describe --tags --always --dirty 2>/dev/null || echo "dev"`
commit := `git rev-parse --short HEAD 2>/dev/null || echo "unknown"`
docker_registry := env("DOCKER_REGISTRY", "ghcr.io")
docker_image := docker_registry + "/" + project
docker_tag := version
clippyflags := "-D warnings"
[doc("Show available recipes")]
default:
@just --list --unsorted
# --- Development ---
[group("development")]
[doc("Build the workspace (debug)")]
build:
cargo build
[group("development")]
[doc("Build release binaries")]
build-release:
cargo build --release
[group("development")]
[doc("Run the default binary")]
run *args:
cargo run {{ args }}
[group("development")]
[doc("Watch and rebuild (requires cargo-watch)")]
dev:
cargo watch -x check -x test
[group("development")]
[doc("Fast compile check")]
check:
cargo check
# --- Testing ---
[group("testing")]
[doc("Run tests")]
test *args:
cargo test {{ args }}
[group("testing")]
[doc("Run documentation tests")]
test-doc:
cargo test --doc
# --- Code Quality ---
[group("quality")]
[doc("Run clippy")]
lint:
cargo clippy --all-targets --all-features -- {{ clippyflags }}
[group("quality")]
[doc("Format with rustfmt")]
fmt:
cargo fmt
[group("quality")]
[doc("Verify formatting (CI)")]
fmt-check:
cargo fmt -- --check
[group("quality")]
[doc("Build rustdoc locally")]
doc:
cargo doc --no-deps
# --- Docker ---
[group("docker")]
[doc("Build Docker image")]
docker-build:
docker build \
--build-arg VERSION={{ version }} \
--build-arg COMMIT={{ commit }} \
-t {{ docker_image }}:{{ docker_tag }} \
-t {{ docker_image }}:latest \
.
[group("docker")]
[doc("Push Docker image")]
docker-push:
docker push {{ docker_image }}:{{ docker_tag }}
docker push {{ docker_image }}:latest
# --- CI ---
[group("ci")]
[doc("Run full CI pipeline")]
ci: fmt-check lint test build
# --- Cleanup ---
[group("maintenance")]
[doc("Remove build artifacts")]
clean:
cargo clean
templates/magefile-basic.go
//go:build mage
// Build automation for the project.
package main
import (
"fmt"
"os"
"time"
"github.com/magefile/mage/mg"
"github.com/magefile/mage/sh"
)
// --- Variables ---
var (
// Default target when running `mage` without arguments.
Default = Build
// Aliases for common targets.
Aliases = map[string]interface{}{
"b": Build,
"t": Test,
"l": Lint,
"c": Clean,
}
)
func version() string {
v, _ := sh.Output("git", "describe", "--tags", "--always", "--dirty")
if v == "" {
return "dev"
}
return v
}
func commit() string {
c, _ := sh.Output("git", "rev-parse", "--short", "HEAD")
if c == "" {
return "unknown"
}
return c
}
func buildTime() string {
return time.Now().UTC().Format(time.RFC3339)
}
func module() string {
m, _ := sh.Output("head", "-1", "go.mod")
// Extract module name: "module github.com/user/project" -> "github.com/user/project"
if len(m) > 7 {
return m[7:]
}
return ""
}
func env(key, fallback string) string {
if v := os.Getenv(key); v != "" {
return v
}
return fallback
}
// --- Development ---
// Build compiles the project binary.
func Build() error {
mod := module()
ldflags := fmt.Sprintf(
"-s -w -X %s/internal/version.Version=%s -X %s/internal/version.Commit=%s -X %s/internal/version.BuildTime=%s",
mod, version(), mod, commit(), mod, buildTime(),
)
return sh.RunV("go", "build", "-ldflags", ldflags, "-o", "bin/app", "./cmd/app")
}
// Run builds and runs the project.
func Run() error {
mg.Deps(Build)
return sh.RunV("./bin/app")
}
// Dev runs the project with hot reload (requires air).
func Dev() error {
return sh.RunV("air")
}
// Generate runs go generate.
func Generate() error {
return sh.RunV("go", "generate", "./...")
}
// Tidy tidies and verifies go.mod.
func Tidy() error {
if err := sh.RunV("go", "mod", "tidy"); err != nil {
return err
}
return sh.RunV("go", "mod", "verify")
}
// --- Testing ---
// Test runs the test suite.
func Test() error {
return sh.RunV("go", "test", "-race", "-count=1", "./...")
}
// TestCover runs tests with coverage report.
func TestCover() error {
if err := sh.RunV("go", "test", "-race", "-count=1", "-coverprofile=coverage.out", "./..."); err != nil {
return err
}
if err := sh.RunV("go", "tool", "cover", "-html=coverage.out", "-o", "coverage.html"); err != nil {
return err
}
fmt.Println("Coverage report: coverage.html")
return nil
}
// TestIntegration runs integration tests.
func TestIntegration() error {
return sh.RunV("go", "test", "-race", "-count=1", "-tags=integration", "./...")
}
// Bench runs benchmarks.
func Bench() error {
return sh.RunV("go", "test", "-bench=.", "-benchmem", "./...")
}
// --- Code Quality ---
// Lint runs golangci-lint.
func Lint() error {
return sh.RunV("golangci-lint", "run", "./...")
}
// Fmt formats the code.
func Fmt() error {
if err := sh.RunV("go", "fmt", "./..."); err != nil {
return err
}
return sh.RunV("goimports", "-w", ".")
}
// Vet runs go vet.
func Vet() error {
return sh.RunV("go", "vet", "./...")
}
// --- CI ---
// CI runs the full CI pipeline (lint, test, build in parallel).
func CI() {
mg.Deps(Lint, Test, Build)
}
// --- Cleanup ---
// Clean removes build artifacts.
func Clean() error {
for _, path := range []string{"bin", "coverage.out", "coverage.html"} {
if err := sh.Rm(path); err != nil {
return fmt.Errorf("removing %s: %w", path, err)
}
}
return nil
}
templates/magefile-full.go
//go:build mage
// Build automation with namespaces for the project.
package main
import (
"fmt"
"os"
"time"
"github.com/magefile/mage/mg"
"github.com/magefile/mage/sh"
)
// --- Variables ---
var (
// Default target when running `mage` without arguments.
Default = Build
// Aliases for common targets.
Aliases = map[string]interface{}{
"b": Build,
"t": Test,
"l": Lint,
"c": Clean,
"db": Docker.Build,
"dp": Docker.Push,
}
)
func version() string {
v, _ := sh.Output("git", "describe", "--tags", "--always", "--dirty")
if v == "" {
return "dev"
}
return v
}
func commit() string {
c, _ := sh.Output("git", "rev-parse", "--short", "HEAD")
if c == "" {
return "unknown"
}
return c
}
func buildTime() string {
return time.Now().UTC().Format(time.RFC3339)
}
func module() string {
m, _ := sh.Output("head", "-1", "go.mod")
if len(m) > 7 {
return m[7:]
}
return ""
}
func env(key, fallback string) string {
if v := os.Getenv(key); v != "" {
return v
}
return fallback
}
func imageName() string {
return env("DOCKER_REGISTRY", "ghcr.io") + "/" + env("PROJECT", "app")
}
// --- Development ---
// Build compiles the project binary.
func Build() error {
mod := module()
ldflags := fmt.Sprintf(
"-s -w -X %s/internal/version.Version=%s -X %s/internal/version.Commit=%s -X %s/internal/version.BuildTime=%s",
mod, version(), mod, commit(), mod, buildTime(),
)
return sh.RunV("go", "build", "-ldflags", ldflags, "-o", "bin/app", "./cmd/app")
}
// Run builds and runs the project.
func Run() error {
mg.Deps(Build)
return sh.RunV("./bin/app")
}
// Dev runs the project with hot reload (requires air).
func Dev() error {
return sh.RunV("air")
}
// Generate runs go generate.
func Generate() error {
return sh.RunV("go", "generate", "./...")
}
// Tidy tidies and verifies go.mod.
func Tidy() error {
if err := sh.RunV("go", "mod", "tidy"); err != nil {
return err
}
return sh.RunV("go", "mod", "verify")
}
// --- Testing ---
// Test runs the test suite.
func Test() error {
return sh.RunV("go", "test", "-race", "-count=1", "./...")
}
// TestCover runs tests with coverage report.
func TestCover() error {
if err := sh.RunV("go", "test", "-race", "-count=1", "-coverprofile=coverage.out", "./..."); err != nil {
return err
}
if err := sh.RunV("go", "tool", "cover", "-html=coverage.out", "-o", "coverage.html"); err != nil {
return err
}
fmt.Println("Coverage report: coverage.html")
return nil
}
// TestIntegration runs integration tests.
func TestIntegration() error {
return sh.RunV("go", "test", "-race", "-count=1", "-tags=integration", "./...")
}
// Bench runs benchmarks.
func Bench() error {
return sh.RunV("go", "test", "-bench=.", "-benchmem", "./...")
}
// --- Code Quality ---
// Lint runs golangci-lint.
func Lint() error {
return sh.RunV("golangci-lint", "run", "./...")
}
// Fmt formats the code.
func Fmt() error {
if err := sh.RunV("go", "fmt", "./..."); err != nil {
return err
}
return sh.RunV("goimports", "-w", ".")
}
// Vet runs go vet.
func Vet() error {
return sh.RunV("go", "vet", "./...")
}
// --- Docker Namespace ---
// Docker contains Docker-related build targets.
type Docker mg.Namespace
// Build creates the Docker image.
func (Docker) Build() error {
tag := fmt.Sprintf("%s:%s", imageName(), version())
latest := fmt.Sprintf("%s:latest", imageName())
return sh.RunV("docker", "build",
"--build-arg", "VERSION="+version(),
"--build-arg", "COMMIT="+commit(),
"-t", tag,
"-t", latest,
".",
)
}
// Push pushes the Docker image to the registry.
func (Docker) Push() error {
tag := fmt.Sprintf("%s:%s", imageName(), version())
latest := fmt.Sprintf("%s:latest", imageName())
if err := sh.RunV("docker", "push", tag); err != nil {
return err
}
return sh.RunV("docker", "push", latest)
}
// Run runs the Docker container locally.
func (Docker) Run() error {
tag := fmt.Sprintf("%s:%s", imageName(), version())
return sh.RunV("docker", "run", "--rm", "-p", "8080:8080", "--env-file", ".env", tag)
}
// --- DB Namespace ---
// DB contains database-related targets.
type DB mg.Namespace
// Migrate runs database migrations.
func (DB) Migrate() error {
return sh.RunV("go", "run", "./cmd/migrate", "up")
}
// Rollback rolls back the last migration.
func (DB) Rollback() error {
return sh.RunV("go", "run", "./cmd/migrate", "down")
}
// Seed seeds the database with test data.
func (DB) Seed() error {
return sh.RunV("go", "run", "./cmd/seed")
}
// Reset resets the database (rollback all + migrate + seed).
func (DB) Reset() error {
mg.SerialDeps(DB.Rollback, DB.Migrate, DB.Seed)
return nil
}
// --- CI ---
// CI runs the full CI pipeline (lint, test, build in parallel).
func CI() {
mg.Deps(Lint, Test, Build)
}
// Release creates a new release (sequential: test -> build -> docker push).
func Release() error {
mg.SerialDeps(Test, Build)
return Docker.Push(Docker{})
}
// --- Cleanup ---
// Clean removes build artifacts.
func Clean() error {
for _, path := range []string{"bin", "coverage.out", "coverage.html"} {
if err := sh.Rm(path); err != nil {
return fmt.Errorf("removing %s: %w", path, err)
}
}
return nil
}
templates/makefile-go.mk
# --- Makefile for Go Projects ---
# Usage: make [target]
SHELL := bash
.ONESHELL:
.SHELLFLAGS := -eu -o pipefail -c
.DELETE_ON_ERROR:
MAKEFLAGS += --warn-undefined-variables
MAKEFLAGS += --no-builtin-rules
# --- Project ---
PROJECT ?= $(shell basename $(CURDIR))
GO ?= go
GOFLAGS ?=
LDFLAGS ?= -s -w
# --- Git ---
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")
COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo "unknown")
BUILD_TIME := $(shell date -u '+%Y-%m-%dT%H:%M:%SZ')
# --- Build ---
MODULE := $(shell head -1 go.mod | awk '{print $$2}')
BIN_DIR := bin
MAIN_PKG ?= ./cmd/$(PROJECT)
BINARY := $(BIN_DIR)/$(PROJECT)
LDFLAGS += -X $(MODULE)/internal/version.Version=$(VERSION)
LDFLAGS += -X $(MODULE)/internal/version.Commit=$(COMMIT)
LDFLAGS += -X $(MODULE)/internal/version.BuildTime=$(BUILD_TIME)
# --- Docker ---
DOCKER_REGISTRY ?= ghcr.io
DOCKER_IMAGE ?= $(DOCKER_REGISTRY)/$(PROJECT)
DOCKER_TAG ?= $(VERSION)
# --- Tools ---
GOLANGCI_LINT ?= golangci-lint
GOTEST ?= $(GO) test
GOTESTFLAGS ?= -race -count=1
# ============================================================================
.DEFAULT_GOAL := help
##@ Development
.PHONY: build
build: ## Build the binary
$(GO) build $(GOFLAGS) -ldflags '$(LDFLAGS)' -o $(BINARY) $(MAIN_PKG)
.PHONY: run
run: build ## Build and run
$(BINARY)
.PHONY: dev
dev: ## Run with hot reload (requires air)
air
.PHONY: generate
generate: ## Run go generate
$(GO) generate ./...
##@ Testing
.PHONY: test
test: ## Run tests
$(GOTEST) $(GOTESTFLAGS) ./...
.PHONY: test-cover
test-cover: ## Run tests with coverage report
$(GOTEST) $(GOTESTFLAGS) -coverprofile=coverage.out ./...
$(GO) tool cover -html=coverage.out -o coverage.html
@echo "Coverage report: coverage.html"
.PHONY: test-integration
test-integration: ## Run integration tests
$(GOTEST) $(GOTESTFLAGS) -tags=integration ./...
.PHONY: bench
bench: ## Run benchmarks
$(GO) test -bench=. -benchmem ./...
##@ Code Quality
.PHONY: lint
lint: ## Run linters
$(GOLANGCI_LINT) run ./...
.PHONY: fmt
fmt: ## Format code
$(GO) fmt ./...
goimports -w .
.PHONY: vet
vet: ## Run go vet
$(GO) vet ./...
.PHONY: tidy
tidy: ## Tidy and verify go.mod
$(GO) mod tidy
$(GO) mod verify
##@ Docker
.PHONY: docker-build
docker-build: ## Build Docker image
docker build \
--build-arg VERSION=$(VERSION) \
--build-arg COMMIT=$(COMMIT) \
-t $(DOCKER_IMAGE):$(DOCKER_TAG) \
-t $(DOCKER_IMAGE):latest \
.
.PHONY: docker-push
docker-push: ## Push Docker image
docker push $(DOCKER_IMAGE):$(DOCKER_TAG)
docker push $(DOCKER_IMAGE):latest
##@ CI
.PHONY: ci
ci: lint test build ## Run full CI pipeline
##@ Cleanup
.PHONY: clean
clean: ## Remove build artifacts
rm -rf $(BIN_DIR) coverage.out coverage.html
##@ Help
.PHONY: help
help: ## Show this help
@awk 'BEGIN {FS = ":.*##"; printf "Usage:\n make \033[36m<target>\033[0m\n"} \
/^[a-zA-Z_-]+:.*?## / {printf " \033[36m%-20s\033[0m %s\n", $$1, $$2} \
/^##@/ {printf "\n\033[1m%s\033[0m\n", substr($$0, 5)}' $(MAKEFILE_LIST)
templates/makefile-gradle.mk
# --- Makefile for JVM Projects (Gradle) ---
# Usage: make [target]
# Canonical quality/migration targets; remove recipes your build.gradle does not wire (see SKILL Step 5).
SHELL := bash
.ONESHELL:
.SHELLFLAGS := -eu -o pipefail -c
.DELETE_ON_ERROR:
MAKEFLAGS += --warn-undefined-variables
MAKEFLAGS += --no-builtin-rules
# --- Project ---
PROJECT ?= $(shell basename $(CURDIR))
# --- Entrypoint ---
ENTRYPOINT ?= $(shell if [ -f ./gradlew ] || [ -f gradle/wrapper/gradle-wrapper.properties ]; then echo "./gradlew"; else echo "gradle"; fi)
# --- Dev task (§2.3): Quarkus > Micronaut > Vert.x > Spring Boot; override DEV_GRADLE_TASK=… if root files omit deps ---
_JVM_GRADLE_DEV_FILES := build.gradle build.gradle.kts settings.gradle settings.gradle.kts gradle/libs.versions.toml
DEV_GRADLE_TASK ?= $(shell for f in $(_JVM_GRADLE_DEV_FILES); do test -f "$$f" || continue; if grep -qE 'quarkus|io\.quarkus' "$$f" 2>/dev/null; then printf %s quarkusDev; exit 0; fi; done; for f in $(_JVM_GRADLE_DEV_FILES); do test -f "$$f" || continue; if grep -qE 'micronaut|io\.micronaut' "$$f" 2>/dev/null; then printf %s run; exit 0; fi; done; for f in $(_JVM_GRADLE_DEV_FILES); do test -f "$$f" || continue; if grep -qE 'vertx-plugin|io\.vertx\.vertx' "$$f" 2>/dev/null; then printf %s vertxRun; exit 0; fi; done; printf %s bootRun)
# --- Git ---
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")
COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo "unknown")
# --- Docker ---
DOCKER_REGISTRY ?= ghcr.io
DOCKER_IMAGE ?= $(DOCKER_REGISTRY)/$(PROJECT)
DOCKER_TAG ?= $(VERSION)
# --- Multi-module (set JVM_MODULE to subproject id, e.g. export JVM_MODULE=api) ---
JVM_MODULE ?= change-me-subproject
# ============================================================================
.DEFAULT_GOAL := help
##@ Development
.PHONY: clean
clean: ## Remove build artifacts
$(ENTRYPOINT) clean
.PHONY: assemble
assemble: ## Build project and package artifacts (JAR/WAR)
$(ENTRYPOINT) assemble
.PHONY: build
build: ## Full build including tests and checks
$(ENTRYPOINT) build
.PHONY: dev
dev: ## Run application locally (framework from §2.3 scan)
$(ENTRYPOINT) $(DEV_GRADLE_TASK)
##@ Testing
.PHONY: test
test: ## Run unit tests
$(ENTRYPOINT) test
.PHONY: check
check: ## Run tests and static analysis (Gradle lifecycle)
$(ENTRYPOINT) check
##@ Multi-module
.PHONY: module-build
module-build: ## Build one subproject (Gradle :JVM_MODULE:build)
$(ENTRYPOINT) :$(JVM_MODULE):build
.PHONY: module-test
module-test: ## Tests for one subproject
$(ENTRYPOINT) :$(JVM_MODULE):test
.PHONY: module-check
module-check: ## Check one subproject (tests + static analysis)
$(ENTRYPOINT) :$(JVM_MODULE):check
##@ Code Quality
.PHONY: lint
lint: check ## Full verification (delegates to Gradle `check`)
.PHONY: fmt
fmt: ## Apply Spotless (`spotlessApply`)
$(ENTRYPOINT) spotlessApply
.PHONY: lint-checkstyle
lint-checkstyle: ## Checkstyle (`checkstyleMain`)
$(ENTRYPOINT) checkstyleMain
.PHONY: lint-spotbugs
lint-spotbugs: ## SpotBugs (`spotbugsMain`)
$(ENTRYPOINT) spotbugsMain
.PHONY: lint-pmd
lint-pmd: ## PMD (`pmdMain`)
$(ENTRYPOINT) pmdMain
.PHONY: lint-spotless
lint-spotless: ## Spotless check only, no write (`spotlessCheck`)
$(ENTRYPOINT) spotlessCheck
##@ Docker
.PHONY: docker-build
docker-build: ## Build Docker image
docker build \
--build-arg VERSION=$(VERSION) \
--build-arg COMMIT=$(COMMIT) \
-t $(DOCKER_IMAGE):$(DOCKER_TAG) \
-t $(DOCKER_IMAGE):latest \
.
.PHONY: docker-push
docker-push: ## Push Docker image
docker push $(DOCKER_IMAGE):$(DOCKER_TAG)
docker push $(DOCKER_IMAGE):latest
##@ Database
.PHONY: db-migrate-liquibase
db-migrate-liquibase: ## Liquibase update (`liquibaseUpdate`)
$(ENTRYPOINT) liquibaseUpdate
.PHONY: db-migrate-flyway
db-migrate-flyway: ## Flyway migrate (`flywayMigrate`; adjust if plugin uses another task name)
$(ENTRYPOINT) flywayMigrate
##@ CI
.PHONY: ci
ci: clean build ## Clean then full Gradle build
##@ Help
.PHONY: help
help: ## Show this help
@awk 'BEGIN {FS = ":.*##"; printf "Usage:\n make \033[36m<target>\033[0m\n"} \
/^[a-zA-Z_-]+:.*?## / {printf " \033[36m%-20s\033[0m %s\n", $$1, $$2} \
/^##@/ {printf "\n\033[1m%s\033[0m\n", substr($$0, 5)}' $(MAKEFILE_LIST)
templates/makefile-maven.mk
# --- Makefile for JVM Projects (Maven) ---
# Usage: make [target]
# Canonical quality/migration targets; remove recipes your pom/plugins do not define (see SKILL Step 5).
SHELL := bash
.ONESHELL:
.SHELLFLAGS := -eu -o pipefail -c
.DELETE_ON_ERROR:
MAKEFLAGS += --warn-undefined-variables
MAKEFLAGS += --no-builtin-rules
# --- Project ---
PROJECT ?= $(shell basename $(CURDIR))
# --- Entrypoint ---
ENTRYPOINT ?= $(shell if [ -f ./mvnw ] || [ -f .mvn/wrapper/maven-wrapper.properties ]; then echo "./mvnw"; else echo "mvn"; fi)
# --- Dev goal (§2.3): Quarkus > Micronaut > Vert.x > Spring Boot; override DEV_MAVEN_GOAL=… if parent POM omits deps ---
DEV_MAVEN_GOAL ?= $(shell if ! test -f pom.xml; then printf %s spring-boot:run; exit 0; fi; if grep -qE 'quarkus|io\.quarkus' pom.xml 2>/dev/null; then printf %s quarkus:dev; exit 0; fi; if grep -qE 'micronaut|io\.micronaut' pom.xml 2>/dev/null; then printf %s mn:run; exit 0; fi; if grep -qE 'vertx-maven-plugin|io\.reactiverse' pom.xml 2>/dev/null; then printf %s vertx:run; exit 0; fi; printf %s spring-boot:run)
# --- Git ---
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")
COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo "unknown")
# --- Docker ---
DOCKER_REGISTRY ?= ghcr.io
DOCKER_IMAGE ?= $(DOCKER_REGISTRY)/$(PROJECT)
DOCKER_TAG ?= $(VERSION)
# --- Multi-module (set JVM_MODULE to Maven module id / artifact dir, e.g. export JVM_MODULE=api) ---
JVM_MODULE ?= change-me-subproject
# ============================================================================
.DEFAULT_GOAL := help
##@ Development
.PHONY: clean
clean: ## Remove build artifacts
$(ENTRYPOINT) clean
.PHONY: assemble
assemble: ## Build project and package artifacts (JAR/WAR)
$(ENTRYPOINT) package
.PHONY: build
build: ## Full build including tests and checks (`verify`)
$(ENTRYPOINT) verify
.PHONY: dev
dev: ## Run application locally (framework from §2.3 scan)
$(ENTRYPOINT) $(DEV_MAVEN_GOAL)
##@ Testing
.PHONY: test
test: ## Run unit tests
$(ENTRYPOINT) test
.PHONY: check
check: ## Run tests and static analysis (`verify`)
$(ENTRYPOINT) verify
##@ Multi-module
.PHONY: module-build
module-build: ## Package one reactor subtree (-pl JVM_MODULE -am package)
$(ENTRYPOINT) -pl $(JVM_MODULE) -am package
.PHONY: module-test
module-test: ## Tests for one reactor subtree
$(ENTRYPOINT) -pl $(JVM_MODULE) -am test
.PHONY: module-check
module-check: ## Verify one reactor subtree
$(ENTRYPOINT) -pl $(JVM_MODULE) -am verify
##@ Code Quality
.PHONY: lint
lint: check ## Full verification (delegates to Maven `verify`)
.PHONY: fmt
fmt: ## Apply Spotless (`spotless:apply`)
$(ENTRYPOINT) spotless:apply
.PHONY: lint-checkstyle
lint-checkstyle: ## Checkstyle (`checkstyle:check`)
$(ENTRYPOINT) checkstyle:check
.PHONY: lint-spotbugs
lint-spotbugs: ## SpotBugs (`spotbugs:check`)
$(ENTRYPOINT) spotbugs:check
.PHONY: lint-pmd
lint-pmd: ## PMD (`pmd:check`)
$(ENTRYPOINT) pmd:check
.PHONY: lint-spotless
lint-spotless: ## Spotless check only, no write (`spotless:check`)
$(ENTRYPOINT) spotless:check
##@ Docker
.PHONY: docker-build
docker-build: ## Build Docker image
docker build \
--build-arg VERSION=$(VERSION) \
--build-arg COMMIT=$(COMMIT) \
-t $(DOCKER_IMAGE):$(DOCKER_TAG) \
-t $(DOCKER_IMAGE):latest \
.
.PHONY: docker-push
docker-push: ## Push Docker image
docker push $(DOCKER_IMAGE):$(DOCKER_TAG)
docker push $(DOCKER_IMAGE):latest
##@ Database
.PHONY: db-migrate-liquibase
db-migrate-liquibase: ## Liquibase (`liquibase:update`)
$(ENTRYPOINT) liquibase:update
.PHONY: db-migrate-flyway
db-migrate-flyway: ## Flyway (`flyway:migrate`)
$(ENTRYPOINT) flyway:migrate
##@ CI
.PHONY: ci
ci: clean build ## Clean then full Maven verify
##@ Help
.PHONY: help
help: ## Show this help
@awk 'BEGIN {FS = ":.*##"; printf "Usage:\n make \033[36m<target>\033[0m\n"} \
/^[a-zA-Z_-]+:.*?## / {printf " \033[36m%-20s\033[0m %s\n", $$1, $$2} \
/^##@/ {printf "\n\033[1m%s\033[0m\n", substr($$0, 5)}' $(MAKEFILE_LIST)
templates/makefile-node.mk
# --- Makefile for Node.js Projects ---
# Usage: make [target]
SHELL := bash
.ONESHELL:
.SHELLFLAGS := -eu -o pipefail -c
.DELETE_ON_ERROR:
MAKEFLAGS += --warn-undefined-variables
MAKEFLAGS += --no-builtin-rules
# --- Project ---
PROJECT ?= $(shell node -p "require('./package.json').name" 2>/dev/null || basename $(CURDIR))
NODE_ENV ?= development
# --- Git ---
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")
COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo "unknown")
BUILD_TIME := $(shell date -u '+%Y-%m-%dT%H:%M:%SZ')
# --- Package Manager Detection ---
# Override: make PM=yarn [target]
PM ?= $(shell \
if [ -f bun.lockb ]; then echo "bun"; \
elif [ -f pnpm-lock.yaml ]; then echo "pnpm"; \
elif [ -f yarn.lock ]; then echo "yarn"; \
else echo "npm"; fi)
PMX := $(shell \
if [ "$(PM)" = "bun" ]; then echo "bunx"; \
elif [ "$(PM)" = "pnpm" ]; then echo "pnpm exec"; \
elif [ "$(PM)" = "yarn" ]; then echo "yarn"; \
else echo "npx"; fi)
# --- Docker ---
DOCKER_REGISTRY ?= ghcr.io
DOCKER_IMAGE ?= $(DOCKER_REGISTRY)/$(PROJECT)
DOCKER_TAG ?= $(VERSION)
# ============================================================================
.DEFAULT_GOAL := help
##@ Development
.PHONY: install
install: ## Install dependencies
$(PM) install
.PHONY: dev
dev: ## Start development server
$(PM) run dev
.PHONY: build
build: ## Build for production
NODE_ENV=production $(PM) run build
.PHONY: start
start: ## Start production server
NODE_ENV=production $(PM) run start
.PHONY: generate
generate: ## Run code generation (if applicable)
$(PM) run generate
##@ Testing
.PHONY: test
test: ## Run tests
$(PM) run test
.PHONY: test-watch
test-watch: ## Run tests in watch mode
$(PM) run test -- --watch
.PHONY: test-cover
test-cover: ## Run tests with coverage
$(PM) run test -- --coverage
.PHONY: e2e
e2e: ## Run end-to-end tests
$(PM) run test:e2e
##@ Code Quality
.PHONY: lint
lint: ## Run linter
$(PM) run lint
.PHONY: lint-fix
lint-fix: ## Run linter with auto-fix
$(PM) run lint -- --fix
.PHONY: fmt
fmt: ## Format code with Prettier
$(PMX) prettier --write .
.PHONY: fmt-check
fmt-check: ## Check code formatting
$(PMX) prettier --check .
.PHONY: typecheck
typecheck: ## Run TypeScript type checking
$(PMX) tsc --noEmit
.PHONY: check
check: lint typecheck test ## Run all checks
##@ Docker
.PHONY: docker-build
docker-build: ## Build Docker image
docker build \
--build-arg VERSION=$(VERSION) \
--build-arg COMMIT=$(COMMIT) \
-t $(DOCKER_IMAGE):$(DOCKER_TAG) \
-t $(DOCKER_IMAGE):latest \
.
.PHONY: docker-push
docker-push: ## Push Docker image
docker push $(DOCKER_IMAGE):$(DOCKER_TAG)
docker push $(DOCKER_IMAGE):latest
.PHONY: docker-run
docker-run: ## Run Docker container locally
docker run --rm -p 3000:3000 --env-file .env $(DOCKER_IMAGE):$(DOCKER_TAG)
##@ Database
.PHONY: db-migrate
db-migrate: ## Run database migrations
$(PM) run db:migrate
.PHONY: db-seed
db-seed: ## Seed the database
$(PM) run db:seed
.PHONY: db-reset
db-reset: ## Reset database (migrate + seed)
$(PM) run db:reset
##@ CI
.PHONY: ci
ci: install lint typecheck test build ## Run full CI pipeline
##@ Cleanup
.PHONY: clean
clean: ## Remove build artifacts and caches
rm -rf dist/ build/ .next/ out/ coverage/ .turbo/ node_modules/.cache
.PHONY: clean-all
clean-all: clean ## Remove everything including node_modules
rm -rf node_modules/
##@ Help
.PHONY: help
help: ## Show this help
@awk 'BEGIN {FS = ":.*##"; printf "Usage:\n make \033[36m<target>\033[0m\n"} \
/^[a-zA-Z_-]+:.*?## / {printf " \033[36m%-20s\033[0m %s\n", $$1, $$2} \
/^##@/ {printf "\n\033[1m%s\033[0m\n", substr($$0, 5)}' $(MAKEFILE_LIST)
templates/makefile-php.mk
# --- Makefile for PHP Projects ---
# Usage: make [target]
SHELL := bash
.ONESHELL:
.SHELLFLAGS := -eu -o pipefail -c
.DELETE_ON_ERROR:
MAKEFLAGS += --warn-undefined-variables
MAKEFLAGS += --no-builtin-rules
# --- Project ---
PROJECT ?= $(shell basename $(CURDIR))
PHP ?= php
COMPOSER ?= composer
# --- Git ---
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")
COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo "unknown")
BUILD_TIME := $(shell date -u '+%Y-%m-%dT%H:%M:%SZ')
# --- Docker ---
DOCKER_REGISTRY ?= ghcr.io
DOCKER_IMAGE ?= $(DOCKER_REGISTRY)/$(PROJECT)
DOCKER_TAG ?= $(VERSION)
# --- Laravel (uncomment if using Laravel) ---
# ARTISAN := $(PHP) artisan
# ============================================================================
.DEFAULT_GOAL := help
##@ Development
.PHONY: install
install: ## Install dependencies
$(COMPOSER) install
.PHONY: update
update: ## Update dependencies
$(COMPOSER) update
.PHONY: dev
dev: ## Start development server
$(PHP) -S localhost:8000 -t public/
.PHONY: serve
serve: ## Start Laravel dev server (requires Laravel)
$(PHP) artisan serve
.PHONY: tinker
tinker: ## Open interactive REPL (requires Laravel)
$(PHP) artisan tinker
.PHONY: routes
routes: ## List application routes (requires Laravel)
$(PHP) artisan route:list
##@ Testing
.PHONY: test
test: ## Run tests
./vendor/bin/phpunit
.PHONY: test-cover
test-cover: ## Run tests with coverage report
./vendor/bin/phpunit --coverage-html coverage/ --coverage-text
@echo "Coverage report: coverage/index.html"
.PHONY: test-filter
test-filter: ## Run filtered tests (usage: make test-filter FILTER="ClassName::testMethod")
./vendor/bin/phpunit --filter="$(FILTER)"
.PHONY: test-parallel
test-parallel: ## Run tests in parallel (requires paratest)
./vendor/bin/paratest
##@ Code Quality
.PHONY: lint
lint: ## Run PHP linter (PHP-CS-Fixer dry-run)
./vendor/bin/php-cs-fixer fix --dry-run --diff
.PHONY: lint-fix
lint-fix: ## Fix code style issues
./vendor/bin/php-cs-fixer fix
.PHONY: phpstan
phpstan: ## Run static analysis
./vendor/bin/phpstan analyse
.PHONY: fmt
fmt: lint-fix ## Alias for lint-fix
.PHONY: check
check: lint phpstan test ## Run all quality checks
##@ Database
.PHONY: db-migrate
db-migrate: ## Run database migrations
$(PHP) artisan migrate
.PHONY: db-rollback
db-rollback: ## Rollback last migration
$(PHP) artisan migrate:rollback
.PHONY: db-seed
db-seed: ## Seed the database
$(PHP) artisan db:seed
.PHONY: db-fresh
db-fresh: ## Drop all tables and re-run all migrations + seeds
$(PHP) artisan migrate:fresh --seed
##@ Cache & Optimization
.PHONY: cache-clear
cache-clear: ## Clear all caches
$(PHP) artisan cache:clear
$(PHP) artisan config:clear
$(PHP) artisan route:clear
$(PHP) artisan view:clear
.PHONY: optimize
optimize: ## Cache config, routes, and views for production
$(PHP) artisan config:cache
$(PHP) artisan route:cache
$(PHP) artisan view:cache
##@ Docker
.PHONY: docker-build
docker-build: ## Build Docker image
docker build \
--build-arg VERSION=$(VERSION) \
--build-arg COMMIT=$(COMMIT) \
-t $(DOCKER_IMAGE):$(DOCKER_TAG) \
-t $(DOCKER_IMAGE):latest \
.
.PHONY: docker-push
docker-push: ## Push Docker image
docker push $(DOCKER_IMAGE):$(DOCKER_TAG)
docker push $(DOCKER_IMAGE):latest
.PHONY: docker-run
docker-run: ## Run Docker container locally
docker run --rm -p 8000:8000 --env-file .env $(DOCKER_IMAGE):$(DOCKER_TAG)
##@ CI
.PHONY: ci
ci: install lint phpstan test ## Run full CI pipeline
##@ Cleanup
.PHONY: clean
clean: ## Remove generated files and caches
rm -rf vendor/ coverage/ bootstrap/cache/*.php storage/framework/cache/*
rm -rf storage/framework/sessions/* storage/framework/views/*
##@ Help
.PHONY: help
help: ## Show this help
@awk 'BEGIN {FS = ":.*##"; printf "Usage:\n make \033[36m<target>\033[0m\n"} \
/^[a-zA-Z_-]+:.*?## / {printf " \033[36m%-20s\033[0m %s\n", $$1, $$2} \
/^##@/ {printf "\n\033[1m%s\033[0m\n", substr($$0, 5)}' $(MAKEFILE_LIST)
templates/makefile-python.mk
# --- Makefile for Python Projects ---
# Usage: make [target]
SHELL := bash
.ONESHELL:
.SHELLFLAGS := -eu -o pipefail -c
.DELETE_ON_ERROR:
MAKEFLAGS += --warn-undefined-variables
MAKEFLAGS += --no-builtin-rules
# --- Project ---
PROJECT ?= $(shell basename $(CURDIR))
PYTHON ?= python3
SRC_DIR ?= src
# --- Git ---
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")
COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo "unknown")
BUILD_TIME := $(shell date -u '+%Y-%m-%dT%H:%M:%SZ')
# --- Package Manager Detection ---
# Override: make PKG=pip [target]
PKG ?= $(shell \
if [ -f uv.lock ]; then echo "uv"; \
elif [ -f poetry.lock ]; then echo "poetry"; \
elif [ -f Pipfile.lock ]; then echo "pipenv"; \
else echo "pip"; fi)
PKG_RUN := $(shell \
if [ "$(PKG)" = "uv" ]; then echo "uv run"; \
elif [ "$(PKG)" = "poetry" ]; then echo "poetry run"; \
elif [ "$(PKG)" = "pipenv" ]; then echo "pipenv run"; \
else echo ""; fi)
# --- Docker ---
DOCKER_REGISTRY ?= ghcr.io
DOCKER_IMAGE ?= $(DOCKER_REGISTRY)/$(PROJECT)
DOCKER_TAG ?= $(VERSION)
# ============================================================================
.DEFAULT_GOAL := help
##@ Development
.PHONY: install
install: ## Install dependencies
ifeq ($(PKG),uv)
uv sync
else ifeq ($(PKG),poetry)
poetry install
else ifeq ($(PKG),pipenv)
pipenv install --dev
else
$(PYTHON) -m pip install -e ".[dev]"
endif
.PHONY: dev
dev: ## Start development server
$(PKG_RUN) $(PYTHON) -m uvicorn main:app --reload --host 0.0.0.0 --port 8000
.PHONY: run
run: ## Run the application
$(PKG_RUN) $(PYTHON) -m $(PROJECT)
.PHONY: shell
shell: ## Open interactive Python shell
$(PKG_RUN) $(PYTHON)
##@ Testing
.PHONY: test
test: ## Run tests
$(PKG_RUN) pytest
.PHONY: test-watch
test-watch: ## Run tests in watch mode
$(PKG_RUN) pytest-watch
.PHONY: test-cover
test-cover: ## Run tests with coverage
$(PKG_RUN) pytest --cov=$(SRC_DIR) --cov-report=html --cov-report=term-missing
@echo "Coverage report: htmlcov/index.html"
.PHONY: test-integration
test-integration: ## Run integration tests
$(PKG_RUN) pytest tests/integration/ -v
##@ Code Quality
.PHONY: lint
lint: ## Run linters
$(PKG_RUN) ruff check $(SRC_DIR) tests/
.PHONY: lint-fix
lint-fix: ## Run linters with auto-fix
$(PKG_RUN) ruff check --fix $(SRC_DIR) tests/
.PHONY: fmt
fmt: ## Format code
$(PKG_RUN) ruff format $(SRC_DIR) tests/
.PHONY: fmt-check
fmt-check: ## Check code formatting
$(PKG_RUN) ruff format --check $(SRC_DIR) tests/
.PHONY: typecheck
typecheck: ## Run type checker
$(PKG_RUN) mypy $(SRC_DIR)
.PHONY: check
check: lint fmt-check typecheck test ## Run all checks
##@ Docker
.PHONY: docker-build
docker-build: ## Build Docker image
docker build \
--build-arg VERSION=$(VERSION) \
--build-arg COMMIT=$(COMMIT) \
-t $(DOCKER_IMAGE):$(DOCKER_TAG) \
-t $(DOCKER_IMAGE):latest \
.
.PHONY: docker-push
docker-push: ## Push Docker image
docker push $(DOCKER_IMAGE):$(DOCKER_TAG)
docker push $(DOCKER_IMAGE):latest
##@ Database
.PHONY: db-migrate
db-migrate: ## Run database migrations
$(PKG_RUN) alembic upgrade head
.PHONY: db-rollback
db-rollback: ## Rollback last migration
$(PKG_RUN) alembic downgrade -1
.PHONY: db-migration
db-migration: ## Create new migration (usage: make db-migration MSG="add users table")
$(PKG_RUN) alembic revision --autogenerate -m "$(MSG)"
##@ CI
.PHONY: ci
ci: install lint fmt-check typecheck test ## Run full CI pipeline
##@ Cleanup
.PHONY: clean
clean: ## Remove build artifacts and caches
rm -rf dist/ build/ *.egg-info .pytest_cache .mypy_cache .ruff_cache htmlcov/ coverage.xml
find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
find . -type f -name "*.pyc" -delete 2>/dev/null || true
##@ Help
.PHONY: help
help: ## Show this help
@awk 'BEGIN {FS = ":.*##"; printf "Usage:\n make \033[36m<target>\033[0m\n"} \
/^[a-zA-Z_-]+:.*?## / {printf " \033[36m%-20s\033[0m %s\n", $$1, $$2} \
/^##@/ {printf "\n\033[1m%s\033[0m\n", substr($$0, 5)}' $(MAKEFILE_LIST)
templates/makefile-ruby.mk
# --- Makefile for Ruby (Bundler) Projects ---
# Usage: make [target]
SHELL := bash
.ONESHELL:
.SHELLFLAGS := -eu -o pipefail -c
.DELETE_ON_ERROR:
MAKEFLAGS += --warn-undefined-variables
MAKEFLAGS += --no-builtin-rules
# --- Project ---
PROJECT ?= $(shell basename $(CURDIR))
BUNDLE ?= bundle
# --- Git ---
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")
COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo "unknown")
# --- Docker ---
DOCKER_REGISTRY ?= ghcr.io
DOCKER_IMAGE ?= $(DOCKER_REGISTRY)/$(PROJECT)
DOCKER_TAG ?= $(VERSION)
# --- Rails (uncomment / adapt if using Rails) ---
# RAILS := $(BUNDLE) exec rails
# ============================================================================
.DEFAULT_GOAL := help
##@ Development
.PHONY: install
install: ## Install gems (bundle install)
$(BUNDLE) install
.PHONY: update
update: ## Update gems
$(BUNDLE) update
.PHONY: dev
dev: ## Start app (override per project: rails server, rackup, etc.)
$(BUNDLE) exec ruby -S rackup -o 0.0.0.0 -p 9292
.PHONY: console
console: ## Rails console (requires rails)
$(BUNDLE) exec rails console
##@ Testing
.PHONY: test
test: ## Run tests (RSpec — use rake test if project uses Minitest)
$(BUNDLE) exec rspec
.PHONY: test-rake
test-rake: ## Run via Rake
$(BUNDLE) exec rake test
##@ Code Quality
.PHONY: lint
lint: ## Rubocop (no auto-correct)
$(BUNDLE) exec rubocop
.PHONY: lint-fix
lint-fix: ## Rubocop auto-correct
$(BUNDLE) exec rubocop -A
.PHONY: fmt
fmt: lint-fix ## Alias for RuboCop auto-correct
.PHONY: check
check: lint test ## Static checks + tests
##@ Docker
.PHONY: docker-build
docker-build: ## Build Docker image
docker build \
--build-arg VERSION=$(VERSION) \
--build-arg COMMIT=$(COMMIT) \
-t $(DOCKER_IMAGE):$(DOCKER_TAG) \
-t $(DOCKER_IMAGE):latest \
.
.PHONY: docker-push
docker-push: ## Push Docker image
docker push $(DOCKER_IMAGE):$(DOCKER_TAG)
docker push $(DOCKER_IMAGE):latest
##@ CI
.PHONY: ci
ci: install lint test ## Run full CI pipeline
##@ Cleanup
.PHONY: clean
clean: ## Remove bundled artifacts / tmp (adjust per app server)
rm -rf tmp/ log/*.log
##@ Help
.PHONY: help
help: ## Show this help
@awk 'BEGIN {FS = ":.*##"; printf "Usage:\n make \033[36m<target>\033[0m\n"} \
/^[a-zA-Z_-]+:.*?## / {printf " \033[36m%-20s\033[0m %s\n", $$1, $$2} \
/^##@/ {printf "\n\033[1m%s\033[0m\n", substr($$0, 5)}' $(MAKEFILE_LIST)
templates/makefile-rust.mk
# --- Makefile for Rust Projects ---
# Usage: make [target]
SHELL := bash
.ONESHELL:
.SHELLFLAGS := -eu -o pipefail -c
.DELETE_ON_ERROR:
MAKEFLAGS += --warn-undefined-variables
MAKEFLAGS += --no-builtin-rules
# --- Project ---
PROJECT ?= $(shell basename $(CURDIR))
CARGO ?= cargo
CARGOFLAGS ?=
# --- Git ---
VERSION ?= $(shell git describe --tags --always --dirty 2>/dev/null || echo "dev")
COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo "unknown")
# --- Docker ---
DOCKER_REGISTRY ?= ghcr.io
DOCKER_IMAGE ?= $(DOCKER_REGISTRY)/$(PROJECT)
DOCKER_TAG ?= $(VERSION)
# --- Tools ---
CLIPPYFLAGS ?= -D warnings
# ============================================================================
.DEFAULT_GOAL := help
##@ Development
.PHONY: build
build: ## Build the workspace (debug)
$(CARGO) build $(CARGOFLAGS)
.PHONY: build-release
build-release: ## Build release binaries
$(CARGO) build --release $(CARGOFLAGS)
.PHONY: run
run: ## Run the default binary (cargo run)
$(CARGO) run $(CARGOFLAGS)
.PHONY: dev
dev: ## Watch and rebuild (requires cargo-watch)
$(CARGO) watch -x check -x test
.PHONY: check
check: ## Fast compile check without producing binaries
$(CARGO) check $(CARGOFLAGS)
##@ Testing
.PHONY: test
test: ## Run tests
$(CARGO) test $(CARGOFLAGS)
.PHONY: test-doc
test-doc: ## Run documentation tests
$(CARGO) test --doc $(CARGOFLAGS)
##@ Code Quality
.PHONY: lint
lint: ## Run clippy
$(CARGO) clippy --all-targets --all-features -- $(CLIPPYFLAGS)
.PHONY: fmt
fmt: ## Format with rustfmt
$(CARGO) fmt
.PHONY: fmt-check
fmt-check: ## Verify formatting (CI)
$(CARGO) fmt -- --check
.PHONY: doc
doc: ## Build rustdoc locally
$(CARGO) doc --no-deps $(CARGOFLAGS)
##@ Docker
.PHONY: docker-build
docker-build: ## Build Docker image
docker build \
--build-arg VERSION=$(VERSION) \
--build-arg COMMIT=$(COMMIT) \
-t $(DOCKER_IMAGE):$(DOCKER_TAG) \
-t $(DOCKER_IMAGE):latest \
.
.PHONY: docker-push
docker-push: ## Push Docker image
docker push $(DOCKER_IMAGE):$(DOCKER_TAG)
docker push $(DOCKER_IMAGE):latest
##@ CI
.PHONY: ci
ci: fmt-check lint test build ## Run full CI pipeline
##@ Cleanup
.PHONY: clean
clean: ## Remove build artifacts
$(CARGO) clean
##@ Help
.PHONY: help
help: ## Show this help
@awk 'BEGIN {FS = ":.*##"; printf "Usage:\n make \033[36m<target>\033[0m\n"} \
/^[a-zA-Z_-]+:.*?## / {printf " \033[36m%-20s\033[0m %s\n", $$1, $$2} \
/^##@/ {printf "\n\033[1m%s\033[0m\n", substr($$0, 5)}' $(MAKEFILE_LIST)
templates/taskfile-go.yml
# --- Taskfile for Go Projects ---
# Usage: task [target]
# Install: https://taskfile.dev/installation/
version: '3'
output: prefixed
dotenv: ['.env', '.env.local']
vars:
PROJECT: '{{.ROOT_DIR | base}}'
MODULE:
sh: head -1 go.mod | awk '{print $2}'
VERSION:
sh: git describe --tags --always --dirty 2>/dev/null || echo "dev"
COMMIT:
sh: git rev-parse --short HEAD 2>/dev/null || echo "unknown"
BUILD_TIME:
sh: date -u '+%Y-%m-%dT%H:%M:%SZ'
BIN_DIR: bin
MAIN_PKG: './cmd/{{.PROJECT}}'
LDFLAGS: >-
-s -w
-X {{.MODULE}}/internal/version.Version={{.VERSION}}
-X {{.MODULE}}/internal/version.Commit={{.COMMIT}}
-X {{.MODULE}}/internal/version.BuildTime={{.BUILD_TIME}}
# Docker
DOCKER_REGISTRY: ghcr.io
DOCKER_IMAGE: '{{.DOCKER_REGISTRY}}/{{.PROJECT}}'
DOCKER_TAG: '{{.VERSION}}'
tasks:
# --- Development ---
build:
desc: Build the binary
sources:
- ./**/*.go
- go.mod
- go.sum
generates:
- ./{{.BIN_DIR}}/{{.PROJECT}}
cmds:
- go build -ldflags '{{.LDFLAGS}}' -o {{.BIN_DIR}}/{{.PROJECT}} {{.MAIN_PKG}}
run:
desc: Build and run
deps: [build]
cmds:
- ./{{.BIN_DIR}}/{{.PROJECT}}
dev:
desc: Run with hot reload (requires air)
cmds:
- air
generate:
desc: Run go generate
cmds:
- go generate ./...
tidy:
desc: Tidy and verify go.mod
cmds:
- go mod tidy
- go mod verify
# --- Testing ---
test:
desc: Run tests
cmds:
- go test -race -count=1 ./...
test:cover:
desc: Run tests with coverage report
cmds:
- go test -race -count=1 -coverprofile=coverage.out ./...
- go tool cover -html=coverage.out -o coverage.html
- echo "Coverage report{{":"}} coverage.html"
test:integration:
desc: Run integration tests
cmds:
- go test -race -count=1 -tags=integration ./...
bench:
desc: Run benchmarks
cmds:
- go test -bench=. -benchmem ./...
# --- Code Quality ---
lint:
desc: Run linters
cmds:
- golangci-lint run ./...
fmt:
desc: Format code
cmds:
- go fmt ./...
- goimports -w .
vet:
desc: Run go vet
cmds:
- go vet ./...
# --- Docker ---
docker:build:
desc: Build Docker image
cmds:
- >-
docker build
--build-arg VERSION={{.VERSION}}
--build-arg COMMIT={{.COMMIT}}
-t {{.DOCKER_IMAGE}}:{{.DOCKER_TAG}}
-t {{.DOCKER_IMAGE}}:latest
.
docker:push:
desc: Push Docker image
cmds:
- docker push {{.DOCKER_IMAGE}}:{{.DOCKER_TAG}}
- docker push {{.DOCKER_IMAGE}}:latest
# --- CI ---
ci:
desc: Run full CI pipeline
deps: [lint, test, build]
# --- Cleanup ---
clean:
desc: Remove build artifacts
cmds:
- rm -rf {{.BIN_DIR}} coverage.out coverage.html
templates/taskfile-gradle.yml
version: '3'
# --- Gradle Taskfile — canonical targets; remove tasks your build does not wire (SKILL Step 5). ---
vars:
PROJECT:
sh: basename $(pwd)
ENTRYPOINT:
sh: if [ -f ./gradlew ] || [ -f gradle/wrapper/gradle-wrapper.properties ]; then echo "./gradlew"; else echo "gradle"; fi
DEV_GRADLE_TASK:
sh: for f in build.gradle build.gradle.kts settings.gradle settings.gradle.kts gradle/libs.versions.toml; do test -f "$f" || continue; if grep -qE 'quarkus|io\.quarkus' "$f" 2>/dev/null; then printf %s quarkusDev; exit 0; fi; done; for f in build.gradle build.gradle.kts settings.gradle settings.gradle.kts gradle/libs.versions.toml; do test -f "$f" || continue; if grep -qE 'micronaut|io\.micronaut' "$f" 2>/dev/null; then printf %s run; exit 0; fi; done; for f in build.gradle build.gradle.kts settings.gradle settings.gradle.kts gradle/libs.versions.toml; do test -f "$f" || continue; if grep -qE 'vertx-plugin|io\.vertx\.vertx' "$f" 2>/dev/null; then printf %s vertxRun; exit 0; fi; done; printf %s bootRun
VERSION:
sh: git describe --tags --always --dirty 2>/dev/null || echo "dev"
COMMIT:
sh: git rev-parse --short HEAD 2>/dev/null || echo "unknown"
DOCKER_REGISTRY: ghcr.io
DOCKER_IMAGE: "{{.DOCKER_REGISTRY}}/{{.PROJECT}}"
JVM_MODULE:
sh: echo "${JVM_MODULE:-change-me-subproject}"
tasks:
default:
desc: List all tasks
cmds:
- task --list
clean:
desc: Remove build artifacts
cmds:
- "{{.ENTRYPOINT}} clean"
build:
desc: Full build including tests and checks
cmds:
- "{{.ENTRYPOINT}} build"
assemble:
desc: Build project and package artifacts
cmds:
- "{{.ENTRYPOINT}} assemble"
test:
desc: Run unit tests
cmds:
- "{{.ENTRYPOINT}} test"
check:
desc: Run tests and static analysis (Gradle `check`)
cmds:
- "{{.ENTRYPOINT}} check"
module:build:
desc: Build one Gradle subproject (export JVM_MODULE; uses env or default)
cmds:
- "{{.ENTRYPOINT}} :{{.JVM_MODULE}}:build"
module:test:
desc: Test one Gradle subproject
cmds:
- "{{.ENTRYPOINT}} :{{.JVM_MODULE}}:test"
module:check:
desc: Check one Gradle subproject
cmds:
- "{{.ENTRYPOINT}} :{{.JVM_MODULE}}:check"
dev:
desc: Run application locally (framework from SKILL §2.3 scan)
cmds:
- "{{.ENTRYPOINT}} {{.DEV_GRADLE_TASK}}"
lint:
desc: Full verification (delegates to `check`)
deps: [check]
fmt:
desc: Apply Spotless (`spotlessApply`)
cmds:
- "{{.ENTRYPOINT}} spotlessApply"
lint:checkstyle:
desc: Checkstyle (`checkstyleMain`)
cmds:
- "{{.ENTRYPOINT}} checkstyleMain"
lint:spotbugs:
desc: SpotBugs (`spotbugsMain`)
cmds:
- "{{.ENTRYPOINT}} spotbugsMain"
lint:pmd:
desc: PMD (`pmdMain`)
cmds:
- "{{.ENTRYPOINT}} pmdMain"
lint:spotless:
desc: Spotless check only (`spotlessCheck`)
cmds:
- "{{.ENTRYPOINT}} spotlessCheck"
db:migrate:liquibase:
desc: Liquibase update (`liquibaseUpdate`)
cmds:
- "{{.ENTRYPOINT}} liquibaseUpdate"
db:migrate:flyway:
desc: Flyway migrate (`flywayMigrate`)
cmds:
- "{{.ENTRYPOINT}} flywayMigrate"
docker:build:
desc: Build Docker image
cmds:
- docker build --build-arg VERSION={{.VERSION}} --build-arg COMMIT={{.COMMIT}} -t {{.DOCKER_IMAGE}}:{{.VERSION}} -t {{.DOCKER_IMAGE}}:latest .
docker:push:
desc: Push Docker image
cmds:
- docker push {{.DOCKER_IMAGE}}:{{.VERSION}}
- docker push {{.DOCKER_IMAGE}}:latest
ci:
desc: Clean then full Gradle build
cmds:
- task: clean
- task: build
templates/taskfile-maven.yml
version: '3'
# --- Maven Taskfile — canonical targets; remove tasks your pom/plugins do not define (SKILL Step 5). ---
vars:
PROJECT:
sh: basename $(pwd)
ENTRYPOINT:
sh: if [ -f ./mvnw ] || [ -f .mvn/wrapper/maven-wrapper.properties ]; then echo "./mvnw"; else echo "mvn"; fi
DEV_MAVEN_GOAL:
sh: if ! test -f pom.xml; then printf %s spring-boot:run; exit 0; fi; if grep -qE 'quarkus|io\.quarkus' pom.xml 2>/dev/null; then printf %s quarkus:dev; exit 0; fi; if grep -qE 'micronaut|io\.micronaut' pom.xml 2>/dev/null; then printf %s mn:run; exit 0; fi; if grep -qE 'vertx-maven-plugin|io\.reactiverse' pom.xml 2>/dev/null; then printf %s vertx:run; exit 0; fi; printf %s spring-boot:run
VERSION:
sh: git describe --tags --always --dirty 2>/dev/null || echo "dev"
COMMIT:
sh: git rev-parse --short HEAD 2>/dev/null || echo "unknown"
DOCKER_REGISTRY: ghcr.io
DOCKER_IMAGE: "{{.DOCKER_REGISTRY}}/{{.PROJECT}}"
JVM_MODULE:
sh: echo "${JVM_MODULE:-change-me-subproject}"
tasks:
default:
desc: List all tasks
cmds:
- task --list
clean:
desc: Remove build artifacts
cmds:
- "{{.ENTRYPOINT}} clean"
build:
desc: Full build including tests and checks (`verify`)
cmds:
- "{{.ENTRYPOINT}} verify"
assemble:
desc: Build project and package artifacts
cmds:
- "{{.ENTRYPOINT}} package"
test:
desc: Run unit tests
cmds:
- "{{.ENTRYPOINT}} test"
check:
desc: Run tests and static analysis (`verify`)
cmds:
- "{{.ENTRYPOINT}} verify"
module:build:
desc: Package one reactor subtree (export JVM_MODULE; -pl … -am package)
cmds:
- "{{.ENTRYPOINT}} -pl {{.JVM_MODULE}} -am package"
module:test:
desc: Test one reactor subtree
cmds:
- "{{.ENTRYPOINT}} -pl {{.JVM_MODULE}} -am test"
module:check:
desc: Verify one reactor subtree
cmds:
- "{{.ENTRYPOINT}} -pl {{.JVM_MODULE}} -am verify"
dev:
desc: Run application locally (framework from SKILL §2.3 scan)
cmds:
- "{{.ENTRYPOINT}} {{.DEV_MAVEN_GOAL}}"
lint:
desc: Full verification (delegates to `verify`)
deps: [check]
fmt:
desc: Apply Spotless (`spotless:apply`)
cmds:
- "{{.ENTRYPOINT}} spotless:apply"
lint:checkstyle:
desc: Checkstyle (`checkstyle:check`)
cmds:
- "{{.ENTRYPOINT}} checkstyle:check"
lint:spotbugs:
desc: SpotBugs (`spotbugs:check`)
cmds:
- "{{.ENTRYPOINT}} spotbugs:check"
lint:pmd:
desc: PMD (`pmd:check`)
cmds:
- "{{.ENTRYPOINT}} pmd:check"
lint:spotless:
desc: Spotless check only (`spotless:check`)
cmds:
- "{{.ENTRYPOINT}} spotless:check"
db:migrate:liquibase:
desc: Liquibase (`liquibase:update`)
cmds:
- "{{.ENTRYPOINT}} liquibase:update"
db:migrate:flyway:
desc: Flyway (`flyway:migrate`)
cmds:
- "{{.ENTRYPOINT}} flyway:migrate"
docker:build:
desc: Build Docker image
cmds:
- docker build --build-arg VERSION={{.VERSION}} --build-arg COMMIT={{.COMMIT}} -t {{.DOCKER_IMAGE}}:{{.VERSION}} -t {{.DOCKER_IMAGE}}:latest .
docker:push:
desc: Push Docker image
cmds:
- docker push {{.DOCKER_IMAGE}}:{{.VERSION}}
- docker push {{.DOCKER_IMAGE}}:latest
ci:
desc: Clean then full Maven verify
cmds:
- task: clean
- task: build
templates/taskfile-node.yml
# --- Taskfile for Node.js Projects ---
# Usage: task [target]
# Install: https://taskfile.dev/installation/
version: '3'
output: prefixed
dotenv: ['.env', '.env.local']
vars:
PROJECT:
sh: node -p "require('./package.json').name" 2>/dev/null || basename $(pwd)
VERSION:
sh: git describe --tags --always --dirty 2>/dev/null || echo "dev"
COMMIT:
sh: git rev-parse --short HEAD 2>/dev/null || echo "unknown"
# Package manager detection
PM:
sh: |
if [ -f bun.lockb ]; then echo "bun"
elif [ -f pnpm-lock.yaml ]; then echo "pnpm"
elif [ -f yarn.lock ]; then echo "yarn"
else echo "npm"; fi
PMX:
sh: |
if [ -f bun.lockb ]; then echo "bunx"
elif [ -f pnpm-lock.yaml ]; then echo "pnpm exec"
elif [ -f yarn.lock ]; then echo "yarn"
else echo "npx"; fi
# Docker
DOCKER_REGISTRY: ghcr.io
DOCKER_IMAGE: '{{.DOCKER_REGISTRY}}/{{.PROJECT}}'
DOCKER_TAG: '{{.VERSION}}'
tasks:
# --- Development ---
install:
desc: Install dependencies
sources:
- package.json
- yarn.lock
- pnpm-lock.yaml
- package-lock.json
- bun.lockb
cmds:
- '{{.PM}} install'
dev:
desc: Start development server
deps: [install]
cmds:
- '{{.PM}} run dev'
build:
desc: Build for production
deps: [install]
env:
NODE_ENV: production
cmds:
- '{{.PM}} run build'
start:
desc: Start production server
env:
NODE_ENV: production
cmds:
- '{{.PM}} run start'
generate:
desc: Run code generation
cmds:
- '{{.PM}} run generate'
# --- Testing ---
test:
desc: Run tests
deps: [install]
cmds:
- '{{.PM}} run test'
test:watch:
desc: Run tests in watch mode
cmds:
- '{{.PM}} run test -- --watch'
test:cover:
desc: Run tests with coverage
cmds:
- '{{.PM}} run test -- --coverage'
test:e2e:
desc: Run end-to-end tests
cmds:
- '{{.PM}} run test:e2e'
# --- Code Quality ---
lint:
desc: Run linter
cmds:
- '{{.PM}} run lint'
lint:fix:
desc: Run linter with auto-fix
cmds:
- '{{.PM}} run lint -- --fix'
fmt:
desc: Format code with Prettier
cmds:
- '{{.PMX}} prettier --write .'
fmt:check:
desc: Check code formatting
cmds:
- '{{.PMX}} prettier --check .'
typecheck:
desc: Run TypeScript type checking
cmds:
- '{{.PMX}} tsc --noEmit'
check:
desc: Run all checks
deps: [lint, typecheck, test]
# --- Docker ---
docker:build:
desc: Build Docker image
cmds:
- >-
docker build
--build-arg VERSION={{.VERSION}}
--build-arg COMMIT={{.COMMIT}}
-t {{.DOCKER_IMAGE}}:{{.DOCKER_TAG}}
-t {{.DOCKER_IMAGE}}:latest
.
docker:push:
desc: Push Docker image
cmds:
- docker push {{.DOCKER_IMAGE}}:{{.DOCKER_TAG}}
- docker push {{.DOCKER_IMAGE}}:latest
docker:run:
desc: Run Docker container locally
cmds:
- docker run --rm -p 3000:3000 --env-file .env {{.DOCKER_IMAGE}}:{{.DOCKER_TAG}}
# --- Database ---
db:migrate:
desc: Run database migrations
cmds:
- '{{.PM}} run db:migrate'
db:seed:
desc: Seed the database
cmds:
- '{{.PM}} run db:seed'
db:reset:
desc: Reset database
cmds:
- '{{.PM}} run db:reset'
# --- CI ---
ci:
desc: Run full CI pipeline
cmds:
- task: install
- task: lint
- task: typecheck
- task: test
- task: build
# --- Cleanup ---
clean:
desc: Remove build artifacts and caches
cmds:
- rm -rf dist/ build/ .next/ out/ coverage/ .turbo/ node_modules/.cache
clean:all:
desc: Remove everything including node_modules
deps: [clean]
cmds:
- rm -rf node_modules/
templates/taskfile-php.yml
# --- Taskfile for PHP Projects ---
# Usage: task [target]
# Install: https://taskfile.dev/installation/
version: '3'
output: prefixed
dotenv: ['.env', '.env.local']
vars:
PROJECT: '{{.ROOT_DIR | base}}'
PHP: php
COMPOSER: composer
VERSION:
sh: git describe --tags --always --dirty 2>/dev/null || echo "dev"
COMMIT:
sh: git rev-parse --short HEAD 2>/dev/null || echo "unknown"
# Docker
DOCKER_REGISTRY: ghcr.io
DOCKER_IMAGE: '{{.DOCKER_REGISTRY}}/{{.PROJECT}}'
DOCKER_TAG: '{{.VERSION}}'
tasks:
# --- Development ---
install:
desc: Install dependencies
sources:
- composer.json
- composer.lock
cmds:
- '{{.COMPOSER}} install'
update:
desc: Update dependencies
cmds:
- '{{.COMPOSER}} update'
dev:
desc: Start development server
deps: [install]
cmds:
- '{{.PHP}} -S localhost:8000 -t public/'
serve:
desc: Start Laravel dev server
cmds:
- '{{.PHP}} artisan serve'
tinker:
desc: Open interactive REPL (Laravel)
cmds:
- '{{.PHP}} artisan tinker'
routes:
desc: List application routes (Laravel)
cmds:
- '{{.PHP}} artisan route:list'
# --- Testing ---
test:
desc: Run tests
cmds:
- ./vendor/bin/phpunit
test:cover:
desc: Run tests with coverage report
cmds:
- ./vendor/bin/phpunit --coverage-html coverage/ --coverage-text
- echo "Coverage report{{":"}} coverage/index.html"
test:filter:
desc: Run filtered tests (usage{{":"}} task test:filter -- ClassName::testMethod)
cmds:
- ./vendor/bin/phpunit --filter="{{.CLI_ARGS}}"
test:parallel:
desc: Run tests in parallel (requires paratest)
cmds:
- ./vendor/bin/paratest
# --- Code Quality ---
lint:
desc: Run PHP linter (PHP-CS-Fixer dry-run)
cmds:
- ./vendor/bin/php-cs-fixer fix --dry-run --diff
lint:fix:
desc: Fix code style issues
cmds:
- ./vendor/bin/php-cs-fixer fix
phpstan:
desc: Run static analysis
cmds:
- ./vendor/bin/phpstan analyse
fmt:
desc: Format code (alias for lint:fix)
cmds:
- task: lint:fix
check:
desc: Run all quality checks
deps: [lint, phpstan, test]
# --- Database ---
db:migrate:
desc: Run database migrations
cmds:
- '{{.PHP}} artisan migrate'
db:rollback:
desc: Rollback last migration
cmds:
- '{{.PHP}} artisan migrate:rollback'
db:seed:
desc: Seed the database
cmds:
- '{{.PHP}} artisan db:seed'
db:fresh:
desc: Drop all tables, re-run migrations + seeds
preconditions:
- sh: '[ "{{.CONFIRM}}" = "yes" ]'
msg: "Pass CONFIRM=yes to run db:fresh (destructive)"
cmds:
- '{{.PHP}} artisan migrate:fresh --seed'
# --- Cache & Optimization ---
cache:clear:
desc: Clear all caches
cmds:
- '{{.PHP}} artisan cache:clear'
- '{{.PHP}} artisan config:clear'
- '{{.PHP}} artisan route:clear'
- '{{.PHP}} artisan view:clear'
optimize:
desc: Cache config, routes, and views for production
cmds:
- '{{.PHP}} artisan config:cache'
- '{{.PHP}} artisan route:cache'
- '{{.PHP}} artisan view:cache'
# --- Docker ---
docker:build:
desc: Build Docker image
cmds:
- >-
docker build
--build-arg VERSION={{.VERSION}}
--build-arg COMMIT={{.COMMIT}}
-t {{.DOCKER_IMAGE}}:{{.DOCKER_TAG}}
-t {{.DOCKER_IMAGE}}:latest
.
docker:push:
desc: Push Docker image
cmds:
- docker push {{.DOCKER_IMAGE}}:{{.DOCKER_TAG}}
- docker push {{.DOCKER_IMAGE}}:latest
docker:run:
desc: Run Docker container locally
cmds:
- docker run --rm -p 8000:8000 --env-file .env {{.DOCKER_IMAGE}}:{{.DOCKER_TAG}}
# --- CI ---
ci:
desc: Run full CI pipeline
cmds:
- task: install
- task: lint
- task: phpstan
- task: test
# --- Cleanup ---
clean:
desc: Remove generated files and caches
cmds:
- rm -rf vendor/ coverage/ bootstrap/cache/*.php
- rm -rf storage/framework/cache/* storage/framework/sessions/* storage/framework/views/*
templates/taskfile-python.yml
# --- Taskfile for Python Projects ---
# Usage: task [target]
# Install: https://taskfile.dev/installation/
version: '3'
output: prefixed
dotenv: ['.env', '.env.local']
vars:
PROJECT: '{{.ROOT_DIR | base}}'
PYTHON: python3
SRC_DIR: src
VERSION:
sh: git describe --tags --always --dirty 2>/dev/null || echo "dev"
COMMIT:
sh: git rev-parse --short HEAD 2>/dev/null || echo "unknown"
# Package manager detection
PKG:
sh: |
if [ -f uv.lock ]; then echo "uv"
elif [ -f poetry.lock ]; then echo "poetry"
elif [ -f Pipfile.lock ]; then echo "pipenv"
else echo "pip"; fi
PKG_RUN:
sh: |
if [ -f uv.lock ]; then echo "uv run"
elif [ -f poetry.lock ]; then echo "poetry run"
elif [ -f Pipfile.lock ]; then echo "pipenv run"
else echo ""; fi
# Docker
DOCKER_REGISTRY: ghcr.io
DOCKER_IMAGE: '{{.DOCKER_REGISTRY}}/{{.PROJECT}}'
DOCKER_TAG: '{{.VERSION}}'
tasks:
# --- Development ---
install:
desc: Install dependencies
cmds:
- |
case "{{.PKG}}" in
uv) uv sync ;;
poetry) poetry install ;;
pipenv) pipenv install --dev ;;
*) {{.PYTHON}} -m pip install -e ".[dev]" ;;
esac
dev:
desc: Start development server
deps: [install]
cmds:
- '{{.PKG_RUN}} {{.PYTHON}} -m uvicorn main:app --reload --host 0.0.0.0 --port 8000'
run:
desc: Run the application
cmds:
- '{{.PKG_RUN}} {{.PYTHON}} -m {{.PROJECT}}'
shell:
desc: Open interactive Python shell
cmds:
- '{{.PKG_RUN}} {{.PYTHON}}'
# --- Testing ---
test:
desc: Run tests
cmds:
- '{{.PKG_RUN}} pytest'
test:watch:
desc: Run tests in watch mode
cmds:
- '{{.PKG_RUN}} pytest-watch'
test:cover:
desc: Run tests with coverage
cmds:
- '{{.PKG_RUN}} pytest --cov={{.SRC_DIR}} --cov-report=html --cov-report=term-missing'
- echo "Coverage report{{":"}} htmlcov/index.html"
test:integration:
desc: Run integration tests
cmds:
- '{{.PKG_RUN}} pytest tests/integration/ -v'
# --- Code Quality ---
lint:
desc: Run linters
cmds:
- '{{.PKG_RUN}} ruff check {{.SRC_DIR}} tests/'
lint:fix:
desc: Run linters with auto-fix
cmds:
- '{{.PKG_RUN}} ruff check --fix {{.SRC_DIR}} tests/'
fmt:
desc: Format code
cmds:
- '{{.PKG_RUN}} ruff format {{.SRC_DIR}} tests/'
fmt:check:
desc: Check code formatting
cmds:
- '{{.PKG_RUN}} ruff format --check {{.SRC_DIR}} tests/'
typecheck:
desc: Run type checker
cmds:
- '{{.PKG_RUN}} mypy {{.SRC_DIR}}'
check:
desc: Run all checks
deps: [lint, typecheck, test]
# --- Docker ---
docker:build:
desc: Build Docker image
cmds:
- >-
docker build
--build-arg VERSION={{.VERSION}}
--build-arg COMMIT={{.COMMIT}}
-t {{.DOCKER_IMAGE}}:{{.DOCKER_TAG}}
-t {{.DOCKER_IMAGE}}:latest
.
docker:push:
desc: Push Docker image
cmds:
- docker push {{.DOCKER_IMAGE}}:{{.DOCKER_TAG}}
- docker push {{.DOCKER_IMAGE}}:latest
# --- Database ---
db:migrate:
desc: Run database migrations
cmds:
- '{{.PKG_RUN}} alembic upgrade head'
db:rollback:
desc: Rollback last migration
cmds:
- '{{.PKG_RUN}} alembic downgrade -1'
db:migration:
desc: Create new migration
cmds:
- '{{.PKG_RUN}} alembic revision --autogenerate -m "{{.CLI_ARGS}}"'
# --- CI ---
ci:
desc: Run full CI pipeline
cmds:
- task: install
- task: lint
- task: fmt:check
- task: typecheck
- task: test
# --- Cleanup ---
clean:
desc: Remove build artifacts and caches
cmds:
- rm -rf dist/ build/ *.egg-info .pytest_cache .mypy_cache .ruff_cache htmlcov/ coverage.xml
- find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
- find . -type f -name "*.pyc" -delete 2>/dev/null || true
templates/taskfile-ruby.yml
# --- Taskfile for Ruby (Bundler) Projects ---
# Usage: task [target]
# Install: https://taskfile.dev/installation/
version: '3'
output: prefixed
dotenv: ['.env', '.env.local']
vars:
PROJECT: '{{.ROOT_DIR | base}}'
VERSION:
sh: git describe --tags --always --dirty 2>/dev/null || echo "dev"
COMMIT:
sh: git rev-parse --short HEAD 2>/dev/null || echo "unknown"
DOCKER_REGISTRY: ghcr.io
DOCKER_IMAGE: '{{.DOCKER_REGISTRY}}/{{.PROJECT}}'
DOCKER_TAG: '{{.VERSION}}'
tasks:
# --- Development ---
install:
desc: Install gems (bundle install)
cmds:
- bundle install
update:
desc: Update gems
cmds:
- bundle update
dev:
desc: Start app (override per project — rails server, rackup, etc.)
deps: [install]
cmds:
- bundle exec ruby -S rackup -o 0.0.0.0 -p 9292
console:
desc: Rails console (requires rails)
cmds:
- bundle exec rails console
# --- Testing ---
test:
desc: Run tests (RSpec — use test:rake for Minitest)
cmds:
- bundle exec rspec
test:rake:
desc: Run via Rake (e.g. minitest)
cmds:
- bundle exec rake test
# --- Code Quality ---
lint:
desc: Rubocop (no auto-correct)
cmds:
- bundle exec rubocop
lint:fix:
desc: Rubocop auto-correct
cmds:
- bundle exec rubocop -A
check:
desc: Static checks + tests
deps: [lint, test]
# --- Docker ---
docker:build:
desc: Build Docker image
cmds:
- >-
docker build
--build-arg VERSION={{.VERSION}}
--build-arg COMMIT={{.COMMIT}}
-t {{.DOCKER_IMAGE}}:{{.DOCKER_TAG}}
-t {{.DOCKER_IMAGE}}:latest
.
docker:push:
desc: Push Docker image
cmds:
- docker push {{.DOCKER_IMAGE}}:{{.DOCKER_TAG}}
- docker push {{.DOCKER_IMAGE}}:latest
# --- CI ---
ci:
desc: Run full CI pipeline
deps: [install, lint, test]
# --- Cleanup ---
clean:
desc: Remove tmp logs (adjust per app)
cmds:
- rm -rf tmp/ log/*.log
templates/taskfile-rust.yml
# --- Taskfile for Rust Projects ---
# Usage: task [target]
# Install: https://taskfile.dev/installation/
version: '3'
output: prefixed
dotenv: ['.env', '.env.local']
vars:
PROJECT: '{{.ROOT_DIR | base}}'
VERSION:
sh: git describe --tags --always --dirty 2>/dev/null || echo "dev"
COMMIT:
sh: git rev-parse --short HEAD 2>/dev/null || echo "unknown"
DOCKER_REGISTRY: ghcr.io
DOCKER_IMAGE: '{{.DOCKER_REGISTRY}}/{{.PROJECT}}'
DOCKER_TAG: '{{.VERSION}}'
CLIPPYFLAGS: '-D warnings'
tasks:
# --- Development ---
build:
desc: Build the workspace (debug)
sources:
- ./**/*.rs
- Cargo.toml
- Cargo.lock
cmds:
- cargo build
build:release:
desc: Build release binaries
cmds:
- cargo build --release
run:
desc: Run the default binary (cargo run)
cmds:
- cargo run
dev:
desc: Watch and rebuild (requires cargo-watch)
cmds:
- cargo watch -x check -x test
check:
desc: Fast compile check
cmds:
- cargo check
# --- Testing ---
test:
desc: Run tests
cmds:
- cargo test
test:doc:
desc: Run documentation tests
cmds:
- cargo test --doc
# --- Code Quality ---
lint:
desc: Run clippy
cmds:
- cargo clippy --all-targets --all-features -- {{.CLIPPYFLAGS}}
fmt:
desc: Format with rustfmt
cmds:
- cargo fmt
fmt:check:
desc: Verify formatting (CI)
cmds:
- cargo fmt -- --check
doc:
desc: Build rustdoc locally
cmds:
- cargo doc --no-deps
# --- Docker ---
docker:build:
desc: Build Docker image
cmds:
- >-
docker build
--build-arg VERSION={{.VERSION}}
--build-arg COMMIT={{.COMMIT}}
-t {{.DOCKER_IMAGE}}:{{.DOCKER_TAG}}
-t {{.DOCKER_IMAGE}}:latest
.
docker:push:
desc: Push Docker image
cmds:
- docker push {{.DOCKER_IMAGE}}:{{.DOCKER_TAG}}
- docker push {{.DOCKER_IMAGE}}:latest
# --- CI ---
ci:
desc: Run full CI pipeline
deps: [fmt:check, lint, test, build]
# --- Cleanup ---
clean:
desc: Remove build artifacts
cmds:
- cargo clean