Created
June 11, 2025 17:18
-
-
Save frankdugan3/ec02a823ee07a711894bee2c87e2b10c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/sh | |
set -eu | |
# --- Helper: Convert snake_case to PascalCase --- | |
snake_to_pascal() { | |
echo "$1" | awk -F_ '{ | |
for (i = 1; i <= NF; i++) { | |
$i = toupper(substr($i, 1, 1)) substr($i, 2) | |
} | |
print $0 | |
}' OFS="" | |
} | |
# --- Validate and normalize root directory --- | |
if [ "$#" -lt 1 ]; then | |
echo "Usage: $0 <project_root_directory>" >&2 | |
exit 1 | |
fi | |
# Expand path to absolute | |
PROJECT_ROOT=$(cd "$1" 2>/dev/null && pwd) | |
if [ ! -d "$PROJECT_ROOT" ]; then | |
echo "Error: '$1' is not a valid directory." >&2 | |
exit 1 | |
fi | |
cd "$PROJECT_ROOT" | |
ROOT_NAME=$(basename "$PROJECT_ROOT") | |
# --- Prompt for inputs with gum --- | |
OLD_SNAKE=$(gum input --prompt "Old project name: " --value "$ROOT_NAME") | |
OLD_PASCAL=$(gum input --prompt "Old module name: " --value "$(snake_to_pascal "$OLD_SNAKE")") | |
NEW_SNAKE=$(gum input --prompt "New project name: ") | |
NEW_PASCAL=$(gum input --prompt "New module name: " --value "$(snake_to_pascal "$NEW_SNAKE")") | |
# --- Confirm renaming --- | |
if ! gum confirm "Rename $OLD_SNAKE / $OLD_PASCAL to $NEW_SNAKE / $NEW_PASCAL?"; then | |
exit 1 | |
fi | |
# --- Ask whether to include .git folder --- | |
RENAME_GIT="yes" | |
if gum confirm "Also rename inside the .git folder?" --default=true; then | |
RENAME_GIT="yes" | |
else | |
RENAME_GIT="no" | |
fi | |
# --- Construct ripgrep ignore flags --- | |
RG_IGNORE=" | |
--glob=!_build/** | |
--glob=!deps/** | |
--glob=!.elixir_ls/** | |
--glob=!node_modules/** | |
" | |
if [ "$RENAME_GIT" = "no" ]; then | |
RG_IGNORE="$RG_IGNORE --glob=!.git/**" | |
fi | |
# --- Replace file contents --- | |
echo "Updating file contents..." | |
# shellcheck disable=SC2086 | |
eval rg --files --hidden $RG_IGNORE | while IFS= read -r file; do | |
sed -i "s/$OLD_SNAKE/$NEW_SNAKE/g" "$file" | |
sed -i "s/$OLD_PASCAL/$NEW_PASCAL/g" "$file" | |
done | |
# --- Rename files and directories --- | |
echo "Renaming files and directories..." | |
# shellcheck disable=SC2010 | |
find . -depth \ | |
\( -path "./_build" -o -path "./deps" -o -path "./.elixir_ls" -o -path "./node_modules" \ | |
$( [ "$RENAME_GIT" = "no" ] && printf '%s' '-o -path "./.git"' ) \ | |
\) -prune -o -print | while IFS= read -r path; do | |
base_name=$(basename "$path") | |
dir_name=$(dirname "$path") | |
new_base_name=$(echo "$base_name" | sed "s/$OLD_SNAKE/$NEW_SNAKE/g" | sed "s/$OLD_PASCAL/$NEW_PASCAL/g") | |
if [ "$new_base_name" != "$base_name" ]; then | |
mv "$path" "$dir_name/$new_base_name" | |
fi | |
done | |
# --- Rename the root directory --- | |
cd .. | |
NEW_ROOT=$(echo "$ROOT_NAME" | sed "s/$OLD_SNAKE/$NEW_SNAKE/g" | sed "s/$OLD_PASCAL/$NEW_PASCAL/g") | |
mv "$PROJECT_ROOT" "$NEW_ROOT" | |
cd "$NEW_ROOT" | |
exec "$SHELL" | |
# --- Run post-commands (safe to fail) --- | |
direnv allow 2>/dev/null || true | |
mise trust 2>/dev/null || true |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment