feat(template): restructure modules into subpackages and add deployment workflows#9
feat(template): restructure modules into subpackages and add deployment workflows#9hasansezertasan wants to merge 12 commits intomainfrom
Conversation
…nt workflows Reorganize template source structure from flat files to proper subpackages: - Move cli.py, gui.py, tui.py, web.py, mcp.py into cli/, gui/, tui/, web/, mcp/ subpackages - Move config.py, dirs.py, logging_setup.py into core/ subpackage - Move utils.py into utils/ subpackage - Mirror test structure to match source layout Add new features: - MCP (Model Context Protocol) server support with include_mcp option - PyCrucible standalone executable building integrated into cd.yml - Docker Hub publishing integrated into cd.yml for web apps - Profiling support with include_profiling option and profile.py script - git-cliff changelog generation config Update CD workflow to unified release flow: - build -> pypi-publish -> attach-github-release - Conditional build-executables job (3 platforms) when include_pycrucible - Conditional docker-publish job when include_web - Document workflow structure in README with ASCII diagram Other improvements: - Add devcontainer queue service configuration - Update VS Code and Zed debug configurations - Fix dependabot.yml template syntax - Update example input with new options Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Reviewer's GuideRestructures the generated project’s runtime modules into subpackages, adds optional MCP server and profiling/packaging features, and unifies/enriches CI/CD to support PyCrucible executables and Docker Hub publishing with framework-conditional web support. Class diagram for restructured runtime subpackages and entry pointsclassDiagram
direction LR
class Package_root {
<<package>>
+__init__
+__main__
+__metadata__
+_version
}
class Package_core {
<<package>>
}
class Core_dirs {
+LOG_FILE_PATH
+ROOT_FOLDER_PATH
}
class Core_logging_setup {
+setup_logger() Logger
}
class Core_app {
<<module>>
}
class Package_utils {
<<package>>
}
class Package_cli {
<<package>>
}
class Cli_app {
+app
+version()
+info()
+interactive() %% optional TUI
+gui() %% optional GUI
+web() %% optional Web
}
class Package_web {
<<package>>
}
class Web_app_fastapi {
+app FastAPI
+get_version() str
+get_info() JSONResponse
+main() void
}
class Web_app_litestar {
+app Litestar
+get_version() str
+get_info() dict~str,str~
+main() void
}
class Package_gui {
<<package>>
}
class Gui_app {
+GuiDisplayError
}
class Package_tui {
<<package>>
}
class Tui_app {
+TuiDisplayError
}
class Package_mcp {
<<package>>
}
class Mcp_app {
+server Server
+list_tools() list~Tool~
+call_tool(name, arguments) list~TextContent~
+run_server() void
+main() void
}
class Profile_module {
+run_app() void
+profile_pyspy() void
+profile_scalene() void
+profile_cprofile() void
+main() void
}
Package_root *-- Package_core
Package_root *-- Package_utils
Package_root *-- Package_cli
Package_root *-- Package_web
Package_root *-- Package_gui
Package_root *-- Package_tui
Package_root *-- Package_mcp
Package_root *-- Profile_module
Package_core *-- Core_dirs
Package_core *-- Core_logging_setup
Package_core *-- Core_app
Package_cli *-- Cli_app
Package_web *-- Web_app_fastapi
Package_web *-- Web_app_litestar
Package_gui *-- Gui_app
Package_tui *-- Tui_app
Package_mcp *-- Mcp_app
Cli_app ..> Core_logging_setup : uses logger
Web_app_fastapi ..> Core_logging_setup : uses logger
Web_app_litestar ..> Core_logging_setup : uses logger
Gui_app ..> Core_logging_setup : uses logger
Tui_app ..> Core_logging_setup : uses logger
Mcp_app ..> Core_logging_setup : uses logger
Cli_app ..> Web_app_fastapi : optional web()
Cli_app ..> Web_app_litestar : optional web()
Cli_app ..> Tui_app : optional interactive()
Cli_app ..> Gui_app : optional gui()
Profile_module ..> Cli_app : optional run_app()
Profile_module ..> Web_app_fastapi : optional run_app()
Profile_module ..> Web_app_litestar : optional run_app()
Profile_module ..> Tui_app : optional run_app()
Flow diagram for web feature and framework-dependent behaviorflowchart TD
direction TB
Start([Template generation]) --> QIncludeWeb{include_web}
QIncludeWeb -- false --> SkipWeb[No web package
no Docker image
no web scripts]
QIncludeWeb -- true --> QFramework{web_framework}
QFramework -- fastapi --> FastAPIDeps[Set dependency group web
fastapi_all]
QFramework -- litestar --> LitestarDeps[Set dependency group web
litestar_standard]
FastAPIDeps --> WebPkgFastAPI[Generate web/app_py
with FastAPI app]
LitestarDeps --> WebPkgLitestar[Generate web/app_py
with Litestar app]
WebPkgFastAPI --> EntryPoints[Configure project_scripts
repo_web -> repo_web_app_main]
WebPkgLitestar --> EntryPoints
EntryPoints --> Dockerfile[Generate Dockerfile
uvicorn repo_web_app_app]
EntryPoints --> CDWorkflow
CDWorkflow[cd_yml
includes docker_publish job] --> DockerHub[Build multi-arch image
push to Docker Hub]
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
There was a problem hiding this comment.
Hey - I've found 1 issue, and left some high level feedback:
- In
template/profile.py.jinja, the imports and entrypoints still reference the old flat module layout (e.g.,from {{github_repo_name}}.cli import app,from {{github_repo_name}}.web import main); these should be updated to the new subpackage paths like{{github_repo_name}}.cli.appand{{github_repo_name}}.web.app:mainto stay consistent with the restructuring. - The new Codecov upload step in
template/.github/workflows/ci.ymlruns unconditionally; consider gating this step (and coverage combination) behind theinclude_codecovoption to align with the template’s configurability and avoid enabling Codecov behavior when it was explicitly disabled. - In the updated
mise.tomltemplate, thepackagetask always callspycrucibleeven wheninclude_pycrucibleis false, which can break users who include mise but not PyCrucible; it would be safer to either guard this task withinclude_pycrucibleor make it a no-op unless PyCrucible is enabled.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In `template/profile.py.jinja`, the imports and entrypoints still reference the old flat module layout (e.g., `from {{github_repo_name}}.cli import app`, `from {{github_repo_name}}.web import main`); these should be updated to the new subpackage paths like `{{github_repo_name}}.cli.app` and `{{github_repo_name}}.web.app:main` to stay consistent with the restructuring.
- The new Codecov upload step in `template/.github/workflows/ci.yml` runs unconditionally; consider gating this step (and coverage combination) behind the `include_codecov` option to align with the template’s configurability and avoid enabling Codecov behavior when it was explicitly disabled.
- In the updated `mise.toml` template, the `package` task always calls `pycrucible` even when `include_pycrucible` is false, which can break users who include mise but not PyCrucible; it would be safer to either guard this task with `include_pycrucible` or make it a no-op unless PyCrucible is enabled.
## Individual Comments
### Comment 1
<location> `template/.github/workflows/ci.yml:59-65` </location>
<code_context>
- name: Run the tests
run: uv run --locked tox run
# https://github.com/marketplace/actions/alls-green#why used for branch protection checks
+ - name: Coverage
+ run: |
+ uv run coverage combine
+ uv run coverage report --show-missing
+ uv run coverage xml
+ - name: Upload coverage
+ uses: codecov/codecov-action@v5
+
check:
</code_context>
<issue_to_address>
**issue (bug_risk):** Codecov upload is now hard-wired even though there is an `include_codecov` template option
With this change, coverage reporting and Codecov upload always run, but the template still exposes an `include_codecov` option. Users who set `include_codecov = false` will still hit failing steps unless they configure Codecov and its secrets. To keep Codecov truly optional, consider conditioning the coverage upload (and possibly XML generation) on `include_codecov`, or remove/repurpose that option so behavior and configuration stay consistent.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
There was a problem hiding this comment.
Review by RecurseML
🔍 Review performed on 61aedc8..38ceab5
✨ No bugs found, your code is sparkling clean
✅ Files analyzed, no issues (47)
• .cliffignore
• .example-input.yml
• AGENTS.md
• CLAUDE.md
• README.md
• cliff.toml
• copier.yml
• template/.devcontainer/docker-compose.queue.yml
• template/.devcontainer/queue/Dockerfile
• template/.github/ISSUE_TEMPLATE/bug_report.md.jinja
• template/.github/workflows/cd.yml.jinja
• template/.github/workflows/ci.yml
• template/.github/{% if dependency_management == 'dependabot' %}dependabot.yml{% endif %}.jinja
• template/.vscode/extensions.json
• template/.vscode/launch.json.jinja
• template/.zed/debug.json.jinja
• template/README.md.jinja
• template/pyproject.toml.jinja
• template/src/{{github_repo_name}}/core.py
• template/src/{{github_repo_name}}/core/__init__.py.jinja
• template/src/{{github_repo_name}}/core/app.py.jinja
• template/src/{{github_repo_name}}/core/logging_setup.py.jinja
• template/src/{{github_repo_name}}/utils/__init__.py.jinja
• template/src/{{github_repo_name}}/{% if include_cli %}cli{% endif %}/__init__.py.jinja
• template/src/{{github_repo_name}}/{% if include_cli %}cli{% endif %}/app.py.jinja
• template/src/{{github_repo_name}}/{% if include_gui %}gui{% endif %}/__init__.py.jinja
• template/src/{{github_repo_name}}/{% if include_gui %}gui{% endif %}/app.py.jinja
• template/src/{{github_repo_name}}/{% if include_mcp %}mcp{% endif %}/__init__.py.jinja
• template/src/{{github_repo_name}}/{% if include_mcp %}mcp{% endif %}/app.py.jinja
• template/src/{{github_repo_name}}/{% if include_tui %}tui{% endif %}/__init__.py.jinja
• template/src/{{github_repo_name}}/{% if include_tui %}tui{% endif %}/app.py.jinja
• template/src/{{github_repo_name}}/{% if include_web %}web.py{% endif %}.jinja
• template/src/{{github_repo_name}}/{% if include_web %}web{% endif %}/__init__.py.jinja
• template/src/{{github_repo_name}}/{% if include_web %}web{% endif %}/app.py.jinja
• template/tests/{% if include_cli %}cli{% endif %}/__init__.py.jinja
• template/tests/{% if include_cli %}cli{% endif %}/test_app.py.jinja
• template/tests/{% if include_gui %}gui{% endif %}/__init__.py.jinja
• template/tests/{% if include_gui %}gui{% endif %}/test_app.py.jinja
• template/tests/{% if include_mcp %}mcp{% endif %}/__init__.py.jinja
• template/tests/{% if include_mcp %}mcp{% endif %}/test_app.py.jinja
• template/tests/{% if include_tui %}tui{% endif %}/__init__.py.jinja
• template/tests/{% if include_tui %}tui{% endif %}/test_app.py.jinja
• template/tests/{% if include_web %}web{% endif %}/__init__.py.jinja
• template/tests/{% if include_web %}web{% endif %}/test_app.py.jinja
• template/{% if include_mise %}mise.toml{% endif %}.jinja
• template/{% if include_profiling %}profile.py{% endif %}.jinja
• template/{% if include_web %}Dockerfile{% endif %}.jinja
⏭️ Files skipped (3)
| Locations |
|---|
template/src/{{github_repo_name}}/core/config.py.jinja |
template/src/{{github_repo_name}}/core/dirs.py.jinja |
template/src/{{github_repo_name}}/utils/app.py.jinja |
- Fix cd.yml.jinja raw block placement (close tag after github.ref_name) - Standardize actions/checkout to v6 across all jobs - Add PackageNotFoundError handling to MCP, CLI, web, GUI, TUI modules - Add pytest-asyncio to test dependencies for async tests - Fix profile.py import paths and add subprocess error handling - Narrow TUI exception catch to not mask KeyboardInterrupt/SystemExit Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix pyproject.toml `all` extra to use actual dependencies instead of invalid extra references (cli -> typer, web -> fastapi, etc.) - Add re-exports to CLI, Web, MCP __init__.py files for cleaner imports - Enables `from pkg.cli import app` style imports Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add include_mcp and web_framework to README documentation - Fix import ordering in core/logging_setup.py and core/config.py - Fix blank lines and trailing newlines in cli/app.py, web/app.py - Fix line length issues in mcp/app.py - Fix EM101 (raw string in exception) in gui and tui tests - Refactor profile.py with noqa directives and improved structure - Fix import ordering in all test files Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Convert ci.yml to Jinja template with coverage steps conditional on include_codecov - Make build-wheels.yml conditional on include_c_extensions (multi-platform wheel building is only needed for C extensions) - Remove JavaScript from CodeQL language matrix (Python-only template) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Convert devcontainer.json to Jinja template - Make mise devcontainer feature conditional on include_mise - Make mise VSCode extension conditional on include_mise in both devcontainer.json and extensions.json Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add message queue worker support: - Add include_worker and worker_broker options (kafka/nats/rabbitmq/redis) - Create worker module with FastStream app and version endpoint - Add worker tests with pytest-asyncio - Add worker entry point and optional dependency Consolidate devcontainer docker-compose files: - Merge all services into single docker-compose.yml.jinja - Add include_dbeaver option for CloudBeaver database management - Add include_vpn option for OpenVPN client - Remove separate docker-compose.queue.yml, docker-compose.dbeaver.yml, docker-compose.vpn.yml, and queue/Dockerfile Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add asyncio_mode = "auto" to pytest configuration for pytest-asyncio >=0.23 - Enhance worker test to verify response is actually published - Test now captures and validates published message content Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add include_worker and worker_broker documentation - Add include_dbeaver and include_vpn devcontainer services - Add devcontainer structure section for consolidated docker-compose - Add "Adding New Optional Components" checklist - Document whitespace control pattern for Jinja templates - Add testing commands for features with choices Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add GitHub Container Registry login step using GITHUB_TOKEN - Push Docker images to both docker.io and ghcr.io simultaneously - Fix test_smoke.py docstrings and noqa comment for local import Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…rkflow - Add explicit pydantic and uvloop dependencies to worker optional deps - Use environment variables for broker URLs (devcontainer compatibility) - Remove invalid FastStream constructor kwargs (title, version, description) - Rewrite worker test using broker-specific TestBroker and .mock assertions - Make Docker Hub login conditional on DOCKERHUB_USERNAME secret - Remove Docker socket mount from CloudBeaver (security risk) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…OF_CONDUCT Move CHANGELOG.md, CONTRIBUTING.md, and CODE_OF_CONDUCT.md from conditional filename inclusion to always-included files. CHANGELOG.md now uses in-file condition to switch between conventional format and a simple GitHub Releases link. Remove include_contributing and include_code_of_conduct copier variables. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Summary
cli/,gui/,tui/,web/,mcp/,core/,utils/) with matching test structureinclude_mcpoptioninclude_webis enabledcd.ymlwith conditional jobsTest plan
tox run -e styleon generated projecttox runon generated project🤖 Generated with Claude Code
High-level PR Summary
This PR restructures a Copier Python template from a flat module layout to a proper subpackage architecture (
cli/,web/,gui/,tui/,mcp/,core/,utils/), adds Model Context Protocol (MCP) server support with aninclude_mcpoption, integrates PyCrucible for building standalone executables across platforms, adds Docker Hub multi-arch publishing for web projects, consolidates all CD workflows into a unifiedcd.ymlwith conditional jobs for PyPI, executables, and Docker images, introduces profiling tools (py-spy,scalene,cProfile) viainclude_profiling, and supports both FastAPI and Litestar web frameworks through a configurableweb_frameworkchoice. Additionally, it updates documentation, renamesuse_precommittoinclude_precommit, removes theinclude_poeoption in favor ofmisefor task running, and adds comprehensive VS Code debugging configurations for all entry points.⏱️ Estimated Review Time: 30-90 minutes
💡 Review Order Suggestion
copier.yml.example-input.ymlREADME.mdCLAUDE.mdAGENTS.mdcliff.toml.cliffignoretemplate/pyproject.toml.jinjatemplate/src/{{github_repo_name}}/core/__init__.py.jinjatemplate/src/{{github_repo_name}}/core/app.py.jinjatemplate/src/{{github_repo_name}}/core/logging_setup.py.jinjatemplate/src/{{github_repo_name}}/utils/__init__.py.jinjatemplate/src/{{github_repo_name}}/{% if include_cli %}cli{% endif %}/__init__.py.jinjatemplate/src/{{github_repo_name}}/{% if include_cli %}cli{% endif %}/app.py.jinjatemplate/src/{{github_repo_name}}/{% if include_web %}web{% endif %}/__init__.py.jinjatemplate/src/{{github_repo_name}}/{% if include_web %}web{% endif %}/app.py.jinjatemplate/src/{{github_repo_name}}/{% if include_tui %}tui{% endif %}/__init__.py.jinjatemplate/src/{{github_repo_name}}/{% if include_tui %}tui{% endif %}/app.py.jinjatemplate/src/{{github_repo_name}}/{% if include_gui %}gui{% endif %}/__init__.py.jinjatemplate/src/{{github_repo_name}}/{% if include_gui %}gui{% endif %}/app.py.jinjatemplate/src/{{github_repo_name}}/{% if include_mcp %}mcp{% endif %}/__init__.py.jinjatemplate/src/{{github_repo_name}}/{% if include_mcp %}mcp{% endif %}/app.py.jinjatemplate/tests/{% if include_cli %}cli{% endif %}/__init__.py.jinjatemplate/tests/{% if include_cli %}cli{% endif %}/test_app.py.jinjatemplate/tests/{% if include_web %}web{% endif %}/__init__.py.jinjatemplate/tests/{% if include_web %}web{% endif %}/test_app.py.jinjatemplate/tests/{% if include_tui %}tui{% endif %}/__init__.py.jinjatemplate/tests/{% if include_tui %}tui{% endif %}/test_app.py.jinjatemplate/tests/{% if include_gui %}gui{% endif %}/__init__.py.jinjatemplate/tests/{% if include_gui %}gui{% endif %}/test_app.py.jinjatemplate/tests/{% if include_mcp %}mcp{% endif %}/__init__.py.jinjatemplate/tests/{% if include_mcp %}mcp{% endif %}/test_app.py.jinjatemplate/{% if include_profiling %}profile.py{% endif %}.jinjatemplate/.github/workflows/cd.yml.jinjatemplate/.github/workflows/ci.ymltemplate/.github/{% if dependency_management == 'dependabot' %}dependabot.yml{% endif %}.jinjatemplate/.github/ISSUE_TEMPLATE/bug_report.md.jinjatemplate/.vscode/launch.json.jinjatemplate/.vscode/extensions.jsontemplate/.zed/debug.json.jinjatemplate/.devcontainer/docker-compose.queue.ymltemplate/.devcontainer/queue/Dockerfiletemplate/{% if include_mise %}mise.toml{% endif %}.jinjatemplate/{% if include_web %}Dockerfile{% endif %}.jinjatemplate/.devcontainer/docker-compose.queue.ymltemplate/.devcontainer/queue/Dockerfiletemplate/.github/ISSUE_TEMPLATE/bug_report.md.jinja