Skip to content

Commit

Permalink
improvement: install from private hex repos and orgs
Browse files Browse the repository at this point in the history
  • Loading branch information
zachallaun committed Nov 15, 2024
1 parent 6c974f4 commit 406f3d6
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 80 deletions.
150 changes: 75 additions & 75 deletions lib/igniter/project/deps.ex
Original file line number Diff line number Diff line change
Expand Up @@ -261,93 +261,93 @@ defmodule Igniter.Project.Deps do

@doc false
def determine_dep_type_and_version(requirement) do
case String.split(requirement, "@", trim: true, parts: 2) do
[package] ->
if Regex.match?(~r/^[a-z][a-z0-9_]*$/, package) do
:inets.start()
:ssl.start()
url = ~c"https://hex.pm/api/packages/#{package}"

case :httpc.request(:get, {url, [{~c"User-Agent", ~c"igniter-installer"}]}, [], []) do
{:ok, {{_version, _, _reason_phrase}, _headers, body}} ->
case Jason.decode(body) do
{:ok,
%{
"releases" => releases
} = body} ->
case first_non_rc_version_or_first_version(releases, body) do
%{"version" => version} ->
{String.to_atom(package),
Igniter.Util.Version.version_string_to_general_requirement!(version)}

_ ->
:error
end

_ ->
:error
end
[package | maybe_version] = String.split(requirement, "@", trim: true, parts: 2)

_ ->
:error
end
else
:error
end
{package, repo_opts} =
case String.split(package, ":", parts: 2) do
[repo, package] -> {package, repo: repo}
[package] -> {package, []}
end

[package, version] ->
case version do
"git:" <> requirement ->
if String.contains?(requirement, "@") do
case String.split(requirement, ["@"], trim: true) do
[url, ref] ->
[git: url, ref: ref, override: true]
{package, org_opts} =
case String.split(package, "/", parts: 2) do
[org, package] -> {package, organization: org}
[package] -> {package, []}
end

_ ->
:error
end
else
[git: requirement, override: true]
end
case maybe_version do
[] ->
with {:ok, version} <- fetch_latest_version(package) do
{version, []}
end

"github:" <> requirement ->
if String.contains?(requirement, "@") do
case String.split(requirement, ["/", "@"], trim: true) do
[org, project, ref] ->
[github: "#{org}/#{project}", ref: ref, override: true]
["git:" <> requirement] ->
{nil, git_dep_opts(requirement, :git)}

_ ->
:error
end
else
[github: requirement, override: true]
end
["github:" <> requirement] ->
{nil, git_dep_opts(requirement, :github)}

"path:" <> requirement ->
[path: requirement, override: true]
["path:" <> requirement] ->
{nil, path: requirement, override: true}

version ->
case Version.parse(version) do
{:ok, version} ->
"== #{version}"
[version] ->
case Version.parse(version) do
{:ok, version} ->
{"== #{version}", []}

_ ->
case Igniter.Util.Version.version_string_to_general_requirement(version) do
{:ok, requirement} ->
requirement
_ ->
case Igniter.Util.Version.version_string_to_general_requirement(version) do
{:ok, requirement} ->
{requirement, []}

_ ->
:error
end
_ ->
:error
end
end
|> case do
:error ->
:error

requirement ->
{String.to_atom(package), requirement}
end
_ ->
:error
end
|> case do
{version, additional_opts} ->
to_dependency_spec(package, version, additional_opts ++ repo_opts ++ org_opts)

:error ->
:error
end
end

defp to_dependency_spec(package, version, opts)
defp to_dependency_spec(package, version, []), do: {String.to_atom(package), version}
defp to_dependency_spec(package, nil, opts), do: {String.to_atom(package), opts}
defp to_dependency_spec(package, version, opts), do: {String.to_atom(package), version, opts}

defp git_dep_opts(string, kind) do
case String.split(string, "@", trim: true) do
[git_dep, ref] ->
[{kind, git_dep}, {:ref, ref}, {:override, true}]

[git_dep] ->
[{kind, git_dep}, {:override, true}]
end
end

defp fetch_latest_version(package) do
if Regex.match?(~r/^[a-z][a-z0-9_]*$/, package) do
:inets.start()
:ssl.start()
url = ~c"https://hex.pm/api/packages/#{package}"

with {:ok, {{_version, _, _reason_phrase}, _headers, body}} <-
:httpc.request(:get, {url, [{~c"User-Agent", ~c"igniter-installer"}]}, [], []),
{:ok, %{"releases" => releases} = body} <- Jason.decode(body),
%{"version" => version} <- first_non_rc_version_or_first_version(releases, body) do
{:ok, Igniter.Util.Version.version_string_to_general_requirement!(version)}
else
_ -> :error
end
else
:error
end
end

Expand Down
6 changes: 3 additions & 3 deletions lib/igniter/util/install.ex
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@ defmodule Igniter.Util.Install do
deps =
Enum.map(deps, fn dep ->
case Igniter.Project.Deps.determine_dep_type_and_version(dep) do
{install, requirement} ->
{install, requirement}

:error ->
raise "Could not determine source for requested package #{dep}"

dep_specification ->
dep_specification
end
end)

Expand Down
12 changes: 10 additions & 2 deletions lib/mix/tasks/igniter.install.ex
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
defmodule Mix.Tasks.Igniter.Install do
@moduledoc """
Install a package or packages, and run any associated installers.
Install a package or packages, running any Igniter installers.
## Args
mix igniter.install package1 package2 package3
mix igniter.install package1 package2 package3
## Package formats
Expand All @@ -16,6 +16,14 @@ defmodule Mix.Tasks.Igniter.Install do
* `package@git:git_url` - The package will be installed from the specified git url.
* `package@github:project/repo` - The package will be installed from the specified github repo.
* `package@path:path/to/dep` - The package will be installed from the specified path.
* `org/package` - The package exists in a private Hex organization. This can be used
along with all the options above, e.g. `org/package@version`.
* `repo:package` - The package exists in a private Hex repo. This can b e used along
with all the options above, e.g. `repo:package@version`.
If you are specifying a private repo and organization, the repo must come first:
mix igniter.install repo:org/package@version
## Switches
Expand Down
32 changes: 32 additions & 0 deletions test/igniter/project/deps_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,36 @@ defmodule Igniter.Project.DepsTest do
""")
end
end

describe "determine_dep_type_and_version/1" do
test "parses to a version" do
tests = [
"[email protected]": {:dep, "== 1.0.0"},
"[email protected]": {:dep, "~> 1.0"},
"dep@git:git_url": {:dep, git: "git_url", override: true},
"dep@git:git_url@ref": {:dep, git: "git_url", ref: "ref", override: true},
"dep@github:user/repo": {:dep, github: "user/repo", override: true},
"dep@github:user/repo@ref": {:dep, github: "user/repo", ref: "ref", override: true},
"dep@path:path/to/dep": {:dep, path: "path/to/dep", override: true},
"org/[email protected]": {:dep, "== 1.0.0", organization: "org"},
"org/[email protected]": {:dep, "~> 1.0", organization: "org"},
"org/dep@git:git_url": {:dep, git: "git_url", override: true, organization: "org"},
"org/dep@git:git_url@ref":
{:dep, git: "git_url", ref: "ref", override: true, organization: "org"},
"org/dep@github:user/repo":
{:dep, github: "user/repo", override: true, organization: "org"},
"org/dep@github:user/repo@ref":
{:dep, github: "user/repo", ref: "ref", override: true, organization: "org"},
"org/dep@path:path/to/dep":
{:dep, path: "path/to/dep", override: true, organization: "org"},
"repo:[email protected]": {:dep, "== 1.0.0", repo: "repo"},
"repo:org/[email protected]": {:dep, "== 1.0.0", repo: "repo", organization: "org"}
]

for {spec, expected} <- tests do
assert spec |> to_string() |> Igniter.Project.Deps.determine_dep_type_and_version() ==
expected
end
end
end
end

0 comments on commit 406f3d6

Please sign in to comment.