plausible_release.ex 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. defmodule Plausible.Release do
  2. use Plausible
  3. use Plausible.Repo
  4. require Logger
  5. @app :plausible
  6. @start_apps [
  7. :ssl,
  8. :postgrex,
  9. :ch,
  10. :ecto
  11. ]
  12. def should_be_first_launch? do
  13. on_ee do
  14. false
  15. else
  16. not (_has_users? = Repo.exists?(Plausible.Auth.User))
  17. end
  18. end
  19. def migrate do
  20. prepare()
  21. Enum.each(repos(), &run_migrations_for/1)
  22. IO.puts("Migrations successful!")
  23. end
  24. def pending_migrations do
  25. prepare()
  26. IO.puts("Pending migrations")
  27. IO.puts("")
  28. Enum.each(repos(), &list_pending_migrations_for/1)
  29. end
  30. def seed do
  31. prepare()
  32. # Run seed script
  33. Enum.each(repos(), &run_seeds_for/1)
  34. # Signal shutdown
  35. IO.puts("Success!")
  36. end
  37. def createdb do
  38. prepare()
  39. for repo <- repos() do
  40. :ok = ensure_repo_created(repo)
  41. end
  42. IO.puts("Creation of Db successful!")
  43. end
  44. def rollback do
  45. prepare()
  46. get_step =
  47. IO.gets("Enter the number of steps: ")
  48. |> String.trim()
  49. |> Integer.parse()
  50. case get_step do
  51. {int, _trailing} ->
  52. Enum.each(repos(), fn repo -> run_rollbacks_for(repo, int) end)
  53. IO.puts("Rollback successful!")
  54. :error ->
  55. IO.puts("Invalid integer")
  56. end
  57. end
  58. def configure_ref_inspector() do
  59. priv_dir = Application.app_dir(:plausible, "priv/ref_inspector")
  60. Application.put_env(:ref_inspector, :database_path, priv_dir)
  61. end
  62. def configure_ua_inspector() do
  63. priv_dir = Application.app_dir(:plausible, "priv/ua_inspector")
  64. Application.put_env(:ua_inspector, :database_path, priv_dir)
  65. end
  66. def dump_plans() do
  67. prepare()
  68. Repo.delete_all("plans")
  69. plans =
  70. Plausible.Billing.Plans.all()
  71. |> Plausible.Billing.Plans.with_prices()
  72. |> Enum.map(fn plan ->
  73. plan = Map.from_struct(plan)
  74. monthly_cost = plan.monthly_cost && Money.to_decimal(plan.monthly_cost)
  75. yearly_cost = plan.yearly_cost && Money.to_decimal(plan.yearly_cost)
  76. {:ok, features} = Plausible.Billing.Ecto.FeatureList.dump(plan.features)
  77. {:ok, team_member_limit} = Plausible.Billing.Ecto.Limit.dump(plan.team_member_limit)
  78. plan
  79. |> Map.drop([:id])
  80. |> Map.put(:kind, Atom.to_string(plan.kind))
  81. |> Map.put(:monthly_cost, monthly_cost)
  82. |> Map.put(:yearly_cost, yearly_cost)
  83. |> Map.put(:features, features)
  84. |> Map.put(:team_member_limit, team_member_limit)
  85. end)
  86. {count, _} = Repo.insert_all("plans", plans)
  87. IO.puts("Inserted #{count} plans")
  88. end
  89. ##############################
  90. defp repos do
  91. Application.fetch_env!(@app, :ecto_repos)
  92. end
  93. defp run_seeds_for(repo) do
  94. # Run the seed script if it exists
  95. seed_script = seeds_path(repo)
  96. if File.exists?(seed_script) do
  97. IO.puts("Running seed script..")
  98. Code.eval_file(seed_script)
  99. end
  100. end
  101. defp run_migrations_for(repo) do
  102. IO.puts("Running migrations for #{repo}")
  103. {:ok, _, _} = Ecto.Migrator.with_repo(repo, &Ecto.Migrator.run(&1, :up, all: true))
  104. end
  105. defp list_pending_migrations_for(repo) do
  106. IO.puts("Listing pending migrations for #{repo}")
  107. IO.puts("")
  108. migration_directory = Ecto.Migrator.migrations_path(repo)
  109. pending =
  110. repo
  111. |> Ecto.Migrator.migrations([migration_directory])
  112. |> Enum.filter(fn {status, _version, _migration} -> status == :down end)
  113. if pending == [] do
  114. IO.puts("No pending migrations")
  115. else
  116. Enum.each(pending, fn {_, version, migration} ->
  117. IO.puts("* #{version}_#{migration}")
  118. end)
  119. end
  120. IO.puts("")
  121. end
  122. defp ensure_repo_created(repo) do
  123. IO.puts("create #{inspect(repo)} database if it doesn't exist")
  124. case repo.__adapter__.storage_up(repo.config) do
  125. :ok -> :ok
  126. {:error, :already_up} -> :ok
  127. {:error, term} -> {:error, term}
  128. end
  129. end
  130. defp run_rollbacks_for(repo, step) do
  131. app = Keyword.get(repo.config, :otp_app)
  132. IO.puts("Running rollbacks for #{app} (STEP=#{step})")
  133. {:ok, _, _} =
  134. Ecto.Migrator.with_repo(repo, &Ecto.Migrator.run(&1, :down, all: false, step: step))
  135. end
  136. defp prepare do
  137. IO.puts("Loading #{@app}..")
  138. # Load the code for myapp, but don't start it
  139. :ok = Application.ensure_loaded(@app)
  140. IO.puts("Starting dependencies..")
  141. # Start apps necessary for executing migrations
  142. Enum.each(@start_apps, &Application.ensure_all_started/1)
  143. # Start the Repo(s) for myapp
  144. IO.puts("Starting repos..")
  145. Enum.each(repos(), & &1.start_link(pool_size: 2))
  146. end
  147. defp seeds_path(repo), do: priv_path_for(repo, "seeds.exs")
  148. defp priv_path_for(repo, filename) do
  149. app = Keyword.get(repo.config, :otp_app)
  150. IO.puts("App: #{app}")
  151. repo_underscore = repo |> Module.split() |> List.last() |> Macro.underscore()
  152. Path.join([priv_dir(app), repo_underscore, filename])
  153. end
  154. defp priv_dir(app), do: "#{:code.priv_dir(app)}"
  155. end