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
36 changes: 36 additions & 0 deletions .github/workflows/code-quality.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Code Quality Checks

on:
pull_request:
branches:
- main

jobs:
lint:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.8'

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install .[test]

- name: Run Black
run: black --check .

- name: Run Isort
run: isort --check-only .

# - name: Run Flake8
# run: flake8 .

# - name: Run MyPy
# run: mypy .
10 changes: 8 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
name: Test

on: [push, pull_request]
on:
pull_request:
branches:
- main


permissions:
contents: read

jobs:
test:
runs-on: ubuntu-latest
runs-on: ubuntu-${{ matrix.ubuntu-version }}
strategy:
matrix:
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
ubuntu-version: ["20.04", "22.04"]
fail-fast: false
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
Expand Down
5 changes: 5 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
repos:
- repo: https://github.com/psf/black
rev: 23.1.0 # Use the latest version
hooks:
- id: black
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,18 @@ Install this tool using `pip`:
```bash
pip install arm-cli
```
## Usage

Once installed, setup the CLI initially by running `arm-cli system setup`. You may need to rerun if you update the CLI via pip. This will do things like configure system settings to enable tab complete.

## Usage
### Initial Setup
For help, run:
```bash
arm-cli --help
```
You can also use:


```bash
python -m arm_cli --help
```
Expand Down
3 changes: 2 additions & 1 deletion arm_cli/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
from beartype.claw import beartype_this_package
beartype_this_package()

beartype_this_package()
13 changes: 9 additions & 4 deletions arm_cli/cli.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,21 @@
import click
from arm_cli.container import container

from arm_cli.container.container import container
from arm_cli.self.self import self
from arm_cli.system.system import system

@click.group()

@click.version_option()
@click.group(context_settings=dict(help_option_names=["-h", "--help"]))
def cli():
"""Experimental CLI for deploying robotic applications"""
pass

# Add the container management commands as a sub-group

# Add command groups
cli.add_command(container)
cli.add_command(self)
cli.add_command(system)

if __name__ == "__main__":
cli()
cli()
55 changes: 0 additions & 55 deletions arm_cli/container.py

This file was deleted.

98 changes: 98 additions & 0 deletions arm_cli/container/container.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import subprocess

import click
import docker
import inquirer


@click.group()
def container():
"""Manage Docker containers"""
pass


def get_running_containers():
"""Retrieve a list of running Docker containers"""
client = docker.from_env()
return client.containers.list(filters={"status": "running"})


@container.command("list")
def list_containers():
"""List all Docker containers"""
containers = get_running_containers()

if containers:
for container in containers:
print(f"{container.id[:12]}: {container.name}")
else:
print("No running containers found.")


@container.command("attach")
def attach_container():
"""Interactively select a running Docker container and attach to it"""
containers = get_running_containers()

if not containers:
print("No running containers found.")
return

container_choices = [
inquirer.List(
"container",
message="Select a container to attach to",
choices=[f"{container.name} ({container.id[:12]})" for container in containers],
carousel=True,
)
]

answers = inquirer.prompt(container_choices)
selected_container_name = answers["container"].split(" ")[0] # Extract container name

print(f"Attaching to {selected_container_name}...")

try:
subprocess.run(["docker", "exec", "-it", selected_container_name, "/bin/bash"], check=True)
except subprocess.CalledProcessError as e:
print(f"Error attaching to container: {e}")
except KeyboardInterrupt:
print("\nExiting interactive session...")


@container.command("stop")
def stop_container():
"""Interactively select a running Docker container and stop it"""
containers = get_running_containers()

if not containers:
print("No running containers found.")
return

container_choices = [
inquirer.List(
"container",
message="Select a container to stop",
choices=[f"{container.name} ({container.id[:12]})" for container in containers],
carousel=True,
)
]

answers = inquirer.prompt(container_choices)
if not answers:
print("No container selected.")
return

selected_container_name = answers["container"].split(" ")[0] # Extract container name

print(f"Stopping {selected_container_name}...")

try:
client = docker.from_env()
container = client.containers.get(selected_container_name)
container.stop()
print(f"Container {selected_container_name} stopped successfully.")
except docker.errors.NotFound:
print(f"Error: Container {selected_container_name} not found.")
except docker.errors.APIError as e:
print(f"Error stopping container: {e}")
Empty file added arm_cli/self/__init__.py
Empty file.
38 changes: 38 additions & 0 deletions arm_cli/self/self.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import os
import subprocess
import sys

import click


@click.group()
def self():
"""Manage the CLI itself"""
pass


@self.command()
@click.option(
"--source",
default=".",
type=click.Path(exists=True),
help="Install from a local source path",
show_default=True,
)
def update(source):
"""Update arm-cli from PyPI or source"""
if source:
print(f"Installing arm-cli from source at {source}...")

# Clear Python import cache
print("Clearing Python caches...")
subprocess.run(["rm", "-rf", os.path.expanduser("~/.cache/pip")])
subprocess.run(["python", "-c", "import importlib; importlib.invalidate_caches()"])

# Install from the provided source path
subprocess.run([sys.executable, "-m", "pip", "install", "-e", source], check=True)
print(f"arm-cli installed from source at {source} successfully!")
else:
print("Updating arm-cli from PyPI...")
subprocess.run([sys.executable, "-m", "pip", "install", "--upgrade", "arm-cli"], check=True)
print("arm-cli updated successfully!")
6 changes: 3 additions & 3 deletions arm_cli/system/setup_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
import subprocess
import sys

from arm_cli.system.shell_scripts import get_current_shell_addins, detect_shell
from arm_cli.system.shell_scripts import detect_shell, get_current_shell_addins


def setup_xhost():
"""Setup xhost for GUI applications"""
Expand All @@ -20,6 +21,7 @@ def is_line_in_file(line, filepath) -> bool:
with open(filepath, "r") as f:
return any(line.strip() in l.strip() for l in f)


def setup_shell():
"""Setup shell addins for autocomplete"""
shell = detect_shell()
Expand All @@ -33,5 +35,3 @@ def setup_shell():
f.write(f"\n{line}\n")
else:
print(f"Unsupported shell: {shell}", file=sys.stderr)


7 changes: 4 additions & 3 deletions arm_cli/system/shell_scripts/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@
def get_script_dir():
return os.path.dirname(__file__)


def detect_shell():
shell = os.path.basename(os.getenv("SHELL", ""))
return shell

def get_current_shell_addins():

def get_current_shell_addins():
shell = detect_shell()
script_dir = get_script_dir()

Expand All @@ -22,5 +23,5 @@ def get_current_shell_addins():
return os.path.join(script_dir, "shell_addins.fish")
else:
print(f"Unsupported shell: {shell}", file=sys.stderr)
return ""

return ""
9 changes: 5 additions & 4 deletions arm_cli/system/system.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import subprocess

import click
import docker
import inquirer
import subprocess

from arm_cli.system.setup_utils import setup_xhost, setup_shell
from arm_cli.system.setup_utils import setup_shell, setup_xhost


@click.group()
def system():
"""Manage the system this CLI is running on"""
pass



@system.command()
def setup():
"""Generic setup (will be refined later)"""
Expand All @@ -21,4 +22,4 @@ def setup():
setup_shell()

# Additional setup code can go here (e.g., starting containers, attaching, etc.)
pass
pass
Loading
Loading