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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
# Changelog

## 0.2.7 - 2026-07-22

### CLI

- Allow options after the project directory (`uvx create-awesome-python-app my-api --template …`). Typer was registering `cache` as a nested command group, so Click treated `--template` as a COMMAND name.

## 0.2.6 - 2026-07-18

### Docs
Expand Down
2 changes: 1 addition & 1 deletion packages/create-awesome-python-app/pyproject.toml
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
[project]
name = "create-awesome-python-app"
version = "0.2.6"
version = "0.2.7"
description = "Composable scaffolding CLI for production-ready Python apps"
readme = "README.md"
requires-python = ">=3.12"
Expand Down
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
"""Create Awesome Python App CLI."""

__version__ = "0.2.6"
__version__ = "0.2.7"
Original file line numberDiff line numberDiff line change
Expand Up@@ -34,9 +34,12 @@
help="Composable scaffolding CLI for production-ready Python apps.",
no_args_is_help=False,
add_completion=True,
# Keep cache out of this Typer app (routed in main()). A nested command
# group turns the CLI into Click Group form `[ARGS] COMMAND`, so
# `cpa my-api --template …` fails with "No such command '--template'".
epilog="Cache: create-awesome-python-app cache [list|dir|clean|verify|…]",
)
cache_app = typer.Typer(help="Inspect and manage the local template cache")
app.add_typer(cache_app, name="cache")
console = Console(stderr=True)


Expand DownExpand Up@@ -209,9 +212,8 @@ def main() -> None:
app()


@app.callback(invoke_without_command=True)
@app.command(name="create-awesome-python-app")
def scaffold(
ctx: typer.Context,
project_directory: str | None = typer.Argument("my-project"),
version: bool = typer.Option(False, "--version"),
info: bool = typer.Option(False, "--info", "-i"),
Expand DownExpand Up@@ -247,8 +249,6 @@ def scaffold(
raise typer.Exit(0)
if info:
print_env_info()
if ctx.invoked_subcommand is not None:
return

# Translate --fixture into env vars before catalog loads
# (--list-templates / interactive / scaffold).
Expand Down
48 changes: 48 additions & 0 deletions packages/create-awesome-python-app/tests/test_cli.py
Original file line numberDiff line numberDiff line change
Expand Up@@ -223,6 +223,54 @@ def test_help_mentions_fixture() -> None:
assert "fixture" in text.lower()


def test_options_after_project_directory(tmp_path: Path, monkeypatch) -> None:
"""Regression: README-style `cpa my-api --template …` must work.

Registering a Typer subcommand group made Click treat ``--template`` as a
COMMAND after the positional project directory.
"""
tpl = tmp_path / "tpl"
tpl.mkdir()
captured: dict[str, object] = {}

async def fake_check_for_latest_version(_package_name):
return None

async def fake_create_python_app(project_directory, options, *_args, **_kwargs):
captured["project_directory"] = project_directory
captured["options"] = options

monkeypatch.setattr(
"create_awesome_python_app.cli.check_for_latest_version",
fake_check_for_latest_version,
)
monkeypatch.setattr(
"create_awesome_python_app.cli.create_python_app",
fake_create_python_app,
)

result = runner.invoke(
app,
[
"my-api",
"--template",
f"file://{tpl}",
"--addons",
"github-setup",
"--no-install",
"--no-interactive",
],
)

text = (result.stdout or "") + (result.stderr or "")
assert result.exit_code == 0, text
assert "No such command" not in text
assert captured["project_directory"] == "my-api"
options = captured["options"]
assert isinstance(options, dict)
assert options["template"] == f"file://{tpl}"


def test_preprocess_fixture_argv_bare_and_with_dir() -> None:
from create_awesome_python_app.cli import _FIXTURE_AUTO, _preprocess_fixture_argv

Expand Down
4 changes: 2 additions & 2 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading