Files
insights/bin/insights-worktree
zeekay 10e6fec4c0 debrand: the brand token is INSIGHTS_, and a wrong one stops the container
The rule table mapped HOG_ -> FN_ ahead of any POSTHOG rule, so POSTHOG_FOO
split into POST + HOG_ and came out POSTFN_FOO. Nothing sets that. The live
pods set INSIGHTS_SKIP_MIGRATION_CHECKS, which bin/migrate-check reads on the
container entrypoint, so the misspelling does not degrade a feature -- it runs
migrate --check against a database whose history this tree does not share and
the container never starts.

Both replacements are measured against the shipped tree rather than chosen:
every HOG_ name with a counterpart there is spelled INSIGHTS_ (HOG_QL ->
INSIGHTS_QL, HOG_FUNCTION_TEMPLATES -> INSIGHTS_FUNCTION_TEMPLATES, 12 of 12
determinate cases), and FN_ appears in it nowhere. Upstream carries 95 HOG_
tokens and zero FN_ ones, so repairing FN_ back out is exactly the inverse of
the bad rule and cannot collide with an upstream name.

The rule is fixed for the next convergence, and the tree it already produced is
repaired in the same commit -- the codemod is idempotent against its own output,
so it cannot undo this itself.
2026-08-01 17:42:28 -07:00

448 lines
17 KiB
Bash
Executable File

#!/bin/bash
set -e
# Source utility functions
source "$(dirname "$0")/helpers/_utils.sh"
# Parse arguments
COMMAND=${1:-"help"}
shift || true
# Parse remaining arguments - flags and positional args
BRANCH_NAME=""
BASE_BRANCH="master"
CUSTOM_DIR=""
while [[ $# -gt 0 ]]; do
case $1 in
--dir)
if [[ -z "$2" ]]; then
error "Missing argument for --dir flag"
exit 1
fi
CUSTOM_DIR="$2"
shift 2
;;
*)
# First non-flag arg is branch name, second is base branch
if [[ -z "$BRANCH_NAME" ]]; then
BRANCH_NAME="$1"
elif [[ "$BASE_BRANCH" == "master" ]]; then
BASE_BRANCH="$1"
fi
shift
;;
esac
done
# Worktree base directory (configurable via INSIGHTS_WORKTREE_BASE)
WORKTREE_BASE="${INSIGHTS_WORKTREE_BASE:-${HOME}/.worktrees/insights}"
# Copy helper that uses copy-on-write clones when available (APFS/Btrfs)
# Returns 0 on success, 1 if CoW not available and require_cow is true
clone_path() {
local source_path="$1"
local destination_path="$2"
local require_cow="${3:-false}" # If true, fail when CoW unavailable
local cp_args=("-a")
if [[ "$(uname -s)" == "Darwin" ]]; then
# macOS: cp -c fails if cloning not supported
if cp -c "${cp_args[@]}" "$source_path" "$destination_path" 2>/dev/null; then
return 0
fi
else
# Linux: --reflink=auto silently falls back to regular copy,
# so use --reflink=always when CoW is required
if [[ "$require_cow" == "true" ]]; then
if cp --reflink=always "${cp_args[@]}" "$source_path" "$destination_path" 2>/dev/null; then
return 0
fi
else
if cp --reflink=auto "${cp_args[@]}" "$source_path" "$destination_path" 2>/dev/null; then
return 0
fi
fi
fi
# CoW not available or failed - either fall back to regular copy or skip
if [[ "$require_cow" == "true" ]]; then
return 1
fi
cp "${cp_args[@]}" "$source_path" "$destination_path"
}
# Clean up a stale worktree directory left over from a previous failed run.
# Prunes git's internal tracking first, then removes the leftover directory —
# but only if git doesn't consider it an active worktree.
cleanup_stale_worktree() {
local worktree_path="$1"
git worktree prune 2>/dev/null || true
if [[ -d "${worktree_path}" ]]; then
# Safety: don't delete a directory that git still tracks as an active worktree
if git worktree list | awk '{print $1}' | grep -qxF "${worktree_path}"; then
fatal "Directory ${worktree_path} is an active worktree — cannot overwrite"
fi
print_color yellow "Removing stale worktree directory: ${worktree_path}"
rm -rf "${worktree_path}"
fi
}
# Function to create a new worktree
create_worktree() {
if [ -z "$BRANCH_NAME" ]; then
error "Branch name required"
echo "Usage: $0 create <branch-name> [base-branch] [--dir <dirname>]"
exit 1
fi
# Use custom dirname if provided, otherwise use branch name
local dirname="${CUSTOM_DIR:-${BRANCH_NAME}}"
local worktree_path="${WORKTREE_BASE}/${dirname}"
# Check if base branch exists
if ! git show-ref --verify --quiet "refs/heads/${BASE_BRANCH}" &&
! git show-ref --verify --quiet "refs/remotes/origin/${BASE_BRANCH}"; then
error "Base branch '${BASE_BRANCH}' not found locally or on origin"
echo "Available branches:"
git branch -a | grep -v HEAD | sed 's/^[* ]*//' | sed 's/remotes\/origin\///' | sort -u | head -20
exit 1
fi
success "Creating worktree for NEW branch: ${BRANCH_NAME} (from ${BASE_BRANCH})"
cleanup_stale_worktree "${worktree_path}"
# Create worktree with new branch from specified base
mkdir -p "${WORKTREE_BASE}"
git worktree add "${worktree_path}" -b "${BRANCH_NAME}" "${BASE_BRANCH}"
# Use common setup function
setup_worktree_environment "${worktree_path}"
}
# Function to remove a worktree
remove_worktree() {
if [ -z "$BRANCH_NAME" ]; then
error "Branch name required"
echo "Usage: $0 remove <branch-name>"
exit 1
fi
print_color yellow "Removing worktree for: ${BRANCH_NAME}"
# Find the actual worktree path using git worktree list
local worktree_path=$(git worktree list | grep -E "\[${BRANCH_NAME}\]$" | awk '{print $1}')
if [ -n "$worktree_path" ]; then
# Remove using the actual path found by git
git worktree remove "${worktree_path}" --force
success "Worktree removed: ${worktree_path}"
else
# Fallback: let git find it by branch name
if git worktree remove "${BRANCH_NAME}" --force 2>/dev/null; then
success "Worktree removed"
else
error "Could not find worktree for branch: ${BRANCH_NAME}"
echo "Available worktrees:"
git worktree list
exit 1
fi
fi
}
# Function to checkout existing branch in worktree
checkout_worktree() {
if [ -z "$BRANCH_NAME" ]; then
error "Branch name required"
echo "Usage: $0 checkout <branch-name> [--dir <dirname>]"
exit 1
fi
# Check if worktree already exists for this branch
local existing_path=$(git worktree list | grep -E "\[${BRANCH_NAME}\]$" | awk '{print $1}')
if [[ -n "$existing_path" ]]; then
success "Worktree already exists for branch: ${BRANCH_NAME}"
success "Location: ${existing_path}"
return 0
fi
# Use custom dirname if provided, otherwise use branch name
local dirname="${CUSTOM_DIR:-${BRANCH_NAME}}"
local worktree_path="${WORKTREE_BASE}/${dirname}"
# Check if branch exists
if ! git show-ref --verify --quiet "refs/heads/${BRANCH_NAME}" &&
! git show-ref --verify --quiet "refs/remotes/origin/${BRANCH_NAME}"; then
error "Branch '${BRANCH_NAME}' not found locally or on origin"
echo "Available branches:"
git branch -a | grep -v HEAD | sed 's/^[* ]*//' | sed 's/remotes\/origin\///' | sort -u | head -20
exit 1
fi
success "Creating worktree for EXISTING branch: ${BRANCH_NAME}"
cleanup_stale_worktree "${worktree_path}"
# Create worktree
mkdir -p "${WORKTREE_BASE}"
# Try to add worktree (will use existing branch)
if git worktree add "${worktree_path}" "${BRANCH_NAME}" 2>/dev/null; then
success "Using local branch ${BRANCH_NAME}"
else
# Try with origin branch
git worktree add "${worktree_path}" "origin/${BRANCH_NAME}" || fatal "Failed to create worktree"
success "Tracking origin/${BRANCH_NAME}"
fi
# Continue with standard setup
setup_worktree_environment "${worktree_path}"
}
# Function to checkout PR in worktree
checkout_pr_worktree() {
local pr_number="$BRANCH_NAME" # Reusing BRANCH_NAME variable for PR number
if [ -z "$pr_number" ]; then
error "PR number required"
echo "Usage: $0 pr <pr-number> [--dir <dirname>]"
exit 1
fi
# Check if gh and jq are installed
if ! command_exists gh; then
fatal "GitHub CLI (gh) is required. Install with: brew install gh"
fi
if ! command_exists jq; then
fatal "jq is required for parsing JSON. Install with: brew install jq"
fi
print_color blue "Fetching PR #${pr_number} information…"
# Get PR info
local pr_info=$(gh pr view "${pr_number}" --json headRefName,author,title 2>/dev/null || echo "")
if [ -z "$pr_info" ]; then
fatal "Could not fetch PR #${pr_number}. Make sure you're authenticated with gh."
fi
local pr_branch=$(echo "$pr_info" | jq -r '.headRefName')
local pr_author=$(echo "$pr_info" | jq -r '.author.login')
local pr_title=$(echo "$pr_info" | jq -r '.title' | cut -c1-50)
# Use custom dirname if provided, otherwise use pr-number-author format
local worktree_dirname="${CUSTOM_DIR:-pr-${pr_number}-${pr_author}}"
local worktree_path="${WORKTREE_BASE}/${worktree_dirname}"
success "Checking out PR #${pr_number}: ${pr_title}…"
print_color blue "Author: ${pr_author}, Branch: ${pr_branch}"
cleanup_stale_worktree "${worktree_path}"
# Create worktree
mkdir -p "${WORKTREE_BASE}"
# Fetch the PR and create worktree
git fetch origin "pull/${pr_number}/head:pr-${pr_number}" || fatal "Failed to fetch PR #${pr_number}"
git worktree add "${worktree_path}" "pr-${pr_number}" || fatal "Failed to create worktree for PR #${pr_number}"
# Continue with standard setup
setup_worktree_environment "${worktree_path}"
}
# Common function to setup worktree environment
setup_worktree_environment() {
local worktree_path="$1"
success "Worktree created at: ${worktree_path}"
# Get the main repo path
local main_repo=$(git worktree list | head -n1 | awk '{print $1}')
# Setup Flox environment for this worktree
cd "${worktree_path}"
# Copy .flox env files and .envrc for direnv
mkdir -p "${worktree_path}/.flox/env"
clone_path "${main_repo}/.flox/env/manifest.toml" "${worktree_path}/.flox/env/"
[[ -f "${main_repo}/.flox/env/on-activate.sh" ]] && \
clone_path "${main_repo}/.flox/env/on-activate.sh" "${worktree_path}/.flox/env/"
clone_path "${main_repo}/.envrc" "${worktree_path}/"
# Copy .vscode/settings.json if it exists
if [[ -f "${main_repo}/.vscode/settings.json" ]]; then
print_color blue "Copying .vscode/settings.json…"
mkdir -p "${worktree_path}/.vscode"
clone_path "${main_repo}/.vscode/settings.json" "${worktree_path}/.vscode/"
fi
# Copy .env and .env.* files if they exist
local env_files_found=false
while IFS= read -r -d '' env_file; do
if [[ "${env_files_found}" == "false" ]]; then
env_files_found=true
print_color blue "Copying .env files…"
fi
clone_path "${env_file}" "${worktree_path}/"
done < <(find "${main_repo}" -maxdepth 1 \( -name ".env" -o -name ".env.*" \) -type f -print0)
# Clone flox cache venv using copy-on-write if available (the real Python venv used by flox)
if [[ -d "${main_repo}/.flox/cache/venv" && ! -e "${worktree_path}/.flox/cache/venv" ]]; then
mkdir -p "${worktree_path}/.flox/cache"
if clone_path "${main_repo}/.flox/cache/venv" "${worktree_path}/.flox/cache/venv" true; then
print_color blue "Cloned .flox/cache/venv (copy-on-write)"
# Fix VIRTUAL_ENV path in activate scripts — the cloned venv has the
# source repo's path hardcoded, which puts the wrong bin/ on PATH.
local venv_bin="${worktree_path}/.flox/cache/venv/bin"
local old_venv="${main_repo}/.flox/cache/venv"
local new_venv="${worktree_path}/.flox/cache/venv"
local sed_inplace=(-i)
[[ "$(uname -s)" == "Darwin" ]] && sed_inplace=(-i '')
for activate_script in "${venv_bin}"/activate*; do
[[ -f "$activate_script" ]] && LC_ALL=C sed "${sed_inplace[@]}" "s|${old_venv}|${new_venv}|g" "$activate_script"
done
print_color blue "Patched venv activate scripts for worktree path"
else
print_color yellow "Skipping venv copy (CoW not available) — flox activate will create it"
fi
fi
# Clone node_modules using copy-on-write if available
if [[ -d "${main_repo}/node_modules" && ! -e "${worktree_path}/node_modules" ]]; then
if clone_path "${main_repo}/node_modules" "${worktree_path}/node_modules" true; then
print_color blue "Cloned node_modules (copy-on-write)"
else
print_color yellow "Skipping node_modules copy (CoW not available) — pnpm install will create it"
fi
fi
# Fix rdkafka dylib paths for worktree portability (macOS only)
if [[ "$(uname -s)" == "Darwin" && -d "${worktree_path}/node_modules" ]]; then
(cd "${worktree_path}" && bin/fix-rdkafka-paths)
fi
# Initialize Flox in the new worktree
cd "${worktree_path}"
print_color blue "Initializing Flox environment (this will share packages from cache)…"
# Check if direnv is installed and set up
if command_exists direnv; then
print_color blue "Detected direnv - allowing automatic activation…"
# Clean up stale environment variables before direnv activation
env -u VIRTUAL_ENV -u PYTHONPATH -u CONDA_DEFAULT_ENV direnv allow .
# direnv will automatically activate flox and run uv sync
else
# Manual activation if direnv not present
print_color blue "Installing Python dependencies via uv sync (this may take a minute)…"
# Clean up any stale environment variables that might conflict
unset VIRTUAL_ENV PYTHONPATH CONDA_DEFAULT_ENV
flox activate --trust -- uv sync
fi
success "✅ Worktree setup complete!"
echo ""
echo "To start working:"
print_color blue " cd ${worktree_path}"
print_color blue " bin/start"
echo ""
print_color yellow "Tip: only running the app needs this full setup — for edit+commit work, plain 'git worktree add' is enough (hooks borrow the main clone's toolchain)."
}
# Function to list all worktrees
list_worktrees() {
print_color green "All Insights Worktrees:"
echo ""
printf "%-30s %-60s %-10s\n" "Branch" "Path" "Location"
printf "%-30s %-60s %-10s\n" "------" "----" "--------"
git worktree list | while read -r line; do
worktree_path=$(echo "$line" | awk '{print $1}')
branch=$(echo "$line" | sed -n 's/.*\[\(.*\)\].*/\1/p')
# Determine location indicator
local location_indicator=""
if [[ "$worktree_path" == "${WORKTREE_BASE}"* ]]; then
location_indicator="current"
else
location_indicator="other"
fi
printf "%-30s %-60s %-10s\n" "$branch" "$worktree_path" "$location_indicator"
done
echo ""
print_color yellow "Legend:"
print_color yellow " current = in current worktree base (${WORKTREE_BASE})"
print_color yellow " other = in different location"
}
# Main command handling
case "$COMMAND" in
create)
create_worktree
;;
remove|rm)
remove_worktree
;;
list|ls)
list_worktrees
;;
checkout)
checkout_worktree
;;
pr)
checkout_pr_worktree
;;
help|*)
echo "Insights Worktree Manager"
echo ""
echo "Sets up a full (\"heavy\") worktree with its own flox env and deps for"
echo "running the app (bin/start). For quick edits and commits you don't need"
echo "this: a plain 'git worktree add <path> <branch>' gives a light worktree"
echo "where pre-commit hooks work automatically (the toolchain is borrowed"
echo "from the main clone while lockfiles match)."
echo ""
echo "Usage: $0 <command> [arguments]"
echo ""
echo "Commands:"
echo " create <branch> [base-branch] [--dir <dirname>] - Create NEW branch in worktree"
echo " checkout <branch> [--dir <dirname>] - Checkout EXISTING branch in worktree"
echo " pr <number> [--dir <dirname>] - Checkout pull request in worktree"
echo " remove <branch> - Remove worktree"
echo " list - List all worktrees"
echo ""
echo " Note: Use 'phw switch <branch>' to switch to existing worktree"
echo ""
echo "Examples:"
echo " $0 create haacked/new-feature # Create new branch from master (default)"
echo " $0 create haacked/fix-bug main # Create new branch from main"
echo " $0 create haacked/first-branch --dir my-theme # Create in custom directory"
echo " $0 checkout main # Checkout existing branch"
echo " $0 checkout haacked/existing --dir my-theme # Checkout to custom directory"
echo " $0 pr 12345 # Checkout PR #12345"
echo " $0 pr 12345 --dir review-theme # Checkout PR to custom directory"
echo " $0 list # List all worktrees"
echo ""
echo "Working with worktrees:"
echo " cd ${WORKTREE_BASE}/<dirname> # Enter worktree directory"
echo " bin/start # Start Insights (standard command)"
echo " git checkout -b another-branch # Switch branches within same worktree"
echo ""
echo "Notes:"
echo " - Worktrees are created in: ${WORKTREE_BASE}/<branch-or-dirname>"
echo " - Use --dir to decouple directory name from branch name"
echo " - Useful for theme-based workflows with multiple related branches"
echo " - Each worktree has its own isolated Flox environment"
echo " - Use standard 'bin/start' command within each worktree"
echo ""
echo "Configuration:"
echo " - Set INSIGHTS_WORKTREE_BASE to customize worktree location"
echo " - Default: ~/.worktrees/insights"
echo ""
;;
esac