From 3a144064e35a1a6515d91383d54f992e4d13152f Mon Sep 17 00:00:00 2001 From: Mikkeli Date: Wed, 2 Sep 2026 16:39:12 +0900 Subject: [PATCH] feat: adopt the template's branding toolkit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces the single check-branding.sh with the four-file toolkit from musiikki/nih-plug-template, moved under tools/ alongside where the deploy scripts will eventually live. The two scripts are byte-identical to the template's; everything specific to this plugin lives in tools/branding.conf and tools/branding-sites.tsv. That is the point of the split: syncing an improvement is now a file copy rather than a shell merge, which is what the last two syncs had become. This repo's data differs from the template's in ways that exercise the design rather than fight it: params live in src/params.rs rather than src/lib.rs, deploy.ps1 exists so its three rows check instead of skip, and the CI bundle command carries no --features flag because VST3 is always on here. All of that is table data now, not forked shell. Nothing needed overriding: PKG, STRUCT and PARAMS_STRUCT all derive correctly from PLUGIN_NAME ("Bari 206" -> bari_206 / Bari206 / Bari206Params). AUTHOR is pinned because this plugin separates the person who wrote it from the organisation that vends it — a distinction the template could not express until integrating here surfaced it. Adopting it required two upstream fixes, both made in the template first so the scripts stay identical: cargo AUTHOR as a value distinct from Plugin::VENDOR, and site patterns narrowed to the smallest distinctive fragment after this repo's rustfmt-wrapped CLAP_DESCRIPTION proved a statement-spanning pattern cannot match correctly formatted source. CI is repointed at tools/check-branding.sh, and its path filter widened to tools/** — the old filter named a file that no longer exists, so a change to the toolkit would have triggered no CI at all. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01WHt4DykxXaQnyaJfyS8KCn --- .github/workflows/ci.yaml | 4 +- check-branding.sh | 217 ----------------------- tools/brand-plugin.sh | 355 ++++++++++++++++++++++++++++++++++++++ tools/branding-sites.tsv | 29 ++++ tools/branding.conf | 66 +++++++ tools/check-branding.sh | 188 ++++++++++++++++++++ 6 files changed, 640 insertions(+), 219 deletions(-) delete mode 100644 check-branding.sh create mode 100755 tools/brand-plugin.sh create mode 100644 tools/branding-sites.tsv create mode 100644 tools/branding.conf create mode 100755 tools/check-branding.sh diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 84265c1..555b323 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -9,7 +9,7 @@ on: - 'Cargo.toml' - 'Cargo.lock' - 'bundler.toml' - - 'check-branding.sh' + - 'tools/**' pull_request: branches: [ master ] workflow_dispatch: @@ -40,7 +40,7 @@ jobs: # Cheap and needs no toolchain, so it runs first: a rename that only # landed in half the files is worth catching before spending a build on it. - name: Branding consistency - run: sh check-branding.sh + run: sh tools/check-branding.sh - name: Build run: cargo build --release diff --git a/check-branding.sh b/check-branding.sh deleted file mode 100644 index b08b0b2..0000000 --- a/check-branding.sh +++ /dev/null @@ -1,217 +0,0 @@ -#!/bin/sh -# SPDX-License-Identifier: GPL-3.0-or-later -# check-branding.sh - verify the plugin's branding/identity strings agree everywhere. -# -# Plugin branding is duplicated across the build manifest, the bundler manifest, -# the source, the docs and the deploy script. Nothing makes them agree, so a -# rename drifts silently: the bundle builds under one name and the deploy script -# looks for another. This asserts they match, and that the identity constants -# are legal. -# -# Kept in sync with musiikki/nih-plug-template's copy - fixes belong there -# first, then come back here. The differences are structural, not stylistic: -# this repo keeps its params in src/params.rs and wraps CLAP_DESCRIPTION. -# -# 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="bari_206" # Cargo package name == bundler.toml table key -DISPLAY="Bari 206" # bundle name -> ".vst3" / ".clap" -PLUGIN_NAME="Bari 206" # Plugin::NAME - what the DAW displays -VENDOR="Novoyuuparosk" # Plugin::VENDOR - the org, not the person -EMAIL="mikkeli@novoyuuparosk.org" # Plugin::EMAIL -HOMEPAGE="https://github.com/mikkeli/codename-206" # Cargo homepage -> Plugin::URL -CLAP_ID="org.novoyuuparosk.bari-206" # CLAP_ID - stable plugin identity -VST3_CLASS_ID="Bari206Multiband" # VST3_CLASS_ID - stable identity, MUST be 16 bytes -DESCRIPTION="A VST3/CLAP multiband compressor/limiter with a custom gain-curve display" -CLAP_DESCRIPTION="Multiband compressor/limiter (stage 3: 3-band + 'All' channel)" - -# Rust type names. Cosmetic - they never reach a host - but a rename that leaves -# half of them behind is the residue that says nobody read the file. -STRUCT="Bari206" # the Plugin impl target (src/lib.rs) -PARAMS_STRUCT="Bari206Params" # the Params impl target (src/params.rs) - -# Old spellings retired by the "Codename 206" -> "Bari 206" rename, plus the new -# ones so the NEXT rename is self-checking too. Each entry is skipped while it is -# still one of the canonical values above and arms the moment it stops being one. -# -# Deliberately NOT listed: the bare "codename-206". The repo, its remote and the -# homepage URL all keep that name on purpose, so it is current branding, not -# residue. The retired CLAP ID is listed in full instead, which cannot collide -# with the homepage. -STALE="codename_206 -Codename 206 -Codename206 -Codename206Params -Codename206Maxi! -206 prototype -com.mikkeli.codename-206 -bari_206 -Bari 206 -Bari206 -Bari206Params -Bari206Multiband -org.novoyuuparosk.bari-206" - -set -u -cd "$(dirname "$0")" || exit 1 - -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; } -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() { grep -qF "$2" "$1" 2>/dev/null; } - -check() { # check LABEL FILE LITERAL - if [ ! -f "$2" ]; then bad "$1" "missing file: $2"; return; fi - if has "$2" "$3"; then ok "$1"; else bad "$1" "$2 does not contain: $3"; fi -} - -# Same as check, but a missing file is "not applicable yet" rather than drift. -# The template ships without a deploy script; this starts checking once you add one. -opt_check() { # opt_check LABEL FILE LITERAL - if [ ! -f "$2" ]; then note "$1 ($2 not present)"; return; fi - if has "$2" "$3"; then ok "$1"; else bad "$1" "$2 does not contain: $3"; fi -} - -printf 'branding check (canonical: %s / "%s")\n\n' "$PKG" "$DISPLAY" - -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 "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\"" -# CLAP_DESCRIPTION wraps onto a second line here, so the const name and its -# value are never on one line. Match the value alone. -check "CLAP_DESCRIPTION" src/lib.rs "Some(\"$CLAP_DESCRIPTION\")" -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 -# 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\")" - -# 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. -n=$(printf '%s' "$VST3_CLASS_ID" | wc -c | tr -d ' ') -if [ "$n" -eq 16 ]; then ok "VST3_CLASS_ID is 16 bytes" -else bad "VST3_CLASS_ID is 16 bytes" "got $n bytes: \"$VST3_CLASS_ID\""; fi - -# CLAP IDs are reverse-DNS by convention and are compared verbatim by hosts, so -# a stray space or capital is a silent identity change between machines. -case "$CLAP_ID" in - *\ *|*[A-Z]*) bad "CLAP_ID is reverse-DNS safe" "no spaces or capitals allowed: \"$CLAP_ID\"" ;; - *.*) ok "CLAP_ID is reverse-DNS safe" ;; - *) bad "CLAP_ID is reverse-DNS safe" "expected dotted reverse-DNS, got: \"$CLAP_ID\"" ;; -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/params.rs "struct $PARAMS_STRUCT {" -check "impl Default for params" src/params.rs "impl Default for $PARAMS_STRUCT {" -check "params field type" src/lib.rs "params: Arc<$PARAMS_STRUCT>," - -printf '\ndocs\n' -opt_check "README bundle command" README.md "cargo xtask bundle $PKG" - -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 and fails in seconds. -opt_check "CI bundle command" .github/workflows/ci.yaml "cargo xtask bundle $PKG" - -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\"" -opt_check "deploy.ps1 CLAP bundle" deploy.ps1 "\$clapName = \"$DISPLAY.clap\"" - -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. -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 - [ -n "$s" ] || continue - - # Still part of a canonical value? Then it is current branding, not a leftover. - # This is what lets the list ship pre-seeded: entries arm themselves as you - # rename. Substring, not equality, because placeholders nest - "your-domain" - # lives inside the canonical CLAP_ID, and "YOUR-USERNAME" inside the homepage. - # Over-skipping is the safe direction: a false FAIL on a name you still use - # would train you to ignore this section. - case "$CANONICAL_BLOB" in - *"$s"*) note "\"$s\" (still canonical)"; continue ;; - esac - - # This script names every placeholder by definition, so it can never be a 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; } - done) - if [ -z "$hits" ]; then ok "no occurrences of \"$s\"" - else bad "no occurrences of \"$s\"" "still present in: $(echo "$hits" | tr '\n' ' ')"; fi -done </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" -[ "$skip" -gt 0 ] && printf ', %d skipped' "$skip" -printf '\n' -[ "$fail" -eq 0 ] || exit 1 diff --git a/tools/brand-plugin.sh b/tools/brand-plugin.sh new file mode 100755 index 0000000..80b0760 --- /dev/null +++ b/tools/brand-plugin.sh @@ -0,0 +1,355 @@ +#!/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 +# Resolve the repo root: these live in tools/, every path they touch is +# repo-relative, and they must behave the same wherever they are invoked from. +cd "$(dirname "$0")/.." || exit 1 +[ -f tools/branding.conf ] || { printf 'tools/branding.conf not found\n' >&2; exit 1; } +# shellcheck disable=SC1091 +. ./tools/branding.conf + +OLD_PKG="$PKG"; OLD_DISPLAY="$DISPLAY"; OLD_PLUGIN_NAME="$PLUGIN_NAME" +OLD_VENDOR="$VENDOR"; OLD_EMAIL="$EMAIL"; OLD_HOMEPAGE="$HOMEPAGE"; OLD_AUTHOR="$AUTHOR" +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_author="" +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 ;; + --author) new_author="${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 + +# Interactive when nothing was passed and there is a terminal to ask at. Every +# prompt offers the current value; Enter keeps it, so a run that answers nothing +# is a no-op rather than a surprise. Optional fields additionally accept "-" to +# clear them. +ask() { # ask VARNAME PROMPT CURRENT + printf ' %s [%s]: ' "$2" "$3" > /dev/tty + IFS= read -r _reply < /dev/tty || _reply="" + [ -n "$_reply" ] || _reply="$3" + [ "$_reply" = "-" ] && _reply="" + eval "$1=\$_reply" +} +if [ -z "$new_display" ] && [ -t 0 ] && [ -c /dev/tty ]; then + printf 'branding this plugin - Enter keeps the current value, "-" clears an optional one\n\n' + ask new_display "display name " "$OLD_DISPLAY" + ask new_vendor "vendor (org) " "$OLD_VENDOR" + ask new_author "cargo author " "$OLD_AUTHOR" + ask new_email "email " "$OLD_EMAIL" + ask new_homepage "homepage " "$OLD_HOMEPAGE" + ask new_description "cargo description" "$OLD_DESCRIPTION" + ask new_clap_description "clap description" "$OLD_CLAP_DESCRIPTION" + printf '\n' +fi +[ -n "$new_display" ] || { printf 'nothing to do: pass --display "New Name" (or run interactively)\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_author" ] || new_author="$OLD_AUTHOR" +[ -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 "cargo author" "$OLD_AUTHOR" "$new_author" +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 ------------------------------------------------------------------ +# Three scratch files, one trap: the regenerated site table, the rows carried +# through from the old one, and the value-substitution pairs. +gen="$(mktemp)"; keep="$(mktemp)"; subst_file="$(mktemp)" +trap 'rm -f "$gen" "$keep" "$subst_file"' EXIT + +T="$(printf '\t')" +{ + printf 'req%spkg.cargo%sCargo.toml%sname = "%s"\n' "$T" "$T" "$T" "$new_pkg" + printf 'req%spkg.bundler%sbundler.toml%s[%s]\n' "$T" "$T" "$T" "$new_pkg" + printf 'opt%spkg.lock%sCargo.lock%sname = "%s"\n' "$T" "$T" "$T" "$new_pkg" + printf 'opt%spkg.ci%s.github/workflows/ci.yaml%scargo xtask bundle %s\n' "$T" "$T" "$T" "$new_pkg" + printf 'opt%spkg.readme%sREADME.md%scargo xtask bundle %s\n' "$T" "$T" "$T" "$new_pkg" + printf 'req%sdisplay.bundler%sbundler.toml%sname = "%s"\n' "$T" "$T" "$T" "$new_display" + printf 'opt%sdisplay.notice%sNOTICE%s%s\n' "$T" "$T" "$T" "$new_display" + printf 'req%sname.plugin%s%s%sconst NAME: &'"'"'static str = "%s"\n' "$T" "$T" "$LIB_FILE" "$T" "$new_display" + printf 'req%svendor.plugin%s%s%sconst VENDOR: &'"'"'static str = "%s"\n' "$T" "$T" "$LIB_FILE" "$T" "$new_vendor" + printf 'req%semail.plugin%s%s%sconst EMAIL: &'"'"'static str = "%s"\n' "$T" "$T" "$LIB_FILE" "$T" "$new_email" + printf 'req%sauthors.cargo%sCargo.toml%sauthors = ["%s <%s>"]\n' "$T" "$T" "$T" "$new_author" "$new_email" + printf 'req%shomepage.cargo%sCargo.toml%shomepage = "%s"\n' "$T" "$T" "$T" "$new_homepage" + printf 'req%surl.plugin%s%s%sconst URL: &'"'"'static str = env!("CARGO_PKG_HOMEPAGE")\n' "$T" "$T" "$LIB_FILE" "$T" + printf 'req%sdescription.cargo%sCargo.toml%sdescription = "%s"\n' "$T" "$T" "$T" "$new_description" + # Narrowest distinctive fragment, not the whole statement: rustfmt wraps a long + # CLAP_DESCRIPTION onto its own line, and a pattern spanning the assignment + # would then never match a correctly formatted file. + printf 'req%sdescription.clap%s%s%sSome("%s")\n' "$T" "$T" "$LIB_FILE" "$T" "$new_clap_description" + printf 'req%sclapid.plugin%s%s%sconst CLAP_ID: &'"'"'static str = "%s"\n' "$T" "$T" "$LIB_FILE" "$T" "$new_clap_id" + printf 'req%svst3id.plugin%s%s%sconst VST3_CLASS_ID: [u8; 16] = *b"%s"\n' "$T" "$T" "$LIB_FILE" "$T" "$new_vst3" + printf 'req%sstruct.def%s%s%sstruct %s {\n' "$T" "$T" "$LIB_FILE" "$T" "$new_struct" + printf 'req%sstruct.plugin%s%s%simpl Plugin for %s {\n' "$T" "$T" "$LIB_FILE" "$T" "$new_struct" + printf 'req%sstruct.clap%s%s%simpl ClapPlugin for %s {\n' "$T" "$T" "$LIB_FILE" "$T" "$new_struct" + printf 'req%sstruct.vst3%s%s%simpl Vst3Plugin for %s {\n' "$T" "$T" "$LIB_FILE" "$T" "$new_struct" + printf 'req%sstruct.exportclap%s%s%snih_export_clap!(%s);\n' "$T" "$T" "$LIB_FILE" "$T" "$new_struct" + printf 'req%sstruct.exportvst3%s%s%snih_export_vst3!(%s);\n' "$T" "$T" "$LIB_FILE" "$T" "$new_struct" + printf 'req%sparams.def%s%s%sstruct %s {\n' "$T" "$T" "$PARAMS_FILE" "$T" "$new_params_struct" + printf 'req%sparams.default%s%s%simpl Default for %s {\n' "$T" "$T" "$PARAMS_FILE" "$T" "$new_params_struct" + printf 'req%sparams.field%s%s%sparams: Arc<%s>,\n' "$T" "$T" "$LIB_FILE" "$T" "$new_params_struct" + printf 'opt%sdeploy.pkg%sdeploy.ps1%s$Package = "%s"\n' "$T" "$T" "$T" "$new_pkg" + printf 'opt%sdeploy.vst3%sdeploy.ps1%s$vst3Name = "%s.vst3"\n' "$T" "$T" "$T" "$new_display" + printf 'opt%sdeploy.clap%sdeploy.ps1%s$clapName = "%s.clap"\n' "$T" "$T" "$T" "$new_display" +} > "$gen" + +# Pass 1 - site-directed. Each owned row in the existing table carries the OLD +# resolved literal for its site; the regenerated row carries the new one. Rewriting +# by that pair is unambiguous, which plain value substitution is not: AUTHOR +# defaults to VENDOR, so both fields can hold the same old string and a global +# replace cannot tell which occurrence belongs to which. This must run BEFORE +# pass 2, or the global replace consumes the old literals first and leaves the +# precise rewrite with nothing to match. +sites="tools/branding-sites.tsv" +T="$(printf '\t')" +if [ -f "$sites" ]; then + while IFS="$T" read -r ok_ oo_ of_ op_; do + case "$ok_" in ''|\#*) continue ;; esac + np_=$(awk -F"$T" -v o="$oo_" '$2==o {print $4; exit}' "$gen") + [ -n "$np_" ] || continue + [ "$np_" != "$op_" ] || continue + [ -f "$of_" ] || continue + esc_o=$(printf '%s' "$op_" | sed 's/[][\.*^$/|&]/\\&/g') + esc_n=$(printf '%s' "$np_" | sed 's/[\\/&|]/\\&/g') + tmp_=$(mktemp) + sed "s|$esc_o|$esc_n|g" "$of_" > "$tmp_" && cat "$tmp_" > "$of_" + rm -f "$tmp_" + done < "$sites" + printf ' pass 1: rewrote known sites from the table\n' +fi + +# Pass 2 - value substitution, for everything the table does not enumerate: +# doc comments, use sites in other modules, prose in the README. +# 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. +{ + 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_AUTHOR" "$new_author" + 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 + # branding.conf alone is excluded: it is rewritten wholesale below, and its + # STALE list names every retired spelling by design. The site table IS + # substituted, so hand-added custom rows track the rename; owned rows are + # regenerated afterwards regardless. + case "$f" in tools/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|^# *AUTHOR=.*|AUTHOR=\"$new_author\"|" \ + -e "s|^AUTHOR=.*|AUTHOR=\"$new_author\"|" \ + -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\"|" \ + tools/branding.conf > tools/branding.conf.new +if [ -n "$retired" ]; then + { sed '$ s/"$//' tools/branding.conf.new; printf '%s"\n' "$retired"; } > tools/branding.conf +else + # Nothing retired (a no-op rename, or a regeneration): leave STALE untouched + # rather than appending a blank line to it on every run. + cat tools/branding.conf.new > tools/branding.conf +fi +rm -f tools/branding.conf.new +if [ "$new_author" = "$new_vendor" ]; then + # Identical to VENDOR, so re-comment it: the conf should keep showing that this + # value derives rather than pinning a duplicate that then has to be maintained. + tmp_c=$(mktemp) + sed "s|^AUTHOR=.*|# AUTHOR=\"$new_author\"|" tools/branding.conf > "$tmp_c" && cat "$tmp_c" > tools/branding.conf + rm -f "$tmp_c" +fi +printf ' rewrote tools/branding.conf (retired spellings appended to STALE)\n' + +# Regenerate the site table. Rows whose owner this script recognises are +# rewritten with the new resolved literals; every other row - anything marked +# custom, or added by hand for a file this template does not ship - is carried +# through untouched. Clobbering those silently is how a tool stops being trusted. +sites="tools/branding-sites.tsv" + +owned=$(cut -f2 "$gen") +if [ -f "$sites" ]; then + grep '^#' "$sites" > "$keep" 2>/dev/null || : > "$keep" + while IFS="$T" read -r k o f pat; do + case "$k" in ''|\#*) continue ;; esac + printf '%s\n' "$owned" | grep -qxF "$o" || printf '%s%s%s%s%s%s%s\n' "$k" "$T" "$o" "$T" "$f" "$T" "$pat" >> "$keep" + done < "$sites" +else + : > "$keep" +fi +{ cat "$keep"; cat "$gen"; } > "$sites" +printf ' rewrote %s (%s owned rows, %s preserved)\n' "$sites" "$(wc -l < "$gen" | tr -d ' ')" "$(grep -cv '^#' "$keep" 2>/dev/null || echo 0)" + +printf '\nverifying with check-branding.sh\n\n' +sh tools/check-branding.sh diff --git a/tools/branding-sites.tsv b/tools/branding-sites.tsv new file mode 100644 index 0000000..aefaad9 --- /dev/null +++ b/tools/branding-sites.tsv @@ -0,0 +1,29 @@ +req pkg.cargo Cargo.toml name = "bari_206" +req pkg.bundler bundler.toml [bari_206] +opt pkg.lock Cargo.lock name = "bari_206" +opt pkg.ci .github/workflows/ci.yaml cargo xtask bundle bari_206 +opt pkg.readme README.md cargo xtask bundle bari_206 +req display.bundler bundler.toml name = "Bari 206" +opt display.notice NOTICE Bari 206 +req name.plugin src/lib.rs const NAME: &'static str = "Bari 206" +req vendor.plugin src/lib.rs const VENDOR: &'static str = "Novoyuuparosk" +req email.plugin src/lib.rs const EMAIL: &'static str = "mikkeli@novoyuuparosk.org" +req authors.cargo Cargo.toml authors = ["Mikkeli Matlock "] +req homepage.cargo Cargo.toml homepage = "https://github.com/mikkeli/codename-206" +req url.plugin src/lib.rs const URL: &'static str = env!("CARGO_PKG_HOMEPAGE") +req description.cargo Cargo.toml description = "A VST3/CLAP multiband compressor/limiter with a custom gain-curve display" +req description.clap src/lib.rs Some("Multiband compressor/limiter (stage 3: 3-band + 'All' channel)") +req clapid.plugin src/lib.rs const CLAP_ID: &'static str = "org.novoyuuparosk.bari-206" +req vst3id.plugin src/lib.rs const VST3_CLASS_ID: [u8; 16] = *b"Bari206Multiband" +req struct.def src/lib.rs struct Bari206 { +req struct.plugin src/lib.rs impl Plugin for Bari206 { +req struct.clap src/lib.rs impl ClapPlugin for Bari206 { +req struct.vst3 src/lib.rs impl Vst3Plugin for Bari206 { +req struct.exportclap src/lib.rs nih_export_clap!(Bari206); +req struct.exportvst3 src/lib.rs nih_export_vst3!(Bari206); +req params.def src/params.rs struct Bari206Params { +req params.default src/params.rs impl Default for Bari206Params { +req params.field src/lib.rs params: Arc, +opt deploy.pkg deploy.ps1 $Package = "bari_206" +opt deploy.vst3 deploy.ps1 $vst3Name = "Bari 206.vst3" +opt deploy.clap deploy.ps1 $clapName = "Bari 206.clap" diff --git a/tools/branding.conf b/tools/branding.conf new file mode 100644 index 0000000..05da915 --- /dev/null +++ b/tools/branding.conf @@ -0,0 +1,66 @@ +# SPDX-License-Identifier: GPL-3.0-or-later +# branding.conf - this plugin's canonical identity. +# +# Managed by tools/brand-plugin.sh, read by tools/check-branding.sh. Hand-editing +# works and is sometimes the quickest thing, but the helper is what keeps this +# file, the source tree and tools/branding-sites.tsv agreeing with each other. +# Edit here and you are on the hook for running the checker afterwards. + +# --- derivations ------------------------------------------------------------ +_derive_pkg() { + printf '%s' "$1" | tr '[:upper:]' '[:lower:]' \ + | sed -e 's/[^a-z0-9]\{1,\}/_/g' -e 's/^_//' -e 's/_$//' +} +_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 ""}' +} + +# --- the names --------------------------------------------------------------- +PLUGIN_NAME="Bari 206" +VENDOR="Novoyuuparosk" +EMAIL="mikkeli@novoyuuparosk.org" +AUTHOR="Mikkeli Matlock" +HOMEPAGE="https://github.com/mikkeli/codename-206" +DESCRIPTION="A VST3/CLAP multiband compressor/limiter with a custom gain-curve display" + +# What the plugin does *now*. No codename, no development stage. +CLAP_DESCRIPTION="Multiband compressor/limiter (stage 3: 3-band + 'All' channel)" + +# --- frozen identity --------------------------------------------------------- +# Hosts key saved projects and presets off these two. Never move them after the +# first release: every existing session silently loses the plugin. +CLAP_ID="org.novoyuuparosk.bari-206" +VST3_CLASS_ID="Bari206Multiband" + +# --- fixed at repo creation -------------------------------------------------- +# The repo keeps its development codename while the plugin carries the product +# name. Recorded only so it can be kept out of user-facing text. +CODENAME="codename-206" + +LIB_FILE="src/lib.rs" +PARAMS_FILE="src/params.rs" + +# --- derived unless overridden ----------------------------------------------- +# PKG, STRUCT and PARAMS_STRUCT all derive correctly from PLUGIN_NAME here, so +# none is pinned. DISPLAY defaults to PLUGIN_NAME. +: "${AUTHOR:=$VENDOR}" +: "${DISPLAY:=$PLUGIN_NAME}" +: "${PKG:=$(_derive_pkg "$PLUGIN_NAME")}" +: "${STRUCT:=$(_derive_struct "$PLUGIN_NAME")}" +: "${PARAMS_STRUCT:=${STRUCT}Params}" + +# --- retired spellings ------------------------------------------------------- +STALE="codename_206 +Codename 206 +Codename206 +Codename206Params +Codename206Maxi! +206 prototype +com.mikkeli.codename-206 +bari_206 +Bari 206 +Bari206 +Bari206Params +Bari206Multiband +org.novoyuuparosk.bari-206" diff --git a/tools/check-branding.sh b/tools/check-branding.sh new file mode 100755 index 0000000..6386030 --- /dev/null +++ b/tools/check-branding.sh @@ -0,0 +1,188 @@ +#!/bin/sh +# SPDX-License-Identifier: GPL-3.0-or-later +# check-branding.sh - verify the plugin's branding/identity strings agree everywhere. +# +# Plugin branding is duplicated across the build manifest, the bundler manifest, +# the source, the docs and (once you add one) the deploy script. Nothing makes +# them agree, so a rename drifts silently: the bundle builds under one name and +# the deploy script looks for another. This asserts they match, and that the +# identity constants are legal. +# +# In a template this matters more than usual: renaming is the first thing you do +# with this repo, and it touches every file below. Run this immediately after, +# and the STALE machinery will list whatever you missed. +# +# Usage: sh tools/check-branding.sh (exit 0 = consistent, 1 = drift found) +# +# 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 +# Resolve the repo root: these live in tools/, every path they touch is +# repo-relative, and they must behave the same wherever they are invoked from. +cd "$(dirname "$0")/.." || exit 1 + +if [ ! -f tools/branding.conf ]; then + printf 'tools/branding.conf not found\n' >&2 + exit 1 +fi +# shellcheck disable=SC1091 +. ./tools/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; } +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() { grep -qF "$2" "$1" 2>/dev/null; } + +check() { # check LABEL FILE LITERAL + if [ ! -f "$2" ]; then bad "$1" "missing file: $2"; return; fi + if has "$2" "$3"; then ok "$1"; else bad "$1" "$2 does not contain: $3"; fi +} + +# Same as check, but a missing file is "not applicable yet" rather than drift. +# The template ships without a deploy script; this starts checking once you add one. +opt_check() { # opt_check LABEL FILE LITERAL + if [ ! -f "$2" ]; then note "$1 ($2 not present)"; return; fi + if has "$2" "$3"; then ok "$1"; else bad "$1" "$2 does not contain: $3"; fi +} + +printf 'branding check (canonical: %s / "%s")\n\n' "$PKG" "$DISPLAY" + +# --- consistency: every site in the table holds its resolved literal ---------- +# The table is data, so adding a site is a row rather than a shell edit, and the +# same script runs unmodified across every plugin built from this template. +SITES="tools/branding-sites.tsv" +if [ ! -f "$SITES" ]; then + bad "site table" "$SITES not found - run tools/brand-plugin.sh to generate it" +else + printf 'sites\n' + while IFS="$(printf '\t')" read -r kind owner file pattern; do + case "$kind" in ''|\#*) continue ;; esac + [ -n "$pattern" ] || continue + label="$owner" + if [ ! -f "$file" ]; then + case "$kind" in + opt) note "$label ($file not present)" ;; + *) bad "$label" "missing file: $file" ;; + esac + continue + fi + if has "$file" "$pattern"; then ok "$label" + else bad "$label" "$file does not contain: $pattern"; fi + done < "$SITES" +fi + +# --- validity: rules the table cannot express -------------------------------- +printf '\nvalidity\n' + +# VST3 class IDs are a fixed 16-byte array. A wrong length is a compile error, +# but saying so here is clearer than reading rustc on it. +n=$(printf '%s' "$VST3_CLASS_ID" | wc -c | tr -d ' ') +if [ "$n" -eq 16 ]; then ok "VST3_CLASS_ID is 16 bytes" +else bad "VST3_CLASS_ID is 16 bytes" "got $n bytes: \"$VST3_CLASS_ID\""; fi + +# CLAP IDs are reverse-DNS by convention and compared verbatim by hosts, so a +# stray space or capital is a silent identity change between machines. +case "$CLAP_ID" in + *\ *|*[A-Z]*) bad "CLAP_ID is reverse-DNS safe" "no spaces or capitals allowed: \"$CLAP_ID\"" ;; + *.*) ok "CLAP_ID is reverse-DNS safe" ;; + *) bad "CLAP_ID is reverse-DNS safe" "expected dotted reverse-DNS, got: \"$CLAP_ID\"" ;; +esac + +# An overridden package name is legitimate - the derivation is sometimes invalid +# - but a display rename that forgot the package is not, and looks identical +# from here. Reported, never failed. +derived_pkg=$(_derive_pkg "$PLUGIN_NAME") +[ "$PKG" = "$derived_pkg" ] || info "PKG \"$PKG\" differs from the derivation of PLUGIN_NAME (\"$derived_pkg\") - fine if deliberate" + +# CLAP_DESCRIPTION is shown to users in a host's plugin browser. "Free of +# development history" is a judgement a script cannot make, but the failure it +# produces is mechanical: a retired name or the repo codename surviving in it. +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 </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" +# 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 + [ -n "$s" ] || continue + + # Still part of a canonical value? Then it is current branding, not a leftover. + # This is what lets the list ship pre-seeded: entries arm themselves as you + # rename. Substring, not equality, because placeholders nest - "your-domain" + # lives inside the canonical CLAP_ID, and "YOUR-USERNAME" inside the homepage. + # Over-skipping is the safe direction: a false FAIL on a name you still use + # would train you to ignore this section. + case "$CANONICAL_BLOB" in + *"$s"*) note "\"$s\" (still canonical)"; continue ;; + esac + + # 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 + case "$f" in tools/check-branding.sh|tools/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 +done </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" +[ "$skip" -gt 0 ] && printf ', %d skipped' "$skip" +printf '\n' +[ "$fail" -eq 0 ] || exit 1