Skip to content

Instantly share code, notes, and snippets.

@tpaksu
Created March 17, 2026 00:02
Show Gist options
  • Select an option

  • Save tpaksu/004a29734c7db6e297ce57d1d21d48f9 to your computer and use it in GitHub Desktop.

Select an option

Save tpaksu/004a29734c7db6e297ce57d1d21d48f9 to your computer and use it in GitHub Desktop.
WCC Support: Build comparison script for verifying repo restructuring safety
#!/bin/bash
#
# Compares the unminified JS/CSS build output between trunk and the current branch
# to verify that the repo restructuring did not change app behavior.
#
# Usage: bin/compare-builds.sh
#
# Requires: nvm, node (via .nvmrc)
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
WORK_DIR=$(mktemp -d /tmp/wcc-build-compare.XXXXXX)
CURRENT_BRANCH=$(git -C "$PROJECT_DIR" rev-parse --abbrev-ref HEAD)
cleanup() {
echo ""
echo "Cleaning up..."
git -C "$PROJECT_DIR" checkout -- package-lock.json 2>/dev/null || true
git -C "$PROJECT_DIR" checkout "$CURRENT_BRANCH" 2>/dev/null || true
rm -rf "$WORK_DIR"
}
trap cleanup EXIT
echo "============================================"
echo " WCC Support Build Comparison"
echo "============================================"
echo ""
echo "Comparing: trunk vs ${CURRENT_BRANCH}"
echo "Work dir: ${WORK_DIR}"
echo ""
# Source nvm
export NVM_DIR="$HOME/.nvm"
# shellcheck disable=SC1091
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
cd "$PROJECT_DIR"
# --- Build from trunk ---
echo "=== Building from trunk ==="
git stash -q 2>/dev/null || true
git checkout trunk -q
nvm use -q
npm install --silent 2>/dev/null
cross-env NODE_ENV=development npx webpack --config webpack.production.config.js 2>&1 | tail -1
cp svn/mc/tools/woo-connect/wcc-support-app.js "$WORK_DIR/old.js"
cp svn/mc/tools/woo-connect/wcc-support-app.css "$WORK_DIR/old.css"
echo "Saved trunk build to ${WORK_DIR}/old.*"
echo ""
# --- Build from current branch ---
echo "=== Building from ${CURRENT_BRANCH} ==="
git checkout -- package-lock.json
git checkout "$CURRENT_BRANCH" -q
nvm use -q
npm install --silent 2>/dev/null
npm run build:dev 2>&1 | tail -1
cp dist/wcc-support-app.js "$WORK_DIR/new.js"
cp dist/wcc-support-app.css "$WORK_DIR/new.css"
echo "Saved ${CURRENT_BRANCH} build to ${WORK_DIR}/new.*"
echo ""
# --- Compare ---
echo "============================================"
echo " Results"
echo "============================================"
echo ""
# CSS comparison (strip source maps)
sed '/sourceMappingURL/d' "$WORK_DIR/old.css" > "$WORK_DIR/old.css.stripped"
sed '/sourceMappingURL/d' "$WORK_DIR/new.css" > "$WORK_DIR/new.css.stripped"
if diff -q "$WORK_DIR/old.css.stripped" "$WORK_DIR/new.css.stripped" > /dev/null 2>&1; then
echo "CSS (sans source maps): IDENTICAL"
else
echo "CSS (sans source maps): DIFFERENT"
diff "$WORK_DIR/old.css.stripped" "$WORK_DIR/new.css.stripped" | head -20
fi
echo ""
# JS comparison
OLD_JS_SIZE=$(wc -c < "$WORK_DIR/old.js")
NEW_JS_SIZE=$(wc -c < "$WORK_DIR/new.js")
echo "JS file sizes: trunk=${OLD_JS_SIZE} bytes, branch=${NEW_JS_SIZE} bytes"
echo ""
# Normalize and extract app code for comparison
python3 - "$WORK_DIR" <<'PYEOF'
import re, sys
work_dir = sys.argv[1]
with open(f'{work_dir}/old.js', 'r') as f:
old = f.read()
with open(f'{work_dir}/new.js', 'r') as f:
new = f.read()
def normalize(text):
# Remove inline source maps
text = re.sub(r'/\*#\s*sourceMappingURL=data:.*?\*/', '', text)
text = re.sub(r'//# sourceMappingURL=data:.*', '', text)
# Normalize paths: client/ -> app/, client/stylesheets/ -> assets/stylesheets/
text = text.replace('./client/stylesheets/', './assets/stylesheets/')
text = text.replace('./client/', './app/')
text = text.replace('client/stylesheets/', 'assets/stylesheets/')
text = text.replace('client/', 'app/')
# Normalize emotion target hashes
text = re.sub(r'target:\s*"[a-z0-9]+"', 'target: "HASH"', text)
# Remove webpack banner comments
text = re.sub(r'/\*![*!\\\\]+\n', '', text)
text = re.sub(r'\\\\[*!]+/', '', text)
text = re.sub(r'!\*\*+!', '', text)
return text
old_n = normalize(old)
new_n = normalize(new)
# Extract all app/ module code blocks
def extract_all_app_code(text):
chunks = []
parts = text.split('"./app/')
for part in parts[1:]:
mod_end = part.find('\n/***/ "', 100)
node_end = part.find('"./node_modules/', 100)
end = len(part)
if mod_end != -1:
end = min(end, mod_end)
if node_end != -1:
end = min(end, node_end)
chunks.append(part[:end])
return '\n'.join(chunks)
old_app = extract_all_app_code(old_n)
new_app = extract_all_app_code(new_n)
print(f'App code size: trunk={len(old_app)} chars, branch={len(new_app)} chars')
print(f'App code lines: trunk={len(old_app.splitlines())}, branch={len(new_app.splitlines())}')
print()
if old_app == new_app:
print('APP CODE (normalized): IDENTICAL')
print()
print('All app module code is byte-for-byte identical after normalizing')
print('path references and emotion CSS hashes.')
else:
old_lines = old_app.splitlines()
new_lines = new_app.splitlines()
diffs = []
for i in range(min(len(old_lines), len(new_lines))):
if old_lines[i].strip() != new_lines[i].strip():
diffs.append((i + 1, old_lines[i].strip()[:120], new_lines[i].strip()[:120]))
print(f'APP CODE DIFFERENCES: {len(diffs)} line(s)')
print()
for line_num, old_line, new_line in diffs:
print(f' Line {line_num}:')
print(f' trunk: {old_line}')
print(f' branch: {new_line}')
print()
# Polyfill analysis
has_babel_old = 'babel-polyfill' in old or 'babel_polyfill' in old
has_babel_new = 'babel-polyfill' in new or 'babel_polyfill' in new
print('POLYFILL CHANGES:')
print(f' babel-polyfill in trunk: {has_babel_old}')
print(f' babel-polyfill in branch: {has_babel_new}')
if has_babel_old and not has_babel_new:
print(' -> babel-polyfill replaced with core-js 3 (intentional)')
print()
# Summary
print('============================================')
print(' Summary')
print('============================================')
print()
categories = []
categories.append(('CSS (sans source maps)', 'IDENTICAL' if open(f'{work_dir}/old.css.stripped').read() == open(f'{work_dir}/new.css.stripped').read() else 'DIFFERENT'))
if old_app == new_app:
categories.append(('JS app code', 'IDENTICAL'))
elif len(diffs) <= 5:
categories.append(('JS app code', f'{len(diffs)} line(s) differ (see above)'))
else:
categories.append(('JS app code', f'{len(diffs)} lines differ (REVIEW NEEDED)'))
if has_babel_old and not has_babel_new:
categories.append(('JS polyfills', 'babel-polyfill -> core-js 3 (intentional)'))
else:
categories.append(('JS polyfills', 'unchanged'))
categories.append(('Source maps', 'path references updated (expected)'))
categories.append(('Emotion hashes', 'changed due to file path rename (no visual impact)'))
categories.append(('Webpack banners', 'width differs due to longer path (cosmetic)'))
for name, result in categories:
print(f' {name}: {result}')
PYEOF
echo ""
echo "============================================"
echo " Done"
echo "============================================"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment