Thanks to visit codestin.com
Credit goes to github.com

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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,51 @@
# Changelog

## 2.0.0 release (2025-12-23)

Major refactoring of the CLI architecture for better maintainability and compatibility with Docker Compose.

### Breaking Changes
* Internal API changes - command handlers now return `Vec<OsString>` instead of `Vec<&OsStr>`

### New Features
* **Declarative argument system**: New type-safe argument definitions with validation
* `ArgDef::Flag` - Boolean flags (--flag)
* `ArgDef::Value` - String values (--option value)
* `ArgDef::Choice` - Predefined choices with validation (--pull always|missing|never)
* `ArgDef::Number` - Numeric values with validation (--timeout 30)
* `ArgDef::Services` - Multiple services support
* `ArgDef::ServiceWithCommand` - Service + command + args (for exec/run)
* **Full exec/run command support**: Now correctly passes commands and arguments to containers
* Example: `dctl exec myproject php bash -c "echo hello"`
* Example: `dctl run --rm myproject php bin/console cache:clear`
* **New `register` command**: Add projects to configuration from CLI
* Supports multiple compose files: `dctl register myapp compose.yml compose.override.yml`
* Optional env file and description: `dctl register myapp compose.yml -e .env -d "My app"`
* **New `unregister` command**: Remove projects from configuration
* With confirmation: `dctl unregister myapp`
* Force mode: `dctl unregister myapp --force`
* **YAML validation in check-config**: Validate compose file syntax
* Use `dctl check-config --validate` to run `docker compose config --quiet`

### Improvements
* **65% code reduction**: Replaced 23 individual command files (~3500 lines) with declarative definitions (~1200 lines)
* **Docker Compose compatibility**: All 24 commands verified against official Docker Compose documentation
* **Better type validation**: Choice arguments reject invalid values, Number arguments reject negative/non-numeric values
* **Async execution**: Migrated to `tokio::process::Command` for better async support
* **Parallel infos command**: Project status checks now run in parallel using `futures::join_all`
* **148 unit tests**: Comprehensive test coverage for all commands

### Technical Changes
* New `CommandHandler` trait with registry pattern
* New `definitions.rs` with all command definitions (including `config` command)
* New `args.rs` with declarative argument system
* New `register.rs` and `unregister.rs` for project management
* Added dependencies: `futures` (parallel execution), `toml_edit` (config file editing)
* Removed duplicate code across command files
* Fixed typos: `CommandOuput` → `CommandOutput`, `docker_commmand_arg` → `docker_command_arg`
* Error messages now output to stderr instead of stdout
* Idiomatic Rust improvements: `!is_empty()`, `if let Some`, proper error context

## 1.5.2 release (2025-09-05)

* Update libraries (deps).
Expand Down
33 changes: 33 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Project Overview

Docker Stack is a two-part project:

1. **dctl CLI** (`cli/`): A Rust-based Docker Compose wrapper that manages multiple projects from anywhere in the terminal. See [cli/CLAUDE.md](cli/CLAUDE.md) for detailed documentation.

2. **Compose Files Collection** (`collection/`): Pre-configured Docker Compose files for local development stacks. See [collection/CLAUDE.md](collection/CLAUDE.md) for detailed documentation.

## Quick Reference

### CLI (in `cli/` directory)
```bash
cargo build # Build
cargo test # Run tests
cargo run -- <args> # Run locally
```

### Collection (in `collection/` directory)
```bash
docker network create stack_dev # Create shared network
cp .env.dist .env # Setup environment
```

## CI/CD

GitHub Actions workflow (`.github/workflows/dctl_cli.yml`):
- Runs tests on push/PR to main
- Code coverage via grcov + Codecov
- Multi-platform release builds on tags (Linux, macOS, Windows; x86_64 and aarch64)
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ With `dctl`, you can start, stop, restart, build, and manage multiple Docker Com
## Roadmap

- [ ] Add more documentation and examples for local development.
- [ ] Add commands to register/unregister projects using docker-compose files.
- [ ] Make default arguments optional and project-specific.
- [ ] Improve merging of default and command-line arguments.

Expand All @@ -51,7 +50,14 @@ With `dctl`, you can start, stop, restart, build, and manage multiple Docker Com

### v2

- [ ] Refactor the CLI tool for better architecture and code quality.
- [x] Refactor the CLI tool for better architecture and code quality.
- [x] Declarative argument system with type validation.
- [x] Full `exec`/`run` command support with command arguments.
- [x] 65% code reduction through declarative definitions.
- [x] All 24 commands verified against Docker Compose documentation.
- [x] `register`/`unregister` commands to manage projects from CLI.
- [x] YAML validation in `check-config` with `--validate` flag.
- [x] Parallel execution for `infos` command.

---

Expand Down
181 changes: 181 additions & 0 deletions cli/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## Build and Test Commands

```bash
# Build
cargo build # Debug build
cargo build --release # Release build (with LTO, optimized for size)

# Test
cargo test # Run all tests
cargo test <test_name> # Run specific test
cargo test -- --nocapture # Show println! output

# Run locally
cargo run -- <args> # Example: cargo run -- up myproject
```

## Architecture

The CLI wraps `docker compose` commands, adding centralized project management via a TOML config file.

### Module Structure

```
src/
├── main.rs # Entry point, config loading
├── cli.rs # Command routing (clap), dispatches to handlers
├── command.rs # CommandHandler trait + module declarations
├── command/
│ ├── args.rs # Declarative argument system (ArgDef, CommandDef)
│ ├── definitions.rs # All 24 docker compose command definitions
│ ├── registry.rs # Command registry with handlers
│ ├── cd.rs # cd command (non-compose)
│ ├── completion.rs # Shell completion
│ ├── config.rs # Config check command (with YAML validation)
│ ├── infos.rs # Project info command (parallel execution)
│ ├── register.rs # Register new projects to config
│ └── unregister.rs # Remove projects from config
├── parser/
│ └── config.rs # TOML config parsing, DctlConfig, ComposeItem
└── utils/
├── docker.rs # Container trait, Docker struct, command execution
└── system.rs # System::execute() - async process spawning
```

### Command Definition System (v2.0)

Commands are defined declaratively in `definitions.rs` using the argument system from `args.rs`:

```rust
// Example from definitions.rs
pub fn up_def() -> CommandDef {
CommandDef {
name: "up",
about: "Create and start containers",
needs_project: true,
args: vec![
ArgDef::Flag { id: "DETACH", long: "detach", short: Some('d'), help: "..." },
ArgDef::Choice { id: "PULL", long: "pull", ..., choices: &["always", "missing", "never"] },
ArgDef::Number { id: "TIMEOUT", long: "timeout", short: Some('t'), help: "..." },
ArgDef::Services,
],
}
}
```

#### Argument Types

| Type | Usage | Example |
|------|-------|---------|
| `ArgDef::Flag` | Boolean flags | `--detach`, `--build` |
| `ArgDef::Value` | String values | `--user root`, `--workdir /app` |
| `ArgDef::Choice` | Predefined choices (validated) | `--pull always\|missing\|never` |
| `ArgDef::Number` | Numeric values (validated >= 0) | `--timeout 30` |
| `ArgDef::Services` | Multiple service names | `web api db` |
| `ArgDef::Container` | Single container name | `web` |
| `ArgDef::ServiceWithCommand` | Service + command + args | `php bash -c "echo hi"` |

### Key Traits

- `CommandHandler` (`command.rs`): Interface for command handlers
```rust
pub trait CommandHandler {
fn name(&self) -> &'static str;
fn cli(&self) -> Command;
fn command_type(&self) -> CommandType;
fn prepare(&self, args: &ArgMatches) -> Vec<OsString>;
}
```
- `CliConfig` (`parser/config.rs`): Interface for config operations
- `Container` (`utils/docker.rs`): Interface for docker command execution

### Config Structure

Config file location: `~/.config/dctl/config.toml` (override with `DCTL_CONFIG_FILE_PATH` env var)

```toml
[main]
docker_bin = "/usr/bin/docker"
default_command_args = [
{ command_name = "up", command_args = ["-d", "--remove-orphans"] }
]

[[collections]]
alias = "myproject"
compose_files = ["/path/to/docker-compose.yml"]
enviroment_file = "/path/to/.env" # optional
use_project_name = true # optional, default true
description = "My project" # optional
```

### Command Flow

1. `main.rs`: Load config from TOML, init `Docker` struct
2. `cli.rs:run()`: Parse command via clap, get project's `ComposeItem`
3. `registry.rs`: Find command handler by name
4. `Docker::compose()`: Build full command with config args + default args + CLI args
5. `System::execute()`: Spawn docker process asynchronously

### Adding a New Command

1. Add definition in `command/definitions.rs`:
```rust
pub fn mycommand_def() -> CommandDef {
CommandDef {
name: "mycommand",
about: "Description",
needs_project: true,
args: vec![/* ... */],
}
}
```

2. Add `CommandType` variant in `utils/docker.rs`:
```rust
pub enum CommandType {
// ...
MyCommand,
}
```

3. Register in `command/registry.rs`:
```rust
define_command_from_def!(MyCommandCommand, MyCommand, mycommand_def);
// Add to get_compose_commands()
```

4. Add tests in `command/definitions_tests.rs`

### Testing

Tests are colocated with modules (`#[cfg(test)] mod tests`). The `mockall` crate is used for mocking `System` in tests.

Key test files:
- `command/args.rs` - Argument system tests
- `command/definitions_tests.rs` - All 24 command definitions tests (65+ tests)
- `command/registry.rs` - Registry tests
- `command/register.rs` - Register command tests
- `command/unregister.rs` - Unregister command tests
- `parser/tests.rs` - Config parsing tests
- `utils/docker.rs` - Command preparation tests
- `utils/system_tests.rs` - System execution tests

Total: **148 unit tests**

### Supported Docker Compose Commands

All 24 docker compose commands are supported:
`build`, `config`, `create`, `down`, `events`, `exec`, `images`, `kill`, `logs`, `ls`, `pause`, `port`, `ps`, `pull`, `push`, `restart`, `rm`, `run`, `start`, `stop`, `top`, `unpause`, `up`, `watch`

### CLI-specific Commands

- `infos` - List all projects with status (parallel execution)
- `cd` - Print project directory path
- `check-config` - Validate config files (use `--validate` for YAML syntax check)
- `completion` - Generate shell completions
- `register` - Add project to config: `dctl register alias file1.yml [file2.yml...] [-e .env] [-d "desc"]`
- `unregister` - Remove project from config: `dctl unregister alias [--force]`
Loading