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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## USER ADDED
.env.secret
.vscode/
.idea/

# MkDocs (no longer used - using Ibis HTML directly)
site/
Expand Down
5 changes: 3 additions & 2 deletions cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@

import typer

from commands import hello, list, version
from commands import hello, list, show, version

app = typer.Typer(help="101 Linux Commands CLI 🚀")
app.add_typer(hello.app, name="hello")
app.add_typer(list.app, name="list")
app.add_typer(version.app, name="version")
app.command()(show.show)


def main() -> None:
Expand All @@ -18,4 +19,4 @@ def main() -> None:


if __name__ == "__main__":
app()
main()
98 changes: 98 additions & 0 deletions cli/commands/show.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
from typing import Dict, TypedDict

import typer


class CommandInfo(TypedDict):
description: str
usage: str
example: str
notes: str


COMMANDS: Dict[str, CommandInfo] = {
"ls": {
"description": (
"List information about the files in the current directory (the default). "
"Options can be used to modify the output format, sort order, and more."
),
"usage": "ls [OPTION]... [FILE]...",
"example": "ls -la /var/log",
"notes": (
"- `-l` : use a long listing format\n"
"- `-a` : show hidden files (starting with `.`)\n"
"- `-h` : with -l, print sizes in human-readable format (e.g., 1K, 234M)"
),
},
"grep": {
"description": (
"Search input files for lines containing a match to the given PATTERN. "
"Often used for filtering log files or searching through code."
),
"usage": "grep [OPTION]... PATTERN [FILE]...",
"example": 'grep -R "TODO" .',
"notes": (
"- `-i` : ignore case distinctions\n"
"- `-R` : recursively search subdirectories\n"
"- `-n` : show line numbers of matches"
),
},
"cat": {
"description": (
"Concatenate files and print on the standard output. "
"Often used to quickly view file contents."
),
"usage": "cat [OPTION]... [FILE]...",
"example": "cat /etc/passwd",
"notes": (
"- `-n` : number all output lines\n"
"- `-b` : number non-blank lines\n"
"- Use with pipes: `cat file.txt | grep pattern`"
),
},
"mkdir": {
"description": (
"Create directories if they do not already exist. "
"By default, it creates a single directory."
),
"usage": "mkdir [OPTION]... DIRECTORY...",
"example": "mkdir -p projects/python/app",
"notes": (
"- `-p` : create parent directories as needed\n"
"- `-v` : print a message for each created directory"
),
},
}


def show(
command: str = typer.Argument(..., help="Linux command to show details for")
) -> None:
"""
Display description, usage, examples, and notes for a given Linux command.
"""

key = command.strip()
info = COMMANDS.get(key)

if not info:
typer.secho(
f"✖ Unknown command '{command}'. Try one of: {', '.join(sorted(COMMANDS))}",
err=True,
fg=typer.colors.RED,
)
raise typer.Exit(code=1)

typer.secho(f"\nCommand: {key}\n", fg=typer.colors.CYAN, bold=True)

typer.secho("Description:", fg=typer.colors.MAGENTA, bold=True)
typer.echo(f" {info['description']}\n")

typer.secho("Usage:", fg=typer.colors.MAGENTA, bold=True)
typer.echo(f" {info['usage']}\n")

typer.secho("Example:", fg=typer.colors.MAGENTA, bold=True)
typer.echo(f" {info['example']}\n")

typer.secho("Notes:", fg=typer.colors.MAGENTA, bold=True)
typer.echo(f"{info['notes']}\n")
91 changes: 48 additions & 43 deletions cli/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,66 +6,49 @@
import sys


def test_cli_help():
"""Test that the CLI shows help."""
def run_cli(args):
"""Helper to run CLI with subprocess and capture output."""
result = subprocess.run(
[sys.executable, "cli.py", "--help"],
[sys.executable, "cli.py", *args],
capture_output=True,
text=True,
cwd=os.path.dirname(__file__),
check=True,
)
return result


def test_cli_help():
"""Test that the CLI shows help."""
result = run_cli(["--help"])
assert result.returncode == 0
assert "101 Linux Commands CLI" in result.stdout


def test_hello_command():
"""Test the hello command."""
result = subprocess.run(
[sys.executable, "cli.py", "hello", "greet"],
capture_output=True,
text=True,
cwd=os.path.dirname(__file__),
check=True,
)
result = run_cli(["hello", "greet"])
assert result.returncode == 0
assert "Hello, World!" in result.stdout


def test_hello_command_with_name():
"""Test the hello command with a custom name."""
result = subprocess.run(
[sys.executable, "cli.py", "hello", "greet", "--name", "Linux"],
capture_output=True,
text=True,
cwd=os.path.dirname(__file__),
check=True,
)
result = run_cli(["hello", "greet", "--name", "Linux"])
assert result.returncode == 0
assert "Hello, Linux!" in result.stdout


def test_hello_help():
"""Test the hello command help."""
result = subprocess.run(
[sys.executable, "cli.py", "hello", "--help"],
capture_output=True,
text=True,
cwd=os.path.dirname(__file__),
check=True,
)
result = run_cli(["hello", "--help"])
assert result.returncode == 0
assert "Hello command group" in result.stdout


def test_list_command():
"""Test the list command."""
result = subprocess.run(
[sys.executable, "cli.py", "list"],
capture_output=True,
text=True,
cwd=os.path.dirname(__file__),
)
result = run_cli(["list"])
assert result.returncode == 0
assert "ls - List directory contents." in result.stdout
assert "cd - Change directory." in result.stdout
assert "pwd - Print working directory." in result.stdout
Expand All @@ -74,29 +57,48 @@ def test_list_command():

def test_version_command():
"""Test the version command"""
result = subprocess.run(
[sys.executable, "cli.py", "version"],
capture_output=True,
text=True,
cwd=os.path.dirname(__file__),
)
result = run_cli(["version"])
assert result.returncode == 0
assert "101-linux v" in result.stdout
assert "0.1.0" in result.stdout


def test_version_show_command():
"""Test the version show subcommand."""
result = subprocess.run(
[sys.executable, "cli.py", "version", "show"],
capture_output=True,
text=True,
cwd=os.path.dirname(__file__),
)
result = run_cli(["version", "show"])
assert result.returncode == 0
assert "101-linux v0.1.0" in result.stdout


# ----------------------------
# Tests for `show` subcommand
# ----------------------------


def test_show_ls():
"""Test the show command with ls."""
result = run_cli(["show", "ls"])
assert result.returncode == 0
assert "ls" in result.stdout
assert "List" in result.stdout


def test_show_grep():
"""Test the show command with grep."""
result = run_cli(["show", "grep"])
assert result.returncode == 0
assert "grep" in result.stdout
assert "Search" in result.stdout or "Print" in result.stdout


def test_show_invalid():
"""Test the show command with an invalid command."""
result = run_cli(["show", "foobar"])
assert result.returncode != 0
combined_output = result.stdout + result.stderr
assert "Unknown" in combined_output or "Error" in combined_output


if __name__ == "__main__":
test_cli_help()
test_hello_command()
Expand All @@ -105,4 +107,7 @@ def test_version_show_command():
test_list_command()
test_version_command()
test_version_show_command()
test_show_ls()
test_show_grep()
test_show_invalid()
print("✅ All tests passed!")