aRustyDev / aRustyDev/mdbook-htmx

docs(adr): ADR-0014: Configuration Schema Versioning

Open
#22 0 comments 0 reactions 1 assignee Claimed by @aRustyDev View on GitHub
documentation
Dominant language
Rust
Stars
0
Forks
1
PR merge metrics
No merged PRs in 30d

Description

# ADR-0014: Configuration Schema Versioning

## Status

Accepted

## Context

The `[output.htmx]` configuration in `book.toml` defines how mdbook-htmx behaves during build. As the project evolves, configuration options will be added, modified, or deprecated. We need a versioning strategy that:

1. Enables backward-compatible changes without breaking existing books
2. Provides clear upgrade paths when breaking changes occur
3. Validates configuration against the correct schema version
4. Allows new features without forcing all users to upgrade

## Decision Drivers

1. **Backward Compatibility** - Existing books should continue working
2. **Clear Upgrades** - Breaking changes must be explicit and documented
3. **Validation** - Catch configuration errors at build time
4. **Discoverability** - IDE autocompletion and documentation
5. **Simplicity** - Don't overcomplicate for authors

## Decision

**Use semantic versioning for configuration schema, with explicit version field and migration tooling.**

### Configuration Version Field

Add an optional `version` field to `[output.htmx]`:

```toml
[output.htmx]
version = "1.0" # Configuration schema version (not HTMX library version)
htmx-version = "2.0.4" # HTMX library version to include
# ... rest of configuration
```

### Version Resolution

| `version` Field | Behavior |
|-----------------|----------|
| Omitted | Use latest compatible (with deprecation warnings) |
| `"1.0"` | Use v1.0 schema, error on unknown fields |
| `"1"` | Use latest v1.x (minor upgrades allowed) |
| `"2.0"` | Use v2.0 schema |

### Schema Evolution Rules

#### Minor Version (1.0 → 1.1)

- Add new optional fields with defaults
- Deprecate fields (warn but accept)
- Never remove or change field semantics

```toml
# v1.0 configuration
[output.htmx]
boost = true

# v1.1 adds new optional field
[output.htmx]
boost = true
preload = true # New in v1.1, defaults to false
```

#### Major Version (1.x → 2.0)

- Remove deprecated fields
- Change field semantics or types
- Rename fields
- Restructure sections

```toml
# v1.x (deprecated)
[output.htmx]
authz.enabled = true

# v2.0 (restructured)
[output.htmx.authorization]
enabled = true
provider = "manifest"
```

## Schema Versioning

### JSON Schema for Validation

```json
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://schemas.arusty.dev/mdbook-htmx/book-config/v1.0.schema.json",
"title": "mdbook-htmx Configuration Schema v1.0",
"description": "Configuration schema for [output.htmx] in book.toml",
"type": "object",
"properties": {
"version": {
"type": "string",
"pattern": "^[0-9]+\\.[0-9]+$",
"description": "Configuration schema version"
},
"htmx-version": {
"type": "string",
"default": "2.0.4",
"description": "HTMX library version to bundle"
},
"boost": {
"type": "boolean",
"default": true,
"description": "Enable hx-boost on body element"
}
},
"additionalProperties": true
}
```

### Version-Specific Schemas

```
schemas/
├── book-config/
│ ├── v1.0.schema.json
│ ├── v1.1.schema.json
│ └── v2.0.schema.json
└── book-config.schema.json # Always latest stable
```

## Implementation

### Rust Configuration Types

```rust
use serde::Deserialize;

#[derive(Debug, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct HtmxConfigV1 {
#[serde(default = "default_version")]
pub version: String,

#[serde(rename = "htmx-version", default = "default_htmx_version")]
pub htmx_version: String,

#[serde(default = "default_boost")]
pub boost: bool,

#[serde(default)]
pub swap_strategy: SwapStrategy,

#[serde(default = "default_target")]
pub target: String,

#[serde(default = "default_push_url")]
pub push_url: bool,

#[serde(rename = "template-engine", default)]
pub template_engine: TemplateEngine,

#[serde(default)]
pub theme: Option,

#[serde(rename = "output-mode", default)]
pub output_mode: OutputMode,

#[serde(default)]
pub navigation: NavigationConfig,

#[serde(default)]
pub authn: AuthnConfig,

#[serde(default)]
pub authz: AuthzConfig,

#[serde(default)]
pub search: SearchConfig,

#[serde(default)]
pub assets: AssetsConfig,

#[serde(default)]
pub scopes: ScopesConfig,
}

fn default_version() -> String { "1.0".to_string() }
fn default_htmx_version() -> String { "2.0.4".to_string() }
fn default_boost() -> bool { true }
fn default_target() -> String { "#content".to_string() }
fn default_push_url() -> bool { true }
```

### Version Detection and Migration

```rust
pub fn load_config(toml_value: &toml::Value) -> Result {
// Extract version field
let version = toml_value
.get("version")
.and_then(|v| v.as_str())
.unwrap_or("1.0");

let (major, minor) = parse_version(version)?;

match major {
1 => {
let config: HtmxConfigV1 = toml_value.clone().try_into()?;
check_deprecations_v1(&config);
Ok(HtmxConfig::V1(config))
}
2 => {
let config: HtmxConfigV2 = toml_value.clone().try_into()?;
Ok(HtmxConfig::V2(config))
}
_ => anyhow::bail!(
"Unsupported configuration version: {}. \
Supported versions: 1.x, 2.x",
version
),
}
}

fn check_deprecations_v1(config: &HtmxConfigV1) {
// Example: auth field renamed to authz in v1.1
if config.auth.is_some() {
log::warn!(
"Configuration field 'auth' is deprecated. \
Use 'authz' instead. See ADR-0008."
);
}
}
```

### CLI Migration Tool

```bash
# Check configuration for issues
$ mdbook-htmx config check
Configuration: book.toml
Version: 1.0
Issues:
- DEPRECATED: 'auth' field should be 'authz' (will be removed in v2.0)
- WARNING: 'theme' path 'my-theme' does not exist

# Migrate to latest version
$ mdbook-htmx config migrate --target 1.1
Migrating configuration from v1.0 to v1.1...
- Renamed 'auth' to 'authz'
- Added default 'scopes.available = ["all"]'
Written: book.toml.new

# Validate against specific schema
$ mdbook-htmx config validate --schema v1.1
Configuration is valid for schema v1.1
```

## Deprecation Policy

1. **Announce** deprecation in release notes
2. **Warn** during build for at least 2 minor versions
3. **Remove** only in major version bump
4. **Provide** migration tool or instructions

### Deprecation Timeline Example

```
v1.0: [auth] introduced
v1.1: [auth] deprecated, [authz] introduced (warn if [auth] used)
v1.2: [auth] still works (warn)
v2.0: [auth] removed (error if present)
```

## Configuration Reference Generation

Generate documentation from JSON Schema:

```bash
# Generate markdown documentation
$ mdbook-htmx docs config > docs/config-reference.md

# Output includes:
# - All fields with types and defaults
# - Deprecation notices
# - Version where field was introduced
# - Links to relevant ADRs
```

## Consequences

### Positive

- Users can lock to specific schema version
- Clear upgrade path with deprecation warnings
- IDE support via JSON Schema
- Validation catches errors early
- Migration tooling eases transitions

### Negative

- Additional complexity in configuration parsing
- Must maintain multiple schema versions
- Version field adds noise to simple configs

### Mitigation

- Version field is optional (defaults to latest compatible)
- Automated migration tools
- Clear deprecation timeline
- Schema versions published to CDN for IDE integration

## Alternatives Considered

### No Versioning

Just evolve configuration organically.

**Rejected** because:
- Breaking changes would silently fail
- No way to validate against known-good schema
- Hard to document which fields work with which versions

### Date-Based Versioning

Use dates like `2024-01` for schema versions.

**Rejected** because:
- Semver is more familiar
- Harder to understand compatibility promises
- Dates don't indicate breaking vs non-breaking

### Per-Field Versioning

Mark each field with its minimum version.

**Rejected** because:
- Overly complex
- Hard to manage dependencies between fields
- Full schema versioning is simpler

## References

- [JSON Schema Versioning](https://json-schema.org/understanding-json-schema/reference/schema.html)
- [Terraform Configuration Versioning](https://developer.hashicorp.com/terraform/language/settings)
- [ADR-0012: MDBook Renderer Trait Implementation](./0012-mdbook-renderer-trait-implementation.md)
- [ADR-0013: Frontmatter Schema and Validation](./0013-frontmatter-schema-and-validation.md)

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.