Merge branch 'feat/branding-toolkit'
Split branding into check-branding.sh and brand-plugin.sh over a shared branding.conf, and give the checker positive assertions as well as absence.
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
nih-plug-template
|
Plugin Name
|
||||||
Copyright 2026 Mikkeli Matlock
|
Copyright 2026 Mikkeli Matlock
|
||||||
|
|
||||||
This product is licensed under the GNU General Public License v3.0 or later.
|
This product is licensed under the GNU General Public License v3.0 or later.
|
||||||
|
|||||||
@@ -20,31 +20,45 @@ sh check-branding.sh
|
|||||||
|
|
||||||
## Renaming
|
## Renaming
|
||||||
|
|
||||||
Branding is duplicated across `Cargo.toml`, `bundler.toml`, `src/lib.rs` and the docs,
|
Identity is duplicated across `Cargo.toml`, `bundler.toml`, `src/lib.rs`, `NOTICE`,
|
||||||
and nothing makes those agree — so a half-finished rename builds a bundle under one name
|
`Cargo.lock`, the CI workflow and the docs. Nothing makes those agree, so a half-finished
|
||||||
while the docs and deploy script look for another.
|
rename builds a bundle under one name while CI bundles another.
|
||||||
|
|
||||||
`check-branding.sh` declares the identity once at the top and asserts every copy matches:
|
`branding.conf` holds the canonical identity once. Two scripts read it:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sh check-branding.sh # exit 0 = consistent, 1 = drift found
|
sh brand-plugin.sh --display "Fuzz Box" # derive, validate, show the plan (dry run)
|
||||||
|
sh brand-plugin.sh --display "Fuzz Box" --apply
|
||||||
|
sh check-branding.sh # exit 0 = consistent, 1 = drift
|
||||||
```
|
```
|
||||||
|
|
||||||
It covers the identity a host actually sees (package and bundle names, `Plugin::NAME`,
|
**`brand-plugin.sh` changes names.** It derives the package name and Rust types from the
|
||||||
`VENDOR`, `EMAIL`, `CLAP_ID`, `VST3_CLASS_ID`, the descriptions) and the Rust type names
|
display name, validates them against what Cargo actually accepts, rewrites every tracked
|
||||||
(`MyPlugin`, `PluginParams`) at every site they appear. Renaming the types is cosmetic —
|
file, appends the retired spellings to `STALE`, and finishes by running the checker — a run
|
||||||
no host ever sees them — but leaving `MyPlugin` in a shipped plugin is the kind of residue
|
that ends green is a rename that landed. It is a dry run unless you pass `--apply`.
|
||||||
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
|
**`check-branding.sh` verifies them.** Both directions: every canonical value is asserted
|
||||||
it reports. The placeholder spellings are pre-seeded into its `STALE` list, and each one
|
present where it belongs, *and* no retired spelling survives anywhere. Absence alone is not
|
||||||
arms itself as soon as you change the matching canonical value — so leftovers surface
|
enough — renaming to the wrong new spelling would pass a stale-sweep and still break CI.
|
||||||
without you having to track them. It also asserts `VST3_CLASS_ID` is exactly 16 bytes and
|
|
||||||
that `CLAP_ID` is reverse-DNS safe, both of which are otherwise a compile error or a
|
|
||||||
silent identity change between machines.
|
|
||||||
|
|
||||||
Checks for files the template doesn't ship (e.g. a `deploy.ps1`) report as `skip` and
|
Three things it will refuse or warn about, all learned the hard way:
|
||||||
start enforcing once you add them.
|
|
||||||
|
- **A package name Cargo rejects.** Names cannot start with a digit (`206 Prototype` →
|
||||||
|
`206_prototype`, rejected), be a Rust keyword, or collide with a built-in crate.
|
||||||
|
- **Moving frozen identity.** `CLAP_ID` and `VST3_CLASS_ID` are how hosts recognise saved
|
||||||
|
projects and presets. Change either and every existing session silently loses the plugin —
|
||||||
|
the track comes up empty, with no error. Pick them once before the first release; after
|
||||||
|
that they never move. `--allow-identity-change` is required to touch them, and the helper
|
||||||
|
proposes a valid 16-byte class ID rather than deriving one, because that choice is yours.
|
||||||
|
- **Development history in `CLAP_DESCRIPTION`.** Hosts show it to users who never knew your
|
||||||
|
codename. Any retired name or the repo's own codename appearing there is rejected.
|
||||||
|
|
||||||
|
The repo name is deliberately *not* derived. It is fixed when the repo is created and keeps
|
||||||
|
its development codename while the plugin gets a product name; `CODENAME` in `branding.conf`
|
||||||
|
records it so it can be kept out of user-facing text.
|
||||||
|
|
||||||
|
Checks for files the template doesn't ship (a `deploy.ps1`, say) report as `skip` and start
|
||||||
|
enforcing once you add them.
|
||||||
|
|
||||||
## Build
|
## Build
|
||||||
|
|
||||||
@@ -105,7 +119,9 @@ bundler.toml # Bundle name for `cargo xtask bundle`
|
|||||||
src/lib.rs # Plugin skeleton — params, DSP, and egui editor, with one example gain param
|
src/lib.rs # Plugin skeleton — params, DSP, and egui editor, with one example gain param
|
||||||
xtask/ # Bundler xtask (cargo xtask bundle)
|
xtask/ # Bundler xtask (cargo xtask bundle)
|
||||||
.cargo/config.toml # xtask alias
|
.cargo/config.toml # xtask alias
|
||||||
check-branding.sh # Asserts the plugin's identity strings agree everywhere (see Renaming)
|
branding.conf # Canonical identity — the one place names are declared
|
||||||
|
check-branding.sh # Asserts every name is present where it belongs and no old one survives
|
||||||
|
brand-plugin.sh # Renames the plugin everywhere, then verifies with the checker
|
||||||
.gitattributes # Pins *.sh to LF so Windows checkouts keep a valid shebang
|
.gitattributes # Pins *.sh to LF so Windows checkouts keep a valid shebang
|
||||||
.github/workflows/ # CI: branding check, both feature sets build, test, bundle
|
.github/workflows/ # CI: branding check, both feature sets build, test, bundle
|
||||||
```
|
```
|
||||||
|
|||||||
Executable
+223
@@ -0,0 +1,223 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
# brand-plugin.sh - rename this plugin, everywhere, in one go.
|
||||||
|
#
|
||||||
|
# check-branding.sh tells you what disagrees. This changes it. They share
|
||||||
|
# branding.conf: this script rewrites that file and every copy of the old values
|
||||||
|
# in tracked files, then runs the checker and reports what it says. A run that
|
||||||
|
# ends with the checker green is a rename that landed.
|
||||||
|
#
|
||||||
|
# Dry run by default:
|
||||||
|
# sh brand-plugin.sh --display "Bari 206"
|
||||||
|
# sh brand-plugin.sh --display "Bari 206" --vendor Novoyuuparosk --apply
|
||||||
|
#
|
||||||
|
# Options:
|
||||||
|
# --display NAME the product name, with spaces and capitals. Everything
|
||||||
|
# else that can be derived, is derived from this.
|
||||||
|
# --pkg NAME override the derived Cargo package name.
|
||||||
|
# --vendor NAME --email ADDR --homepage URL
|
||||||
|
# --description D --clap-description D
|
||||||
|
# --struct NAME override the derived Rust type name.
|
||||||
|
# --allow-identity-change permit CLAP_ID / VST3_CLASS_ID to move. See below.
|
||||||
|
# --apply actually write. Without it, nothing is modified.
|
||||||
|
|
||||||
|
set -u
|
||||||
|
cd "$(dirname "$0")" || exit 1
|
||||||
|
[ -f branding.conf ] || { printf 'branding.conf not found next to this script\n' >&2; exit 1; }
|
||||||
|
# shellcheck disable=SC1091
|
||||||
|
. ./branding.conf
|
||||||
|
|
||||||
|
OLD_PKG="$PKG"; OLD_DISPLAY="$DISPLAY"; OLD_PLUGIN_NAME="$PLUGIN_NAME"
|
||||||
|
OLD_VENDOR="$VENDOR"; OLD_EMAIL="$EMAIL"; OLD_HOMEPAGE="$HOMEPAGE"
|
||||||
|
OLD_DESCRIPTION="$DESCRIPTION"; OLD_CLAP_DESCRIPTION="$CLAP_DESCRIPTION"
|
||||||
|
OLD_CLAP_ID="$CLAP_ID"; OLD_VST3="$VST3_CLASS_ID"
|
||||||
|
OLD_STRUCT="$STRUCT"; OLD_PARAMS_STRUCT="$PARAMS_STRUCT"
|
||||||
|
|
||||||
|
new_display=""; new_pkg=""; new_vendor=""; new_email=""; new_homepage=""
|
||||||
|
new_description=""; new_clap_description=""; new_struct=""
|
||||||
|
new_clap_id=""; new_vst3=""; apply=0; allow_identity=0
|
||||||
|
|
||||||
|
while [ $# -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
--display) new_display="${2:?}"; shift 2 ;;
|
||||||
|
--pkg) new_pkg="${2:?}"; shift 2 ;;
|
||||||
|
--vendor) new_vendor="${2:?}"; shift 2 ;;
|
||||||
|
--email) new_email="${2:?}"; shift 2 ;;
|
||||||
|
--homepage) new_homepage="${2:?}"; shift 2 ;;
|
||||||
|
--description) new_description="${2:?}"; shift 2 ;;
|
||||||
|
--clap-description) new_clap_description="${2:?}"; shift 2 ;;
|
||||||
|
--struct) new_struct="${2:?}"; shift 2 ;;
|
||||||
|
--clap-id) new_clap_id="${2:?}"; shift 2 ;;
|
||||||
|
--vst3-class-id) new_vst3="${2:?}"; shift 2 ;;
|
||||||
|
--allow-identity-change) allow_identity=1; shift ;;
|
||||||
|
--apply) apply=1; shift ;;
|
||||||
|
-h|--help) sed -n '3,25p' "$0"; exit 0 ;;
|
||||||
|
*) printf 'unknown option: %s\n' "$1" >&2; exit 2 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
[ -n "$new_display" ] || { printf 'nothing to do: pass --display "New Name"\n' >&2; exit 2; }
|
||||||
|
|
||||||
|
# --- derivation -------------------------------------------------------------
|
||||||
|
# Package name: lowercase, every run of non-alphanumerics becomes one separator,
|
||||||
|
# ends trimmed. Cargo accepts both hyphens and underscores in a package name and
|
||||||
|
# converts to underscores for the built artifact, which nih_plug_xtask already
|
||||||
|
# accounts for; underscores are used here to match the Rust convention.
|
||||||
|
derive_pkg() {
|
||||||
|
printf '%s' "$1" | tr '[:upper:]' '[:lower:]' \
|
||||||
|
| sed -e 's/[^a-z0-9]\{1,\}/_/g' -e 's/^_//' -e 's/_$//'
|
||||||
|
}
|
||||||
|
# Rust type name: CamelCase, non-alphanumerics dropped entirely.
|
||||||
|
derive_struct() {
|
||||||
|
printf '%s' "$1" | sed -e 's/[^A-Za-z0-9]\{1,\}/ /g' \
|
||||||
|
| awk '{for(i=1;i<=NF;i++) printf "%s%s", toupper(substr($i,1,1)), substr($i,2); print ""}'
|
||||||
|
}
|
||||||
|
[ -n "$new_pkg" ] || new_pkg=$(derive_pkg "$new_display")
|
||||||
|
[ -n "$new_struct" ] || new_struct=$(derive_struct "$new_display")
|
||||||
|
new_params_struct="${new_struct}Params"
|
||||||
|
[ -n "$new_vendor" ] || new_vendor="$OLD_VENDOR"
|
||||||
|
[ -n "$new_email" ] || new_email="$OLD_EMAIL"
|
||||||
|
[ -n "$new_homepage" ] || new_homepage="$OLD_HOMEPAGE"
|
||||||
|
[ -n "$new_description" ] || new_description="$OLD_DESCRIPTION"
|
||||||
|
[ -n "$new_clap_description" ] || new_clap_description="$OLD_CLAP_DESCRIPTION"
|
||||||
|
|
||||||
|
# --- validation -------------------------------------------------------------
|
||||||
|
fail=0
|
||||||
|
die() { printf ' INVALID %s\n' "$1"; fail=$((fail+1)); }
|
||||||
|
|
||||||
|
case "$new_pkg" in
|
||||||
|
"") die "derived package name is empty" ;;
|
||||||
|
[0-9]*) die "package name \"$new_pkg\" starts with a digit - cargo rejects this outright" ;;
|
||||||
|
esac
|
||||||
|
case "$new_pkg" in
|
||||||
|
test|core|std|alloc|proc_macro)
|
||||||
|
die "package name \"$new_pkg\" collides with a built-in crate" ;;
|
||||||
|
as|break|const|continue|crate|dyn|else|enum|extern|false|fn|for|if|impl|in|let|loop|match|mod|move|mut|pub|ref|return|self|static|struct|super|trait|true|type|unsafe|use|where|while|async|await|box|do|final|macro|override|priv|typeof|unsized|virtual|yield|try)
|
||||||
|
die "package name \"$new_pkg\" is a Rust keyword - cargo rejects this" ;;
|
||||||
|
esac
|
||||||
|
# bundler.toml uses the package name as a TOML bare key.
|
||||||
|
case "$new_pkg" in *[!a-zA-Z0-9_-]*) die "package name \"$new_pkg\" is not a valid TOML bare key" ;; esac
|
||||||
|
case "$new_struct" in ""|[0-9]*) die "derived type name \"$new_struct\" is not a valid Rust identifier" ;; esac
|
||||||
|
|
||||||
|
# --- frozen identity --------------------------------------------------------
|
||||||
|
identity_note=""
|
||||||
|
if [ -n "$new_clap_id" ] || [ -n "$new_vst3" ]; then
|
||||||
|
if [ "$allow_identity" -eq 0 ]; then
|
||||||
|
die "CLAP_ID / VST3_CLASS_ID are frozen identity; pass --allow-identity-change to move them"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
[ -n "$new_clap_id" ] || new_clap_id="$OLD_CLAP_ID"
|
||||||
|
[ -n "$new_vst3" ] || new_vst3="$OLD_VST3"
|
||||||
|
|
||||||
|
# VST3_CLASS_ID is a fixed 16-byte array, so it cannot simply be derived. Propose
|
||||||
|
# one from the new name and let a human decide - it is frozen the moment it ships.
|
||||||
|
proposed_vst3=$(printf '%s' "${new_struct}Plugin000000000000" | cut -c1-16)
|
||||||
|
n=$(printf '%s' "$new_vst3" | wc -c | tr -d ' ')
|
||||||
|
[ "$n" -eq 16 ] || die "VST3_CLASS_ID \"$new_vst3\" is $n bytes, must be exactly 16"
|
||||||
|
|
||||||
|
case "$new_clap_id" in
|
||||||
|
*\ *|*[A-Z]*) die "CLAP_ID \"$new_clap_id\" must be lowercase with no spaces" ;;
|
||||||
|
*.*) ;;
|
||||||
|
*) die "CLAP_ID \"$new_clap_id\" should be dotted reverse-DNS" ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# CLAP_DESCRIPTION is user-facing: keep development history out of it.
|
||||||
|
for hist in "$CODENAME" "$OLD_DISPLAY" "$OLD_PKG"; do
|
||||||
|
case "$new_clap_description" in
|
||||||
|
*"$hist"*) die "CLAP_DESCRIPTION still mentions \"$hist\" - it is shown to users, who never knew the development name" ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# --- plan -------------------------------------------------------------------
|
||||||
|
printf 'rename plan\n\n'
|
||||||
|
row() { [ "$2" = "$3" ] && printf ' %-18s %s (unchanged)\n' "$1" "$2" || printf ' %-18s %s -> %s\n' "$1" "$2" "$3"; }
|
||||||
|
row "package" "$OLD_PKG" "$new_pkg"
|
||||||
|
row "display" "$OLD_DISPLAY" "$new_display"
|
||||||
|
row "Plugin::NAME" "$OLD_PLUGIN_NAME" "$new_display"
|
||||||
|
row "vendor" "$OLD_VENDOR" "$new_vendor"
|
||||||
|
row "email" "$OLD_EMAIL" "$new_email"
|
||||||
|
row "homepage" "$OLD_HOMEPAGE" "$new_homepage"
|
||||||
|
row "description" "$OLD_DESCRIPTION" "$new_description"
|
||||||
|
row "clap desc" "$OLD_CLAP_DESCRIPTION" "$new_clap_description"
|
||||||
|
row "struct" "$OLD_STRUCT" "$new_struct"
|
||||||
|
row "params struct" "$OLD_PARAMS_STRUCT" "$new_params_struct"
|
||||||
|
row "CLAP_ID" "$OLD_CLAP_ID" "$new_clap_id"
|
||||||
|
row "VST3_CLASS_ID" "$OLD_VST3" "$new_vst3"
|
||||||
|
printf '\n repo codename stays %s - fixed at creation, never renamed\n' "$CODENAME"
|
||||||
|
if [ "$new_vst3" = "$OLD_VST3" ]; then
|
||||||
|
printf ' suggested VST3_CLASS_ID for this name (16 bytes): "%s"\n' "$proposed_vst3"
|
||||||
|
printf ' apply with --vst3-class-id "%s" --allow-identity-change, and only before first release\n' "$proposed_vst3"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$fail" -ne 0 ]; then
|
||||||
|
printf '\n%d problem(s); nothing written.\n' "$fail"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$apply" -eq 0 ]; then
|
||||||
|
printf '\nDry run. Re-run with --apply to write these changes.\n'
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# --- apply ------------------------------------------------------------------
|
||||||
|
# Longest values first: a shorter spelling can be a substring of a longer one
|
||||||
|
# (the package name inside a URL, say), and replacing it first would corrupt the
|
||||||
|
# longer match. branding.conf is excluded and rewritten wholesale below, so its
|
||||||
|
# STALE list is never rewritten into meaninglessness.
|
||||||
|
subst_file="$(mktemp)"
|
||||||
|
trap 'rm -f "$subst_file"' EXIT
|
||||||
|
{
|
||||||
|
printf '%s\t%s\n' "$OLD_CLAP_ID" "$new_clap_id"
|
||||||
|
printf '%s\t%s\n' "$OLD_HOMEPAGE" "$new_homepage"
|
||||||
|
printf '%s\t%s\n' "$OLD_CLAP_DESCRIPTION" "$new_clap_description"
|
||||||
|
printf '%s\t%s\n' "$OLD_DESCRIPTION" "$new_description"
|
||||||
|
printf '%s\t%s\n' "$OLD_PARAMS_STRUCT" "$new_params_struct"
|
||||||
|
printf '%s\t%s\n' "$OLD_VST3" "$new_vst3"
|
||||||
|
printf '%s\t%s\n' "$OLD_PLUGIN_NAME" "$new_display"
|
||||||
|
printf '%s\t%s\n' "$OLD_DISPLAY" "$new_display"
|
||||||
|
printf '%s\t%s\n' "$OLD_STRUCT" "$new_struct"
|
||||||
|
printf '%s\t%s\n' "$OLD_EMAIL" "$new_email"
|
||||||
|
printf '%s\t%s\n' "$OLD_VENDOR" "$new_vendor"
|
||||||
|
printf '%s\t%s\n' "$OLD_PKG" "$new_pkg"
|
||||||
|
} | awk -F'\t' '$1 != $2 { print length($1)"\t"$0 }' | sort -rn | cut -f2- > "$subst_file"
|
||||||
|
|
||||||
|
changed=0
|
||||||
|
git ls-files 2>/dev/null | while IFS= read -r f; do
|
||||||
|
[ -f "$f" ] || continue
|
||||||
|
case "$f" in branding.conf) continue ;; esac
|
||||||
|
before=$(cat "$f")
|
||||||
|
after="$before"
|
||||||
|
while IFS="$(printf '\t')" read -r old new; do
|
||||||
|
[ -n "$old" ] || continue
|
||||||
|
after=$(printf '%s' "$after" | sed "s|$(printf '%s' "$old" | sed 's/[][\.*^$/|&]/\\&/g')|$(printf '%s' "$new" | sed 's/[\\/&|]/\\&/g')|g")
|
||||||
|
done < "$subst_file"
|
||||||
|
if [ "$before" != "$after" ]; then printf '%s\n' "$after" > "$f"; printf ' rewrote %s\n' "$f"; fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# branding.conf last, with the retired spellings appended to STALE so the next
|
||||||
|
# run of the checker sweeps for anything this pass missed.
|
||||||
|
# Deduplicated: two fields can retire to the same spelling - DISPLAY and
|
||||||
|
# PLUGIN_NAME are commonly identical - and listing it twice makes the checker
|
||||||
|
# report the same drift twice. Note that neither this script nor the checker
|
||||||
|
# contains a branding literal anywhere: both read every name from
|
||||||
|
# branding.conf, so a rename never has to rewrite its own tooling.
|
||||||
|
retired=$(cut -f1 "$subst_file" | awk '!seen[$0]++')
|
||||||
|
sed -e "s|^PKG=.*|PKG=\"$new_pkg\"|" \
|
||||||
|
-e "s|^DISPLAY=.*|DISPLAY=\"$new_display\"|" \
|
||||||
|
-e "s|^PLUGIN_NAME=.*|PLUGIN_NAME=\"$new_display\"|" \
|
||||||
|
-e "s|^VENDOR=.*|VENDOR=\"$new_vendor\"|" \
|
||||||
|
-e "s|^EMAIL=.*|EMAIL=\"$new_email\"|" \
|
||||||
|
-e "s|^HOMEPAGE=.*|HOMEPAGE=\"$new_homepage\"|" \
|
||||||
|
-e "s|^DESCRIPTION=.*|DESCRIPTION=\"$new_description\"|" \
|
||||||
|
-e "s|^CLAP_DESCRIPTION=.*|CLAP_DESCRIPTION=\"$new_clap_description\"|" \
|
||||||
|
-e "s|^CLAP_ID=.*|CLAP_ID=\"$new_clap_id\"|" \
|
||||||
|
-e "s|^VST3_CLASS_ID=.*|VST3_CLASS_ID=\"$new_vst3\"|" \
|
||||||
|
-e "s|^STRUCT=.*|STRUCT=\"$new_struct\"|" \
|
||||||
|
-e "s|^PARAMS_STRUCT=.*|PARAMS_STRUCT=\"$new_params_struct\"|" \
|
||||||
|
branding.conf > branding.conf.new
|
||||||
|
{ sed '$ s/"$//' branding.conf.new; printf '%s"\n' "$retired"; } > branding.conf
|
||||||
|
rm -f branding.conf.new
|
||||||
|
printf ' rewrote branding.conf (retired spellings appended to STALE)\n'
|
||||||
|
|
||||||
|
printf '\nverifying with check-branding.sh\n\n'
|
||||||
|
sh check-branding.sh
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
# branding.conf - the plugin's canonical identity, in one place.
|
||||||
|
#
|
||||||
|
# Sourced by both check-branding.sh (which asserts every copy matches) and
|
||||||
|
# brand-plugin.sh (which rewrites this file and every copy when you rename).
|
||||||
|
# Editing a value here and running the checker tells you every place that still
|
||||||
|
# holds the old one; running the helper changes them for you.
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Names that move when the plugin is renamed
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
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 - the org, not the person
|
||||||
|
EMAIL="your@email.com" # Plugin::EMAIL
|
||||||
|
HOMEPAGE="https://github.com/YOUR-USERNAME/YOUR-PLUGIN-NAME" # Cargo homepage -> Plugin::URL
|
||||||
|
DESCRIPTION="A minimal NIH-plug audio plugin" # Cargo package description
|
||||||
|
|
||||||
|
# What the plugin does *now*. Keep development history out of it - no codename,
|
||||||
|
# no "prototype", no "v2 rewrite". Hosts show this to users, who have no idea
|
||||||
|
# what any of that meant. check-branding.sh enforces the mechanical half of this
|
||||||
|
# by rejecting any retired spelling or the repo codename appearing here.
|
||||||
|
CLAP_DESCRIPTION="Your plugin description"
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Frozen identity - hosts key saved projects and presets off these two. Changing
|
||||||
|
# either orphans every existing session silently: the track comes up empty, with
|
||||||
|
# no error. Choose them once before the first release and never again.
|
||||||
|
# brand-plugin.sh refuses to touch them without --allow-identity-change.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
CLAP_ID="com.your-domain.plugin-name" # reverse-DNS, lowercase, no spaces
|
||||||
|
VST3_CLASS_ID="Unique16CharId!!" # MUST be exactly 16 bytes
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Rust type names. Never reach a host, so renaming them is cosmetic - but
|
||||||
|
# leaving "MyPlugin" in a shipped plugin is residue that says nobody read the
|
||||||
|
# file. Leave them alone and the checks simply stay green.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
STRUCT="MyPlugin" # the Plugin impl target
|
||||||
|
PARAMS_STRUCT="PluginParams" # the Params impl target
|
||||||
|
|
||||||
|
# Where those two are defined. A plugin that outgrows one file points these at
|
||||||
|
# the real locations (e.g. PARAMS_FILE="src/params.rs") - the checker asserts
|
||||||
|
# definitions here, and the STALE sweep catches every use site elsewhere.
|
||||||
|
LIB_FILE="src/lib.rs"
|
||||||
|
PARAMS_FILE="src/lib.rs"
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# The development codename: this repository's own name, fixed when the repo was
|
||||||
|
# created and deliberately NOT renamed when the plugin is. It keeps its history
|
||||||
|
# while the plugin gets a product name. Never checked for as branding - it is
|
||||||
|
# only used to keep it out of user-facing descriptions.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
CODENAME="nih-plug-template"
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Spellings that must no longer appear in tracked files. Seeded with this
|
||||||
|
# template's placeholders and with the current values, so renaming is
|
||||||
|
# self-checking in both directions: an entry is skipped while it is still part
|
||||||
|
# of a canonical value above, and arms the moment it stops being one.
|
||||||
|
# brand-plugin.sh appends retired values here automatically.
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
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"
|
||||||
+76
-72
@@ -14,51 +14,33 @@
|
|||||||
#
|
#
|
||||||
# Usage: sh check-branding.sh (exit 0 = consistent, 1 = drift found)
|
# Usage: sh check-branding.sh (exit 0 = consistent, 1 = drift found)
|
||||||
#
|
#
|
||||||
# ---------------------------------------------------------------------------
|
# The canonical identity lives in branding.conf, shared with brand-plugin.sh.
|
||||||
# CANONICAL IDENTITY - the single source of truth. Edit these to rename, then
|
# Edit it there, then run this to find every copy that still holds the old value.
|
||||||
# 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"
|
|
||||||
|
|
||||||
set -u
|
set -u
|
||||||
cd "$(dirname "$0")" || exit 1
|
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
|
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; }
|
||||||
@@ -87,20 +69,25 @@ 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 "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 table key" bundler.toml "[$PKG]"
|
||||||
check "bundler.toml display name" bundler.toml "name = \"$DISPLAY\""
|
check "bundler.toml display name" bundler.toml "name = \"$DISPLAY\""
|
||||||
|
|
||||||
printf '\nsource identity (src/lib.rs)\n'
|
printf '\nsource identity (src/lib.rs)\n'
|
||||||
check "Plugin::NAME" src/lib.rs "const NAME: &'static str = \"$PLUGIN_NAME\""
|
check "Plugin::NAME" "$LIB_FILE" "const NAME: &'static str = \"$PLUGIN_NAME\""
|
||||||
check "Plugin::VENDOR" src/lib.rs "const VENDOR: &'static str = \"$VENDOR\""
|
check "Plugin::VENDOR" "$LIB_FILE" "const VENDOR: &'static str = \"$VENDOR\""
|
||||||
check "Plugin::EMAIL" src/lib.rs "const EMAIL: &'static str = \"$EMAIL\""
|
check "Plugin::EMAIL" "$LIB_FILE" "const EMAIL: &'static str = \"$EMAIL\""
|
||||||
check "CLAP_ID" src/lib.rs "const CLAP_ID: &'static str = \"$CLAP_ID\""
|
check "CLAP_ID" "$LIB_FILE" "const CLAP_ID: &'static str = \"$CLAP_ID\""
|
||||||
check "CLAP_DESCRIPTION" src/lib.rs "const CLAP_DESCRIPTION: Option<&'static str> = Some(\"$CLAP_DESCRIPTION\")"
|
check "CLAP_DESCRIPTION" "$LIB_FILE" "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" "$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
|
# 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.
|
# 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
|
# 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.
|
# 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
|
# 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,
|
# 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.
|
# and the Vst3Plugin impl sits behind a cfg so a default build never reports it.
|
||||||
check "struct $STRUCT" src/lib.rs "struct $STRUCT {"
|
check "struct $STRUCT" "$LIB_FILE" "struct $STRUCT {"
|
||||||
check "impl Plugin" src/lib.rs "impl Plugin for $STRUCT {"
|
check "impl Plugin" "$LIB_FILE" "impl Plugin for $STRUCT {"
|
||||||
check "impl ClapPlugin" src/lib.rs "impl ClapPlugin for $STRUCT {"
|
check "impl ClapPlugin" "$LIB_FILE" "impl ClapPlugin for $STRUCT {"
|
||||||
check "impl Vst3Plugin" src/lib.rs "impl Vst3Plugin for $STRUCT {"
|
check "impl Vst3Plugin" "$LIB_FILE" "impl Vst3Plugin for $STRUCT {"
|
||||||
check "nih_export_clap!" src/lib.rs "nih_export_clap!($STRUCT);"
|
check "nih_export_clap!" "$LIB_FILE" "nih_export_clap!($STRUCT);"
|
||||||
check "nih_export_vst3!" src/lib.rs "nih_export_vst3!($STRUCT);"
|
check "nih_export_vst3!" "$LIB_FILE" "nih_export_vst3!($STRUCT);"
|
||||||
check "struct $PARAMS_STRUCT" src/lib.rs "struct $PARAMS_STRUCT {"
|
check "struct $PARAMS_STRUCT" "$PARAMS_FILE" "struct $PARAMS_STRUCT {"
|
||||||
check "impl Default for params" src/lib.rs "impl Default for $PARAMS_STRUCT {"
|
check "impl Default for params" "$PARAMS_FILE" "impl Default for $PARAMS_STRUCT {"
|
||||||
check "params field type" src/lib.rs "params: Arc<$PARAMS_STRUCT>,"
|
check "params field type" "$LIB_FILE" "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"
|
||||||
|
|
||||||
|
# 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'
|
printf '\ndeploy script\n'
|
||||||
opt_check "deploy.ps1 default package" deploy.ps1 "\$Package = \"$PKG\""
|
opt_check "deploy.ps1 default package" deploy.ps1 "\$Package = \"$PKG\""
|
||||||
opt_check "deploy.ps1 VST3 bundle" deploy.ps1 "\$vst3Name = \"$DISPLAY.vst3\""
|
opt_check "deploy.ps1 VST3 bundle" deploy.ps1 "\$vst3Name = \"$DISPLAY.vst3\""
|
||||||
@@ -153,19 +168,6 @@ else
|
|||||||
scan_source="find (not a git checkout)"
|
scan_source="find (not a git checkout)"
|
||||||
fi
|
fi
|
||||||
info "scanning tracked files via $scan_source"
|
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
|
# 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.
|
# subshell, and the pass/fail counters it increments would be discarded.
|
||||||
while IFS= read -r s; do
|
while IFS= read -r s; do
|
||||||
@@ -181,9 +183,11 @@ while IFS= read -r s; do
|
|||||||
*"$s"*) note "\"$s\" (still canonical)"; continue ;;
|
*"$s"*) note "\"$s\" (still canonical)"; continue ;;
|
||||||
esac
|
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
|
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)
|
done)
|
||||||
if [ -z "$hits" ]; then ok "no occurrences of \"$s\""
|
if [ -z "$hits" ]; then ok "no occurrences of \"$s\""
|
||||||
else bad "no occurrences of \"$s\"" "still present in: $(echo "$hits" | tr '\n' ' ')"; fi
|
else bad "no occurrences of \"$s\"" "still present in: $(echo "$hits" | tr '\n' ' ')"; fi
|
||||||
|
|||||||
Reference in New Issue
Block a user