diff --git a/lib/mix/tasks/phoenix_kit.install.ex b/lib/mix/tasks/phoenix_kit.install.ex index 23c43a078..4ad875c35 100644 --- a/lib/mix/tasks/phoenix_kit.install.ex +++ b/lib/mix/tasks/phoenix_kit.install.ex @@ -135,18 +135,40 @@ if Code.ensure_loaded?(Igniter.Mix.Task) do # CRITICAL: Check if required configuration exists BEFORE starting app # This prevents configuration timing issues where config is added via Igniter # but the app has already started with cached (missing) configuration + + # Check if this is a retry pass (automatic restart after adding config) + is_retry = Process.get(:phoenix_kit_retry_pass, false) config_status = check_required_configuration() - case config_status do - :missing -> + case {config_status, is_retry} do + {:missing, false} -> # First pass: Add configuration via Igniter without starting app + # Store status in Process dictionary for tracking + Process.put(:phoenix_kit_config_status, :missing) + show_missing_config_message(argv) - result = super(argv) - show_config_added_message(argv) - result + super(argv) + + # AUTOMATIC RESTART instead of asking user to run again manually + Mix.shell().info(""" + + ✅ Configuration added successfully! + 🔄 Automatically restarting to complete the installation... + """) - :ok -> + # Clean Process dictionary to ensure fresh state for retry + Process.delete(:phoenix_kit_config_status) + + # Mark this as a retry pass to prevent infinite loops + Process.put(:phoenix_kit_retry_pass, true) + + # Recursive call with same arguments - automatic restart + run(argv) + + {:ok, _} -> # Second pass: Configuration exists, safe to start app and complete installation + Process.put(:phoenix_kit_config_status, :ok) + # Run standard igniter process result = super(argv) @@ -158,7 +180,27 @@ if Code.ensure_loaded?(Igniter.Mix.Task) do AssetRebuild.check_and_rebuild(verbose: true) end + # Clean up retry flag on successful completion + Process.delete(:phoenix_kit_retry_pass) result + + {:missing, true} -> + # Safety check: Configuration still missing after automatic retry + # This prevents infinite loops if configuration addition fails + Mix.shell().error(""" + + ❌ Configuration was not added successfully after automatic retry. + + Please check config/config.exs manually and ensure it contains: + - config :ueberauth, Ueberauth (with providers: %{}) + - config :hammer (with backend and expiry_ms) + - config :phoenix_kit, Oban (with queues configuration) + + Then run: mix phoenix_kit.install #{Enum.join(argv, " ")} + """) + + Process.delete(:phoenix_kit_retry_pass) + :error end end end @@ -286,17 +328,6 @@ if Code.ensure_loaded?(Igniter.Mix.Task) do """) end - # Display message after configuration is added - defp show_config_added_message(argv) do - Mix.shell().info(""" - - ✅ Configuration added successfully! - - Next step: Run the install command again to complete the installation: - mix phoenix_kit.install #{Enum.join(argv, " ")} - """) - end - # Check if all required configuration exists # Returns :ok if all config present, :missing if any config is missing defp check_required_configuration do @@ -361,9 +392,9 @@ if Code.ensure_loaded?(Igniter.Mix.Task) do has_oban_config = Enum.any?(lines, fn line -> trimmed = String.trim(line) - # Not a comment and contains config :phoenix_kit, Oban + # Not a comment and contains config :any_app, Oban !String.starts_with?(trimmed, "#") and - String.contains?(line, "config :phoenix_kit, Oban") + String.contains?(line, ", Oban") end) has_queues = diff --git a/lib/mix/tasks/phoenix_kit.status.ex b/lib/mix/tasks/phoenix_kit.status.ex index f3aee7abd..6189f4ee9 100644 --- a/lib/mix/tasks/phoenix_kit.status.ex +++ b/lib/mix/tasks/phoenix_kit.status.ex @@ -60,24 +60,15 @@ defmodule Mix.Tasks.PhoenixKit.Status do ] def run(argv) do - # Conditionally start the application if repositories aren't available - app_started_by_us = ensure_app_started() - - try do - {opts, _argv, _errors} = OptionParser.parse(argv, switches: @switches, aliases: @aliases) - - prefix = opts[:prefix] || "public" - verbose = opts[:verbose] || false - - show_comprehensive_status(prefix, verbose) - after - # Stop the application if we started it - if app_started_by_us do - # Note: Mix doesn't have app.stop task, and typically apps stay running - # This is normal behavior for Mix tasks - :ok - end - end + # Start the application to ensure repo is available + Mix.Task.run("app.start") + + {opts, _argv, _errors} = OptionParser.parse(argv, switches: @switches, aliases: @aliases) + + prefix = opts[:prefix] || "public" + verbose = opts[:verbose] || false + + show_comprehensive_status(prefix, verbose) end # Main status display function @@ -526,29 +517,6 @@ defmodule Mix.Tasks.PhoenixKit.Status do _ -> false end - # Ensure application is started only if needed - # Returns true if we started the app, false if it was already running - defp ensure_app_started do - # Check if we can get repo configuration - case get_repo_with_fallback() do - nil -> - # No repo found, start app and try again - Mix.Task.run("app.start") - true - - repo -> - # Repo found, check if it's actually available - if repo_available?(repo) do - # Repo is available, no need to start app - false - else - # Repo not available, start app - Mix.Task.run("app.start") - true - end - end - end - # Pad version number for consistent display defp pad_version(version) when version < 10, do: "0#{version}" defp pad_version(version), do: to_string(version) diff --git a/lib/mix/tasks/phoenix_kit.update.ex b/lib/mix/tasks/phoenix_kit.update.ex index 0fdeec72a..c5fee6650 100644 --- a/lib/mix/tasks/phoenix_kit.update.ex +++ b/lib/mix/tasks/phoenix_kit.update.ex @@ -167,26 +167,60 @@ if Code.ensure_loaded?(Igniter.Mix.Task) do # CRITICAL: Check if required configuration exists BEFORE starting app # This prevents configuration timing issues where config is added via Igniter # but the app has already started with cached (missing) configuration + + # Check if this is a retry pass (automatic restart after adding config) + is_retry = Process.get(:phoenix_kit_retry_pass, false) config_status = check_required_configuration() - case config_status do - :missing -> + case {config_status, is_retry} do + {:missing, false} -> # First pass: Add configuration via Igniter without starting app # Store config status in Process dictionary for igniter/1 to read Process.put(:phoenix_kit_config_status, :missing) show_missing_config_message(argv) - result = super(argv) - show_config_added_message(argv) - result + super(argv) + + # Automatic restart instead of manual prompt + Mix.shell().info(""" + + ✅ Configuration added successfully! + 🔄 Automatically restarting to complete the update... + """) - :ok -> - # Second pass: Configuration exists, safe to start app and update + # Clean Process dictionary for fresh state + Process.delete(:phoenix_kit_config_status) + Process.put(:phoenix_kit_retry_pass, true) + + # Recursive call with same arguments + run(argv) + + {:ok, _} -> + # Second pass (automatic or manual): Configuration exists, safe to start app # Store config status in Process dictionary for igniter/1 to read Process.put(:phoenix_kit_config_status, :ok) Mix.Task.run("app.start") result = super(argv) post_igniter_tasks(elem(opts, 0)) + + # Clean retry flag + Process.delete(:phoenix_kit_retry_pass) result + + {:missing, true} -> + # Safety: Configuration still missing after retry + Mix.shell().error(""" + + ❌ Configuration was not added successfully after automatic retry. + + This may indicate a problem with your config/config.exs file. + Please check the file manually and ensure it's writable. + + Then run manually: + mix phoenix_kit.update #{Enum.join(argv, " ")} + """) + + Process.delete(:phoenix_kit_retry_pass) + :error end end end @@ -201,6 +235,7 @@ if Code.ensure_loaded?(Igniter.Mix.Task) do PhoenixKit requires configuration for: - Ueberauth (OAuth authentication) - Hammer (rate limiting) + - Oban (background jobs for file processing) This configuration will be added now. @@ -209,17 +244,6 @@ if Code.ensure_loaded?(Igniter.Mix.Task) do """) end - # Display message after configuration is added - defp show_config_added_message(argv) do - Mix.shell().info(""" - - ✅ Configuration added successfully! - - Next step: Run the update command again to complete the upgrade: - mix phoenix_kit.update #{Enum.join(argv, " ")} - """) - end - # Check if all required configuration exists # Returns :ok if all config present, :missing if any config is missing defp check_required_configuration do @@ -284,9 +308,10 @@ if Code.ensure_loaded?(Igniter.Mix.Task) do has_oban_config = Enum.any?(lines, fn line -> trimmed = String.trim(line) - # Not a comment and contains config :phoenix_kit, Oban + # Not a comment and contains config for any app with Oban + # Matches: "config :any_app, Oban" or "config :any_app, Oban," !String.starts_with?(trimmed, "#") and - String.contains?(line, "config :phoenix_kit, Oban") + String.contains?(line, ", Oban") end) has_queues = diff --git a/mix.exs b/mix.exs index 66a80709f..03d8a52fa 100644 --- a/mix.exs +++ b/mix.exs @@ -128,6 +128,8 @@ defmodule PhoenixKit.MixProject do {:finch, "~> 0.18"}, # Code generation and project patching + # Note: Available in all environments for library code, but typically + # only needed in :dev when used as a dependency in parent projects {:igniter, "~> 0.7"} ] end