mirror of
https://github.com/tcsenpai/deu.git
synced 2025-06-06 11:36:04 +00:00
global configs
This commit is contained in:
parent
546ffb5f10
commit
6a460d16cf
59
README.md
59
README.md
@ -1,62 +1,67 @@
|
|||||||
# deu (Docker Environment Utility)
|
# DEU - Docker Environment Utility
|
||||||
|
|
||||||
A simple utility to create and manage development containers.
|
A simple utility to create and manage development containers using Docker Compose.
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
deu helps you quickly set up development containers with a consistent configuration. It creates a Docker Compose setup and manages container lifecycle.
|
`deu` helps you quickly set up development environments in Docker containers. It creates a Docker Compose configuration and manages container states (start, stop, logs, etc.).
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Make the script executable
|
|
||||||
chmod +x deu.py
|
chmod +x deu.py
|
||||||
|
sudo ln -s $(pwd)/deu.py /usr/local/bin/deu
|
||||||
# Optionally, create a symlink
|
|
||||||
ln -s $(pwd)/deu.py /usr/local/bin/deu
|
|
||||||
|
|
||||||
# Or copy it
|
|
||||||
|
|
||||||
cp $(pwd)/deu.py /usr/local/bin/deu
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
### Initialize a container
|
### Initialize a Container
|
||||||
|
|
||||||
|
Create a new development container:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Initialize with a random service name
|
# Local container (in current directory)
|
||||||
deu init .container --image python:3.11
|
deu init --image ubuntu:24.04
|
||||||
|
|
||||||
# Initialize with a specific service name
|
# Global container (stored in ~/.config/deu/)
|
||||||
deu init .container --image python:3.11 --service my_service
|
deu init -g --image ubuntu:24.04
|
||||||
|
|
||||||
|
# With custom service name
|
||||||
|
deu init --image ubuntu:24.04 --service my_container
|
||||||
```
|
```
|
||||||
|
|
||||||
### Container Management
|
### Manage Containers
|
||||||
|
|
||||||
|
All commands support both local and global containers:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Start container in background
|
|
||||||
deu background
|
|
||||||
|
|
||||||
# Activate container shell
|
# Activate container shell
|
||||||
deu activate
|
deu activate # Use local container
|
||||||
|
deu activate my_container # Use specific container (local or global)
|
||||||
|
|
||||||
|
# Start container in background
|
||||||
|
deu background # Use local container
|
||||||
|
deu background my_container
|
||||||
|
|
||||||
# View container logs
|
# View container logs
|
||||||
deu logs
|
deu logs # Use local container
|
||||||
|
deu logs my_container
|
||||||
|
|
||||||
# Stop container
|
# Stop container
|
||||||
deu stop
|
deu stop # Use local container
|
||||||
|
deu stop my_container
|
||||||
|
|
||||||
# Remove container
|
# Remove container
|
||||||
deu rm
|
deu rm # Use local container
|
||||||
|
deu rm my_container
|
||||||
```
|
```
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
deu creates two configuration files:
|
`deu` creates two configuration files:
|
||||||
|
|
||||||
1. `.container/docker-compose.yml`: Docker Compose configuration
|
1. `deu.toml` - Container configuration (local or in ~/.config/deu/)
|
||||||
2. `deu.toml`: Container metadata and settings
|
2. `.container/docker-compose.yml` - Docker Compose configuration (local or in ~/.config/deu/.container_name/)
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
|
250
deu.py
250
deu.py
@ -199,60 +199,66 @@ def get_container_config() -> ContainerConfig:
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|
||||||
def handle_activate() -> None:
|
def get_global_config_dir() -> Path:
|
||||||
"""Activate the container shell."""
|
"""Get the global configuration directory."""
|
||||||
config = get_container_config()
|
return Path.home() / ".config" / "deu"
|
||||||
print(f"Trying to activate container shell for service '{config.service}'...")
|
|
||||||
|
|
||||||
# Check if container is running, start it if not
|
|
||||||
|
def find_container_config(
|
||||||
|
container_name: Optional[str] = None,
|
||||||
|
) -> Optional[ContainerConfig]:
|
||||||
|
"""Find container configuration, checking local then global."""
|
||||||
|
# If container name is provided, search for it
|
||||||
|
if container_name:
|
||||||
|
# First check local deu.toml
|
||||||
|
local_toml = Path.cwd() / "deu.toml"
|
||||||
|
if local_toml.exists():
|
||||||
try:
|
try:
|
||||||
ps_output = subprocess.run(
|
config = toml.loads(local_toml.read_text())
|
||||||
["docker", "compose", "-f", str(config.path / "docker-compose.yml"), "ps"],
|
if config["container"]["service"] == container_name:
|
||||||
check=True,
|
return get_container_config()
|
||||||
capture_output=True,
|
except Exception:
|
||||||
text=True,
|
pass
|
||||||
|
|
||||||
|
# Then check global configs
|
||||||
|
global_dir = get_global_config_dir()
|
||||||
|
if global_dir.exists():
|
||||||
|
for toml_file in global_dir.glob("*.toml"):
|
||||||
|
try:
|
||||||
|
config = toml.loads(toml_file.read_text())
|
||||||
|
if config["container"]["service"] == container_name:
|
||||||
|
return ContainerConfig(
|
||||||
|
path=Path(config["container"]["path"]),
|
||||||
|
service=config["container"]["service"],
|
||||||
|
image=config["container"]["image"],
|
||||||
|
shell=ShellType[config["container"]["shell"]],
|
||||||
)
|
)
|
||||||
if config.service not in ps_output.stdout:
|
except Exception:
|
||||||
logger.info(f"Container '{config.service}' is not running. Starting it...")
|
continue
|
||||||
run_docker_compose_command(config.path, "up -d")
|
return None
|
||||||
except subprocess.CalledProcessError:
|
|
||||||
logger.info(f"Container '{config.service}' is not running. Starting it...")
|
|
||||||
run_docker_compose_command(config.path, "up -d")
|
|
||||||
|
|
||||||
# Use appropriate shell based on configuration
|
# If no container name, just check local deu.toml
|
||||||
shell = "bash" if config.shell == ShellType.BASH else "sh"
|
local_toml = Path.cwd() / "deu.toml"
|
||||||
run_docker_compose_command(config.path, f"exec {config.service} {shell}")
|
if local_toml.exists():
|
||||||
|
return get_container_config()
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def handle_background() -> None:
|
def create_dev_container(
|
||||||
"""Start the container in background mode."""
|
path: str, image: str, service_name: str, is_global: bool = False
|
||||||
container_path = get_container_config().path
|
) -> None:
|
||||||
run_docker_compose_command(container_path, "up", detach=True)
|
|
||||||
|
|
||||||
|
|
||||||
def handle_logs() -> None:
|
|
||||||
"""Show container logs."""
|
|
||||||
container_path = get_container_config().path
|
|
||||||
run_docker_compose_command(container_path, "logs -f")
|
|
||||||
|
|
||||||
|
|
||||||
def handle_stop() -> None:
|
|
||||||
"""Stop the container."""
|
|
||||||
container_path = get_container_config().path
|
|
||||||
run_docker_compose_command(container_path, "stop")
|
|
||||||
|
|
||||||
|
|
||||||
def handle_rm() -> None:
|
|
||||||
"""Remove the container."""
|
|
||||||
container_path = get_container_config().path
|
|
||||||
run_docker_compose_command(container_path, "down")
|
|
||||||
|
|
||||||
|
|
||||||
def create_dev_container(path: str, image: str, service_name: str) -> None:
|
|
||||||
"""Create a development container configuration."""
|
"""Create a development container configuration."""
|
||||||
try:
|
try:
|
||||||
|
if is_global:
|
||||||
|
# Create global config directory first
|
||||||
|
global_dir = get_global_config_dir()
|
||||||
|
global_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
container_path = global_dir / f".{service_name}"
|
||||||
|
toml_path = global_dir / f"{service_name}.toml"
|
||||||
|
else:
|
||||||
container_path = Path(path)
|
container_path = Path(path)
|
||||||
validate_path(container_path)
|
toml_path = Path.cwd() / "deu.toml"
|
||||||
|
|
||||||
validate_image(image)
|
validate_image(image)
|
||||||
|
|
||||||
# Ensure image exists and detect shell
|
# Ensure image exists and detect shell
|
||||||
@ -264,7 +270,7 @@ def create_dev_container(path: str, image: str, service_name: str) -> None:
|
|||||||
)
|
)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# Create the container directory if it doesn't exist
|
# Create the container directory
|
||||||
container_path.mkdir(parents=True, exist_ok=True)
|
container_path.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
# Create configurations
|
# Create configurations
|
||||||
@ -278,7 +284,7 @@ def create_dev_container(path: str, image: str, service_name: str) -> None:
|
|||||||
|
|
||||||
# Write configurations
|
# Write configurations
|
||||||
write_config(container_path / "docker-compose.yml", compose_config, "yaml")
|
write_config(container_path / "docker-compose.yml", compose_config, "yaml")
|
||||||
write_config(Path.cwd() / "deu.toml", toml_config, "toml")
|
write_config(toml_path, toml_config, "toml")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to create development container: {e}")
|
logger.error(f"Failed to create development container: {e}")
|
||||||
@ -302,6 +308,96 @@ def generate_service_name() -> str:
|
|||||||
return f"{random.choice(adjectives)}_{random.choice(nouns)}_{random_number}"
|
return f"{random.choice(adjectives)}_{random.choice(nouns)}_{random_number}"
|
||||||
|
|
||||||
|
|
||||||
|
def handle_activate(container_name: Optional[str] = None) -> None:
|
||||||
|
"""Activate the container shell."""
|
||||||
|
config = find_container_config(container_name)
|
||||||
|
if not config:
|
||||||
|
if container_name:
|
||||||
|
logger.error(f"Container '{container_name}' not found locally or globally.")
|
||||||
|
else:
|
||||||
|
logger.error(
|
||||||
|
"No local container configuration found. Run 'deu init' first or specify a container name."
|
||||||
|
)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
print(f"Trying to activate container shell for service '{config.service}'...")
|
||||||
|
|
||||||
|
# Check if container is running, start it if not
|
||||||
|
try:
|
||||||
|
ps_output = subprocess.run(
|
||||||
|
["docker", "compose", "-f", str(config.path / "docker-compose.yml"), "ps"],
|
||||||
|
check=True,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
)
|
||||||
|
if config.service not in ps_output.stdout:
|
||||||
|
logger.info(f"Container '{config.service}' is not running. Starting it...")
|
||||||
|
run_docker_compose_command(config.path, "up -d")
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
logger.info(f"Container '{config.service}' is not running. Starting it...")
|
||||||
|
run_docker_compose_command(config.path, "up -d")
|
||||||
|
|
||||||
|
# Use appropriate shell based on configuration
|
||||||
|
shell = "bash" if config.shell == ShellType.BASH else "sh"
|
||||||
|
run_docker_compose_command(config.path, f"exec {config.service} {shell}")
|
||||||
|
|
||||||
|
|
||||||
|
def handle_background(container_name: Optional[str] = None) -> None:
|
||||||
|
"""Start the container in background mode."""
|
||||||
|
config = find_container_config(container_name)
|
||||||
|
if not config:
|
||||||
|
if container_name:
|
||||||
|
logger.error(f"Container '{container_name}' not found locally or globally.")
|
||||||
|
else:
|
||||||
|
logger.error(
|
||||||
|
"No local container configuration found. Run 'deu init' first or specify a container name."
|
||||||
|
)
|
||||||
|
sys.exit(1)
|
||||||
|
run_docker_compose_command(config.path, "up", detach=True)
|
||||||
|
|
||||||
|
|
||||||
|
def handle_logs(container_name: Optional[str] = None) -> None:
|
||||||
|
"""Show container logs."""
|
||||||
|
config = find_container_config(container_name)
|
||||||
|
if not config:
|
||||||
|
if container_name:
|
||||||
|
logger.error(f"Container '{container_name}' not found locally or globally.")
|
||||||
|
else:
|
||||||
|
logger.error(
|
||||||
|
"No local container configuration found. Run 'deu init' first or specify a container name."
|
||||||
|
)
|
||||||
|
sys.exit(1)
|
||||||
|
run_docker_compose_command(config.path, "logs -f")
|
||||||
|
|
||||||
|
|
||||||
|
def handle_stop(container_name: Optional[str] = None) -> None:
|
||||||
|
"""Stop the container."""
|
||||||
|
config = find_container_config(container_name)
|
||||||
|
if not config:
|
||||||
|
if container_name:
|
||||||
|
logger.error(f"Container '{container_name}' not found locally or globally.")
|
||||||
|
else:
|
||||||
|
logger.error(
|
||||||
|
"No local container configuration found. Run 'deu init' first or specify a container name."
|
||||||
|
)
|
||||||
|
sys.exit(1)
|
||||||
|
run_docker_compose_command(config.path, "stop")
|
||||||
|
|
||||||
|
|
||||||
|
def handle_rm(container_name: Optional[str] = None) -> None:
|
||||||
|
"""Remove the container."""
|
||||||
|
config = find_container_config(container_name)
|
||||||
|
if not config:
|
||||||
|
if container_name:
|
||||||
|
logger.error(f"Container '{container_name}' not found locally or globally.")
|
||||||
|
else:
|
||||||
|
logger.error(
|
||||||
|
"No local container configuration found. Run 'deu init' first or specify a container name."
|
||||||
|
)
|
||||||
|
sys.exit(1)
|
||||||
|
run_docker_compose_command(config.path, "down")
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
"""Main entry point for the script."""
|
"""Main entry point for the script."""
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
@ -312,35 +408,69 @@ def main() -> None:
|
|||||||
|
|
||||||
# Init command
|
# Init command
|
||||||
init_parser = subparsers.add_parser("init", help="Initialize a new container")
|
init_parser = subparsers.add_parser("init", help="Initialize a new container")
|
||||||
init_parser.add_argument("path", help="Path to the .container directory")
|
init_parser.add_argument("path", nargs="?", help="Path to the .container directory")
|
||||||
init_parser.add_argument("--image", required=True, help="Docker image to use")
|
init_parser.add_argument("--image", required=True, help="Docker image to use")
|
||||||
init_parser.add_argument(
|
init_parser.add_argument(
|
||||||
"--service",
|
"--service",
|
||||||
default=generate_service_name(),
|
default=generate_service_name(),
|
||||||
help="Name of the container service",
|
help="Name of the container service",
|
||||||
)
|
)
|
||||||
|
init_parser.add_argument(
|
||||||
|
"-g",
|
||||||
|
"--global",
|
||||||
|
dest="is_global",
|
||||||
|
action="store_true",
|
||||||
|
help="Create container in global config",
|
||||||
|
)
|
||||||
|
|
||||||
# Other commands
|
# Activate command
|
||||||
subparsers.add_parser("activate", help="Activate container shell")
|
activate_parser = subparsers.add_parser("activate", help="Activate container shell")
|
||||||
subparsers.add_parser("background", help="Start container in background")
|
activate_parser.add_argument(
|
||||||
subparsers.add_parser("logs", help="Show container logs")
|
"container_name", nargs="?", help="Name of the container to activate"
|
||||||
subparsers.add_parser("stop", help="Stop container")
|
)
|
||||||
subparsers.add_parser("rm", help="Remove container")
|
|
||||||
|
# Background command
|
||||||
|
background_parser = subparsers.add_parser(
|
||||||
|
"background", help="Start container in background"
|
||||||
|
)
|
||||||
|
background_parser.add_argument(
|
||||||
|
"container_name", nargs="?", help="Name of the container to start"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Logs command
|
||||||
|
logs_parser = subparsers.add_parser("logs", help="Show container logs")
|
||||||
|
logs_parser.add_argument(
|
||||||
|
"container_name", nargs="?", help="Name of the container to show logs for"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Stop command
|
||||||
|
stop_parser = subparsers.add_parser("stop", help="Stop container")
|
||||||
|
stop_parser.add_argument(
|
||||||
|
"container_name", nargs="?", help="Name of the container to stop"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Remove command
|
||||||
|
rm_parser = subparsers.add_parser("rm", help="Remove container")
|
||||||
|
rm_parser.add_argument(
|
||||||
|
"container_name", nargs="?", help="Name of the container to remove"
|
||||||
|
)
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
if args.command == "init":
|
if args.command == "init":
|
||||||
create_dev_container(args.path, args.image, args.service)
|
create_dev_container(
|
||||||
|
args.path or ".container", args.image, args.service, args.is_global
|
||||||
|
)
|
||||||
elif args.command == "activate":
|
elif args.command == "activate":
|
||||||
handle_activate()
|
handle_activate(args.container_name)
|
||||||
elif args.command == "background":
|
elif args.command == "background":
|
||||||
handle_background()
|
handle_background(args.container_name)
|
||||||
elif args.command == "logs":
|
elif args.command == "logs":
|
||||||
handle_logs()
|
handle_logs(args.container_name)
|
||||||
elif args.command == "stop":
|
elif args.command == "stop":
|
||||||
handle_stop()
|
handle_stop(args.container_name)
|
||||||
elif args.command == "rm":
|
elif args.command == "rm":
|
||||||
handle_rm()
|
handle_rm(args.container_name)
|
||||||
else:
|
else:
|
||||||
parser.print_help()
|
parser.print_help()
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user