Skip to content

Commit

Permalink
Add support for docker-compose.yml files. Closes dependabot#390
Browse files Browse the repository at this point in the history
  • Loading branch information
Pedro Pombeiro committed Oct 6, 2019
1 parent 790a7c2 commit d2d3d5b
Show file tree
Hide file tree
Showing 41 changed files with 2,564 additions and 804 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@
end
end

context "and is idential, but with different subdependency_metadata" do
context "and is identical, but with different subdependency_metadata" do
let(:existing_subdependency_metadata) { [{ npm_bundled: true }] }
let(:subdependency_metadata) { [{ npm_bundled: false }] }
let(:existing_dependency) do
Expand Down
90 changes: 90 additions & 0 deletions docker/lib/dependabot/common/file_parser_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# frozen_string_literal: true

require "docker_registry2"

require "dependabot/dependency"
require "dependabot/errors"
require "dependabot/docker/utils/credentials_finder"

module Dependabot
module Docker
module FileParserHelper
private

def version_from(parsed_info)
return parsed_info.fetch("tag") if parsed_info.fetch("tag")

version_from_digest(
registry: parsed_info.fetch("registry"),
image: parsed_info.fetch("image"),
digest: parsed_info.fetch("digest")
)
end

def source_from(parsed_info)
source = {}

%w(registry tag digest).each do |part|
value = parsed_info.fetch(part)
source[part.to_sym] = value if value
end

source
end

def version_from_digest(registry:, image:, digest:)
return unless digest

repo = docker_repo_name(image, registry)
client = docker_registry_client(registry)
client.tags(repo, auto_paginate: true).fetch("tags").find do |tag|
digest == client.digest(repo, tag)
rescue DockerRegistry2::NotFound
# Shouldn't happen, but it does. Example of existing tag with
# no manifest is "library/python", "2-windowsservercore".
false
end
rescue DockerRegistry2::RegistryAuthenticationException,
RestClient::Forbidden
raise if standard_registry?(registry)

raise PrivateSourceAuthenticationFailure, registry
end

def docker_repo_name(image, registry)
return image unless standard_registry?(registry)
return image unless image.split("/").count < 2

"library/#{image}"
end

def docker_registry_client(registry)
if registry
credentials = registry_credentials(registry)

DockerRegistry2::Registry.new(
"https://#{registry}",
user: credentials&.fetch("username", nil),
password: credentials&.fetch("password", nil)
)
else
DockerRegistry2::Registry.new("https://registry.hub.docker.com")
end
end

def registry_credentials(registry_url)
credentials_finder.credentials_for_registry(registry_url)
end

def credentials_finder
@credentials_finder ||= Utils::CredentialsFinder.new(credentials)
end

def standard_registry?(registry)
return true if registry.nil?

registry == "registry.hub.docker.com"
end
end
end
end
92 changes: 92 additions & 0 deletions docker/lib/dependabot/common/file_updater_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# frozen_string_literal: true

require "dependabot/file_updaters"
require "dependabot/file_updaters/base"
require "dependabot/errors"

module Dependabot
module Docker
module FileUpdaterHelper
private

def update_digest_and_tag(file)
old_declaration_regex = digest_and_tag_regex(old_digest(file))

file.content.gsub(old_declaration_regex) do |old_dec|
old_dec.
gsub("@#{old_digest(file)}", "@#{new_digest(file)}").
gsub(":#{dependency.previous_version}",
":#{dependency.version}")
end
end

def update_tag(file)
return unless old_tag(file)

old_declaration =
if private_registry_url(file) then "#{private_registry_url(file)}/"
else ""
end
old_declaration += "#{dependency.name}:#{old_tag(file)}"

old_declaration_regex = tag_regex(old_declaration)

file.content.gsub(old_declaration_regex) do |old_dec|
old_dec.gsub(":#{old_tag(file)}", ":#{new_tag(file)}")
end
end

def fetch_file_source(file, reqs)
reqs.
find { |req| req[:file] == file.name }.
fetch(:source)
end

def fetch_property_in_file_source(file, reqs, property)
fetch_file_source(file, reqs).fetch(property)
end

def specified_with_digest?(file)
fetch_file_source(file, dependency.requirements)[:digest]
end

def new_digest(file)
return unless specified_with_digest?(file)

fetch_property_in_file_source(file, dependency.requirements, :digest)
end

def old_digest(file)
return unless specified_with_digest?(file)

fetch_property_in_file_source(
file,
dependency.previous_requirements,
:digest
)
end

def digest(file, reqs)
return unless specified_with_digest?(file)

fetch_property_in_file_source(file, reqs, :digest)
end

def new_tag(file)
fetch_property_in_file_source(file, dependency.requirements, :tag)
end

def old_tag(file)
fetch_property_in_file_source(
file,
dependency.previous_requirements,
:tag
)
end

def private_registry_url(file)
fetch_file_source(file, dependency.requirements)[:registry]
end
end
end
end
2 changes: 2 additions & 0 deletions docker/lib/dependabot/docker.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
require "dependabot/docker/requirement"
require "dependabot/docker/version"

require_relative "docker_compose"

require "dependabot/pull_request_creator/labeler"
Dependabot::PullRequestCreator::Labeler.
register_label_details("docker", name: "docker", colour: "21ceff")
Expand Down
90 changes: 3 additions & 87 deletions docker/lib/dependabot/docker/file_parser.rb
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
# frozen_string_literal: true

require "docker_registry2"

require "dependabot/dependency"
require "dependabot/file_parsers"
require "dependabot/file_parsers/base"
require "dependabot/errors"
require "dependabot/docker/utils/credentials_finder"
require "dependabot/common/file_parser_helper"

module Dependabot
module Docker
class FileParser < Dependabot::FileParsers::Base
require "dependabot/file_parsers/base/dependency_set"

include Dependabot::Docker::FileParserHelper

# Detials of Docker regular expressions is at
# https://github.com/docker/distribution/blob/master/reference/regexp.go
DOMAIN_COMPONENT =
Expand Down Expand Up @@ -72,88 +70,6 @@ def dockerfiles
dependency_files
end

def version_from(parsed_from_line)
return parsed_from_line.fetch("tag") if parsed_from_line.fetch("tag")

version_from_digest(
registry: parsed_from_line.fetch("registry"),
image: parsed_from_line.fetch("image"),
digest: parsed_from_line.fetch("digest")
)
end

def source_from(parsed_from_line)
source = {}

if parsed_from_line.fetch("registry")
source[:registry] = parsed_from_line.fetch("registry")
end

if parsed_from_line.fetch("tag")
source[:tag] = parsed_from_line.fetch("tag")
end

if parsed_from_line.fetch("digest")
source[:digest] = parsed_from_line.fetch("digest")
end

source
end

def version_from_digest(registry:, image:, digest:)
return unless digest

repo = docker_repo_name(image, registry)
client = docker_registry_client(registry)
client.tags(repo, auto_paginate: true).fetch("tags").find do |tag|
digest == client.digest(repo, tag)
rescue DockerRegistry2::NotFound
# Shouldn't happen, but it does. Example of existing tag with
# no manifest is "library/python", "2-windowsservercore".
false
end
rescue DockerRegistry2::RegistryAuthenticationException,
RestClient::Forbidden
raise if standard_registry?(registry)

raise PrivateSourceAuthenticationFailure, registry
end

def docker_repo_name(image, registry)
return image unless standard_registry?(registry)
return image unless image.split("/").count < 2

"library/#{image}"
end

def docker_registry_client(registry)
if registry
credentials = registry_credentials(registry)

DockerRegistry2::Registry.new(
"https://#{registry}",
user: credentials&.fetch("username", nil),
password: credentials&.fetch("password", nil)
)
else
DockerRegistry2::Registry.new("https://registry.hub.docker.com")
end
end

def registry_credentials(registry_url)
credentials_finder.credentials_for_registry(registry_url)
end

def credentials_finder
@credentials_finder ||= Utils::CredentialsFinder.new(credentials)
end

def standard_registry?(registry)
return true if registry.nil?

registry == "registry.hub.docker.com"
end

def check_required_files
# Just check if there are any files at all.
return if dependency_files.any?
Expand Down
Loading

0 comments on commit d2d3d5b

Please sign in to comment.