Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds support for elixir sub-projects without the need for umbrella applications #3944

Merged
merged 2 commits into from
Jun 23, 2021
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
21 changes: 16 additions & 5 deletions hex/lib/dependabot/hex/file_fetcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class FileFetcher < Dependabot::FileFetchers::Base
SUPPORTED_METHODS = %w(eval_file require_file).join("|").freeze
SUPPORT_FILE = /Code\.(?:#{SUPPORTED_METHODS})\(#{STRING_ARG}(?:\s*,\s*#{STRING_ARG})?\)/.
freeze
PATH_DEPS_REGEX = /{.*path: ?#{STRING_ARG}.*}/.freeze

def self.required_files_in?(filenames)
filenames.include?("mix.exs")
Expand Down Expand Up @@ -44,16 +45,26 @@ def lockfile
nil
end

def subapp_mixfiles
def umbrella_app_directories
apps_path = mixfile.content.match(APPS_PATH_REGEX)&.
named_captures&.fetch("path")
return [] unless apps_path

app_directories = repo_contents(dir: apps_path).
select { |f| f.type == "dir" }.
map { |f| File.join(apps_path, f.name) }
repo_contents(dir: apps_path).
select { |f| f.type == "dir" }.
map { |f| File.join(apps_path, f.name) }
end

def sub_project_directories
mixfile.content.scan(PATH_DEPS_REGEX).flatten
end

def subapp_mixfiles
subapp_directories = []
subapp_directories += umbrella_app_directories
subapp_directories += sub_project_directories

app_directories.map do |dir|
subapp_directories.map do |dir|
fetch_file_from_host("#{dir}/mix.exs")
rescue Dependabot::DependencyFileNotFound
# If the folder doesn't have a mix.exs it *might* be because it's
Expand Down
42 changes: 42 additions & 0 deletions hex/spec/dependabot/hex/file_fetcher_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -334,4 +334,46 @@
to include("apps/bank/mix.exs")
end
end

context "with sub-projects" do
before do
stub_request(:get, url + "?ref=sha").
with(headers: { "Authorization" => "token token" }).
to_return(
status: 200,
body: fixture("github", "contents_elixir_umbrella.json"),
headers: json_header
)

stub_request(:get, url + "mix.exs?ref=sha").
with(headers: { "Authorization" => "token token" }).
to_return(
status: 200,
body: fixture("github", "contents_elixir_sub_project_mixfile.json"),
headers: json_header
)

stub_request(:get, url + "apps?ref=sha").
with(headers: { "Authorization" => "token token" }).
to_return(
status: 200,
body: fixture("github", "contents_elixir_umbrella_apps.json"),
headers: json_header
)

stub_request(:get, url + "apps/bank/mix.exs?ref=sha").
with(headers: { "Authorization" => "token token" }).
to_return(
status: 200,
body: fixture("github", "contents_elixir_bank_mixfile.json"),
headers: json_header
)
end

it "fetches the mixfiles for the sub-apps" do
expect(file_fetcher_instance.files.count).to eq(3)
expect(file_fetcher_instance.files.map(&:name)).
to include("apps/bank/mix.exs")
end
end
end
60 changes: 60 additions & 0 deletions hex/spec/dependabot/hex/file_updater_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,66 @@
expect(updated_business_content).to include(%({:plug, "~> 1.4.0"},))
end
end

context "with sub projects" do
let(:files) { project_dependency_files("umbrella_sub_projects") }

let(:dependency) do
Dependabot::Dependency.new(
name: "plug",
version: "1.4.5",
requirements: [
{
requirement: "~> 1.4.0",
file: "apps/dependabot_business/mix.exs",
groups: [],
source: nil
},
{
requirement: "1.4.5",
file: "apps/dependabot_web/mix.exs",
groups: [],
source: nil
}
],
previous_version: "1.3.6",
previous_requirements: [
{
requirement: "~> 1.3.0",
file: "apps/dependabot_business/mix.exs",
groups: [],
source: nil
},
{
requirement: "1.3.6",
file: "apps/dependabot_web/mix.exs",
groups: [],
source: nil
}
],
package_manager: "hex"
)
end

it "updates the right files" do
expect(updated_files.map(&:name)).
to match_array(
%w(mix.lock
apps/dependabot_business/mix.exs
apps/dependabot_web/mix.exs)
)

updated_web_content = updated_files.find do |f|
f.name == "apps/dependabot_web/mix.exs"
end.content
expect(updated_web_content).to include(%({:plug, "1.4.5"},))

updated_business_content = updated_files.find do |f|
f.name == "apps/dependabot_business/mix.exs"
end.content
expect(updated_business_content).to include(%({:plug, "~> 1.4.0"},))
end
end
end

describe "the updated lockfile" do
Expand Down
22 changes: 22 additions & 0 deletions hex/spec/dependabot/hex/update_checker_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,28 @@

it { is_expected.to be >= Gem::Version.new("1.4.3") }
end

context "with sub projects" do
let(:files) { project_dependency_files("umbrella_sub_projects") }

let(:dependency_name) { "plug" }
let(:version) { "1.3.6" }
let(:dependency_requirements) do
[{
requirement: "~> 1.3.0",
file: "apps/dependabot_business/mix.exs",
groups: [],
source: nil
}, {
requirement: "1.3.6",
file: "apps/dependabot_web/mix.exs",
groups: [],
source: nil
}]
end

it { is_expected.to be >= Gem::Version.new("1.4.3") }
end
end

describe "#latest_resolvable_version_with_no_unlock" do
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "mix.exs",
"path": "mix.exs",
"sha": "84ef495b10f6a5a6a04c65606440621b44c173f9",
"size": 782,
"url": "https://api.github.com/repos/wojtekmach/acme_bank/contents/mix.exs?ref=master",
"html_url": "https://github.com/wojtekmach/acme_bank/blob/master/mix.exs",
"git_url": "https://api.github.com/repos/wojtekmach/acme_bank/git/blobs/84ef495b10f6a5a6a04c65606440621b44c173f9",
"download_url": "https://raw.githubusercontent.com/wojtekmach/acme_bank/master/mix.exs",
"type": "file",
"content": "ZGVmbW9kdWxlIEJhbmtQbGF0Zm9ybS5NaXhmaWxlIGRvCiAgdXNlIE1peC5Qcm9qZWN0CgogIGRlZiBwcm9qZWN0IGRvCiAgICBbYnVpbGRfZW1iZWRkZWQ6IE1peC5lbnYgPT0gOnByb2QsCiAgICAgc3RhcnRfcGVybWFuZW50OiBNaXguZW52ID09IDpwcm9kLAogICAgIHZlcnNpb246ICIxLjAuMC1kZXYiLAogICAgIHNvdXJjZV91cmw6ICJodHRwczovL2dpdGh1Yi5jb20vd29qdGVrbWFjaC9hY21lX2JhbmsiLAogICAgIG5hbWU6ICJBY21lIEJhbmsiLAogICAgIGRvY3M6IFtzb3VyY2VfcmVmOiAiSEVBRCIsIG1haW46ICJtYWluIiwgYXNzZXRzOiAiZG9jcyIsIGV4dHJhczogWyJkb2NzL21haW4ubWQiXV0sCiAgICAgZGVwczogZGVwcygpLAogICAgIGFsaWFzZXM6IGFsaWFzZXMoKV0KICBlbmQKCiAgZGVmcCBkZXBzIGRvCiAgICBbezpleF9kb2MsIGdpdGh1YjogImVsaXhpci1sYW5nL2V4X2RvYyIsIGJyYW5jaDogIm1hc3RlciIsIG9ubHk6IDpkZXZ9LAogICAgIHs6YmFuaywgcGF0aDogImFwcHMvYmFuayIsIGZyb21fdW1icmVsbGE6IHRydWV9XQogIGVuZAoKICBkZWZwIGFsaWFzZXMgZG8KICAgIFsiZWN0by5zZXR1cCI6IFsiZWN0by5jcmVhdGUiLCAiZWN0by5taWdyYXRlIiwgImVjdG8uc2VlZCJdLAogICAgICJlY3RvLnNlZWQiOiBbInJ1biAuL2FwcHMvYmFuay9wcml2L3JlcG8vc2VlZHMuZXhzIl0sCiAgICAgImVjdG8ucmVzZXQiOiBbImVjdG8uZHJvcCIsICJlY3RvLnNldHVwIl0sCiAgICAgInRlc3QiOiBbImVjdG8uY3JlYXRlIC0tcXVpZXQiLCAiZWN0by5taWdyYXRlIiwgInRlc3QiXV0KICBlbmQKZW5k4o+OCg==\n",
"encoding": "base64",
"_links": {
"self": "https://api.github.com/repos/wojtekmach/acme_bank/contents/mix.exs?ref=master",
"git": "https://api.github.com/repos/wojtekmach/acme_bank/git/blobs/84ef495b10f6a5a6a04c65606440621b44c173f9",
"html": "https://github.com/wojtekmach/acme_bank/blob/master/mix.exs"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
defmodule DependabotBusiness.MixProject do
use Mix.Project

def project do
[
app: :dependabot_business,
version: "0.1.0",
build_path: "../../_build",
config_path: "../../config/config.exs",
deps_path: "../../deps",
lockfile: "../../mix.lock",
elixir: "~> 1.6",
start_permanent: Mix.env() == :prod,
deps: deps()
]
end

def application do
[
extra_applications: [:logger]
]
end

defp deps do
[
{:plug, "~> 1.3.0"},
{:jason, "~> 1.0"}
]
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
defmodule DependabotWeb.MixProject do
use Mix.Project

def project do
[
app: :dependabot_web,
version: "0.1.0",
build_path: "../../_build",
config_path: "../../config/config.exs",
deps_path: "../../deps",
lockfile: "../../mix.lock",
elixir: "~> 1.6",
start_permanent: Mix.env() == :prod,
deps: deps()
]
end

def application do
[
extra_applications: [:logger]
]
end

defp deps do
[
{:plug, "1.3.6"},
{:dependabot_business, in_umbrella: true}
]
end
end
19 changes: 19 additions & 0 deletions hex/spec/fixtures/projects/umbrella_sub_projects/mix.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
defmodule DependabotUmbrella.MixProject do
use Mix.Project

def project do
[
app: :dependabot_test,
start_permanent: Mix.env() == :prod,
deps: deps()
]
end

defp deps do
[
{:distillery, "~> 1.5", runtime: false},
{:dependabot_business, path: "apps/dependabot_business", from_umbrella: true},
{:dependabot_web, path: "apps/dependabot_web", from_umbrella: true}
]
end
end
4 changes: 4 additions & 0 deletions hex/spec/fixtures/projects/umbrella_sub_projects/mix.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
%{"distillery": {:hex, :distillery, "1.5.2", "eec18b2d37b55b0bcb670cf2bcf64228ed38ce8b046bb30a9b636a6f5a4c0080", [:mix], [], "hexpm"},
"jason": {:hex, :jason, "1.0.0", "0f7cfa9bdb23fed721ec05419bcee2b2c21a77e926bce0deda029b5adc716fe2", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"},
"mime": {:hex, :mime, "1.2.0", "78adaa84832b3680de06f88f0997e3ead3b451a440d183d688085be2d709b534", [:mix], [], "hexpm"},
"plug": {:hex, :plug, "1.3.6", "bcdf94ac0f4bc3b804bdbdbde37ebf598bd7ed2bfa5106ed1ab5984a09b7e75f", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1", [hex: :cowboy, repo: "hexpm", optional: true]}, {:mime, "~> 1.0", [hex: :mime, repo: "hexpm", optional: false]}], "hexpm"}}