Files
alfred/CONTRIBUTE.md

262 lines
6.7 KiB
Markdown

# Contributing to Alfred
## Settings Management System
Alfred uses a **declarative, schema-based configuration system** that ensures type safety, validation, and maintainability.
### Architecture Overview
```
settings.toml # Schema definitions (single source of truth)
settings_schema.py # Parser & validation
settings_bootstrap.py # Generation & resolution
.env # Runtime configuration
.env.make # Build variables for Makefile
settings.py # Pydantic Settings (runtime validation)
```
### Key Files
- **`settings.toml`** — Declarative schema for all settings
- **`alfred/settings_schema.py`** — Schema parser and validation logic
- **`alfred/settings_bootstrap.py`** — Bootstrap logic (generates `.env` and `.env.make`)
- **`alfred/settings.py`** — Pydantic Settings class (runtime)
- **`.env`** — Generated configuration file (gitignored)
- **`.env.make`** — Build variables for Makefile (gitignored)
### Setting Sources
Settings can come from different sources:
| Source | Description | Example |
|--------|-------------|---------|
| `toml` | From `pyproject.toml` | Version numbers, build config |
| `env` | From `.env` file | User-provided values, API keys |
| `generated` | Auto-generated secrets | JWT secrets, passwords |
| `computed` | Calculated from other settings | Database URIs |
### How to Add a New Setting
#### 1. Define in `settings.toml`
```toml
[tool.alfred.settings_schema.MY_NEW_SETTING]
type = "string" # string, integer, float, boolean, secret, computed
source = "env" # env, toml, generated, computed
default = "default_value" # Optional: default value
description = "Description here" # Required: clear description
category = "app" # app, api, database, security, build
required = true # Optional: default is true
validator = "range:1:100" # Optional: validation rule
export_to_env_make = false # Optional: export to .env.make for Makefile
```
#### 2. Regenerate Configuration
```bash
make bootstrap
```
This will:
- Read the schema from `settings.toml`
- Generate/update `.env` with the new setting
- Generate/update `.env.make` if `export_to_env_make = true`
- Preserve existing secrets
#### 3. Validate
```bash
make validate
```
This ensures all settings are valid according to the schema.
#### 4. Use in Code
The setting is automatically available in `settings.py`:
```python
from alfred.settings import settings
print(settings.my_new_setting)
```
### Setting Types
#### String Setting
```toml
[tool.alfred.settings_schema.API_URL]
type = "string"
source = "env"
default = "https://api.example.com"
description = "API base URL"
category = "api"
```
#### Integer Setting with Validation
```toml
[tool.alfred.settings_schema.MAX_RETRIES]
type = "integer"
source = "env"
default = 3
description = "Maximum retry attempts"
category = "app"
validator = "range:1:10"
```
#### Secret (Auto-generated)
```toml
[tool.alfred.settings_schema.API_SECRET]
type = "secret"
source = "generated"
secret_rule = "32:b64" # 32 bytes, base64 encoded
description = "API secret key"
category = "security"
```
Secret rules:
- `"32:b64"` — 32 bytes, URL-safe base64
- `"16:hex"` — 16 bytes, hexadecimal
#### Computed Setting
```toml
[tool.alfred.settings_schema.DATABASE_URL]
type = "computed"
source = "computed"
compute_from = ["DB_HOST", "DB_PORT", "DB_NAME"]
compute_template = "postgresql://{DB_HOST}:{DB_PORT}/{DB_NAME}"
description = "Database connection URL"
category = "database"
```
#### From TOML (Build Variables)
```toml
[tool.alfred.settings_schema.APP_VERSION]
type = "string"
source = "toml"
toml_path = "tool.poetry.version"
description = "Application version"
category = "build"
export_to_env_make = true # Available in Makefile
```
### Validators
Available validators:
- **`range:min:max`** — Numeric range validation
```toml
validator = "range:0.0:2.0" # For floats
validator = "range:1:100" # For integers
```
### Categories
Organize settings by category:
- **`app`** — Application settings
- **`api`** — API keys and external services
- **`database`** — Database configuration
- **`security`** — Secrets and security keys
- **`build`** — Build-time configuration
### Best Practices
1. **Always add a description** — Make it clear what the setting does
2. **Use appropriate types** — Don't use strings for numbers
3. **Add validation** — Use validators for numeric ranges
4. **Categorize properly** — Helps with organization
5. **Use computed settings** — For values derived from others (e.g., URIs)
6. **Mark secrets as generated** — Let the system handle secret generation
7. **Export build vars** — Set `export_to_env_make = true` for Makefile variables
### Workflow Example
```bash
# 1. Edit settings.toml
vim settings.toml
# 2. Regenerate configuration
make bootstrap
# 3. Validate
make validate
# 4. Test
python -c "from alfred.settings import settings; print(settings.my_new_setting)"
# 5. Commit (settings.toml only, not .env)
git add settings.toml
git commit -m "Add MY_NEW_SETTING"
```
### Commands
```bash
make bootstrap # Generate .env and .env.make from schema
make validate # Validate all settings against schema
make help # Show all available commands
```
### Troubleshooting
**Setting not found in schema:**
```
KeyError: Missing [tool.alfred.settings_schema] section
```
→ Check that `settings.toml` exists and has the correct structure
**Validation error:**
```
ValueError: MY_SETTING must be between 1 and 100, got 150
```
→ Check the validator in `settings.toml` and adjust the value in `.env`
**Secret not preserved:**
→ Secrets are automatically preserved during `make bootstrap`. If lost, they were never in `.env` (check `.env` exists before running bootstrap)
### Testing
When adding a new setting, consider adding tests:
```python
# tests/test_settings_schema.py
def test_my_new_setting(self, create_schema_file):
"""Test MY_NEW_SETTING definition."""
schema_toml = """
[tool.alfred.settings_schema.MY_NEW_SETTING]
type = "string"
source = "env"
default = "test"
"""
base_dir = create_schema_file(schema_toml)
schema = load_schema(base_dir)
definition = schema.get("MY_NEW_SETTING")
assert definition.default == "test"
```
### Migration from Old System
If you're migrating from the old system:
1. Settings are now in `settings.toml` instead of scattered across files
2. No more `.env.example` — schema is the source of truth
3. Secrets are auto-generated and preserved
4. Validation happens at bootstrap time, not just runtime
---
## Questions?
Open an issue or check the existing settings in `settings.toml` for examples.