feat: split branding into a checker and a helper over one canonical file
check-branding.sh could say what disagreed but not fix it, and its canonical block lived in its own header — so renaming meant hand-editing the checker, which is exactly the fragile step it exists to catch. branding.conf now holds the identity once. Both scripts read it and neither contains a branding literal anywhere, so a rename never has to rewrite its own tooling. brand-plugin.sh derives the package name and Rust types from the display name, validates them against what Cargo actually accepts, rewrites every tracked file longest-value-first (a short name can be a substring of a longer one), appends the retired spellings to STALE, and finishes by running the checker. A run that ends green is a rename that landed. Dry run unless --apply. It refuses three things worth refusing: - Package names Cargo rejects — digit-leading (`206 Prototype` derives to `206_prototype`), Rust keywords, built-in crate collisions. Verified against cargo init rather than assumed. - Moving CLAP_ID or VST3_CLASS_ID without --allow-identity-change. Hosts key saved projects off those; changing either orphans every existing session silently. The 16-byte class ID is *proposed*, never derived — it is a fixed-width frozen identity, so the choice stays human. - Development history in CLAP_DESCRIPTION. Hosts show it to users who never knew the codename, so any retired name or the repo codename appearing there is rejected. The checker gains the other half of its job: asserting canonical values are present where they belong, not only that retired ones are gone. Absence alone would pass a rename to the *wrong* new spelling. New positive assertions cover Cargo authors, Cargo.lock, the CI bundle command and NOTICE — every one of which the recent Bari 206 rename had to touch. Structural checks read their file locations from branding.conf so a plugin whose params outgrow src/lib.rs can point them elsewhere. Also fixes NOTICE naming the repo rather than the product, found by the new assertion, and excludes branding.conf from the stale sweep — it lists every retired spelling by definition, the same reason check-branding.sh was already excluded. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WHt4DykxXaQnyaJfyS8KCn
This commit is contained in:
+76
-72
@@ -14,51 +14,33 @@
|
||||
#
|
||||
# Usage: sh check-branding.sh (exit 0 = consistent, 1 = drift found)
|
||||
#
|
||||
# ---------------------------------------------------------------------------
|
||||
# CANONICAL IDENTITY - the single source of truth. Edit these to rename, then
|
||||
# run this script; every failure it prints is a place still holding the old name.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
PKG="nih_plug_template" # Cargo package name == bundler.toml table key
|
||||
DISPLAY="Plugin Name" # bundle name -> "<DISPLAY>.vst3" / "<DISPLAY>.clap"
|
||||
PLUGIN_NAME="Plugin Name" # Plugin::NAME - what the DAW displays
|
||||
VENDOR="Your Name" # Plugin::VENDOR
|
||||
EMAIL="your@email.com" # Plugin::EMAIL
|
||||
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
|
||||
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
|
||||
# this template's own placeholders, so that renaming is self-checking: each entry
|
||||
# is skipped while it is still one of the canonical values above, and becomes a
|
||||
# live check the moment you change that value. Add your own old names here after
|
||||
# a later rename.
|
||||
STALE="nih_plug_template
|
||||
MyPlugin
|
||||
PluginParams
|
||||
A minimal NIH-plug audio plugin
|
||||
Your plugin description
|
||||
Plugin Name
|
||||
Your Name
|
||||
your@email.com
|
||||
com.your-domain.plugin-name
|
||||
Unique16CharId!!
|
||||
YOUR-USERNAME
|
||||
YOUR-PLUGIN-NAME
|
||||
your-domain"
|
||||
# The canonical identity lives in branding.conf, shared with brand-plugin.sh.
|
||||
# Edit it there, then run this to find every copy that still holds the old value.
|
||||
|
||||
set -u
|
||||
cd "$(dirname "$0")" || exit 1
|
||||
|
||||
if [ ! -f branding.conf ]; then
|
||||
printf 'branding.conf not found next to this script\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
# shellcheck disable=SC1091
|
||||
. ./branding.conf
|
||||
|
||||
# Every canonical spelling concatenated, for the substring test below.
|
||||
CANONICAL_BLOB="$PKG
|
||||
$DESCRIPTION
|
||||
$CLAP_DESCRIPTION
|
||||
$STRUCT
|
||||
$PARAMS_STRUCT
|
||||
$DISPLAY
|
||||
$PLUGIN_NAME
|
||||
$VENDOR
|
||||
$EMAIL
|
||||
$HOMEPAGE
|
||||
$CLAP_ID
|
||||
$VST3_CLASS_ID"
|
||||
|
||||
pass=0; fail=0; skip=0
|
||||
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; }
|
||||
@@ -87,20 +69,25 @@ printf 'build manifests\n'
|
||||
check "Cargo.toml package name" Cargo.toml "name = \"$PKG\""
|
||||
check "Cargo.toml homepage" Cargo.toml "homepage = \"$HOMEPAGE\""
|
||||
check "Cargo.toml description" Cargo.toml "description = \"$DESCRIPTION\""
|
||||
check "Cargo.toml authors" Cargo.toml "authors = [\"$VENDOR <$EMAIL>\"]"
|
||||
# Cargo regenerates the lockfile on the next build, but only if someone builds
|
||||
# before pushing. A stale root entry here means CI resolves a package that no
|
||||
# longer exists under that name.
|
||||
opt_check "Cargo.lock package" Cargo.lock "name = \"$PKG\""
|
||||
check "bundler.toml table key" bundler.toml "[$PKG]"
|
||||
check "bundler.toml display name" bundler.toml "name = \"$DISPLAY\""
|
||||
|
||||
printf '\nsource identity (src/lib.rs)\n'
|
||||
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::EMAIL" src/lib.rs "const EMAIL: &'static str = \"$EMAIL\""
|
||||
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 "Plugin::NAME" "$LIB_FILE" "const NAME: &'static str = \"$PLUGIN_NAME\""
|
||||
check "Plugin::VENDOR" "$LIB_FILE" "const VENDOR: &'static str = \"$VENDOR\""
|
||||
check "Plugin::EMAIL" "$LIB_FILE" "const EMAIL: &'static str = \"$EMAIL\""
|
||||
check "CLAP_ID" "$LIB_FILE" "const CLAP_ID: &'static str = \"$CLAP_ID\""
|
||||
check "CLAP_DESCRIPTION" "$LIB_FILE" "const CLAP_DESCRIPTION: Option<&'static str> = Some(\"$CLAP_DESCRIPTION\")"
|
||||
check "VST3_CLASS_ID" "$LIB_FILE" "const VST3_CLASS_ID: [u8; 16] = *b\"$VST3_CLASS_ID\""
|
||||
|
||||
# Plugin::URL is env!("CARGO_PKG_HOMEPAGE"), so the manifest is the only place
|
||||
# it is spelled. Assert the indirection is still in place rather than hardcoded.
|
||||
check "Plugin::URL from manifest" src/lib.rs "const URL: &'static str = env!(\"CARGO_PKG_HOMEPAGE\")"
|
||||
check "Plugin::URL from manifest" "$LIB_FILE" "const URL: &'static str = env!(\"CARGO_PKG_HOMEPAGE\")"
|
||||
|
||||
# VST3 class IDs are a fixed 16-byte array. A shorter or longer literal is a
|
||||
# compile error, but it is far clearer to say so here than to read rustc on it.
|
||||
@@ -120,19 +107,47 @@ 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>,"
|
||||
check "struct $STRUCT" "$LIB_FILE" "struct $STRUCT {"
|
||||
check "impl Plugin" "$LIB_FILE" "impl Plugin for $STRUCT {"
|
||||
check "impl ClapPlugin" "$LIB_FILE" "impl ClapPlugin for $STRUCT {"
|
||||
check "impl Vst3Plugin" "$LIB_FILE" "impl Vst3Plugin for $STRUCT {"
|
||||
check "nih_export_clap!" "$LIB_FILE" "nih_export_clap!($STRUCT);"
|
||||
check "nih_export_vst3!" "$LIB_FILE" "nih_export_vst3!($STRUCT);"
|
||||
check "struct $PARAMS_STRUCT" "$PARAMS_FILE" "struct $PARAMS_STRUCT {"
|
||||
check "impl Default for params" "$PARAMS_FILE" "impl Default for $PARAMS_STRUCT {"
|
||||
check "params field type" "$LIB_FILE" "params: Arc<$PARAMS_STRUCT>,"
|
||||
|
||||
printf '\ndocs\n'
|
||||
opt_check "README bundle command" README.md "cargo xtask bundle $PKG"
|
||||
|
||||
# CLAP_DESCRIPTION is shown to users in a host's plugin browser. It should say
|
||||
# what the plugin does now, with no development history in it. "Free of history"
|
||||
# is a judgement a script cannot make, but the failure it produces is mechanical:
|
||||
# a description still carrying a retired name or the repo's development codename.
|
||||
printf '\ndescription hygiene\n'
|
||||
hygiene_hits=""
|
||||
while IFS= read -r s_entry; do
|
||||
[ -n "$s_entry" ] || continue
|
||||
case "$CANONICAL_BLOB" in *"$s_entry"*) continue ;; esac
|
||||
case "$CLAP_DESCRIPTION" in *"$s_entry"*) hygiene_hits="$hygiene_hits \"$s_entry\"" ;; esac
|
||||
done <<HYGIENE_LIST
|
||||
$STALE
|
||||
$CODENAME
|
||||
HYGIENE_LIST
|
||||
if [ -z "$hygiene_hits" ]; then ok "CLAP_DESCRIPTION carries no retired name or codename"
|
||||
else bad "CLAP_DESCRIPTION carries no retired name or codename" "found:$hygiene_hits"; fi
|
||||
|
||||
printf '\nci\n'
|
||||
# CI bundles by package name, so a rename that misses this file breaks the build
|
||||
# rather than drifting quietly - but it breaks after a full compile, which is a
|
||||
# slow way to learn about a typo. Checking it here costs nothing.
|
||||
opt_check "CI bundle command" .github/workflows/ci.yaml "cargo xtask bundle $PKG"
|
||||
|
||||
printf '\nattribution\n'
|
||||
# NOTICE names the product in its first line; a rename that misses it ships an
|
||||
# artifact crediting a name that no longer exists.
|
||||
opt_check "NOTICE product name" NOTICE "$DISPLAY"
|
||||
|
||||
printf '\ndeploy script\n'
|
||||
opt_check "deploy.ps1 default package" deploy.ps1 "\$Package = \"$PKG\""
|
||||
opt_check "deploy.ps1 VST3 bundle" deploy.ps1 "\$vst3Name = \"$DISPLAY.vst3\""
|
||||
@@ -153,19 +168,6 @@ else
|
||||
scan_source="find (not a git checkout)"
|
||||
fi
|
||||
info "scanning tracked files via $scan_source"
|
||||
# Every canonical spelling concatenated, for the substring test below.
|
||||
CANONICAL_BLOB="$PKG
|
||||
$DESCRIPTION
|
||||
$CLAP_DESCRIPTION
|
||||
$STRUCT
|
||||
$PARAMS_STRUCT
|
||||
$DISPLAY
|
||||
$PLUGIN_NAME
|
||||
$VENDOR
|
||||
$EMAIL
|
||||
$HOMEPAGE
|
||||
$CLAP_ID
|
||||
$VST3_CLASS_ID"
|
||||
# Fed by here-doc rather than a pipe on purpose: a piped `while` runs in a
|
||||
# subshell, and the pass/fail counters it increments would be discarded.
|
||||
while IFS= read -r s; do
|
||||
@@ -181,9 +183,11 @@ while IFS= read -r s; do
|
||||
*"$s"*) note "\"$s\" (still canonical)"; continue ;;
|
||||
esac
|
||||
|
||||
# This script names every placeholder by definition, so it can never be a hit.
|
||||
# These two name every retired spelling by definition - the STALE list lives in
|
||||
# branding.conf and this script reads it - so neither can ever be a real hit.
|
||||
hits=$(file_list 2>/dev/null | while read -r f; do
|
||||
[ "$f" = "check-branding.sh" ] || { [ -f "$f" ] && grep -lF "$s" "$f" 2>/dev/null; }
|
||||
case "$f" in check-branding.sh|branding.conf) continue ;; esac
|
||||
[ -f "$f" ] && grep -lF "$s" "$f" 2>/dev/null
|
||||
done)
|
||||
if [ -z "$hits" ]; then ok "no occurrences of \"$s\""
|
||||
else bad "no occurrences of \"$s\"" "still present in: $(echo "$hits" | tr '\n' ' ')"; fi
|
||||
|
||||
Reference in New Issue
Block a user