|
| 1 | +# CLAUDE.md |
| 2 | + |
| 3 | +This file provides guidance to Claude Code when working with code in this repository. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +terranix-codegen transforms Terraform provider schemas (go-cty type system) into type-safe NixOS modules for Terranix. It parses provider specs, runs `tofu providers schema`, then generates Nix code through a linear pipeline of AST transformations. |
| 8 | + |
| 9 | +## Build and Development |
| 10 | + |
| 11 | +```bash |
| 12 | +nix develop # Enter dev shell (provides GHC, cabal, formatters, linters) |
| 13 | +cabal build # Build the project |
| 14 | +cabal test --enable-tests # Run all tests |
| 15 | +cabal test --enable-coverage # Run tests with HPC coverage |
| 16 | +nix build # Full Nix build |
| 17 | +nix flake check # Run all checks (build, format, lint) |
| 18 | +``` |
| 19 | + |
| 20 | +GHC 9.10.3 with GHC2024 edition. The flake uses flake-parts. |
| 21 | + |
| 22 | +## Testing |
| 23 | + |
| 24 | +Hspec with hspec-discover. Tests are in `test/TerranixCodegen/` with `*Spec.hs` suffix. |
| 25 | + |
| 26 | +Key testing patterns: |
| 27 | + |
| 28 | +- **`shouldMapTo` custom matcher** in `test/TestUtils.hs` — compares `NExpr` ASTs via pretty-printed output |
| 29 | +- **QuasiQuotes** — tests use `[nix| ... |]` syntax for inline Nix AST literals |
| 30 | +- HLint is configured (`.hlint.yaml`) to ignore parse errors from QuasiQuotes in test files |
| 31 | + |
| 32 | +## Formatting and Linting |
| 33 | + |
| 34 | +Treefmt-based with pre-commit hooks via hk. Hooks install automatically on dev shell entry. |
| 35 | + |
| 36 | +| Tool | Purpose | |
| 37 | +| -------------------------- | ----------------------------------------------------------------------- | |
| 38 | +| fourmolu | Haskell formatting (2-space indent, leading commas, record brace space) | |
| 39 | +| hlint | Haskell linting | |
| 40 | +| cabal-gild | .cabal file formatting | |
| 41 | +| alejandra, deadnix, statix | Nix formatting and linting | |
| 42 | + |
| 43 | +## Architecture |
| 44 | + |
| 45 | +### Pipeline |
| 46 | + |
| 47 | +``` |
| 48 | +ProviderSpec (text) → TerraformGenerator (runs tofu CLI) |
| 49 | + → ProviderSchema (JSON via aeson) → TypeMapper (CtyType → NExpr) |
| 50 | + → OptionBuilder (SchemaAttribute → mkOption) → ModuleGenerator (NixOS module assembly) |
| 51 | + → FileOrganizer (directory tree output) |
| 52 | +``` |
| 53 | + |
| 54 | +### Source Layout |
| 55 | + |
| 56 | +- `app/` — CLI entry point using optparse-applicative (`CLI/Types.hs`, `CLI/Parser.hs`, `CLI/Commands.hs`) |
| 57 | +- `lib/TerranixCodegen/` — Library code (pipeline stages) |
| 58 | + - `ProviderSchema/` — Aeson-based JSON parsing into ADTs (`CtyType.hs`, `Attribute.hs`, `Block.hs`, `Schema.hs`, `Provider.hs`, `Function.hs`, `Identity.hs`, `Types.hs`) |
| 59 | + - `ProviderSpec.hs` — Megaparsec parser for provider specs (e.g. `hashicorp/aws:5.0.0`) |
| 60 | + - `TypeMapper.hs` — Maps go-cty types to Nix types via hnix AST |
| 61 | + - `OptionBuilder.hs` — Builds `mkOption` expressions from schema attributes |
| 62 | + - `ModuleGenerator.hs` — Assembles complete NixOS module expressions |
| 63 | + - `FileOrganizer.hs` — Writes organized directory tree with auto-generated `default.nix` imports |
| 64 | + - `PrettyPrint.hs` — Colorized terminal output |
| 65 | +- `test/` — Hspec test suite |
| 66 | +- `nix/` — Flake modules (haskell build, devshell, treefmt, docs, CI workflow generation) |
| 67 | +- `vendor/` — Vendored terraform-json schema library |
| 68 | + |
| 69 | +### Critical Design Decisions |
| 70 | + |
| 71 | +- **AST-based code generation, not string templates.** All Nix output goes through hnix's `NExpr` AST and `prettyNix`. This guarantees syntactic validity. Never generate Nix as raw strings. |
| 72 | +- **`types.nullOr` for optional attributes** — preserves Terraform's null semantics (unset ≠ default value). |
| 73 | +- **Custom `types.tupleOf`** — implemented in `nix/lib/tuple.nix` for fixed-length, per-position type validation matching Terraform tuples. |
| 74 | +- **`StrictData` extension** — used in all `ProviderSchema/` modules for strict-by-default record fields. |
| 75 | + |
| 76 | +### Key Types |
| 77 | + |
| 78 | +| Type | Module | Role | |
| 79 | +| ----------------- | -------------------------- | --------------------------------------------------------------------------------- | |
| 80 | +| `CtyType` | `ProviderSchema.CtyType` | go-cty type system (Bool, Number, String, Dynamic, List, Set, Map, Object, Tuple) | |
| 81 | +| `SchemaAttribute` | `ProviderSchema.Attribute` | Attribute metadata (type, required/optional, computed, deprecated, sensitive) | |
| 82 | +| `SchemaBlock` | `ProviderSchema.Block` | Block with attributes, nested blocks, and nesting mode | |
| 83 | +| `ProviderSpec` | `ProviderSpec` | Parsed provider specification (namespace/name:version) | |
| 84 | +| `NExpr` | hnix | Nix expression AST — the core intermediate representation | |
| 85 | + |
| 86 | +## CI |
| 87 | + |
| 88 | +GitHub Actions workflow auto-generated from Nix (`nix/flake/ci/`). Three jobs: `check` (flake check), `build` (nix build), `coverage` (HPC report). |
0 commit comments