Skip to content
Open
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
3 changes: 3 additions & 0 deletions docs_src/src/pages/documentation/en/api_reference/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ While there are other more extensions of Robyn like

```bash {{ title: 'pip' }}
pip install "robyn[templating]"
pip install "robyn[pydantic]"
pip install "robyn[robyn-config]"
pip install "robyn[all]"
```

```bash {{ title: 'conda' }}
Expand Down
27 changes: 27 additions & 0 deletions docs_src/src/pages/documentation/en/plugins.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,33 @@ In this example, robyn-rate-limits is used to enforce a rate limit of 3 requests

The plugin integrates seamlessly with the Robyn web framework, enhancing the security and stability of your application by preventing excessive requests from a single client.

### Robyn Config

- Description: A powerful CLI tool to bootstrap and manage production-ready Robyn applications with best practices built-in. It scaffolds projects using DDD or MVC architecture with SQLAlchemy or Tortoise ORM, adds new entities/routes/repositories, sets up admin panels, and configures monitoring pipelines.
- GitHub repository: [robyn-config](https://github.com/Lehsqa/robyn-config)
- Installation:
`python -m pip install robyn-config`

Or as a Robyn optional dependency:
`python -m pip install robyn[robyn-config]`
- Usage:

```bash
# Create a new Robyn project with DDD design pattern and SQLAlchemy
robyn-config create my_project ./my_project --design ddd --orm sqlalchemy

# Add new business logic entity to an existing project
robyn-config add users ./my_project

# Add admin panel scaffolding
robyn-config adminpanel ./my_project

# Add monitoring pipeline (Alloy + Loki + Grafana)
robyn-config monitoring ./my_project
```
Comment thread
coderabbitai[bot] marked this conversation as resolved.

The `robyn-config` CLI helps you quickly scaffold production-ready Robyn applications following established patterns and best practices. It generates boilerplate code for routes, repositories, services, and models so you can focus on business logic.

## What's next?

After exploring the plugins, Batman wanted to explore the community.So, Robyn pointed him to
Expand Down
134 changes: 134 additions & 0 deletions integration_tests/test_robyn_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
import os
import shutil
import subprocess
import tempfile

import pytest

_HAS_ROBYN_CONFIG = shutil.which("robyn-config") is not None

pytestmark = pytest.mark.skipif(not _HAS_ROBYN_CONFIG, reason="robyn-config not installed")


class TestRobynConfigCLI:
"""Verify that the robyn-config CLI is accessible and functional."""

def test_cli_help_exits_zero(self):
"""robyn-config --help should exit with code 0."""
result = subprocess.run(
["robyn-config", "--help"],
capture_output=True,
text=True,
timeout=30,
)
assert result.returncode == 0
assert "Usage" in result.stdout

def test_cli_create_help(self):
"""robyn-config create --help should list available options."""
result = subprocess.run(
["robyn-config", "create", "--help"],
capture_output=True,
text=True,
timeout=30,
)
assert result.returncode == 0
assert "create" in result.stdout.lower() or "Usage" in result.stdout

def test_cli_add_help(self):
"""robyn-config add --help should list available options."""
result = subprocess.run(
["robyn-config", "add", "--help"],
capture_output=True,
text=True,
timeout=30,
)
assert result.returncode == 0


class TestRobynConfigScaffolding:
"""Integration tests for project scaffolding."""

@pytest.fixture
def temp_dir(self):
"""Create a temporary directory for scaffolding tests."""
tmp = tempfile.mkdtemp(prefix="robyn_config_test_")
yield tmp
shutil.rmtree(tmp, ignore_errors=True)

@pytest.fixture
def scaffolded_project(self):
"""Create a scaffolded project for tests that build on top of it."""
tmp = tempfile.mkdtemp(prefix="robyn_config_scaffolded_")
subprocess.run(
["robyn-config", "create", "testproj", tmp, "--design", "ddd", "--orm", "sqlalchemy"],
capture_output=True,
text=True,
timeout=60,
)
yield tmp
shutil.rmtree(tmp, ignore_errors=True)

@pytest.mark.parametrize("design", ["ddd", "mvc"])
@pytest.mark.parametrize("orm", ["sqlalchemy", "tortoise"])
def test_create_project_scaffold(self, temp_dir, design, orm):
"""robyn-config create should scaffold a valid project structure."""
project_name = f"test_project_{design}_{orm}"

result = subprocess.run(
["robyn-config", "create", project_name, temp_dir, "--design", design, "--orm", orm],
capture_output=True,
text=True,
timeout=60,
)
assert result.returncode == 0, f"stderr: {result.stderr}"

# Verify essential files/directories exist in the scaffolded project
assert os.path.isfile(os.path.join(temp_dir, "pyproject.toml")), "pyproject.toml not found"
assert os.path.isdir(os.path.join(temp_dir, "src")), "src/ directory not found"
assert os.path.isfile(os.path.join(temp_dir, "Makefile")), "Makefile not found"
Comment thread
coderabbitai[bot] marked this conversation as resolved.

def test_adminpanel_scaffolding(self, scaffolded_project):
"""robyn-config adminpanel should add admin panel files to an existing project."""
result = subprocess.run(
["robyn-config", "adminpanel", scaffolded_project],
capture_output=True,
text=True,
timeout=60,
)
assert result.returncode == 0, f"stderr: {result.stderr}"

# Verify admin panel files were created
adminpanel_dir = os.path.join(scaffolded_project, "src", "app", "infrastructure", "adminpanel")
assert os.path.isdir(adminpanel_dir), "adminpanel directory not created"

def test_adminpanel_with_custom_credentials(self, scaffolded_project):
"""robyn-config adminpanel should accept custom username and password."""
result = subprocess.run(
["robyn-config", "adminpanel", scaffolded_project, "--username", "testadmin", "--password", "testpass"],
capture_output=True,
text=True,
timeout=60,
)
assert result.returncode == 0, f"stderr: {result.stderr}"

adminpanel_dir = os.path.join(scaffolded_project, "src", "app", "infrastructure", "adminpanel")
assert os.path.isdir(adminpanel_dir), "adminpanel directory not created"

def test_monitoring_scaffolding(self, scaffolded_project):
"""robyn-config monitoring should add monitoring pipeline to an existing project."""
result = subprocess.run(
["robyn-config", "monitoring", scaffolded_project],
capture_output=True,
text=True,
timeout=60,
)
assert result.returncode == 0, f"stderr: {result.stderr}"

# Verify monitoring files were created
assert os.path.isfile(os.path.join(scaffolded_project, "docker-compose.monitoring.yml")), "docker-compose.monitoring.yml not found"

monitoring_dir = os.path.join(scaffolded_project, "compose", "monitoring")
assert os.path.isdir(monitoring_dir), "compose/monitoring directory not created"
assert os.path.isdir(os.path.join(monitoring_dir, "alloy")), "alloy config not found"
assert os.path.isdir(os.path.join(monitoring_dir, "grafana")), "grafana config not found"
7 changes: 5 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ dependencies = [
[project.optional-dependencies]
"templating" = ["jinja2 >= 3.1.6, < 4.0.0"]
"pydantic" = ["pydantic >= 2.0.0, < 3.0.0"]
"all" = ["jinja2 >= 3.1.6, < 4.0.0", "pydantic >= 2.0.0, < 3.0.0"]
"robyn-config" = ["robyn-config >= 1.0.0, < 2.0.0"]
"all" = ["jinja2 >= 3.1.6, < 4.0.0", "pydantic >= 2.0.0, < 3.0.0", "robyn-config >= 1.0.0, < 2.0.0"]

[project.urls]
Documentation = "https://robyn.tech/"
Expand Down Expand Up @@ -83,13 +84,15 @@ multiprocess = "^0.70.18"
uvloop = { version = "0.22.1", markers = "sys_platform != 'win32' and (sys_platform != 'cygwin' and platform_python_implementation != 'PyPy')" }
jinja2 = { version = "^3.1.6", optional = true }
pydantic = { version = "^2.0.0", optional = true }
robyn-config = { version = "^1.0.0", optional = true }
rustimport = "^1.3.4"
orjson = "^3.11.5"

[tool.poetry.extras]
templating = ["jinja2"]
pydantic = ["pydantic"]
all = ["jinja2", "pydantic"]
robyn-config = ["robyn-config"]
all = ["jinja2", "pydantic", "robyn-config"]

[tool.poetry.group.dev]
optional = true
Expand Down