262 lines
6.7 KiB
Markdown
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.
|