diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 7201fd5..0ca3a58 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -19,7 +19,11 @@ }, "features": { "ghcr.io/devcontainers/features/docker-in-docker:2": {}, - "ghcr.io/jsburckhardt/devcontainer-features/just:1": {} + "ghcr.io/jsburckhardt/devcontainer-features/just:1": {}, + "ghcr.io/devcontainers/features/common-utils": { + "installOhMyZsh": true, + "configureZshAsDefaultShell": true + } }, "remoteUser": "node", "updateContentCommand": "npm install -g @devcontainers/cli" diff --git a/justfile b/justfile index 674c4e9..d258399 100644 --- a/justfile +++ b/justfile @@ -6,7 +6,7 @@ build-feature feature: rm -rf .devcontainer/{{feature}} mkdir -p .devcontainer/{{feature}} cp src/{{feature}}/devcontainer-feature.json .devcontainer/{{feature}}/ - cp src/{{feature}}/install.sh .devcontainer/{{feature}}/ + cp src/{{feature}}/*.sh .devcontainer/{{feature}}/ echo "✓ Feature copied to .devcontainer/{{feature}}" # Build Oh My Posh feature diff --git a/src/ohmyposh/configure-shell.sh b/src/ohmyposh/configure-shell.sh new file mode 100755 index 0000000..da08b9f --- /dev/null +++ b/src/ohmyposh/configure-shell.sh @@ -0,0 +1,86 @@ +#!/bin/bash +set -e + +# This script ensures Oh My Posh configuration is at the end of shell RC files +# to avoid being overwritten by other features (like shell-history). + +move_to_end() { + local rc_file="$1" + local start_marker="# region Oh My Posh configuration" + local end_marker="# endregion Oh My Posh configuration" + + if [ -f "$rc_file" ]; then + # Check for the region markers + if grep -q "$start_marker" "$rc_file"; then + echo "Ensuring Oh My Posh configuration is at the end of $rc_file..." + + # Extract the block + local block=$(sed -n "/$start_marker/,/$end_marker/p" "$rc_file") + + if [ -n "$block" ]; then + # Create a temp file + local temp_file=$(mktemp) + + # Write file content WITHOUT the block to temp file + # We use awk to exclude the range + if ! awk -v start="$start_marker" -v end="$end_marker" ' + $0 ~ start {skip=1; next} + $0 ~ end {skip=0; next} + !skip {print} + ' "$rc_file" > "$temp_file"; then + echo "Error: Failed to process $rc_file" + rm -f "$temp_file" + return 1 + fi + + # Append the block to the end + echo "" >> "$temp_file" + echo "$block" >> "$temp_file" + + # Replace original file + mv "$temp_file" "$rc_file" + + echo "Moved Oh My Posh configuration to the end of $rc_file" + fi + # Fallback: Check for old configuration without markers + elif grep -q "oh-my-posh init" "$rc_file"; then + echo "Found legacy Oh My Posh configuration in $rc_file. Moving to end and adding markers..." + + # Create a temp file + local temp_file=$(mktemp) + + # Replace lines containing "oh-my-posh init" with ":" (no-op) to disable old config + # while preserving syntax validity of surrounding if/else blocks. + sed 's/^\([[:space:]]*\).*oh-my-posh init.*/\1:/' "$rc_file" > "$temp_file" + + # Replace original file with cleaned content + mv "$temp_file" "$rc_file" + + # Append new block + # Determine shell from filename + local shell_name="bash" + if [[ "$rc_file" == *".zshrc" ]]; then + shell_name="zsh" + fi + + # Use echo to append to avoid issues with cat and EOF in some environments or if file is empty + echo "" >> "$rc_file" + echo "$start_marker" >> "$rc_file" + echo "if [ -s ~/.ohmyposh.json ]; then" >> "$rc_file" + echo " eval \"\$(oh-my-posh init $shell_name --config ~/.ohmyposh.json)\"" >> "$rc_file" + echo "else" >> "$rc_file" + echo " eval \"\$(oh-my-posh init $shell_name --config jandedobbeleer)\"" >> "$rc_file" + echo "fi" >> "$rc_file" + echo "$end_marker" >> "$rc_file" + + echo "Updated legacy configuration in $rc_file" + fi + fi +} + +# Check bash +move_to_end "$HOME/.bashrc" + +# Check zsh +move_to_end "$HOME/.zshrc" + diff --git a/src/ohmyposh/devcontainer-feature.json b/src/ohmyposh/devcontainer-feature.json index e0209af..5ff895a 100644 --- a/src/ohmyposh/devcontainer-feature.json +++ b/src/ohmyposh/devcontainer-feature.json @@ -29,6 +29,7 @@ "installsAfter": [ "ghcr.io/devcontainers/features/common-utils" ], + "postCreateCommand": "/usr/local/bin/oh-my-posh-configure-shell", "customizations": { "vscode": { "settings": { diff --git a/src/ohmyposh/install.sh b/src/ohmyposh/install.sh old mode 100644 new mode 100755 index 42bf42e..5c924e7 --- a/src/ohmyposh/install.sh +++ b/src/ohmyposh/install.sh @@ -78,6 +78,11 @@ mkdir -p "$INSTALL_PATH" mv "$TEMP_FILE" "$INSTALL_PATH/oh-my-posh" chmod +x "$INSTALL_PATH/oh-my-posh" +# Install the configure script +HELPER_SCRIPT="/usr/local/bin/oh-my-posh-configure-shell" +cp "$(dirname "$0")/configure-shell.sh" "$HELPER_SCRIPT" +chmod +x "$HELPER_SCRIPT" + echo -e "${GREEN}Oh My Posh binary installed to $INSTALL_PATH/oh-my-posh${NC}" # Verify installation @@ -146,7 +151,7 @@ for SHELL_NAME in "${SHELL_ARRAY[@]}"; do # Add Oh My Posh initialization cat >> "$RC_FILE" << EOF -# Oh My Posh configuration +# region Oh My Posh configuration if [ -s ~/.ohmyposh.json ]; then # Use custom theme if mounted eval "\$(oh-my-posh init $SHELL_CMD --config ~/.ohmyposh.json)" @@ -154,6 +159,7 @@ else # Use built-in theme eval "\$(oh-my-posh init $SHELL_CMD --config $THEME)" fi +# endregion Oh My Posh configuration EOF chown "$USER_NAME:$USER_NAME" "$RC_FILE" 2>/dev/null || true diff --git a/test/ohmyposh/legacy_config_migration.sh b/test/ohmyposh/legacy_config_migration.sh new file mode 100644 index 0000000..78747b3 --- /dev/null +++ b/test/ohmyposh/legacy_config_migration.sh @@ -0,0 +1,72 @@ +#!/bin/bash + +set -e + +source dev-container-features-test-lib + +# 1. Verify the helper script is installed +check "helper script installed" test -x /usr/local/bin/oh-my-posh-configure-shell + +# 2. Simulate legacy configuration (no markers) +cat > ~/.bashrc << EOF +# Some initial content +export PATH=\$PATH:/something + +# Oh My Posh configuration +if [ -s ~/.ohmyposh.json ]; then + # Use custom theme if mounted + eval "\$(oh-my-posh init bash --config ~/.ohmyposh.json)" +else + # Use built-in theme + eval "\$(oh-my-posh init bash --config jandedobbeleer)" +fi + +# Some other content that should be preserved +export FOO=bar +EOF + +# 3. Run the helper script +# We need to set USERNAME to root because the test runs as root +# and the helper script defaults to 'vscode' if not set. +export USERNAME=root +/usr/local/bin/oh-my-posh-configure-shell + +# 4. Verify the result +# Read the last few lines of .bashrc +LAST_LINES=$(tail -n 10 ~/.bashrc) +echo "Last lines of .bashrc:" +echo "$LAST_LINES" + +# Check if "oh-my-posh init" is in the last lines +check "oh-my-posh is at the end" bash -c "tail -n 10 ~/.bashrc | grep -q 'oh-my-posh init'" + +# Check if markers are present +check "markers are present" grep -q "# region Oh My Posh configuration" ~/.bashrc + +# Check if old config is gone (or at least the comment) +# We removed lines with "oh-my-posh init" and "# Oh My Posh configuration" +# But we added them back at the end. +# So we check if there is only ONE occurrence of the block (or markers) +COUNT=$(grep -c "# region Oh My Posh configuration" ~/.bashrc) +if [ "$COUNT" -eq 1 ]; then + echo "✅ Only one configuration block found" +else + echo "❌ Found $COUNT configuration blocks" + exit 1 +fi + +# Check if FOO=bar is preserved and BEFORE the new block +POS_FOO=$(grep -n "export FOO=bar" ~/.bashrc | cut -d: -f1) +POS_OMP=$(grep -n "# region Oh My Posh configuration" ~/.bashrc | cut -d: -f1) + +echo "FOO position: $POS_FOO" +echo "OMP position: $POS_OMP" + +if [ "$POS_OMP" -gt "$POS_FOO" ]; then + echo "✅ Oh My Posh config is after existing content" +else + echo "❌ Oh My Posh config is NOT after existing content" + exit 1 +fi + +reportResults diff --git a/test/ohmyposh/repro_legacy.sh b/test/ohmyposh/repro_legacy.sh new file mode 100755 index 0000000..b99fa22 --- /dev/null +++ b/test/ohmyposh/repro_legacy.sh @@ -0,0 +1,130 @@ +#!/bin/bash +set -e + +# Mock environment +USER_HOME=$(pwd) +RC_FILE="$USER_HOME/.bashrc_repro" + +# Create legacy file +cat > "$RC_FILE" << EOF +# Some initial content +export PATH=\$PATH:/something + +# Oh My Posh configuration +if [ -s ~/.ohmyposh.json ]; then + # Use custom theme if mounted + eval "\$(oh-my-posh init bash --config ~/.ohmyposh.json)" +else + # Use built-in theme + eval "\$(oh-my-posh init bash --config jandedobbeleer)" +fi + +# Some other content that should be preserved +export FOO=bar +EOF + +echo "--- Initial Content ---" +cat "$RC_FILE" +echo "-----------------------" + +# Define the function exactly as in the file +move_to_end() { + local rc_file="$1" + local start_marker="# region Oh My Posh configuration" + local end_marker="# endregion Oh My Posh configuration" + + if [ -f "$rc_file" ]; then + # Check for the region markers + if grep -q "$start_marker" "$rc_file"; then + echo "Ensuring Oh My Posh configuration is at the end of $rc_file..." + + # Extract the block + local block=$(sed -n "/$start_marker/,/$end_marker/p" "$rc_file") + + if [ -n "$block" ]; then + # Create a temp file + local temp_file=$(mktemp) + + # Write file content WITHOUT the block to temp file + # We use awk to exclude the range + awk -v start="$start_marker" -v end="$end_marker" ' + $0 ~ start {skip=1; next} + $0 ~ end {skip=0; next} + !skip {print} + ' "$rc_file" > "$temp_file" + + # Append the block to the end + echo "" >> "$temp_file" + echo "$block" >> "$temp_file" + + # Replace original file + mv "$temp_file" "$rc_file" + + echo "Moved Oh My Posh configuration to the end of $rc_file" + fi + # Fallback: Check for old configuration without markers + elif grep -q "oh-my-posh init" "$rc_file"; then + echo "Found legacy Oh My Posh configuration in $rc_file. Moving to end and adding markers..." + + # Create a temp file + local temp_file=$(mktemp) + + # Remove lines containing "oh-my-posh init" and surrounding if/else block if possible + # This is harder to do reliably with regex, so we'll just comment out the old init line + # and add a new block at the end. + + # Actually, let's just append the new block and assume the old one will be overridden or harmless + # if it's not setting PROMPT_COMMAND in a conflicting way. + # But the old one DOES set PROMPT_COMMAND via eval. + + # Let's try to remove the old block if it matches the standard pattern + # Standard pattern: + # # Oh My Posh configuration + # if [ -s ~/.ohmyposh.json ]; then + # ... + # fi + + # We'll use a simpler approach: Remove any lines containing "oh-my-posh init" + # and the specific comment "# Oh My Posh configuration" + + # Use a temp file for grep output to avoid issues with reading/writing same file + sed 's/^\([[:space:]]*\).*oh-my-posh init.*/\1:/' "$rc_file" | grep -v "# Oh My Posh configuration" > "$temp_file" + + # Replace original file with cleaned content + mv "$temp_file" "$rc_file" + + # Append new block + # Determine shell from filename + local shell_name="bash" + if [[ "$rc_file" == *".zshrc" ]]; then + shell_name="zsh" + fi + + # Use echo to append to avoid issues with cat and EOF in some environments or if file is empty + echo "" >> "$rc_file" + echo "$start_marker" >> "$rc_file" + echo "if [ -s ~/.ohmyposh.json ]; then" >> "$rc_file" + echo " eval \"\$(oh-my-posh init $shell_name --config ~/.ohmyposh.json)\"" >> "$rc_file" + echo "else" >> "$rc_file" + echo " eval \"\$(oh-my-posh init $shell_name --config jandedobbeleer)\"" >> "$rc_file" + echo "fi" >> "$rc_file" + echo "$end_marker" >> "$rc_file" + + echo "Updated legacy configuration in $rc_file" + fi + fi +} + +# Run it +move_to_end "$RC_FILE" + +echo "--- Final Content ---" +cat "$RC_FILE" +echo "---------------------" + +# Check for markers +if grep -q "# region Oh My Posh configuration" "$RC_FILE"; then + echo "✅ Markers found" +else + echo "❌ Markers NOT found" +fi diff --git a/test/ohmyposh/scenarios.json b/test/ohmyposh/scenarios.json index f5a3dd8..c87bf52 100644 --- a/test/ohmyposh/scenarios.json +++ b/test/ohmyposh/scenarios.json @@ -30,5 +30,11 @@ "installPath": "/usr/bin" } } + }, + "legacy_config_migration": { + "image": "ubuntu:latest", + "features": { + "ohmyposh": {} + } } } diff --git a/test/ohmyposh/test_sed.sh b/test/ohmyposh/test_sed.sh new file mode 100755 index 0000000..e7a0716 --- /dev/null +++ b/test/ohmyposh/test_sed.sh @@ -0,0 +1,25 @@ +#!/bin/bash +set -e + +cat > test.sh << EOF +# Oh My Posh configuration +if [ -s ~/.ohmyposh.json ]; then + # Use custom theme if mounted + eval "\$(oh-my-posh init bash --config ~/.ohmyposh.json)" +else + # Use built-in theme + eval "\$(oh-my-posh init bash --config jandedobbeleer)" +fi +EOF + +sed 's/.*oh-my-posh init.*/ :/' test.sh > test.sh.tmp +mv test.sh.tmp test.sh + +cat test.sh + +# Check syntax +if bash -n test.sh; then + echo "Syntax OK" +else + echo "Syntax Error" +fi