Skip to content

Commit dae2f7c

Browse files
fix(cli): allow options after project directory (#241)
* fix(cli): allow options after project directory Typer registered `cache` as a nested command group, so Click treated tokens after the positional project path as COMMAND names (`No such command '--template'`). Use a single TyperCommand for scaffold and keep cache routing in main(). Bump create-awesome-python-app to 0.2.7. * docs: avoid duplicate CHANGELOG Fixes heading for markdownlint
1 parent 2bfd96d commit dae2f7c

6 files changed

Lines changed: 63 additions & 9 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## 0.2.7 - 2026-07-22
4+
5+
### CLI
6+
7+
- 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.
8+
39
## 0.2.6 - 2026-07-18
410

511
### Docs

packages/create-awesome-python-app/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "create-awesome-python-app"
3-
version = "0.2.6"
3+
version = "0.2.7"
44
description = "Composable scaffolding CLI for production-ready Python apps"
55
readme = "README.md"
66
requires-python = ">=3.12"
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
"""Create Awesome Python App CLI."""
22

3-
__version__ = "0.2.6"
3+
__version__ = "0.2.7"

packages/create-awesome-python-app/src/create_awesome_python_app/cli.py

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

4245

@@ -209,9 +212,8 @@ def main() -> None:
209212
app()
210213

211214

212-
@app.callback(invoke_without_command=True)
215+
@app.command(name="create-awesome-python-app")
213216
def scaffold(
214-
ctx: typer.Context,
215217
project_directory: str | None = typer.Argument("my-project"),
216218
version: bool = typer.Option(False, "--version"),
217219
info: bool = typer.Option(False, "--info", "-i"),
@@ -247,8 +249,6 @@ def scaffold(
247249
raise typer.Exit(0)
248250
if info:
249251
print_env_info()
250-
if ctx.invoked_subcommand is not None:
251-
return
252252

253253
# Translate --fixture into env vars before catalog loads
254254
# (--list-templates / interactive / scaffold).

packages/create-awesome-python-app/tests/test_cli.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,54 @@ def test_help_mentions_fixture() -> None:
223223
assert "fixture" in text.lower()
224224

225225

226+
def test_options_after_project_directory(tmp_path: Path, monkeypatch) -> None:
227+
"""Regression: README-style `cpa my-api --template …` must work.
228+
229+
Registering a Typer subcommand group made Click treat ``--template`` as a
230+
COMMAND after the positional project directory.
231+
"""
232+
tpl = tmp_path / "tpl"
233+
tpl.mkdir()
234+
captured: dict[str, object] = {}
235+
236+
async def fake_check_for_latest_version(_package_name):
237+
return None
238+
239+
async def fake_create_python_app(project_directory, options, *_args, **_kwargs):
240+
captured["project_directory"] = project_directory
241+
captured["options"] = options
242+
243+
monkeypatch.setattr(
244+
"create_awesome_python_app.cli.check_for_latest_version",
245+
fake_check_for_latest_version,
246+
)
247+
monkeypatch.setattr(
248+
"create_awesome_python_app.cli.create_python_app",
249+
fake_create_python_app,
250+
)
251+
252+
result = runner.invoke(
253+
app,
254+
[
255+
"my-api",
256+
"--template",
257+
f"file://{tpl}",
258+
"--addons",
259+
"github-setup",
260+
"--no-install",
261+
"--no-interactive",
262+
],
263+
)
264+
265+
text = (result.stdout or "") + (result.stderr or "")
266+
assert result.exit_code == 0, text
267+
assert "No such command" not in text
268+
assert captured["project_directory"] == "my-api"
269+
options = captured["options"]
270+
assert isinstance(options, dict)
271+
assert options["template"] == f"file://{tpl}"
272+
273+
226274
def test_preprocess_fixture_argv_bare_and_with_dir() -> None:
227275
from create_awesome_python_app.cli import _FIXTURE_AUTO, _preprocess_fixture_argv
228276

uv.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)