diff --git a/.dev/15-implementation-todo-plan.md b/.dev/15-implementation-todo-plan.md index 0a8dd69..18e786c 100644 --- a/.dev/15-implementation-todo-plan.md +++ b/.dev/15-implementation-todo-plan.md @@ -1,3 +1,5 @@ +[DONE] + # Implementation Todo Plan: Semantic Versioning Bump Behavior ## Overview diff --git a/.dev/17-schema-based-bump-implementation-plan.md b/.dev/17-schema-based-bump-implementation-plan.md new file mode 100644 index 0000000..d9406f2 --- /dev/null +++ b/.dev/17-schema-based-bump-implementation-plan.md @@ -0,0 +1,858 @@ +# Schema-Based Bump Implementation Plan + +## Overview + +This document outlines the implementation plan for schema-based bump functionality, which addresses: + +1. **Doc 13**: Schema-based bump functionality - higher-level bumping system +2. **Doc 16**: Reset logic schema component issue - proper handling of schema components during bumping + +## Current State vs Ideal State + +### Current State (Problems) + +#### 1. Limited Bumping Capability + +```bash +# Current: Only field-based bumps +zerv version --bump-major # Bumps vars.major +zerv version --bump-minor # Bumps vars.minor + +# Missing: Schema-based bumps +zerv version --bump-core 0 1 # Bump first component of core schema +zerv version --bump-extra-core 2 3 # Bump third component of extra_core schema +zerv version --bump-build 1 5 # Bump second component of build schema +``` + +#### 2. Incomplete Reset Logic + +```bash +# Input: 1.5.2-rc.1+build.456 +# Command: --bump-major +# Expected: 2.0.0 (all lower precedence removed) +# Actual: 2.0.0+build.456 (build metadata persists - BUG!) +``` + +**Root Cause**: Schema components are not reset when their underlying `ZervVars` fields are reset. + +### Ideal State (Goals) + +#### 1. Schema-Based Bumping + +- Bump any schema component by position (index) +- Automatically resolve component type (VarField, Integer, String, Timestamp) +- Apply appropriate reset behavior based on schema precedence +- Handle complex scenarios with multiple index bumps + +#### 2. Complete Reset Logic + +- Reset both ZervVars fields AND schema components +- Schema-aware component filtering +- Preserve static components when appropriate + +## Ideal Architecture + +### Core Data Structures + +```rust +use indexmap::IndexMap; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Precedence { + // Field-based precedence + Epoch, + Major, + Minor, + Patch, + PreReleaseLabel, + PreReleaseNum, + Post, + Dev, + + // Schema-based precedence + Core, + ExtraCore, + Build, +} + +#[derive(Debug, Clone)] +pub struct PrecedenceOrder { + order: IndexMap, +} + +impl PrecedenceOrder { + pub fn from_precedences(precedences: Vec) -> Self { + let order = precedences.into_iter() + .map(|p| (p, ())) + .collect(); + Self { order } + } + + pub fn pep440_based() -> Self { + Self::from_precedences(vec![ + Precedence::Epoch, + Precedence::Major, + Precedence::Minor, + Precedence::Patch, + Precedence::Core, + Precedence::PreReleaseLabel, + Precedence::PreReleaseNum, + Precedence::Post, + Precedence::Dev, + Precedence::ExtraCore, + Precedence::Build, + ]) + } + + /// O(1) get precedence by index + pub fn get_precedence(&self, index: usize) -> Option<&Precedence> { + self.order.get_index(index).map(|(precedence, _)| precedence) + } + + /// O(1) get index by precedence + pub fn get_index(&self, precedence: &Precedence) -> Option { + self.order.get_index_of(precedence) + } + + pub fn len(&self) -> usize { + self.order.len() + } + + pub fn iter(&self) -> impl Iterator { + self.order.keys() + } + + /// Get field precedence names in order (for backward compatibility with BumpType::PRECEDENCE_NAMES) + pub fn field_precedence_names(&self) -> &[&'static str] { + // Return the field-based precedence names in order + // This maintains compatibility with existing BumpType logic + &[ + "epoch", "major", "minor", "patch", + "pre_release_label", "pre_release_num", "post", "dev" + ] + } +} + +#[derive(Debug, Clone)] +pub struct ZervSchema { + pub core: Vec, + pub extra_core: Vec, + pub build: Vec, + precedence_order: PrecedenceOrder, // Single source of truth +} + +impl ZervSchema { + pub fn new( + core: Vec, + extra_core: Vec, + build: Vec, + precedence_order: PrecedenceOrder + ) -> Self { + Self { core, extra_core, build, precedence_order } + } + +} +``` + +### CLI Interface + +```bash +# Schema-based bump arguments (relative modifications) +--bump-core [=] [[=] ...] +--bump-extra-core [=] [[=] ...] +--bump-build [=] [[=] ...] + +# Schema-based override arguments (absolute values) +--core = [= ...] +--extra-core = [= ...] +--build = [= ...] +``` + +**Value Behavior:** + +- **With value**: `--bump-core 0=5` → bump core[0] by 5 +- **Without value**: `--bump-core 0` → bump core[0] by 1 (default) +- **Mixed usage**: `--bump-core 0 --bump-core 1=5` → bump core[0] by 1, core[1] by 5 + +**Index and Value Constraints:** + +- **Indices**: Positive integers (0, 1, 2, 3, ...) and negative integers (-1, -2, -3, ...) for counting from end +- **Values**: Only positive values for numeric components - negative bump values not supported +- **String values**: Any string value allowed for String components + +**Negative Index Behavior:** + +- `-1` → last component in schema +- `-2` → second-to-last component in schema +- `-3` → third-to-last component in schema +- Example: Schema `[major, minor, patch]` → `-1` = `patch`, `-2` = `minor`, `-3` = `major` + +**Value Parameter Types:** + +- **Numeric values**: For `VarField` and `Integer` components (e.g., `--bump-core 0=1`) +- **String values**: For `String` components (e.g., `--bump-core 1=release`) + +**String Component Bumping:** + +- String values override the existing string content in `String("xxxxxx")` components +- Example: `--bump-core 1=release` will change `String("snapshot")` to `String("release")` + +**Multiple Bump Examples:** + +```bash +# Multiple bumps with explicit values +zerv version --bump-core 1=1 --bump-core 2=3 +zerv version --bump-core 1=1 2=3 + +# Multiple bumps with default values +zerv version --bump-core 1 --bump-core 2 +zerv version --bump-core 1 2 + +# Mixed explicit and default values +zerv version --bump-core 1 --bump-core 2=5 +zerv version --bump-core 1 2=5 + +# Negative indices (counting from end) +zerv version --bump-core -1 # bump last component +zerv version --bump-core 0 -1 # bump first and last components +zerv version --bump-core -2=5 # bump second-to-last by 5 + +# Mixed types +zerv version --bump-core 1=5 --bump-core 2=release --bump-core 3=10 +``` + +### Clap Implementation + +**Argument Definition:** + +```rust +#[derive(Parser)] +struct VersionArgs { + // Schema-based bumps using key[=value] syntax + #[arg(long, num_args = 1.., value_names = ["INDEX[=VALUE]"])] + bump_core: Vec, + + #[arg(long, num_args = 1.., value_names = ["INDEX[=VALUE]"])] + bump_extra_core: Vec, + + #[arg(long, num_args = 1.., value_names = ["INDEX[=VALUE]"])] + bump_build: Vec, +} +``` + +**Parsing Logic:** + +```rust +// Process bump_core Vec into (index, value) pairs +// Examples: +// ["1=5", "2=release"] -> [(1,"5"), (2,"release")] +// ["1", "2=5"] -> [(1,"1"), (2,"5")] +fn parse_bump_spec(spec: &str, schema_len: usize) -> Result<(usize, String), ZervError> { + if let Some((index_str, value)) = spec.split_once('=') { + // Explicit value: "1=5" -> (1, "5") + let index = parse_index(index_str, schema_len)?; + let value = parse_positive_value(value)?; + Ok((index, value)) + } else { + // Default value: "1" -> (1, "1") + let index = parse_index(spec, schema_len)?; + Ok((index, "1".to_string())) + } +} + +fn parse_index(index_str: &str, schema_len: usize) -> Result { + let index: i32 = index_str.parse() + .map_err(|_| ZervError::InvalidBumpTarget("Invalid index".to_string()))?; + + if index >= 0 { + // Positive index: 0, 1, 2, ... + let idx = index as usize; + if idx >= schema_len { + return Err(ZervError::InvalidBumpTarget(format!( + "Index {} out of bounds for schema of length {}", idx, schema_len + ))); + } + Ok(idx) + } else { + // Negative index: -1, -2, -3, ... (count from end) + let idx = (schema_len as i32 + index) as usize; + if idx >= schema_len { + return Err(ZervError::InvalidBumpTarget(format!( + "Negative index {} out of bounds for schema of length {}", index, schema_len + ))); + } + Ok(idx) + } +} + +fn parse_positive_value(value_str: &str) -> Result { + // For numeric values, ensure they're positive + if let Ok(num) = value_str.parse::() { + if num < 0 { + return Err(ZervError::InvalidBumpTarget("Negative bump values not supported".to_string())); + } + } + + Ok(value_str.to_string()) +} + +// Process all bump specs +let schema_len = self.schema.core.len(); +for spec in args.bump_core { + let (index, value) = parse_bump_spec(spec, schema_len)?; + bump_schema_component("core", index, value)?; +} +``` + +**CLI Processing Integration:** + +```rust +// In main version processing pipeline +impl Zerv { + pub fn process_schema_bumps( + &mut self, + bump_core: &[String], + bump_extra_core: &[String], + bump_build: &[String], + ) -> Result<(), ZervError> { + // Process core schema bumps + for spec in bump_core { + let (index, value) = parse_bump_spec(spec)?; + self.bump_schema_component("core", index, value)?; + } + + // Process extra_core schema bumps + for spec in bump_extra_core { + let (index, value) = parse_bump_spec(spec)?; + self.bump_schema_component("extra_core", index, value)?; + } + + // Process build schema bumps + for spec in bump_build { + let (index, value) = parse_bump_spec(spec)?; + self.bump_schema_component("build", index, value)?; + } + + Ok(()) + } +} +``` + +**Benefits:** + +- **No ambiguity**: Clear separation of index and value +- **Familiar syntax**: Users know `key=value` pattern from many CLI tools +- **Easy parsing**: Simple split on `=` character +- **Multiple bumps**: Natural support for multiple `--bump-core` flags +- **Default values**: Convenient `--bump-core 0` syntax for common case +- **Flexible**: Supports both explicit and default values in same command +- **Negative indices**: Python-style negative indexing for counting from end +- **Dynamic schemas**: Works with schemas of any length using negative indices + +### RON Schema Support + +```ron +SchemaConfig( + core: [ + VarField(field: "major"), + VarField(field: "minor"), + VarField(field: "patch"), + ], + extra_core: [ + VarField(field: "pre_release"), + ], + build: [ + VarField(field: "branch"), + ], + precedence_order: [ // Optional - uses default if not specified + Epoch, + Major, + Minor, + Patch, + Core, + PreReleaseLabel, + PreReleaseNum, + Post, + Dev, + ExtraCore, + Build + ] +) +``` + +## Unified Reset Logic Implementation (Doc 16) + +### Architecture Decision: Option 1a + 2a + 3a + +**Chosen Approach:** + +- **1a**: Method on `Zerv` (not `ZervVars`) - coordinates both vars and schema reset +- **2a**: Loop through field-based precedence using Precedence Order +- **3a**: Remove ALL component types (VarField, String, Integer, Timestamp) with lower precedence +- **Constraint**: Timestamp components (`VarTimestamp`) cannot be bumped - will raise error if attempted + +### Implementation Details + +```rust +impl Zerv { + /// Reset all components (vars + schema) with lower precedence than the given component + pub fn reset_lower_precedence_components(&mut self, component: &str) -> Result<(), ZervError> { + let current_precedence = BumpType::precedence_from_str(component); + + // 1. Reset ZervVars fields with lower precedence + for (index, &name) in self.schema.precedence_order.field_precedence_names().iter().enumerate() { + if index > current_precedence { + self.vars.reset_component_by_name(name); + } + } + + // 2. Determine which fields are reset + let reset_fields: HashSet<&str> = self.schema.precedence_order.field_precedence_names() + .iter() + .skip(current_precedence + 1) + .copied() + .collect(); + + // 3. Filter each schema section + self.schema.core = Self::filter_section(&self.schema.core, &reset_fields); + self.schema.extra_core = Self::filter_section(&self.schema.extra_core, &reset_fields); + self.schema.build = Self::filter_section(&self.schema.build, &reset_fields); + + Ok(()) + } + + /// Bump a schema component by index with validation + /// Parses key=value format: "1=5" -> index=1, value="5" + pub fn bump_schema_component(&mut self, section: &str, index: usize, value: String) -> Result<(), ZervError> { + let components = match section { + "core" => &self.schema.core, + "extra_core" => &self.schema.extra_core, + "build" => &self.schema.build, + _ => return Err(ZervError::InvalidBumpTarget(format!("Unknown schema section: {}", section))), + }; + + let component = components.get(index) + .ok_or_else(|| ZervError::InvalidBumpTarget(format!("Index {} out of bounds for {} section", index, section)))?; + + match component { + Component::VarField(field_name) => { + // Validate field can be bumped + if !self.schema.precedence_order.field_precedence_names().contains(&field_name.as_str()) { + return Err(ZervError::InvalidBumpTarget(format!("Cannot bump custom field: {}", field_name))); + } + // Parse value as numeric for VarField components + let numeric_value = value.parse::() + .map_err(|_| ZervError::InvalidBumpTarget(format!("Expected numeric value for VarField component, got: {}", value)))?; + // Bump the field and reset lower precedence components + self.bump_field_and_reset(field_name, numeric_value)?; + } + Component::String(_) => { + // String components can be bumped by replacing the string value + // The value parameter is already a string that replaces the current string + // Implementation: Replace String(current_value) with String(new_value) + return Err(ZervError::NotImplemented("String component bumping not yet implemented".to_string())); + } + Component::Integer(_) => { + // Integer components can be bumped (e.g., build numbers, patch versions) + // Parse value as numeric for Integer components + let numeric_value = value.parse::() + .map_err(|_| ZervError::InvalidBumpTarget(format!("Expected numeric value for Integer component, got: {}", value)))?; + return Err(ZervError::NotImplemented("Integer component bumping not yet implemented".to_string())); + } + Component::VarTimestamp(_) => { + return Err(ZervError::InvalidBumpTarget("Cannot bump timestamp component - timestamps are generated dynamically".to_string())); + } + } + + Ok(()) + } + + /// Filter a schema section: + /// - If section contains ANY reset VarField, clear ENTIRE section (aggressive per 3a) + /// - Otherwise, keep section as-is + fn filter_section(components: &[Component], reset_fields: &HashSet<&str>) -> Vec { + // Check if section contains any reset fields + let has_reset_field = components.iter().any(|comp| { + matches!(comp, Component::VarField(field_name) if reset_fields.contains(field_name.as_str())) + }); + + if has_reset_field { + // Clear entire section (aggressive removal per 3a) + Vec::new() + } else { + // Keep section unchanged + components.to_vec() + } + } +} +``` + +### Example Behavior + +**Input Schema:** + +```ron +core: [VarField("major"), String("."), VarField("minor"), String("."), VarField("patch")] +extra_core: [VarField("pre_release")] +build: [VarField("branch"), String("."), VarField("commit_hash_short")] +``` + +**Bump Major (precedence 1):** + +- Reset fields: `minor`, `patch`, `pre_release_label`, `pre_release_num`, `post`, `dev` +- Core section: Contains `minor` and `patch` → **Clear entire section** → `[VarField("major")]` +- ExtraCore section: Contains `pre_release` → **Clear entire section** → `[]` +- Build section: No reset fields → **Keep unchanged** → `[VarField("branch"), String("."), VarField("commit_hash_short")]` + +**Result:** `2.0.0+branch.abc123` (build metadata preserved because no reset fields in build section) + +**Bump Minor (precedence 2):** + +- Reset fields: `patch`, `pre_release_label`, `pre_release_num`, `post`, `dev` +- Core section: Contains `patch` → **Clear entire section** → `[VarField("major"), String("."), VarField("minor")]` +- ExtraCore section: Contains `pre_release` → **Clear entire section** → `[]` +- Build section: No reset fields → **Keep unchanged** → `[VarField("branch"), String("."), VarField("commit_hash_short")]` + +**Result:** `1.3.0+branch.abc123` + +### Component Bump Validation + +**Allowed Components:** + +- `VarField` with field names in Precedence Order (major, minor, patch, etc.) - uses numeric values +- `String` - Static string literals (e.g., version labels, build identifiers) - uses string values +- `Integer` - Static integer literals (e.g., build numbers, patch versions) - uses numeric values + +**Forbidden Components:** + +- `VarTimestamp` - Timestamps are generated dynamically, not bumped +- `VarField` with custom field names (e.g., `custom.build_id`) + +**String Component Bumping:** + +- String components can be bumped by providing a string value +- The string value replaces the existing string content +- Example: `String("alpha")` becomes `String("beta")` when bumped with `"beta"` + +**Error Types:** + +```rust +// New error variants for ZervError +InvalidBumpTarget(String) // "Cannot bump timestamp component - timestamps are generated dynamically" +NotImplemented(String) // "Integer component bumping not yet implemented" +``` + +**Example Usage Scenarios:** + +```bash +# Bumping VarField component (explicit value) +zerv version --bump-core 0=1 # If core[0] is VarField("major") - bumps major by 1 + +# Bumping VarField component (default value) +zerv version --bump-core 0 # If core[0] is VarField("major") - bumps major by 1 (default) + +# Bumping String component (string value) +zerv version --bump-core 1=release # If core[1] is String("snapshot") - changes to String("release") + +# Bumping Integer component (explicit value) +zerv version --bump-core 2=5 # If core[2] is Integer(42) - bumps by 5 to Integer(47) + +# Bumping Integer component (default value) +zerv version --bump-core 2 # If core[2] is Integer(42) - bumps by 1 to Integer(43) + +# Multiple bumps (mixed explicit and default) +zerv version --bump-core 0 --bump-core 1=release --bump-core 2=5 +``` + +**Example Error Scenarios:** + +```bash +# Attempting to bump timestamp component +zerv version --bump-core 2=1 # If core[2] is VarTimestamp("YYYY") +# Error: Cannot bump timestamp component - timestamps are generated dynamically + +# Attempting to bump integer component (not yet implemented) +zerv version --bump-core 3=1 # If core[3] is Integer(42) +# Error: Integer component bumping not yet implemented + +# Attempting to bump custom field +zerv version --bump-build 0=1 # If build[0] is VarField("custom.build_id") +# Error: Cannot bump custom field: custom.build_id + +# Wrong value type for component +zerv version --bump-core 0=release # VarField expects numeric +# Error: Expected numeric value for VarField component, got: release + +zerv version --bump-core 1=123 # String expects string +# Error: Expected string value for String component, got: 123 + +# Negative indices (valid) +zerv version --bump-core -1 # bump last component +zerv version --bump-core 0 -1 # bump first and last components + +# Negative index out of bounds +zerv version --bump-core -5 # if schema only has 3 components +# Error: Negative index -5 out of bounds for schema of length 3 + +# Negative bump values not supported +zerv version --bump-core 0=-5 # Negative bump value +# Error: Negative bump values not supported +``` + +## Implementation Roadmap + +### Phase 1: Core Infrastructure (Week 1) + +**Goal**: Establish the foundation for schema-based bumping + +**Tasks**: + +- [x] Add `Precedence` enum +- [x] Update `ZervSchema` to use `IndexMap` +- [x] Add `pep440_based_precedence_order()` method +- [x] Update `SchemaConfig` for RON parsing with default precedence +- [x] Add CLI argument parsing for schema-based flags + +**Files to Create/Modify**: + +- `src/version/zerv/bump/precedence.rs` - New Precedence enum +- `src/version/zerv/schema.rs` - Update ZervSchema +- `src/version/zerv/schema_config.rs` - Update for IndexMap +- `src/cli/version/args.rs` - Add schema-based arguments + +**Success Criteria**: + +- [x] Can create ZervSchema with custom precedence +- [x] RON parsing works with and without precedence_order +- [x] CLI can parse schema-based arguments + +**Status**: ✅ **COMPLETED** - All tasks and success criteria met + +**Verification**: + +- ✅ 1712 tests passing +- ✅ `make lint` passes with no warnings +- ✅ CLI arguments parse and validate correctly +- ✅ ZervSchema creation with custom precedence works +- ✅ RON parsing with/without precedence_order works +- ✅ Schema-based bump arguments (`--bump-core`, `--bump-extra-core`, `--bump-build`) implemented + +### Phase 2: Schema-Based Bump Logic (Week 2) + +**Goal**: Implement the core bumping functionality with `key=value` syntax + +**Tasks**: + +- [x] Add `SchemaBump` variant to `BumpType` enum +- [x] Implement `bump_schema_component()` method (basic version) +- [x] Add component type resolution logic (VarField, String, Integer) +- [x] Implement precedence-based sorting +- [x] Add error handling for invalid operations +- [ ] Add `key=value` parsing logic for CLI arguments +- [ ] Update CLI argument definitions for `--bump-core`, `--bump-extra-core`, `--bump-build` +- [ ] Update `bump_schema_component()` to handle `key=value` format + +**Files to Create/Modify**: + +- `src/version/zerv/bump/types.rs` - Add SchemaBump variant ✅ +- `src/version/zerv/bump/schema.rs` - New schema bump logic ✅ +- `src/version/zerv/bump/mod.rs` - Integrate schema bumps ✅ +- `src/cli/version/args/bumps.rs` - Add `key=value` parsing for schema bumps +- `src/cli/version/args/tests/bumps_tests.rs` - Add tests for `key=value` syntax + +**Success Criteria**: + +- [x] Can bump VarField components (basic implementation) +- [x] Can bump String components (basic implementation) +- [x] Can bump Integer components (basic implementation) +- [x] Appropriate errors for unsupported components +- [x] Precedence-based processing works +- [ ] `key=value` parsing works correctly +- [ ] Multiple `--bump-core` flags work as expected +- [ ] CLI integration with `key=value` syntax + +**Status**: 🔄 **IN PROGRESS** - Core functionality implemented, CLI integration pending + +**Current Implementation**: + +- ✅ Basic schema-based bumping functionality implemented +- ✅ VarField, String, Integer component support +- ✅ Error handling for invalid operations +- ✅ Precedence-based processing and reset logic integrated +- ⏳ CLI `key=value` parsing not yet implemented +- ⏳ CLI argument definitions need updating + +### Phase 3: Reset Logic Enhancement (Week 3) + +**Goal**: Fix Doc 16 - complete reset logic with unified Zerv method + +**Tasks**: + +- [ ] Move `reset_lower_precedence_components()` from `ZervVars` to `Zerv` impl +- [ ] Implement section-based schema filtering (aggressive removal per Option 3a) +- [ ] Add `filter_section()` helper method for schema component removal +- [ ] Update call sites to use `zerv.reset_lower_precedence_components()` +- [ ] Update tests for unified reset behavior + +**Files to Create/Modify**: + +- `src/version/zerv/bump/reset.rs` - Move method to Zerv impl, add schema filtering +- `src/version/zerv/bump/vars_primary.rs` - Update call sites +- `src/version/zerv/core.rs` - Add Component imports if needed + +**Success Criteria**: + +- [ ] `Zerv::reset_lower_precedence_components()` resets both vars and schema +- [ ] Sections with reset fields are completely cleared (aggressive per 3a) +- [ ] Sections without reset fields are preserved +- [ ] Doc 16 issue is resolved (build metadata removed when appropriate) + +### Phase 4: Integration and Testing (Week 4) + +**Goal**: Complete integration and ensure reliability + +**Tasks**: + +- [ ] Integrate schema-based bumps into main processing loop +- [ ] Add conflict detection and validation +- [ ] Write comprehensive tests +- [ ] Test end-to-end scenarios + +**Files to Create/Modify**: + +- `src/version/zerv/bump/mod.rs` - Main integration +- `tests/integration_tests/` - Add schema bump tests + +**Success Criteria**: + +- [ ] Schema-based bumps work in CLI +- [ ] All tests pass +- [ ] No regressions in existing functionality + +### Phase 5: Documentation and Polish (Week 5) + +**Goal**: Complete the feature with proper documentation + +**Tasks**: + +- [ ] Update CLI help text +- [ ] Update README with examples +- [ ] Add migration guide +- [ ] Polish error messages + +**Success Criteria**: + +- [ ] Complete documentation +- [ ] Clear error messages +- [ ] User-friendly examples + +## Key Design Decisions + +### 1. Unified Reset Logic (Doc 16 Solution) + +**Decision**: Move `reset_lower_precedence_components()` to `Zerv` and implement aggressive section-based filtering +**Rationale**: + +- Zerv owns both schema and vars - natural place for coordinated reset logic +- Aggressive removal (Option 3a) ensures semantic correctness +- Example: Bumping major in `1.5.2-rc.1+build.456` yields `2.0.0`, not `2.0.0+build` + +**Implementation Approach**: + +- Loop through field-based precedence using Precedence Order from schema +- Reset ZervVars fields with lower precedence +- Clear entire schema sections that contain reset fields (aggressive per 3a) +- Preserve sections without reset fields + +### 2. IndexMap for Precedence Order + +**Decision**: Use `IndexMap` instead of `Vec` +**Rationale**: Provides O(1) bidirectional lookup, guarantees no duplicates, maintains order + +### 3. Explicit Precedence in Constructor + +**Decision**: `ZervSchema::new()` requires explicit precedence_order parameter +**Rationale**: Makes precedence explicit, allows custom precedence, uses `pep440_based_precedence_order()` for defaults + +### 4. RON with Default Precedence + +**Decision**: Use `#[serde(default)]` for precedence_order in RON +**Rationale**: Backward compatible, no Option complexity, clear default behavior + +### 5. Component Type Resolution + +**Decision**: Resolve component type at bump time with validation +**Rationale**: Flexible, handles different component types appropriately, clear error messages + +### 6. Component Bump Constraints + +**Decision**: Different component types have different bump constraints +**Rationale**: + +- `VarField`: Can be bumped if field name is in Precedence Order +- `String`/`Integer`: Can be bumped (useful for version labels, build numbers) +- `VarTimestamp`: Cannot be bumped (dynamic time-based values) +- Custom `VarField`: Cannot be bumped (not in Precedence Order) + +**Error Handling**: + +- `ZervError::InvalidBumpTarget` for forbidden components (timestamps, custom fields) +- `ZervError::NotImplemented` for not-yet-implemented components (strings, integers) + +## Benefits + +### 1. Solves Doc 13: Schema-Based Bumping + +- ✅ Bump any schema component by position +- ✅ Works with any schema configuration +- ✅ Automatically adapts to schema changes +- ✅ No hardcoded field assumptions + +### 2. Solves Doc 16: Reset Logic Issue + +- ✅ Unified `Zerv::reset_lower_precedence_components()` method +- ✅ Aggressive section-based filtering removes entire sections with reset fields +- ✅ Schema components are properly reset alongside vars +- ✅ Build metadata is removed when appropriate (e.g., major bump clears build section) +- ✅ Semantic versioning compliance maintained + +### 3. Flexible and Extensible + +- ✅ Works across all schema parts (core, extra_core, build) +- ✅ Type-safe component resolution +- ✅ Extensible to new schema structures +- ✅ Backwards compatible with existing field-based bumps + +### 4. User-Friendly + +- ✅ Intuitive CLI syntax +- ✅ Clear error messages +- ✅ Supports complex scenarios +- ✅ Deterministic behavior + +## Success Metrics + +### Functional Requirements + +- [ ] Can bump schema components by position +- [ ] Can bump VarField components with numeric values +- [ ] Can bump String components with string values +- [ ] Can bump Integer components with numeric values +- [ ] Component type resolution works correctly +- [ ] Reset logic handles schema components +- [ ] CLI arguments parse correctly +- [ ] Doc 16 issue is resolved + +### Non-Functional Requirements + +- [ ] Performance is not degraded +- [ ] Error messages are clear +- [ ] Documentation is complete +- [ ] Tests provide good coverage +- [ ] Backwards compatible with existing functionality + +## Conclusion + +This implementation plan provides a clear path from the current state to the ideal state of schema-based bumping. The phased approach ensures that each step builds on the previous one, with clear success criteria and measurable outcomes. + +The key insight is that schema-based bumping and proper reset logic are two sides of the same coin - both require understanding the precedence relationship between schema components and their underlying data structures. diff --git a/.github/workflows/ci-pre-commit.yml b/.github/workflows/ci-pre-commit.yml index 7b0459d..1f0d9e4 100644 --- a/.github/workflows/ci-pre-commit.yml +++ b/.github/workflows/ci-pre-commit.yml @@ -24,7 +24,7 @@ jobs: - uses: dtolnay/rust-toolchain@6d653acede28d24f02e3cd41383119e8b1b35921 # stable with: - toolchain: stable + toolchain: nightly components: rustfmt, clippy - uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd #v3.0.1 diff --git a/.kiro/specs/version-command-complete/design.md b/.kiro/specs/version-command-complete/design.md deleted file mode 100644 index c2de8a2..0000000 --- a/.kiro/specs/version-command-complete/design.md +++ /dev/null @@ -1,566 +0,0 @@ -# Design Document - -## Overview - -This design document outlines the implementation approach for enhancing the Zerv version command to support comprehensive input sources, VCS overrides, enhanced error handling, and Zerv RON piping workflows. The design builds upon the existing architecture while adding significant new capabilities for advanced use cases. - -## Architecture - -### High-Level Flow - -``` -Input Sources → Validation/Format Check → Data Processing → Schema Application → Output Formatting - ↓ ↓ ↓ ↓ ↓ - Git/Stdin → Parse Version Objects → Override Merge → Zerv Transform → PEP440/SemVer/RON - (PEP440/SemVer from tags) -``` - -**Key Processing Points:** - -- **Git Source**: Tag versions are parsed into PEP440 or SemVer objects during validation -- **Tag Override**: `--tag-version` values are parsed into appropriate version objects -- **Stdin Source**: Zerv RON is parsed and validated for structure -- **Override Merge**: VCS data is merged with CLI overrides -- **Zerv Transform**: All inputs are normalized to Zerv internal representation - -### Component Interaction - -```mermaid -graph TD - A[CLI Parser] --> B[Input Source Handler] - B --> C{Source Type} - C -->|git| D[Git VCS Handler] - C -->|stdin| E[Stdin RON Parser] - D --> F[VCS Override Processor] - E --> F - F --> G[Zerv Object Builder] - G --> H[Output Format Handler] - H --> I[Result Display] - - J[Error Handler] --> K[User-Friendly Messages] - D -.-> J - E -.-> J - F -.-> J - G -.-> J - H -.-> J -``` - -## Version Object Parsing Strategy - -During the validation/format check phase, version strings (from git tags or `--tag-version` overrides) are parsed into structured version objects: - -1. **Auto-Detection**: Try SemVer parsing first, fall back to PEP440 -2. **Validation**: Ensure version strings conform to format specifications -3. **Normalization**: Convert to internal representation for processing -4. **Error Handling**: Provide clear messages for invalid version formats - -```rust -pub enum VersionObject { - SemVer(SemVer), - PEP440(PEP440), -} - -impl VersionObject { - pub fn parse_auto(version_str: &str) -> Result { - // Try SemVer first - if let Ok(semver) = SemVer::from_str(version_str) { - return Ok(VersionObject::SemVer(semver)); - } - - // Fall back to PEP440 - if let Ok(pep440) = PEP440::from_str(version_str) { - return Ok(VersionObject::PEP440(pep440)); - } - - Err(ZervError::InvalidVersion(format!( - "Version '{}' is not valid SemVer or PEP440 format", version_str - ))) - } -} -``` - -## Components and Interfaces - -### 1. Enhanced CLI Parser - -**Location:** `src/cli/version.rs` - -**New Fields:** - -```rust -#[derive(Parser)] -pub struct VersionArgs { - // Existing fields - pub version: Option, - pub source: String, - pub schema: Option, - pub schema_ron: Option, - pub output_format: String, - - // Input format for version parsing (semver, pep440, zerv) - #[arg(long, default_value = "semver")] - pub input_format: String, - - // VCS override options - #[arg(long)] - pub tag_version: Option, - #[arg(long)] - pub distance: Option, - #[arg(long)] - pub dirty: Option, - #[arg(long)] - pub clean: bool, - #[arg(long)] - pub current_branch: Option, - #[arg(long)] - pub commit_hash: Option, - - // Output options - #[arg(long)] - pub output_template: Option, - #[arg(long)] - pub output_prefix: Option, -} -``` - -### 2. Fuzzy Boolean Parser - -**Location:** `src/cli/utils/fuzzy_bool.rs` (new file) - -```rust -#[derive(Debug, Clone, PartialEq)] -pub struct FuzzyBool(pub bool); - -impl FromStr for FuzzyBool { - type Err = String; - - fn from_str(s: &str) -> Result { - match s.to_lowercase().as_str() { - "true" | "t" | "yes" | "y" | "1" | "on" => Ok(FuzzyBool(true)), - "false" | "f" | "no" | "n" | "0" | "off" => Ok(FuzzyBool(false)), - _ => Err(format!("Invalid boolean value: '{}'. Supported values: true/false, t/f, yes/no, y/n, 1/0, on/off", s)) - } - } -} -``` - -### 3. Input Source Handler - -**Location:** `src/cli/utils/source_handler.rs` (new file) - -```rust -pub enum InputSource { - Git(GitVcsData), - Stdin(ZervRonData), -} - -pub struct InputSourceHandler; - -impl InputSourceHandler { - pub fn resolve_input_source(args: &VersionArgs, work_dir: &Path) -> Result { - match args.source.as_str() { - "git" => { - let vcs_data = Self::get_git_data(work_dir)?; - Ok(InputSource::Git(vcs_data)) - } - "stdin" => { - let ron_data = Self::read_stdin_ron()?; - Ok(InputSource::Stdin(ron_data)) - } - source => Err(ZervError::UnknownSource(source.to_string())) - } - } - - fn read_stdin_ron() -> Result { - // Implementation for reading and validating Zerv RON from stdin - } -} -``` - -### 4. VCS Override Processor - -**Location:** `src/cli/utils/vcs_override.rs` (new file) - -```rust -pub struct VcsOverrideProcessor; - -impl VcsOverrideProcessor { - pub fn apply_overrides( - mut vcs_data: VcsData, - args: &VersionArgs - ) -> Result { - // Validate conflicting options - Self::validate_override_conflicts(args)?; - - // Apply individual overrides - if let Some(tag) = &args.tag_version { - vcs_data.tag_version = Some(tag.clone()); - } - - if let Some(distance) = args.distance { - vcs_data.distance = distance; - } - - if let Some(dirty) = &args.dirty { - vcs_data.is_dirty = dirty.0; - } - - if args.clean { - vcs_data.distance = 0; - vcs_data.is_dirty = false; - } - - // Apply other overrides... - - Ok(vcs_data) - } - - fn validate_override_conflicts(args: &VersionArgs) -> Result<()> { - if args.clean && (args.distance.is_some() || args.dirty.is_some()) { - return Err(ZervError::ConflictingOptions( - "Cannot use --clean with --distance or --dirty (conflicting options)".to_string() - )); - } - Ok(()) - } -} -``` - -### 5. Input Format Handler - -**Location:** `src/cli/utils/format_handler.rs` (new file) - -```rust -pub struct InputFormatHandler; - -impl InputFormatHandler { - pub fn parse_version_string(version_str: &str, input_format: &str) -> Result { - match input_format { - "semver" => { - SemVer::from_str(version_str) - .map(VersionObject::SemVer) - .map_err(|e| ZervError::InvalidFormat( - format!("Invalid SemVer format: {}", e) - )) - } - "pep440" => { - PEP440::from_str(version_str) - .map(VersionObject::PEP440) - .map_err(|e| ZervError::InvalidFormat( - format!("Invalid PEP440 format: {}", e) - )) - } - "auto" => { - // Auto-detection fallback - VersionObject::parse_auto(version_str) - } - _ => Err(ZervError::UnknownFormat(input_format.to_string())) - } - } - - pub fn parse_stdin(input_format: &str) -> Result { - let mut input = String::new(); - std::io::stdin().read_to_string(&mut input) - .map_err(|_| ZervError::StdinError("No input provided via stdin".to_string()))?; - - if input.trim().is_empty() { - return Err(ZervError::StdinError("No input provided via stdin".to_string())); - } - - match input_format { - "zerv" => { - // Parse as Zerv RON - ron::from_str::(&input) - .map_err(|e| ZervError::InvalidFormat( - format!("Invalid Zerv RON format: {}", e) - )) - } - "semver" | "pep440" => { - // Error: stdin should be Zerv RON when using these formats - Err(ZervError::StdinError( - format!("When using --source stdin with --input-format {}, stdin must contain Zerv RON format. Use --input-format zerv or provide version via --tag-version instead.", input_format) - )) - } - _ => Err(ZervError::UnknownFormat(input_format.to_string())) - } - } -} - -pub enum VersionObject { - SemVer(SemVer), - PEP440(PEP440), -} - -impl VersionObject { - // Auto-detection for backward compatibility - pub fn parse_auto(version_str: &str) -> Result { - if let Ok(semver) = SemVer::from_str(version_str) { - return Ok(VersionObject::SemVer(semver)); - } - - if let Ok(pep440) = PEP440::from_str(version_str) { - return Ok(VersionObject::PEP440(pep440)); - } - - Err(ZervError::InvalidVersion(format!( - "Version '{}' is not valid SemVer or PEP440 format", version_str - ))) - } -} -``` - -### 6. Enhanced Error Handler - -**Location:** `src/error.rs` (enhanced) - -**New Error Types:** - -```rust -pub enum ZervError { - // Existing variants... - - // New CLI-specific errors - UnknownSource(String), - ConflictingOptions(String), - StdinError(String), - BooleanParseError(String), - - // Enhanced VCS errors with source context - VcsNotFoundWithSource { source: String, message: String }, - NoTagsFoundWithSource { source: String }, - CommandFailedWithContext { source: String, operation: String, message: String }, -} -``` - -### 7. Pipeline Orchestrator - -**Location:** `src/cli/version.rs` (enhanced) - -```rust -pub fn run_version_pipeline( - args: VersionArgs, - directory: Option<&str>, -) -> Result { - // 1. Determine working directory - let work_dir = resolve_work_directory(directory)?; - - // 2. Resolve input source and parse version data - let mut zerv_object = match args.source.as_str() { - "git" => { - // Get git VCS data - let vcs_data = detect_vcs(&work_dir)?.get_vcs_data()?; - - // Parse git tag with input format if available - let mut processed_data = vcs_data; - if let Some(tag) = &processed_data.tag_version { - let version_obj = InputFormatHandler::parse_version_string(tag, &args.input_format)?; - // Convert back to VCS data with parsed version - processed_data = update_vcs_data_with_parsed_version(processed_data, version_obj)?; - } - - // Apply overrides (including --tag-version with input format) - let final_vcs_data = VcsOverrideProcessor::apply_overrides(processed_data, &args)?; - - // Convert to Zerv object - vcs_data_to_zerv_object(final_vcs_data)? - } - "stdin" => { - // Parse stdin as Zerv RON (input_format must be "zerv") - let mut zerv_from_stdin = InputFormatHandler::parse_stdin(&args.input_format)?; - - // Apply overrides to the parsed Zerv object (like --tag-version) - if args.has_overrides() { - zerv_from_stdin = VcsOverrideProcessor::apply_overrides_to_zerv(zerv_from_stdin, &args)?; - } - - zerv_from_stdin - } - source => return Err(ZervError::UnknownSource(source.to_string())) - }; - - // 3. Apply schema if specified - if args.schema.is_some() || args.schema_ron.is_some() { - zerv_object = apply_schema_to_zerv(zerv_object, args.schema.as_deref(), args.schema_ron.as_deref())?; - } - - // 4. Apply output formatting - let output = OutputFormatter::format_output(&zerv_object, &args)?; - - Ok(output) -} -``` - -## Data Models - -### VcsData Structure (Unchanged) - -The existing `VcsData` structure remains unchanged as it properly represents version control metadata without implementation details: - -```rust -#[derive(Debug, Clone, PartialEq)] -pub struct VcsData { - pub tag_version: Option, - pub distance: u32, - pub commit_hash: String, - pub commit_hash_short: String, - pub current_branch: Option, - pub commit_timestamp: i64, - pub tag_timestamp: Option, - pub is_dirty: bool, -} -``` - -**Design Principle**: `VcsData` contains only version control metadata, not process or source information. - -### Zerv RON Data Structure - -```rust -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -pub struct ZervRonData { - pub schema: ZervSchema, - pub vars: ZervVars, -} - -impl ZervRonData { - pub fn to_vcs_data(&self) -> Result { - // Convert Zerv RON back to VcsData for processing - } - - pub fn validate(&self) -> Result<()> { - // Validate that required fields are present - } -} -``` - -## Error Handling - -### Error Message Strategy - -1. **Source-Aware Messages**: All VCS-related errors include the specific source -2. **Actionable Guidance**: Errors provide clear next steps -3. **Context Preservation**: Include relevant context (file paths, command args) -4. **Consistent Formatting**: Standardized error message patterns - -### Error Translation Patterns - -```rust -impl GitVcs { - pub fn translate_git_error(&self, stderr: &[u8]) -> ZervError { - let stderr_str = String::from_utf8_lossy(stderr); - - if stderr_str.contains("fatal: ambiguous argument 'HEAD'") { - return ZervError::CommandFailedWithContext { - source: "git".to_string(), - operation: "get commits".to_string(), - message: "No commits found in git repository".to_string(), - }; - } - - if stderr_str.contains("not a git repository") { - return ZervError::VcsNotFoundWithSource { - source: "git".to_string(), - message: "Not in a git repository (--source git)".to_string(), - }; - } - - // Additional patterns... - } -} -``` - -### Stdin Validation Strategy - -1. **Format Detection**: Distinguish between RON and simple strings -2. **RON Validation**: Parse and validate Zerv RON structure -3. **Helpful Suggestions**: Guide users to correct usage patterns -4. **Detailed Parsing Errors**: Include line/column information for RON errors - -## Testing Strategy - -### Unit Tests - -1. **CLI Parser Tests**: Validate all new argument combinations -2. **Boolean Parser Tests**: Test all supported boolean formats -3. **Override Logic Tests**: Validate conflict detection and application -4. **Stdin Parser Tests**: Test RON parsing and error cases -5. **Error Translation Tests**: Verify all error message patterns - -### Integration Tests - -1. **End-to-End Workflows**: Test complete piping scenarios -2. **Git Integration**: Test with various repository states -3. **Error Scenarios**: Test all error conditions with real git repos -4. **Performance Tests**: Validate response time requirements - -### Test Data Strategy - -```rust -// Test fixtures for various scenarios -pub struct VersionCommandTestFixtures; - -impl VersionCommandTestFixtures { - pub fn create_tagged_repo() -> GitRepoFixture { /* ... */ } - pub fn create_dirty_repo() -> GitRepoFixture { /* ... */ } - pub fn create_distance_repo(distance: u32) -> GitRepoFixture { /* ... */ } - pub fn sample_zerv_ron() -> String { /* ... */ } - pub fn invalid_ron_samples() -> Vec { /* ... */ } -} -``` - -## Implementation Phases - -### Phase 1: Core Infrastructure - -- Fuzzy boolean parser -- VCS override processor -- Enhanced error types -- Basic stdin support - -### Phase 2: Stdin RON Support - -- RON parser implementation -- Input validation -- Format detection -- Error message enhancement - -### Phase 3: Advanced Features - -- Output formatting options -- Template support -- Performance optimization -- Comprehensive testing - -### Phase 4: Integration & Polish - -- End-to-end testing -- Documentation updates -- Error message refinement -- Performance validation - -## Security Considerations - -1. **Input Validation**: Strict validation of all user inputs -2. **Command Injection**: Safe handling of git commands and arguments -3. **Path Traversal**: Secure directory operations -4. **Resource Limits**: Prevent excessive memory usage with large inputs - -## Performance Considerations - -1. **Git Command Optimization**: Minimize git command executions -2. **RON Parsing**: Efficient parsing of large RON inputs -3. **Memory Management**: Avoid unnecessary data copying -4. **Caching**: Cache git repository metadata when appropriate - -## Backward Compatibility - -1. **Existing CLI**: All current command patterns continue to work -2. **Output Formats**: Existing output remains unchanged -3. **Error Codes**: Maintain existing exit code behavior -4. **Configuration**: Existing schema and configuration files work unchanged - -## Future Extensions - -1. **Additional Sources**: Support for other VCS systems (hg, svn) -2. **Custom Templates**: Advanced output template system -3. **Configuration Files**: Support for .zervrc configuration -4. **Plugin System**: Extensible architecture for custom processors diff --git a/.kiro/specs/version-command-complete/requirements.md b/.kiro/specs/version-command-complete/requirements.md deleted file mode 100644 index bbcccc8..0000000 --- a/.kiro/specs/version-command-complete/requirements.md +++ /dev/null @@ -1,134 +0,0 @@ -# Requirements Document - -## Introduction - -This specification defines the requirements for implementing a complete version command for Zerv that matches the ideal design outlined in `.dev/05-version-command-complete-spec.md`. The version command should support multiple input sources, comprehensive VCS overrides, enhanced error handling, and piping workflows with full data preservation through Zerv RON format. - -## Requirements - -### Requirement 1: Enhanced Source Support - -**User Story:** As a developer, I want to use different input sources for version generation, so that I can integrate Zerv into complex workflows and pipelines. - -#### Acceptance Criteria - -1. WHEN I run `zerv version` without source flags THEN the system SHALL default to git source -2. WHEN I run `zerv version --source git` THEN the system SHALL extract version data from the git repository -3. WHEN I run `zerv version --source stdin` THEN the system SHALL read Zerv RON format from stdin -4. WHEN I provide simple version strings to stdin THEN the system SHALL reject them with helpful error message -5. WHEN I provide invalid RON format to stdin THEN the system SHALL provide clear parsing error messages -6. WHEN no stdin input is available with `--source stdin` THEN the system SHALL report "No input provided via stdin" - -### Requirement 2: VCS Override Capabilities - -**User Story:** As a CI/CD engineer, I want to override VCS-detected values for testing and simulation purposes, so that I can validate version generation under different scenarios. - -#### Acceptance Criteria - -1. WHEN I use `--tag-version ` THEN the system SHALL override the detected tag version -2. WHEN I use `--distance ` THEN the system SHALL override the calculated distance from tag -3. WHEN I use `--dirty ` THEN the system SHALL override the detected dirty state -4. WHEN I use `--clean` THEN the system SHALL set distance=0 and dirty=false -5. WHEN I use `--current-branch ` THEN the system SHALL override the detected branch name -6. WHEN I use `--commit-hash ` THEN the system SHALL override the detected commit hash -7. WHEN I use conflicting flags like `--clean` with `--distance` THEN the system SHALL report a clear error - -### Requirement 3: Enhanced Boolean Parsing - -**User Story:** As a CLI user, I want flexible boolean input options, so that I can use natural language values for boolean flags. - -#### Acceptance Criteria - -1. WHEN I use `--dirty true` THEN the system SHALL accept it as true -2. WHEN I use `--dirty t`, `--dirty yes`, `--dirty y`, `--dirty 1`, or `--dirty on` THEN the system SHALL accept them as true (case-insensitive) -3. WHEN I use `--dirty false` THEN the system SHALL accept it as false -4. WHEN I use `--dirty f`, `--dirty no`, `--dirty n`, `--dirty 0`, or `--dirty off` THEN the system SHALL accept them as false (case-insensitive) -5. WHEN I provide invalid boolean values THEN the system SHALL report clear error with supported values - -### Requirement 4: Comprehensive Error Handling - -**User Story:** As a developer, I want clear and actionable error messages, so that I can quickly understand and resolve issues. - -#### Acceptance Criteria - -1. WHEN I'm not in a git repository THEN the system SHALL report "Not in a git repository (--source git)" -2. WHEN no tags are found THEN the system SHALL report "No version tags found in git repository" -3. WHEN no commits exist THEN the system SHALL report "No commits found in git repository" -4. WHEN git command is not found THEN the system SHALL report "Git command not found. Please install git and try again." -5. WHEN permission is denied THEN the system SHALL report "Permission denied accessing git repository" -6. WHEN unknown output format is used THEN the system SHALL list supported formats -7. WHEN shallow clone is detected THEN the system SHALL warn about inaccurate distance calculations - -### Requirement 5: Zerv RON Piping Support - -**User Story:** As a power user, I want to pipe Zerv RON format between commands, so that I can create complex transformation workflows with full data preservation. - -#### Acceptance Criteria - -1. WHEN I use `--output-format zerv` THEN the system SHALL output complete Zerv RON format with schema and vars -2. WHEN I pipe Zerv RON to `zerv version --source stdin` THEN the system SHALL parse and process it correctly -3. WHEN I chain multiple Zerv commands with RON format THEN all version data SHALL be preserved through the pipeline -4. WHEN I apply different schemas in a pipeline THEN the transformations SHALL work correctly -5. WHEN RON format is malformed THEN the system SHALL provide specific parsing error messages - -### Requirement 6: Input Format Validation - -**User Story:** As a user, I want clear validation of input formats, so that I understand what inputs are supported and how to use them correctly. - -#### Acceptance Criteria - -1. WHEN using `--source stdin` THEN the system SHALL only accept Zerv RON format -2. WHEN simple version strings are provided to stdin THEN the system SHALL suggest using `--tag-version` instead -3. WHEN PEP440 or SemVer strings are provided to stdin THEN the system SHALL reject them with clear guidance -4. WHEN RON structure is invalid THEN the system SHALL provide line and column error information -5. WHEN Zerv RON is missing required fields THEN the system SHALL report specific missing fields - -### Requirement 7: Output Format Enhancement - -**User Story:** As an integrator, I want consistent and clean output formats, so that I can reliably parse version information in scripts and tools. - -#### Acceptance Criteria - -1. WHEN I request any output format THEN the system SHALL produce single-line clean output -2. WHEN I use `--output-format pep440` THEN the system SHALL produce valid PEP440 format -3. WHEN I use `--output-format semver` THEN the system SHALL produce valid SemVer format -4. WHEN I use `--output-format zerv` THEN the system SHALL produce valid RON format -5. WHEN unknown format is requested THEN the system SHALL list all supported formats -6. WHEN output prefix is requested THEN the system SHALL apply it correctly - -### Requirement 8: State-Based Version Tiers - -**User Story:** As a release engineer, I want version output to reflect repository state accurately, so that I can distinguish between clean releases, development versions, and dirty builds. - -#### Acceptance Criteria - -1. WHEN on a tagged commit with clean working tree THEN the system SHALL output clean version (Tier 1) -2. WHEN commits exist after tag with clean working tree THEN the system SHALL include distance and branch info (Tier 2) -3. WHEN working tree is dirty THEN the system SHALL include development timestamp and dirty indicators (Tier 3) -4. WHEN overrides are used THEN the system SHALL respect the override values for tier calculation -5. WHEN `--clean` flag is used THEN the system SHALL force Tier 1 output regardless of actual state - -### Requirement 9: Command Line Interface Consistency - -**User Story:** As a CLI user, I want consistent command-line interface patterns, so that the tool behaves predictably and follows standard conventions. - -#### Acceptance Criteria - -1. WHEN I use `--help` THEN the system SHALL show comprehensive help with all options -2. WHEN I use invalid flag combinations THEN the system SHALL report specific conflicts -3. WHEN I use `--version` THEN the system SHALL show Zerv version information -4. WHEN command succeeds THEN the system SHALL exit with code 0 -5. WHEN command fails THEN the system SHALL exit with code 1 -6. WHEN I use short flags THEN they SHALL work equivalently to long flags where applicable - -### Requirement 10: Performance and Reliability - -**User Story:** As a CI/CD system, I want fast and reliable version generation, so that build pipelines remain efficient and stable. - -#### Acceptance Criteria - -1. WHEN processing version data THEN the system SHALL complete in under 100ms for typical repositories -2. WHEN git operations fail THEN the system SHALL retry appropriately or fail fast with clear errors -3. WHEN large repositories are processed THEN memory usage SHALL remain reasonable -4. WHEN concurrent executions occur THEN the system SHALL handle them safely without interference -5. WHEN network issues affect git operations THEN the system SHALL provide appropriate error messages diff --git a/.kiro/specs/version-command-complete/tasks.md b/.kiro/specs/version-command-complete/tasks.md deleted file mode 100644 index c48ce20..0000000 --- a/.kiro/specs/version-command-complete/tasks.md +++ /dev/null @@ -1,102 +0,0 @@ -# Implementation Plan - -- [x] 1. Create fuzzy boolean parser for CLI arguments - - Implement `FuzzyBool` struct with `FromStr` trait in `src/cli/utils/fuzzy_bool.rs` - - Support true/false, t/f, yes/no, y/n, 1/0, on/off (case-insensitive) - - Write comprehensive unit tests for all boolean value combinations - - _Requirements: 3.1, 3.2, 3.3, 3.4, 3.5_ - -- [x] 2. Enhance CLI argument structure with new options - - Add VCS override fields to `VersionArgs` in `src/cli/version.rs` - - Add `tag_version`, `distance`, `dirty`, `clean`, `current_branch`, `commit_hash` fields - - Update `input_format` field with proper default value - - Add `output_template` and `output_prefix` fields for future extension - - _Requirements: 2.1, 2.2, 2.3, 2.4, 2.5, 2.6_ - -- [x] 3. Implement input format handler for version string parsing - - Create `InputFormatHandler` in `src/cli/utils/format_handler.rs` - - Implement `parse_version_string()` method with format-specific parsing - - Support semver, pep440, and auto-detection modes - - Implement `parse_stdin()` method for Zerv RON parsing from stdin - - Add comprehensive error handling with format-specific messages - - _Requirements: 5.1, 5.2, 5.3, 6.1, 6.2, 6.3, 6.4_ - -- [x] 4. Create VCS override processor with conflict validation - - Implement `VcsOverrideProcessor` in `src/cli/utils/vcs_override.rs` - - Add `apply_overrides()` method to merge CLI overrides with VCS data - - Implement `validate_override_conflicts()` to detect conflicting options - - Handle `--clean` flag conflicts with `--distance` and `--dirty` - - Add support for parsing `--tag-version` with input format - - _Requirements: 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 2.7_ - -- [x] 5. Enhance error handling with source-aware messages - - Add new error variants to `ZervError` enum in `src/error.rs` - - Add `UnknownSource`, `ConflictingOptions`, `StdinError`, `BooleanParseError` - - Update git error translation in `GitVcs` to use source-aware messages - - Implement user-friendly error messages for all new error cases - - Add comprehensive error message tests - - _Requirements: 4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 4.7_ - -- [x] 6. Implement enhanced pipeline orchestrator - - Update `run_version_pipeline()` in `src/cli/version.rs` - - Add source-specific processing for git vs stdin inputs - - Integrate input format parsing for git tags and overrides - - Handle Zerv RON parsing from stdin with override application - - Add proper error handling and validation throughout pipeline - - _Requirements: 1.1, 1.2, 1.3, 5.4, 5.5_ - -- [x] 7. Add comprehensive input validation for stdin - - Implement stdin format detection and validation - - Add specific error messages for simple version strings vs RON format - - Validate Zerv RON structure and required fields - - Provide helpful suggestions for incorrect usage patterns - - Add line/column error information for RON parsing failures - - _Requirements: 6.1, 6.2, 6.3, 6.4, 6.5_ - -- [x] 8. Create comprehensive unit tests for new components - - Write tests for `FuzzyBool` parser with all supported formats - - Test `InputFormatHandler` with valid and invalid inputs - - Test `VcsOverrideProcessor` conflict detection and application - - Test enhanced error handling and message formatting - - Test stdin parsing with various input formats and error cases - - _Requirements: All requirements validation_ - -- [x] 9. Implement integration tests for end-to-end workflows - - Test git source with various override combinations - - Test stdin source with Zerv RON input and overrides - - Test piping workflows between multiple Zerv commands - - Test error scenarios with real git repositories - - Test performance requirements with large repositories - - _Requirements: 5.1, 5.2, 5.3, 5.4, 5.5, 10.1, 10.2, 10.3, 10.4, 10.5_ - -- [x] 10. Add CLI help and documentation updates - - Update command help text with new options and examples - - Add usage examples for override options and piping workflows - - Document input format behavior and supported values - - Add error message consistency validation - - Ensure backward compatibility with existing command patterns - - _Requirements: 9.1, 9.2, 9.3, 9.4, 9.5, 9.6_ - -- [ ] 11. Implement output formatting enhancements - - Add support for output prefix option - - Ensure clean single-line output for all formats - - Add template support infrastructure for future extension - - Validate output format consistency across all scenarios - - Test output with various version states and overrides - - _Requirements: 7.1, 7.2, 7.3, 7.4, 7.5, 7.6_ - -- [ ] 12. Add performance optimization and validation - - Optimize git command execution to minimize calls - - Add efficient RON parsing for large inputs - - Implement memory usage optimization for large repositories - - Add performance benchmarks and validation tests - - Ensure sub-100ms response time for typical repositories - - _Requirements: 10.1, 10.2, 10.3, 10.4, 10.5_ - -- [ ] 13. Comprehensive testing and validation - - Run full test suite with Docker and native git implementations - - Test on multiple platforms (Linux, macOS, Windows via CI) - - Validate error message consistency across all scenarios - - Test backward compatibility with existing usage patterns - - Perform end-to-end validation of all requirements - - _Requirements: All requirements final validation_ diff --git a/.kiro/steering/cli-implementation.md b/.kiro/steering/cli-implementation.md deleted file mode 100644 index a0d9693..0000000 --- a/.kiro/steering/cli-implementation.md +++ /dev/null @@ -1,140 +0,0 @@ ---- -inclusion: fileMatch -fileMatchPattern: "src/cli/**/*.rs" ---- - -# CLI Implementation Standards - -## Core Commands - -### `zerv version [OPTIONS]` - -Main version processing pipeline with composable operations. - -### `zerv check [OPTIONS]` - -Validation-only command for version strings. - -## Pipeline Architecture - -``` -Input → Version Object → Zerv Object → Transform → Output Version Object → Display -``` - -## Key Implementation Patterns - -### Version Command Args Structure - -```rust -#[derive(Parser)] -struct VersionArgs { - version: Option, - #[arg(long, default_value = "git")] - source: String, - #[arg(long, default_value = "zerv-default")] - schema: String, - #[arg(long)] - schema_ron: Option, - #[arg(long)] - output_format: Option, -} -``` - -### Check Command Args Structure - -```rust -#[derive(Parser)] -struct CheckArgs { - version: String, - #[arg(long)] - format: Option, // pep440, semver, auto-detect (default) -} -``` - -### Core Pipeline Function - -```rust -pub fn run_version_pipeline(args: VersionArgs) -> Result { - // 1. Get VCS data - let vcs_data = detect_vcs(current_dir())?.get_vcs_data()?; - - // 2. Convert to ZervVars - let vars = vcs_data_to_zerv_vars(vcs_data)?; - - // 3. Apply schema and output format - match args.output_format.as_deref() { - Some("pep440") => Ok(PEP440::from_zerv(&vars)?.to_string()), - Some("semver") => Ok(SemVer::from_zerv(&vars)?.to_string()), - _ => Ok(vars.to_string()), - } -} -``` - -## State-Based Versioning Tiers - -**Tier 1** (Tagged, clean): `major.minor.patch` -**Tier 2** (Distance, clean): `major.minor.patch.post+branch.` -**Tier 3** (Dirty): `major.minor.patch.dev+branch.` - -## Format Flag Validation Pattern - -```rust -// Error if conflicting format flags used -if args.format.is_some() && (args.input_format.is_some() || args.output_format.is_some()) { - return Err(ZervError::ConflictingFlags( - "Cannot use --format with --input-format or --output-format".to_string() - )); -} -``` - -## Check Command Auto-Detection Pattern - -```rust -fn run_check_command(args: CheckArgs) -> Result<()> { - match args.format.as_deref() { - Some("pep440") => { - PEP440::parse(&args.version)?; - println!("✓ Valid PEP440 version"); - } - Some("semver") => { - SemVer::parse(&args.version)?; - println!("✓ Valid SemVer version"); - } - None => { - // Auto-detect format - let pep440_valid = PEP440::parse(&args.version).is_ok(); - let semver_valid = SemVer::parse(&args.version).is_ok(); - - match (pep440_valid, semver_valid) { - (true, false) => println!("✓ Valid PEP440 version"), - (false, true) => println!("✓ Valid SemVer version"), - (true, true) => { - println!("✓ Valid PEP440 version"); - println!("✓ Valid SemVer version"); - } - (false, false) => return Err(ZervError::InvalidVersion(args.version)), - } - } - Some(format) => return Err(ZervError::UnknownFormat(format.to_string())), - } - Ok(()) -} -``` - -## Essential CLI Options - -### Input Sources - -- `--source git` (default) - Auto-detect Git -- `--source string ` - Parse version string - -### Schema Control - -- `--schema zerv-default` (default) - Tier-aware schema -- `--schema-ron ` - Custom RON schema - -### Output Control - -- `--output-format ` - Target format: pep440, semver -- `--output-template