- Add stt-ptt language support documentation - Add rofi-project-opener module documentation - Add rofi-project-opener package documentation - Update zellij-ps documentation - Update guides and reference patterns - Update AGENTS.md with latest commands
499 lines
7.7 KiB
Markdown
499 lines
7.7 KiB
Markdown
# 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.cli.tool1.enable {...})
|
|
(mkIf config.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 for m3ta modules, `cli.*` namespace for CLI modules
|
|
|
|
```nix
|
|
m3ta.ports.enable = true;
|
|
cli.zellij-ps.enable = true;
|
|
```
|
|
|
|
**Bad**: Flat namespace
|
|
|
|
```nix
|
|
ports.enable = true; # Potential conflict
|
|
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
|