Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ATTRIBUTIONS.md
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ SOFTWARE.

```

## aignostics-foundry-core (0.11.0) - MIT License
## aignostics-foundry-core (0.12.0) - MIT License

🏭 Foundational infrastructure for Foundry components.

Expand Down
166 changes: 129 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,13 @@ Or follow the [installation guide](https://mise.jdx.dev/getting-started.html) fo

## Usage

### Initialise the context at application startup
### Quickstart

Call `set_context()` once, before any library code runs, then call `boot()` to
initialise logging, the SSL trust chain, and optional Sentry integration:
`FoundryContext` is the single source of truth for all project-specific values. One call at
application startup makes everything available library-wide — logging, Sentry, database settings,
and more all derive from it automatically.

#### Initialise and boot

```python
# main.py
Expand All @@ -42,32 +45,43 @@ set_context(FoundryContext.from_package("myproject"))
boot()
```

`FoundryContext.from_package()` derives everything from package metadata and
environment variables:
`FoundryContext.from_package("myproject")` reads package metadata and environment variables to
populate every field:

- `name`, `version`, `version_full` — from `importlib.metadata`
- `environment` — from `MYPROJECT_ENVIRONMENT` → `ENV` → `VERCEL_ENV` →
`RAILWAY_ENVIRONMENT` → `"local"`
- `environment` — resolved from env vars in priority order (see [Configuration reference](#configuration-reference) below)
- `env_prefix` (`"MYPROJECT_"`) — used by every settings class; all env vars for this project share this prefix
- `is_container`, `is_cli`, `is_test`, `is_library` — detected automatically
- `env_prefix` (`"MYPROJECT_"`) — used by every settings class in the library

### Access the context from any module
`boot()` initialises logging (loguru), amends the SSL trust chain (truststore + certifi), and
optionally starts Sentry — all in one call.

#### Env file search order

Settings are loaded from the environment **and** from env files. Highest priority first:

1. `.env.{environment}`
2. `.env`
3. `{MYPROJECT_ENV_FILE}` (optional extra file; when the variable is set)
4. `~/.myproject/.env.{environment}`
5. `~/.myproject/.env`

#### Access the context from any module

```python
from aignostics_foundry_core.foundry import get_context

ctx = get_context()
print(f"Running {ctx.name} v{ctx.version_full} in {ctx.environment}")
# → Running myproject v1.2.3+main---run.12345 in staging
# → Running myproject v1.2.3+main-abc1234---run.12345---build.42 in staging
```

`get_context()` raises `RuntimeError` with a clear message if `set_context()`
was never called.
`get_context()` raises `RuntimeError` with a clear message if `set_context()` was never called.

### Pass context explicitly in tests
#### Testing pattern

Never call `set_context()` in tests. Pass a `FoundryContext` directly to each
function via its optional `context` parameter instead:
Never call `set_context()` in tests. Pass a `FoundryContext` directly to functions via their
optional `context` parameter:

```python
from aignostics_foundry_core.foundry import FoundryContext
Expand All @@ -77,14 +91,93 @@ ctx = FoundryContext(name="myproject", version="0.0.0", version_full="0.0.0", en
logging_initialize(context=ctx)
```

All public library functions (`logging_initialize`, `sentry_initialize`, `boot`,
`load_modules`, etc.) accept an optional `context` keyword argument and fall
back to `get_context()` when it is `None`.
All public library functions (`logging_initialize`, `sentry_initialize`, `boot`, `load_modules`,
etc.) accept an optional `context` keyword argument and fall back to `get_context()` when it is
`None`.

---

### Configuration reference

All settings classes read from environment variables prefixed with `{PREFIX}` where
`{PREFIX}` = `MYPROJECT_` for a package named `myproject`.

#### Context & deployment environment

### Database
Read directly by `FoundryContext.from_package()` — no settings class.

Once a context is configured via `set_context()`, all database functions work
with no arguments — the URL and pool settings are read from the context:
| Variable | Default | Description |
|---|---|---|
| `{PREFIX}ENVIRONMENT` | `"local"` | Deployment environment name. Highest priority. |
| `ENV` | — | Fallback environment (lower priority than `{PREFIX}ENVIRONMENT`). |
| `VERCEL_ENV` | — | Vercel deployment environment (lower priority). |
| `RAILWAY_ENVIRONMENT` | — | Railway deployment environment (lower priority). |
| `{PREFIX}RUNNING_IN_CONTAINER` | unset | Set to any non-empty value to mark `is_container = True`. |
| `{PREFIX}ENV_FILE` | unset | Path to an additional env file, inserted between the home-dir files and the local `.env`. |

#### Build metadata

Read by `FoundryContext.from_package()` to build `version_full` and `version_with_vcs_ref`. All
optional; most useful in CI.

| Variable | Default | Description |
|---|---|---|
| `VCS_REF` | read from `.git/HEAD` | Branch name or commit SHA. Falls back to reading `.git/HEAD` when project path is found. |
| `COMMIT_SHA` | `"unknown"` | Full commit SHA; first 7 chars used. |
| `BUILD_DATE` | `"unknown"` | Build date string. |
| `CI_RUN_ID` | `"unknown"` | CI system run ID. |
| `CI_RUN_NUMBER` | `"unknown"` | CI system build number. |
| `BUILDER` | `"uv"` | Build tool name. |

When any of these variables is set, `version_full` gains a `+…` suffix, e.g.
`1.2.3+main-abc1234---run.12345---build.42`.

#### Logging (`{PREFIX}LOG_`)

Settings class: `LogSettings`

| Variable | Default | Description |
|---|---|---|
| `{PREFIX}LOG_LEVEL` | `INFO` | Log level: `CRITICAL`, `ERROR`, `WARNING`, `SUCCESS`, `INFO`, `DEBUG`, or `TRACE`. |
| `{PREFIX}LOG_STDERR_ENABLED` | `true` | Enable logging to stderr. |
| `{PREFIX}LOG_FILE_ENABLED` | `false` | Enable logging to a file. |
| `{PREFIX}LOG_FILE_NAME` | platform log dir | Path to the log file (validated on startup when `FILE_ENABLED` is true). |
| `{PREFIX}LOG_REDIRECT_LOGGING` | `true` | Redirect stdlib `logging` to loguru via `InterceptHandler`. |

#### Sentry (`{PREFIX}SENTRY_`)

Settings class: `SentrySettings`. Sentry is only initialised when `ENABLED=true` **and** `DSN` is
set.

| Variable | Default | Description |
|---|---|---|
| `{PREFIX}SENTRY_ENABLED` | `false` | Enable Sentry error and performance monitoring. |
| `{PREFIX}SENTRY_DSN` | unset | Sentry DSN (must be an HTTPS URL with a valid `ingest.*.sentry.io` domain). |
| `{PREFIX}SENTRY_DEBUG` | `false` | Enable Sentry SDK debug mode. |
| `{PREFIX}SENTRY_SEND_DEFAULT_PII` | `false` | Include personally-identifiable information in events. |
| `{PREFIX}SENTRY_MAX_BREADCRUMBS` | `50` | Maximum breadcrumbs stored per event. |
| `{PREFIX}SENTRY_SAMPLE_RATE` | `1.0` | Error event sample rate (0.0–1.0). |
| `{PREFIX}SENTRY_TRACES_SAMPLE_RATE` | `0.1` | Transaction/trace sample rate. |
| `{PREFIX}SENTRY_PROFILES_SAMPLE_RATE` | `0.1` | Profiler sample rate. |
| `{PREFIX}SENTRY_PROFILE_SESSION_SAMPLE_RATE` | `0.1` | Profile session sample rate. |
| `{PREFIX}SENTRY_PROFILE_LIFECYCLE` | `"trace"` | Profile lifecycle mode: `"trace"` or `"manual"`. |
| `{PREFIX}SENTRY_ENABLE_LOGS` | `true` | Forward log records to Sentry. |

#### Database (`{PREFIX}DB_`)

Settings class: `DatabaseSettings`. Database configuration is only activated when `{PREFIX}DB_URL`
is present (in the environment or in an env file).

| Variable | Required | Default | Description |
|---|---|---|---|
| `{PREFIX}DB_URL` | to activate | — | Full async database connection URL (e.g. `postgresql+asyncpg://user:pass@host/db`). Always access via `DatabaseSettings.get_url()`. |
| `{PREFIX}DB_POOL_SIZE` | no | `10` | SQLAlchemy connection pool size. |
| `{PREFIX}DB_POOL_MAX_OVERFLOW` | no | `10` | Max connections above pool size. |
| `{PREFIX}DB_POOL_TIMEOUT` | no | `30.0` | Seconds to wait for a pool connection. |
| `{PREFIX}DB_NAME` | no | unset | Override the database name in the URL path at runtime. |

Once a context is configured via `set_context()`, all database functions work with no arguments —
the URL and pool settings are read from the context:

```python
from aignostics_foundry_core.database import init_engine, cli_run_with_db, with_engine
Expand All @@ -106,17 +199,6 @@ async def my_job(): ...
async def my_other_job(): ...
```

`FoundryContext.from_package()` activates database configuration automatically
when the following environment variables are present:

| Variable | Required | Description |
|---|---|---|
| `{PREFIX}DB_URL` | yes (to activate) | Full database connection URL |
| `{PREFIX}DB_POOL_SIZE` | no | Connection pool size (default `10`) |
| `{PREFIX}DB_MAX_OVERFLOW` | no | Max pool overflow (default `10`) |
| `{PREFIX}DB_POOL_TIMEOUT` | no | Pool wait timeout in seconds (default `30.0`) |
| `{PREFIX}DB_NAME` | no | Override database name in the URL path |

In tests, construct `DatabaseSettings` directly instead of setting env vars:

```python
Expand All @@ -126,13 +208,23 @@ from tests.conftest import make_context
ctx = make_context(database=DatabaseSettings(_env_prefix="TEST_DB_", url="sqlite+aiosqlite:///test.db"))
```

### Health API
#### Authentication (`{PREFIX}AUTH_`)

```python
from aignostics_foundry_core.health import Health, HealthStatus
Settings class: `AuthSettings`. Both fields are required — no defaults. Only needed when using
`aignostics_foundry_core.api.auth` dependencies.

health = Health(status=HealthStatus.UP)
```
| Variable | Required | Description |
|---|---|---|
| `{PREFIX}AUTH_INTERNAL_ORG_ID` | yes | Auth0 organization ID identifying the internal org (used by `require_internal`). |
| `{PREFIX}AUTH_AUTH0_ROLE_CLAIM` | yes | JWT claim name containing the user's role (e.g. `https://myapp.example.com/roles`). |

#### Console

Read directly from the environment — no settings class.

| Variable | Default | Description |
|---|---|---|
| `{PREFIX}CONSOLE_WIDTH` | auto-detect | Override Rich console width (integer, characters). Defaults to terminal width or 80 in non-TTY environments. |

## Further Reading

Expand Down
Loading