diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 2705b2b..471cb04 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,7 +1,26 @@ + + ## Summary - + + +## How to test + + -## Related issues +--- - + diff --git a/.gitignore b/.gitignore index 7b70820..cd8f692 100644 --- a/.gitignore +++ b/.gitignore @@ -420,3 +420,7 @@ CLAUDE.md AGENTS.md DESIGN.md .claude/ + +# Lefthook local overrides (personal, not shared) +.lefthook-local.yml +lefthook-local.yml diff --git a/.lefthook.yml b/.lefthook.yml new file mode 100644 index 0000000..9de3957 --- /dev/null +++ b/.lefthook.yml @@ -0,0 +1,43 @@ +# Lefthook Git Hooks Configuration +min_version: 1.9.0 +assert_lefthook_installed: true +colors: true +output: + - summary + - execution + - execution_out + - execution_info + +pre-commit: + parallel: true + commands: + dotnet-fmt: + glob: "*.cs" + run: dotnet csharpier check . + fail_text: "C# formatting issues found! Run 'just format-dotnet' to fix them" + + ts-lint: + root: "clients/typescript/" + glob: "*.{ts,tsx,js,jsx,json}" + run: deno task lint + fail_text: "Linting failed! Run 'cd clients/typescript && deno task lint' to see errors" + + ts-fmt: + root: "clients/typescript/" + glob: "*.{ts,tsx,js,jsx,json}" + run: deno task fmt + fail_text: "Formatting issues found! Run 'just format-ts' to fix them" + +pre-push: + parallel: true + commands: + dotnet-build: + glob: "*.{cs,csproj,sln}" + run: dotnet build JsonApiToolkit.sln --configuration Release + fail_text: "Build failed! Run 'dotnet build JsonApiToolkit.sln' to see errors" + + ts-check: + root: "clients/typescript/" + glob: "*.{ts,tsx,js,jsx}" + run: deno task check + fail_text: "Type checking failed! Run 'cd clients/typescript && deno task check' to see errors" diff --git a/docs/contributing.md b/docs/contributing.md index 89c22f3..58ad8a5 100644 --- a/docs/contributing.md +++ b/docs/contributing.md @@ -2,22 +2,42 @@ ## Prerequisites -- .NET 10 SDK +- .NET 10 SDK, [Deno](https://deno.com) 2.x, [just](https://github.com/casey/just), [lefthook](https://lefthook.dev) + (all pinned in `mise.toml`; run `mise install` if you use [mise](https://mise.jdx.dev)) - `uv` for the docs site (`brew install uv` or `mise use uv`) ## Setup ```bash -dotnet tool restore # csharpier + dotnet-api-docs -dotnet restore +just setup # dotnet tool restore, dotnet restore, lefthook install ``` +This installs git hooks (lefthook) that format-check on commit and type-check +on push; see `.lefthook.yml`. CI (`ci-cd.yml`, `typescript-ci.yml`, +`contract-tests.yml`) is still the source of truth and runs the full suites. + ## Daily Commands +Run `just` (no arguments) to list all recipes, or `just --list`. + +```bash +just format # format .NET + TypeScript +just check # format-check + type-check everything, no fixes +just test # .NET tests + TypeScript unit tests, in parallel +just test-contract # build & run samples/ContractApi, run the Deno contract suite against it +just test-all # check + test + test-contract (what CI runs) +``` + +Narrower recipes (`format-dotnet`, `format-ts`, `test-dotnet`, `test-ts`, +`lint`, `clean`) are also available; see the `justfile`. + +Equivalent plain commands, if you don't have `just`: + ```bash dotnet build --configuration Release dotnet test --configuration Release -dotnet csharpier format . +dotnet csharpier format . +cd clients/typescript && deno task test ``` ## Docs diff --git a/justfile b/justfile new file mode 100644 index 0000000..6d1b10e --- /dev/null +++ b/justfile @@ -0,0 +1,134 @@ +set dotenv-load := false + +sample := "samples/ContractApi" +ts := "clients/typescript" + +# List available recipes +default: + @just --list + +# --------------------------------------------------------------------------- +# Setup +# --------------------------------------------------------------------------- + +# Full bootstrap: dotnet tools/packages, git hooks +[group('setup')] +setup: tools restore hooks + @echo "Setup complete. Run 'just test' to verify everything works." + +# Restore local dotnet tools (csharpier) +[group('setup')] +tools: + dotnet tool restore + +# Restore NuGet packages +[group('setup')] +restore: + dotnet restore --locked-mode + +# Install lefthook git hooks +[group('setup')] +hooks: + lefthook install + +# --------------------------------------------------------------------------- +# Quality +# --------------------------------------------------------------------------- + +# Format all code (.NET + TypeScript) in parallel +[group('quality')] +format: + #!/usr/bin/env bash + set -uo pipefail + pids=() + dotnet csharpier format . & pids+=($!) + (cd {{ts}} && deno task fmt:fix) & pids+=($!) + code=0 + for pid in "${pids[@]}"; do wait "$pid" || code=1; done + exit $code + +# Format .NET only (CSharpier) +[group('quality')] +format-dotnet: + dotnet csharpier format . + +# Format the TypeScript client only (deno fmt) +[group('quality')] +format-ts: + cd {{ts}} && deno task fmt:fix + +# Lint the TypeScript client (deno lint) +[group('quality')] +lint: + cd {{ts}} && deno task lint + +# Type-check and format-check everything, no fixes (.NET + TypeScript) in parallel +[group('quality')] +check: + #!/usr/bin/env bash + set -uo pipefail + pids=() + dotnet csharpier check . & pids+=($!) + (cd {{ts}} && deno task check && deno task lint && deno task fmt) & pids+=($!) + code=0 + for pid in "${pids[@]}"; do wait "$pid" || code=1; done + exit $code + +# Run .NET + TypeScript unit tests in parallel (excludes the contract suite; see test-contract) +[group('quality')] +test: + #!/usr/bin/env bash + set -uo pipefail + pids=() + dotnet test --configuration Release & pids+=($!) + (cd {{ts}} && deno task test) & pids+=($!) + code=0 + for pid in "${pids[@]}"; do wait "$pid" || code=1; done + exit $code + +# .NET tests only (JsonApiToolkit.Tests) +[group('quality')] +test-dotnet: + dotnet test --configuration Release + +# TypeScript client unit tests only +[group('quality')] +test-ts: + cd {{ts}} && deno task test + +# Build & run ContractApi (default + all-strict-opt-ins), run the Deno contract suite, then stop the servers +[group('quality')] +test-contract: + #!/usr/bin/env bash + set -euo pipefail + dotnet build {{sample}} --configuration Release + ASPNETCORE_URLS=http://localhost:5198 \ + dotnet run --project {{sample}} --configuration Release --no-build & + default_pid=$! + JSONAPI_STRICT=true ASPNETCORE_URLS=http://localhost:5199 \ + dotnet run --project {{sample}} --configuration Release --no-build & + strict_pid=$! + trap 'kill $default_pid $strict_pid 2>/dev/null || true' EXIT + + for url in http://localhost:5198/articles http://localhost:5199/articles; do + timeout 60 bash -c "until curl -sf -o /dev/null $url; do sleep 1; done" + done + + cd {{ts}} && deno task test:contract + +# Everything CI runs: format check, unit tests, and the contract suite +[group('quality')] +test-all: check test test-contract + +# --------------------------------------------------------------------------- +# Clean +# --------------------------------------------------------------------------- + +# Remove build artifacts (.NET bin/obj) +[group('clean')] +clean: + #!/usr/bin/env bash + set -uo pipefail + dotnet clean JsonApiToolkit.sln > /dev/null 2>&1 || true + find . -type d \( -name bin -o -name obj \) -prune -exec rm -rf {} + + echo "Cleaned build artifacts." diff --git a/mise.toml b/mise.toml index 70aaafc..39620f4 100644 --- a/mise.toml +++ b/mise.toml @@ -1,2 +1,5 @@ [tools] dotnet = "10.0.201" +deno = "2.7.13" +just = "latest" +lefthook = "latest"