- Nix 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
The flake wrapper applied the entrypoint once per `(attr, system)` pair. `import` memoizes the *file* value, not the *application*, so none of those applications shared any of the entrypoint's internal work -- each re-ran the whole `let`, including the `pkgs ? import sources.nixpkgs { ... }` default argument, i.e. a whole fresh nixpkgs.
Binding one instantiation per system in the `let` and reading every keyed output out of it collapses that.
Measured on `fediversity-ci`, `nix flake show --no-eval-cache`, warm store, second of two runs:
| | wall | user+sys | maxrss | nrThunks | gc.totalBytes |
|---|---|---|---|---|---|
| before | 1.99 s | 1.31 s | 223 MB | 565,116 | 168.7 MB |
| after | 1.50 s | 0.95 s | 132 MB | 261,485 | 76.0 MB |
Output is unchanged: `nix flake show --json --all-systems --no-eval-cache | jq -S .` is byte-identical before and after, and every `drvPath` is unmoved bar the `treefmt` check's, which hashes the project source and so moves with any edit to `flake.nix` itself.
This is the same defect that cost the fediversity monorepo far more (its entrypoints carry a whole deployment surface, not just a nixpkgs); the wrapper was copied from https://codeberg.org/kiara/poc-override-nix-deps, which is being fixed at the source too.
Reviewed-on: #6
|
||
| .forgejo/workflows | ||
| md | ||
| npins | ||
| conversion-test.nix | ||
| conversion.nix | ||
| default.nix | ||
| flake.nix | ||
| lib.nix | ||
| LICENSE | ||
| README.md | ||
| treefmt.nix | ||
nix-tf-schema
A small, self-contained library of pure Nix helpers that translate between
OpenTofu/Terraform provider schemas (tofu providers schema -json) and Nix
module types / tfvars JSON.
It has no domain logic -- nothing about any particular infrastructure, service, or deployment. Given a provider schema it can:
- wrap Terraform attribute schemas into
tfvarsvariableblocks (wrapTfType,wrapTfAttr,wrapTfAttrs,wrapTfProviderSchema, ...); - convert Terraform types and attributes into nixpkgs module types
(
fromTfTypes,fromTfVar,fromTfVars,fromTfProviderSchema, ...); - turn a resource schema into Nix options and
${var.*}references (resourceAttrsToOptions,resourceAttrsToVarRefs); - build cross-resource reference expressions for the hand-written parts of a
module (
ref,referenceType,refOr,unbrace); - type Terraform's own resource meta-arguments --
count,for_each,depends_on,provider,lifecycle(metaArgOptions,metaArgsToResourceBody); - assemble a complete Terraform module as JSON (
mkTfModuleJson); and - extract provider schemas at build time by running
tofu providers schemain a sandboxed derivation (extractProviderSchemas, an IFD).
Layout
conversion.nix-- the library ({ lib, pkgs, ... }:-> conversion attrset).lib.nix-- two vendored helpers (cast,evalOption) it depends on.conversion-test.nix-- thenix-unitsuite (20 tests).default.nix-- source of truth: builds apkgsfrom the localnixpkgspin,callPackagesconversion.nixunderlib, and exposesformatter+checks+packages.docs.flake.nix-- thin wrapper adding the per-system layer overdefault.nix.md/-- the doc-site prose pages, rendered with the sharednix-docs-lib.
Consuming it
default.nix is callPackage-shaped and exposes the helpers under lib
(so they do not clash with the flake-schema attrs), so a consumer that already
has its own nixpkgs passes it in and eval uses that nixpkgs:
# in a repo that pins this via npins as `nix-tf-schema`
let
sources = import ./npins;
tfSchema = pkgs.callPackage "${sources.nix-tf-schema}" { };
in
tfSchema.lib.mkTfModuleJson { /* ... */ }
Pin it with npins:
npins add forgejo https://git.fediversity.eu fediversity nix-tf-schema --branch main
Documentation
The doc-commented API renders to a static site (via the shared
nix-docs-lib ndg
machinery):
nix build .#packages.x86_64-linux.docs
Known limitations
Three places where the translation is lossy. None of them is observable in what we generate today; each is recorded so the next person hitting one knows it is deliberate rather than an oversight.
nullmeans unset, with no way to say "explicitly null". Optional attributes becomenullOr Twithdefault = null. Terraform needs no distinction here -- an argument set tonullbehaves exactly as if omitted, and theoptional(...)type constraints we render carry no default, so the two coincide inside object types too. tofunix carries anunsetsentinel because it bakes literals into the module JSON, where an unset option would emit anullkey into blocks that reject one; our unset values are simply absent from the environment. It would start to matter if a code path renderedoptional(T, default), or needed a key absent from a JSON payload rather than present-and-null.int64is typed asints.s32. nixpkgs has noints.s64, so the wider range is not enforced at eval time.setis typed aslistOf. Nix has no unordered collection type, so a set's ordering is not normalized and duplicates are not rejected.
Debugging
The schema walks tag every recursion step with builtins.addErrorContext, so a
failure names the resource, attribute and block it was walking rather than
surfacing as a bare attribute 'foo' missing. Every such breadcrumb is prefixed
with [nix-tf-schema], which is what makes them greppable out of an otherwise
enormous trace:
nix ... --show-trace 2> >(grep "… \[nix-tf-schema\]")
The contexts nest, so the surviving lines read back as the path into the schema, innermost first.
Development
# format
nix fmt
# run the unit tests (needs `nix` on PATH for the IFD test-extractProviderSchemas)
nix develop --command nix-unit ./conversion-test.nix
# sandbox-safe checks (treefmt)
nix flake check -L
The nix-unit suite is run by CI through the Forgejo workflow rather than as a
nix flake check derivation, because test-extractProviderSchemas performs an
import-from-derivation (tofu providers schema) that needs a recursive nix
build the flake-check sandbox forbids.