diff --git a/.claude/skills/makebind/SKILL.md b/.claude/skills/makebind/SKILL.md index 93f8eab..c5ec6a0 100644 --- a/.claude/skills/makebind/SKILL.md +++ b/.claude/skills/makebind/SKILL.md @@ -289,7 +289,7 @@ endef ## Writing Tests -Tests use `tests/make_testing.mk`: +Tests use `tests/test_runner.mk` and `tests/asserts.mk`: ```makefile define test_my_feature @@ -377,6 +377,8 @@ include $(__mb_mymod_dir)functions.mk | `containers/` | docker, docker_compose | | `webservers/` | nginx | | `cloud_providers/aws/` | s3, sqs, sns | +| `databases/` | postgresql | +| `infrastructure/` | terraform | | `project_builder/` | project scaffolding | ## Quick Reference diff --git a/.gitignore b/.gitignore index 6ce1b38..ee211d8 100644 --- a/.gitignore +++ b/.gitignore @@ -2,8 +2,13 @@ tmp/* *.log .claude/mcp.json -docs +docs/assessments/ +docs/session-notes-* +docs/plans/ +docs/proposals/ +docs/localstack-module-review.md .kelux.toml # Claude Code workspace state .claude/workspace/ +.claude-workspace/ diff --git a/CLAUDE.md b/CLAUDE.md index 69408f3..e538d99 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,420 +1,54 @@ # CLAUDE.md -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. +MakeBind is a modular Makefile project manager providing a plugin/module system for GNU Make 4.4+. -## Project Overview +For code conventions and patterns, invoke the `/makebind` skill. +For architecture details, see @docs/architecture.md -MakeBind is a modular Makefile project manager that simplifies and customizes Makefile workflows. It provides a plugin/module system for GNU Make, allowing users to compose reusable Makefile functionality and manage project-specific build configurations. +## Key Commands -**Key Requirement**: GNU Make 4.4 or higher is required. - -## Development Commands - -### Running Tests -```bash -# Run all core tests -make -C tests - -# Run specific test -make -C tests filter=test_name - -# Exclude specific tests -make -C tests exclude=test_name - -# Run module tests only -make -C tests run_module_tests - -# Run specific module tests -make -C tests run_module_tests module=docker_compose - -# Run all tests (core + modules) -make -C tests run_all_tests -``` - -### Module Management -```bash -# List all available modules -make mb/modules/list - -# Add module(s) to project -make mb/modules/add/ -make mb/modules/add// # Add multiple - -# Remove module from project -make mb/modules/remove/ - -# Create new module -make mb/modules/create/ -``` - -### Target Management ```bash -# List all available targets (default) -make -make mb/targets-list - -# Get help on specific topic -make mb/help -make mb/help- +# Tests +make -C tests # All core tests +make -C tests filter=test_name # Specific test +make -C tests run_module_tests module=docker_compose # Module tests +make -C tests run_all_tests # Core + module tests + +# Modules +make mb/modules/list # List available +make mb/modules/add/ # Add module +make mb/modules/create/ # Create new module + +# Targets +make # List all targets +make mb/help- # Help on topic + +# Debugging +# Set in environment or config.mk: +# mb_debug=1 mb_debug_modules=1 mb_debug_targets=1 mb_debug_show_all_commands=1 ``` -### Debugging -Set debug flags in your environment or config.mk: -- `mb_debug=1` - Enable general debugging -- `mb_debug_modules=1` - Debug module loading -- `mb_debug_targets=1` - Debug target listing -- `mb_debug_show_all_commands=1` - Show all shell commands - -## Architecture - -### Core System Structure - -**Entry Points**: -- `main.mk` - Main entry point that orchestrates the entire system -- `Makefile.tpl.mk` (template) - Project-level Makefile that references main.mk - -**Core Components** (in `core/`): -1. **modules_manager.mk** - Module discovery, loading, and dependency resolution -2. **functions.mk** - Core utility functions (mb_invoke, mb_shell_capture, mb_user_confirm) -3. **targets.mk** - Target listing and help system -4. **util.mk** - Utility functions and helper includes -5. **init_project.mk** - Project initialization when bind-hub folder is missing - -**Utility Components** (in `core/util/`): -- `os_detection.mk` - OS detection (Linux/macOS) -- `colours.mk` - Terminal color output helpers -- `cache.mk` - File-based caching system with TTL support -- `debug.mk` - Debug output utilities -- `variables.mk` - Common variable definitions (mb_true, mb_false, mb_on, mb_off, etc.) - -### Module System - -**Module Structure**: Each module must have: -- `mod_info.mk` - Metadata file defining: - - `mb_module_name` - Module identifier - - `mb_module_version` - Version string - - `mb_module_description` - Human-readable description - - `mb_module_depends` - Space-separated list of dependency modules - - `mb_module_filename` (optional) - Custom .mk filename (defaults to `.mk`) -- `.mk` - Implementation file with actual targets/functions -- `mod_config.mk` (optional) - Module configuration variables - -**Module Discovery**: -- System modules: `modules/` directory (searched recursively) -- Project modules: `bind-hub/modules/` directory (searched recursively) -- Database built at startup by `mb_modules_build_db` function - -**Module Loading Process**: -1. Build module database from all mod_info.mk files -2. Read `bind-hub/internal/modules.mk` for enabled modules -3. For each enabled module: - - Load module's `mod_config.mk` (if exists) - - Load project override: `bind-hub/configs/_config.mk` (if exists) - - Load module implementation file -4. Dependencies are automatically loaded when adding modules +## main.mk Flags (Know Before Writing Code) -**Available Modules** (in `modules/`): -- `php/` - PHP ecosystem (php, composer, phpunit) -- `php/frameworks/` - Framework support (symfony, laravel) -- `containers/` - Container tools (docker, docker_compose) -- `webservers/` - Web servers (nginx) -- `cloud_providers/aws/` - AWS services (s3, sqs, sns) -- `project_builder/` - Project scaffolding - -### Project Configuration - -**Configuration Hierarchy** (bind-hub folder): -1. `config.mk` - Project configuration (committed) -2. `config.local.mk` - Local overrides (gitignored) -3. `project.mk` - Project-specific targets (committed) -4. `project.local.mk` - Local target overrides (gitignored) -5. `internal/modules.mk` - Auto-generated list of enabled modules (DO NOT EDIT) -6. `configs/` - Module configuration overrides (created on-demand) -7. `modules/` - Project-specific custom modules (created on-demand) - -**Important Variables** (in config.mk): -- `mb_project_path` - Absolute path to project root (REQUIRED) -- `mb_makebind_path` - Path to MakeBind installation -- `mb_default_target` - Default target (default: mb/targets-list) -- `mb_target_spacing` - Column spacing for target listing (default: 40) -- `mb_targets_only_project` - Show only project/module targets, hide MakeBind core (default: false) - -**Environment Variables**: -- `MB_MAKEBIND_GLOBAL_PATH_ENV` - Global path to MakeBind installation. Set this in your shell profile (e.g., `.bashrc`) to avoid hardcoding the path in each project's Makefile. - -### Loading Order - -Understanding the load order is critical: -1. `Makefile` includes `main.mk` -2. `main.mk` loads `config.mk` and `config.local.mk` -3. Core utilities loaded (util.mk, functions.mk) -4. Module database built (`mb_modules_build_db`) -5. Modules loaded (`mb_load_modules`) -6. Project targets loaded (`project.mk`, `project.local.mk`) - -This order ensures: -- Module targets can be overridden by project targets -- Pre-hooks run before module targets -- Post-hooks can be defined after module targets - -### Make Flags and Shell Configuration - -MakeBind sets specific Make behavior: -- `.ONESHELL:` - Multi-line recipes run in single shell -- `.POSIX:` - POSIX compliance -- `.SECONDEXPANSION:` - Enables `$$@` in prerequisites -- Shell: `/bin/bash` (configurable via `mb_default_shell_not_windows`) -- Shell flags: `-euc[x]o pipefail` (strict error handling) -- `--no-builtin-rules` and `--no-builtin-variables` - Clean slate -- `--silent` mode (unless `mb_debug_no_silence=1`) - -## Writing Tests - -Tests use a custom test framework in `tests/make_testing.mk`: - -**Test Structure**: -```makefile -# Define test as a variable containing commands -define test_ - $(call mb_assert,,) - $(call mb_assert_eq,,,) - $(call mb_assert_neq,,,) -endef -``` - -**Running Tests**: Tests are discovered by the `test_` prefix and executed via `mb_run_tests`. - -**Assertion Functions**: -- `mb_assert` - Assert truthy condition -- `mb_assert_eq` - Assert equality -- `mb_assert_neq` - Assert inequality -- `mb_assert_was_called` - Track function invocations - -## Writing Modules - -**Minimal Module Example**: -```makefile -# modules/mymodule/mod_info.mk -mb_module_name := mymodule -mb_module_version := 1.0.0 -mb_module_description := My custom module -mb_module_depends := # Optional dependencies - -# modules/mymodule/mymodule.mk -ifndef __MB_MODULES_MYMODULE__ -__MB_MODULES_MYMODULE__ := 1 - -mymodule/target: ## Description shown in target list - $(call mb_printf_info,Running mymodule target) - -endif -``` - -**Module Best Practices**: -- Use include guard pattern: `ifndef __MB_MODULES___` -- Prefix all targets with module name (e.g., `docker/up`, `symfony/cache-clear`) -- Use `##` comments for target descriptions (shown in `make` output) -- Store configuration in `mod_config.mk` with sensible defaults -- Document dependencies in `mb_module_depends` - -## Common Patterns - -### Invoking Commands -Use `mb_invoke` for consistent command execution with logging: -```makefile -target: - $(call mb_invoke,docker compose up -d) -``` - -Variables control behavior: -- `mb_invoke_print` - Show command before execution -- `mb_invoke_dry_run` - Don't execute, just print -- `mb_invoke_run_in_shell` - Capture output/exit code - -### OS-Specific Commands -Use `mb_os_call` for Linux vs macOS differences: -```makefile -$(call mb_os_call,,) -``` - -Check OS: `mb_os_is_linux`, `mb_os_is_osx`, `mb_os_is_linux_or_osx` - -### File Operations -- `mb_exists` / `mb_not_exists` - Check file existence -- `mb_is_url` - Validate URL format -- `mb_timestamp` - Get current Unix timestamp - -### Caching -Use the cache system for expensive operations: -```makefile -$(call mb_cache_read,,) -$(call mb_cache_write,,,) -``` - -Cache files stored in `tmp/cache/` with TTL support. - -## Common Pitfalls - -1. **Variable Assignment**: Use `:=` for immediate assignment, `=` for deferred -2. **Function Calls**: Always use `$(call func,args)` not `$(func args)` -3. **Recursive Variables**: The module loading uses recursion - be careful with variable naming to avoid collisions (pattern: `$0_prm__$1`) -4. **OS Differences**: Some commands differ between Linux and macOS - use `mb_os_call` when needed -5. **Module Dependencies**: Circular dependencies are not detected - avoid them -6. **Include Guards**: Always use unique include guards in .mk files -7. **Comments in define blocks**: `##` comments inside `define...endef` are NOT comments - they become literal output text. Only use comments OUTSIDE define blocks or use `$(info ...)` for debug output - -## Code Style - -- Prefix internal variables with `mb_` or function name (e.g., `$0_var`) -- Use descriptive function/variable names -- Document complex functions with comment headers -- Keep lines under 120 characters where possible -- Use consistent indentation (tabs for recipes, spaces for variable definitions) - -### `$(if)` Formatting - -**Never write `$(if)` as one-liners.** Use multi-line format for readability: - -```makefile -## Correct - multi-line format -$(if $(call mb_not_exists,$(some_path)),\ - $(error Path not found)\ -) - -$(if $(condition),\ - $(do_if_true)\ -,\ - $(do_if_false)\ -) - -## Wrong - one-liner (hard to read, hard to debug) -$(if $(call mb_not_exists,$(some_path)),$(error Path not found)) -``` - -**Exception:** Simple variable assignments can use inline `$(if)`: -```makefile -## OK - simple inline assignment -mb_exists = $(if $(wildcard $1),$(mb_true)) -$0_arg := $(if $(value 1),$(strip $1),default) -``` - -### Function Argument Naming Convention - -Use `$0_arg_` pattern for function arguments to match `@arg N` in docblocks: - -```makefile -## @function my_function -## @arg 1: command (required) - Command to execute -## @arg 2: prefix (required) - Variable prefix -## @arg 3: shell (optional) - Shell to use (default: /bin/sh) -define my_function -$(strip - ## Required args: inline check is safe because $(if $(value N),...) guards access - $(eval $0_arg1_cmd := $(if $(value 1),$(strip $1),$(call mb_printf_error,$0: command required))) - $(eval $0_arg2_prefix := $(if $(value 2),$(strip $2),$(call mb_printf_error,$0: prefix required))) - - ## Optional arg with default - $(eval $0_arg3_shell := $(if $(value 3),$(strip $3),/bin/sh)) - - ## Complex validation (e.g., mb_require_var): check BEFORE assignment - $(eval $0_bin := $(call mb_require_var,$($0_arg2_prefix)_bin,$0: $($0_arg2_prefix)_bin not defined)) -) -endef -``` - -**Rules:** -1. **Simple required arg**: inline check in assignment using `$(if $(value N),...)` -2. **Optional arg with default**: `$(if $(value N),$(strip $N),default)` - no error needed -3. **Complex validation**: validate before assignment to avoid `--warn-undefined-variables` warnings - -### File Header Block - -Every `.mk` file must start with a standard header: - -```makefile -##################################################################################### -# Project: MakeBind -# File: -# Description: -# Author: -# License: MIT License -##################################################################################### -``` - -### Define Blocks with `$(strip)` - -Always wrap `define` block content in `$(strip ...)` to avoid trailing whitespace issues: - -```makefile -define my_function -$(strip - $(eval $0_arg1 := ...) - $(if ...,\ - ...\ - ) -) -endef -``` - -### Debug Logging - -Use the debug print function with appropriate debug flags: - -```makefile -$(call mb_debug_print,Message here,$(mb_debug_modules)) -``` - -Available debug flags: `mb_debug`, `mb_debug_modules`, `mb_debug_targets`, `mb_debug_cache` - -### Nested Includes - -When a module needs to include other files, get the current directory first: - -```makefile -__mb_mymodule_dir := $(dir $(lastword $(MAKEFILE_LIST))) - -include $(__mb_mymodule_dir)functions.mk -include $(__mb_mymodule_dir)targets.mk -``` - -## Important Reminders - -### Check main.mk Flags and Directives -Before writing module code, review `main.mk` for active settings: - -**MAKEFLAGS:** -- `--always-make` - all targets always rebuild, so `.PHONY` is **redundant** - do not add it +- `--always-make` - `.PHONY` is redundant, do not add it - `--warn-undefined-variables` - guard variable access with `$(value VAR)` or `$(if ...)` -- `--no-builtin-rules` and `--no-builtin-variables` - clean slate, no implicit rules -- `--no-print-directory` - suppress directory change messages +- `.ONESHELL:` - entire recipe runs in single shell invocation +- `.SHELLFLAGS := -euco pipefail` - strict error handling - `--silent` - quiet mode (unless `mb_debug_no_silence=1`) -**Shell Configuration:** -- `SHELL := /bin/bash` (via `mb_default_shell_not_windows`) -- `.SHELLFLAGS := -euc[x]o pipefail` - strict error handling (`-x` added when `mb_debug_show_all_commands=1`) -- `MAKESHELL := $(SHELL)` - -**Special Directives:** -- `.ONESHELL:` - entire recipe runs in single shell invocation (multi-line commands share state) -- `.POSIX:` - POSIX compliance mode -- `.SECONDEXPANSION:` - enables `$$@` and `$$*` in prerequisites for dynamic expansion +## Contributor Workflow ### Changelog -**IMPORTANT**: When making significant changes, you MUST update `CHANGELOG.md`. This includes: -- New modules -- New features or targets -- Breaking changes -- Bug fixes +**IMPORTANT**: Update `CHANGELOG.md` for significant changes (new modules, features, breaking changes, bug fixes). Rules: - Follow [Keep a Changelog](https://keepachangelog.com/) format -- Group changes under: Added, Changed, Deprecated, Removed, Fixed, Security -- Include version number and date for releases +- Group under: Added, Changed, Deprecated, Removed, Fixed, Security - Bump minor version (e.g., 2.1.0 → 2.2.0) for new features/modules - Bump patch version (e.g., 2.1.0 → 2.1.1) for bug fixes ### README Files -**IMPORTANT**: Keep README files up to date when making changes: +Keep README files up to date when making changes: | File | Update When | |------|-------------| @@ -423,13 +57,11 @@ Rules: | `core/README.md` | New core functions, changes to loading order, new utilities | | `tests/README.md` | Changes to test framework, new assertions, test conventions | -When adding a new module, ensure the "Available Modules" table in `README.md` is updated. +When adding a new module, update the "Available Modules" table in `README.md`. ### Trello Board **Board ID: `vBmmD6it`** - Configured via `.kelux.toml` (local config). -**Always use `klx` CLI for Trello operations.** Fall back to Trello MCP only if klx doesn't support the operation. - -Before using any Trello tool, invoke the `/kelux` skill to get correct syntax and avoid common gotchas. +**Always use `klx` CLI for Trello operations.** Invoke `/kelux` skill for correct syntax. -Feature proposals should be added as cards in the Trello "Proposals" list instead of markdown files. \ No newline at end of file +Feature proposals go as cards in the Trello "Proposals" list. diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..39b6435 --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,95 @@ +# MakeBind Architecture + +Internal architecture reference for MakeBind contributors. + +For code conventions and best practices, invoke the `/makebind` skill. + +## Entry Points + +- `main.mk` - Main entry point that orchestrates the entire system +- `templates/Makefile.tpl.mk` - Project-level Makefile template that references main.mk + +## Core Components (core/) + +| File | Purpose | +|------|---------| +| `modules_manager.mk` | Module discovery, loading, and dependency resolution | +| `functions.mk` | Core utility functions (mb_invoke, mb_shell_capture, mb_user_confirm) | +| `targets.mk` | Target listing and help system | +| `util.mk` | Utility functions and helper includes | +| `init_project.mk` | Project initialization when bind-hub folder is missing | + +## Utility Components (core/util/) + +| File | Purpose | +|------|---------| +| `os_detection.mk` | OS detection (Linux/macOS), `mb_os_call` | +| `colours.mk` | Terminal color output helpers | +| `cache.mk` | File-based caching system with TTL support | +| `debug.mk` | Debug output utilities | +| `variables.mk` | Common variable definitions (mb_true, mb_false, mb_on, mb_off) | +| `git.mk` | Git utilities | + +## Module Loading Process + +1. Build module database from all `mod_info.mk` files (`mb_modules_build_db`) +2. Read `bind-hub/internal/modules.mk` for enabled modules +3. For each enabled module: + - Load module's `mod_config.mk` (if exists) + - Load project override: `bind-hub/configs/_config.mk` (if exists) + - Load module implementation file +4. Dependencies are automatically loaded when adding modules + +Module discovery locations: +- System modules: `modules/` directory (searched recursively) +- Project modules: `bind-hub/modules/` directory (searched recursively) + +## Loading Order + +Understanding the load order is critical: + +1. `Makefile` includes `main.mk` +2. `main.mk` loads `config.mk` and `config.local.mk` +3. Core utilities loaded (util.mk, functions.mk) +4. Module database built (`mb_modules_build_db`) +5. Modules loaded (`mb_load_modules`) +6. Project targets loaded (`project.mk`, `project.local.mk`) + +This order ensures: +- Module targets can be overridden by project targets +- Pre-hooks run before module targets +- Post-hooks can be defined after module targets + +## Available Modules + +| Category | Modules | Description | +|----------|---------|-------------| +| `php/` | php, composer, phpunit, phpcs, phpstan, psalm | PHP ecosystem | +| `php/frameworks/` | symfony, laravel | Framework support | +| `containers/` | docker, docker_compose | Container tools | +| `webservers/` | nginx | Web servers | +| `cloud_providers/aws/` | s3, sqs, sns | AWS services | +| `databases/` | postgresql | Database management | +| `infrastructure/` | terraform | Infrastructure as code | +| `project_builder/` | project_builder | Project scaffolding | + +## Test Framework + +Tests are in `tests/` using two framework files: +- `tests/test_runner.mk` - Test discovery and execution (`mb_run_tests`) +- `tests/asserts.mk` - Assertion functions + +### Assertion Functions + +| Function | Purpose | +|----------|---------| +| `mb_assert` | Assert truthy condition | +| `mb_assert_eq` | Assert equality | +| `mb_assert_neq` | Assert inequality | +| `mb_assert_empty` | Assert value is empty | +| `mb_assert_not_empty` | Assert value is not empty | +| `mb_assert_filter` | Assert value matches filter | +| `mb_assert_contains` | Assert string contains substring | +| `mb_assert_exists` | Assert file/path exists | +| `mb_assert_not_exists` | Assert file/path does not exist | +| `mb_assert_was_called` | Track function invocations (in test_runner.mk) |