chore(shared): 🔧 Update shared configuration files and scripts

This commit is contained in:
Lilith 2026-01-16 19:59:29 -08:00
parent e55c876ddb
commit ce79d5b7d4

View file

@ -0,0 +1,495 @@
#!/bin/bash
set -uo pipefail
# Note: -e disabled because some operations like empty array iteration can fail
# Package Language-Based Reorganization Script
#
# Migrates all packages to language-based directories:
# - TypeScript packages → @ts/
# - Python packages → @py/
#
# Package names are updated to remove -ts/-py suffixes:
# - @lilith/queue-ts → @lilith/queue
# - lilith-queue-py → lilith-queue
#
# Usage: ./reorganize-by-language.sh [--dry-run] [--execute] [--verbose]
PACKAGES_ROOT="${PACKAGES_ROOT:-/var/home/lilith/Code/@packages}"
DRY_RUN=true
VERBOSE=false
EXECUTE=false
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
MAGENTA='\033[0;35m'
GRAY='\033[0;90m'
NC='\033[0m'
# Tracking
declare -A MOVE_PLAN=() # source_path -> target_path
declare -A OLD_TO_NEW_NAME=() # old_pkg_name -> new_pkg_name
declare -A PATH_TO_NAME=() # path -> package_name
declare -A PATH_TO_TYPE=() # path -> ts|py
# Categories that should NOT be migrated (monorepo, special)
SKIP_CATEGORIES=(
"scripts"
"tooling"
".deprecated"
".claude"
".git"
"node_modules"
"docs"
)
# Special handling for these packages
declare -A SPECIAL_TARGETS=(
# Monorepo stays together (but moves to @ts/)
["@ui-react"]="@ts/ui-react"
# Configs packages
["@configs-ts"]="@ts/configs"
["@configs-py"]="@py/configs"
["@configs-swift"]="@ts/configs-swift"
)
# Parse args
for arg in "$@"; do
case $arg in
--dry-run) DRY_RUN=true; EXECUTE=false ;;
--execute) EXECUTE=true; DRY_RUN=false ;;
--verbose) VERBOSE=true ;;
--help|-h)
echo "Usage: $0 [--dry-run] [--execute] [--verbose]"
echo ""
echo "Reorganizes @packages workspace to language-based directories:"
echo " - All TypeScript packages → @ts/"
echo " - All Python packages → @py/"
echo " - Package names updated to remove -ts/-py suffixes"
echo ""
echo "Options:"
echo " --dry-run Show what would change (default)"
echo " --execute Actually perform the migration"
echo " --verbose Show detailed progress"
exit 0
;;
esac
done
echo "=== Package Language-Based Reorganization ==="
echo "Root: $PACKAGES_ROOT"
$DRY_RUN && echo -e "${YELLOW}DRY RUN MODE - no changes will be made${NC}"
$EXECUTE && echo -e "${GREEN}EXECUTE MODE - changes will be applied${NC}"
echo ""
cd "$PACKAGES_ROOT" || {
echo -e "${RED}ERROR: Cannot cd to $PACKAGES_ROOT${NC}"
exit 1
}
# ============================================================================
# Helper Functions
# ============================================================================
log_verbose() {
if $VERBOSE; then
echo -e " ${GRAY}[verbose]${NC} $1"
fi
return 0
}
log_move() {
echo -e " ${BLUE}MOVE${NC}: $1$2"
}
log_rename() {
echo -e " ${MAGENTA}RENAME${NC}: $1$2"
}
log_skip() {
echo -e " ${GRAY}SKIP${NC}: $1 ${GRAY}($2)${NC}"
}
log_error() {
echo -e " ${RED}ERROR${NC}: $1"
}
log_success() {
echo -e " ${GREEN}${NC} $1"
}
# Check if category should be skipped
is_skip_category() {
local category="$1"
for skip in "${SKIP_CATEGORIES[@]}"; do
[[ "$category" == "$skip" ]] && return 0
done
return 1
}
# Get base name (strip -ts/-py suffix)
strip_lang_suffix() {
local name="$1"
if [[ "$name" == *-ts ]]; then
echo "${name%-ts}"
elif [[ "$name" == *-py ]]; then
echo "${name%-py}"
else
echo "$name"
fi
}
# Get new package name
compute_new_pkg_name() {
local old_name="$1"
local pkg_type="$2"
# TypeScript: @lilith/foo-ts → @lilith/foo
if [[ "$pkg_type" == "ts" ]]; then
if [[ "$old_name" == *-ts ]]; then
echo "${old_name%-ts}"
else
echo "$old_name"
fi
# Python: lilith-foo-py → lilith-foo
else
if [[ "$old_name" == *-py ]]; then
echo "${old_name%-py}"
else
echo "$old_name"
fi
fi
}
# Compute target directory for a package
compute_target_dir() {
local source_path="$1"
local pkg_type="$2"
local rel_path="${source_path#$PACKAGES_ROOT/}"
local category="${rel_path%%/*}"
local dir_name
dir_name=$(basename "$source_path")
local base_name
base_name=$(strip_lang_suffix "$dir_name")
# Check for special handling
if [[ -n "${SPECIAL_TARGETS[$category]:-}" ]]; then
echo "${SPECIAL_TARGETS[$category]}"
return
fi
# Standard handling: @category/pkg-ts → @ts/pkg
if [[ "$pkg_type" == "ts" ]]; then
echo "@ts/$base_name"
else
echo "@py/$base_name"
fi
}
# ============================================================================
# Phase 1: Create target directories
# ============================================================================
echo -e "${CYAN}[Phase 1/7] Creating target directories...${NC}"
if $EXECUTE; then
mkdir -p "@ts" "@py"
log_success "Created @ts/ and @py/"
else
echo " Would create: @ts/ and @py/"
fi
# ============================================================================
# Phase 2: Discover all packages
# ============================================================================
echo -e "\n${CYAN}[Phase 2/7] Discovering packages...${NC}"
ts_count=0
py_count=0
# Find TypeScript packages (package.json with name starting with @lilith/)
while IFS= read -r pkg_json; do
# Skip excluded paths
[[ "$pkg_json" == *"/node_modules/"* ]] && continue
[[ "$pkg_json" == *"/dist/"* ]] && continue
[[ "$pkg_json" == *"/.venv/"* ]] && continue
[[ "$pkg_json" == *"/_archived/"* ]] && continue
[[ "$pkg_json" == *"/tooling/"* ]] && continue
[[ "$pkg_json" == *"/scripts/"* ]] && continue
pkg_dir=$(dirname "$pkg_json")
[[ "$pkg_dir" == "$PACKAGES_ROOT" ]] && continue
pkg_name=$(jq -r '.name // empty' "$pkg_json" 2>/dev/null) || continue
[[ -z "$pkg_name" ]] && continue
[[ "$pkg_name" == "@packages/root" ]] && continue
# Only include @lilith/ scoped packages
[[ "$pkg_name" != @lilith/* ]] && continue
PATH_TO_NAME["$pkg_dir"]="$pkg_name"
PATH_TO_TYPE["$pkg_dir"]="ts"
((ts_count++))
log_verbose "TS: $pkg_name @ ${pkg_dir#$PACKAGES_ROOT/}"
done < <(find "$PACKAGES_ROOT" -name "package.json" -type f 2>/dev/null)
# Find Python packages (pyproject.toml with name starting with lilith-)
while IFS= read -r pyproject; do
[[ "$pyproject" == *"/node_modules/"* ]] && continue
[[ "$pyproject" == *"/.venv/"* ]] && continue
[[ "$pyproject" == *"/venv/"* ]] && continue
[[ "$pyproject" == *"/_archived/"* ]] && continue
pkg_dir=$(dirname "$pyproject")
pkg_name=$(grep -E '^name\s*=' "$pyproject" 2>/dev/null | head -1 | sed 's/.*=\s*"\([^"]*\)".*/\1/')
[[ -z "$pkg_name" ]] && continue
[[ "$pkg_name" != lilith-* ]] && continue
PATH_TO_NAME["$pkg_dir"]="$pkg_name"
PATH_TO_TYPE["$pkg_dir"]="py"
((py_count++))
log_verbose "PY: $pkg_name @ ${pkg_dir#$PACKAGES_ROOT/}"
done < <(find "$PACKAGES_ROOT" -name "pyproject.toml" -type f 2>/dev/null)
echo " Found $ts_count TypeScript packages"
echo " Found $py_count Python packages"
# ============================================================================
# Phase 3: Generate migration plan
# ============================================================================
echo -e "\n${CYAN}[Phase 3/7] Generating migration plan...${NC}"
move_count=0
rename_count=0
for pkg_path in "${!PATH_TO_NAME[@]}"; do
pkg_name="${PATH_TO_NAME[$pkg_path]}"
pkg_type="${PATH_TO_TYPE[$pkg_path]}"
rel_path="${pkg_path#$PACKAGES_ROOT/}"
category="${rel_path%%/*}"
# Skip if in @ui-react sub-packages (monorepo children)
if [[ "$rel_path" == "@ui-react/packages/"* ]]; then
log_skip "$pkg_name" "monorepo sub-package"
continue
fi
# Skip if already in target location
if [[ "$rel_path" == "@ts/"* ]] || [[ "$rel_path" == "@py/"* ]]; then
log_skip "$pkg_name" "already in @${pkg_type}/"
continue
fi
# Skip categories that shouldn't be migrated
if is_skip_category "$category"; then
log_skip "$pkg_name" "excluded category: $category"
continue
fi
# Compute target
target_dir=$(compute_target_dir "$pkg_path" "$pkg_type")
target_path="$PACKAGES_ROOT/$target_dir"
# Skip if source IS the category (like @ui-react itself)
if [[ "$pkg_path" == "$PACKAGES_ROOT/$category" ]]; then
# This is a root-level category package (like @ui-react or @configs-ts)
# Handle specially
case "$category" in
"@ui-react"|"@ui-astro"|"@configs-ts"|"@configs-swift")
# These move as directories
target_dir="${SPECIAL_TARGETS[$category]:-@ts/$(strip_lang_suffix "$category")}"
target_path="$PACKAGES_ROOT/$target_dir"
;;
"@configs-py")
# Python configs - skip the directory, children are processed separately
log_skip "$pkg_name" "parent directory (children processed separately)"
continue
;;
*)
# Normal category-level package
;;
esac
fi
# Check for conflicts
if [[ -d "$target_path" ]] && [[ "$pkg_path" != "$target_path" ]]; then
log_error "Target conflict: $target_dir already exists"
continue
fi
# Add to move plan
MOVE_PLAN["$pkg_path"]="$target_dir"
((move_count++))
# Compute name change
new_name=$(compute_new_pkg_name "$pkg_name" "$pkg_type")
if [[ "$new_name" != "$pkg_name" ]]; then
OLD_TO_NEW_NAME["$pkg_name"]="$new_name"
((rename_count++))
fi
done
echo " Planned $move_count package moves"
echo " Planned $rename_count name changes"
# ============================================================================
# Phase 4: Show migration plan
# ============================================================================
echo -e "\n${CYAN}[Phase 4/7] Migration plan:${NC}"
if [[ $move_count -gt 0 ]]; then
echo -e "\n${BLUE}Package moves ($move_count):${NC}"
for source in $(printf '%s\n' "${!MOVE_PLAN[@]}" | sort); do
target="${MOVE_PLAN[$source]}"
rel_source="${source#$PACKAGES_ROOT/}"
log_move "$rel_source" "$target"
done
fi
if [[ $rename_count -gt 0 ]]; then
echo -e "\n${MAGENTA}Name changes ($rename_count):${NC}"
for old_name in $(printf '%s\n' "${!OLD_TO_NEW_NAME[@]}" | sort); do
new_name="${OLD_TO_NEW_NAME[$old_name]}"
log_rename "$old_name" "$new_name"
done
fi
# ============================================================================
# Phase 5: Execute migration
# ============================================================================
if $EXECUTE; then
echo -e "\n${CYAN}[Phase 5/7] Executing migration...${NC}"
# Move packages
for source in $(printf '%s\n' "${!MOVE_PLAN[@]}" | sort -r); do
target="${MOVE_PLAN[$source]}"
target_path="$PACKAGES_ROOT/$target"
pkg_name="${PATH_TO_NAME[$source]:-unknown}"
echo -e " Moving: ${BLUE}$(basename "$source")${NC}$target"
# Ensure parent directory exists
mkdir -p "$(dirname "$target_path")"
# Check if source has its own git repo
if [[ -d "$source/.git" ]]; then
# Standalone git repo - just move it
mv "$source" "$target_path"
else
# Part of main repo or no git - use git mv if available
if git rev-parse --git-dir > /dev/null 2>&1; then
git mv "$source" "$target_path" 2>/dev/null || mv "$source" "$target_path"
else
mv "$source" "$target_path"
fi
fi
log_success "Moved $pkg_name"
done
# Update package names in manifests
echo -e "\n${CYAN}Updating package names...${NC}"
for old_name in "${!OLD_TO_NEW_NAME[@]}"; do
new_name="${OLD_TO_NEW_NAME[$old_name]}"
# Find this package's new location
for source in "${!MOVE_PLAN[@]}"; do
source_name="${PATH_TO_NAME[$source]:-}"
if [[ "$source_name" == "$old_name" ]]; then
target="${MOVE_PLAN[$source]}"
pkg_path="$PACKAGES_ROOT/$target"
pkg_type="${PATH_TO_TYPE[$source]}"
if [[ "$pkg_type" == "ts" ]] && [[ -f "$pkg_path/package.json" ]]; then
echo -e " Updating: ${pkg_path#$PACKAGES_ROOT/}/package.json"
# Use jq to update the name field
tmp_file=$(mktemp)
jq --arg new "$new_name" '.name = $new' "$pkg_path/package.json" > "$tmp_file"
mv "$tmp_file" "$pkg_path/package.json"
fi
if [[ "$pkg_type" == "py" ]] && [[ -f "$pkg_path/pyproject.toml" ]]; then
echo -e " Updating: ${pkg_path#$PACKAGES_ROOT/}/pyproject.toml"
sed -i "s/^name = \"$old_name\"/name = \"$new_name\"/" "$pkg_path/pyproject.toml"
fi
break
fi
done
done
log_success "Updated $rename_count package names"
else
echo -e "\n${YELLOW}[Phase 5/7] Skipped (dry-run mode)${NC}"
fi
# ============================================================================
# Phase 6: Clean up empty directories
# ============================================================================
if $EXECUTE; then
echo -e "\n${CYAN}[Phase 6/7] Cleaning up empty directories...${NC}"
# Find and remove empty category directories
for category_dir in "$PACKAGES_ROOT"/@*/; do
[[ ! -d "$category_dir" ]] && continue
category=$(basename "$category_dir")
# Skip target directories
[[ "$category" == "@ts" ]] && continue
[[ "$category" == "@py" ]] && continue
# Check if directory is empty (only .git allowed)
remaining=$(find "$category_dir" -mindepth 1 -maxdepth 1 -not -name ".git" 2>/dev/null | wc -l)
if [[ $remaining -eq 0 ]]; then
echo -e " Removing empty: $category/"
rm -rf "$category_dir"
fi
done
log_success "Cleaned up empty directories"
else
echo -e "\n${YELLOW}[Phase 6/7] Skipped (dry-run mode)${NC}"
fi
# ============================================================================
# Phase 7: Summary
# ============================================================================
echo -e "\n${CYAN}[Phase 7/7] Summary${NC}"
echo " TypeScript packages discovered: $ts_count"
echo " Python packages discovered: $py_count"
echo " Packages to move: $move_count"
echo " Names to update: $rename_count"
if $DRY_RUN; then
echo -e "\n${YELLOW}This was a dry run. Use --execute to apply changes.${NC}"
echo " Example: $0 --execute"
fi
if $EXECUTE; then
echo -e "\n${GREEN}Migration complete!${NC}"
echo ""
echo -e "${CYAN}Next steps:${NC}"
echo " 1. Update pnpm-workspace.yaml:"
echo " packages:"
echo " - \"@ts/*\""
echo " - \"@ts/ui-react/packages/*\""
echo " - \"@py/*\""
echo ""
echo " 2. Run: pnpm install"
echo ""
echo " 3. Regenerate manifest:"
echo " ./scripts/analysis/generate-manifest.sh"
echo ""
echo " 4. Update consumer projects to use new package names"
fi