diff --git a/bin/omarchy-theme-install b/bin/omarchy-theme-install index 3b6f52542c..900588a5d5 100755 --- a/bin/omarchy-theme-install +++ b/bin/omarchy-theme-install @@ -17,23 +17,72 @@ if [[ -z $REPO_URL ]]; then fi THEMES_DIR="$HOME/.config/omarchy/themes" +if ! mkdir -p "$THEMES_DIR"; then + echo "Error: Failed to create themes directory." + exit 1 +fi # Strip user@host: prefix from scp-style SSH URLs so basename sees just the path REPO_PATH="$REPO_URL" [[ $REPO_PATH != *"://"* && $REPO_PATH == *:*/* ]] && REPO_PATH="${REPO_PATH#*:}" THEME_NAME=$(basename "$REPO_PATH" .git | sed -E 's/^omarchy-//; s/-theme$//' | tr '[:upper:]' '[:lower:]') THEME_PATH="$THEMES_DIR/$THEME_NAME" - -# Remove existing theme if present -if [[ -d $THEME_PATH ]]; then - rm -rf "$THEME_PATH" +if ! STAGING_DIR=$(mktemp -d "$THEMES_DIR/.theme-install.XXXXXX"); then + echo "Error: Failed to create theme staging directory." + exit 1 fi +STAGED_THEME_PATH="$STAGING_DIR/$THEME_NAME" +if ! BACKUP_THEME_DIR=$(mktemp -d "$THEMES_DIR/.theme-backup.XXXXXX"); then + rm -rf "$STAGING_DIR" + echo "Error: Failed to create theme backup directory." + exit 1 +fi +BACKUP_THEME_PATH="$BACKUP_THEME_DIR/$THEME_NAME" +PRESERVE_BACKUP=0 -# Clone the repo directly to ~/.config/omarchy/themes -if ! git clone "$REPO_URL" "$THEME_PATH"; then +cleanup() { + rm -rf "$STAGING_DIR" + (( PRESERVE_BACKUP )) || rm -rf "$BACKUP_THEME_DIR" +} +trap cleanup EXIT + +# Clone into a staging directory so a failed clone cannot remove the installed theme +if ! git clone "$REPO_URL" "$STAGED_THEME_PATH"; then echo "Error: Failed to clone theme repo." exit 1 fi +if [[ -e $THEME_PATH ]] && ! mv "$THEME_PATH" "$BACKUP_THEME_PATH"; then + echo "Error: Failed to stage the existing theme." + exit 1 +fi + +if ! mv "$STAGED_THEME_PATH" "$THEME_PATH"; then + if [[ -e $BACKUP_THEME_PATH ]] && ! mv "$BACKUP_THEME_PATH" "$THEME_PATH"; then + echo "Error: Failed to install theme repo and restore the existing theme." + exit 1 + fi + echo "Error: Failed to install theme repo." + exit 1 +fi + # Apply the new theme with omarchy-theme-set -omarchy-theme-set $THEME_NAME +if ! omarchy-theme-set "$THEME_NAME"; then + if [[ -e $BACKUP_THEME_PATH ]]; then + FAILED_THEME_PATH="$BACKUP_THEME_DIR/failed-$THEME_NAME" + if mv "$THEME_PATH" "$FAILED_THEME_PATH" && mv "$BACKUP_THEME_PATH" "$THEME_PATH"; then + PRESERVE_BACKUP=1 + echo "Error: Failed to apply installed theme; the previous theme was restored." >&2 + echo "The failed theme remains at $FAILED_THEME_PATH for inspection." >&2 + else + PRESERVE_BACKUP=1 + echo "Error: Failed to apply installed theme and restore the previous theme." >&2 + echo "The backup remains at $BACKUP_THEME_PATH." >&2 + fi + else + echo "Error: Failed to apply installed theme; the new theme remains installed for retry." >&2 + fi + exit 1 +fi + +rm -rf "$BACKUP_THEME_PATH" diff --git a/bin/omarchy-theme-set b/bin/omarchy-theme-set index fe57c2af85..332b7aadd6 100755 --- a/bin/omarchy-theme-set +++ b/bin/omarchy-theme-set @@ -9,11 +9,49 @@ if [[ -z $1 ]]; then exit 1 fi -CURRENT_THEME_PATH="$HOME/.config/omarchy/current/theme" -NEXT_THEME_PATH="$HOME/.config/omarchy/current/next-theme" +CURRENT_THEME_DIR="$HOME/.config/omarchy/current" +CURRENT_THEME_PATH="$CURRENT_THEME_DIR/theme" USER_THEMES_PATH="$HOME/.config/omarchy/themes" OMARCHY_THEMES_PATH="$OMARCHY_PATH/themes" +if ! mkdir -p "$CURRENT_THEME_DIR"; then + echo "Failed to create theme state directory" + exit 1 +fi + +if ! NEXT_THEME_PATH=$(mktemp -d "$CURRENT_THEME_DIR/next-theme.XXXXXX"); then + echo "Failed to create theme staging directory" + exit 1 +fi + +if ! PREVIOUS_THEME_DIR=$(mktemp -d "$CURRENT_THEME_DIR/previous-theme.XXXXXX"); then + rm -rf "$NEXT_THEME_PATH" + echo "Failed to create theme backup directory" + exit 1 +fi +PREVIOUS_THEME_PATH="$PREVIOUS_THEME_DIR/theme" +CURRENT_THEME_STAGED=0 +POST_THEME_FAILURES=() + +cleanup() { + local exit_status=$? + + if (( CURRENT_THEME_STAGED )) && [[ ! -e $CURRENT_THEME_PATH ]] && [[ -e $PREVIOUS_THEME_PATH ]]; then + if mv "$PREVIOUS_THEME_PATH" "$CURRENT_THEME_PATH"; then + echo "Restored the previous theme after interrupted activation" >&2 + else + echo "Failed to restore the previous theme after interrupted activation" >&2 + exit_status=1 + fi + fi + + rm -rf "$NEXT_THEME_PATH" "$PREVIOUS_THEME_DIR" + return "$exit_status" +} +trap cleanup EXIT +trap 'exit 130' INT +trap 'exit 143' TERM + THEME_NAME=$(echo "$1" | sed -E 's/<[^>]+>//g' | tr '[:upper:]' '[:lower:]' | tr ' ' '-') if [[ ! -d $OMARCHY_THEMES_PATH/$THEME_NAME ]] && [[ ! -d $USER_THEMES_PATH/$THEME_NAME ]]; then @@ -21,53 +59,101 @@ if [[ ! -d $OMARCHY_THEMES_PATH/$THEME_NAME ]] && [[ ! -d $USER_THEMES_PATH/$THE exit 1 fi -# Setup clean next theme directory (for atomic theme config swapping) -rm -rf "$NEXT_THEME_PATH" +# Setup a unique staging directory for atomic theme config swapping mkdir -p "$NEXT_THEME_PATH" # Copy official theme first, then overlay user customizations on top -cp -r "$OMARCHY_THEMES_PATH/$THEME_NAME/"* "$NEXT_THEME_PATH/" 2>/dev/null -cp -r "$USER_THEMES_PATH/$THEME_NAME/"* "$NEXT_THEME_PATH/" 2>/dev/null +copy_theme_files() { + local source="$1" + [[ -d $source ]] || return 0 + cp -a "$source"/. "$NEXT_THEME_PATH/" +} + +if ! copy_theme_files "$OMARCHY_THEMES_PATH/$THEME_NAME" || + ! copy_theme_files "$USER_THEMES_PATH/$THEME_NAME"; then + echo "Failed to copy theme '$THEME_NAME'" + exit 1 +fi # Generate colors.toml from alacritty.toml if theme is missing colors.toml if [[ ! -f $NEXT_THEME_PATH/colors.toml && -f $NEXT_THEME_PATH/alacritty.toml ]]; then - omarchy-theme-colors-from-alacritty "$NEXT_THEME_PATH" + if ! omarchy-theme-colors-from-alacritty "$NEXT_THEME_PATH"; then + echo "Failed to generate theme colors for '$THEME_NAME'" + exit 1 + fi fi # Generate dynamic configs -omarchy-theme-set-templates +if ! OMARCHY_THEME_STAGE_DIR="$NEXT_THEME_PATH" omarchy-theme-set-templates; then + echo "Failed to generate themed configuration for '$THEME_NAME'" + exit 1 +fi -# Swap next theme in as current -rm -rf "$CURRENT_THEME_PATH" -mv "$NEXT_THEME_PATH" "$CURRENT_THEME_PATH" +# Swap the staged theme in as current, retaining the old theme until the move succeeds +if [[ -e $CURRENT_THEME_PATH ]]; then + CURRENT_THEME_STAGED=1 + if ! mv "$CURRENT_THEME_PATH" "$PREVIOUS_THEME_PATH"; then + CURRENT_THEME_STAGED=0 + echo "Failed to stage the current theme for replacement" + exit 1 + fi +fi + +if ! mv "$NEXT_THEME_PATH" "$CURRENT_THEME_PATH"; then + if [[ -e $PREVIOUS_THEME_PATH ]]; then + if ! mv "$PREVIOUS_THEME_PATH" "$CURRENT_THEME_PATH"; then + echo "Failed to activate theme '$THEME_NAME' and restore the previous theme" + exit 1 + fi + fi + echo "Failed to activate theme '$THEME_NAME'" + exit 1 +fi +CURRENT_THEME_STAGED=0 # Store theme name for reference -echo "$THEME_NAME" >"$HOME/.config/omarchy/current/theme.name" +if ! echo "$THEME_NAME" >"$HOME/.config/omarchy/current/theme.name"; then + POST_THEME_FAILURES+=("recording the current theme") +fi + +run_optional() { + local description="$1" + shift + + if ! "$@"; then + POST_THEME_FAILURES+=("$description") + echo "Warning: $description failed" >&2 + fi +} # Change background with theme if [[ $OMARCHY_THEME_SKIP_BACKGROUND != "1" ]]; then - omarchy-theme-bg-next + run_optional "changing the theme background" omarchy-theme-bg-next fi # Restart components to apply new theme if pgrep -x waybar >/dev/null; then - omarchy-restart-waybar + run_optional "restarting Waybar" omarchy-restart-waybar fi -omarchy-restart-swayosd -omarchy-restart-terminal -omarchy-restart-hyprctl -omarchy-restart-btop -omarchy-restart-opencode -omarchy-restart-mako -omarchy-restart-helix +run_optional "restarting SwayOSD" omarchy-restart-swayosd +run_optional "restarting the terminal" omarchy-restart-terminal +run_optional "restarting Hyprland helpers" omarchy-restart-hyprctl +run_optional "restarting btop" omarchy-restart-btop +run_optional "restarting Opencode" omarchy-restart-opencode +run_optional "restarting Mako" omarchy-restart-mako +run_optional "restarting Helix" omarchy-restart-helix # Change app-specific themes -omarchy-theme-set-foot -omarchy-theme-set-gnome -omarchy-theme-set-browser -omarchy-theme-set-vscode -omarchy-theme-set-obsidian -omarchy-theme-set-keyboard +run_optional "updating Foot theme" omarchy-theme-set-foot +run_optional "updating GNOME theme" omarchy-theme-set-gnome +run_optional "updating browser theme" omarchy-theme-set-browser +run_optional "updating VS Code theme" omarchy-theme-set-vscode +run_optional "updating Obsidian theme" omarchy-theme-set-obsidian +run_optional "updating keyboard theme" omarchy-theme-set-keyboard # Call hook on theme set -omarchy-hook theme-set "$THEME_NAME" >/dev/null +run_optional "running the theme-set hook" omarchy-hook theme-set "$THEME_NAME" + +if (( ${#POST_THEME_FAILURES[@]} > 0 )); then + echo "Theme '$THEME_NAME' activated with ${#POST_THEME_FAILURES[@]} integration warning(s)." >&2 +fi diff --git a/bin/omarchy-theme-set-templates b/bin/omarchy-theme-set-templates index 92236d1950..c5fbc05bc6 100755 --- a/bin/omarchy-theme-set-templates +++ b/bin/omarchy-theme-set-templates @@ -5,7 +5,7 @@ TEMPLATES_DIR="$OMARCHY_PATH/default/themed" USER_TEMPLATES_DIR="$HOME/.config/omarchy/themed" -NEXT_THEME_DIR="$HOME/.config/omarchy/current/next-theme" +NEXT_THEME_DIR="${OMARCHY_THEME_STAGE_DIR:-$HOME/.config/omarchy/current/next-theme}" COLORS_FILE="$NEXT_THEME_DIR/colors.toml" # Convert hex color to decimal RGB (e.g., "#1e1e2e" -> "30,30,46") @@ -16,7 +16,10 @@ hex_to_rgb() { # Only generate dynamic templates for themes with a colors.toml definition if [[ -f $COLORS_FILE ]]; then - sed_script=$(mktemp) + if ! sed_script=$(mktemp); then + echo "Failed to create theme template staging file" + exit 1 + fi while IFS='=' read -r key value; do key="${key//[\"\' ]/}" # strip quotes and spaces from key @@ -41,7 +44,11 @@ if [[ -f $COLORS_FILE ]]; then # Don't overwrite configs already exists in the output directory (copied from theme specific folder) if [[ ! -f $output_path ]]; then - sed -f "$sed_script" "$tpl" >"$output_path" + if ! sed -f "$sed_script" "$tpl" >"$output_path"; then + rm -f "$sed_script" + echo "Failed to generate themed file '$output_path'" + exit 1 + fi fi done diff --git a/test/omarchy-cli-test.sh b/test/omarchy-cli-test.sh index 8e9fd34166..32cde668c6 100644 --- a/test/omarchy-cli-test.sh +++ b/test/omarchy-cli-test.sh @@ -263,3 +263,171 @@ assert_output_contains "partial metadata command dispatches" "$output" "partial- output=$("$TMPDIR/omarchy" body metadata test) assert_output_contains "body metadata command dispatches by filename" "$output" "body-metadata-ok" + +THEME_TEST_DIR="$TMPDIR/theme-failure" +THEME_TEST_HOME="$THEME_TEST_DIR/home" +THEME_TEST_BIN="$THEME_TEST_DIR/bin" +mkdir -p "$THEME_TEST_HOME/.config/omarchy/current/theme" "$THEME_TEST_HOME/.config/omarchy/themes/tokyo-night" "$THEME_TEST_BIN" + +for command_name in \ + omarchy-hook \ + omarchy-restart-btop \ + omarchy-restart-helix \ + omarchy-restart-hyprctl \ + omarchy-restart-mako \ + omarchy-restart-opencode \ + omarchy-restart-swayosd \ + omarchy-restart-terminal \ + omarchy-theme-bg-next \ + omarchy-theme-set-browser \ + omarchy-theme-set-foot \ + omarchy-theme-set-gnome \ + omarchy-theme-set-keyboard \ + omarchy-theme-set-obsidian \ + omarchy-theme-set-templates \ + omarchy-theme-set-vscode \ + pgrep; do + printf '#!/bin/bash\n%s\n' "[[ \$0 == *omarchy-theme-set-templates ]] && exit 42 || exit 0" >"$THEME_TEST_BIN/$command_name" + chmod +x "$THEME_TEST_BIN/$command_name" +done + +printf 'active-theme-sentinel\n' >"$THEME_TEST_HOME/.config/omarchy/current/theme/KEEP-ME.txt" +if HOME="$THEME_TEST_HOME" OMARCHY_PATH="$ROOT" OMARCHY_THEME_SKIP_BACKGROUND=1 PATH="$THEME_TEST_BIN:$ROOT/bin:$PATH" bash "$ROOT/bin/omarchy-theme-set" tokyo-night; then + fail "theme set returns non-zero when template generation fails" +fi +[[ -f $THEME_TEST_HOME/.config/omarchy/current/theme/KEEP-ME.txt ]] || fail "theme set preserves active theme when template generation fails" +pass "theme set preserves active theme when template generation fails" + +THEME_COLOR_DIR="$TMPDIR/theme-color-failure" +THEME_COLOR_HOME="$THEME_COLOR_DIR/home" +THEME_COLOR_BIN="$THEME_COLOR_DIR/bin" +mkdir -p "$THEME_COLOR_HOME/.config/omarchy/current/theme" "$THEME_COLOR_HOME/.config/omarchy/themes/broken-theme" "$THEME_COLOR_BIN" +printf 'background = "bad"\n' >"$THEME_COLOR_HOME/.config/omarchy/themes/broken-theme/alacritty.toml" +printf 'active-theme-sentinel\n' >"$THEME_COLOR_HOME/.config/omarchy/current/theme/KEEP-ME.txt" +for command_name in \ + omarchy-hook \ + omarchy-restart-btop \ + omarchy-restart-helix \ + omarchy-restart-hyprctl \ + omarchy-restart-mako \ + omarchy-restart-opencode \ + omarchy-restart-swayosd \ + omarchy-restart-terminal \ + omarchy-theme-bg-next \ + omarchy-theme-set-browser \ + omarchy-theme-set-foot \ + omarchy-theme-set-gnome \ + omarchy-theme-set-keyboard \ + omarchy-theme-set-obsidian \ + omarchy-theme-set-templates \ + omarchy-theme-set-vscode \ + pgrep; do + ln -s /usr/bin/true "$THEME_COLOR_BIN/$command_name" +done +printf '#!/bin/bash\nexit 42\n' >"$THEME_COLOR_BIN/omarchy-theme-colors-from-alacritty" +chmod +x "$THEME_COLOR_BIN/omarchy-theme-colors-from-alacritty" +if HOME="$THEME_COLOR_HOME" OMARCHY_PATH="$ROOT" OMARCHY_THEME_SKIP_BACKGROUND=1 PATH="$THEME_COLOR_BIN:$ROOT/bin:$PATH" bash "$ROOT/bin/omarchy-theme-set" broken-theme; then + fail "theme set returns non-zero when color generation fails" +fi +[[ -f $THEME_COLOR_HOME/.config/omarchy/current/theme/KEEP-ME.txt ]] || fail "theme set preserves active theme when color generation fails" +pass "theme set preserves active theme when color generation fails" + +THEME_TEMPLATE_DIR="$TMPDIR/theme-template-failure" +THEME_TEMPLATE_HOME="$THEME_TEMPLATE_DIR/home" +THEME_TEMPLATE_BIN="$THEME_TEMPLATE_DIR/bin" +mkdir -p "$THEME_TEMPLATE_HOME/.config/omarchy/current/next-theme" "$THEME_TEMPLATE_BIN" +cp "$ROOT/themes/tokyo-night/colors.toml" "$THEME_TEMPLATE_HOME/.config/omarchy/current/next-theme/colors.toml" +printf '#!/bin/bash\nexit 42\n' >"$THEME_TEMPLATE_BIN/sed" +chmod +x "$THEME_TEMPLATE_BIN/sed" +if HOME="$THEME_TEMPLATE_HOME" OMARCHY_PATH="$ROOT" PATH="$THEME_TEMPLATE_BIN:/usr/bin:/bin" bash "$ROOT/bin/omarchy-theme-set-templates"; then + fail "theme template generation returns non-zero when sed fails" +fi +pass "theme template generation reports rendering failures" + +printf 'installed-theme-sentinel\n' >"$THEME_TEST_HOME/.config/omarchy/themes/tokyo-night/KEEP-ME.txt" +printf '#!/bin/bash\nexit 42\n' >"$THEME_TEST_BIN/git" +chmod +x "$THEME_TEST_BIN/git" +if HOME="$THEME_TEST_HOME" OMARCHY_PATH="$ROOT" PATH="$THEME_TEST_BIN:$ROOT/bin:$PATH" bash "$ROOT/bin/omarchy-theme-install" https://example.invalid/omarchy-tokyo-night.git; then + fail "theme install returns non-zero when git clone fails" +fi +[[ -f $THEME_TEST_HOME/.config/omarchy/themes/tokyo-night/KEEP-ME.txt ]] || fail "theme install preserves existing theme when git clone fails" +pass "theme install preserves existing theme when git clone fails" + +printf '#!/bin/bash\nmkdir -p "$3"\nprintf "new-theme\\n" >"$3/new.txt"\n' >"$THEME_TEST_BIN/git" +printf '#!/bin/bash\nexit 42\n' >"$THEME_TEST_BIN/omarchy-theme-set" +chmod +x "$THEME_TEST_BIN/git" "$THEME_TEST_BIN/omarchy-theme-set" +if HOME="$THEME_TEST_HOME" OMARCHY_PATH="$ROOT" PATH="$THEME_TEST_BIN:$ROOT/bin:$PATH" bash "$ROOT/bin/omarchy-theme-install" https://example.invalid/omarchy-tokyo-night.git; then + fail "theme install returns non-zero when applying the cloned theme fails" +fi +[[ -f $THEME_TEST_HOME/.config/omarchy/themes/tokyo-night/KEEP-ME.txt ]] || fail "theme install restores the previous theme when applying the clone fails" +failed_theme=$(find "$THEME_TEST_HOME/.config/omarchy/themes" -path '*/failed-tokyo-night/new.txt' -print -quit) +[[ -n $failed_theme ]] || fail "theme install keeps the failed clone available for inspection" +pass "theme install restores the previous theme when applying the clone fails" + +THEME_INTERRUPT_DIR="$TMPDIR/theme-interrupt" +THEME_INTERRUPT_HOME="$THEME_INTERRUPT_DIR/home" +THEME_INTERRUPT_BIN="$THEME_INTERRUPT_DIR/bin" +mkdir -p "$THEME_INTERRUPT_HOME/.config/omarchy/current/theme" "$THEME_INTERRUPT_BIN" +printf 'active-theme-sentinel\n' >"$THEME_INTERRUPT_HOME/.config/omarchy/current/theme/KEEP-ME.txt" +for command_name in \ + omarchy-hook \ + omarchy-restart-btop \ + omarchy-restart-helix \ + omarchy-restart-hyprctl \ + omarchy-restart-mako \ + omarchy-restart-opencode \ + omarchy-restart-swayosd \ + omarchy-restart-terminal \ + omarchy-theme-bg-next \ + omarchy-theme-set-browser \ + omarchy-theme-set-foot \ + omarchy-theme-set-gnome \ + omarchy-theme-set-keyboard \ + omarchy-theme-set-obsidian \ + omarchy-theme-set-vscode \ + pgrep; do + ln -s /usr/bin/true "$THEME_INTERRUPT_BIN/$command_name" +done +printf '#!/bin/bash\n/usr/bin/mv "$@"\ncase "$2" in *previous-theme*) kill -TERM "$PPID" ;; esac\n' >"$THEME_INTERRUPT_BIN/mv" +chmod +x "$THEME_INTERRUPT_BIN/mv" +if HOME="$THEME_INTERRUPT_HOME" OMARCHY_PATH="$ROOT" OMARCHY_THEME_SKIP_BACKGROUND=1 PATH="$THEME_INTERRUPT_BIN:$ROOT/bin:$PATH" bash "$ROOT/bin/omarchy-theme-set" tokyo-night; then + fail "theme set returns non-zero when interrupted during activation" +fi +[[ -f $THEME_INTERRUPT_HOME/.config/omarchy/current/theme/KEEP-ME.txt ]] || fail "theme set restores the active theme after an interrupted activation" +pass "theme set restores the active theme after an interrupted activation" + +THEME_SUCCESS_DIR="$TMPDIR/theme-success" +THEME_SUCCESS_HOME="$THEME_SUCCESS_DIR/home" +THEME_SUCCESS_BIN="$THEME_SUCCESS_DIR/bin" +mkdir -p "$THEME_SUCCESS_HOME/.config/omarchy/current/theme" "$THEME_SUCCESS_BIN" +for command_name in \ + omarchy-hook \ + omarchy-restart-btop \ + omarchy-restart-helix \ + omarchy-restart-hyprctl \ + omarchy-restart-mako \ + omarchy-restart-opencode \ + omarchy-restart-swayosd \ + omarchy-restart-terminal \ + omarchy-restart-waybar \ + omarchy-theme-bg-next \ + omarchy-theme-set-browser \ + omarchy-theme-set-foot \ + omarchy-theme-set-gnome \ + omarchy-theme-set-keyboard \ + omarchy-theme-set-obsidian \ + omarchy-theme-set-vscode \ + pgrep; do + ln -s /usr/bin/true "$THEME_SUCCESS_BIN/$command_name" +done +rm -f "$THEME_SUCCESS_BIN/omarchy-hook" +printf '#!/bin/bash\nexit 42\n' >"$THEME_SUCCESS_BIN/omarchy-hook" +chmod +x "$THEME_SUCCESS_BIN/omarchy-hook" + +printf 'old-theme-sentinel\n' >"$THEME_SUCCESS_HOME/.config/omarchy/current/theme/KEEP-ME.txt" +success_output=$(HOME="$THEME_SUCCESS_HOME" OMARCHY_PATH="$ROOT" OMARCHY_THEME_SKIP_BACKGROUND=1 PATH="$THEME_SUCCESS_BIN:$ROOT/bin:$PATH" bash "$ROOT/bin/omarchy-theme-set" tokyo-night 2>&1) || fail "theme set succeeds when an optional integration fails" +[[ $success_output == *"integration warning"* ]] || fail "theme set reports optional integration failures" +[[ -f $THEME_SUCCESS_HOME/.config/omarchy/current/theme/colors.toml ]] || fail "theme set generates the replacement theme" +[[ -f $THEME_SUCCESS_HOME/.config/omarchy/current/theme/waybar.css ]] || fail "theme set generates themed templates in the staging directory" +[[ ! -f $THEME_SUCCESS_HOME/.config/omarchy/current/theme/KEEP-ME.txt ]] || fail "theme set replaces the old theme after successful generation" +pass "theme set activates successfully generated theme"