rhiza 0.2.0__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. {rhiza-0.2.0 → rhiza-0.3.0}/.devcontainer/devcontainer.json +1 -1
  2. {rhiza-0.2.0 → rhiza-0.3.0}/.github/scripts/marimushka.sh +11 -1
  3. {rhiza-0.2.0 → rhiza-0.3.0}/Makefile +8 -2
  4. {rhiza-0.2.0 → rhiza-0.3.0}/PKG-INFO +24 -5
  5. {rhiza-0.2.0 → rhiza-0.3.0}/README.md +22 -0
  6. {rhiza-0.2.0 → rhiza-0.3.0}/book/marimo/df.py +3 -1
  7. rhiza-0.3.0/book/minibook-templates/custom.html.jinja2 +210 -0
  8. rhiza-0.3.0/book/pdoc-templates/module.html.jinja2 +19 -0
  9. {rhiza-0.2.0 → rhiza-0.3.0}/pyproject.toml +2 -14
  10. rhiza-0.3.0/src/rhiza/.gitkeep +0 -0
  11. {rhiza-0.2.0 → rhiza-0.3.0}/tests/test_rhiza/conftest.py +46 -1
  12. {rhiza-0.2.0 → rhiza-0.3.0}/tests/test_rhiza/test_makefile.py +36 -17
  13. rhiza-0.3.0/tests/test_rhiza/test_marimushka_script.py +72 -0
  14. rhiza-0.3.0/tests/test_rhiza/test_updatereadme_script.py +94 -0
  15. {rhiza-0.2.0 → rhiza-0.3.0}/uv.lock +1 -89
  16. rhiza-0.2.0/.devcontainer/startup.sh +0 -10
  17. rhiza-0.2.0/src/rhiza/__init__.py +0 -5
  18. rhiza-0.2.0/src/rhiza/__main__.py +0 -10
  19. rhiza-0.2.0/src/rhiza/cli.py +0 -47
  20. rhiza-0.2.0/src/rhiza/commands/__init__.py +0 -5
  21. rhiza-0.2.0/src/rhiza/commands/hello.py +0 -9
  22. rhiza-0.2.0/src/rhiza/commands/inject.py +0 -160
  23. rhiza-0.2.0/tests/test_rhiza/test_cli_commands.py +0 -482
  24. {rhiza-0.2.0 → rhiza-0.3.0}/.devcontainer/bootstrap.sh +0 -0
  25. {rhiza-0.2.0 → rhiza-0.3.0}/.editorconfig +0 -0
  26. {rhiza-0.2.0 → rhiza-0.3.0}/.github/actions/setup-project/action.yml +0 -0
  27. {rhiza-0.2.0 → rhiza-0.3.0}/.github/renovate.json +0 -0
  28. {rhiza-0.2.0 → rhiza-0.3.0}/.github/scripts/book.sh +0 -0
  29. {rhiza-0.2.0 → rhiza-0.3.0}/.github/scripts/bump.sh +0 -0
  30. {rhiza-0.2.0 → rhiza-0.3.0}/.github/scripts/customisations/build-extras.sh +0 -0
  31. {rhiza-0.2.0 → rhiza-0.3.0}/.github/scripts/customisations/post-release.sh +0 -0
  32. {rhiza-0.2.0 → rhiza-0.3.0}/.github/scripts/release.sh +0 -0
  33. {rhiza-0.2.0 → rhiza-0.3.0}/.github/scripts/update-readme-help.sh +0 -0
  34. {rhiza-0.2.0 → rhiza-0.3.0}/.github/workflows/book.yml +0 -0
  35. {rhiza-0.2.0 → rhiza-0.3.0}/.github/workflows/ci.yml +0 -0
  36. {rhiza-0.2.0 → rhiza-0.3.0}/.github/workflows/deptry.yml +0 -0
  37. {rhiza-0.2.0 → rhiza-0.3.0}/.github/workflows/devcontainer.yml +0 -0
  38. {rhiza-0.2.0 → rhiza-0.3.0}/.github/workflows/docker.yml +0 -0
  39. {rhiza-0.2.0 → rhiza-0.3.0}/.github/workflows/marimo.yml +0 -0
  40. {rhiza-0.2.0 → rhiza-0.3.0}/.github/workflows/pre-commit.yml +0 -0
  41. {rhiza-0.2.0 → rhiza-0.3.0}/.github/workflows/release.yml +0 -0
  42. {rhiza-0.2.0 → rhiza-0.3.0}/.github/workflows/scripts/version_matrix.py +0 -0
  43. {rhiza-0.2.0 → rhiza-0.3.0}/.github/workflows/scripts/version_max.py +0 -0
  44. {rhiza-0.2.0 → rhiza-0.3.0}/.github/workflows/sync.yml +0 -0
  45. {rhiza-0.2.0 → rhiza-0.3.0}/.gitignore +0 -0
  46. {rhiza-0.2.0 → rhiza-0.3.0}/.pre-commit-config.yaml +0 -0
  47. {rhiza-0.2.0 → rhiza-0.3.0}/CODE_OF_CONDUCT.md +0 -0
  48. {rhiza-0.2.0 → rhiza-0.3.0}/CONTRIBUTING.md +0 -0
  49. {rhiza-0.2.0 → rhiza-0.3.0}/LICENSE +0 -0
  50. {rhiza-0.2.0 → rhiza-0.3.0}/assets/rhiza-logo.svg +0 -0
  51. {rhiza-0.2.0 → rhiza-0.3.0}/docker/Dockerfile +0 -0
  52. {rhiza-0.2.0 → rhiza-0.3.0}/docker/Dockerfile.dockerignore +0 -0
  53. {rhiza-0.2.0 → rhiza-0.3.0}/docker/README.md +0 -0
  54. {rhiza-0.2.0 → rhiza-0.3.0}/pytest.ini +0 -0
  55. {rhiza-0.2.0 → rhiza-0.3.0}/ruff.toml +0 -0
  56. {rhiza-0.2.0 → rhiza-0.3.0}/tests/test_rhiza/README.md +0 -0
  57. {rhiza-0.2.0 → rhiza-0.3.0}/tests/test_rhiza/test_bump_script.py +0 -0
  58. {rhiza-0.2.0 → rhiza-0.3.0}/tests/test_rhiza/test_docstrings.py +0 -0
  59. {rhiza-0.2.0 → rhiza-0.3.0}/tests/test_rhiza/test_git_repo_fixture.py +0 -0
  60. {rhiza-0.2.0 → rhiza-0.3.0}/tests/test_rhiza/test_readme.py +0 -0
  61. {rhiza-0.2.0 → rhiza-0.3.0}/tests/test_rhiza/test_release_script.py +0 -0
  62. {rhiza-0.2.0 → rhiza-0.3.0}/tests/test_rhiza/test_structure.py +0 -0
@@ -43,6 +43,6 @@
43
43
  ]
44
44
  }
45
45
  },
46
- "onCreateCommand": ".devcontainer/startup.sh",
46
+ "onCreateCommand": ".devcontainer/bootstrap.sh",
47
47
  "remoteUser": "vscode"
48
48
  }
@@ -43,11 +43,21 @@ case "$UVX_BIN" in
43
43
  *) ;;
44
44
  esac
45
45
 
46
+ # Resolve UV_BIN to absolute path
47
+ case "$UV_BIN" in
48
+ /*) ;;
49
+ */*) UV_BIN="$CURRENT_DIR/$UV_BIN" ;;
50
+ *) ;;
51
+ esac
52
+
53
+ # Derive UV_INSTALL_DIR from UV_BIN
54
+ UV_INSTALL_DIR=$(dirname "$UV_BIN")
55
+
46
56
  # Change to the notebook directory to ensure relative paths in notebooks work correctly
47
57
  cd "$MARIMO_FOLDER"
48
58
 
49
59
  # Run marimushka export
50
- "$UVX_BIN" "marimushka>=0.1.9" export --notebooks "." --output "$OUTPUT_DIR" --bin-path "$CURRENT_DIR/bin"
60
+ "$UVX_BIN" "marimushka>=0.1.9" export --notebooks "." --output "$OUTPUT_DIR" --bin-path "$UV_INSTALL_DIR"
51
61
 
52
62
  # Ensure GitHub Pages does not process with Jekyll
53
63
  : > "$OUTPUT_DIR/.nojekyll"
@@ -30,6 +30,7 @@ BOOK_SUBTITLE := Generated by minibook
30
30
  SCRIPTS_FOLDER := .github/scripts
31
31
  CUSTOM_SCRIPTS_FOLDER := .github/scripts/customisations
32
32
  PDOC_TEMPLATE_DIR := book/pdoc-templates
33
+ BOOK_TEMPLATE := book/minibook-templates/custom.html.jinja2
33
34
  DOCFORMAT :=
34
35
 
35
36
  export UV_NO_MODIFY_PATH := 1
@@ -110,7 +111,7 @@ marimo: install ## fire up Marimo server
110
111
  @if [ ! -d "${MARIMO_FOLDER}" ]; then \
111
112
  printf " ${YELLOW}[WARN] Marimo folder '${MARIMO_FOLDER}' not found, skipping start${RESET}\n"; \
112
113
  else \
113
- ${UV_BIN} run --with marimo marimo edit "${MARIMO_FOLDER}"; \
114
+ ${UV_BIN} run --with marimo marimo edit --no-token --headless "${MARIMO_FOLDER}"; \
114
115
  fi
115
116
 
116
117
  marimushka: install-uv ## export Marimo notebooks to HTML
@@ -162,7 +163,12 @@ docs: install ## create documentation with pdoc
162
163
  book: test docs marimushka ## compile the companion book
163
164
  @${UV_BIN} pip install marimo
164
165
  @/bin/sh "${SCRIPTS_FOLDER}/book.sh"
165
- @${UVX_BIN} minibook --title "${BOOK_TITLE}" --subtitle "${BOOK_SUBTITLE}" --links "$$(python3 -c 'import json,sys; print(json.dumps(json.load(open("_book/links.json"))))')" --output "_book"
166
+ @TEMPLATE_ARG=""; \
167
+ if [ -f "${BOOK_TEMPLATE}" ]; then \
168
+ TEMPLATE_ARG="--template ${BOOK_TEMPLATE}"; \
169
+ printf "${BLUE}[INFO] Using book template ${BOOK_TEMPLATE}${RESET}\n"; \
170
+ fi; \
171
+ ${UVX_BIN} minibook --title "${BOOK_TITLE}" --subtitle "${BOOK_SUBTITLE}" $$TEMPLATE_ARG --links "$$(python3 -c 'import json,sys; print(json.dumps(json.load(open("_book/links.json"))))')" --output "_book"
166
172
  @touch "_book/.nojekyll"
167
173
 
168
174
  fmt: install-uv ## check the pre-commit hooks and the linting
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rhiza
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Summary: Reusable configuration templates for modern Python projects
5
5
  Project-URL: Homepage, https://github.com/jebel-quant/rhiza
6
6
  Project-URL: Repository, https://github.com/jebel-quant/rhiza
@@ -8,7 +8,7 @@ Project-URL: Issues, https://github.com/jebel-quant/rhiza/issues
8
8
  Author: Thomas Schmelzer
9
9
  License: MIT
10
10
  License-File: LICENSE
11
- Keywords: ci,configuration,ruff,taskfile,templates
11
+ Keywords: ci,configuration,ruff,templates
12
12
  Classifier: Intended Audience :: Developers
13
13
  Classifier: License :: OSI Approved :: MIT License
14
14
  Classifier: Programming Language :: Python :: 3
@@ -19,9 +19,6 @@ Classifier: Programming Language :: Python :: 3.13
19
19
  Classifier: Programming Language :: Python :: 3.14
20
20
  Classifier: Topic :: Software Development :: Build Tools
21
21
  Requires-Python: >=3.11
22
- Requires-Dist: loguru>=0.7.3
23
- Requires-Dist: pyyaml==6.0.3
24
- Requires-Dist: typer>=0.20.0
25
22
  Provides-Extra: dev
26
23
  Requires-Dist: marimo==0.18.4; extra == 'dev'
27
24
  Requires-Dist: pdoc>=16.0.0; extra == 'dev'
@@ -205,6 +202,28 @@ Hello, World!
205
202
  0.71
206
203
  ```
207
204
 
205
+ ## 🎨 Documentation Customization
206
+
207
+ You can customize the look and feel of your documentation by providing your own templates.
208
+
209
+ ### API Documentation (pdoc)
210
+
211
+ The `make docs` command checks for a directory at `book/pdoc-templates`. If found, it uses the templates within that directory to generate the API documentation.
212
+
213
+ To customize the API docs:
214
+ 1. Create the directory: `mkdir -p book/pdoc-templates`
215
+ 2. Add your Jinja2 templates (e.g., `module.html.jinja2`) to this directory.
216
+
217
+ See the [pdoc documentation](https://pdoc.dev/docs/pdoc.html#templates) for more details on templating.
218
+
219
+ ### Companion Book (minibook)
220
+
221
+ The `make book` command checks for a template at `book/minibook-templates/custom.html.jinja2`. If found, it uses this template for the minibook generation.
222
+
223
+ To customize the book:
224
+ 1. Create the directory: `mkdir -p book/minibook-templates`
225
+ 2. Create your custom template at `book/minibook-templates/custom.html.jinja2`.
226
+
208
227
  ## 📁 Available Templates
209
228
 
210
229
  This repository provides a curated set of reusable configuration templates, organised by purpose.
@@ -172,6 +172,28 @@ Hello, World!
172
172
  0.71
173
173
  ```
174
174
 
175
+ ## 🎨 Documentation Customization
176
+
177
+ You can customize the look and feel of your documentation by providing your own templates.
178
+
179
+ ### API Documentation (pdoc)
180
+
181
+ The `make docs` command checks for a directory at `book/pdoc-templates`. If found, it uses the templates within that directory to generate the API documentation.
182
+
183
+ To customize the API docs:
184
+ 1. Create the directory: `mkdir -p book/pdoc-templates`
185
+ 2. Add your Jinja2 templates (e.g., `module.html.jinja2`) to this directory.
186
+
187
+ See the [pdoc documentation](https://pdoc.dev/docs/pdoc.html#templates) for more details on templating.
188
+
189
+ ### Companion Book (minibook)
190
+
191
+ The `make book` command checks for a template at `book/minibook-templates/custom.html.jinja2`. If found, it uses this template for the minibook generation.
192
+
193
+ To customize the book:
194
+ 1. Create the directory: `mkdir -p book/minibook-templates`
195
+ 2. Create your custom template at `book/minibook-templates/custom.html.jinja2`.
196
+
175
197
  ## 📁 Available Templates
176
198
 
177
199
  This repository provides a curated set of reusable configuration templates, organised by purpose.
@@ -25,7 +25,9 @@ app = marimo.App(width="medium")
25
25
 
26
26
  @app.cell
27
27
  def _():
28
- from rhiza.commands.hello import hello
28
+ # from rhiza.commands.hello import hello
29
+ def hello():
30
+ return "Guten Tach"
29
31
 
30
32
  hello()
31
33
  return
@@ -0,0 +1,210 @@
1
+ <! DOCTYPE html>
2
+ <html lang="en" class="dark">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>{{ title }}</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <script>
9
+ tailwind.config = {
10
+ darkMode: 'class',
11
+ theme: {
12
+ extend: {
13
+ animation: {
14
+ 'gradient': 'gradient 15s ease infinite',
15
+ },
16
+ keyframes: {
17
+ gradient: {
18
+ '0%, 100%': { backgroundPosition: '0% 50%' },
19
+ '50%': { backgroundPosition: '100% 50%' },
20
+ }
21
+ }
22
+ }
23
+ }
24
+ }
25
+ </script>
26
+ <style>
27
+ @import url('https://fonts.googleapis.com/css2?family=Inter: wght@300;400;500;600;700&display=swap');
28
+
29
+ body {
30
+ font-family: 'Inter', sans-serif;
31
+ }
32
+
33
+ .link-card {
34
+ /* Animation removed as requested */
35
+ }
36
+
37
+ .gradient-text {
38
+ background-clip: text;
39
+ -webkit-background-clip: text;
40
+ color: transparent;
41
+ background-image: linear-gradient(to right, #6366f1, #8b5cf6, #d946ef);
42
+ }
43
+
44
+ /* Logo styling */
45
+ .logo-img {
46
+ max-height: 60px;
47
+ width: auto;
48
+ }
49
+
50
+ /* Light mode styles */
51
+ html:not(.dark) body {
52
+ background-color: #f9fafb;
53
+ color: #1f2937;
54
+ }
55
+
56
+ html:not(.dark) .theme-container {
57
+ background: linear-gradient(to bottom right, #f3f4f6, #e5e7eb, #f3f4f6);
58
+ }
59
+
60
+ html: not(.dark) .content-container {
61
+ background-color: rgba(255, 255, 255, 0.8);
62
+ border-color: #e5e7eb;
63
+ }
64
+
65
+ html:not(.dark) .link-card a {
66
+ background-color: rgba(243, 244, 246, 0.5);
67
+ border-color: #e5e7eb;
68
+ }
69
+
70
+ html:not(.dark) .link-card a:hover {
71
+ background-color: rgba(249, 250, 251, 0.5);
72
+ border-color: #6366f1;
73
+ }
74
+
75
+ html:not(.dark) .card-title {
76
+ color: #1f2937;
77
+ }
78
+
79
+ html:not(.dark) .card-url {
80
+ color: #6b7280;
81
+ }
82
+
83
+ html:not(.dark) .footer-border {
84
+ border-color: #e5e7eb;
85
+ }
86
+
87
+ html:not(.dark) .footer-text {
88
+ color: #6b7280;
89
+ }
90
+ </style>
91
+ <script>
92
+ // Check for saved theme preference or use default (dark)
93
+ document.addEventListener('DOMContentLoaded', () => {
94
+ const theme = localStorage.getItem('theme') || 'dark';
95
+ document.documentElement. className = theme;
96
+
97
+ // Update toggle button appearance
98
+ updateToggleButton();
99
+ });
100
+
101
+ // Toggle between dark and light themes
102
+ function toggleTheme() {
103
+ const html = document.documentElement;
104
+ const newTheme = html.classList.contains('dark') ? '' : 'dark';
105
+
106
+ html.className = newTheme;
107
+ localStorage.setItem('theme', newTheme);
108
+
109
+ // Update toggle button appearance
110
+ updateToggleButton();
111
+ }
112
+
113
+ // Update toggle button icon based on current theme
114
+ function updateToggleButton() {
115
+ const isDark = document.documentElement.classList. contains('dark');
116
+ const sunIcon = document.getElementById('sun-icon');
117
+ const moonIcon = document.getElementById('moon-icon');
118
+
119
+ if (isDark) {
120
+ sunIcon.classList.remove('hidden');
121
+ moonIcon. classList.add('hidden');
122
+ } else {
123
+ sunIcon. classList.add('hidden');
124
+ moonIcon.classList.remove('hidden');
125
+ }
126
+ }
127
+ </script>
128
+ </head>
129
+ <body class="dark:bg-black dark:text-gray-200 text-gray-800 font-sans leading-normal tracking-normal min-h-screen">
130
+ <div class="theme-container dark:bg-gradient-to-br dark:from-gray-900 dark:via-gray-800 dark:to-gray-900 min-h-screen py-12 px-4 sm: px-6 dark:animate-gradient dark:bg-[length: 200%_200%]">
131
+ <div class="content-container container max-w-4xl mx-auto backdrop-blur-sm dark:bg-black/30 rounded-xl shadow-2xl p-8 border dark:border-gray-800">
132
+
133
+ <!-- LOGO SECTION - ADD THIS -->
134
+ <div class="flex justify-center mb-6">
135
+ <img src="https://raw.githubusercontent.com/Jebel-Quant/rhiza/main/assets/rhiza-logo.svg" alt="Logo" class="logo-img">
136
+ </div>
137
+ <!-- END LOGO SECTION -->
138
+
139
+ <div class="flex items-center justify-between mb-8">
140
+ <div>
141
+ <h1 class="text-4xl font-bold mb-2 gradient-text">{{ title }}</h1>
142
+ {% if description %}
143
+ <div class="dark:text-gray-400 text-gray-600 italic">{{ description }}</div>
144
+ {% endif %}
145
+ </div>
146
+ <div class="flex items-center space-x-4">
147
+ <button onclick="toggleTheme()" class="flex items-center px-3 py-2 rounded-lg dark: bg-gray-800 bg-gray-200 border dark:border-gray-700 border-gray-300 hover:border-indigo-500 transition-colors duration-200">
148
+ <!-- Sun icon for dark mode (shows when in dark mode) -->
149
+ <svg id="sun-icon" xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2 text-yellow-400" fill="none" viewBox="0 0 24 24" stroke="currentColor">
150
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" />
151
+ </svg>
152
+ <!-- Moon icon for light mode (shows when in light mode) -->
153
+ <svg id="moon-icon" xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2 text-indigo-600 hidden" fill="none" viewBox="0 0 24 24" stroke="currentColor">
154
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z" />
155
+ </svg>
156
+ <span class="dark:text-white text-gray-800 text-sm font-medium">Toggle Theme</span>
157
+ </button>
158
+ <a href="{{ repository_url }}" target="_blank" class="flex items-center px-3 py-2 rounded-lg dark:bg-gray-800 bg-gray-200 border dark:border-gray-700 border-gray-300 hover:border-indigo-500 transition-colors duration-200">
159
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2 dark:text-indigo-400 text-indigo-600" fill="currentColor" viewBox="0 0 24 24">
160
+ <path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z"/>
161
+ </svg>
162
+ <span class="dark:text-white text-gray-800 text-sm font-medium">Home Repo</span>
163
+ </a>
164
+ </div>
165
+ </div>
166
+
167
+ <div class="grid grid-cols-1 md: grid-cols-2 gap-4 mb-8">
168
+ {% for name, url in links %}
169
+ <div class="link-card group">
170
+ <a href="{{ url }}" target="_blank" class="block p-4 rounded-lg dark:bg-gray-800/50 bg-gray-100/80 border dark:border-gray-700 border-gray-300 dark:hover:border-indigo-500 hover: border-indigo-600 transition-all duration-200">
171
+ <div class="flex items-center">
172
+ <div class="flex-shrink-0 w-10 h-10 rounded-full bg-gradient-to-r from-indigo-500 to-purple-600 flex items-center justify-center mr-4 group-hover:from-purple-600 group-hover:to-pink-600 transition-all duration-200">
173
+ <span class="text-white font-bold">{{ name[0] | upper }}</span>
174
+ </div>
175
+ <div>
176
+ <h3 class="card-title text-lg font-medium dark:text-white text-gray-800 dark:group-hover:text-indigo-300 group-hover:text-indigo-600 transition-colors duration-200">{{ name }}</h3>
177
+ <p class="card-url text-xs dark:text-gray-400 text-gray-500 truncate">{{ url }}</p>
178
+ </div>
179
+ </div>
180
+ </a>
181
+ </div>
182
+ {% endfor %}
183
+ </div>
184
+
185
+ <div class="mt-12 pt-6 border-t footer-border dark:border-gray-800 border-gray-300 text-center">
186
+ <div class="footer-text dark:text-gray-500 text-gray-600 text-sm mb-2">
187
+ Generated by <a href="https://pypi.org/project/minibook/" class="dark:text-indigo-400 text-indigo-600 dark:hover:text-indigo-300 hover: text-indigo-500 transition-colors duration-200 font-medium">MiniBook</a> on {{ timestamp }}
188
+ </div>
189
+ <div class="flex items-center justify-center space-x-2 mb-2">
190
+ <span class="footer-text dark:text-gray-500 text-gray-600">A product by</span>
191
+ <a href="https://jqr.ae" class="dark:text-indigo-400 text-indigo-600 dark:hover:text-indigo-300 hover: text-indigo-500 transition-colors duration-200 font-medium flex items-center">
192
+ jqr
193
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 ml-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
194
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
195
+ </svg>
196
+ </a>
197
+ </div>
198
+ <div class="flex items-center justify-center">
199
+ <a href="https://pypi.org/project/minibook/" class="dark:text-indigo-400 text-indigo-600 dark:hover:text-indigo-300 hover:text-indigo-500 transition-colors duration-200 font-medium flex items-center">
200
+ PyPI Package
201
+ <svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4 ml-1" fill="none" viewBox="0 0 24 24" stroke="currentColor">
202
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
203
+ </svg>
204
+ </a>
205
+ </div>
206
+ </div>
207
+ </div>
208
+ </div>
209
+ </body>
210
+ </html>
@@ -0,0 +1,19 @@
1
+ {% extends "default/module.html.jinja2" %}
2
+
3
+ {% block nav %}
4
+ <style>
5
+ .logo img {
6
+ width: 100%;
7
+ max-width: 100px;
8
+ height: auto;
9
+ display: block;
10
+ margin: 0 auto 20px;
11
+ }
12
+ </style>
13
+ <div class="logo">
14
+ <a href="https://github.com/Jebel-Quant/rhiza" title="Home">
15
+ <img src="https://raw.githubusercontent.com/Jebel-Quant/rhiza/main/assets/rhiza-logo.svg" alt="Rhiza Logo">
16
+ </a>
17
+ </div>
18
+ {{ super() }}
19
+ {% endblock nav %}
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "rhiza"
7
- version = "0.2.0"
7
+ version = "0.3.0"
8
8
  description = "Reusable configuration templates for modern Python projects"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -12,7 +12,7 @@ license = { text = "MIT" }
12
12
  authors = [
13
13
  { name = "Thomas Schmelzer" }
14
14
  ]
15
- keywords = ["templates", "configuration", "taskfile", "ci", "ruff"]
15
+ keywords = ["templates", "configuration", "ci", "ruff"]
16
16
  classifiers = [
17
17
  "Programming Language :: Python :: 3",
18
18
  "Programming Language :: Python :: 3 :: Only",
@@ -25,12 +25,6 @@ classifiers = [
25
25
  "Topic :: Software Development :: Build Tools",
26
26
  ]
27
27
 
28
- dependencies = [
29
- "loguru>=0.7.3",
30
- "typer>=0.20.0",
31
- "PyYAML==6.0.3"
32
- ]
33
-
34
28
  [project.urls]
35
29
  Homepage = "https://github.com/jebel-quant/rhiza"
36
30
  Repository = "https://github.com/jebel-quant/rhiza"
@@ -55,11 +49,5 @@ packages = ["src/rhiza"]
55
49
  # see https://deptry.com/usage/#pep-621-dev-dependency-groups
56
50
  pep621_dev_dependency_groups = ["dev"]
57
51
 
58
- [tool.deptry.package_module_name_map]
59
- PyYAML = "yaml"
60
-
61
52
  [tool.marimo.runtime]
62
53
  pythonpath = ["src"]
63
-
64
- [project.scripts]
65
- rhiza = "rhiza.__main__:app"
File without changes
@@ -14,6 +14,32 @@ import subprocess
14
14
 
15
15
  import pytest
16
16
 
17
+ MOCK_MAKE_SCRIPT = """#!/usr/bin/env python3
18
+ import sys
19
+
20
+ if len(sys.argv) > 1 and sys.argv[1] == "help":
21
+ print("Mock Makefile Help")
22
+ print("target: ## Description")
23
+ """
24
+
25
+ MOCK_UVX_SCRIPT = """#!/usr/bin/env python3
26
+ import sys
27
+ import os
28
+
29
+ # args look like: marimushka>=0.1.9 export --notebooks . --output /path/to/output --bin-path ...
30
+ args = sys.argv[1:]
31
+ if "export" in args:
32
+ try:
33
+ if "--output" in args:
34
+ output_idx = args.index("--output")
35
+ output_dir = args[output_idx + 1]
36
+ os.makedirs(output_dir, exist_ok=True)
37
+ with open(os.path.join(output_dir, "index.html"), "w") as f:
38
+ f.write("<html>Mock Export</html>")
39
+ except ValueError:
40
+ pass
41
+ """
42
+
17
43
  MOCK_UV_SCRIPT = """#!/usr/bin/env python3
18
44
  import sys
19
45
  import re
@@ -50,7 +76,12 @@ def bump_version(current, bump_type):
50
76
  def main():
51
77
  args = sys.argv[1:]
52
78
  # Expected invocations from release.sh start with 'version'
53
- if not args or args[0] != "version":
79
+ if not args:
80
+ sys.exit(1)
81
+
82
+ if args[0] != "version":
83
+ # It might be a uvx call if we use the same script, but let's keep them separate or handle it here.
84
+ # For now, let's assume this is only for uv version commands as per original design.
54
85
  sys.exit(1)
55
86
 
56
87
  # uv version --short
@@ -157,6 +188,16 @@ def git_repo(root, tmp_path, monkeypatch):
157
188
  f.write(MOCK_UV_SCRIPT)
158
189
  uv_path.chmod(0o755)
159
190
 
191
+ uvx_path = bin_dir / "uvx"
192
+ with open(uvx_path, "w") as f:
193
+ f.write(MOCK_UVX_SCRIPT)
194
+ uvx_path.chmod(0o755)
195
+
196
+ make_path = bin_dir / "make"
197
+ with open(make_path, "w") as f:
198
+ f.write(MOCK_MAKE_SCRIPT)
199
+ make_path.chmod(0o755)
200
+
160
201
  # Ensure our bin comes first on PATH so 'uv' resolves to mock
161
202
  monkeypatch.setenv("PATH", f"{bin_dir}:{os.environ.get('PATH', '')}")
162
203
 
@@ -166,9 +207,13 @@ def git_repo(root, tmp_path, monkeypatch):
166
207
 
167
208
  shutil.copy(root / ".github" / "scripts" / "release.sh", script_dir / "release.sh")
168
209
  shutil.copy(root / ".github" / "scripts" / "bump.sh", script_dir / "bump.sh")
210
+ shutil.copy(root / ".github" / "scripts" / "marimushka.sh", script_dir / "marimushka.sh")
211
+ shutil.copy(root / ".github" / "scripts" / "update-readme-help.sh", script_dir / "update-readme-help.sh")
169
212
 
170
213
  (script_dir / "release.sh").chmod(0o755)
171
214
  (script_dir / "bump.sh").chmod(0o755)
215
+ (script_dir / "marimushka.sh").chmod(0o755)
216
+ (script_dir / "update-readme-help.sh").chmod(0o755)
172
217
 
173
218
  # Commit and push initial state
174
219
  subprocess.run(["git", "config", "user.email", "test@example.com"], check=True)
@@ -27,6 +27,12 @@ def strip_ansi(text: str) -> str:
27
27
  return ansi_escape.sub("", text)
28
28
 
29
29
 
30
+ @pytest.fixture
31
+ def expected_uv_install_dir() -> str:
32
+ """Get the expected UV_INSTALL_DIR from environment or default to ./bin."""
33
+ return os.environ.get("UV_INSTALL_DIR", "./bin")
34
+
35
+
30
36
  @pytest.fixture(autouse=True)
31
37
  def setup_tmp_makefile(logger, root, tmp_path: Path):
32
38
  """Copy only the Makefile into a temp directory and chdir there.
@@ -100,32 +106,40 @@ class TestMakefile:
100
106
  assert "Targets:" in out
101
107
  assert "Bootstrap" in out or "Meta" in out # section headers
102
108
 
103
- def test_fmt_target_dry_run(self, logger):
109
+ def test_fmt_target_dry_run(self, logger, expected_uv_install_dir):
104
110
  """Fmt target should invoke pre-commit via uvx in dry-run output."""
105
111
  proc = run_make(logger, ["fmt"])
106
112
  out = proc.stdout
107
- assert "./bin/uvx pre-commit run --all-files" in out
113
+ # Check for uvx command with the configured path
114
+ expected_uvx = f"{expected_uv_install_dir}/uvx"
115
+ assert f"{expected_uvx} pre-commit run --all-files" in out
108
116
 
109
- def test_deptry_target_dry_run(self, logger):
117
+ def test_deptry_target_dry_run(self, logger, expected_uv_install_dir):
110
118
  """Deptry target should invoke deptry via uvx in dry-run output."""
111
119
  proc = run_make(logger, ["deptry"])
112
120
  out = proc.stdout
113
- assert './bin/uvx deptry "src"' in out
121
+ # Check for uvx command with the configured path
122
+ expected_uvx = f"{expected_uv_install_dir}/uvx"
123
+ assert f'{expected_uvx} deptry "src"' in out
114
124
 
115
- def test_test_target_dry_run(self, logger):
125
+ def test_test_target_dry_run(self, logger, expected_uv_install_dir):
116
126
  """Test target should invoke pytest via uv with coverage and HTML outputs in dry-run output."""
117
127
  proc = run_make(logger, ["test"])
118
128
  out = proc.stdout
119
129
  # Expect key steps
120
130
  assert "mkdir -p _tests/html-coverage _tests/html-report" in out
121
- assert "./bin/uv run pytest" in out
131
+ # Check for uv command with the configured path
132
+ expected_uv = f"{expected_uv_install_dir}/uv"
133
+ assert f"{expected_uv} run pytest" in out
122
134
 
123
- def test_book_target_dry_run(self, logger):
135
+ def test_book_target_dry_run(self, logger, expected_uv_install_dir):
124
136
  """Book target should run inline commands to assemble the book without go-task."""
125
137
  proc = run_make(logger, ["book"])
126
138
  out = proc.stdout
127
139
  # Expect marimushka export to install marimo and minibook to be invoked
128
- assert "./bin/uvx minibook" in out
140
+ # Check for uvx command with the configured path
141
+ expected_uvx = f"{expected_uv_install_dir}/uvx"
142
+ assert f"{expected_uvx} minibook" in out
129
143
 
130
144
  def test_all_target_dry_run(self, logger):
131
145
  """All target echoes a composite message in dry-run output."""
@@ -140,23 +154,28 @@ class TestMakefile:
140
154
  out = strip_ansi(proc.stdout)
141
155
  assert "Value of UV_NO_MODIFY_PATH:\n1" in out
142
156
 
143
- def test_uv_install_dir_is_bin(self, logger):
144
- """`UV_INSTALL_DIR` should point to `./bin`."""
157
+ def test_uv_install_dir_is_bin(self, logger, expected_uv_install_dir):
158
+ """`UV_INSTALL_DIR` can be configured via environment variable or defaults to ./bin."""
145
159
  proc = run_make(logger, ["print-UV_INSTALL_DIR"], dry_run=False)
146
160
  out = strip_ansi(proc.stdout)
147
- assert "Value of UV_INSTALL_DIR:\n./bin" in out
161
+ # Check if UV_INSTALL_DIR is set in environment, otherwise expect default ./bin
162
+ assert f"Value of UV_INSTALL_DIR:\n{expected_uv_install_dir}" in out
148
163
 
149
- def test_uv_bin_is_bin_uv(self, logger):
150
- """`UV_BIN` should point to `./bin/uv`."""
164
+ def test_uv_bin_is_bin_uv(self, logger, expected_uv_install_dir):
165
+ """`UV_BIN` is derived from UV_INSTALL_DIR environment variable or defaults to ./bin/uv."""
151
166
  proc = run_make(logger, ["print-UV_BIN"], dry_run=False)
152
167
  out = strip_ansi(proc.stdout)
153
- assert "Value of UV_BIN:\n./bin/uv" in out
168
+ # Check if UV_INSTALL_DIR is set in environment, otherwise expect default ./bin
169
+ expected_bin = f"{expected_uv_install_dir}/uv"
170
+ assert f"Value of UV_BIN:\n{expected_bin}" in out
154
171
 
155
- def test_uvx_bin_is_bin_uvx(self, logger):
156
- """`UVX_BIN` should point to `./bin/uvx`."""
172
+ def test_uvx_bin_is_bin_uvx(self, logger, expected_uv_install_dir):
173
+ """`UVX_BIN` is derived from UV_INSTALL_DIR environment variable or defaults to ./bin/uvx."""
157
174
  proc = run_make(logger, ["print-UVX_BIN"], dry_run=False)
158
175
  out = strip_ansi(proc.stdout)
159
- assert "Value of UVX_BIN:\n./bin/uvx" in out
176
+ # Check if UV_INSTALL_DIR is set in environment, otherwise expect default ./bin
177
+ expected_bin = f"{expected_uv_install_dir}/uvx"
178
+ assert f"Value of UVX_BIN:\n{expected_bin}" in out
160
179
 
161
180
  def test_script_folder_is_github_scripts(self, logger):
162
181
  """`SCRIPTS_FOLDER` should point to `.github/scripts`."""