docs: update zellij-ps to reflect project switcher functionality
- Update package description and fix mainProgram typo - Rewrite documentation to describe project switching, not process viewing - Add PROJECT_FOLDERS configuration and usage examples - Update all references across docs (README, guides, module overviews)
This commit is contained in:
272
docs/reference/functions.md
Normal file
272
docs/reference/functions.md
Normal file
@@ -0,0 +1,272 @@
|
||||
# Library Functions
|
||||
|
||||
Documentation for library functions available in m3ta-nixpkgs.
|
||||
|
||||
## Overview
|
||||
|
||||
The library provides helper functions for your NixOS and Home Manager configurations.
|
||||
|
||||
## Available Libraries
|
||||
|
||||
### `m3ta-lib.ports`
|
||||
|
||||
Port management utilities for managing service ports across hosts.
|
||||
|
||||
## Port Management Functions
|
||||
|
||||
### `mkPortHelpers`
|
||||
|
||||
Create port helper functions from a ports configuration.
|
||||
|
||||
#### Signature
|
||||
|
||||
```nix
|
||||
mkPortHelpers :: portsConfig -> portHelpers
|
||||
```
|
||||
|
||||
#### Arguments
|
||||
|
||||
`portsConfig` - An attribute set with structure:
|
||||
|
||||
```nix
|
||||
{
|
||||
ports = { service-name = port-number; ... };
|
||||
hostPorts = { hostname = { service-name = port-number; ... }; ... };
|
||||
}
|
||||
```
|
||||
|
||||
#### Returns
|
||||
|
||||
An attribute set containing helper functions:
|
||||
|
||||
```nix
|
||||
{
|
||||
getPort = service: host -> port-number-or-null;
|
||||
getHostPorts = host -> ports-attrs;
|
||||
listServices = -> [string];
|
||||
}
|
||||
```
|
||||
|
||||
#### Usage
|
||||
|
||||
```nix
|
||||
{config, inputs, ...}: let
|
||||
m3taLib = inputs.m3ta-nixpkgs.lib.${config.system};
|
||||
|
||||
myPorts = {
|
||||
ports = {
|
||||
nginx = 80;
|
||||
grafana = 3000;
|
||||
};
|
||||
hostPorts = {
|
||||
laptop = {
|
||||
nginx = 8080;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
portHelpers = m3taLib.ports.mkPortHelpers myPorts;
|
||||
in {
|
||||
# Get port with host override
|
||||
services.nginx.port = portHelpers.getPort "nginx" config.networking.hostName;
|
||||
|
||||
# Get all ports for host
|
||||
laptopPorts = portHelpers.getHostPorts "laptop";
|
||||
|
||||
# List all services
|
||||
allServices = portHelpers.listServices;
|
||||
}
|
||||
```
|
||||
|
||||
### `getPort` (from portHelpers)
|
||||
|
||||
Get port for a service, with optional host-specific override.
|
||||
|
||||
#### Signature
|
||||
|
||||
```nix
|
||||
getPort :: string -> string -> int-or-null
|
||||
```
|
||||
|
||||
#### Arguments
|
||||
|
||||
1. `service` - The service name (string)
|
||||
2. `host` - The hostname (string), or `null` for default
|
||||
|
||||
#### Returns
|
||||
|
||||
Port number (int) or `null` if service not found.
|
||||
|
||||
#### Usage
|
||||
|
||||
```nix
|
||||
services.nginx = {
|
||||
port = portHelpers.getPort "nginx" "laptop"; # Returns host-specific port
|
||||
# or
|
||||
port = portHelpers.getPort "nginx" null; # Returns default port
|
||||
};
|
||||
```
|
||||
|
||||
### `getHostPorts` (from portHelpers)
|
||||
|
||||
Get all ports for a specific host (merges defaults with host overrides).
|
||||
|
||||
#### Signature
|
||||
|
||||
```nix
|
||||
getHostPorts :: string -> attrs
|
||||
```
|
||||
|
||||
#### Arguments
|
||||
|
||||
1. `host` - The hostname (string)
|
||||
|
||||
#### Returns
|
||||
|
||||
Attribute set of all ports for the host.
|
||||
|
||||
#### Usage
|
||||
|
||||
```nix
|
||||
laptopPorts = portHelpers.getHostPorts "laptop";
|
||||
# Returns: { nginx = 8080; grafana = 3000; prometheus = 9090; ... }
|
||||
```
|
||||
|
||||
### `listServices` (from portHelpers)
|
||||
|
||||
List all defined service names.
|
||||
|
||||
#### Signature
|
||||
|
||||
```nix
|
||||
listServices :: -> [string]
|
||||
```
|
||||
|
||||
#### Returns
|
||||
|
||||
List of service names (strings).
|
||||
|
||||
#### Usage
|
||||
|
||||
```nix
|
||||
allServices = portHelpers.listServices;
|
||||
# Returns: ["nginx" "grafana" "prometheus" "homepage"]
|
||||
```
|
||||
|
||||
### `getDefaultPort`
|
||||
|
||||
Simple helper to get a port without host override.
|
||||
|
||||
#### Signature
|
||||
|
||||
```nix
|
||||
getDefaultPort :: portsConfig -> string -> int-or-null
|
||||
```
|
||||
|
||||
#### Arguments
|
||||
|
||||
1. `portsConfig` - Same structure as `mkPortHelpers`
|
||||
2. `service` - The service name (string)
|
||||
|
||||
#### Returns
|
||||
|
||||
Port number (int) or `null` if service not found.
|
||||
|
||||
#### Usage
|
||||
|
||||
```nix
|
||||
services.my-service = {
|
||||
port = m3taLib.ports.getDefaultPort myPorts "my-service";
|
||||
};
|
||||
```
|
||||
|
||||
## Using Library Functions
|
||||
|
||||
### Importing
|
||||
|
||||
```nix
|
||||
{config, inputs, ...}: let
|
||||
# Import library
|
||||
m3taLib = inputs.m3ta-nixpkgs.lib.${config.system};
|
||||
in {
|
||||
# Use library functions
|
||||
}
|
||||
```
|
||||
|
||||
### Example: Custom Port Management
|
||||
|
||||
```nix
|
||||
{config, inputs, ...}: let
|
||||
m3taLib = inputs.m3ta-nixpkgs.lib.${config.system};
|
||||
|
||||
myPorts = {
|
||||
ports = {
|
||||
web = 80;
|
||||
api = 8080;
|
||||
db = 5432;
|
||||
};
|
||||
hostPorts = {
|
||||
dev = {
|
||||
web = 8080;
|
||||
api = 8081;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
portHelpers = m3taLib.ports.mkPortHelpers myPorts;
|
||||
|
||||
hostname = config.networking.hostName;
|
||||
in {
|
||||
services.nginx = {
|
||||
enable = true;
|
||||
virtualHosts.${hostname} = {
|
||||
locations."/" = {
|
||||
proxyPass = "http://localhost:${toString (portHelpers.getPort "api" hostname)}";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
services.postgresql = {
|
||||
enable = true;
|
||||
port = portHelpers.getPort "db" hostname;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
### Example: Generate Config Files
|
||||
|
||||
```nix
|
||||
{inputs, ...}: let
|
||||
m3taLib = inputs.m3ta-nixpkgs.lib.${system};
|
||||
|
||||
myPorts = {
|
||||
ports = {
|
||||
service1 = 3000;
|
||||
service2 = 3001;
|
||||
};
|
||||
};
|
||||
|
||||
portHelpers = m3taLib.ports.mkPortHelpers myPorts;
|
||||
in {
|
||||
environment.etc."ports.toml".text = generators.toTOML {} {
|
||||
services = portHelpers.getHostPorts "desktop";
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## Function Reference Summary
|
||||
|
||||
| Function | Purpose | Return Type |
|
||||
|----------|---------|------------|
|
||||
| `mkPortHelpers` | Create port helper functions | `portHelpers` attrs |
|
||||
| `getPort` | Get port with optional host override | `int or null` |
|
||||
| `getHostPorts` | Get all ports for host | `attrs` |
|
||||
| `listServices` | List all service names | `[string]` |
|
||||
| `getDefaultPort` | Get default port only | `int or null` |
|
||||
|
||||
## Related
|
||||
|
||||
- [Port Management Guide](../guides/port-management.md) - Detailed usage guide
|
||||
- [NixOS Ports Module](../modules/nixos/ports.md) - Port management module
|
||||
- [Home Manager Ports Module](../modules/home-manager/ports.md) - User-level port management
|
||||
- [Architecture](../ARCHITECTURE.md) - Understanding library functions
|
||||
498
docs/reference/patterns.md
Normal file
498
docs/reference/patterns.md
Normal file
@@ -0,0 +1,498 @@
|
||||
# Code Patterns and Anti-Patterns
|
||||
|
||||
Common code patterns and anti-patterns used in m3ta-nixpkgs.
|
||||
|
||||
## Overview
|
||||
|
||||
This document outlines recommended patterns and common pitfalls when working with m3ta-nixpkgs.
|
||||
|
||||
## Code Patterns
|
||||
|
||||
### Package Pattern
|
||||
|
||||
#### CallPackage Registry
|
||||
|
||||
Use `callPackage` for lazy evaluation:
|
||||
|
||||
```nix
|
||||
# Good - pkgs/default.nix
|
||||
{
|
||||
inherit (pkgs) callPackage;
|
||||
} rec {
|
||||
code2prompt = callPackage ./code2prompt {};
|
||||
mem0 = callPackage ./mem0 {};
|
||||
}
|
||||
```
|
||||
|
||||
#### Meta Fields
|
||||
|
||||
Always include complete `meta` information:
|
||||
|
||||
```nix
|
||||
# Good
|
||||
meta = with lib; {
|
||||
description = "My awesome package";
|
||||
homepage = "https://github.com/author/package";
|
||||
changelog = "https://github.com/author/package/releases/tag/v${version}";
|
||||
license = licenses.mit;
|
||||
platforms = platforms.linux;
|
||||
mainProgram = "program-name";
|
||||
};
|
||||
```
|
||||
|
||||
### Module Pattern
|
||||
|
||||
#### Standard Module Structure
|
||||
|
||||
```nix
|
||||
# Good
|
||||
{ config, lib, pkgs, ... }:
|
||||
with lib; let
|
||||
cfg = config.m3ta.myModule;
|
||||
in {
|
||||
options.m3ta.myModule = {
|
||||
enable = mkEnableOption "my module";
|
||||
# ... options
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
# ... configuration
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
#### mkEnableOption
|
||||
|
||||
Always use `mkEnableOption` for enable flags:
|
||||
|
||||
```nix
|
||||
# Good
|
||||
options.m3ta.myModule = {
|
||||
enable = mkEnableOption "my module";
|
||||
};
|
||||
|
||||
# Bad
|
||||
options.m3ta.myModule.enable = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
};
|
||||
```
|
||||
|
||||
#### Conditional Configuration
|
||||
|
||||
Use `mkIf` for conditional config:
|
||||
|
||||
```nix
|
||||
# Good
|
||||
config = mkIf cfg.enable {
|
||||
services.my-service.enable = true;
|
||||
};
|
||||
```
|
||||
|
||||
#### Multiple Conditions
|
||||
|
||||
Use `mkMerge` for multiple conditions:
|
||||
|
||||
```nix
|
||||
# Good
|
||||
config = mkMerge [
|
||||
(mkIf cfg.feature1.enable {
|
||||
# config for feature1
|
||||
})
|
||||
(mkIf cfg.feature2.enable {
|
||||
# config for feature2
|
||||
})
|
||||
];
|
||||
```
|
||||
|
||||
### Import Pattern
|
||||
|
||||
#### Multi-line Imports
|
||||
|
||||
Multi-line, trailing commas:
|
||||
|
||||
```nix
|
||||
# Good
|
||||
{
|
||||
lib,
|
||||
stdenv,
|
||||
fetchFromGitHub,
|
||||
}:
|
||||
```
|
||||
|
||||
#### Explicit Dependencies
|
||||
|
||||
```nix
|
||||
# Good
|
||||
{
|
||||
lib,
|
||||
stdenv,
|
||||
openssl,
|
||||
pkg-config,
|
||||
}:
|
||||
stdenv.mkDerivation {
|
||||
buildInputs = [openssl pkg-config];
|
||||
}
|
||||
```
|
||||
|
||||
## Anti-Patterns
|
||||
|
||||
### lib.fakeHash in Commits
|
||||
|
||||
**Bad**: Committing `lib.fakeHash`
|
||||
|
||||
```nix
|
||||
# Bad - Never commit this!
|
||||
src = fetchFromGitHub {
|
||||
hash = lib.fakeHash;
|
||||
};
|
||||
```
|
||||
|
||||
**Solution**: Build to get real hash:
|
||||
|
||||
```bash
|
||||
nix build .#your-package
|
||||
# Copy actual hash from error message
|
||||
```
|
||||
|
||||
### Flat Module Files
|
||||
|
||||
**Bad**: All modules in one file
|
||||
|
||||
```nix
|
||||
# Bad - Hard to maintain
|
||||
{config, lib, pkgs, ...}: {
|
||||
options.m3ta.cli = {
|
||||
tool1 = mkEnableOption "tool1";
|
||||
tool2 = mkEnableOption "tool2";
|
||||
# ... many more
|
||||
};
|
||||
|
||||
config = mkMerge [
|
||||
(mkIf config.m3ta.cli.tool1.enable {...})
|
||||
(mkIf config.m3ta.cli.tool2.enable {...})
|
||||
# ... many more
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
**Solution**: Organize by category
|
||||
|
||||
```nix
|
||||
# Good - modules/home-manager/cli/
|
||||
# modules/home-manager/cli/default.nix
|
||||
{
|
||||
imports = [
|
||||
./tool1.nix
|
||||
./tool2.nix
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
### Hardcoded Ports
|
||||
|
||||
**Bad**: Hardcoding ports in services
|
||||
|
||||
```nix
|
||||
# Bad
|
||||
services.nginx = {
|
||||
enable = true;
|
||||
httpConfig = ''
|
||||
server {
|
||||
listen 80;
|
||||
}
|
||||
'';
|
||||
};
|
||||
```
|
||||
|
||||
**Solution**: Use port management
|
||||
|
||||
```nix
|
||||
# Good
|
||||
m3ta.ports = {
|
||||
enable = true;
|
||||
definitions = {nginx = 80;};
|
||||
};
|
||||
|
||||
services.nginx = {
|
||||
enable = true;
|
||||
httpConfig = ''
|
||||
server {
|
||||
listen ${toString (config.m3ta.ports.get "nginx")};
|
||||
}
|
||||
'';
|
||||
};
|
||||
```
|
||||
|
||||
### Skipping Meta Fields
|
||||
|
||||
**Bad**: Incomplete meta information
|
||||
|
||||
```nix
|
||||
# Bad
|
||||
meta = {
|
||||
description = "My package";
|
||||
};
|
||||
```
|
||||
|
||||
**Solution**: Include all fields
|
||||
|
||||
```nix
|
||||
# Good
|
||||
meta = with lib; {
|
||||
description = "My awesome package";
|
||||
homepage = "https://github.com/author/package";
|
||||
license = licenses.mit;
|
||||
platforms = platforms.linux;
|
||||
mainProgram = "program-name";
|
||||
};
|
||||
```
|
||||
|
||||
### with pkgs; in Modules
|
||||
|
||||
**Bad**: Using `with pkgs;` at module level
|
||||
|
||||
```nix
|
||||
# Bad
|
||||
{config, lib, pkgs, ...}: with pkgs; {
|
||||
config.environment.systemPackages = [
|
||||
vim
|
||||
git
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
**Solution**: Explicit package references or limited with
|
||||
|
||||
```nix
|
||||
# Good - Explicit references
|
||||
{config, lib, pkgs, ...}: {
|
||||
config.environment.systemPackages = with pkgs; [
|
||||
vim
|
||||
git
|
||||
];
|
||||
}
|
||||
|
||||
# Or - Full references
|
||||
{config, lib, pkgs, ...}: {
|
||||
config.environment.systemPackages = [
|
||||
pkgs.vim
|
||||
pkgs.git
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
### Orphaned Package Directories
|
||||
|
||||
**Bad**: Creating directory without registering
|
||||
|
||||
```nix
|
||||
# Bad - Package not visible
|
||||
# pkgs/my-package/default.nix exists
|
||||
# But pkgs/default.nix doesn't reference it
|
||||
```
|
||||
|
||||
**Solution**: Register in `pkgs/default.nix`
|
||||
|
||||
```nix
|
||||
# Good
|
||||
{
|
||||
inherit (pkgs) callPackage;
|
||||
} rec {
|
||||
my-package = callPackage ./my-package {};
|
||||
}
|
||||
```
|
||||
|
||||
## Type Safety
|
||||
|
||||
### No Type Suppression
|
||||
|
||||
**Bad**: Using `as any`
|
||||
|
||||
```nix
|
||||
# Bad
|
||||
let
|
||||
value = someFunction config as any;
|
||||
in
|
||||
# ...
|
||||
```
|
||||
|
||||
**Solution**: Fix underlying type issues
|
||||
|
||||
```nix
|
||||
# Good
|
||||
let
|
||||
value = someFunction config;
|
||||
in
|
||||
# Ensure value has correct type
|
||||
```
|
||||
|
||||
### Proper Type Definitions
|
||||
|
||||
```nix
|
||||
# Good
|
||||
options.m3ta.myModule = {
|
||||
enable = mkEnableOption "my module";
|
||||
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
default = 8080;
|
||||
description = "Port to run on";
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## Naming Conventions
|
||||
|
||||
### Package Names
|
||||
|
||||
**Good**: `lowercase-hyphen`
|
||||
|
||||
```nix
|
||||
code2prompt
|
||||
hyprpaper-random
|
||||
launch-webapp
|
||||
```
|
||||
|
||||
**Bad**: CamelCase or underscores
|
||||
|
||||
```nix
|
||||
code2Prompt # Bad
|
||||
hyprpaper_random # Bad
|
||||
```
|
||||
|
||||
### Variables
|
||||
|
||||
**Good**: `camelCase`
|
||||
|
||||
```nix
|
||||
portHelpers
|
||||
configFile
|
||||
serviceName
|
||||
```
|
||||
|
||||
**Bad**: Snake_case or kebab-case
|
||||
|
||||
```nix
|
||||
port_helpers # Bad
|
||||
port-helpers # Bad
|
||||
```
|
||||
|
||||
### Module Options
|
||||
|
||||
**Good**: `m3ta.*` namespace
|
||||
|
||||
```nix
|
||||
m3ta.ports.enable = true;
|
||||
m3ta.cli.zellij-ps.enable = true;
|
||||
```
|
||||
|
||||
**Bad**: Flat namespace
|
||||
|
||||
```nix
|
||||
ports.enable = true; # Potential conflict
|
||||
cli.zellij-ps.enable = true; # Hard to find
|
||||
```
|
||||
|
||||
## Performance
|
||||
|
||||
### Lazy Evaluation
|
||||
|
||||
**Good**: Use `callPackage`
|
||||
|
||||
```nix
|
||||
# Good - Only builds requested package
|
||||
code2prompt = callPackage ./code2prompt {};
|
||||
```
|
||||
|
||||
**Bad**: Building all packages
|
||||
|
||||
```nix
|
||||
# Bad - Builds everything even if not used
|
||||
code2prompt = import ./code2prompt {};
|
||||
```
|
||||
|
||||
### Selective Imports
|
||||
|
||||
**Good**: Import only needed modules
|
||||
|
||||
```nix
|
||||
# Good
|
||||
imports = [
|
||||
m3ta-nixpkgs.nixosModules.mem0
|
||||
];
|
||||
```
|
||||
|
||||
**Bad**: Importing all modules
|
||||
|
||||
```nix
|
||||
# Bad - Imports and evaluates all modules
|
||||
imports = [
|
||||
m3ta-nixpkgs.nixosModules.default
|
||||
];
|
||||
```
|
||||
|
||||
## Security
|
||||
|
||||
### No Secrets in Store
|
||||
|
||||
**Bad**: Putting secrets in configuration
|
||||
|
||||
```nix
|
||||
# Bad - Secret in Nix store
|
||||
m3ta.mem0.llm.apiKey = "sk-xxx";
|
||||
```
|
||||
|
||||
**Solution**: Use secret files
|
||||
|
||||
```nix
|
||||
# Good - Secret from file
|
||||
m3ta.mem0.llm.apiKeyFile = "/run/secrets/openai-api-key";
|
||||
```
|
||||
|
||||
### Proper User/Group
|
||||
|
||||
**Good**: Dedicated users for services
|
||||
|
||||
```nix
|
||||
# Good
|
||||
users.users.mem0 = {
|
||||
isSystemUser = true;
|
||||
group = "mem0";
|
||||
};
|
||||
```
|
||||
|
||||
### Service Hardening
|
||||
|
||||
**Good**: Enable systemd hardening
|
||||
|
||||
```nix
|
||||
# Good
|
||||
systemd.services.mem0.serviceConfig = {
|
||||
NoNewPrivileges = true;
|
||||
PrivateTmp = true;
|
||||
ProtectSystem = "strict";
|
||||
ProtectHome = true;
|
||||
};
|
||||
```
|
||||
|
||||
## Best Practices Summary
|
||||
|
||||
| Practice | Do | Don't |
|
||||
|----------|-----|--------|
|
||||
| Hash fetching | Get real hash from build error | Commit `lib.fakeHash` |
|
||||
| Module organization | Categorize by function | Put all in one file |
|
||||
| Port management | Use `m3ta.ports` module | Hardcode ports |
|
||||
| Meta fields | Include all fields | Skip fields |
|
||||
| Type safety | Fix type errors | Use `as any` |
|
||||
| Dependencies | Explicit declarations | Implicit deps |
|
||||
| Imports | Multi-line, trailing comma | Single-line, no comma |
|
||||
| Naming | Follow conventions | Mix styles |
|
||||
| Secrets | Use file-based | Put in config |
|
||||
| Evaluation | Lazy (`callPackage`) | Import everything |
|
||||
|
||||
## Related
|
||||
|
||||
- [Contributing Guide](../CONTRIBUTING.md) - Code style and guidelines
|
||||
- [Architecture](../ARCHITECTURE.md) - Understanding repository structure
|
||||
- [Adding Packages](../guides/adding-packages.md) - Package creation patterns
|
||||
Reference in New Issue
Block a user