From 4cf91ff44af5dca23775c694af9d242fccaecbef Mon Sep 17 00:00:00 2001 From: Wabo Date: Thu, 4 Jun 2026 17:50:24 -0400 Subject: [PATCH] Harden upstream merge (Phase 2): brand-asset baseline, sync-upstream gates, docs Phase 2 of the merge-hardening effort (Phase 1 added bin/fork-check + the fork-invariants manifest). - config/brand_assets.sha256: checksum baseline of the WaboSign 'W' mark assets (the 6 logos + both apple-touch icons, which the old hardcoded 6-file restore list missed). bin/fork-check now verifies each asset's sha256 (catching a silent upstream overwrite that bypasses the text sweep) and warns about public/ brand-looking files not in the baseline. Single source of truth. - bin/sync-upstream: restore brand assets by reading the baseline (not a hardcoded list, so new brand files are covered automatically); run BOTH rebrand-check and fork-check after the post-merge sweep, aborting with a remediation message and leaving the tree in place if either fails; optional RUN_TESTS=1 to run rspec; print a status summary + the human-judgment residue. - .gitattributes: add the two apple-touch icons to the -merge brand list, kept in sync with the baseline. - REBRANDING.md: replace the 21-item manual post-merge checklist (which was not run reliably) with an automated tier delegated to fork-check + the manifest, plus a slim human-judgment residue; add an agent sync runbook and an 'Adding a new fork invariant' guide; correct the stale based_on attribution note. - rebrand-check/rebrand-sync: allowlist/deny the new baseline file (its comment legitimately references DocuSeal's logo). Co-Authored-By: Claude Sonnet 4.6 --- .gitattributes | 18 +++-- REBRANDING.md | 158 ++++++++++++++++--------------------- bin/fork-check | 45 +++++++++++ bin/rebrand-check | 1 + bin/rebrand-sync | 1 + bin/sync-upstream | 74 ++++++++++++----- config/brand_assets.sha256 | 23 ++++++ 7 files changed, 202 insertions(+), 118 deletions(-) create mode 100644 config/brand_assets.sha256 diff --git a/.gitattributes b/.gitattributes index 76c29095..711158a6 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,9 +1,13 @@ *.html linguist-detectable=false -# WaboSign brand binary files — never merge upstream versions; always keep ours -public/favicon.svg -merge -public/favicon.ico -merge -public/favicon-16x16.png -merge -public/favicon-32x32.png -merge -public/favicon-96x96.png -merge -public/logo.svg -merge +# WaboSign brand binary files — never merge upstream versions; always keep ours. +# Keep this list in sync with config/brand_assets.sha256 (the source of truth +# that bin/fork-check verifies and bin/sync-upstream restores from). +public/favicon.svg -merge +public/favicon.ico -merge +public/favicon-16x16.png -merge +public/favicon-32x32.png -merge +public/favicon-96x96.png -merge +public/logo.svg -merge +public/apple-touch-icon.png -merge +public/apple-touch-icon-precomposed.png -merge diff --git a/REBRANDING.md b/REBRANDING.md index 037d0bb8..c505575c 100644 --- a/REBRANDING.md +++ b/REBRANDING.md @@ -20,9 +20,9 @@ Scope: ~183 files modified. Decisions were made in a planning conversation befor ## AGPL §7(b) attribution - `Wabosign::UPSTREAM_NAME = 'DocuSeal'` and `Wabosign::UPSTREAM_URL` constants added -- [app/views/shared/_powered_by.html.erb](app/views/shared/_powered_by.html.erb) and [app/views/shared/_email_attribution.html.erb](app/views/shared/_email_attribution.html.erb) now render a "based on DocuSeal (AGPLv3)" credit alongside the WaboSign brand +- [app/views/shared/_powered_by.html.erb](app/views/shared/_powered_by.html.erb) renders "WaboSign, a fork of DocuSeal" in the footer; [app/views/shared/_email_attribution.html.erb](app/views/shared/_email_attribution.html.erb) states the fork relationship in the email footer - [app/javascript/submission_form/completed.vue](app/javascript/submission_form/completed.vue) carries the same credit on the post-signing completion screen -- New `based_on` i18n key added to all 14 language sections +- i18n keys `fork_of` and `product_name_is_a_fork_of_upstream_html` carry the credit text (English; other locales fall back via `config.i18n.fallbacks`). `bin/fork-check` asserts these surfaces keep the credit - [README.md](README.md) and [LICENSE_ADDITIONAL_TERMS](LICENSE_ADDITIONAL_TERMS) rewritten - New [NOTICE](NOTICE) file added crediting DocuSeal LLC and listing modifications @@ -73,103 +73,81 @@ Upstream lives at `docusealco/docuseal`. Each upstream release is brought in by ### Tooling - [bin/rebrand-sync](bin/rebrand-sync) — Ruby script that performs the DocuSeal → WaboSign rename sweep across the working tree. Idempotent. Honors a deny-list (see §"Intentionally preserved upstream references" above) and sentinel-protects AGPL §7(b) attribution phrases, SDK custom-element names (`docuseal-form`, `docuseal-builder`), `@docuseal/*` npm packages, and the `github.com/docusealco/{fields-detection,pdfium-binaries,turbo}` binary URLs. -- [bin/rebrand-check](bin/rebrand-check) — fails (exit 1) if any unintended DocuSeal reference survives. Wired into [.github/workflows/ci.yml](.github/workflows/ci.yml) as the `Rebrand check` job. +- [bin/rebrand-check](bin/rebrand-check) — fails (exit 1) if any unintended DocuSeal *text* reference survives. Wired into CI as the `Rebrand check` job. +- [bin/fork-check](bin/fork-check) — fails (exit 1) if any **fork invariant** is broken: re-introduced Pro gate, deleted fork code, overwritten brand asset, lost attribution, dangling partial render, or PRESERVE↔ALLOW_PATTERNS drift. Driven by the declarative manifest [config/fork_invariants.yml](config/fork_invariants.yml). Wired into CI as the `Fork invariants` job. This is the executable form of the old manual post-merge checklist. +- [config/fork_invariants.yml](config/fork_invariants.yml) — the data behind `bin/fork-check`. Extend **this file** (not the script) when upstream adds a new gate; every entry carries a `why:`. +- [config/brand_assets.sha256](config/brand_assets.sha256) — checksum baseline of the WaboSign "W" mark assets. The single source of truth for "what is a brand asset": `bin/fork-check` verifies it, `bin/sync-upstream` restores from it, and [.gitattributes](.gitattributes) `-merge` should mirror it. - `git config rerere.enabled true && git config rerere.autoupdate true` — once-per-checkout setup; remembers semantic conflict resolutions so the same call is not re-made each release. -- [.gitattributes](.gitattributes) marks `Gemfile.lock` and `yarn.lock` as `-merge` (regenerate after merge rather than diffing). +- [.gitattributes](.gitattributes) marks the brand binary assets as `-merge` (always keep ours; never blend an upstream version during a merge). -### Per-sync steps +### Runbook (for a human or AI agent) + +The whole sync — fetch, branch from the tag, sweep, merge, restore brand assets, +re-sweep, and run both gates — is automated: ```sh -git fetch upstream --tags -git checkout -b sync/upstream- # e.g. 3.0.0 -bin/rebrand-sync -git add -A && git commit -m "Apply WaboSign rebrand sweep to upstream " +bin/sync-upstream # e.g. bin/sync-upstream 3.0.2 +# RUN_TESTS=1 bin/sync-upstream # also run rspec before declaring done +``` -git checkout master -git merge --no-ff sync/upstream- -# Resolve conflicts. Rerere caches recurring resolutions. +When it stops, decide based on which gate failed: + +1. **Merge conflict** — resolve it. `rerere` caches recurring resolutions. +2. **`rebrand-check` failed** — un-rebranded DocuSeal text survived. If a token + must be preserved, add it to `PRESERVE` (bin/rebrand-sync) **and** + `ALLOW_PATTERNS` (bin/rebrand-check) together (see below). +3. **`fork-check` failed** — a fork invariant broke. Each violation names the + file + the `why:` from the manifest: + - re-introduced gate → remove it; + - brand asset overwritten → `git checkout ORIG_HEAD -- `; + - genuinely new upstream feature/gate → add a scoped invariant to + [config/fork_invariants.yml](config/fork_invariants.yml). +4. Re-run `bin/fork-check` (and `bin/rebrand-check`) until both print `ok`. +5. `bundle install && yarn install`, then **tag + push only when both gates and + `rspec` are green**: `git tag wabosign-synced-with- && git push origin master --tags`. + +The equivalent manual steps (if you are not using the script) are: branch from +the tag, `bin/rebrand-sync`, commit, `git merge --no-ff` into master, restore +the brand assets listed in `config/brand_assets.sha256` from `ORIG_HEAD`, +`bin/rebrand-sync` again, then `bin/rebrand-check && bin/fork-check`. -# Restore WaboSign brand assets that the merge may have overwritten: -git checkout ORIG_HEAD -- public/favicon.svg public/favicon.ico \ - public/favicon-16x16.png public/favicon-32x32.png \ - public/favicon-96x96.png public/logo.svg +### Adding new preserved tokens -bin/rebrand-sync # catch upstream-only new files -bin/rebrand-check # CI gate +When upstream introduces a new SDK identifier, binary URL, or attribution surface that must survive the sweep, edit `PRESERVE` in [bin/rebrand-sync](bin/rebrand-sync) and `ALLOW_PATTERNS` in [bin/rebrand-check](bin/rebrand-check) together. The two must stay in sync — `rebrand-sync` decides what the sweep ignores, `rebrand-check` decides what CI tolerates. **This pairing is now CI-enforced:** `bin/fork-check` fails if a `PRESERVE` token containing "docuseal" has no matching `ALLOW_PATTERN`. -bundle install -yarn install +### Adding a new fork invariant -# Verify (see "Verification" in the plan), then: -git tag wabosign-synced-with- -``` +When upstream re-introduces a gate, deletes fork code, or adds a brand asset, encode the rule in [config/fork_invariants.yml](config/fork_invariants.yml) — not in `bin/fork-check`. Use a path-scoped `must_not_contain` for re-added gates (never ban a token tree-wide unless it is genuinely unique to the gate — `Wabosign.multitenant?` is legitimate in ~19 views), `must_exist`/`must_not_exist` for files, and add new brand files to `config/brand_assets.sha256` (and the `.gitattributes` `-merge` list). Always include a `why:` — it is the institutional memory the next sync will need. -Or use the automated script: -```sh -bin/sync-upstream -``` +## Post-Merge Verification -### Adding new preserved tokens +Most of what used to be a 21-item manual checklist is now executed by CI. After a +sync, the gates below must be green; only a short residue needs a human eye. + +### Automated (CI — must pass) + +- **`bin/rebrand-check`** — no unintended DocuSeal *text* survived the sweep. +- **`bin/fork-check`** — every fork invariant holds. The assertions (and the + rationale for each) live in [config/fork_invariants.yml](config/fork_invariants.yml): + attribution surfaces present; renamed identifiers + SDK tokens present; brand + assets match [config/brand_assets.sha256](config/brand_assets.sha256); no + re-introduced Pro gates (`ENTERPRISE_PATHS`, `console_redirect_index_path`, the + reminder `multitenant?` gate, …); placeholders / `console_redirect_controller` / + `lib/docuseal.rb` absent; SMS stack + `lib/ability.rb` present; no dead paywall + i18n keys; no dangling partial renders; PRESERVE↔ALLOW_PATTERNS in sync. +- **`rspec`** — suite passes (also catches Zeitwerk module conflicts at boot). + +When upstream changes something the manifest does not yet know about, **extend +the manifest** (see "Adding a new fork invariant" above) rather than re-checking +by hand. That way the next sync inherits the protection. + +### Human-judgment residue (not automatable) -When upstream introduces a new SDK identifier, binary URL, or attribution surface that must survive the sweep, edit `PRESERVE` in [bin/rebrand-sync](bin/rebrand-sync) and `ALLOW_PATTERNS` in [bin/rebrand-check](bin/rebrand-check) together. The two must stay in sync — `rebrand-sync` decides what the sweep ignores, `rebrand-check` decides what CI tolerates. - -## Post-Merge Verification Checklist - -Run through these checks after every upstream merge. The earlier failures are caught by `bin/rebrand-check`; the later ones require manual inspection or `rspec`. - -### Automatic (`bin/rebrand-check`) -- Rebrand check passes (no unintended DocuSeal references) -- RSpec suite passes (360+ examples, 0 failures) - -### Footer / Attribution -- [ ] `app/views/shared/_powered_by.html.erb` links both WaboSign *and* DocuSeal (upstream AGPL credit) -- [ ] `app/views/shared/_email_attribution.html.erb` uses WaboSign product name, not DocuSeal -- [ ] `app/javascript/submission_form/completed.vue` still has the hardcoded DocuSeal upstream credit - -### Logo / Branding -- [ ] `app/views/shared/_logo.html.erb` shows the WaboSign "W" mark (not the DocuSeal abstract shape) -- [ ] `public/favicon.svg`, `public/logo.svg` show the WaboSign "W" mark -- [ ] `app/views/shared/_account_logo.html.erb` renders attached logo or falls back to the W mark - -### Console / Plans / Pro / Upgrade -- [ ] `app/controllers/console_redirect_controller.rb` does not exist -- [ ] `config/routes.rb` has no `console_redirect`, `upgrade`, or `manage` routes -- [ ] `app/controllers/sessions_controller.rb` has no `console_redirect_index_path` call -- [ ] `lib/wabosign.rb` has no `CONSOLE_URL`, `CLOUD_URL`, or `CDN_URL` constants -- [ ] `app/views/shared/_settings_nav.html.erb` has no "Plans" link or "Pro" badge -- [ ] `app/views/shared/_navbar.html.erb` has no "Console" link in dropdown -- [ ] `app/views/shared/_navbar_buttons.html.erb` has no "Upgrade" button -- [ ] No view file contains `unlock_with_docuseal_pro`, `activate_with_docuseal_pro`, or `console_redirect_index_path` - -### Feature Gates (all freely available) -- [ ] `app/views/sms_settings/index.html.erb` shows provider form (BulkVS/Twilio/VoIP.ms/SignalWire) — not a placeholder -- [ ] `app/views/personalization_settings/_logo_placeholder.html.erb` shows upload form — not a Pro upsell -- [ ] `app/views/notifications_settings/_reminder_placeholder.html.erb` is empty (reminder form renders freely) -- [ ] `app/views/submissions/_bulk_send_placeholder.html.erb` is empty (bulk send freely available) -- [ ] `app/views/submissions/_send_sms_button.html.erb` is a functional button (not Pro-gated tooltip) -- [ ] `app/views/users/_role_select.html.erb` has no disabled options or Pro upsell link -- [ ] `app/views/accounts/show.html.erb` has no console-redirect Pro gates on Decline/Delegate toggles - -### Google SSO -- [ ] `app/views/sso_settings/index.html.erb` shows the Google SSO config form (client_id, client_secret, allowed_domains) -- [ ] `app/views/devise/sessions/_omniauthable.html.erb` has the "Sign in with Google" button -- [ ] `app/views/sso_settings/_placeholder.html.erb` does not exist (was replaced by the real form) -- [ ] OmniAuth routes for `auth/google_oauth2` are present in `config/routes.rb` - -### E-Signature Settings -- [ ] `app/views/esign_settings/_default_signature_row.html.erb` does not exist -- [ ] `config/locales/i18n.yml` has no `wabosign_trusted_signature` or `sign_documents_with_trusted_certificate_*` keys - -### Social / Extras -- [ ] `app/views/shared/_github.html.erb` does not exist (no hardcoded star count) -- [ ] `app/views/shared/_navbar.html.erb` does not render `shared/github` or `shared/github_button` -- [ ] `app/views/shared/_settings_nav.html.erb` has no Discord or AI Assistant links in support channels -- [ ] `config/locales/i18n.yml` has no `discord_community` or `ai_assistant` keys - -### SMS (independently developed) -- [ ] `app/views/sms_settings/index.html.erb` is the full provider form (not placeholder) -- [ ] `lib/sms.rb` exists with all 4 providers (BulkVS, Twilio, VoIP.ms, SignalWire) -- [ ] `lib/sms/providers/` directory exists with all 4 provider implementations -- [ ] `app/controllers/sms_settings_controller.rb` handles `test_message` action -- [ ] `app/models/encrypted_config.rb` has `SMS_CONFIGS_KEY = 'sms_configs'` constant -- [ ] `config/routes.rb` has the SMS routes with `test_message` collection route +- [ ] The rendered WaboSign "W" mark *looks* right (checksum proves the file is + unchanged; a human confirms it is the intended asset, e.g. after a + deliberate brand update + baseline regen). +- [ ] Upstream did not introduce a genuinely **new** feature or freemium gate + that needs a fork-policy decision (free it, and add an invariant) — skim + the merge diff for new `multitenant?` / Pro / Console / placeholder code. +- [ ] New upstream **UI strings** read correctly after the sweep (the rename is + mechanical; some phrasings need a human's rebranding nuance). diff --git a/bin/fork-check b/bin/fork-check index 8eeb478d..7bb0289f 100755 --- a/bin/fork-check +++ b/bin/fork-check @@ -15,6 +15,7 @@ require 'find' require 'set' require 'yaml' +require 'digest' ROOT = File.expand_path('..', __dir__) MANIFEST = File.join(ROOT, 'config/fork_invariants.yml') @@ -86,6 +87,7 @@ end manifest = YAML.safe_load(File.read(MANIFEST)) || {} violations = [] +warnings = [] # 1. Files that must exist (fork code + brand assets upstream tends to delete). Array(manifest['must_exist']).each do |path| @@ -219,6 +221,49 @@ else end end +# 9. Brand-asset checksums: each file in config/brand_assets.sha256 must exist +# and match its recorded hash, or an upstream merge silently overwrote our +# "W" mark. Binary, so it bypasses the text sweep — this is the only guard. +BRAND_BASELINE = 'config/brand_assets.sha256' +baselined = [] +if File.exist?(abs(BRAND_BASELINE)) + read_lines(abs(BRAND_BASELINE)).each do |line| + next if line.strip.empty? || line.lstrip.start_with?('#') + + digest, path = line.strip.split(/\s+/, 2) + next if digest.nil? || path.nil? + + baselined << path + unless File.exist?(abs(path)) + violations << "brand_asset: missing baselined asset: #{path} (restore: git checkout ORIG_HEAD -- #{path})" + next + end + actual = Digest::SHA256.file(abs(path)).hexdigest + next if actual == digest + + violations << "brand_asset: #{path} checksum mismatch — overwritten? " \ + "(restore: git checkout ORIG_HEAD -- #{path}, or regenerate the baseline if intentional)" + end +end + +# 10. Glob detector (warn-level): a public/ image that looks like a brand asset +# but is not in the baseline — upstream may have added it; decide whether to +# rebrand + baseline it, or leave it. Never fails the build. +brand_globs = %w[public/favicon* public/logo* public/apple-touch-icon*] +brand_globs.each do |g| + Dir.glob(abs(g)).each do |p| + next unless File.file?(p) + + rel = p.sub(%r{\A#{Regexp.escape(ROOT)}/}, '') + next if baselined.include?(rel) + + warnings << "brand-asset candidate not in #{BRAND_BASELINE}: #{rel} " \ + '(add the WaboSign version to the baseline, or confirm it is upstream-neutral)' + end +end + +warnings.each { |w| warn "fork-check: warning: #{w}" } unless warnings.empty? + if violations.empty? puts 'fork-check: ok' exit 0 diff --git a/bin/rebrand-check b/bin/rebrand-check index 2376d97a..0542c84a 100755 --- a/bin/rebrand-check +++ b/bin/rebrand-check @@ -57,6 +57,7 @@ ALLOW_FILES = Set.new([ # (e.g. lib/docuseal.rb, docuseal-form) as markers to assert on. 'bin/fork-check', 'config/fork_invariants.yml', + 'config/brand_assets.sha256', 'lib/wabosign.rb', 'lib/docuseal.rb', # Migration that finds rows by the legacy docuseal_aatl name. diff --git a/bin/rebrand-sync b/bin/rebrand-sync index 5aca8331..cf9782b3 100755 --- a/bin/rebrand-sync +++ b/bin/rebrand-sync @@ -55,6 +55,7 @@ DENY_PATHS = Set.new([ # marker), or the guard would silently stop catching that regression. 'bin/fork-check', 'config/fork_invariants.yml', + 'config/brand_assets.sha256', # WaboSign brand logo files — must never be touched by the sweep; # restored from ORIG_HEAD by bin/sync-upstream after an upstream merge. 'public/favicon.svg', diff --git a/bin/sync-upstream b/bin/sync-upstream index bedb2350..986dca9b 100755 --- a/bin/sync-upstream +++ b/bin/sync-upstream @@ -56,35 +56,67 @@ echo "=== Merging into master ===" git checkout master git merge --no-ff "sync/upstream-$TAG" -m "Merge upstream $TAG into master" -echo "=== Restoring WaboSign binary assets overwritten by merge ===" -# Merging an upstream tag may overwrite our brand logo files that rebrand-sync -# cannot protect (they are binary / opaque-image and bypass the text sweep). -# Restore them from pre-merge master (ORIG_HEAD). -LOGO_FILES=( - public/favicon.svg - public/favicon.ico - public/favicon-16x16.png - public/favicon-32x32.png - public/favicon-96x96.png - public/logo.svg -) -for f in "${LOGO_FILES[@]}"; do - if git show ORIG_HEAD:"$f" &>/dev/null 2>&1; then +echo "=== Restoring WaboSign brand assets overwritten by merge ===" +# Merging an upstream tag may overwrite our brand files that rebrand-sync cannot +# protect (binary / opaque-image, they bypass the text sweep). The authoritative +# list lives in config/brand_assets.sha256 — derive it from there so a new brand +# file added to the baseline is automatically restored too. Restore from the +# pre-merge master (ORIG_HEAD). +RESTORED=() +mapfile -t BRAND_FILES < <(awk '!/^#/ && NF {print $2}' config/brand_assets.sha256) +for f in "${BRAND_FILES[@]}"; do + if git show "ORIG_HEAD:$f" &>/dev/null; then git checkout ORIG_HEAD -- "$f" + RESTORED+=("$f") echo " restored: $f" fi done echo "=== Catching new upstream files (post-merge sweep) ===" bin/rebrand-sync -bin/rebrand-check + +echo "=== Verifying invariants ===" +# Both checks must pass before this sync can be considered clean. With +# `set -e` a failure already aborts, but wrap them so the operator/agent sees a +# clear remediation path and the half-merged tree is left in place to inspect. +if ! bin/rebrand-check; then + echo "" >&2 + echo "!! rebrand-check failed: un-rebranded DocuSeal text survived the sweep." >&2 + echo " Inspect the lines above; if a token must be preserved, add it to" >&2 + echo " PRESERVE (bin/rebrand-sync) AND ALLOW_PATTERNS (bin/rebrand-check)." >&2 + echo " The merge is left in place for you to fix; do NOT push until green." >&2 + exit 1 +fi +if ! bin/fork-check; then + echo "" >&2 + echo "!! fork-check failed: a fork invariant was broken by this merge." >&2 + echo " Each violation above names the file + the reason (from" >&2 + echo " config/fork_invariants.yml's why:). Typical fixes:" >&2 + echo " - re-introduced gate -> remove it" >&2 + echo " - brand asset overwritten -> git checkout ORIG_HEAD -- " >&2 + echo " - new upstream feature -> add a scoped invariant to the manifest" >&2 + echo " The merge is left in place for you to fix; do NOT push until green." >&2 + exit 1 +fi + +if [ "${RUN_TESTS:-0}" = "1" ]; then + echo "=== Running test suite (RUN_TESTS=1) ===" + bundle exec rspec +fi echo "" echo "============================================================" -echo "Sync of $TAG complete." -echo "Next steps:" -echo " 1. bundle install && yarn install" -echo " 2. Run tests: bundle exec rspec" -echo " 3. Tag: git tag wabosign-synced-with-$TAG" -echo " 4. Push: git push origin master --tags" +echo "Sync of $TAG complete. Invariants: rebrand-check + fork-check PASS." +echo "Brand assets restored: ${#RESTORED[@]}" +if [ "${RUN_TESTS:-0}" != "1" ]; then + echo "Tests: not run (set RUN_TESTS=1 to run rspec, or rely on CI)." +fi +echo "" +echo "Remaining human-judgment review (see REBRANDING.md \"Human-judgment\"):" +echo " - Does the rendered WaboSign 'W' mark look right?" +echo " - Did upstream add a genuinely new feature/gate needing a fork policy call?" +echo " - Do new upstream UI strings need rebranding nuance the sweep can't infer?" +echo "" +echo "Then: bundle install && yarn install, tag, and push:" +echo " git tag wabosign-synced-with-$TAG && git push origin master --tags" echo "============================================================" diff --git a/config/brand_assets.sha256 b/config/brand_assets.sha256 new file mode 100644 index 00000000..b2f01ce5 --- /dev/null +++ b/config/brand_assets.sha256 @@ -0,0 +1,23 @@ +# WaboSign brand-asset checksum baseline. +# +# bin/fork-check verifies every file listed here exists and matches its sha256, +# failing if one was silently overwritten by an upstream merge (the "W" mark +# replaced by DocuSeal's logo). bin/sync-upstream restores these from ORIG_HEAD +# after a merge. This is the single source of truth for "what is a brand asset": +# .gitattributes (-merge) and the sync-upstream restore list both derive from it. +# +# google_g.svg is intentionally NOT here — it is Google's logo for the SSO +# button, legitimately shared with upstream, not a WaboSign rebrand target. +# +# Regenerate after an intentional brand change (review the diff deliberately): +# sha256sum $(awk '!/^#/{print $2}' config/brand_assets.sha256) > /tmp/b && \ +# { grep '^#' config/brand_assets.sha256; cat /tmp/b; } > config/brand_assets.sha256 +# +0a269a2b86c2413c08c10e8ab75ce0349d9cc59cc470613696d54480a4688ef0 public/favicon.svg +b78aa6db776b326df6ac48ba75f50541fb3cbcd09c78dc99e14a695ab80b84d6 public/favicon.ico +8fd83aa2a2f56b993e680a1babb5d6984d3ec8b72b40ac470cf4f3acff9f9ed9 public/favicon-16x16.png +e8d9b8fe8346b12dd34eed410a4113992b7708d844441b15f607c49c0574f825 public/favicon-32x32.png +204023b29fdf57cf45ef2352ddb56964ed35ab1be7d58c0dd3ab1b2a45cc6671 public/favicon-96x96.png +0a269a2b86c2413c08c10e8ab75ce0349d9cc59cc470613696d54480a4688ef0 public/logo.svg +9b9507aaefcbe06b58c464724d418940c610df480e75b4cc1a58d1939d5d2142 public/apple-touch-icon.png +9b9507aaefcbe06b58c464724d418940c610df480e75b4cc1a58d1939d5d2142 public/apple-touch-icon-precomposed.png