diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..45bac514f --- /dev/null +++ b/Makefile @@ -0,0 +1,41 @@ +# Root task runner for YieldVault-RWA. +# Thin wrappers around the existing pnpm/cargo scripts so contributors have one +# entry point. Works with GNU make on Linux, macOS and Git Bash on Windows. + +PNPM ?= pnpm + +.DEFAULT_GOAL := help +.PHONY: help install test lint build format validate + +help: ## List available targets + @echo "Usage: make " + @echo "" + @awk 'BEGIN { FS = ":.*## " } /^[a-zA-Z_-]+:.*## / { printf " %-10s %s\n", $$1, $$2 }' $(MAKEFILE_LIST) + +install: ## Install workspace dependencies (backend, frontend, packages) + $(PNPM) check:pnpm-version + $(PNPM) install + +test: ## Run backend, frontend and root script tests + $(PNPM) test:all + +lint: ## Lint backend and frontend with ESLint + $(PNPM) --filter backend lint + $(PNPM) --filter frontend lint + +build: ## Build API schemas, backend and frontend + $(PNPM) build + +format: ## Format backend sources (and Rust contracts when cargo is installed) + $(PNPM) --filter backend format + @if command -v cargo >/dev/null 2>&1; then cargo fmt --all; else echo "cargo not found, skipping cargo fmt"; fi + +validate: ## Run repository convention and governance validators + $(PNPM) validate:contribution-standards + $(PNPM) validate:testing-strategy + $(PNPM) validate:sprint-and-triage + $(PNPM) validate:release-notes + $(PNPM) validate:nfr-baselines + $(PNPM) validate:codeowners + $(PNPM) validate:pr-template + $(PNPM) validate:renovate diff --git a/README.md b/README.md index 845a54112..0b9f98d79 100644 --- a/README.md +++ b/README.md @@ -91,6 +91,22 @@ For detailed setup instructions, prerequisites, validation steps, and troublesho For a complete environment variable reference with defaults, required flags, and production recommendations, see **[Environment Variable Matrix](./docs/ENV_VARIABLE_MATRIX.md)**. +### Makefile Targets + +The root `Makefile` wraps the existing pnpm scripts so common tasks have one entry point. Run `make` (or `make help`) to list them: + +| Command | What it runs | +| --------------- | ---------------------------------------------------------------------------- | +| `make help` | Lists all targets (default goal) | +| `make install` | `pnpm check:pnpm-version` then `pnpm install` for the whole workspace | +| `make test` | `pnpm test:all` (backend Jest, frontend Vitest, root script tests) | +| `make lint` | ESLint for `backend` and `frontend` | +| `make build` | `pnpm build` (API schemas, backend, frontend) | +| `make format` | Prettier for `backend`, plus `cargo fmt --all` when `cargo` is installed | +| `make validate` | Contribution, testing-strategy, triage, release-notes, NFR, CODEOWNERS, PR template and Renovate validators | + +The targets use GNU make and POSIX shell, so they run on Linux, macOS and Git Bash on Windows. Git Bash does not ship `make`; install it with `choco install make`, `scoop install make`, or `winget install ezwinports.make`. Set `PNPM` to override the pnpm binary, for example `make install PNPM="corepack pnpm"`. + ## API Documentation Generate contract and frontend API docs: diff --git a/scripts/makefile.test.ts b/scripts/makefile.test.ts new file mode 100644 index 000000000..ef07702a4 --- /dev/null +++ b/scripts/makefile.test.ts @@ -0,0 +1,37 @@ +import { describe, it, expect } from 'vitest'; +import { readFileSync, existsSync } from 'node:fs'; +import { resolve } from 'node:path'; + +const rootDir = resolve(__dirname, '..'); +const TARGETS = ['help', 'install', 'test', 'lint', 'build', 'format', 'validate']; + +describe('Root Makefile', () => { + const makefilePath = resolve(rootDir, 'Makefile'); + + it('exists at the repository root', () => { + expect(existsSync(makefilePath)).toBe(true); + }); + + it('declares every target as phony and defaults to help', () => { + const makefile = readFileSync(makefilePath, 'utf8'); + const phony = makefile.match(/^\.PHONY:(.*)$/m)?.[1].split(/\s+/) ?? []; + for (const target of TARGETS) { + expect(phony).toContain(target); + expect(makefile).toMatch(new RegExp(`^${target}:.*## `, 'm')); + } + expect(makefile).toMatch(/^\.DEFAULT_GOAL := help$/m); + }); + + it('uses tab-indented recipes and LF line endings', () => { + const makefile = readFileSync(makefilePath, 'utf8'); + expect(makefile).not.toContain('\r'); + expect(makefile).not.toMatch(/^ +(@|\$\(PNPM\))/m); + }); + + it('is documented in README.md', () => { + const readme = readFileSync(resolve(rootDir, 'README.md'), 'utf8'); + for (const target of TARGETS) { + expect(readme).toContain(`make ${target}`); + } + }); +});