From b837a994097b39631353a7e22f9e6496a700c1a7 Mon Sep 17 00:00:00 2001 From: Thomas Schmelzer Date: Tue, 18 Aug 2026 07:26:45 +0400 Subject: [PATCH] chore: refresh the inert config and data files from rhiza v1.3.3 Nothing here executes at build, test or CI time, which is why it is separate from the rest of the template sync: - .gitignore gains entries for artifact directories the newer targets write; - cliff.toml is the git-cliff changelog config the v1.3.3 release flow reads. It is inert until that flow lands, and harmless in the meantime; - .rhiza/completions/* are the bash and zsh completion scripts, data files sourced by the user's shell, never by the build; - docs/index.md drops a stale line. Verified only in the sense that matters for this set: nothing reads any of these during a build or test run. Co-Authored-By: Claude Opus 5 (1M context) --- .gitignore | 6 ++ .rhiza/completions/README.md | 40 +++++++---- .rhiza/completions/rhiza-completion.bash | 44 +++++++++--- .rhiza/completions/rhiza-completion.zsh | 56 +++++++++++---- cliff.toml | 91 ++++++++++++++++++++++++ docs/index.md | 1 - 6 files changed, 202 insertions(+), 36 deletions(-) create mode 100644 cliff.toml diff --git a/.gitignore b/.gitignore index 3c08606..1c9d3f0 100644 --- a/.gitignore +++ b/.gitignore @@ -133,3 +133,9 @@ local.mk .bandit-baseline.json + +# Rust (rust-core bundle). Kept in core rather than the language layer because +# .gitignore has one owner and git opens it with O_NOFOLLOW, so it cannot be a +# per-layer file. These entries are inert in a Python repo. +target/ +**/*.rs.bk diff --git a/.rhiza/completions/README.md b/.rhiza/completions/README.md index b37cb15..ff62b4d 100644 --- a/.rhiza/completions/README.md +++ b/.rhiza/completions/README.md @@ -6,12 +6,27 @@ This directory contains shell completion scripts for Bash and Zsh that provide t - ✅ Tab-complete all available make targets - ✅ Show target descriptions in Zsh -- ✅ Complete common make variables (DRY_RUN, BUMP, ENV, etc.) +- ✅ Complete common make variables (DRY_RUN, ENV, etc.) - ✅ Works with any Rhiza-based project - ✅ Auto-discovers targets from Makefile and included .mk files ## Installation +### Quick install (recommended) + +From the project root: + +```bash +make install-completions # install for both bash and zsh +make install-completions SHELL_KIND=zsh # or just one: bash | zsh | both +``` + +This copies the appropriate script into your user completion directory +(`${XDG_DATA_HOME:-~/.local/share}/bash-completion/completions/make` for bash, +`${XDG_DATA_HOME:-~/.local/share}/zsh/site-functions/_make` for zsh) and prints +any follow-up step. Start a new shell afterwards. The manual methods below remain +available if you prefer to wire it up yourself. + ### Bash #### Method 1: Source in your shell config @@ -102,7 +117,7 @@ make make te # Expands to: make test # Complete variables -make BUMP= # Shows: patch, minor, major +make ENV= # Shows: dev, staging, prod # Works with any target make doc # Shows: docs, docker-build, docker-run, etc. @@ -129,7 +144,6 @@ The completion scripts understand these common variables: | Variable | Values | Description | |----------|--------|-------------| | `DRY_RUN` | `1` | Preview mode without making changes | -| `BUMP` | `patch`, `minor`, `major` | Version bump type | | `ENV` | `dev`, `staging`, `prod` | Target environment | | `COVERAGE_FAIL_UNDER` | (number) | Minimum coverage threshold | | `PYTHON_VERSION` | (version) | Override Python version | @@ -141,10 +155,10 @@ Example usage: make DRY_ # Expands to: make DRY_RUN=1 # Tab-complete variable values -make BUMP= # Shows: patch minor major +make ENV= # Shows: dev staging prod # Combine with targets -make bump BUMP= +make deploy ENV= ``` ## Troubleshooting @@ -244,20 +258,20 @@ m te # Expands to: m test 1. **Target Discovery**: Parses `make -qp` output to find all targets 2. **Description Extraction**: Looks for `##` comments after target names 3. **Variable Detection**: Includes common Makefile variables -4. **Dynamic Completion**: Regenerates list each time you tab +4. **Cached Completion**: The target list is cached per directory and refreshed automatically ### Performance -- Completions are generated on-demand (when you press Tab) -- For large Makefiles (100+ targets), there may be a small delay -- Results are not cached to ensure targets are always current +- The target list is cached under `${XDG_CACHE_HOME:-~/.cache}/rhiza/`, keyed per directory +- The cache refreshes automatically whenever the `Makefile`, `local.mk`, + `.rhiza/rhiza.mk`, or any `.rhiza/make.d/*.mk` file changes +- Only the first Tab press after a makefile change pays the full `make -qp` parsing cost +- To force a refresh manually, delete the cache: `rm -rf "${XDG_CACHE_HOME:-$HOME/.cache}/rhiza"` +- If the cache directory cannot be created (e.g. read-only home), completion + falls back to direct parsing on every Tab press ## See Also - [Tools Reference](../../docs/reference/TOOLS_REFERENCE.md) - Complete command reference - [Quick Reference](../../docs/guides/QUICK_REFERENCE.md) - Quick command reference - [Extending Rhiza](../../docs/guides/EXTENDING_RHIZA.md) - How to add custom targets - ---- - -*Last updated: 2026-02-15* diff --git a/.rhiza/completions/rhiza-completion.bash b/.rhiza/completions/rhiza-completion.bash index 0f860da..eca9e8a 100644 --- a/.rhiza/completions/rhiza-completion.bash +++ b/.rhiza/completions/rhiza-completion.bash @@ -9,8 +9,19 @@ # sudo cp .rhiza/completions/rhiza-completion.bash /etc/bash_completion.d/rhiza # +# Return 0 (stale) when the cache file is missing or any makefile source +# changed since it was written. +_rhiza_make_cache_stale() { + local cache_file="$1" src + [[ -f "$cache_file" ]] || return 0 + for src in Makefile local.mk .rhiza/rhiza.mk .rhiza/make.d/*.mk; do + [[ -f "$src" && "$src" -nt "$cache_file" ]] && return 0 + done + return 1 +} + _rhiza_make_completion() { - local cur prev opts + local cur prev opts cache_dir cache_file COMPREPLY=() cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" @@ -20,15 +31,32 @@ _rhiza_make_completion() { return 0 fi - # Extract make targets from Makefile and all included .mk files - # Looks for lines like: target: ## description - opts=$(make -qp 2>/dev/null | \ - awk -F':' '/^[a-zA-Z0-9][^$#\/\t=]*:([^=]|$)/ {split($1,A,/ /);for(i in A)print A[i]}' | \ - grep -v '^Makefile$' | \ - sort -u) + # Target extraction parses the full make database (make -qp), which is + # slow on large Makefiles - cache the result per directory and refresh + # only when a makefile source changes. + cache_dir="${XDG_CACHE_HOME:-$HOME/.cache}/rhiza" + cache_file="$cache_dir/targets-$(pwd | cksum | cut -d' ' -f1)" + + if _rhiza_make_cache_stale "$cache_file" && mkdir -p "$cache_dir" 2>/dev/null; then + # Extract make targets from Makefile and all included .mk files + make -qp 2>/dev/null | \ + awk -F':' '/^[a-zA-Z0-9][^$#\/\t=]*:([^=]|$)/ {split($1,A,/ /);for(i in A)print A[i]}' | \ + grep -v '^Makefile$' | \ + sort -u > "$cache_file" + fi + + if [[ -r "$cache_file" ]]; then + opts=$(cat "$cache_file") + else + # Cache unavailable (e.g. unwritable HOME): fall back to direct parsing + opts=$(make -qp 2>/dev/null | \ + awk -F':' '/^[a-zA-Z0-9][^$#\/\t=]*:([^=]|$)/ {split($1,A,/ /);for(i in A)print A[i]}' | \ + grep -v '^Makefile$' | \ + sort -u) + fi # Add common make variables that can be overridden - local vars="DRY_RUN=1 BUMP=patch BUMP=minor BUMP=major ENV=dev ENV=staging ENV=prod" + local vars="DRY_RUN=1 ENV=dev ENV=staging ENV=prod" opts="$opts $vars" # Generate completions diff --git a/.rhiza/completions/rhiza-completion.zsh b/.rhiza/completions/rhiza-completion.zsh index 03931c9..f1c3891 100644 --- a/.rhiza/completions/rhiza-completion.zsh +++ b/.rhiza/completions/rhiza-completion.zsh @@ -19,17 +19,34 @@ # sudo cp .rhiza/completions/rhiza-completion.zsh /usr/local/share/zsh/site-functions/_make # +# Return 0 (stale) when the cache file is missing or any makefile source +# changed since it was written. +_rhiza_make_cache_stale() { + local cache_file="$1" src + [[ -f "$cache_file" ]] || return 0 + for src in Makefile local.mk .rhiza/rhiza.mk .rhiza/make.d/*.mk(N); do + [[ -f "$src" && "$src" -nt "$cache_file" ]] && return 0 + done + return 1 +} + _rhiza_make() { local -a targets variables - + local cache_dir cache_file + # Check if we're in a directory with a Makefile if [[ ! -f "Makefile" ]]; then return 0 fi - # Extract make targets with descriptions - # Format: target:description - targets=(${(f)"$( + # Target extraction parses the full make database (make -qp) twice, which + # is slow on large Makefiles - cache both lists per directory and refresh + # only when a makefile source changes. + cache_dir="${XDG_CACHE_HOME:-$HOME/.cache}/rhiza" + cache_file="$cache_dir/targets-$(pwd | cksum | cut -d' ' -f1)" + + if _rhiza_make_cache_stale "$cache_file.desc" && mkdir -p "$cache_dir" 2>/dev/null; then + # Extract make targets with descriptions (format: target:description) make -qp 2>/dev/null | \ awk -F':' ' /^# Files/,/^# Finished Make data base/ { @@ -43,27 +60,38 @@ _rhiza_make() { } ' | \ grep -v '^Makefile:' | \ - sort -u - )"}) + sort -u > "$cache_file.desc" - # Also get targets without descriptions - local -a plain_targets - plain_targets=(${(f)"$( + # Also get targets without descriptions make -qp 2>/dev/null | \ awk -F':' '/^[a-zA-Z0-9_-]+:([^=]|$)/ { split($1,A,/ /) for(i in A) print A[i] }' | \ grep -v '^Makefile$' | \ - sort -u - )"}) + sort -u > "$cache_file.plain" + fi + + local -a plain_targets + if [[ -r "$cache_file.desc" ]]; then + targets=(${(f)"$(cat "$cache_file.desc")"}) + plain_targets=(${(f)"$(cat "$cache_file.plain" 2>/dev/null)"}) + else + # Cache unavailable (e.g. unwritable HOME): fall back to direct parsing + plain_targets=(${(f)"$( + make -qp 2>/dev/null | \ + awk -F':' '/^[a-zA-Z0-9_-]+:([^=]|$)/ { + split($1,A,/ /) + for(i in A) print A[i] + }' | \ + grep -v '^Makefile$' | \ + sort -u + )"}) + fi # Common make variables variables=( 'DRY_RUN=1:preview mode without making changes' - 'BUMP=patch:bump patch version' - 'BUMP=minor:bump minor version' - 'BUMP=major:bump major version' 'ENV=dev:development environment' 'ENV=staging:staging environment' 'ENV=prod:production environment' diff --git a/cliff.toml b/cliff.toml new file mode 100644 index 0000000..6b1e880 --- /dev/null +++ b/cliff.toml @@ -0,0 +1,91 @@ +# cliff.toml — git-cliff configuration for CHANGELOG generation. +# +# Synced from the rhiza `core` bundle. Drives `make changelog`, which runs +# `uvx git-cliff --output CHANGELOG.md`. +# +# This template is intentionally forge-agnostic: it does not hard-code an +# owner/repo. Pull-request and issue references like `(#123)` are left intact +# in the generated entries, and both GitHub and GitLab auto-link bare `#123` +# references when rendering Markdown inside a repository. Downstream projects +# that want richer links (full URLs, contributor handles) can enable +# git-cliff's remote integration — see https://git-cliff.org/docs/integration. +# +# Reference: https://git-cliff.org/docs/configuration + +[changelog] +# A markdown header rendered once at the top of the changelog. +header = """ +# Changelog + +All notable changes to this project are documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com), +and entries are generated from [Conventional Commits](https://www.conventionalcommits.org). + +""" +# The body is a Tera template rendered once per release. +# https://keats.github.io/tera/docs/#introduction +body = """ +{% if version %}\ + ## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }} +{% else %}\ + ## [Unreleased] +{% endif %}\ +{% for group, commits in commits | group_by(attribute="group") %} + ### {{ group | striptags | trim | upper_first }} + {% for commit in commits %}\ + - {% if commit.scope %}*({{ commit.scope }})* {% endif %}\ + {% if commit.breaking %}[**breaking**] {% endif %}\ + {{ commit.message | split(pat="\n") | first | trim | upper_first }} + {% endfor %}\ +{% endfor %}\n +""" +footer = """ + +""" +# Trim leading/trailing whitespace from the rendered body. +trim = true + +[git] +# Parse commits according to the Conventional Commits spec. +conventional_commits = true +# Keep non-conventional commits too, grouped under "Other Changes". +filter_unconventional = false +# Do not split a commit into multiple entries on newlines. +split_commits = false +# Do not drop commits that fail to match a parser below. +filter_commits = false +# Skip merge commits. +filter_merge_commits = true +# Match release tags (v1.2.3, v0.5.1, ...). +tag_pattern = "v[0-9].*" +# Order commits within a section oldest-first. +sort_commits = "oldest" +# Group commits into changelog sections. The leading HTML comment controls the +# section ordering and is stripped from the rendered heading via `striptags`. +commit_parsers = [ + # Drop automated noise commits that don't provide user-facing signal. + { message = ".*\\[skip ci\\].*", skip = true }, + # Only the release flow's own commits. A bare `bump` alternative here also ate every + # `chore(deps): bump ` — the rhiza-hooks v1.2.0 bump (#1487) vanished from + # v1.3.2's notes that way, and had been vanishing for a while unnoticed: a Dependabot + # subject escapes by the accident of its doubled `(deps)(deps)` scope, and #1482's + # identical bump survived only because it was typed `fix(deps):`. So this names + # `release` and the older `bump version` form explicitly rather than `bump` at large. + { message = "^chore(\\([^)]+\\))?:\\s*release\\b", skip = true }, + { message = "^chore(\\([^)]+\\))?:\\s*bump version\\b", skip = true }, + { message = "^chore:\\s*update changelog\\.md\\b", skip = true }, + { message = "^feat", group = "New Features" }, + { message = "^fix", group = "Bug Fixes" }, + { message = "^docs?", group = "Documentation" }, + { message = "^perf", group = "Performance" }, + { message = "^(build|chore)\\(deps[^)]*\\):", group = "Dependencies" }, + { message = "^refactor", group = "Maintenance" }, + { message = "^style", group = "Maintenance" }, + { message = "^chore", group = "Maintenance" }, + { message = "^build", group = "Maintenance" }, + { message = "^ci", group = "Maintenance" }, + { message = "^test", group = "Maintenance" }, + { message = "^revert", group = "Reverts" }, + { message = ".*", group = "Other Changes" }, +] diff --git a/docs/index.md b/docs/index.md index 0e0b6b4..612c7a5 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,2 +1 @@ --8<-- "README.md" -