Packages
phoenix_kit
1.2.7
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.
## Usage
$ mix phoenix_kit.update
$ mix phoenix_kit.update --prefix=myapp
$ mix phoenix_kit.update --status
$ mix phoenix_kit.update --skip-assets
## 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
## 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
## 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}
@shortdoc "Updates PhoenixKit to the latest version"
@switches [
prefix: :string,
status: :boolean,
force: :boolean,
skip_assets: :boolean
]
@aliases [
p: :prefix,
s: :status,
f: :force
]
@impl Mix.Task
def run(argv) do
# 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
# 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) 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)
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) do
migration_file = create_update_migration(prefix, current_version, target_version, force)
# Run interactive migration execution
run_update_migration_interactive(migration_file, skip_assets)
end
# Perform the actual update
defp perform_update(opts) do
prefix = opts[:prefix] || "public"
force = opts[:force] || false
skip_assets = opts[:skip_assets] || 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)
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, skip_assets) do
# Check if we can run migrations safely
case check_migration_conditions() do
:ok ->
run_interactive_migration_prompt(migration_file, skip_assets)
{:error, reason} ->
Mix.shell().info("""
💡 Migration not run automatically (#{reason}).
To run migration manually:
mix ecto.migrate
""")
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, skip_assets) do
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(skip_assets)
_ ->
Mix.shell().info("""
⚠️ Migration skipped. To run it manually later:
mix ecto.migrate
""")
end
end
# Execute migration with feedback
defp run_migration_with_feedback(skip_assets) 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)
# Rebuild assets after successful migration (unless skipped)
if skip_assets do
show_update_success_notice(:assets_skipped)
else
asset_result = AssetRebuild.check_and_rebuild(verbose: false)
show_update_success_notice(asset_result)
end
{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(:rebuild_completed) do
Mix.shell().info("""
🎉 PhoenixKit updated successfully! Visit: /phoenix_kit/users/register
""")
end
defp show_update_success_notice(:assets_skipped) do
Mix.shell().info("""
🎉 PhoenixKit updated successfully! Visit: /phoenix_kit/users/register
💡 You may want to rebuild assets later:
mix phoenix_kit.assets.rebuild
""")
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