chore(shared): 🔧 Update shared configuration files and scripts
This commit is contained in:
parent
e55c876ddb
commit
ce79d5b7d4
1 changed files with 495 additions and 0 deletions
495
migration/reorganize-by-language.sh
Executable file
495
migration/reorganize-by-language.sh
Executable 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
|
||||
Loading…
Add table
Reference in a new issue