Files
alfred/CONTRIBUTE.md

6.7 KiB

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

[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

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

make validate

This ensures all settings are valid according to the schema.

4. Use in Code

The setting is automatically available in settings.py:

from alfred.settings import settings

print(settings.my_new_setting)

Setting Types

String Setting

[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

[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)

[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

[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)

[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
    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

# 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

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:

# 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.