You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add JSON output format support to the release command (roadmap task 12.7). This is part of Phase 2 of the JSON output epic (#348), which aims to implement JSON output for all remaining commands so that JSON can eventually become the default output format.
The release command currently only outputs human-readable text progress messages. This task adds a machine-readable JSON alternative that automation workflows and AI agents can parse programmatically.
Goals
Add output_format: OutputFormat parameter to ReleaseCommandController::execute()
Add a ReleaseDetailsData view DTO covering the released environment data
Implement JsonView for the release command — render() returns String using the list-command pattern (inline unwrap_or_else fallback, no OutputFormatting error variant)
Implement TextView to present the same data in human-readable format
Handle OutputFormat::Json and OutputFormat::Text branches in complete_workflow() using Strategy Pattern
Update router to pass output_format from context to controller
Add unit tests for JsonView and TextView
🏗️ Architecture Requirements
DDD Layer: Presentation
Module Paths:
src/presentation/controllers/release/handler.rs — add output_format param to execute(), update complete_workflow() method
src/presentation/views/commands/release/ — new module with DTO and views (mirrors configure/ structure with view_data/ subdir)
Pattern: Strategy Pattern for rendering (same as provision, create, show, run, list, configure)
Module Structure Requirements
Follow the existing view module structure established in configure/ (has view_data/) — release needs view_data/ because Environment<Released> is a domain type
ReleaseDetailsData is a plain presentation DTO deriving only Serialize (not Deserialize) with a From<&Environment<Released>> impl
JsonView::render() returns String — serialization errors handled inline via unwrap_or_else (list pattern, not provision pattern)
TextView::render() formats the same data as human-readable text and also returns String
Create view_data/mod.rs and view_data/release_details.rs
Create views/mod.rs, views/text_view.rs, and views/json_view.rs
Step 2: Implement DTO
Implement ReleaseDetailsData struct with all required fields
Add #[derive(Debug, Clone, Serialize)]
Implement From<&Environment<Released>> conversion
Step 3: Implement Views
Implement JsonView::render() with inline error handling
Implement TextView::render() with formatted text output
Follow existing patterns from configure command
Step 4: Update Controller
Add output_format: OutputFormat parameter to execute()
Store released_env from release_application() (currently discarded)
Update complete_workflow() to accept released_env and output_format
Implement Strategy Pattern for view selection
Remove hardcoded success message, replace with view output
Step 5: Update Router
Update src/presentation/dispatch/router.rs to pass output_format from context
Ensure --output-format flag is propagated to controller
Step 6: Add Tests
Unit tests for ReleaseDetailsData::from()
Unit tests for JsonView::render() (valid JSON structure)
Unit tests for TextView::render() (contains expected strings)
Integration test for complete workflow with both formats
Step 7: Documentation
Update command documentation in docs/user-guide/commands/release.md
Add JSON output examples
Update CLI help text if needed
Testing Strategy
Unit Tests
#[cfg(test)]mod tests {usesuper::*;#[test]fnit_should_convert_released_env_to_dto(){// Test DTO conversion}#[test]fnit_should_render_valid_json(){// Test JSON view produces valid JSON}#[test]fnit_should_render_text_with_all_fields(){// Test text view contains all expected information}}
Integration Tests
Test release command with --output-format json
Test release command with --output-format text (default)
Overview
Add JSON output format support to the
releasecommand (roadmap task 12.7). This is part of Phase 2 of the JSON output epic (#348), which aims to implement JSON output for all remaining commands so that JSON can eventually become the default output format.The
releasecommand currently only outputs human-readable text progress messages. This task adds a machine-readable JSON alternative that automation workflows and AI agents can parse programmatically.Goals
output_format: OutputFormatparameter toReleaseCommandController::execute()ReleaseDetailsDataview DTO covering the released environment dataJsonViewfor the release command —render()returnsStringusing the list-command pattern (inlineunwrap_or_elsefallback, noOutputFormattingerror variant)TextViewto present the same data in human-readable formatOutputFormat::JsonandOutputFormat::Textbranches incomplete_workflow()using Strategy Patternoutput_formatfrom context to controllerJsonViewandTextView🏗️ Architecture Requirements
DDD Layer: Presentation
Module Paths:
src/presentation/controllers/release/handler.rs— addoutput_formatparam toexecute(), updatecomplete_workflow()methodsrc/presentation/views/commands/release/— new module with DTO and views (mirrorsconfigure/structure withview_data/subdir)mod.rsview_data/release_details.rs—ReleaseDetailsDataDTOviews/text_view.rs—TextViewviews/json_view.rs—JsonViewPattern: Strategy Pattern for rendering (same as
provision,create,show,run,list,configure)Module Structure Requirements
configure/(hasview_data/) —releaseneedsview_data/becauseEnvironment<Released>is a domain typeReleaseDetailsDatais a plain presentation DTO deriving onlySerialize(notDeserialize) with aFrom<&Environment<Released>>implJsonView::render()returnsString— serialization errors handled inline viaunwrap_or_else(list pattern, not provision pattern)TextView::render()formats the same data as human-readable text and also returnsStringdocs/contributing/module-organization.md)Architectural Constraints
docs/contributing/error-handling.md)UserOutputmethods — neverprintln!oreprintln!directly (docs/contributing/output-handling.md)ReleaseDetailsDataDTO must deriveserde::Serialize(output-only — noDeserializeneeded)Anti-Patterns to Avoid
println!/eprintln!instead ofUserOutputSpecifications
JSON Output Format
When
--output-format jsonis passed, thereleasecommand outputs a single JSON object to stdout:{ "environment_name": "my-env", "instance_name": "torrust-tracker-vm-my-env", "provider": "lxd", "state": "Released", "instance_ip": "10.140.190.39", "created_at": "2026-02-20T10:00:00Z" }Fields:
environment_nameinstance_nameproviderlxd,hetzner, etc.)state"Released"on successinstance_ipcreated_atReleaseDetailsDataDTOView Implementations
JsonView
TextView
Controller Integration
Update
ReleaseCommandController:Implementation Checklist
Step 1: Create View Module Structure
src/presentation/views/commands/release/directorymod.rswith module declarationsview_data/mod.rsandview_data/release_details.rsviews/mod.rs,views/text_view.rs, andviews/json_view.rsStep 2: Implement DTO
ReleaseDetailsDatastruct with all required fields#[derive(Debug, Clone, Serialize)]From<&Environment<Released>>conversionStep 3: Implement Views
JsonView::render()with inline error handlingTextView::render()with formatted text outputconfigurecommandStep 4: Update Controller
output_format: OutputFormatparameter toexecute()released_envfromrelease_application()(currently discarded)complete_workflow()to acceptreleased_envandoutput_formatStep 5: Update Router
src/presentation/dispatch/router.rsto passoutput_formatfrom context--output-formatflag is propagated to controllerStep 6: Add Tests
ReleaseDetailsData::from()JsonView::render()(valid JSON structure)TextView::render()(contains expected strings)Step 7: Documentation
docs/user-guide/commands/release.mdTesting Strategy
Unit Tests
Integration Tests
releasecommand with--output-format jsonreleasecommand with--output-format text(default)Definition of Done
Related
docs/issues/348-epic-add-json-output-format-support.mddocs/roadmap.mdsrc/presentation/views/commands/configure/src/presentation/views/commands/list/