Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 17 additions & 22 deletions lib/mix/lib/mix/local.ex
Original file line number Diff line number Diff line change
Expand Up @@ -232,40 +232,35 @@ defmodule Mix.Local do

defp find_latest_eligible_version(entries, artifact_version) do
elixir_version = Version.parse!(System.version())
otp_release = System.otp_release()

entries
|> Enum.reverse()
|> find_version(artifact_version, elixir_version)
|> find_version(artifact_version, elixir_version, otp_release)
end

defp find_version(entries, _artifact_version = nil, elixir_version) do
Enum.find_value(entries, &find_by_elixir_version(&1, elixir_version))
end
defp find_version(entries, artifact_version, elixir_version, otp_release) do
entries =
if artifact_version do
Enum.filter(entries, &(hd(&1) == artifact_version))
else
entries
end

defp find_version(entries, artifact_version, elixir_version) do
Enum.find_value(entries, &find_by_artifact_version(&1, artifact_version, elixir_version))
Enum.find_value(entries, &find_by_elixir_version(&1, elixir_version, otp_release))
end

defp find_by_elixir_version([artifact_version, digest | versions], elixir_version) do
if version = Enum.find(versions, &(Version.compare(&1, elixir_version) != :gt)) do
{version, artifact_version, digest}
end
end

defp find_by_artifact_version(
[artifact_version, digest | versions],
artifact_version,
elixir_version
defp find_by_elixir_version(
[artifact_version, digest, hex_elixir_version, hex_otp_release | _],
elixir_version,
otp_release
) do
if version = Enum.find(versions, &(Version.compare(&1, elixir_version) != :gt)) do
{version, artifact_version, digest}
if Version.compare(hex_elixir_version, elixir_version) != :gt and
hex_otp_release <= otp_release do
{hex_elixir_version, artifact_version, digest, hex_otp_release}
end
end

defp find_by_artifact_version(_entry, _artifact_version, _elixir_version) do
nil
end

## Public keys

@doc """
Expand Down
10 changes: 8 additions & 2 deletions lib/mix/lib/mix/rebar.ex
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,18 @@ defmodule Mix.Rebar do
@doc """
Returns the path supposed to host the local copy of `rebar`.

The rebar3 installation is specific to the Elixir version,
The rebar3 installation is specific to the Elixir version and OTP release,
in order to force updates when new Elixir versions come out.
"""
def local_rebar_path(:rebar3) do
[major, minor | _] = String.split(System.version(), ".")
Path.join([Mix.Utils.mix_home(), "elixir", "#{major}-#{minor}", "rebar3"])

Path.join([
Mix.Utils.mix_home(),
"elixir",
"#{major}-#{minor}-otp-#{System.otp_release()}",
"rebar3"
])
end

@doc """
Expand Down
13 changes: 5 additions & 8 deletions lib/mix/lib/mix/tasks/local.hex.ex
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
defmodule Mix.Tasks.Local.Hex do
use Mix.Task

@hex_list_path "/installs/hex-1.x.csv"
@hex_archive_path "/installs/[ELIXIR_VERSION]/hex-[HEX_VERSION].ez"
@hex_list_path "/installs/hex.csv"
@hex_archive_path "/installs/[ELIXIR_VERSION]/hex-[HEX_VERSION]-otp-[OTP_RELEASE].ez"

@shortdoc "Installs Hex locally"

Expand Down Expand Up @@ -68,16 +68,13 @@ defmodule Mix.Tasks.Local.Hex do
defp run_install(version, argv) do
hex_url = Mix.Hex.url()

{elixir_version, hex_version, sha512} =
Mix.Local.find_matching_versions_from_signed_csv!(
"Hex",
version,
hex_url <> @hex_list_path
)
{elixir_version, hex_version, sha512, otp_release} =
Mix.Local.find_matching_versions_from_signed_csv!("Hex", version, hex_url <> @hex_list_path)

url =
(hex_url <> @hex_archive_path)
|> String.replace("[ELIXIR_VERSION]", elixir_version)
|> String.replace("[OTP_RELEASE]", otp_release)
|> String.replace("[HEX_VERSION]", hex_version)

# Unload the Hex module we loaded earlier to avoid conflicts when Hex is updated
Expand Down
7 changes: 4 additions & 3 deletions lib/mix/lib/mix/tasks/local.rebar.ex
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
defmodule Mix.Tasks.Local.Rebar do
use Mix.Task

@rebar3_list_url "/installs/rebar3-1.x.csv"
@rebar3_escript_url "/installs/[ELIXIR_VERSION]/rebar3-[REBAR_VERSION]"
@rebar3_list_url "/installs/rebar.csv"
@rebar3_escript_url "/installs/[ELIXIR_VERSION]/rebar3-[REBAR_VERSION]-otp-[OTP_RELEASE]"

@shortdoc "Installs Rebar locally"

Expand Down Expand Up @@ -110,13 +110,14 @@ defmodule Mix.Tasks.Local.Rebar do
hex_url = Mix.Hex.url()
list_url = hex_url <> list_url

{elixir_version, rebar_version, sha512} =
{elixir_version, rebar_version, sha512, otp_release} =
Mix.Local.find_matching_versions_from_signed_csv!("Rebar", _version = nil, list_url)

url =
(hex_url <> escript_url)
|> String.replace("[ELIXIR_VERSION]", elixir_version)
|> String.replace("[REBAR_VERSION]", rebar_version)
|> String.replace("[OTP_RELEASE]", otp_release)

install_from_path(manager, url, Keyword.put(opts, :sha512, sha512))
end
Expand Down
115 changes: 63 additions & 52 deletions lib/mix/test/mix/local_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -3,67 +3,70 @@ Code.require_file("../test_helper.exs", __DIR__)
defmodule Mix.LocalTest do
use MixTest.Case

# openssl rsa -in elixirest.pem -pubout > elixirest.pub
# openssl rsa -in elixirtest.pem -pubout > elixirtest.pub
@public_key """
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA37moKP1dGGLhsP3d8Fwv
W25SoYZUY2K+Iq7A0OBV36Rnb8yW3BWjfh5YtmPvUCfYUbNCW2HTMMgBntkQ4YmN
B9tHVZazl2uX9lGCfZZPFc/9umvKRojCPkMN81MfTxqnY0oaLHr6DB86RsWHB+ld
782Xf+nd9q3LFdUl8SGlKX7uzfVWd4EWYNcL7aLeLSupZWeNg8uVmY3zua0EgIlQ
XryalIOZb/R+pwprWZoftCl+20FGYi/mJpo/idFtXsR0sJKF4X0W3NORT9RIRbs9
WdjiFi+eIP7Nm8KSF4pbaXCqSmVf9cgvUuGTxc9/P5GcIPAlkcsSrE5peLyUCk5f
2QIDAQAB
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkIaIn8uhwSVp9d+aV+GL
tFjMt0TylxDu6x5jwjDvXCD3Of6VAriqcdXDJAqGPN1C4HNTxfihYpLZB+qXCNQM
oEa/I1h6OB5CXb8zIOl2Yriforr9LS+EkP+1xVmz7w7lNAagD0rJyJrTDbYDV+2Y
EnRq84FpZc/+7z6ojc3RMh9x/5t9jDE4Ft3NCujCmGsy2AcBlMpAQkcNveyplNmu
fZH2vId8h8t6rABwEZgSazHObkcHwE3bndynFO7zXvBu6ebNUDuU3DrDJ1Iepy9I
jEuZ6h13j7hL5l+j+9bGRQtasWgwvCkCGD+/2Lz4Ehox58voDCzAK09rsCwQ6gAT
CQIDAQAB
-----END PUBLIC KEY-----
"""

# openssl genrsa -aes256 -out elixirtest.pem -passout stdin 2048
# openssl genrsa -aes256 -out elixirtest.pem -passout pass:secret12 2048
@private_key """
-----BEGIN RSA PRIVATE KEY-----
Proc-Type: 4,ENCRYPTED
DEK-Info: AES-256-CBC,48BA5153DA2F120ECE063B33C1204A49

5gp3daNWujH7o9S/dJQEt9TYTRP0pPZtU55PlZrzWt52optr7XHW/ENOm84g5J70
QCPELp12jfQsNiPwbVWXKy2zD3QlNiAelf65hqLWJTWli7XIXfdP46VXOu67OKf9
Ziw4HQ+AdBEwFt20wJst77iy17sNlyxp5DhNDonnSizzIowgUAJkoNI5aBUU6D8X
KTSIftZW35Z4SudkazdoHepEfItZTI8mB4rvfn71Q4oOBA1rAuUUmdPWoPBfUHDa
hvIp2T2Q8zZYqm0+SjDxZUYOOreE7fuf5NSLhHHt7+jyWQmtaVxnOWms72G+9xT0
NGmOEB0WEg1kBsUbYOXXwyCAZhNA6MaKCtgjQczRTK+geS1xNaFc9FDEk3ZjN4Z8
PxrKQoqo+2aQGVcatZWCom80Dci3bIv7iZNA/y1rjfBn+MeitMOGscP7/CBrJAbI
bh1mvCu0McSnqlN0a+EuCVfJQYFMzjibpRVzKAST0QeaxXd5QxHfcPFPBLOpiVWc
NjHaZsHORyoJbUKGA4rgOiSB63mv7SDRA2mvxWpwV/+6MuwBah6t6CGoEsAr1Hbn
1ySt5w27bw3QEf2KTiuxDubo8UrF0eYzP5A9MH8vRpSRZHg8T3SBVfPJ/pM16Lnn
5BaMUdxDFJeet5HUYoke9Zm3udh2BvwGiKhzc9Pbw/EcsCcvChMimRTasqTaRf+S
uIm0Un7o+7kTuvBo2y87j2urCEUzft5QqEynbkR7p3vZnwoLLj+supXh3V8ivW4s
Z6ql+ukRcWd/ode+lbSiYfAJCLc1tCqJ3kTnMnADJBlL0TX7YnwBwWuwwPuZgeAv
F6nnBE1SBQ1WK+bjSVzIqmNFqsZw34wgpnz2heX0q8msF5pzd6EIeA+uz86k8XYh
4eVZYGXxa4Exodh/MqEpRuN1ytWDXvHULh0gml7xwZC3R50UD8uBNt5RGjXUkjXc
V0atKuvgzVlsB4xbDhVP7EVYHBF02NfNOsvo7kh0Yl1IcT/42UaCGYuU1o9zotPv
9b3SHz/HOmBVj2uCdR5XZ4EolP5Iv9vqIDt9DsuDpOyO+AFOww0FnJNCQ1Hmfb0T
qBYPv994oSPYLCGR4a8i/xfmmV8KbAIVEgK3AMbz8RxKr3WBWXWnzQdr4+y4EG24
hSnR52XQ42edv/fkqf9ez+fKNQ9i7PtlPE96Q21NeLMNKHh43X8hJFDh+oPz3Aio
YSNMCZnoyRdrjBRCsVBpnyoLmuhWwG9RlcrEj3G0BxYPh/weaBOAKAHjSr28yuUj
yIa8uddszC6XHSiVUgu7SGO8gQmq++eNdckjX/pEug5MjcWLUqaUg6+YLFWY6NLf
uDPOYuivq7ErtKTvP2xl3TBEDKhdfqxA2+RFxbBDmKjffZnRkcknQsxhlzAdbg22
Jwa2B1nrfjJpX5F+1Av2jHQGbIKMqZzv8fo1binMKpptFzokbWEOjcPCb3tPuomG
ZRkW3qO2pdyYX2N7VXYG9tGi2HrN/oFrWnHPoYF23v85V8WxNkODOCpTz85e6R5v
PVu+FCNFj5weEOTRhtEQyJo7mU5qIRwYeZvVxiC6W+XeFs95wdBE/Lvpg8yZ8D9d
-----END RSA PRIVATE KEY-----
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIFNTBfBgkqhkiG9w0BBQ0wUjAxBgkqhkiG9w0BBQwwJAQQjNQQlREZJuQveXPa
0ACpEwICCAAwDAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEEFoBWTpEyO8y6xdG
fuud2AIEggTQDQRXrxBpj1yIbnLcP7i43Gat2tEM+XXkvqbzwL+CdjfEzaqiBGwc
FEyWnJj5hENWtNq/AAwzcEALa3QdLJ2W+tX/hc5r1vILpy2oW0vPKfv4yadNyPWq
UzolKKU3MbREP4KXsuAVYepolRWAcWSZKozYBm9HUPwsSyM6E9K0aWmNr/YBbZPE
UEA949Pq6buQvwT2Gj/6FQlutEvJMuY8woM2I/RKSVHeSoB8FWJGSNd7ZY2DN7DG
o6kmhK0OVjxx0SYQwUm++4gh3yFRX6vnTf8ZADOwCOJE/hjMfqivNelJHGojYAD2
0c3zdo0HGvVghg2bzUxhc0HJqUFiUDNW1W3AVBrUJiQgHQb/ENYaMrd6klrslHDV
cnPkz+dZAq/WC3KFqufkSmHxU97dDs9G6hKdm80JVEdPG4aMJvHzVL6SvS+g/e7r
IJGZCFGch9SbikmESKegIfyLJYQrfjuLdXWPHx9J+LAmtRTA1xPwLH/aqCr9ahBB
JCl4RaezF0BH2GYLAV+Ailpy2jqsC3v9RW8vpQmU227H8ERFu9ZcWhZITbRhbjRB
oh3ERnOmvU3VWIXQ0j1cm9Qjl52y8HZJxef2dYsW8nmTSRj32gi9UnEsyOFL9FB7
KcFDq6o+go4nIfsmaAEOF1Hh01JZkbuX1SgmHvGRbNksTuljP/XZn7Wzz7fTcVZR
NsTgMbBl1+ctDHUBT7YzsHeEx8tZvLPDP8lO23aEnmRb6bHD4hVorUEM/kOpSm6g
DNP0buYGEAu4rZrp+5s46Y/RVAIhU4+663jqzmbmwOwEcuOJJCJn8eUGVweAgWX3
1PQTfpkk7RLxxuLeMHxEIynxyZNpSgfX0VGKe5UtplwcTQL8VgeZHayDxnhFwtSm
lBfPIM/i4kj7RonuehyPjuBA6n1CyqRVqc49c/3kVMgovqvbUQxQ7TtSfzfkhVyV
yRnRxcek+6Gctoahas5DPQl7y8jnwgg5qvUjYo6GIwBoxLUzIw+0MBn7gdMYHNmh
Xd9XZpRilAMJF5ynr2QyaE95XaQ3yK62FPgmIDdzfnN7es169O4SiXsgUyaSWsx9
VxOHP4r0j6epWLH4W11MQEj4Bb8wG//pCD5Kz2xvwhWaKjkqzuqGDaXSOTKV0FQM
NG5GbNNELNCy/ocvlsaC+acWcOfsx4f6yovCGVTpTr+wjbDqd1kEBoRTuyTPY+6P
0OU1Vf2GpMA2NDw0ZUOAllGVLrgD+326bu7ipePgiT8SiPNNsagMJNCSk5d3C4+K
bc0hakF/MIjPs3a+5/2mwtxk3QYYCIHITN8SIj+kFd7WMXALyClgWt3tcKwLTgMa
BSatAvwsoi2kiBu2A49msvka7YD8fENhtUl8vU12ZZdTih1chyPDifgfRgOfkZFW
a1XiLKjnWqeBqvJ3hBKuXweN6P7nvZidBYSESl3yrM00pA97Eta2in7ok80MRYH3
bsqzC636h43Mu8PbTKGpK66j1ts6uY9YONAGfGCcfj/wyBWuqNViv7vK38th8wj8
AG4rKyIlKxbFwo4Oh3jZTbfVF1Azl8pr0rK2P3W/EhfvWIrZVfB4Upy0wL6MjysE
RZ/57N2wFq4KBb9x9hCMFxignWmgyzPK+1J0iPRkSmpn9P/SzywDBIg=
-----END ENCRYPTED PRIVATE KEY-----
"""

@csv """
1.2.5,ABC,0.9.0
1.2.3,DEF,1.0.0
1.2.4,GHI,1.0.0
1.2.5,ABC,0.9.0,25
1.2.5,ABC,0.9.0,26
1.2.3,DEF,1.0.0,25
1.2.3,DEF,1.0.0,26
1.2.4,GHI,1.0.0,25
1.2.4,GHI,1.0.0,26
"""

# openssl dgst -sha512 -sign elixirtest.pem hex-1.x.csv | openssl base64 > elixirtest.csv.signed
@csv_signed """
VRydmXOdEXQcKJu/SK/nKnE00T+s/T4mpXrYROMSXhD/s8ClvdimnGg61ie3YBS6
LXOjlEhbtMHRM2rTOUvv4z7FcyzwvSxSjunlVi2g3c1pVOZ78MonnYhGb44tZw/q
SOVmV+jJhc9EZFMIAAM3plMoyssyw2pMh7ZB/DxCQTIem3Qf0Ujzc2bYkLVlw7R+
1Rn6dcYEgCzyldVkAUMaYBwieyweWALA+YVDCMudJJK2J7p1OnuoPSVV+N3OkB/Z
T6Jj5ljD+54XnuxAMcgCoF9lpOwXscnw/Ma+8JqIoWo0jNFE3ji+8dGCUzQUdSe8
llLXgJJE2tGpDhEXBA3idg==
CVkhTiuCAfooYPhjyynDq40QhmDwLAEJvpwYytPCf6mpLXVrLXo/d/A2L8iBRJVx
uk4PNVksLRZ1ChBzGFvEqaFjrH+ndQAYLbwqcaMIn743YNUjGNVfTZkU47nBybtJ
BwDSBaAsow0Iitsl+UkDN/QvVoOLiX/x2cpnwCMrCgbTMroTzhH07vfLo3uCf8iY
cncImd07ffCewt77AsVPpKgJNOLzn+EBnvh4LbGWQya8EkgyQKuMBuNU86MYtFiW
NVpR7vbvqgWpEyr1XeknxKkhzYpna3+irXdMxGZw65WvFNWGJKnpuBTNHnHL+wX8
oNQLUfakH8/VMV/8v6Irbg==
"""

# We don't actually use it but it exists for documentation purposes.
Expand All @@ -83,8 +86,10 @@ defmodule Mix.LocalTest do
File.write!("csv", @csv)
File.write!("csv.signed", @csv_signed)

assert {"1.0.0", "1.2.4", "GHI"} =
assert {"1.0.0", "1.2.4", "GHI", otp_release} =
Mix.Local.find_matching_versions_from_signed_csv!("name", nil, "csv")

assert otp_release <= System.otp_release()
end)
end

Expand All @@ -94,12 +99,16 @@ defmodule Mix.LocalTest do
File.write!("csv", @csv)
File.write!("csv.signed", @csv_signed)

assert {"0.9.0", "1.2.5", "ABC"} =
assert {"0.9.0", "1.2.5", "ABC", otp_release} =
Mix.Local.find_matching_versions_from_signed_csv!("name", "1.2.5", "csv")

assert {"1.0.0", "1.2.3", "DEF"} =
assert otp_release <= System.otp_release()

assert {"1.0.0", "1.2.3", "DEF", otp_release} =
Mix.Local.find_matching_versions_from_signed_csv!("name", "1.2.3", "csv")

assert otp_release <= System.otp_release()

assert_raise Mix.Error, "Could not find a version of name matching: 1.3.0", fn ->
Mix.Local.find_matching_versions_from_signed_csv!("name", "1.3.0", "csv")
end
Expand All @@ -109,7 +118,9 @@ defmodule Mix.LocalTest do
@tag :tmp_dir
test "raise on bad signature", %{tmp_dir: tmp_dir} do
File.cd!(tmp_dir, fn ->
csv_signed = String.replace(@csv_signed, "VRy", "BAD")
<<_, _, _>> <> rest = @csv_signed
csv_signed = "BAD" <> rest

File.write!("csv", @csv)
File.write!("csv.signed", csv_signed)

Expand Down
3 changes: 2 additions & 1 deletion lib/mix/test/test_helper.exs
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,8 @@ end

rebar3_source = System.get_env("REBAR3") || Path.expand("fixtures/rebar3", __DIR__)
[major, minor | _] = String.split(System.version(), ".")
rebar3_target = Path.join([mix, "elixir", "#{major}-#{minor}", "rebar3"])
version_dir = "#{major}-#{minor}-otp-#{System.otp_release()}"
rebar3_target = Path.join([mix, "elixir", version_dir, "rebar3"])
File.mkdir_p!(Path.dirname(rebar3_target))
File.cp!(rebar3_source, rebar3_target)

Expand Down
Loading