Packages
igniter
0.1.4
0.8.2
0.8.1
0.8.0
0.7.9
0.7.8
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.30
0.6.29
0.6.28
0.6.27
0.6.26
0.6.25
0.6.24
0.6.23
0.6.22
0.6.21
0.6.20
0.6.19
0.6.18
0.6.17
0.6.16
0.6.15
0.6.14
0.6.13
0.6.12
0.6.11
0.6.10
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.2
0.6.1
0.6.0
0.5.52
0.5.51
0.5.50
0.5.49
0.5.48
0.5.47
0.5.46
0.5.45
0.5.44
0.5.43
0.5.42
0.5.41
0.5.40
0.5.39
0.5.38
0.5.37
0.5.36
0.5.35
0.5.34
0.5.33
0.5.32
0.5.31
0.5.30
0.5.29
0.5.28
0.5.27
0.5.26
0.5.25
0.5.24
0.5.23
0.5.22
0.5.21
0.5.20
0.5.19
0.5.18
0.5.17
0.5.16
0.5.15
0.5.14
0.5.13
0.5.12
0.5.11
0.5.10
0.5.9
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.78
0.3.77
0.3.76
0.3.75
0.3.74
0.3.73
0.3.72
0.3.71
0.3.70
0.3.69
0.3.68
0.3.67
0.3.66
0.3.65
0.3.64
0.3.63
0.3.62
0.3.61
0.3.60
0.3.59
0.3.58
0.3.57
0.3.56
0.3.55
0.3.54
0.3.53
0.3.52
0.3.51
0.3.50
0.3.49
0.3.48
0.3.47
0.3.46
0.3.45
0.3.44
0.3.43
0.3.42
0.3.41
0.3.40
0.3.39
0.3.38
0.3.37
0.3.36
0.3.35
0.3.34
0.3.33
0.3.32
0.3.31
0.3.30
0.3.29
0.3.28
0.3.27
0.3.26
0.3.25
0.3.24
0.3.23
0.3.22
0.3.21
0.3.20
0.3.19
0.3.18
0.3.17
0.3.16
0.3.15
0.3.14
0.3.13
0.3.12
0.3.11
0.3.10
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.13
0.2.12
0.2.11
0.2.10
0.2.9
0.2.8
0.2.7
0.2.6
0.2.5
0.2.4
0.2.3
0.2.2
0.2.1
0.2.0
0.1.8
0.1.7
0.1.6
0.1.5
0.1.4
0.1.3
0.1.2
0.1.1
0.1.0
A code generation and project patching framework
Current section
Files
Jump to
Current section
Files
lib/install.ex
defmodule Igniter.Install do
@moduledoc false
@option_schema [
strict: [
example: :boolean,
dry_run: :boolean,
yes: :boolean
],
aliases: [
d: :dry_run,
e: :example,
y: :yes
]
]
# sobelow_skip ["DOS.StringToAtom"]
def install(install, argv) do
install_list =
if is_binary(install) do
String.split(install, ",")
else
Enum.map(List.wrap(install), &to_string/1)
end
Application.ensure_all_started(:req)
{options, _errors, _unprocessed_argv} =
OptionParser.parse(argv, @option_schema)
igniter = Igniter.new()
{igniter, install_list} =
install_list
|> Enum.reduce({igniter, []}, fn install, {igniter, install_list} ->
case determine_dep_type_and_version(install) do
{install, requirement} ->
install = String.to_atom(install)
if local_dep?(install) do
Mix.shell().info(
"Not looking up dependency for #{install}, because a local dependency is detected"
)
{igniter, [install | install_list]}
else
{Igniter.Deps.add_dependency(igniter, install, requirement, "--yes" in argv),
[install | install_list]}
end
:error ->
{Igniter.add_issue(
igniter,
"Could not determine source for requested package #{install}"
), install_list}
end
end)
confirmation_message =
unless options[:dry_run] do
"Dependencies changes must go into effect before individual installers can be run. Proceed with changes?"
end
dependency_add_result =
Igniter.do_or_dry_run(igniter, argv,
title: "Fetching Dependency",
quiet_on_no_changes?: true,
confirmation_message: confirmation_message
)
if dependency_add_result == :issues do
raise "Exiting due to issues found while fetching dependency"
end
if dependency_add_result == :dry_run_with_changes do
install_dep_now? =
Mix.shell().yes?("""
Cannot run any associated installers for the requested packages without
commiting changes and fetching dependencies.
Would you like to do so now? The remaining steps will be displayed as a dry run.
""")
if install_dep_now? do
Igniter.do_or_dry_run(igniter, (argv ++ ["--yes"]) -- ["--dry-run"],
title: "Fetching Dependency",
quiet_on_no_changes?: true
)
end
end
if dependency_add_result == :changes_aborted do
Mix.shell().info("\nChanges aborted by user request.")
else
Mix.shell().info("running mix deps.get")
case Mix.shell().cmd("mix deps.get") do
0 ->
Mix.Task.reenable("compile")
Mix.Task.run("compile")
exit_code ->
Mix.shell().info("""
mix deps.get returned exited with code: `#{exit_code}`
""")
end
all_tasks =
Enum.filter(Mix.Task.load_all(), &implements_behaviour?(&1, Igniter.Mix.Task))
igniter =
Igniter.new()
|> Igniter.assign(%{manually_installed: install_list})
install_list
|> Enum.flat_map(fn install ->
all_tasks
|> Enum.find(fn task ->
Mix.Task.task_name(task) == "#{install}.install" &&
implements_behaviour?(task, Igniter.Mix.Task)
end)
|> List.wrap()
end)
|> Enum.reduce(igniter, fn task, igniter ->
Igniter.compose_task(igniter, task, argv)
end)
|> Igniter.do_or_dry_run(argv)
end
:ok
end
defp implements_behaviour?(module, behaviour) do
:attributes
|> module.module_info()
|> Enum.any?(fn
{:behaviour, ^behaviour} ->
true
# optimizations, probably extremely minor but this is in a tight loop in some places
{:behaviour, [^behaviour | _]} ->
true
{:behaviour, [_, ^behaviour | _]} ->
true
{:behaviour, [_, _, ^behaviour | _]} ->
true
# never seen a module with three behaviours in real life, let alone four.
{:behaviour, behaviours} when is_list(behaviours) ->
module in behaviours
_ ->
false
end)
rescue
_ ->
false
end
defp local_dep?(install) do
config = Mix.Project.config()[:deps][install]
Keyword.keyword?(config) && config[:path]
end
defp determine_dep_type_and_version(requirement) do
case String.split(requirement, "@", trim: true) do
[package] ->
if Regex.match?(~r/^[a-z][a-z0-9_]*$/, package) do
case Req.get!("https://hex.pm/api/packages/#{package}").body do
%{
"releases" => [
%{"version" => version}
| _
]
} ->
{package, Igniter.Version.version_string_to_general_requirement!(version)}
_ ->
:error
end
else
:error
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]
_ ->
:error
end
else
[git: requirement]
end
"github:" <> requirement ->
if String.contains?(requirement, "@") do
case String.split(requirement, ["/", "@"], trim: true) do
[org, project, ref] ->
[github: "#{org}/#{project}", ref: ref]
_ ->
:error
end
else
[github: requirement]
end
"path:" <> requirement ->
[path: requirement]
version ->
case Version.parse(version) do
{:ok, version} ->
"== #{version}"
_ ->
case Igniter.Version.version_string_to_general_requirement(version) do
{:ok, requirement} ->
requirement
_ ->
:error
end
end
end
|> case do
:error ->
:error
requirement ->
{package, requirement}
end
end
end
end