Packages
phoenix_kit
1.4.0
1.7.208
1.7.207
1.7.206
1.7.205
1.7.204
1.7.203
1.7.202
1.7.201
1.7.200
1.7.199
1.7.198
1.7.197
1.7.196
1.7.194
1.7.193
1.7.192
1.7.191
1.7.190
1.7.189
1.7.187
1.7.186
1.7.185
1.7.184
1.7.183
1.7.182
1.7.181
1.7.180
1.7.179
1.7.178
1.7.177
1.7.176
1.7.175
1.7.174
1.7.173
1.7.172
1.7.171
1.7.170
1.7.169
1.7.168
1.7.167
1.7.166
1.7.165
1.7.164
1.7.162
1.7.161
1.7.160
1.7.159
1.7.157
1.7.156
1.7.155
1.7.154
1.7.153
1.7.152
1.7.151
1.7.150
1.7.149
1.7.146
1.7.145
1.7.144
1.7.143
1.7.138
1.7.133
1.7.132
1.7.131
1.7.130
1.7.128
1.7.126
1.7.125
1.7.121
1.7.120
1.7.119
1.7.118
1.7.117
1.7.116
1.7.115
1.7.114
1.7.113
1.7.112
1.7.111
1.7.110
1.7.109
1.7.108
1.7.107
1.7.106
1.7.105
1.7.104
1.7.103
1.7.102
1.7.101
1.7.100
1.7.99
1.7.98
1.7.97
1.7.96
1.7.95
1.7.94
1.7.93
1.7.92
1.7.91
1.7.90
1.7.89
1.7.88
1.7.87
1.7.86
1.7.85
1.7.84
1.7.83
1.7.82
1.7.81
1.7.80
1.7.79
1.7.78
1.7.77
1.7.76
1.7.75
1.7.74
1.7.71
1.7.70
1.7.69
1.7.66
1.7.65
1.7.64
1.7.63
1.7.62
1.7.61
1.7.59
1.7.58
1.7.57
1.7.56
1.7.55
1.7.54
1.7.53
1.7.52
1.7.51
1.7.49
1.7.44
1.7.43
1.7.42
1.7.41
1.7.39
1.7.38
1.7.37
1.7.36
1.7.34
1.7.33
1.7.31
1.7.30
1.7.29
1.7.28
1.7.27
1.7.26
1.7.25
1.7.24
1.7.23
1.7.22
1.7.21
1.7.20
1.7.19
1.7.18
1.7.17
1.7.16
1.7.15
1.7.14
1.7.13
1.7.12
1.7.11
1.7.10
1.7.9
1.7.8
1.7.7
1.7.6
1.7.5
1.7.4
1.7.3
1.7.2
1.7.1
1.7.0
1.6.20
1.6.19
1.6.18
1.6.17
1.6.16
1.6.15
1.6.14
1.6.13
1.6.12
1.6.11
1.6.10
1.6.9
1.6.8
1.6.7
1.6.6
1.6.5
1.6.4
1.6.3
1.5.2
1.5.1
1.5.0
1.4.9
1.4.8
1.4.7
1.4.6
1.4.5
1.4.4
1.4.3
1.4.2
1.4.1
1.4.0
1.3.2
1.3.1
1.3.0
1.2.10
1.2.9
1.2.8
1.2.7
1.2.5
1.2.4
1.2.2
1.2.1
1.2.0
1.1.0
1.0.0
A foundation for building Elixir Phoenix apps â SaaS, social networks, ERP systems, marketplaces, and more
Current section
Files
Jump to
Current section
Files
lib/mix/tasks/phoenix_kit.update.ex
defmodule Mix.Tasks.PhoenixKit.Update do
use Mix.Task
@moduledoc """
Updates PhoenixKit to the latest version.
This task handles updating an existing PhoenixKit installation to the latest version
by creating upgrade migrations that preserve existing data while adding new features.
The update process also automatically:
- Updates CSS configuration (enables daisyUI themes if disabled)
- Rebuilds assets using the Phoenix asset pipeline
- Applies database migrations (with optional interactive prompt)
## Usage
$ mix phoenix_kit.update
$ mix phoenix_kit.update --prefix=myapp
$ mix phoenix_kit.update --status
$ mix phoenix_kit.update --skip-assets
$ mix phoenix_kit.update -y
## Options
* `--prefix` - Database schema prefix (default: "public")
* `--status` - Show current installation status and available updates
* `--force` - Force update even if already up to date
* `--skip-assets` - Skip automatic asset rebuild check
* `--yes` / `-y` - Skip confirmation prompts and run migrations automatically
## Examples
# Update PhoenixKit to latest version
mix phoenix_kit.update
# Check what version is installed and what updates are available
mix phoenix_kit.update --status
# Update with custom schema prefix
mix phoenix_kit.update --prefix=auth
# Update without prompts (useful for CI/CD)
mix phoenix_kit.update -y
# Force update with automatic migration
mix phoenix_kit.update --force -y
## Version Management
PhoenixKit uses a versioned migration system similar to Oban. Each version
contains specific database schema changes that can be applied incrementally.
Current version: V07 (latest version with comprehensive features)
- V01: Basic authentication with role system
- V02: Remove is_active column from role assignments (direct deletion)
- V03-V07: Additional features and improvements (see migration files for details)
## Safe Updates
All PhoenixKit updates are designed to be:
- Non-destructive (existing data is preserved)
- Backward compatible (existing code continues to work)
- Idempotent (safe to run multiple times)
- Rollback-capable (can be reverted if needed)
"""
alias PhoenixKit.Install.{AssetRebuild, Common, CssIntegration}
alias PhoenixKit.Utils.Routes
@shortdoc "Updates PhoenixKit to the latest version"
@switches [
prefix: :string,
status: :boolean,
force: :boolean,
skip_assets: :boolean,
yes: :boolean
]
@aliases [
p: :prefix,
s: :status,
f: :force,
y: :yes
]
@impl Mix.Task
def run(argv) do
# Handle --help flag
if "--help" in argv or "-h" in argv do
show_help()
else
# Ensure application is started for proper version detection
Mix.Task.run("app.start")
{opts, _argv, _errors} = OptionParser.parse(argv, switches: @switches, aliases: @aliases)
if opts[:status] do
show_status(opts)
else
perform_update(opts)
end
end
end
# Display comprehensive help information
defp show_help do
Mix.shell().info("""
mix phoenix_kit.update - Update PhoenixKit to the latest version
USAGE
mix phoenix_kit.update [OPTIONS]
DESCRIPTION
Updates an existing PhoenixKit installation to the latest version by:
âĸ Creating upgrade migrations that preserve existing data
âĸ Adding new features and improvements
âĸ Updating CSS configuration (enables daisyUI themes if disabled)
âĸ Rebuilding assets using the Phoenix asset pipeline
âĸ Optionally running database migrations automatically
OPTIONS
--prefix SCHEMA Database schema prefix for PhoenixKit tables
Default: "public" (standard PostgreSQL schema)
Must match prefix used during installation
Example: --prefix "auth"
--status, -s Show current installation status and available updates
Does not perform any changes
--force, -f Force update even if already up to date
Useful for regenerating migrations
--skip-assets Skip automatic asset rebuild check
Default: false
--yes, -y Skip confirmation prompts
Automatically runs migrations without asking
Useful for CI/CD environments
-h, --help Show this help message
EXAMPLES
# Update PhoenixKit to latest version (uses default "public" schema)
mix phoenix_kit.update
# Check current version and available updates
mix phoenix_kit.update --status
# Update with custom schema prefix (must match installation prefix)
mix phoenix_kit.update --prefix "auth"
# Update without prompts (useful for CI/CD)
mix phoenix_kit.update -y
# Force update and run migrations automatically
mix phoenix_kit.update --force -y
# Update without rebuilding assets
mix phoenix_kit.update --skip-assets
VERSION MANAGEMENT
PhoenixKit uses a versioned migration system similar to Oban.
Each version contains specific database schema changes that can
be applied incrementally.
Current latest version: V17
âĸ V01: Basic authentication with role system
âĸ V02: Remove is_active column from role assignments
âĸ V03-V06: Additional features and improvements
âĸ V07: Email system tables (logs, events, blocklist)
âĸ V08-V17: Settings, OAuth, magic links, and more
SAFE UPDATES
All PhoenixKit updates are designed to be:
âĸ Non-destructive (existing data is preserved)
âĸ Backward compatible (existing code continues to work)
âĸ Idempotent (safe to run multiple times)
âĸ Rollback-capable (can be reverted if needed)
AFTER UPDATE
1. If migrations weren't run automatically:
mix ecto.migrate
2. Restart your Phoenix server:
mix phx.server
3. Visit your application:
http://localhost:4000/phoenix_kit/users/register
CI/CD USAGE
For automated deployments, use the --yes flag to skip prompts:
mix phoenix_kit.update -y
TROUBLESHOOTING
If the update fails or you need to check status:
âĸ Check version: mix phoenix_kit.update --status
âĸ Force regeneration: mix phoenix_kit.update --force
âĸ Manual migration: mix ecto.migrate
âĸ Rollback: mix ecto.rollback
DOCUMENTATION
For more information, visit:
https://hexdocs.pm/phoenix_kit
""")
end
# Show current installation status and available updates
defp show_status(opts) do
prefix = opts[:prefix] || "public"
# Use the status command to show current status
args = if prefix == "public", do: [], else: ["--prefix=#{prefix}"]
Mix.Task.run("phoenix_kit.status", args)
end
# Handle not installed scenario
defp handle_not_installed do
Mix.shell().error("""
â PhoenixKit is not installed.
Please run: mix phoenix_kit.install
""")
end
# Handle update check logic
defp handle_update_check(prefix, current_version, force, skip_assets, yes) do
target_version = Common.current_version()
cond do
current_version >= target_version && !force ->
handle_already_up_to_date(current_version)
current_version < target_version || force ->
handle_update_needed(prefix, current_version, target_version, force, skip_assets, yes)
true ->
Mix.shell().info("No update needed.")
end
end
# Handle already up to date scenario
defp handle_already_up_to_date(current_version) do
Mix.shell().info("""
â
PhoenixKit is already up to date (V#{pad_version(current_version)}).
Use --force to regenerate the migration anyway.
""")
end
# Handle update needed scenario
defp handle_update_needed(prefix, current_version, target_version, force, skip_assets, yes) do
migration_file = create_update_migration(prefix, current_version, target_version, force)
# Update CSS integration (enables daisyUI themes if disabled)
update_css_integration()
# Always rebuild assets unless explicitly skipped
unless skip_assets do
AssetRebuild.check_and_rebuild(verbose: true)
end
# Run interactive migration execution
run_update_migration_interactive(migration_file, yes)
end
# Update CSS integration during PhoenixKit updates
defp update_css_integration do
css_paths = [
"assets/css/app.css",
"priv/static/css/app.css",
"lib/#{Mix.Phoenix.otp_app()}_web/assets/css/app.css"
]
case Enum.find(css_paths, &File.exists?/1) do
nil ->
# No app.css found - skip CSS integration
:ok
css_path ->
# Update CSS file to enable daisyUI themes if disabled
content = File.read!(css_path)
existing = CssIntegration.check_existing_integration(content)
if existing.daisyui_themes_disabled do
# Use regex to update themes: false -> themes: all
pattern = ~r/@plugin\s+(["'][^"']*daisyui["'])\s*\{([^}]*themes:\s*)false([^}]*)\}/
updated_content =
String.replace(content, pattern, fn match ->
String.replace(match, ~r/(themes:\s*)false/, "\\1all")
end)
File.write!(css_path, updated_content)
Mix.shell().info("""
â
Updated daisyUI configuration to enable all themes!
File: #{css_path}
Changed: themes: false â themes: all
""")
end
end
rescue
error ->
# Non-critical error - log and continue
Mix.shell().info("âšī¸ Could not update CSS integration: #{inspect(error)}")
end
# Perform the actual update
defp perform_update(opts) do
prefix = opts[:prefix] || "public"
force = opts[:force] || false
skip_assets = opts[:skip_assets] || false
yes = opts[:yes] || false
case Common.check_installation_status(prefix) do
{:not_installed} ->
handle_not_installed()
{:current_version, current_version} ->
handle_update_check(prefix, current_version, force, skip_assets, yes)
end
end
# Create update migration from current to target version
defp create_update_migration(prefix, current_version, target_version, force) do
create_schema = prefix != "public"
# Ensure migrations directory exists
migrations_dir = "priv/repo/migrations"
File.mkdir_p!(migrations_dir)
# Generate timestamp and migration file name using Ecto format
timestamp = generate_timestamp()
action = if force, do: "force_update", else: "update"
migration_name =
"#{timestamp}_phoenix_kit_#{action}_v#{pad_version(current_version)}_to_v#{pad_version(target_version)}.exs"
migration_file = Path.join(migrations_dir, migration_name)
# Generate module name
module_name =
"PhoenixKit#{String.capitalize(action)}V#{pad_version(current_version)}ToV#{pad_version(target_version)}"
# Create migration content
migration_content = """
defmodule Ecto.Migrations.#{module_name} do
@moduledoc false
use Ecto.Migration
def up do
# PhoenixKit Update Migration: V#{pad_version(current_version)} -> V#{pad_version(target_version)}
PhoenixKit.Migrations.up([
prefix: "#{prefix}",
version: #{target_version},
create_schema: #{create_schema}
])
end
def down do
# Rollback PhoenixKit to V#{pad_version(current_version)}
PhoenixKit.Migrations.down([
prefix: "#{prefix}",
version: #{current_version}
])
end
end
"""
# Write migration file
File.write!(migration_file, migration_content)
# Show brief success notice
Mix.shell().info("""
đĻ PhoenixKit Update Migration Created: #{migration_name}
- Updating from V#{pad_version(current_version)} to V#{pad_version(target_version)}
""")
# Return migration file for interactive execution
migration_name
end
# Run interactive migration execution (similar to install command)
defp run_update_migration_interactive(migration_file, yes) do
# Check if we can run migrations safely
case check_migration_conditions() do
:ok ->
run_interactive_migration_prompt(migration_file, yes)
{:error, reason} ->
if yes do
# If -y flag is used but conditions aren't met, try to run migration anyway
Mix.shell().info(
"\nâ ī¸ Migration conditions not optimal (#{reason}), but running due to -y flag..."
)
run_migration_with_feedback()
else
Mix.shell().info("""
đĄ Migration not run automatically (#{reason}).
To run migration manually:
mix ecto.migrate
""")
end
end
end
# Check if migration can be run interactively
defp check_migration_conditions do
# Check if we have an app name
case Mix.Project.config()[:app] do
nil ->
{:error, "No app name found"}
_app ->
# Check if we're in interactive environment
if System.get_env("CI") || !System.get_env("TERM") do
{:error, "Non-interactive environment"}
else
:ok
end
end
rescue
_ -> {:error, "Error checking conditions"}
end
# Prompt user for migration execution
defp run_interactive_migration_prompt(_migration_file, yes) do
if yes do
# Skip prompt and run migration directly
Mix.shell().info("\nđ Running database migration automatically (--yes flag)...")
run_migration_with_feedback()
else
Mix.shell().info("""
đ Would you like to run the database migration now?
This will update your PhoenixKit installation.
Options:
- y/yes: Run 'mix ecto.migrate' now
- n/no: Skip migration (you can run it manually later)
""")
case Mix.shell().prompt("Run migration? [Y/n]")
|> String.trim()
|> String.downcase() do
response when response in ["", "y", "yes"] ->
run_migration_with_feedback()
_ ->
Mix.shell().info("""
â ī¸ Migration skipped. To run it manually later:
mix ecto.migrate
""")
end
end
end
# Execute migration with feedback
defp run_migration_with_feedback do
Mix.shell().info("\nâŗ Running database migration...")
try do
case System.cmd("mix", ["ecto.migrate"], stderr_to_stdout: true) do
{output, 0} ->
Mix.shell().info("\nâ
Migration completed successfully!")
Mix.shell().info(output)
show_update_success_notice()
{output, _} ->
Mix.shell().info("\nâ Migration failed:")
Mix.shell().info(output)
show_manual_migration_instructions()
end
rescue
error ->
Mix.shell().info("\nâ ī¸ Migration execution failed: #{inspect(error)}")
show_manual_migration_instructions()
end
end
# Show success notice after update
defp show_update_success_notice do
Mix.shell().info("""
đ PhoenixKit updated successfully! Visit: #{Routes.path("/users/register")}
""")
end
# Show manual migration instructions
defp show_manual_migration_instructions do
Mix.shell().info("""
Please run the migration manually:
mix ecto.migrate
Then start your server:
mix phx.server
""")
end
# Generate timestamp in Ecto migration format (same as phoenix_kit.install.ex)
defp generate_timestamp do
{{y, m, d}, {hh, mm, ss}} = :calendar.universal_time()
"#{y}#{pad(m)}#{pad(d)}#{pad(hh)}#{pad(mm)}#{pad(ss)}"
end
defp pad(i) when i < 10, do: <<?0, ?0 + i>>
defp pad(i), do: to_string(i)
# Pad version number for consistent naming
defp pad_version(version) when version < 10, do: "0#{version}"
defp pad_version(version), do: to_string(version)
end