Skip to content

Commit 20a3df0

Browse files
committed
Support PHX_NEW_CACHE_DIR for cached builds
1 parent 2614f2a commit 20a3df0

File tree

3 files changed

+93
-1
lines changed

3 files changed

+93
-1
lines changed

installer/lib/mix/tasks/phx.new.ex

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,28 @@ defmodule Mix.Tasks.Phx.New do
113113
114114
You can read more about umbrella projects using the
115115
official [Elixir guide](https://hexdocs.pm/elixir/dependencies-and-umbrella-projects.html#umbrella-projects)
116+
117+
## PHX_NEW_CACHE_DIR
118+
In rare cases, it may be useful to copy the build from a previously
119+
cached build. To do this, set the `PHX_NEW_CACHE_DIR` environment
120+
variable before running `mix phx.new`. For example, you could generate a
121+
cache by running:
122+
123+
```shell
124+
mix phx.new mycache --no-install && cd mycache \
125+
&& mix deps.get && mix deps.compile && mix assets.setup \
126+
&& rm -rf assets config lib priv test mix.exs README.md
127+
```
128+
129+
Your cached build directory
130+
should contain:
131+
132+
_build
133+
deps
134+
mix.lock
135+
136+
The entire cache directory will be copied to the new project, replacing
137+
any existing files where conflicts exist.
116138
"""
117139
use Mix.Task
118140
alias Phx.New.{Generator, Project, Single, Umbrella, Web, Ecto}
@@ -185,7 +207,8 @@ defmodule Mix.Tasks.Phx.New do
185207
|> Generator.put_binding()
186208
|> validate_project(path)
187209
|> generator.generate()
188-
|> prompt_to_install_deps(generator, path)
210+
|> maybe_copy_cached_build(path)
211+
|> maybe_prompt_to_install_deps(generator, path)
189212
end
190213

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

223+
defp maybe_prompt_to_install_deps(%Project{} = project, generator, path_key) do
224+
# we can skip the install deps setup, even with --install, because we already copied deps
225+
if project.cached_build_path do
226+
project
227+
else
228+
prompt_to_install_deps(project, generator, path_key)
229+
end
230+
end
231+
200232
defp prompt_to_install_deps(%Project{} = project, generator, path_key) do
201233
path = Map.fetch!(project, path_key)
202234

@@ -404,4 +436,24 @@ defmodule Mix.Tasks.Phx.New do
404436
)
405437
end
406438
end
439+
440+
defp maybe_copy_cached_build(%Project{} = project, path_key) do
441+
project_path = Map.fetch!(project, path_key)
442+
443+
case System.fetch_env("PHX_NEW_CACHE_DIR") do
444+
{:ok, cache_dir} ->
445+
copy_cached_build(%{project_path: project_path, cache_dir: cache_dir})
446+
%Project{project | cached_build_path: cache_dir}
447+
448+
:error ->
449+
project
450+
end
451+
end
452+
453+
defp copy_cached_build(%{project_path: project_path, cache_dir: cache_dir}) do
454+
if File.exists?(cache_dir) do
455+
Mix.shell().info("Copying cached build from #{cache_dir}")
456+
System.cmd("cp", ["-Rp", Path.join(cache_dir, "."), project_path])
457+
end
458+
end
407459
end

installer/lib/phx_new/project.ex

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ defmodule Phx.New.Project do
1616
opts: :unset,
1717
in_umbrella?: false,
1818
binding: [],
19+
cached_build_path: nil,
1920
generators: [timestamp_type: :utc_datetime]
2021

2122
def new(project_path, opts) do

installer/test/phx_new_test.exs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -829,4 +829,43 @@ defmodule Mix.Tasks.Phx.NewTest do
829829
end)
830830
end)
831831
end
832+
833+
describe "PHX_CACHE_DIR" do
834+
@phx_new_cache_dir System.get_env("PHX_NEW_CACHE_DIR")
835+
test "new with PHX_NEW_CACHE_DIR" do
836+
System.put_env("PHX_NEW_CACHE_DIR", __DIR__)
837+
cache_files = File.ls!(__DIR__)
838+
in_tmp("new with cache dir", fn ->
839+
Mix.Tasks.Phx.New.run([@app_name])
840+
project_files = File.ls!(Path.join(File.cwd!(), @app_name))
841+
assert "mix.exs" in project_files
842+
for file <- cache_files do
843+
assert file in project_files, "#{file} not copied to new project"
844+
end
845+
end)
846+
after
847+
if @phx_new_cache_dir do
848+
System.put_env("PHX_NEW_CACHE_DIR", @phx_new_cache_dir)
849+
else
850+
System.delete_env("PHX_NEW_CACHE_DIR")
851+
end
852+
end
853+
854+
test "new with PHX_NEW_CACHE_DIR that doesn't exist" do
855+
cache_dir = Path.join(__DIR__, "does-not-exist")
856+
System.put_env("PHX_NEW_CACHE_DIR", cache_dir)
857+
refute File.exists?(cache_dir)
858+
in_tmp("new with cache dir", fn ->
859+
Mix.Tasks.Phx.New.run([@app_name])
860+
project_files = File.ls!(Path.join(File.cwd!(), @app_name))
861+
assert "mix.exs" in project_files
862+
end)
863+
after
864+
if @phx_new_cache_dir do
865+
System.put_env("PHX_NEW_CACHE_DIR", @phx_new_cache_dir)
866+
else
867+
System.delete_env("PHX_NEW_CACHE_DIR")
868+
end
869+
end
870+
end
832871
end

0 commit comments

Comments
 (0)