feat: check descriptions and Rust type names for branding drift
The checker covered the identity strings a host sees, but not two other kinds of template residue. Descriptions: `Cargo.toml`'s `description` and `CLAP_DESCRIPTION` both shipped as placeholders, and the latter surfaces in some host browsers. Type names: `MyPlugin` and `PluginParams` appear at nine sites between the struct definitions, the three trait impls, the two export macros and the params field. Missing one is a compile error rather than silent drift, so this is a convenience rather than a safety net - but naming the site beats reading rustc, and the `Vst3Plugin` impl sits behind a cfg, so a default build never reports it at all. Renaming the types is optional: leave the canonical values alone and the checks stay green. TODO markers are reported at a new informational level that never counts toward pass/fail. They are legitimate while building, so failing on them would just train you to ignore the run - but a finished plugin should not quietly ship with five of them. Two bugs surfaced while testing this: - The stale scan drew its file list from `git ls-files`, which returns nothing outside a checkout. In an exported tarball every stale spelling was therefore reported absent - the section passed hardest exactly when it could see least. It now falls back to `find` and says which source it used. - `grep -c` prints 0 and exits 1 when there are no matches, so the TODO count's `|| echo 0` appended a second zero and produced an unparseable value once a rename removed the last TODO. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WHt4DykxXaQnyaJfyS8KCn
This commit is contained in:
@@ -30,6 +30,12 @@ while the docs and deploy script look for another.
|
|||||||
sh check-branding.sh # exit 0 = consistent, 1 = drift found
|
sh check-branding.sh # exit 0 = consistent, 1 = drift found
|
||||||
```
|
```
|
||||||
|
|
||||||
|
It covers the identity a host actually sees (package and bundle names, `Plugin::NAME`,
|
||||||
|
`VENDOR`, `EMAIL`, `CLAP_ID`, `VST3_CLASS_ID`, the descriptions) and the Rust type names
|
||||||
|
(`MyPlugin`, `PluginParams`) at every site they appear. Renaming the types is cosmetic —
|
||||||
|
no host ever sees them — but leaving `MyPlugin` in a shipped plugin is the kind of residue
|
||||||
|
that says nobody read the file. Leave them as-is and those checks simply stay green.
|
||||||
|
|
||||||
To rename: edit the canonical block at the top of the script, run it, and fix everything
|
To rename: edit the canonical block at the top of the script, run it, and fix everything
|
||||||
it reports. The placeholder spellings are pre-seeded into its `STALE` list, and each one
|
it reports. The placeholder spellings are pre-seeded into its `STALE` list, and each one
|
||||||
arms itself as soon as you change the matching canonical value — so leftovers surface
|
arms itself as soon as you change the matching canonical value — so leftovers surface
|
||||||
|
|||||||
+61
-1
@@ -26,6 +26,15 @@ EMAIL="your@email.com" # Plugin::EMAIL
|
|||||||
HOMEPAGE="https://github.com/YOUR-USERNAME/YOUR-PLUGIN-NAME" # Cargo homepage -> Plugin::URL
|
HOMEPAGE="https://github.com/YOUR-USERNAME/YOUR-PLUGIN-NAME" # Cargo homepage -> Plugin::URL
|
||||||
CLAP_ID="com.your-domain.plugin-name" # CLAP_ID - stable plugin identity
|
CLAP_ID="com.your-domain.plugin-name" # CLAP_ID - stable plugin identity
|
||||||
VST3_CLASS_ID="Unique16CharId!!" # VST3_CLASS_ID - stable identity, MUST be 16 bytes
|
VST3_CLASS_ID="Unique16CharId!!" # VST3_CLASS_ID - stable identity, MUST be 16 bytes
|
||||||
|
DESCRIPTION="A minimal NIH-plug audio plugin" # Cargo package description
|
||||||
|
CLAP_DESCRIPTION="Your plugin description" # CLAP_DESCRIPTION - shown in some host browsers
|
||||||
|
|
||||||
|
# Rust type names. These never reach a host, so renaming them is cosmetic - but
|
||||||
|
# leaving "MyPlugin" in a shipped plugin is the sort of residue that says nobody
|
||||||
|
# read the file. Change these to make the checks below enforce your own names;
|
||||||
|
# leave them alone and they simply stay green.
|
||||||
|
STRUCT="MyPlugin" # the Plugin impl target
|
||||||
|
PARAMS_STRUCT="PluginParams" # the Params impl target
|
||||||
|
|
||||||
# Spellings that must no longer appear anywhere in tracked files. Seeded with
|
# Spellings that must no longer appear anywhere in tracked files. Seeded with
|
||||||
# this template's own placeholders, so that renaming is self-checking: each entry
|
# this template's own placeholders, so that renaming is self-checking: each entry
|
||||||
@@ -33,6 +42,10 @@ VST3_CLASS_ID="Unique16CharId!!" # VST3_CLASS_ID - stable identity, M
|
|||||||
# live check the moment you change that value. Add your own old names here after
|
# live check the moment you change that value. Add your own old names here after
|
||||||
# a later rename.
|
# a later rename.
|
||||||
STALE="nih_plug_template
|
STALE="nih_plug_template
|
||||||
|
MyPlugin
|
||||||
|
PluginParams
|
||||||
|
A minimal NIH-plug audio plugin
|
||||||
|
Your plugin description
|
||||||
Plugin Name
|
Plugin Name
|
||||||
Your Name
|
Your Name
|
||||||
your@email.com
|
your@email.com
|
||||||
@@ -49,6 +62,8 @@ pass=0; fail=0; skip=0
|
|||||||
ok() { pass=$((pass+1)); printf ' ok %s\n' "$1"; }
|
ok() { pass=$((pass+1)); printf ' ok %s\n' "$1"; }
|
||||||
bad() { fail=$((fail+1)); printf ' FAIL %s\n' "$1"; [ $# -gt 1 ] && printf ' %s\n' "$2"; return 0; }
|
bad() { fail=$((fail+1)); printf ' FAIL %s\n' "$1"; [ $# -gt 1 ] && printf ' %s\n' "$2"; return 0; }
|
||||||
note() { skip=$((skip+1)); printf ' skip %s\n' "$1"; }
|
note() { skip=$((skip+1)); printf ' skip %s\n' "$1"; }
|
||||||
|
# Reported for awareness only - never counted, never fails the run.
|
||||||
|
info() { printf ' -- %s\n' "$1"; }
|
||||||
|
|
||||||
# has FILE PATTERN -> fixed-string search
|
# has FILE PATTERN -> fixed-string search
|
||||||
has() { grep -qF "$2" "$1" 2>/dev/null; }
|
has() { grep -qF "$2" "$1" 2>/dev/null; }
|
||||||
@@ -70,6 +85,7 @@ printf 'branding check (canonical: %s / "%s")\n\n' "$PKG" "$DISPLAY"
|
|||||||
printf 'build manifests\n'
|
printf 'build manifests\n'
|
||||||
check "Cargo.toml package name" Cargo.toml "name = \"$PKG\""
|
check "Cargo.toml package name" Cargo.toml "name = \"$PKG\""
|
||||||
check "Cargo.toml homepage" Cargo.toml "homepage = \"$HOMEPAGE\""
|
check "Cargo.toml homepage" Cargo.toml "homepage = \"$HOMEPAGE\""
|
||||||
|
check "Cargo.toml description" Cargo.toml "description = \"$DESCRIPTION\""
|
||||||
check "bundler.toml table key" bundler.toml "[$PKG]"
|
check "bundler.toml table key" bundler.toml "[$PKG]"
|
||||||
check "bundler.toml display name" bundler.toml "name = \"$DISPLAY\""
|
check "bundler.toml display name" bundler.toml "name = \"$DISPLAY\""
|
||||||
|
|
||||||
@@ -78,6 +94,7 @@ check "Plugin::NAME" src/lib.rs "const NAME: &'static str = \"$PLUGIN_NAME\""
|
|||||||
check "Plugin::VENDOR" src/lib.rs "const VENDOR: &'static str = \"$VENDOR\""
|
check "Plugin::VENDOR" src/lib.rs "const VENDOR: &'static str = \"$VENDOR\""
|
||||||
check "Plugin::EMAIL" src/lib.rs "const EMAIL: &'static str = \"$EMAIL\""
|
check "Plugin::EMAIL" src/lib.rs "const EMAIL: &'static str = \"$EMAIL\""
|
||||||
check "CLAP_ID" src/lib.rs "const CLAP_ID: &'static str = \"$CLAP_ID\""
|
check "CLAP_ID" src/lib.rs "const CLAP_ID: &'static str = \"$CLAP_ID\""
|
||||||
|
check "CLAP_DESCRIPTION" src/lib.rs "const CLAP_DESCRIPTION: Option<&'static str> = Some(\"$CLAP_DESCRIPTION\")"
|
||||||
check "VST3_CLASS_ID" src/lib.rs "const VST3_CLASS_ID: [u8; 16] = *b\"$VST3_CLASS_ID\""
|
check "VST3_CLASS_ID" src/lib.rs "const VST3_CLASS_ID: [u8; 16] = *b\"$VST3_CLASS_ID\""
|
||||||
|
|
||||||
# Plugin::URL is env!("CARGO_PKG_HOMEPAGE"), so the manifest is the only place
|
# Plugin::URL is env!("CARGO_PKG_HOMEPAGE"), so the manifest is the only place
|
||||||
@@ -98,6 +115,20 @@ case "$CLAP_ID" in
|
|||||||
*) bad "CLAP_ID is reverse-DNS safe" "expected dotted reverse-DNS, got: \"$CLAP_ID\"" ;;
|
*) bad "CLAP_ID is reverse-DNS safe" "expected dotted reverse-DNS, got: \"$CLAP_ID\"" ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
printf '\nstructural names (src/lib.rs)\n'
|
||||||
|
# Every site the type names appear. A rename that misses one is a compile error
|
||||||
|
# rather than silent drift, but naming the site is faster than reading rustc,
|
||||||
|
# and the Vst3Plugin impl sits behind a cfg so a default build never reports it.
|
||||||
|
check "struct $STRUCT" src/lib.rs "struct $STRUCT {"
|
||||||
|
check "impl Plugin" src/lib.rs "impl Plugin for $STRUCT {"
|
||||||
|
check "impl ClapPlugin" src/lib.rs "impl ClapPlugin for $STRUCT {"
|
||||||
|
check "impl Vst3Plugin" src/lib.rs "impl Vst3Plugin for $STRUCT {"
|
||||||
|
check "nih_export_clap!" src/lib.rs "nih_export_clap!($STRUCT);"
|
||||||
|
check "nih_export_vst3!" src/lib.rs "nih_export_vst3!($STRUCT);"
|
||||||
|
check "struct $PARAMS_STRUCT" src/lib.rs "struct $PARAMS_STRUCT {"
|
||||||
|
check "impl Default for params" src/lib.rs "impl Default for $PARAMS_STRUCT {"
|
||||||
|
check "params field type" src/lib.rs "params: Arc<$PARAMS_STRUCT>,"
|
||||||
|
|
||||||
printf '\ndocs\n'
|
printf '\ndocs\n'
|
||||||
opt_check "README bundle command" README.md "cargo xtask bundle $PKG"
|
opt_check "README bundle command" README.md "cargo xtask bundle $PKG"
|
||||||
|
|
||||||
@@ -107,8 +138,26 @@ opt_check "deploy.ps1 VST3 bundle" deploy.ps1 "\$vst3Name = \"$DISPLAY.vst3\
|
|||||||
opt_check "deploy.ps1 CLAP bundle" deploy.ps1 "\$clapName = \"$DISPLAY.clap\""
|
opt_check "deploy.ps1 CLAP bundle" deploy.ps1 "\$clapName = \"$DISPLAY.clap\""
|
||||||
|
|
||||||
printf '\nstale spellings\n'
|
printf '\nstale spellings\n'
|
||||||
|
# File list for the scan below. Prefer git, so build output and any vendored
|
||||||
|
# tree stay out of it. Fall back to find when this is not a checkout - an
|
||||||
|
# exported tarball, or a CI step that fetched without git metadata - because
|
||||||
|
# `git ls-files` there returns nothing, and an empty list would report every
|
||||||
|
# stale spelling as absent. A check that passes hardest when it can see least
|
||||||
|
# is worse than no check.
|
||||||
|
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
||||||
|
file_list() { git ls-files; }
|
||||||
|
scan_source="git"
|
||||||
|
else
|
||||||
|
file_list() { find . -path ./.git -prune -o -path ./target -prune -o -type f -print | sed 's|^\./||'; }
|
||||||
|
scan_source="find (not a git checkout)"
|
||||||
|
fi
|
||||||
|
info "scanning tracked files via $scan_source"
|
||||||
# Every canonical spelling concatenated, for the substring test below.
|
# Every canonical spelling concatenated, for the substring test below.
|
||||||
CANONICAL_BLOB="$PKG
|
CANONICAL_BLOB="$PKG
|
||||||
|
$DESCRIPTION
|
||||||
|
$CLAP_DESCRIPTION
|
||||||
|
$STRUCT
|
||||||
|
$PARAMS_STRUCT
|
||||||
$DISPLAY
|
$DISPLAY
|
||||||
$PLUGIN_NAME
|
$PLUGIN_NAME
|
||||||
$VENDOR
|
$VENDOR
|
||||||
@@ -132,7 +181,7 @@ while IFS= read -r s; do
|
|||||||
esac
|
esac
|
||||||
|
|
||||||
# This script names every placeholder by definition, so it can never be a hit.
|
# This script names every placeholder by definition, so it can never be a hit.
|
||||||
hits=$(git ls-files 2>/dev/null | while read -r f; do
|
hits=$(file_list 2>/dev/null | while read -r f; do
|
||||||
[ "$f" = "check-branding.sh" ] || { [ -f "$f" ] && grep -lF "$s" "$f" 2>/dev/null; }
|
[ "$f" = "check-branding.sh" ] || { [ -f "$f" ] && grep -lF "$s" "$f" 2>/dev/null; }
|
||||||
done)
|
done)
|
||||||
if [ -z "$hits" ]; then ok "no occurrences of \"$s\""
|
if [ -z "$hits" ]; then ok "no occurrences of \"$s\""
|
||||||
@@ -141,6 +190,17 @@ done <<STALE_LIST
|
|||||||
$STALE
|
$STALE
|
||||||
STALE_LIST
|
STALE_LIST
|
||||||
|
|
||||||
|
# Template scaffolding, not drift: TODOs are legitimate while you are still
|
||||||
|
# building. Reported so a finished plugin does not quietly ship with them.
|
||||||
|
# `grep -c` prints 0 AND exits 1 when there are no matches, so `|| echo 0`
|
||||||
|
# would append a second zero and make the count unparseable. Swallow the
|
||||||
|
# status instead, and default an empty result (missing file) to zero.
|
||||||
|
todos=$(grep -c 'TODO' src/lib.rs 2>/dev/null || :)
|
||||||
|
[ -n "$todos" ] || todos=0
|
||||||
|
printf '\nscaffolding\n'
|
||||||
|
if [ "$todos" -eq 0 ]; then ok "no TODO markers left in src/lib.rs"
|
||||||
|
else info "$todos TODO marker(s) still in src/lib.rs (not a failure)"; fi
|
||||||
|
|
||||||
printf '\n%d passed, %d failed' "$pass" "$fail"
|
printf '\n%d passed, %d failed' "$pass" "$fail"
|
||||||
[ "$skip" -gt 0 ] && printf ', %d skipped' "$skip"
|
[ "$skip" -gt 0 ] && printf ', %d skipped' "$skip"
|
||||||
printf '\n'
|
printf '\n'
|
||||||
|
|||||||
Reference in New Issue
Block a user