chore: update rhiza to v1.3.2 - #26
Conversation
📝 WalkthroughWalkthroughThis change updates GitHub governance and workflows to Rhiza v1.3.2, expands Rhiza Make targets and shell completions, adds repository validation tests, and refreshes development tooling and release configuration. ChangesRhiza repository refresh
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
This PR syncs the repository against the upstream jebel-quant/rhiza template, bumping from v0.18.8 to v1.3.2. It modernizes the Rhiza-managed project scaffolding (Makefile fragments, lint/test tooling, and GitHub workflows) to match the v1.x template structure.
Changes:
- Updates Rhiza template pin and lockfile to
v1.3.2, bringing in new/updated template-owned files (tests, make fragments, workflows, rulesets, config docs). - Refactors Makefile responsibilities into language-neutral core (
.rhiza/rhiza.mk, quality/bootstrap) plus a Python language layer (.rhiza/make.d/python.mk), and moves optional test extras into thetestsbundle fragment. - Updates lint/test config (
ruff.toml,pytest.ini) and adds baseline packaging/test invariants.
Reviewed changes
Copilot reviewed 46 out of 49 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/test_rhiza_packaging.py | Adds a baseline packaging invariant test (declared vs installed version). |
| ruff.toml | Updates Ruff rule selection and per-file ignores; removes explicit target-version setting. |
| pytest.ini | Adds .rhiza/tests to pythonpath, changes default live logging, adds a specific warning-as-error filter. |
| Makefile | Adjusts template override variables (notably mkdocs extra packages). |
| docs/index.md | Trims trailing blank line from docs include stub. |
| docs/development/TESTS.md | Documents opting into live pytest CLI logs; updates test dependency provisioning notes. |
| cliff.toml | Adds git-cliff configuration for changelog generation. |
| .rhiza/tests/test_release_tags.py | Adds git tag reachability invariant test for releases/changelog boundaries. |
| .rhiza/tests/test_readme.py | Adds README existence + bash-fence syntax validation (language-neutral). |
| .rhiza/tests/test_readme_validation.py | Adds Python README fence execution + result diffing (Python-only). |
| .rhiza/tests/test_pyproject.py | Adds pyproject structure + release-config invariants (requires bump-my-version discoverability). |
| .rhiza/tests/test_docstrings.py | Adds doctest discovery across source modules with .rhiza/.env-driven source folder. |
| .rhiza/tests/README.md | Rewrites Rhiza test-suite documentation to match the new flat-per-concern layout. |
| .rhiza/tests/conftest.py | Replaces legacy fixtures with shared root/logger/latest_tag fixtures. |
| .rhiza/template.yml | Updates template ref to v1.3.2. |
| .rhiza/template.lock | Updates template SHA/ref and the template-owned file list for v1.3.2. |
| .rhiza/rhiza.mk | Updates core make logic (POSIX shell check on Windows, uv tool-runner framing, help regex tweak, etc.). |
| .rhiza/make.d/test.mk | Moves core Python gates out; keeps optional extras (bench/hypothesis/stress/mutation) with uv run --with …. |
| .rhiza/make.d/quality.mk | Switches fmt to prek; adds/updates rhiza-test runner; simplifies core quality gates. |
| .rhiza/make.d/python.mk | Adds the Python language layer: install, all, test, typecheck, security, docs-coverage, deps, license, etc. |
| .rhiza/make.d/marimo.mk | Contributes marimo folder to deptry scan with appropriate ignores. |
| .rhiza/make.d/github.mk | Adds GitHub maintenance helper targets that shell out to gh. |
| .rhiza/make.d/doctor.mk | Makes doctor extensible via double-colon; adjusts status output formatting. |
| .rhiza/make.d/completions.mk | Adds a make target to install bundled bash/zsh completion scripts. |
| .rhiza/make.d/bootstrap.mk | Removes language-specific install; keeps uv bootstrap + clean hooks; tightens stale-branch deletion filter. |
| .rhiza/make.d/book.mk | Updates book build and marimo export to provision tools via uv run --with …. |
| .rhiza/completions/rhiza-completion.zsh | Adds cached target/description parsing for faster completions. |
| .rhiza/completions/rhiza-completion.bash | Adds cached target parsing for faster completions. |
| .rhiza/completions/README.md | Updates completion docs; adds quick-install instructions; documents caching behavior. |
| .rhiza/.env | Expands documentation and provides default override examples for core env variables. |
| .pre-commit-config.yaml | Updates hook versions; pins node version for hook runtime; adds betterleaks; bumps rhiza-hooks major. |
| .gitignore | Adds Rust-related ignores (template-shared). |
| .github/workflows/rhiza_weekly.yml | Updates reusable workflow pin to v1.3.2. |
| .github/workflows/rhiza_scorecard.yml | Adds OSSF Scorecard workflow stub pinned to v1.3.2. |
| .github/workflows/rhiza_release.yml | Major release workflow updates (permissions hardening, attestation assets, changelog flow change, robustness tweaks). |
| .github/workflows/rhiza_mutation.yml | Adds opt-in mutation testing workflow stub pinned to v1.3.2. |
| .github/workflows/rhiza_marimo.yml | Updates reusable workflow pin to v1.3.2. |
| .github/workflows/rhiza_fuzzing.yml | Adds fuzzing workflow stub pinned to v1.3.2. |
| .github/workflows/rhiza_codeql.yml | Updates reusable workflow pin; moves required permissions to job-level. |
| .github/workflows/rhiza_ci.yml | Updates reusable workflow pin to v1.3.2 and documents Python matrix source-of-truth. |
| .github/workflows/rhiza_book.yml | Updates triggers/permissions; updates reusable workflow pin to v1.3.2. |
| .github/workflows/rhiza_benchmark.yml | Updates reusable workflow pin to v1.3.2. |
| .github/rulesets/tag-protection.json | Adds template-provided tag protection ruleset definition. |
| .github/rulesets/main-branch-protection.json | Adds template-provided main branch protection ruleset definition. |
| .github/pull_request_template.md | Updates checklist (CHANGELOG entry + make deps terminology). |
| .github/DISCUSSION_TEMPLATE/ideas.yml | Adds an “Idea” discussion template. |
| .github/DISCUSSION_TEMPLATE/help-wanted.yml | Adds a “Help Wanted” discussion template. |
| .github/dependabot.yml | Trims trailing whitespace / formatting fix. |
| .github/CONFIG.md | Adds documentation for configuring secrets used by Rhiza workflows. |
Suppressed comments (1)
.rhiza/tests/test_pyproject.py:281
test_a_discoverable_config_existswill fail in this repository because there is currently no[tool.bumpversion]table inpyproject.tomland no other discoverable bump-my-version config files (.bumpversion.toml/.cfg/setup.cfg). To keep the new rhiza-test gate green after the template bump, add a minimal[tool.bumpversion]table topyproject.toml(commit=false, tag=false, allow_dirty=false) as described in this docstring.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| code = "".join(code_blocks) # merged code | ||
| expected = "".join(result_blocks) # merged results | ||
|
|
| Provides shared session-scoped fixtures (``root``, ``logger`` and ``latest_tag``) used | ||
| across the test modules. | ||
|
|
| def test_no_license_classifier(self, project: dict) -> None: | ||
| """No deprecated 'License :: ' classifier may be present. | ||
|
|
||
| PyPI has deprecated the ``License ::`` trove classifiers in favor of the SPDX | ||
| ``license`` expression field, so the shipped pyproject must not declare one. | ||
| """ | ||
| classifiers = project.get("classifiers", []) | ||
| license_classifiers = [c for c in classifiers if c.startswith("License ::")] | ||
| assert not license_classifiers, ( | ||
| f"classifiers must not include any deprecated 'License :: ' entry; found {license_classifiers}" | ||
| ) |
| "A", # flake8-builtins - Don't shadow Python builtins | ||
| "ANN001", # flake8-annotations - Require function argument annotations | ||
| "ANN2", # flake8-annotations - Require function return annotations | ||
| "ARG", # flake8-unused-arguments - Unused arguments (tests exempt below: pytest fixtures) |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/rhiza_weekly.yml:
- Around line 31-32: Update the reusable workflow call in rhiza_weekly.yml to
replace the version tag with a full commit SHA pin and remove secrets: inherit.
Pass only the explicitly required secrets using the called workflow’s expected
secret names.
In @.rhiza/rhiza.mk:
- Around line 128-146: Remove the orphaned readme and version-matrix entries
from the relevant .PHONY declaration in .rhiza/rhiza.mk, leaving all currently
defined targets such as print-logo, help, and ci-os-matrix unchanged.
In @.rhiza/tests/test_pyproject.py:
- Line 31: Update the _SEMVER_RE validation and its usage in test_pyproject.py
to require the entire version string to match the semantic-version pattern,
using fullmatch() and anchoring the pattern so values like 1.2.3invalid are
rejected.
In @.rhiza/tests/test_readme_validation.py:
- Around line 66-79: Update the README example execution around subprocess.run
in the test to enforce a finite timeout, and catch subprocess.TimeoutExpired so
the example is reported as a failed validation rather than hanging indefinitely.
Preserve the existing stdout, stderr, return-code, and expected-output
assertions for processes that complete normally.
In `@docs/development/TESTS.md`:
- Around line 80-82: Update the pytest command code block in TESTS.md by
replacing its opening and closing backtick fences with tilde fences, preserving
the existing bash content unchanged.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 6504fadd-dbc2-4c4e-a3c3-d1b4e58c3feb
⛔ Files ignored due to path filters (1)
.rhiza/template.lockis excluded by!**/*.lock
📒 Files selected for processing (48)
.github/CONFIG.md.github/DISCUSSION_TEMPLATE/help-wanted.yml.github/DISCUSSION_TEMPLATE/ideas.yml.github/dependabot.yml.github/pull_request_template.md.github/rulesets/main-branch-protection.json.github/rulesets/tag-protection.json.github/workflows/rhiza_benchmark.yml.github/workflows/rhiza_book.yml.github/workflows/rhiza_ci.yml.github/workflows/rhiza_codeql.yml.github/workflows/rhiza_fuzzing.yml.github/workflows/rhiza_marimo.yml.github/workflows/rhiza_mutation.yml.github/workflows/rhiza_release.yml.github/workflows/rhiza_scorecard.yml.github/workflows/rhiza_weekly.yml.gitignore.pre-commit-config.yaml.rhiza/.env.rhiza/completions/README.md.rhiza/completions/rhiza-completion.bash.rhiza/completions/rhiza-completion.zsh.rhiza/make.d/book.mk.rhiza/make.d/bootstrap.mk.rhiza/make.d/completions.mk.rhiza/make.d/doctor.mk.rhiza/make.d/github.mk.rhiza/make.d/marimo.mk.rhiza/make.d/python.mk.rhiza/make.d/quality.mk.rhiza/make.d/test.mk.rhiza/rhiza.mk.rhiza/template.yml.rhiza/tests/README.md.rhiza/tests/conftest.py.rhiza/tests/test_docstrings.py.rhiza/tests/test_pyproject.py.rhiza/tests/test_readme.py.rhiza/tests/test_readme_validation.py.rhiza/tests/test_release_tags.pyMakefilecliff.tomldocs/development/TESTS.mddocs/index.mdpytest.iniruff.tomltests/test_rhiza_packaging.py
💤 Files with no reviewable changes (1)
- docs/index.md
| uses: jebel-quant/rhiza/.github/workflows/rhiza_weekly.yml@v1.3.2 | ||
| secrets: inherit |
There was a problem hiding this comment.
🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Files matching rhiza_weekly:"
fd -a 'rhiza_weekly\.yml|rhiza_weekly\.yaml' .github . 2>/dev/null || true
echo
echo "Target workflow:"
if [ -f .github/workflows/rhiza_weekly.yml ]; then
nl -ba .github/workflows/rhiza_weekly.yml | sed -n '1,120p'
fi
echo
echo "Reusable workflow references in workflows:"
rg -n "uses: .*\.github/workflows/.*@|secrets:\s*inherit|secrets:" .github/workflows || trueRepository: tschm/pycharting
Length of output: 296
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "Target workflow:"
awk '{printf "%6d\t%s\n", NR, $0}' .github/workflows/rhiza_weekly.yml | sed -n '1,120p'
echo
echo "Reusable workflow references in workflows:"
rg -n "uses: .*\.github/workflows/.*@|secrets:\s*inherit|secrets:" .github/workflows || true
echo
echo "Workflow files:"
git ls-files .github/workflowsRepository: tschm/pycharting
Length of output: 3486
🌐 Web query:
GitHub Actions reusable workflows secrets inherit includes all secrets
💡 Result:
Yes, the secrets: inherit keyword in GitHub Actions reusable workflows does include all secrets that the calling workflow has access to [1][2]. When you use secrets: inherit, the called (reusable) workflow automatically receives all secrets available to the caller workflow, including repository-level secrets, organization-level secrets, and environment-scoped secrets [1][3][2]. Key points regarding this behavior: 1. Scope: The reusable workflow gains access to the entire set of secrets that the calling workflow is authorized to use [2]. 2. Implicit Access: Unlike standard reusable workflow calls where you must explicitly map secrets in the on.workflow_call.secrets block, secrets: inherit makes them available implicitly [1][4]. You do not need to define them in the reusable workflow's on key to use them [1][5]. 3. Security Implication: Because it passes the entire collection of available secrets, it is generally considered a best practice to use this feature with caution, particularly when calling third-party or external reusable workflows [2]. If the reusable workflow is compromised, it could theoretically access any secret inherited from the caller [2]. Explicitly passing only the required secrets by name is recommended for sensitive or untrusted workflows to minimize the attack surface [2]. 4. Chain Limitations: Secrets are only inherited by the directly called workflow [1]. If workflow A calls workflow B with secrets: inherit, and workflow B calls workflow C, workflow C will not automatically inherit secrets from A unless they are explicitly passed from B to C or B also uses secrets: inherit [1][4].
Citations:
- 1: https://docs.github.com/en/actions/how-tos/reuse-automations/reuse-workflows
- 2: https://gethasp.com/guides/github-actions-secrets-correctly/
- 3: [BUG] Environment-scoped secrets unreachable from reusable workflow without secrets: inherit, despite called job declaring environment actions/runner#4453
- 4: https://docs.github.com/actions/using-workflows/workflow-syntax-for-github-actions
- 5: https://github.com/github/docs/blob/main/content/actions/how-tos/reuse-automations/reuse-workflows.md
Sensitive Data Exposure (CWE-200): Exposure of Sensitive Information to an Unauthorized Actor
Reachability: Internal
Avoid inheriting secrets into reusable workflow calls.
secrets: inherit forwards all caller secrets to the referenced workflow. Use a full-commit pin for .github/workflows/rhiza_weekly.yml and pass only the specific secrets the called workflow needs.
🧰 Tools
🪛 zizmor (1.29.0)
[warning] 31-31: secrets unconditionally inherited by called workflow (secrets-inherit): this reusable workflow
(secrets-inherit)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/rhiza_weekly.yml around lines 31 - 32, Update the reusable
workflow call in rhiza_weekly.yml to replace the version tag with a full commit
SHA pin and remove secrets: inherit. Pass only the explicitly required secrets
using the called workflow’s expected secret names.
Source: Linters/SAST tools
| .PHONY: print-logo | ||
|
|
||
| ##@ Rhiza Workflows | ||
|
|
||
| print-logo: | ||
| @printf "${BLUE}$$RHIZA_LOGO${RESET}\n" | ||
|
|
||
|
|
||
| sync: pre-sync ## sync with template repository as defined in .rhiza/template.yml | ||
| @if git remote get-url origin 2>/dev/null | grep -iqE 'jebel-quant/rhiza(\.git)?$$'; then \ | ||
| printf "${BLUE}[INFO] Skipping sync in rhiza repository (no template.yml by design)${RESET}\n"; \ | ||
| else \ | ||
| $(MAKE) install-uv && \ | ||
| ${UVX_BIN} "rhiza==$(RHIZA_VERSION)" sync . && \ | ||
| $(MAKE) _apply-sync-schedule; \ | ||
| fi | ||
| @$(MAKE) post-sync | ||
|
|
||
| _apply-sync-schedule: ## (internal) apply RHIZA_SYNC_SCHEDULE override to GitHub Actions sync workflow | ||
| @if [ "$(RHIZA_SYNC_SCHEDULE)" != "0 0 * * 1" ] && [ -f .github/workflows/rhiza_sync.yml ]; then \ | ||
| sed -i.bak "s|cron: '[^']*'|cron: '$(RHIZA_SYNC_SCHEDULE)'|" .github/workflows/rhiza_sync.yml && rm -f .github/workflows/rhiza_sync.yml.bak; \ | ||
| printf "${BLUE}[INFO] Applied custom sync schedule: $(RHIZA_SYNC_SCHEDULE)${RESET}\n"; \ | ||
| fi | ||
|
|
||
| materialize: ## [DEPRECATED] use 'make sync' instead — materialize --force is now sync | ||
| @printf "${YELLOW}[WARN] 'make materialize' is deprecated and will be removed in a future release.${RESET}\n" | ||
| @printf "${YELLOW}[WARN] Please use 'make sync' instead (e.g. 'materialize --force' is now 'make sync').${RESET}\n" | ||
| @$(MAKE) sync | ||
|
|
||
| summarise-sync: install-uv ## summarise differences created by sync with template repository | ||
| @if git remote get-url origin 2>/dev/null | grep -iqE 'jebel-quant/rhiza(\.git)?$$'; then \ | ||
| printf "${BLUE}[INFO] Skipping summarise-sync in rhiza repository (no template.yml by design)${RESET}\n"; \ | ||
| else \ | ||
| $(MAKE) install-uv; \ | ||
| ${UVX_BIN} "rhiza==$(RHIZA_VERSION)" summarise .; \ | ||
| fi | ||
|
|
||
| rhiza-test: install ## run rhiza's own tests (if any) | ||
| @if [ -d ".rhiza/tests" ]; then \ | ||
| ${UV_BIN} run pytest .rhiza/tests; \ | ||
| else \ | ||
| printf "${YELLOW}[WARN] No .rhiza/tests directory found, skipping rhiza-tests${RESET}\n"; \ | ||
| fi | ||
|
|
||
| validate: pre-validate rhiza-test ## validate project structure against template repository as defined in .rhiza/template.yml | ||
| @if git remote get-url origin 2>/dev/null | grep -iqE 'jebel-quant/rhiza(\.git)?$$'; then \ | ||
| printf "${BLUE}[INFO] Skipping validate in rhiza repository (no template.yml by design)${RESET}\n"; \ | ||
| else \ | ||
| $(MAKE) install-uv; \ | ||
| ${UVX_BIN} "rhiza==$(RHIZA_VERSION)" validate .; \ | ||
| fi | ||
| @$(MAKE) post-validate | ||
|
|
||
| readme: install-uv ## update README.md with current Makefile help output | ||
| @${UVX_BIN} "rhiza-tools>=0.2.0" update-readme | ||
|
|
||
| ##@ Meta | ||
|
|
||
| help: print-logo ## Display this help message | ||
| +@printf "$(BOLD)Usage:$(RESET)\n" | ||
| +@printf " make $(BLUE)<target>$(RESET)\n\n" | ||
| +@printf "$(BOLD)Targets:$(RESET)\n" | ||
| +@awk 'BEGIN {FS = ":.*##"; printf ""} /^[a-zA-Z_-]+:.*?##/ { printf " $(BLUE)%-20s$(RESET) %s\n", $$1, $$2 } /^##@/ { printf "\n$(BOLD)%s$(RESET)\n", substr($$0, 5) }' $(MAKEFILE_LIST) | ||
| +@awk 'BEGIN {FS = ":.*##"; printf ""} /^[a-zA-Z0-9_-]+:.*?##/ { printf " $(BLUE)%-20s$(RESET) %s\n", $$1, $$2 } /^##@/ { printf "\n$(BOLD)%s$(RESET)\n", substr($$0, 5) }' $(MAKEFILE_LIST) | ||
| +@printf "\n" | ||
|
|
||
| version-matrix: install-uv ## Emit the list of supported Python versions from pyproject.toml | ||
| @${UVX_BIN} "rhiza-tools>=0.2.2" version-matrix | ||
|
|
||
| ci-os-matrix: ## Emit GitHub CI OSes (RHIZA_CI_OS_MATRIX as JSON array, default ["ubuntu-latest"]) | ||
| @printf '%s\n' '$(or $(RHIZA_CI_OS_MATRIX),["ubuntu-latest"])' | ||
| @$(info $(or $(RHIZA_CI_OS_MATRIX),["ubuntu-latest"])) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '== Target definitions =='
rg -nP --glob 'Makefile' --iglob '*.mk' \
'^\s*(readme|version-matrix)\s*:(:)?(?:\s|#|$)' .
printf '%s\n' '== Target callers =='
rg -nP --glob 'Makefile' --iglob '*.mk' --iglob '*.yml' --iglob '*.yaml' \
'(?:\$\(MAKE\)|make)\s+(?:[^\n]*\s)?(readme|version-matrix)\b' .Repository: tschm/pycharting
Length of output: 179
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '== Candidate Makefiles =="
git ls-files | rg '(^|/)(Makefile|.*\.mk)$' || true
printf '%s\n' '== .rhiza/rhiza.mk target declarations/lines 45-150 =='
sed -n '45,155p' .rhiza/rhiza.mk
printf '%s\n' '== All readme/version-matrix references =='
rg -n --glob 'Makefile' --iglob '*.mk' --iglob '*.yml' --iglob '*.yaml' --iglob '*.md' 'readme|version-matrix' . || true
printf '%s\n' '== Makefile parsing probe: defined/readable targets and phony declarations =='
python3 - <<'PY'
import pathlib, re
files = [p for p in pathlib.Path('.').rglob('*') if p.is_file() and (p.name == 'Makefile' or p.suffix == '.mk')]
print('files=', files)
for path in files:
text = path.read_text(errors='replace')
lines = text.splitlines()
print(f'--- {path} ---')
for i,l in enumerate(lines,1):
if re.match(r'^\s*(readme|version-matrix)\s*:(:)?(?:\s|#|$)', l):
print(f'definition {i}: {l}')
if re.match(r'^\s*\.PHONY\s*:', l):
print(f'phony {i}: {l}')
PYRepository: tschm/pycharting
Length of output: 207
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '== Candidate Makefiles =='
git ls-files | rg '(^|/)(Makefile|.*\.mk)$' || true
printf '%s\n' '== .rhiza/rhiza.mk target declarations/lines 45-150 =='
sed -n '45,155p' .rhiza/rhiza.mk
printf '%s\n' '== All readme/version-matrix references =='
rg -n --glob 'Makefile' --iglob '*.mk' --iglob '*.yml' --iglob '*.yaml' --iglob '*.md' 'readme|version-matrix' . || true
printf '%s\n' '== Makefile parsing probe: definitions and .PHONY declarations =='
python3 - <<'PY'
import pathlib, re
files = []
for p in pathlib.Path('.').rglob('*'):
if p.is_file() and (p.name == 'Makefile' or p.suffix == '.mk'):
files.append(p)
print('files=', files)
for path in files:
text = path.read_text(errors='replace')
for i, line in enumerate(text.splitlines(), 1):
if re.match(r'^\s*(readme|version-matrix)\s*:(:)?(?:\s|#|$)', line):
print(f'definition {path}:{i}: {line}')
if re.match(r'^\s*\.PHONY\s*:', line):
print(f'phony {path}:{i}: {line}')
PYRepository: tschm/pycharting
Length of output: 6471
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '%s\n' '== Root Makefile =='
sed -n '1,220p' Makefile
printf '%s\n' '== README target references in workflow files =='
rg -n --iglob '*.yml' --iglob '*.yaml' 'readme|version-matrix|\.rhiza/tests/sync/test_readme_validation.py' .github .rhiza || true
printf '%s\n' '== Readme validation test file =='
sed -n '1,260p' .rhiza/tests/sync/test_readme_validation.py
printf '%s\n' '== Target definition text search in relevant files =='
python3 - <<'PY'
import pathlib, re
targets = ["readme", "version-matrix"]
files = [
pathlib.Path("Makefile"),
pathlib.Path(".rhiza/rhiza.mk"),
] + sorted(pathlib.Path(".rhiza/make.d").glob("*.mk"))
for path in files:
text = path.read_text(errors="replace")
hits = []
for i, line in enumerate(text.splitlines(), 1):
if re.search(r"(^|\s)(" + "|".join(re.escape(t) for t in targets) + r")\b", line):
hits.append((i, line))
if hits:
print(f"--- {path} ---")
for i, line in hits:
print(f"{i}: {line}")
PYRepository: tschm/pycharting
Length of output: 8960
Remove the orphaned phony declarations for readme and version-matrix.
.rhiza/rhiza.mk still declares these targets in .PHONY, but no included fragment defines them or calls them. make readme and make version-matrix will complete as no-op targets; remove the declarations unless the targets are restored.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.rhiza/rhiza.mk around lines 128 - 146, Remove the orphaned readme and
version-matrix entries from the relevant .PHONY declaration in .rhiza/rhiza.mk,
leaving all currently defined targets such as print-logo, help, and ci-os-matrix
unchanged.
| import pytest | ||
| from packaging.version import Version | ||
|
|
||
| _SEMVER_RE = re.compile(r"^\d+\.\d+\.\d+") |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Reject invalid version suffixes.
_SEMVER_RE.match() accepts values such as 1.2.3invalid. This lets an invalid project version pass the semantic-version gate. Anchor the full version pattern and use fullmatch().
Proposed fix
-_SEMVER_RE = re.compile(r"^\d+\.\d+\.\d+")
+_SEMVER_RE = re.compile(r"\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?")
...
- assert _SEMVER_RE.match(str(version)), (
+ assert _SEMVER_RE.fullmatch(str(version)), (Also applies to: 115-120
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.rhiza/tests/test_pyproject.py at line 31, Update the _SEMVER_RE validation
and its usage in test_pyproject.py to require the entire version string to match
the semantic-version pattern, using fullmatch() and anchoring the pattern so
values like 1.2.3invalid are rejected.
| # Trust boundary: we execute Python snippets sourced from README.md in this repo. | ||
| # The README is part of the trusted repository content and reviewed in PRs. | ||
| logger.debug("Executing README code via %s -c ...", sys.executable) | ||
| result = subprocess.run([sys.executable, "-c", code], capture_output=True, text=True, cwd=root) # nosec | ||
|
|
||
| stdout = result.stdout | ||
| logger.debug("Execution finished with return code %d", result.returncode) | ||
| if result.stderr: | ||
| logger.debug("Stderr from README code:\n%s", result.stderr) | ||
| logger.debug("Stdout from README code:\n%s", stdout) | ||
|
|
||
| assert result.returncode == 0, f"README code exited with {result.returncode}. Stderr:\n{result.stderr}" | ||
| logger.info("README code executed successfully; comparing output to expected result") | ||
| assert stdout.strip() == expected.strip() |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Set a timeout for README example execution.
If a non-skipped README fence blocks or loops, subprocess.run() waits indefinitely. Set a child-process timeout and report TimeoutExpired as a failed example.
Proposed fix
- result = subprocess.run([sys.executable, "-c", code], capture_output=True, text=True, cwd=root) # nosec
+ try:
+ result = subprocess.run(
+ [sys.executable, "-c", code],
+ capture_output=True,
+ text=True,
+ cwd=root,
+ timeout=30,
+ ) # nosec
+ except subprocess.TimeoutExpired:
+ pytest.fail("README code exceeded the execution timeout")📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| # Trust boundary: we execute Python snippets sourced from README.md in this repo. | |
| # The README is part of the trusted repository content and reviewed in PRs. | |
| logger.debug("Executing README code via %s -c ...", sys.executable) | |
| result = subprocess.run([sys.executable, "-c", code], capture_output=True, text=True, cwd=root) # nosec | |
| stdout = result.stdout | |
| logger.debug("Execution finished with return code %d", result.returncode) | |
| if result.stderr: | |
| logger.debug("Stderr from README code:\n%s", result.stderr) | |
| logger.debug("Stdout from README code:\n%s", stdout) | |
| assert result.returncode == 0, f"README code exited with {result.returncode}. Stderr:\n{result.stderr}" | |
| logger.info("README code executed successfully; comparing output to expected result") | |
| assert stdout.strip() == expected.strip() | |
| # Trust boundary: we execute Python snippets sourced from README.md in this repo. | |
| # The README is part of the trusted repository content and reviewed in PRs. | |
| logger.debug("Executing README code via %s -c ...", sys.executable) | |
| try: | |
| result = subprocess.run( | |
| [sys.executable, "-c", code], | |
| capture_output=True, | |
| text=True, | |
| cwd=root, | |
| timeout=30, | |
| ) # nosec | |
| except subprocess.TimeoutExpired: | |
| pytest.fail("README code exceeded the execution timeout") | |
| stdout = result.stdout | |
| logger.debug("Execution finished with return code %d", result.returncode) | |
| if result.stderr: | |
| logger.debug("Stderr from README code:\n%s", result.stderr) | |
| logger.debug("Stdout from README code:\n%s", stdout) | |
| assert result.returncode == 0, f"README code exited with {result.returncode}. Stderr:\n{result.stderr}" | |
| logger.info("README code executed successfully; comparing output to expected result") | |
| assert stdout.strip() == expected.strip() |
🧰 Tools
🪛 ast-grep (0.45.0)
[error] 68-68: Command coming from incoming request
Context: subprocess.run([sys.executable, "-c", code], capture_output=True, text=True, cwd=root)
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').
(subprocess-from-request)
🪛 Ruff (0.16.1)
[error] 69-69: subprocess call: check for execution of untrusted input
(S603)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.rhiza/tests/test_readme_validation.py around lines 66 - 79, Update the
README example execution around subprocess.run in the test to enforce a finite
timeout, and catch subprocess.TimeoutExpired so the example is reported as a
failed validation rather than hanging indefinitely. Preserve the existing
stdout, stderr, return-code, and expected-output assertions for processes that
complete normally.
| ```bash | ||
| uv run pytest -o log_cli=true --log-cli-level=DEBUG | ||
| ``` |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Use the configured tilde fence style.
Line 80 starts a backtick fence. Markdownlint reports MD048 because this repository requires tilde fences. Replace this fence pair with ~~~.
🧰 Tools
🪛 markdownlint-cli2 (0.23.2)
[warning] 80-80: Code fence style
Expected: tilde; Actual: backtick
(MD048, code-fence-style)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/development/TESTS.md` around lines 80 - 82, Update the pytest command
code block in TESTS.md by replacing its opening and closing backtick fences with
tilde fences, preserving the existing bash content unchanged.
Source: Linters/SAST tools
Template:
jebel-quant/rhiza—v0.18.8→v1.3.2This is a major-version bump (0.x → 1.x), so the diff is much larger than a
routine sync: 48 template-owned files committed (30 modified, 18 added).
Conflicts
7 workflow files conflicted and were resolved by taking the upstream (template) side:
rhiza_benchmark.yml,rhiza_book.yml,rhiza_ci.yml,rhiza_codeql.yml,rhiza_marimo.yml,rhiza_release.yml(2 blocks),rhiza_weekly.yml.The v1.x template dropped the whole
.rhiza/tests/{api,integration,structure,sync, stress,utils}/,.rhiza/requirements/and.rhiza/utils/layout, plus.github/workflows/rhiza_sync.yml,.rhiza/make.d/releasing.mk,.rhiza/.rhiza-versionand.rhiza/.cfg.toml.The sync removed them from the working tree, but they are not in the new
template.lockfile list, sostage_synced.pydeliberately left those deletionsunstaged —
/rhiza:updateonly commits paths the template owns. They are thereforestill present on this branch. Full list:
They almost certainly should be removed as part of this migration — but that's a
deliberate call, so it needs a separate commit rather than being folded in here.
Note
profiles:is still justgithub-project; the other five repos also carrytemplates: [legal]. Worth considering separately — a version bump shouldn't change it.No gates were run — run
/rhiza:qualityfor a scorecard.Summary by CodeRabbit