Skip to content

Usage Guide

This guide covers how to use Soonix to manage your project's configuration files, templates, and other assets.

Installation

Add Soonix to your flake inputs:

{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    soonix.url = "gitlab:TECHNOFAB/soonix?dir=lib";
  };
}

Basic Usage

1. Simple Configuration File Generation

Generate a JSON configuration file from Nix data:

let
  shellHook = (soonix.lib { inherit pkgs; }).mkShellHook {
    hooks = {
      package-json = {
        output = "package.json";
        generator = "nix";
        data = {
          name = "my-project";
          version = "1.0.0";
          scripts = {
            dev = "vite dev";
            build = "vite build";
          };
          dependencies = {
            vite = "^4.0.0";
          };
        };
        opts = { format = "json"; };
      };
    };
  };
in
  pkgs.mkShell { inherit shellHook; }

2. Multiple Format Support

The nix generator supports various formats:

hooks = {
  # YAML configuration
  docker-compose = {
    output = "docker-compose.yml";
    generator = "nix";
    data = {
      version = "3.8";
      services = {
        web = {
          image = "nginx:alpine";
          ports = ["80:80"];
        };
      };
    };
    opts = { format = "yaml"; };
  };

  # TOML configuration  
  pyproject = {
    output = "pyproject.toml";
    generator = "nix";
    data = {
      build-system = {
        requires = ["hatchling"];
        build-backend = "hatchling.build";
      };
      project = {
        name = "my-python-project";
        version = "0.1.0";
      };
    };
    opts = { format = "toml"; };
  };
};

3. Template-Based Generation

Use Go templates for more complex file generation:

hooks = {
  dockerfile = {
    output = "Dockerfile";
    generator = "gotmpl";
    data = {
      baseImage = "node:18-alpine";
      workdir = "/app";
      port = 3000;
      deps = ["curl" "git"];
    };
    opts.template = pkgs.writeText "dockerfile.tmpl" ''
      FROM {{ .baseImage }}

      WORKDIR {{ .workdir }}

      {{- if .deps }}
      RUN apk add --no-cache \
        {{- range $i, $dep := .deps }}
        {{- if $i }} \{{- end }}
        {{ $dep }}
        {{- end }}
      {{- end }}

      COPY package*.json ./
      RUN npm ci --only=production

      COPY . .

      EXPOSE {{ .port }}
      CMD ["npm", "start"]
    '';
  };
};

4. Jinja2 Templates

For Python-style templating:

hooks = {
  nginx-config = {
    output = "nginx.conf";
    generator = "jinja";
    data = {
      server_name = "example.com";
      port = 80;
      root = "/var/www/html";
      locations = [
        { path = "/api"; proxy_pass = "http://backend:8000"; }
        { path = "/static"; root = "/var/www/static"; }
      ];
    };
    opts.template = pkgs.writeText "nginx.conf.j2" ''
      server {
          listen {{ port }};
          server_name {{ server_name }};
          root {{ root }};

          {% for location in locations %}
          location {{ location.path }} {
              {% if location.proxy_pass %}
              proxy_pass {{ location.proxy_pass }};
              {% elif location.root %}
              root {{ location.root }};
              {% endif %}
          }
          {% endfor %}
      }
    '';
  };
};

5. Script Generation

Create executable scripts:

hooks = {
  dev-script = {
    output = "scripts/dev.sh";
    generator = "string";
    data = ''
      #!/usr/bin/env bash
      set -euo pipefail

      echo "Starting development environment..."

      # Start database
      docker-compose up -d postgres

      # Run migrations
      npm run db:migrate

      # Start dev server
      npm run dev
    '';
    opts.executable = true;
  };
};

6. Using Existing Derivations

Include pre-built files or derivations:

hooks = {
  config-file = {
    output = ".myapprc";
    generator = "derivation";
    data = pkgs.writeText "myapprc" ''
      # Generated configuration
      api_url = "https://api.example.com"
      debug = true
    '';
  };
};

Hook Configuration

File Management Modes

Control how files are managed:

hooks = {
  # Symlink mode (default)
  dev-config = {
    output = "config.json";
    generator = "nix";
    data = { debug = true; };
    hook.mode = "link";  # Creates symlinks
  };

  # Copy mode - for tools that don't support symlinks or file is not gitignored and needs to be portable
  editable-config = {
    output = "editable.json";
    generator = "nix"; 
    data = { editable = true; };
    hook.mode = "copy";  # Copies files
  };
};

GitIgnore Management

Control .gitignore entries:

hooks = {
  # Don't add to gitignore (you want to commit this file)
  committed-config = {
    output = "config.json";
    generator = "nix";
    data = { production = true; };
    hook.gitignore = false;
  };

  # Auto-add to gitignore (default behavior)
  generated-file = {
    output = "temp.json";
    generator = "nix";
    data = { temp = true; };
    hook.gitignore = true;  # Default
  };
};

Post-Processing Commands

Run commands after file operations:

hooks = {
  formatted-json = {
    output = "package.json";
    generator = "nix";
    data = { /* ... */ };
    hook = {
      mode = "copy";
      extra = "npx prettier --write package.json";
    };
  };

  executable-script = {
    output = "build.sh";
    generator = "string";
    data = "#!/bin/bash\necho 'Building...'";
    hook.extra = "chmod +x build.sh";
  };
};

Advanced Usage

Environment-Specific Configurations

let
  mkConfig = env: {
    database_url = if env == "development" 
      then "sqlite:///dev.db"
      else "postgresql://prod-db:5432/app";
    debug = env == "development";
    log_level = if env == "development" then "debug" else "info";
  };
in {
  hooks = {
    dev-config = {
      output = "config.dev.json";
      generator = "nix";
      data = mkConfig "development";
      opts = { format = "json"; };
    };

    prod-config = {
      output = "config.prod.json";
      generator = "nix"; 
      data = mkConfig "production";
      opts = { format = "json"; };
    };
  };
}

Complex Template Logic

hooks = {
  kubernetes-manifest = {
    output = "k8s/deployment.yaml";
    generator = "gotmpl";
    data = {
      app = {
        name = "my-app";
        version = "1.2.3";
        replicas = 3;
        port = 8080;
      };
      env = [
        { name = "NODE_ENV"; value = "production"; }
        { name = "PORT"; value = "8080"; }
      ];
      secrets = [
        { name = "DB_PASSWORD"; secretName = "db-secret"; key = "password"; }
      ];
      resources = {
        requests = { memory = "128Mi"; cpu = "100m"; };
        limits = { memory = "512Mi"; cpu = "500m"; };
      };
    };
    opts.template = ./templates/deployment.yaml.tmpl;
  };
};

Integration Patterns

With Development Shells

Standard mkShell integration:

pkgs.mkShell {
  packages = with pkgs; [ nodejs python3 docker ];
  shellHook = soonix.mkShellHook { inherit hooks; };
}

With Devshell

Using the devshell module:

{
  inputs = {
    devshell.url = "github:rensa-nix/devshell?dir=lib";
    soonix.url = "gitlab:TECHNOFAB/soonix?dir=lib";
  };

  outputs = { devshell, soonix, ... }: {
    # load pkgs etc...
    devShells.default = (devshell.lib { inherit pkgs; }).mkShell {
      imports = [ soonix.devshellModule ];

      packages = [ pkgs.hello ];

      soonix.hooks = {
        # Your hooks here
      };
    };
  };
}

Troubleshooting

Enable Logging

Set SOONIX_LOG=true to see detailed status messages:

SOONIX_LOG=true nix develop

Manual Updates

Update the files without shellHook/devshells:

nix run .#soonix:update

Check Generated Files

Inspect what Soonix would generate without writing files:

nix build .#soonixFiles
ls result/