torrust-tracker-deployer 0.1.0

Torrust Tracker Deployer - Deployment Infrastructure with Ansible and OpenTofu
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
# JSON Schema Generation Specification

## ๐Ÿ“‹ Overview

Add a new `create schema` CLI subcommand that generates JSON Schema from the Rust configuration types used for environment creation. This enables IDE validation, auto-completion, and inline documentation for users editing environment JSON files.

### Context

Currently, users create environment configuration JSON files using the `create template` command, which generates a JSON file with placeholder values. However, users have no automated way to:

- Validate their JSON against the expected structure
- Get auto-completion when editing configuration
- See documentation for each field inline in their editor
- Detect typos or invalid values before running commands

Many modern IDEs and editors support JSON Schema for validation and tooling. By generating a schema from our Rust types, we can provide immediate feedback to users as they edit configuration files.

### Problem Statement

Users editing environment JSON files lack IDE support for validation, auto-completion, and documentation. This leads to:

1. **Configuration errors** discovered only at runtime
2. **Poor discoverability** of available options
3. **Trial and error** when filling in values
4. **No inline documentation** explaining what each field does
5. **Inconsistent formatting** across different users' files

## ๐ŸŽฏ Goals

### Primary Goals

- **Generate valid JSON Schema** from Rust configuration types using Schemars
- **Provide CLI command** to output schema: `create schema [PATH]`
- **Print to stdout** when no path is provided
- **Write to file** when path argument is given
- **Improve template output** to inform users about schema generation
- **Enable IDE integration** through standard JSON Schema format
- **AI agent support** - JSON Schema significantly enhances AI agents' ability to generate valid configuration files

### Secondary Goals (Nice-to-Have)

- Include Rust doc comments as descriptions in schema
- Add schema examples for common configuration patterns
- Provide IDE configuration examples (VS Code, IntelliJ)
- Auto-generate and commit schema to repository in CI

### Non-Goals

What this feature explicitly does NOT aim to do:

- Runtime validation using the schema (Rust deserialization already validates)
- Schema versioning or migration tooling
- IDE plugins or extensions
- Online schema registry or hosting
- Support for other config formats (TOML, YAML)

## ๐Ÿ’ก Proposed Solution

### Approach

Use the [Schemars](https://graham.cool/schemars/) crate to derive JSON Schema from the existing Rust configuration types. Schemars provides a `JsonSchema` derive macro that works with Serde types, making it straightforward to generate schemas without duplicating type definitions.

**Why Schemars?**

- Works seamlessly with existing Serde derives
- Mature crate with wide adoption
- Supports custom schema attributes for fine-tuning
- Includes descriptions from doc comments
- Handles complex Rust types (enums, generics, options)

### Design Overview

```text
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚                    User runs command                        โ”‚
โ”‚           cargo run create schema [optional-path]           โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                         โ”‚
                         โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚              Presentation Layer (CLI Parser)                โ”‚
โ”‚  - Parse `create schema` subcommand                         โ”‚
โ”‚  - Extract optional output path                             โ”‚
โ”‚  - Dispatch to CreateSchemaCommand                          โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                         โ”‚
                         โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚            Application Layer (Command Handler)              โ”‚
โ”‚  - CreateSchemaCommandHandler                               โ”‚
โ”‚  - Calls SchemaGenerator directly (no Step needed)          โ”‚
โ”‚  - Handles output routing (stdout vs file)                  โ”‚
โ”‚  - Manages success/error presentation via UserOutput        โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                         โ”‚
                         โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚              Infrastructure Layer                           โ”‚
โ”‚  - SchemaGenerator (technical implementation)               โ”‚
โ”‚  - Uses schemars crate to generate JSON Schema              โ”‚
โ”‚  - Calls EnvironmentCreationConfig::json_schema()           โ”‚
โ”‚  - Returns schema as String (JSON format)                   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
                         โ”‚
                         โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚              Config Types (with JsonSchema derive)          โ”‚
โ”‚  - EnvironmentCreationConfig                                โ”‚
โ”‚  - All nested types derive JsonSchema                       โ”‚
โ”‚  - Schema includes doc comments as descriptions             โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
```

**Note**: No Step layer needed - command has only one operation. Handler directly calls infrastructure service.

### Key Design Decisions

1. **Use Schemars derive macro**: Minimizes code duplication and maintenance burden
2. **Output flexibility**: Support both stdout (for piping) and file output
3. **Schema location**: Generate from `EnvironmentCreationConfig` (top-level type)
4. **Update template output**: Inform users about schema availability after template creation
5. **No Step layer**: Command has single operation - handler directly calls `SchemaGenerator`
6. **Infrastructure placement**: `SchemaGenerator` is infrastructure (external dependency, technical mechanism)

### Alternatives Considered

#### Option 1: Manual Schema Definition

- **Pros**: Full control over schema structure, no new dependency
- **Cons**: High maintenance burden, prone to drift from Rust types, duplicates effort
- **Decision**: Rejected - too much manual work and error-prone

#### Option 2: Build-time Schema Generation

- **Pros**: Schema always in sync, can be committed to repo
- **Cons**: More complex build setup, harder to debug
- **Decision**: Deferred - start with runtime generation, consider build-time later

#### Option 3: External Schema Tool

- **Pros**: No Rust code changes needed
- **Cons**: Doesn't integrate with existing types, requires separate tooling
- **Decision**: Rejected - Schemars provides better integration

## ๐Ÿ”ง Implementation Details

### Architecture Changes

**No major architectural changes** - this feature adds new functionality without modifying existing components.

### Component Design

#### Component 1: Config Types with JsonSchema Derive

**Purpose**: Add JSON Schema generation capability to existing config types

**Changes**:

```rust
// src/application/command_handlers/create/config/environment_config.rs

use schemars::JsonSchema;

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
pub struct EnvironmentCreationConfig {
    /// Environment-specific settings
    pub environment: EnvironmentSection,

    /// SSH credentials configuration
    pub ssh_credentials: SshCredentialsConfig,

    /// Provider-specific configuration (LXD, Hetzner, etc.)
    pub provider: ProviderSection,

    /// Tracker deployment configuration
    pub tracker: TrackerSection,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
pub struct EnvironmentSection {
    /// Name of the environment to create
    /// Must follow environment naming rules
    pub name: String,

    /// Optional instance name override
    pub instance_name: Option<String>,
}
```

**Dependencies**: Schemars crate

#### Component 2: Schema Generator (Infrastructure)

**Purpose**: Generate JSON Schema from config types

**Interface**:

```rust
// src/infrastructure/schema/generator.rs

use schemars::schema_for;
use crate::application::command_handlers::create::config::EnvironmentCreationConfig;

pub struct SchemaGenerator;

impl SchemaGenerator {
    /// Generate JSON Schema for environment configuration
    pub fn generate() -> Result<String, SchemaGenerationError> {
        let schema = schema_for!(EnvironmentCreationConfig);
        serde_json::to_string_pretty(&schema)
            .map_err(|e| SchemaGenerationError::SerializationFailed(e))
    }
}

#[derive(Debug, thiserror::Error)]
pub enum SchemaGenerationError {
    #[error("Failed to serialize schema: {0}")]
    SerializationFailed(#[from] serde_json::Error),
}
```

**Dependencies**: Schemars, serde_json

**Design Note**: Placed in infrastructure layer because:

- Uses external crate (`schemars`) - could be swapped for alternatives
- Technical implementation detail, not business logic
- Format-specific (JSON Schema) - could add other formats (TOML schema, OpenAPI, etc.)

#### Component 3: Create Schema Command Handler (Application)

**Purpose**: Handle the `create schema` command - directly calls infrastructure service

**Interface**:

```rust
// src/application/command_handlers/create_schema/handler.rs

use std::path::PathBuf;
use crate::infrastructure::schema::SchemaGenerator;
use crate::presentation::views::UserOutput;

use super::errors::CreateSchemaError;

pub struct CreateSchemaCommandHandler;

impl CreateSchemaCommandHandler {
    pub fn handle(output_path: Option<PathBuf>, output: &mut UserOutput) -> Result<(), CreateSchemaError> {
        output.step(1, 1, "Generating JSON Schema...");

        match output_path {
            None => {
                // Generate and output to stdout
                let schema = SchemaGenerator::generate()
                    .map_err(CreateSchemaError::from)?;
                output.data(&schema);  // Output raw JSON schema to stdout
                output.success("JSON Schema generated");
            }
            Some(path) => {
                // Generate and write to file
                let schema = SchemaGenerator::generate()
                    .map_err(CreateSchemaError::from)?;
                std::fs::write(&path, schema)
                    .map_err(|e| CreateSchemaError::FileWriteFailed {
                        path: path.clone(),
                        source: e,
                    })?;
                output.success_with_detail(
                    "JSON Schema generated",
                    &format!("Schema written to: {}", path.display())
                );
            }
        }

        Ok(())
    }
}
```

**Dependencies**: Infrastructure SchemaGenerator, presentation views

**Design Notes**:

- **No Step layer** - single operation command calls infrastructure directly
- Handler accepts `&mut UserOutput` parameter for consistent output routing
- Uses `output.data()` to write schema to stdout (machine-readable JSON output)
- All output goes through `UserOutput` service - no direct `println!` usage
- Respects user's configured output formatter and verbosity settings

#### Component 4: CLI Integration (Presentation)

**Purpose**: Add `create schema` subcommand to CLI parser

**Interface**:

```rust
// src/presentation/cli.rs

#[derive(Subcommand)]
pub enum CreateSubcommand {
    /// Create a deployment environment configuration
    Environment {
        #[arg(long, value_name = "FILE")]
        env_file: PathBuf,
    },

    /// Generate a configuration template
    Template {
        #[arg(long, value_name = "PROVIDER")]
        provider: String,

        #[arg(value_name = "OUTPUT_PATH")]
        output_path: PathBuf,
    },

    /// Generate JSON Schema for environment configuration
    Schema {
        /// Optional output file path. If not provided, prints to stdout.
        #[arg(value_name = "OUTPUT_PATH")]
        output_path: Option<PathBuf>,
    },
}
```

**Dependencies**: Clap

### Data Model

No new data models required - schema is generated from existing config types.

### API Changes

**New CLI Command**:

```bash
# Print schema to stdout
cargo run create schema

# Write schema to file
cargo run create schema ./envs/environment-schema.json
```

**Updated Output for `create template`**:

```text
โœ… Configuration template ready: ./envs/example.json

๐Ÿ’ก Tip: Generate JSON Schema for IDE validation:
   torrust-tracker-deployer create schema ./envs/environment-schema.json
```

### Configuration

No new configuration options needed.

## ๐Ÿ“Š Impact Analysis

### Files to Create

| File Path                                                   | Purpose                        | Effort |
| ----------------------------------------------------------- | ------------------------------ | ------ |
| `src/infrastructure/schema/mod.rs`                          | Schema module                  | Low    |
| `src/infrastructure/schema/generator.rs`                    | Schema generation logic        | Low    |
| `src/infrastructure/schema/errors.rs`                       | Schema-specific errors         | Low    |
| `src/application/command_handlers/create_schema/mod.rs`     | Command handler module         | Low    |
| `src/application/command_handlers/create_schema/handler.rs` | Command handler implementation | Medium |
| `src/application/command_handlers/create_schema/errors.rs`  | Command-specific errors        | Low    |
| `examples/environment-schema.json`                          | Example schema output          | Low    |
| `.vscode/settings.json.example`                             | VS Code integration example    | Low    |

### Files to Modify

| File Path                                                     | Changes Required                | Effort |
| ------------------------------------------------------------- | ------------------------------- | ------ |
| `Cargo.toml`                                                  | Add schemars dependency         | Low    |
| `src/application/command_handlers/create/config/*.rs`         | Add `JsonSchema` derives        | Low    |
| `src/presentation/cli.rs`                                     | Add `Schema` subcommand         | Low    |
| `src/presentation/dispatch/mod.rs`                            | Handle `Schema` subcommand      | Low    |
| `src/application/command_handlers/create_template_handler.rs` | Add schema tip to output        | Low    |
| `src/infrastructure/mod.rs`                                   | Export schema module            | Low    |
| `src/application/command_handlers/mod.rs`                     | Export create_schema module     | Low    |
| `src/presentation/views/user_output.rs`                       | (Optional) Add `raw()` method\* | Low    |
| `docs/user-guide/commands/create.md`                          | Document schema subcommand\*\*  | Medium |
| `docs/console-commands.md`                                    | Add schema command reference    | Low    |
| `README.md`                                                   | Mention schema generation       | Low    |
| `tests/e2e/create_command.rs`                                 | Add E2E tests for schema        | Medium |

\* **Optional Enhancement**: If `data()` method applies formatting that interferes with raw JSON output, add a `raw()` method to `UserOutput` that outputs unmodified strings to stdout. This ensures schema output remains valid JSON regardless of formatter settings.

\*\* **Documentation Location**: All `create` subcommand documentation goes in `docs/user-guide/commands/create.md` - don't create separate files for subcommands.

### Breaking Changes

**None** - This is a purely additive feature.

### Performance Impact

**Neutral to Positive**:

- Schema generation should complete in reasonable time (no specific requirement, just shouldn't hang or crash)
- No impact on other commands
- Improves user productivity (fewer config errors)

### Security Considerations

**Low Risk**:

- Schema generation is read-only operation
- No sensitive data in schema
- File writes use standard permissions
- No network access required

## ๐Ÿ—“๏ธ Implementation Plan

### Phase 1: Foundation

- [ ] Add `schemars` dependency to `Cargo.toml`
- [ ] Add `JsonSchema` derive to `EnvironmentCreationConfig`
- [ ] Add `JsonSchema` derive to all nested config types
- [ ] Verify schema compiles without errors

**Estimated Time**: 1-2 hours

### Phase 2: Infrastructure Layer

- [ ] Create `src/infrastructure/schema/` module
- [ ] Implement `SchemaGenerator` with `generate()` method
- [ ] Create `SchemaGenerationError` with `.help()` method
- [ ] Write unit tests for schema generation

**Estimated Time**: 2-3 hours

### Phase 3: Application Layer

- [ ] Create `CreateSchemaCommandHandler` in `src/application/command_handlers/create_schema/`
- [ ] Handler directly calls `SchemaGenerator::generate()` (no Step layer needed)
- [ ] Implement stdout and file output logic in handler
- [ ] Create `CreateSchemaError` with `.help()` method
- [ ] Write unit tests for command handler

**Estimated Time**: 1-2 hours

**Note**: No Step layer - this command has only one operation

### Phase 4: Presentation Layer

- [ ] Add `Schema` subcommand to CLI parser in `src/presentation/cli.rs`
- [ ] Update dispatch logic to handle schema command
- [ ] Pass `UserOutput` instance to command handler (follow existing patterns)
- [ ] Use `output.data()` for schema output to stdout (never use `println!` directly)
- [ ] Optional Add `raw()` method to `UserOutput` if `data()` applies unwanted formatting
- [ ] Update `create_template_handler.rs` output with schema tip
- [ ] Write integration tests for CLI command

**Estimated Time**: 2-3 hours

**Important**: All output must go through `UserOutput` service to respect user's formatter and verbosity settings. Never use `println!`, `eprintln!`, or direct stdout/stderr writes in command handlers.

### Phase 5: Documentation & Examples

- [ ] Generate example schema file: `examples/environment-schema.json`
- [ ] Create VS Code settings example: `.vscode/settings.json.example`
- [ ] Update `docs/user-guide/commands/create.md`
- [ ] Update `docs/console-commands.md`
- [ ] Update README with schema generation mention
- [ ] Add troubleshooting section for IDE integration

**Estimated Time**: 2-3 hours

### Phase 6: Testing & Validation

- [ ] Run full test suite
- [ ] Test schema command with stdout output
- [ ] Test schema command with file output
- [ ] Validate generated schema against example JSON files
- [ ] Test IDE integration (VS Code)
- [ ] Run linters and fix issues

**Estimated Time**: 1-2 hours

### Phase 7: Finalization

- [ ] Code review
- [ ] Address feedback
- [ ] Update feature documentation status
- [ ] Commit with conventional commit message
- [ ] Create pull request

**Estimated Time**: 1-2 hours

## โœ… Definition of Done

The feature is complete when:

- [ ] All code changes implemented and tested
- [ ] `cargo run create schema` prints schema to stdout
- [ ] `cargo run create schema path/to/file.json` writes schema to file
- [ ] `create template` output mentions schema generation
- [ ] Generated schema validates example JSON files
- [ ] Unit tests pass for all new components
- [ ] Integration tests pass for CLI command
- [ ] Documentation updated (user guide, console commands, README)
- [ ] Example schema committed to repository
- [ ] VS Code settings example provided
- [ ] All linters pass
- [ ] No unused dependencies
- [ ] Feature marked as complete in `docs/features/README.md`

## ๐Ÿงช Testing Strategy

### Unit Tests

```rust
// tests for schema generator (infrastructure)
#[test]
fn it_should_generate_valid_json_schema_when_called() {
    let result = SchemaGenerator::generate();
    assert!(result.is_ok());

    let schema_str = result.unwrap();
    let schema: serde_json::Value = serde_json::from_str(&schema_str).unwrap();

    assert_eq!(schema["$schema"], "http://json-schema.org/draft-07/schema#");
    assert!(schema["properties"].is_object());
    assert!(schema["properties"]["environment"].is_object());
    assert!(schema["properties"]["ssh_credentials"].is_object());
}

// tests for command handler (application)
#[test]
fn it_should_generate_schema_to_stdout_when_no_path_provided() {
    let mut output = UserOutput::new(VerbosityLevel::Normal);
    let result = CreateSchemaCommandHandler::handle(None, &mut output);
    assert!(result.is_ok());
}

#[test]
fn it_should_write_schema_to_file_when_path_provided() {
    let temp_dir = tempfile::tempdir().unwrap();
    let output_path = temp_dir.path().join("schema.json");
    let mut output = UserOutput::new(VerbosityLevel::Normal);

    let result = CreateSchemaCommandHandler::handle(Some(output_path.clone()), &mut output);
    assert!(result.is_ok());
    assert!(output_path.exists());

    let content = std::fs::read_to_string(&output_path).unwrap();
    assert!(content.contains("environment"));
    assert!(content.contains("ssh_credentials"));
}
```

### Integration Tests

```rust
// tests for CLI command
#[test]
fn it_should_output_schema_to_stdout_when_no_path_provided() {
    let output = Command::new("cargo")
        .args(&["run", "create", "schema"])
        .output()
        .unwrap();

    assert!(output.status.success());

    let stdout = String::from_utf8(output.stdout).unwrap();
    assert!(stdout.contains("$schema"));
    assert!(stdout.contains("environment"));
}

#[test]
fn it_should_write_schema_to_file_when_path_provided() {
    let temp_dir = tempfile::tempdir().unwrap();
    let output_path = temp_dir.path().join("schema.json");

    let output = Command::new("cargo")
        .args(&["run", "create", "schema", output_path.to_str().unwrap()])
        .output()
        .unwrap();

    assert!(output.status.success());
    assert!(output_path.exists());
}
```

### Manual Testing Checklist

- [ ] Run `cargo run create schema` and verify output
- [ ] Run `cargo run create schema ./test-schema.json` and verify file created
- [ ] Configure VS Code to use schema for validation
- [ ] Edit environment JSON file and verify IDE shows errors for invalid values
- [ ] Verify IDE provides auto-completion for known fields
- [ ] Verify IDE shows descriptions from doc comments
- [ ] Test schema validates against all example JSON files in `envs/`

## ๐Ÿ” Validation Criteria

### Schema Quality

- Schema includes all fields from `EnvironmentCreationConfig`
- Descriptions are present (from Rust doc comments)
- Enum values are correctly represented
- Required vs optional fields are correctly marked
- Schema follows JSON Schema Draft 7 spec

### Code Quality

- All code follows project coding standards
- DDD layer placement is correct
- Error handling follows project principles
- All errors have `.help()` methods
- Code is well-documented with doc comments
- **All output uses `UserOutput` service** - no direct `println!` or `eprintln!` usage
- Handler accepts `&mut UserOutput` parameter following existing command handler patterns

### User Experience

- CLI command is intuitive and follows existing patterns
- Output messages are clear and actionable
- Error messages provide specific guidance
- Documentation is comprehensive and easy to follow

## ๐Ÿ“š Documentation Updates

### User Guide

Update `docs/user-guide/commands/create.md`:

````markdown
### Generate JSON Schema

Generate a JSON Schema for environment configuration files:

```bash
# Print schema to stdout
torrust-tracker-deployer create schema

# Write schema to file
torrust-tracker-deployer create schema ./envs/environment-schema.json
```
````

#### IDE Integration

To enable IDE validation and auto-completion:

1. Generate the schema file
2. Configure your IDE to associate JSON files with the schema

**VS Code Example:**

```json
{
  "json.schemas": [
    {
      "fileMatch": ["envs/*.json"],
      "url": "./envs/environment-schema.json"
    }
  ]
}
```

### Console Commands Reference

Update `docs/console-commands.md`:

````markdown
#### create schema

Generate JSON Schema for environment configuration.

**Usage:**

```bash
torrust-tracker-deployer create schema [OUTPUT_PATH]
```
````

**Arguments:**

- `OUTPUT_PATH` - Optional file path to write schema. If omitted, prints to stdout.

**Examples:**

```bash
# Print to stdout
torrust-tracker-deployer create schema

# Write to file
torrust-tracker-deployer create schema ./envs/environment-schema.json
```

## ๐ŸŽ“ Lessons Learned

(To be filled after implementation)

## ๐Ÿ”— References

- [Schemars Documentation]https://graham.cool/schemars/
- [JSON Schema Specification]https://json-schema.org/
- [VS Code JSON Schema Support]https://code.visualstudio.com/docs/languages/json#_json-schemas-and-settings

---

**Status**: Draft specification, awaiting implementation
**Created**: December 12, 2025
**Last Updated**: December 12, 2025