Skip to content

Commit

Permalink
Support PHX_NEW_CACHE_DIR for cached builds
Browse files Browse the repository at this point in the history
  • Loading branch information
chrismccord committed Feb 20, 2025
1 parent 2614f2a commit 20a3df0
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 1 deletion.
54 changes: 53 additions & 1 deletion installer/lib/mix/tasks/phx.new.ex
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,28 @@ defmodule Mix.Tasks.Phx.New do
You can read more about umbrella projects using the
official [Elixir guide](https://hexdocs.pm/elixir/dependencies-and-umbrella-projects.html#umbrella-projects)
## PHX_NEW_CACHE_DIR
In rare cases, it may be useful to copy the build from a previously
cached build. To do this, set the `PHX_NEW_CACHE_DIR` environment
variable before running `mix phx.new`. For example, you could generate a
cache by running:
```shell
mix phx.new mycache --no-install && cd mycache \
&& mix deps.get && mix deps.compile && mix assets.setup \
&& rm -rf assets config lib priv test mix.exs README.md
```
Your cached build directory
should contain:
_build
deps
mix.lock
The entire cache directory will be copied to the new project, replacing
any existing files where conflicts exist.
"""
use Mix.Task
alias Phx.New.{Generator, Project, Single, Umbrella, Web, Ecto}
Expand Down Expand Up @@ -185,7 +207,8 @@ defmodule Mix.Tasks.Phx.New do
|> Generator.put_binding()
|> validate_project(path)
|> generator.generate()
|> prompt_to_install_deps(generator, path)
|> maybe_copy_cached_build(path)
|> maybe_prompt_to_install_deps(generator, path)
end

defp validate_project(%Project{opts: opts} = project, path) do
Expand All @@ -197,6 +220,15 @@ defmodule Mix.Tasks.Phx.New do
project
end

defp maybe_prompt_to_install_deps(%Project{} = project, generator, path_key) do
# we can skip the install deps setup, even with --install, because we already copied deps
if project.cached_build_path do
project
else
prompt_to_install_deps(project, generator, path_key)
end
end

defp prompt_to_install_deps(%Project{} = project, generator, path_key) do
path = Map.fetch!(project, path_key)

Expand Down Expand Up @@ -404,4 +436,24 @@ defmodule Mix.Tasks.Phx.New do
)
end
end

defp maybe_copy_cached_build(%Project{} = project, path_key) do
project_path = Map.fetch!(project, path_key)

case System.fetch_env("PHX_NEW_CACHE_DIR") do
{:ok, cache_dir} ->
copy_cached_build(%{project_path: project_path, cache_dir: cache_dir})
%Project{project | cached_build_path: cache_dir}

:error ->
project
end
end

defp copy_cached_build(%{project_path: project_path, cache_dir: cache_dir}) do
if File.exists?(cache_dir) do
Mix.shell().info("Copying cached build from #{cache_dir}")
System.cmd("cp", ["-Rp", Path.join(cache_dir, "."), project_path])
end
end
end
1 change: 1 addition & 0 deletions installer/lib/phx_new/project.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ defmodule Phx.New.Project do
opts: :unset,
in_umbrella?: false,
binding: [],
cached_build_path: nil,
generators: [timestamp_type: :utc_datetime]

def new(project_path, opts) do
Expand Down
39 changes: 39 additions & 0 deletions installer/test/phx_new_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -829,4 +829,43 @@ defmodule Mix.Tasks.Phx.NewTest do
end)
end)
end

describe "PHX_CACHE_DIR" do
@phx_new_cache_dir System.get_env("PHX_NEW_CACHE_DIR")
test "new with PHX_NEW_CACHE_DIR" do
System.put_env("PHX_NEW_CACHE_DIR", __DIR__)
cache_files = File.ls!(__DIR__)
in_tmp("new with cache dir", fn ->
Mix.Tasks.Phx.New.run([@app_name])
project_files = File.ls!(Path.join(File.cwd!(), @app_name))
assert "mix.exs" in project_files
for file <- cache_files do
assert file in project_files, "#{file} not copied to new project"
end
end)
after
if @phx_new_cache_dir do
System.put_env("PHX_NEW_CACHE_DIR", @phx_new_cache_dir)
else
System.delete_env("PHX_NEW_CACHE_DIR")
end
end

test "new with PHX_NEW_CACHE_DIR that doesn't exist" do
cache_dir = Path.join(__DIR__, "does-not-exist")
System.put_env("PHX_NEW_CACHE_DIR", cache_dir)
refute File.exists?(cache_dir)
in_tmp("new with cache dir", fn ->
Mix.Tasks.Phx.New.run([@app_name])
project_files = File.ls!(Path.join(File.cwd!(), @app_name))
assert "mix.exs" in project_files
end)
after
if @phx_new_cache_dir do
System.put_env("PHX_NEW_CACHE_DIR", @phx_new_cache_dir)
else
System.delete_env("PHX_NEW_CACHE_DIR")
end
end
end
end

0 comments on commit 20a3df0

Please sign in to comment.