-
Notifications
You must be signed in to change notification settings - Fork 44
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add make_precompiler_downloader option (#87)
Allows users to customize download behavior, such as adding HTTP authentication or using an alternate protocol like SFTP.
- Loading branch information
1 parent
d12a4a6
commit 4ce33b5
Showing
7 changed files
with
220 additions
and
142 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
defmodule ElixirMake.Downloader do | ||
@moduledoc """ | ||
The behaviour for downloader modules. | ||
""" | ||
|
||
@doc """ | ||
This callback should download the artefact from the given URL. | ||
""" | ||
@callback download(url :: String.t()) :: {:ok, iolist() | binary()} | {:error, String.t()} | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
defmodule ElixirMake.Downloader.Httpc do | ||
@moduledoc false | ||
|
||
@behaviour ElixirMake.Downloader | ||
|
||
@impl ElixirMake.Downloader | ||
def download(url) do | ||
url_charlist = String.to_charlist(url) | ||
|
||
# TODO: Remove me when we require Elixir v1.15 | ||
{:ok, _} = Application.ensure_all_started(:inets) | ||
{:ok, _} = Application.ensure_all_started(:ssl) | ||
{:ok, _} = Application.ensure_all_started(:public_key) | ||
|
||
if proxy = System.get_env("HTTP_PROXY") || System.get_env("http_proxy") do | ||
Mix.shell().info("Using HTTP_PROXY: #{proxy}") | ||
%{host: host, port: port} = URI.parse(proxy) | ||
|
||
:httpc.set_options([{:proxy, {{String.to_charlist(host), port}, []}}]) | ||
end | ||
|
||
if proxy = System.get_env("HTTPS_PROXY") || System.get_env("https_proxy") do | ||
Mix.shell().info("Using HTTPS_PROXY: #{proxy}") | ||
%{host: host, port: port} = URI.parse(proxy) | ||
:httpc.set_options([{:https_proxy, {{String.to_charlist(host), port}, []}}]) | ||
end | ||
|
||
# https://erlef.github.io/security-wg/secure_coding_and_deployment_hardening/inets | ||
# TODO: This may no longer be necessary from Erlang/OTP 25.0 or later. | ||
https_options = [ | ||
ssl: | ||
[ | ||
verify: :verify_peer, | ||
customize_hostname_check: [ | ||
match_fun: :public_key.pkix_verify_hostname_match_fun(:https) | ||
] | ||
] ++ cacerts_options() | ||
] | ||
|
||
options = [body_format: :binary] | ||
|
||
case :httpc.request(:get, {url_charlist, []}, https_options, options) do | ||
{:ok, {{_, 200, _}, _headers, body}} -> | ||
{:ok, body} | ||
|
||
other -> | ||
{:error, "couldn't fetch NIF from #{url}: #{inspect(other)}"} | ||
end | ||
end | ||
|
||
defp cacerts_options do | ||
cond do | ||
path = System.get_env("ELIXIR_MAKE_CACERT") -> | ||
[cacertfile: path] | ||
|
||
certs = otp_cacerts() -> | ||
[cacerts: certs] | ||
|
||
Application.spec(:castore, :vsn) -> | ||
[cacertfile: Application.app_dir(:castore, "priv/cacerts.pem")] | ||
|
||
Application.spec(:certifi, :vsn) -> | ||
[cacertfile: Application.app_dir(:certifi, "priv/cacerts.pem")] | ||
|
||
path = cacerts_from_os() -> | ||
[cacertfile: path] | ||
|
||
true -> | ||
warn_no_cacerts() | ||
[] | ||
end | ||
end | ||
|
||
defp otp_cacerts do | ||
if System.otp_release() >= "25" do | ||
# cacerts_get/0 raises if no certs found | ||
try do | ||
:public_key.cacerts_get() | ||
rescue | ||
_ -> | ||
nil | ||
end | ||
end | ||
end | ||
|
||
# https_opts and related code are taken from | ||
# https://github.com/elixir-cldr/cldr_utils/blob/v2.19.1/lib/cldr/http/http.ex | ||
@certificate_locations [ | ||
# Debian/Ubuntu/Gentoo etc. | ||
"/etc/ssl/certs/ca-certificates.crt", | ||
|
||
# Fedora/RHEL 6 | ||
"/etc/pki/tls/certs/ca-bundle.crt", | ||
|
||
# OpenSUSE | ||
"/etc/ssl/ca-bundle.pem", | ||
|
||
# OpenELEC | ||
"/etc/pki/tls/cacert.pem", | ||
|
||
# CentOS/RHEL 7 | ||
"/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem", | ||
|
||
# Open SSL on MacOS | ||
"/usr/local/etc/openssl/cert.pem", | ||
|
||
# MacOS & Alpine Linux | ||
"/etc/ssl/cert.pem" | ||
] | ||
|
||
defp cacerts_from_os do | ||
Enum.find(@certificate_locations, &File.exists?/1) | ||
end | ||
|
||
defp warn_no_cacerts do | ||
Mix.shell().error(""" | ||
No certificate trust store was found. | ||
Tried looking for: #{inspect(@certificate_locations)} | ||
A certificate trust store is required in | ||
order to download locales for your configuration. | ||
Since elixir_make could not detect a system | ||
installed certificate trust store one of the | ||
following actions may be taken: | ||
1. Install the hex package `castore`. It will | ||
be automatically detected after recompilation. | ||
2. Install the hex package `certifi`. It will | ||
be automatically detected after recompilation. | ||
3. Specify the location of a certificate trust store | ||
by configuring it in environment variable: | ||
export ELIXIR_MAKE_CACERT="/path/to/cacerts.pem" | ||
4. Use OTP 25+ on an OS that has built-in certificate | ||
trust store. | ||
""") | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.