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.
- {rhiza-0.2.0 → rhiza-0.3.0}/.devcontainer/devcontainer.json +1 -1
- {rhiza-0.2.0 → rhiza-0.3.0}/.github/scripts/marimushka.sh +11 -1
- {rhiza-0.2.0 → rhiza-0.3.0}/Makefile +8 -2
- {rhiza-0.2.0 → rhiza-0.3.0}/PKG-INFO +24 -5
- {rhiza-0.2.0 → rhiza-0.3.0}/README.md +22 -0
- {rhiza-0.2.0 → rhiza-0.3.0}/book/marimo/df.py +3 -1
- rhiza-0.3.0/book/minibook-templates/custom.html.jinja2 +210 -0
- rhiza-0.3.0/book/pdoc-templates/module.html.jinja2 +19 -0
- {rhiza-0.2.0 → rhiza-0.3.0}/pyproject.toml +2 -14
- rhiza-0.3.0/src/rhiza/.gitkeep +0 -0
- {rhiza-0.2.0 → rhiza-0.3.0}/tests/test_rhiza/conftest.py +46 -1
- {rhiza-0.2.0 → rhiza-0.3.0}/tests/test_rhiza/test_makefile.py +36 -17
- rhiza-0.3.0/tests/test_rhiza/test_marimushka_script.py +72 -0
- rhiza-0.3.0/tests/test_rhiza/test_updatereadme_script.py +94 -0
- {rhiza-0.2.0 → rhiza-0.3.0}/uv.lock +1 -89
- rhiza-0.2.0/.devcontainer/startup.sh +0 -10
- rhiza-0.2.0/src/rhiza/__init__.py +0 -5
- rhiza-0.2.0/src/rhiza/__main__.py +0 -10
- rhiza-0.2.0/src/rhiza/cli.py +0 -47
- rhiza-0.2.0/src/rhiza/commands/__init__.py +0 -5
- rhiza-0.2.0/src/rhiza/commands/hello.py +0 -9
- rhiza-0.2.0/src/rhiza/commands/inject.py +0 -160
- rhiza-0.2.0/tests/test_rhiza/test_cli_commands.py +0 -482
- {rhiza-0.2.0 → rhiza-0.3.0}/.devcontainer/bootstrap.sh +0 -0
- {rhiza-0.2.0 → rhiza-0.3.0}/.editorconfig +0 -0
- {rhiza-0.2.0 → rhiza-0.3.0}/.github/actions/setup-project/action.yml +0 -0
- {rhiza-0.2.0 → rhiza-0.3.0}/.github/renovate.json +0 -0
- {rhiza-0.2.0 → rhiza-0.3.0}/.github/scripts/book.sh +0 -0
- {rhiza-0.2.0 → rhiza-0.3.0}/.github/scripts/bump.sh +0 -0
- {rhiza-0.2.0 → rhiza-0.3.0}/.github/scripts/customisations/build-extras.sh +0 -0
- {rhiza-0.2.0 → rhiza-0.3.0}/.github/scripts/customisations/post-release.sh +0 -0
- {rhiza-0.2.0 → rhiza-0.3.0}/.github/scripts/release.sh +0 -0
- {rhiza-0.2.0 → rhiza-0.3.0}/.github/scripts/update-readme-help.sh +0 -0
- {rhiza-0.2.0 → rhiza-0.3.0}/.github/workflows/book.yml +0 -0
- {rhiza-0.2.0 → rhiza-0.3.0}/.github/workflows/ci.yml +0 -0
- {rhiza-0.2.0 → rhiza-0.3.0}/.github/workflows/deptry.yml +0 -0
- {rhiza-0.2.0 → rhiza-0.3.0}/.github/workflows/devcontainer.yml +0 -0
- {rhiza-0.2.0 → rhiza-0.3.0}/.github/workflows/docker.yml +0 -0
- {rhiza-0.2.0 → rhiza-0.3.0}/.github/workflows/marimo.yml +0 -0
- {rhiza-0.2.0 → rhiza-0.3.0}/.github/workflows/pre-commit.yml +0 -0
- {rhiza-0.2.0 → rhiza-0.3.0}/.github/workflows/release.yml +0 -0
- {rhiza-0.2.0 → rhiza-0.3.0}/.github/workflows/scripts/version_matrix.py +0 -0
- {rhiza-0.2.0 → rhiza-0.3.0}/.github/workflows/scripts/version_max.py +0 -0
- {rhiza-0.2.0 → rhiza-0.3.0}/.github/workflows/sync.yml +0 -0
- {rhiza-0.2.0 → rhiza-0.3.0}/.gitignore +0 -0
- {rhiza-0.2.0 → rhiza-0.3.0}/.pre-commit-config.yaml +0 -0
- {rhiza-0.2.0 → rhiza-0.3.0}/CODE_OF_CONDUCT.md +0 -0
- {rhiza-0.2.0 → rhiza-0.3.0}/CONTRIBUTING.md +0 -0
- {rhiza-0.2.0 → rhiza-0.3.0}/LICENSE +0 -0
- {rhiza-0.2.0 → rhiza-0.3.0}/assets/rhiza-logo.svg +0 -0
- {rhiza-0.2.0 → rhiza-0.3.0}/docker/Dockerfile +0 -0
- {rhiza-0.2.0 → rhiza-0.3.0}/docker/Dockerfile.dockerignore +0 -0
- {rhiza-0.2.0 → rhiza-0.3.0}/docker/README.md +0 -0
- {rhiza-0.2.0 → rhiza-0.3.0}/pytest.ini +0 -0
- {rhiza-0.2.0 → rhiza-0.3.0}/ruff.toml +0 -0
- {rhiza-0.2.0 → rhiza-0.3.0}/tests/test_rhiza/README.md +0 -0
- {rhiza-0.2.0 → rhiza-0.3.0}/tests/test_rhiza/test_bump_script.py +0 -0
- {rhiza-0.2.0 → rhiza-0.3.0}/tests/test_rhiza/test_docstrings.py +0 -0
- {rhiza-0.2.0 → rhiza-0.3.0}/tests/test_rhiza/test_git_repo_fixture.py +0 -0
- {rhiza-0.2.0 → rhiza-0.3.0}/tests/test_rhiza/test_readme.py +0 -0
- {rhiza-0.2.0 → rhiza-0.3.0}/tests/test_rhiza/test_release_script.py +0 -0
- {rhiza-0.2.0 → rhiza-0.3.0}/tests/test_rhiza/test_structure.py +0 -0
|
@@ -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 "$
|
|
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
|
-
|
|
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.
|
|
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,
|
|
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.
|
|
@@ -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.
|
|
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", "
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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`
|
|
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
|
-
|
|
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`
|
|
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
|
-
|
|
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`
|
|
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
|
-
|
|
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`."""
|