diff --git a/fixtures/example/deps.nix b/fixtures/example/deps.nix index c2917f9..53ca22a 100644 --- a/fixtures/example/deps.nix +++ b/fixtures/example/deps.nix @@ -377,6 +377,20 @@ let in drv; + daisyui = stdenv.mkDerivation { + name = "daisyui"; + src = fetchFromGitHub { + owner = "saadeghi"; + repo = "daisyui"; + rev = "22ecff57f2c391b80a75617325748cf4d13fdf47"; + hash = "sha256-I2LI9VYVxQcvoMDgDWmvNyQToS1rFm12V167YxqDs24="; + }; + buildPhase = '' + mkdir $out + ln -sv $src $out/src + ''; + }; + db_connection = let version = "2.10.2"; diff --git a/fixtures/example/mix.exs b/fixtures/example/mix.exs index 220161f..94b3ed8 100644 --- a/fixtures/example/mix.exs +++ b/fixtures/example/mix.exs @@ -23,6 +23,13 @@ defmodule Example.MixProject do [ {:bandit, github: "mtrudel/bandit", ref: "1.4.2", override: true}, {:brod, "~> 3.16"}, + {:daisyui, + github: "saadeghi/daisyui", + tag: "v5.5.20", + sparse: "packages/bundle", + app: false, + compile: false, + depth: 1}, {:deps_nix, path: "../..", only: [:dev]}, {:eventstore, "~> 1.4"}, {:ex_heroicons, "~> 3.1"}, diff --git a/fixtures/example/mix.lock b/fixtures/example/mix.lock index 83032a6..678b46f 100644 --- a/fixtures/example/mix.lock +++ b/fixtures/example/mix.lock @@ -9,6 +9,7 @@ "cldr_utils": {:hex, :cldr_utils, "2.29.7", "2189bc0117efe91c684558e79174b45eb43135595b7d1fe9b57f53917be195c1", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:certifi, "~> 2.5", [hex: :certifi, repo: "hexpm", optional: true]}, {:decimal, "~> 1.9 or ~> 2.0 or ~> 3.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "4bddcd597fee34e2d2829ae9ef62bcfef8d97ae5f6b75f0c6ee37a3db31aa73a"}, "crc32cer": {:hex, :crc32cer, "0.1.8", "c6c2275c5fb60a95f4935d414f30b50ee9cfed494081c9b36ebb02edfc2f48db", [:rebar3], [], "hexpm", "251499085482920deb6c9b7aadabf9fb4c432f96add97ab42aee4501e5b6f591"}, "ctx": {:hex, :ctx, "0.6.0", "8ff88b70e6400c4df90142e7f130625b82086077a45364a78d208ed3ed53c7fe", [:rebar3], [], "hexpm", "a14ed2d1b67723dbebbe423b28d7615eb0bdcba6ff28f2d1f1b0a7e1d4aa5fc2"}, + "daisyui": {:git, "https://github.com/saadeghi/daisyui.git", "22ecff57f2c391b80a75617325748cf4d13fdf47", [tag: "v5.5.20", sparse: "packages/bundle", depth: 1]}, "db_connection": {:hex, :db_connection, "2.10.2", "ae391e803a5adff104da913c2fc1c0c14a37f8b10001dcef568796e1fb7bf95c", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "510b14482330f1af6490a2fa0efd8d4f1435d1529b165647df22ac0f2df0fa93"}, "decimal": {:hex, :decimal, "3.1.1", "430d87b04011ce6cbd4fd205be758311a81f87d552d40904abd00f015935b1d0", [:mix], [], "hexpm", "c5f25f2ced74a0587d03e6023f595db8e924c9d3922c8c8ffd9edfc4498cf1f6"}, "digital_token": {:hex, :digital_token, "2.0.0", "fb3c74411790994895c4a61387ab322b64a682995ef2f54a4627c92d46be5d62", [:mix], [], "hexpm", "cbd2fff52770284a8251540a4b4e529e9738c6fe052d7f3c3428eb5c817385cd"}, diff --git a/lib/deps_nix.ex b/lib/deps_nix.ex index 4922c6e..b3810cb 100644 --- a/lib/deps_nix.ex +++ b/lib/deps_nix.ex @@ -190,11 +190,10 @@ defmodule DepsNix do end end + # Fetch-only deps become plain source derivations, but that only works for + # git checkouts; anything else with `app: false, compile: false` is dropped. defp unwanted(dep) do - dep.app != :heroicons && - Enum.all?([:app, :compile], fn opt -> - Keyword.fetch(dep.opts, opt) == {:ok, false} - end) + dep.scm != Mix.SCM.Git && Packages.fetch_only?(dep) end defp add_output(%Options{} = options, parsed_args) do diff --git a/lib/deps_nix/derivation.ex b/lib/deps_nix/derivation.ex index d59fdbd..71295a3 100644 --- a/lib/deps_nix/derivation.ex +++ b/lib/deps_nix/derivation.ex @@ -13,7 +13,8 @@ defmodule DepsNix.Derivation do DepsNix.FetchFromGitHub.t() | DepsNix.FetchGit.t() | DepsNix.FetchHex.t(), - beam_deps: list(atom()) + beam_deps: list(atom()), + fetch_only: boolean() } @enforce_keys [ @@ -30,7 +31,8 @@ defmodule DepsNix.Derivation do :name, :version, :src, - :beam_deps + :beam_deps, + fetch_only: false ] def new(dep, opts) do @@ -38,7 +40,8 @@ defmodule DepsNix.Derivation do __MODULE__, [ name: dep.app, - beam_deps: Enum.map(dep.deps, & &1.app) + beam_deps: Enum.map(dep.deps, & &1.app), + fetch_only: DepsNix.Packages.fetch_only?(dep) ] |> Keyword.merge(opts) ) @@ -196,10 +199,12 @@ defmodule DepsNix.Derivation do """ end - def to_string(%DepsNix.Derivation{name: :heroicons} = drv) do + # Fetch-only deps aren't BEAM packages; expose the bare checkout so + # consumers can symlink ${pkg}/src into deps/. + def to_string(%DepsNix.Derivation{fetch_only: true} = drv) do """ #{drv.name} = stdenv.mkDerivation { - name = "heroicons"; + name = "#{drv.name}"; src = #{drv.src |> Kernel.to_string() |> Util.indent(from: 1)} buildPhase = '' mkdir $out diff --git a/lib/deps_nix/packages.ex b/lib/deps_nix/packages.ex index 304268f..09713c7 100644 --- a/lib/deps_nix/packages.ex +++ b/lib/deps_nix/packages.ex @@ -28,6 +28,16 @@ defmodule DepsNix.Packages do Enum.filter(packages, &("#{&1.app}" in permitted_names)) end + @doc """ + Deps declared with `app: false, compile: false` are plain source checkouts + (e.g. asset repos like heroicons or daisyui) rather than BEAM packages. + """ + def fetch_only?(%Mix.Dep{} = dep) do + Enum.all?([:app, :compile], fn opt -> + Keyword.fetch(dep.opts, opt) == {:ok, false} + end) + end + def reject_paths(deps) do deps |> Enum.reject(fn dep -> diff --git a/test/deps_nix/special_treatment_test.exs b/test/deps_nix/special_treatment_test.exs index a17c654..4db76d0 100644 --- a/test/deps_nix/special_treatment_test.exs +++ b/test/deps_nix/special_treatment_test.exs @@ -6,7 +6,7 @@ defmodule DepsNix.SpecialTreatmentTest do alias DepsNix.FetchFromGitHub alias DepsNix.FetchHex - test "heroicons needs a nested GitHub fetch" do + test "fetch-only deps like heroicons or daisyui get a nested GitHub fetch" do # the src dir is expected by mixRelease, which symlinks deps into place assert %Derivation{ builder: "buildMix", @@ -19,7 +19,8 @@ defmodule DepsNix.SpecialTreatmentTest do hash: "sha256-4yRqfY8r2Ar9Fr45ikD/8jK+H3g4veEHfXa9BorLxXg=" }, beam_deps: [], - app_config_path: "foo" + app_config_path: "foo", + fetch_only: true } |> to_string() == """ heroicons = stdenv.mkDerivation { @@ -36,6 +37,36 @@ defmodule DepsNix.SpecialTreatmentTest do ''; }; """ + + assert %Derivation{ + builder: "buildMix", + name: :daisyui, + version: nil, + src: %FetchFromGitHub{ + owner: "saadeghi", + repo: "daisyui", + rev: "4dbf88f9dd80e0917b7d366b0f4e28ec33ba6b52", + hash: "sha256-4yRqfY8r2Ar9Fr45ikD/8jK+H3g4veEHfXa9BorLxXg=" + }, + beam_deps: [], + app_config_path: "foo", + fetch_only: true + } + |> to_string() == """ + daisyui = stdenv.mkDerivation { + name = "daisyui"; + src = fetchFromGitHub { + owner = "saadeghi"; + repo = "daisyui"; + rev = "4dbf88f9dd80e0917b7d366b0f4e28ec33ba6b52"; + hash = "sha256-4yRqfY8r2Ar9Fr45ikD/8jK+H3g4veEHfXa9BorLxXg="; + }; + buildPhase = '' + mkdir $out + ln -sv $src $out/src + ''; + }; + """ end test "ex_heroicons symlinks the required heroicons into place" do diff --git a/test/deps_nix_test.exs b/test/deps_nix_test.exs index bfa56ed..1c7b243 100644 --- a/test/deps_nix_test.exs +++ b/test/deps_nix_test.exs @@ -166,17 +166,28 @@ defmodule DepsNixTest do assert output(%DepsNix.Options{}, &stub_converger/1) =~ "with self;\n {\n \n };" end - test "creates a special fetcher derivation for heroicons, which is included in new Phoenix apps" do - dep = dep(name: :heroicons, dep_opts: [app: false, compile: false]) |> pick() - converger = fn [env: :prod] -> [dep] end - nix = output(%DepsNix.Options{envs: %{"prod" => :all}}, converger) + property "creates fetch-only derivations for git deps marked app: false and compile: false" do + check all dep <- + dep( + name: :an_asset_dep, + scm: Mix.SCM.Git, + dep_opts: [app: false, compile: false] + ) do + converger = fn [env: :prod] -> [dep] end + nix = output(%DepsNix.Options{envs: %{"prod" => :all}}, converger) - assert Regex.scan(~r(heroicons), nix) |> length() >= 1, - "Couldn't find special-cased heroicons fetcher in #{nix}" + assert nix =~ "an_asset_dep = stdenv.mkDerivation", + "Couldn't find fetch-only fetcher for #{dep.app} in #{nix}" + end end - property "doesn't create derivations marked both app: false and compile: false" do - check all dep <- dep(name: :not_a_mix_dep, dep_opts: [app: false, compile: false]) do + property "doesn't create derivations for non-git deps marked both app: false and compile: false" do + check all dep <- + dep( + name: :not_a_mix_dep, + scm: Mix.SCM.Hex, + dep_opts: [app: false, compile: false] + ) do converger = fn [env: :prod] -> [dep] end nix = output(%DepsNix.Options{envs: %{"prod" => :all}}, converger)