Skip to content

Misc updates #35

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 10, 2025
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: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ django_mongodb_cli.egg-info/
__pycache__
manage.py
mongo_app/
mongo_migrations/
mongo_project/
node_modules/
server.log
Expand Down
33 changes: 27 additions & 6 deletions django_mongodb_cli/app.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import click
import os
import shutil
import subprocess


from .utils import get_management_command
from .utils import get_management_command, random_app_name


class App:
Expand Down Expand Up @@ -35,17 +36,37 @@ def app(context):


@app.command()
@click.argument("app_name", required=False, default="mongo_app")
@click.argument("app_name", required=False)
def start(app_name):
"""Run startapp command with the template from src/django-mongodb-app."""
"""Run startapp with a custom template and move the app into ./apps/."""

click.echo("Running startapp.")
if not app_name:
app_name = random_app_name()

if not app_name.isidentifier():
raise click.UsageError(
f"App name '{app_name}' is not a valid Python identifier."
)

temp_path = app_name # Django will create the app here temporarily
target_path = os.path.join("apps", app_name)

click.echo(f"Creating app '{app_name}' in ./apps")

# Make sure ./apps exists
os.makedirs("apps", exist_ok=True)

# Run the Django startapp command
command = get_management_command("startapp")
subprocess.run(
command
+ [
app_name,
temp_path,
"--template",
os.path.join("src", "django-project-templates", "app_template"),
os.path.join("templates", "app_template"),
],
check=True,
)

# Move the generated app into ./apps/
shutil.move(temp_path, target_path)
65 changes: 19 additions & 46 deletions django_mongodb_cli/proj.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import subprocess


from .utils import get_management_command
from .utils import DELETE_DIRS_AND_FILES, get_management_command


class Proj:
Expand All @@ -22,13 +22,27 @@ def __repr__(self):


@click.group(invoke_without_command=True)
@click.option("-d", "--delete", is_flag=True, help="Delete existing project files")
@click.pass_context
def proj(context):
def proj(context, delete):
"""
Create Django projects configured to test django-mongodb-backend.
"""
context.obj = Proj()

if delete:
for item, check_function in DELETE_DIRS_AND_FILES.items():
if check_function(item):
if os.path.isdir(item):
shutil.rmtree(item)
click.echo(f"Removed directory: {item}")
elif os.path.isfile(item):
os.remove(item)
click.echo(f"Removed file: {item}")
else:
click.echo(f"Skipping: {item} does not exist")
return

# Show help only if no subcommand is invoked
if context.invoked_subcommand is None:
click.echo(context.get_help())
Expand Down Expand Up @@ -69,54 +83,17 @@ def run():


@proj.command()
@click.option("-d", "--delete", is_flag=True, help="Delete existing project files")
@click.option("-dj", "--django", is_flag=True, help="Use django mongodb template")
@click.option("-w", "--wagtail", is_flag=True, help="Use wagtail mongodb template")
@click.argument("project_name", required=False, default="backend")
def start(
delete,
django,
wagtail,
project_name,
):
"""Run Django's `startproject` with custom templates."""
if os.path.exists("manage.py"):
click.echo("manage.py already exists")
if not delete:
click.echo("Use -d to delete existing project files")
return
if delete:
items = {
".babelrc": os.path.isfile,
".dockerignore": os.path.isfile,
".browserslistrc": os.path.isfile,
".eslintrc": os.path.isfile,
".nvmrc": os.path.isfile,
".stylelintrc.json": os.path.isfile,
"Dockerfile": os.path.isfile,
"apps": os.path.isdir,
"home": os.path.isdir,
"backend": os.path.isdir,
"db.sqlite3": os.path.isfile,
"frontend": os.path.isdir,
"mongo_migrations": os.path.isdir,
"manage.py": os.path.isfile,
"package-lock.json": os.path.isfile,
"package.json": os.path.isfile,
"postcss.config.js": os.path.isfile,
"requirements.txt": os.path.isfile,
"search": os.path.isdir,
}
for item, check_function in items.items():
if check_function(item):
if os.path.isdir(item):
shutil.rmtree(item)
click.echo(f"Removed directory: {item}")
elif os.path.isfile(item):
os.remove(item)
click.echo(f"Removed file: {item}")
else:
click.echo(f"Skipping: {item} does not exist")
return
template = None
django_admin = "django-admin"
Expand All @@ -129,9 +106,7 @@ def start(
elif django:
template = os.path.join(os.path.join("src", "django-mongodb-project"))
if not template:
template = os.path.join(
os.path.join("src", "django-mongodb-templates", "project_template")
)
template = os.path.join(os.path.join("templates", "project_template"))
click.echo(f"Using template: {template}")
subprocess.run(
[
Expand All @@ -143,9 +118,7 @@ def start(
template,
]
)
frontend_template = os.path.join(
"src", "django-mongodb-templates", "frontend_template"
)
frontend_template = os.path.join("templates", "frontend_template")
click.echo(f"Using template: {frontend_template}")
subprocess.run(
[
Expand All @@ -158,7 +131,7 @@ def start(
]
)
if not wagtail:
home_template = os.path.join("src", "django-mongodb-templates", "home_template")
home_template = os.path.join("templates", "home_template")
click.echo(f"Using template: {home_template}")
subprocess.run(
[
Expand Down
145 changes: 78 additions & 67 deletions django_mongodb_cli/repo.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
get_management_command,
get_repos,
get_status,
get_repo_name_map,
install_package,
)

Expand All @@ -33,15 +34,6 @@ def __repr__(self):
pass_repo = click.make_pass_decorator(Repo)


def get_repo_name_map(repos, url_pattern):
"""Return a dict mapping repo_name to repo_url from a list of repo URLs."""
return {
os.path.basename(url_pattern.search(url).group(0)): url
for url in repos
if url_pattern.search(url)
}


@click.group(invoke_without_command=True)
@click.option(
"-l",
Expand All @@ -52,7 +44,7 @@ def get_repo_name_map(repos, url_pattern):
@click.pass_context
def repo(context, list_repos):
"""
Run Django fork and third-party library tests.
Run tests configured to test django-mongodb-backend.
"""
context.obj = Repo()
repos, url_pattern, branch_pattern = get_repos("pyproject.toml")
Expand Down Expand Up @@ -207,15 +199,75 @@ def makemigrations(context, repo_name, args):
click.echo(context.get_help())


@repo.command()
@click.argument("repo_names", nargs=-1)
@click.option("-a", "--all-repos", is_flag=True, help="Status for all repositories")
@click.option("-r", "--reset", is_flag=True, help="Reset")
@click.option("-d", "--diff", is_flag=True, help="Show diff")
@click.option("-b", "--branch", is_flag=True, help="Show branch")
@click.option("-u", "--update", is_flag=True, help="Update repos")
@click.option("-l", "--log", is_flag=True, help="Show log")
@click.pass_context
@pass_repo
def status(repo, context, repo_names, all_repos, reset, diff, branch, update, log):
"""Repository status."""
repos, url_pattern, _ = get_repos("pyproject.toml")
repo_name_map = get_repo_name_map(repos, url_pattern)

# Status for specified repo names
if repo_names:
not_found = []
for repo_name in repo_names:
repo_url = repo_name_map.get(repo_name)
if repo_url:
get_status(
repo_url,
url_pattern,
repo,
reset=reset,
diff=diff,
branch=branch,
update=update,
log=log,
)
else:
not_found.append(repo_name)
for name in not_found:
click.echo(f"Repository '{name}' not found.")
return

# Status for all repos
if all_repos:
click.echo(f"Status of {len(repos)} repositories...")
for repo_name, repo_url in repo_name_map.items():
get_status(
repo_url,
url_pattern,
repo,
reset=reset,
diff=diff,
branch=branch,
update=update,
log=log,
)
return

# Show help if nothing selected
click.echo(context.get_help())


@repo.command()
@click.argument("repo_name", required=False)
@click.argument("modules", nargs=-1)
@click.option("-k", "--keyword", help="Filter tests by keyword")
@click.option("-l", "--list-tests", is_flag=True, help="List tests")
@click.option("-s", "--show", is_flag=True, help="Show settings")
@click.option("-s", "--show-settings", is_flag=True, help="Show settings")
@click.option("-a", "--all-repos", is_flag=True, help="All repos")
@click.option("--keepdb", is_flag=True, help="Keep db")
@click.pass_context
def test(context, repo_name, modules, keyword, list_tests, show, keepdb):
def test(
context, repo_name, modules, keyword, list_tests, show_settings, keepdb, all_repos
):
"""Run tests for Django fork and third-party libraries."""
repos, url_pattern, _ = get_repos("pyproject.toml")
repo_name_map = get_repo_name_map(repos, url_pattern)
Expand All @@ -229,7 +281,7 @@ def test(context, repo_name, modules, keyword, list_tests, show, keepdb):
)
return

if show:
if show_settings:
click.echo(f"⚙️ Test settings for 📦 {repo_name}:")
settings_dict = dict(sorted(test_settings_map[repo_name].items()))
formatted = format_str(str(settings_dict), mode=Mode())
Expand Down Expand Up @@ -327,64 +379,23 @@ def test(context, repo_name, modules, keyword, list_tests, show, keepdb):
subprocess.run(command, cwd=settings["test_dir"])
return

# No repo_name, show help
click.echo(context.get_help())


@repo.command()
@click.argument("repo_names", nargs=-1)
@click.option("-a", "--all-repos", is_flag=True, help="Status for all repositories")
@click.option("-r", "--reset", is_flag=True, help="Reset")
@click.option("-d", "--diff", is_flag=True, help="Show diff")
@click.option("-b", "--branch", is_flag=True, help="Show branch")
@click.option("-u", "--update", is_flag=True, help="Update repos")
@click.option("-l", "--log", is_flag=True, help="Show log")
@click.pass_context
@pass_repo
def status(repo, context, repo_names, all_repos, reset, diff, branch, update, log):
"""Repository status."""
repos, url_pattern, _ = get_repos("pyproject.toml")
repo_name_map = get_repo_name_map(repos, url_pattern)

# Status for specified repo names
if repo_names:
not_found = []
for repo_name in repo_names:
repo_url = repo_name_map.get(repo_name)
if repo_url:
get_status(
repo_url,
url_pattern,
repo,
reset=reset,
diff=diff,
branch=branch,
update=update,
log=log,
)
if all_repos and show_settings:
repos, url_pattern, _ = get_repos("pyproject.toml")
repo_name_map = get_repo_name_map(repos, url_pattern)
for repo_name in repo_name_map:
if repo_name in test_settings_map:
click.echo(f"⚙️ Test settings for 📦 {repo_name}:")
settings_dict = dict(sorted(test_settings_map[repo_name].items()))
formatted = format_str(str(settings_dict), mode=Mode())
rprint(formatted)
else:
not_found.append(repo_name)
for name in not_found:
click.echo(f"Repository '{name}' not found.")
click.echo(f"Settings for '{repo_name}' not found.")
return

# Status for all repos
if all_repos:
click.echo(f"Status of {len(repos)} repositories...")
for repo_name, repo_url in repo_name_map.items():
get_status(
repo_url,
url_pattern,
repo,
reset=reset,
diff=diff,
branch=branch,
update=update,
log=log,
)
else:
click.echo("Can only use --all-repos with --show-settings")
return

# Show help if nothing selected
# No repo_name, show help
click.echo(context.get_help())


Expand Down
Loading