diff --git a/Dockerfile b/Dockerfile index 1569944b1a8..6dbbd5f9d81 100644 --- a/Dockerfile +++ b/Dockerfile @@ -129,8 +129,21 @@ ENV RUSTUP_HOME=/opt/rust \ RUN export CARGO_HOME=/opt/rust ; curl https://sh.rustup.rs -sSf | sh -s -- -y +### NEW NATIVE HELPERS LOCATION + +ENV DEPENDABOT_NATIVE_HELPERS_PATH="/opt" + + ### TERRAFORM -COPY terraform/helpers/build /tmp/terraform-build -RUN bash /tmp/terraform-build /opt/terraform -ENV PATH="$PATH:/opt/terraform/bin" DEPENDABOT_NATIVE_HELPERS_PATH="/opt" +COPY terraform/helpers /opt/terraform/helpers +RUN bash /opt/terraform/helpers/build /opt/terraform +ENV PATH="$PATH:/opt/terraform/bin" + + +### PYTHON (MERGE WITH SECTION FUTHER UP) + +COPY python/helpers /opt/python/helpers +RUN bash /opt/python/helpers/build /opt/python +ENV PATH="$PATH:/opt/python/bin" + diff --git a/Dockerfile.development b/Dockerfile.development index 60821913646..256c037fffa 100644 --- a/Dockerfile.development +++ b/Dockerfile.development @@ -21,13 +21,17 @@ COPY lib/dependabot/version.rb /home/dependabot/dependabot-core/lib/dependabot/ RUN bundle install RUN mkdir -p /home/dependabot/dependabot-core/terraform -COPY terraform/Gemfile terraform/Gemfile.lock terraform/dependabot-terraform.gemspec /home/dependabot/dependabot-core/terraform/ +COPY terraform/Gemfile terraform/dependabot-terraform.gemspec /home/dependabot/dependabot-core/terraform/ RUN cd terraform && bundle install RUN mkdir -p /home/dependabot/dependabot-core/docker -COPY docker/Gemfile docker/Gemfile.lock docker/dependabot-docker.gemspec /home/dependabot/dependabot-core/docker/ +COPY docker/Gemfile docker/dependabot-docker.gemspec /home/dependabot/dependabot-core/docker/ RUN cd docker && bundle install RUN mkdir -p /home/dependabot/dependabot-core/git_submodules -COPY git_submodules/Gemfile git_submodules/Gemfile.lock git_submodules/dependabot-git-submodules.gemspec /home/dependabot/dependabot-core/git_submodules/ +COPY git_submodules/Gemfile git_submodules/dependabot-git-submodules.gemspec /home/dependabot/dependabot-core/git_submodules/ RUN cd git_submodules && bundle install + +RUN mkdir -p /home/dependabot/dependabot-core/python +COPY python/Gemfile python/dependabot-python.gemspec /home/dependabot/dependabot-core/python/ +RUN cd python && bundle install diff --git a/bin/docker-dev-shell b/bin/docker-dev-shell index d9d96386bcd..221a3c92fb3 100755 --- a/bin/docker-dev-shell +++ b/bin/docker-dev-shell @@ -59,17 +59,21 @@ docker run --rm -ti \ -v "$(pwd)/spec:$CODE_DIR/spec" \ -v "$(pwd)/helpers:$CODE_DIR/helpers" \ -v "$(pwd)/Gemfile:$CODE_DIR/Gemfile" \ - -v "$(pwd)/terraform/Gemfile.lock:$CODE_DIR/terraform/Gemfile.lock" \ - -v "$(pwd)/terraform/dependabot-terraform.gemspec:$CODE_DIR/terraform/dependabot-core.gemspec" \ + -v "$(pwd)/terraform/Gemfile:$CODE_DIR/terraform/Gemfile" \ + -v "$(pwd)/terraform/dependabot-terraform.gemspec:$CODE_DIR/terraform/dependabot-terraform.gemspec" \ -v "$(pwd)/terraform/lib:$CODE_DIR/terraform/lib" \ -v "$(pwd)/terraform/spec:$CODE_DIR/terraform/spec" \ - -v "$(pwd)/docker/Gemfile.lock:$CODE_DIR/docker/Gemfile.lock" \ - -v "$(pwd)/docker/dependabot-docker.gemspec:$CODE_DIR/docker/dependabot-core.gemspec" \ + -v "$(pwd)/docker/Gemfile:$CODE_DIR/docker/Gemfile" \ + -v "$(pwd)/docker/dependabot-docker.gemspec:$CODE_DIR/docker/dependabot-docker.gemspec" \ -v "$(pwd)/docker/lib:$CODE_DIR/docker/lib" \ -v "$(pwd)/docker/spec:$CODE_DIR/docker/spec" \ - -v "$(pwd)/git_submodules/Gemfile.lock:$CODE_DIR/git_submodules/Gemfile.lock" \ + -v "$(pwd)/git_submodules/Gemfile:$CODE_DIR/git_submodules/Gemfile" \ -v "$(pwd)/git_submodules/dependabot-git-submodules.gemspec:$CODE_DIR/git_submodules/dependabot-core.gemspec" \ -v "$(pwd)/git_submodules/lib:$CODE_DIR/git_submodules/lib" \ -v "$(pwd)/git_submodules/spec:$CODE_DIR/git_submodules/spec" \ + -v "$(pwd)/python/Gemfile:$CODE_DIR/python/Gemfile" \ + -v "$(pwd)/python/dependabot-python.gemspec:$CODE_DIR/python/dependabot-python.gemspec" \ + -v "$(pwd)/python/lib:$CODE_DIR/python/lib" \ + -v "$(pwd)/python/spec:$CODE_DIR/python/spec" \ --cap-add=SYS_PTRACE \ "$IMAGE_NAME" "${CONTAINER_ARGS[@]}" diff --git a/python/Gemfile b/python/Gemfile new file mode 100644 index 00000000000..4c3c2338f10 --- /dev/null +++ b/python/Gemfile @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +source "https://rubygems.org" + +gem "dependabot-core", path: "../" + +gemspec diff --git a/python/README.md b/python/README.md new file mode 100644 index 00000000000..7ed0814cb1c --- /dev/null +++ b/python/README.md @@ -0,0 +1,22 @@ +## `dependabot-terraform` + +Terraform support for [`dependabot-core`][core-repo]. + +### Running locally + +1. Install native helpers + ``` + $ helpers/build helpers/install-dir/terraform + ``` + +2. Install Ruby dependencies + ``` + $ bundle install + ``` + +3. Run tests + ``` + $ bundle exec rspec spec + ``` + +[core-repo]: https://github.com/dependabot/dependabot-core diff --git a/python/dependabot-python.gemspec b/python/dependabot-python.gemspec new file mode 100644 index 00000000000..28549fafcb3 --- /dev/null +++ b/python/dependabot-python.gemspec @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +require "find" + +Gem::Specification.new do |spec| + core_gemspec = Bundler.load_gemspec_uncached("../dependabot-core.gemspec") + + spec.name = "dependabot-python" + spec.summary = "Python support for dependabot-core" + spec.version = core_gemspec.version + spec.description = core_gemspec.description + + spec.author = core_gemspec.author + spec.email = core_gemspec.email + spec.homepage = core_gemspec.homepage + spec.license = core_gemspec.license + + spec.require_path = "lib" + spec.files = [] + + spec.required_ruby_version = core_gemspec.required_ruby_version + spec.required_rubygems_version = core_gemspec.required_ruby_version + + spec.add_dependency "dependabot-core", Dependabot::VERSION + + core_gemspec.development_dependencies.each do |dep| + spec.add_development_dependency dep.name, dep.requirement.to_s + end + + next unless File.exist?("../.gitignore") + + ignores = File.readlines("../.gitignore").grep(/\S+/).map(&:chomp) + + next unless File.directory?("lib") && File.directory?("helpers") + + prefix = "/" + File.basename(File.expand_path(__dir__)) + "/" + Find.find("lib", "helpers") do |path| + if ignores.any? { |i| File.fnmatch(i, prefix + path, File::FNM_DOTMATCH) } + Find.prune + else + spec.files << path unless File.directory?(path) + end + end +end diff --git a/python/helpers/build b/python/helpers/build new file mode 100644 index 00000000000..0ca5be38adf --- /dev/null +++ b/python/helpers/build @@ -0,0 +1,17 @@ +#!/bin/bash + +set -e + +install_dir=$1 +if [ -z "$install_dir" ]; then + echo "usage: $0 INSTALL_DIR" + exit 1 +fi + +if [ ! -d "$install_dir/bin" ]; then + mkdir -p "$install_dir/bin" +fi + +helpers_dir="$(dirname "${BASH_SOURCE[0]}")" +PYENV_VERSION=2.7.15 pyenv exec pip install -r "${helpers_dir}/requirements.txt" +PYENV_VERSION=3.6.7 pyenv exec pip install -r "${helpers_dir}/requirements.txt" diff --git a/python/lib/dependabot/python/file_fetcher.rb b/python/lib/dependabot/python/file_fetcher.rb index 547e2003eb0..cb4d07f6c3d 100644 --- a/python/lib/dependabot/python/file_fetcher.rb +++ b/python/lib/dependabot/python/file_fetcher.rb @@ -7,298 +7,296 @@ require "dependabot/errors" module Dependabot - module FileFetchers - module Python - class Pip < Dependabot::FileFetchers::Base - CHILD_REQUIREMENT_REGEX = /^-r\s?(?.*\.txt)/.freeze - CONSTRAINT_REGEX = /^-c\s?(?\..*)/.freeze + module Python + class FileFetcher < Dependabot::FileFetchers::Base + CHILD_REQUIREMENT_REGEX = /^-r\s?(?.*\.txt)/.freeze + CONSTRAINT_REGEX = /^-c\s?(?\..*)/.freeze - def self.required_files_in?(filenames) - return true if filenames.any? { |name| name.end_with?(".txt", ".in") } + def self.required_files_in?(filenames) + return true if filenames.any? { |name| name.end_with?(".txt", ".in") } - # If there is a directory of requirements return true - return true if filenames.include?("requirements") + # If there is a directory of requirements return true + return true if filenames.include?("requirements") - # If this repo is using a Pipfile return true - return true if filenames.include?("Pipfile") + # If this repo is using a Pipfile return true + return true if filenames.include?("Pipfile") - # If this repo is using Poetry return true - return true if filenames.include?("pyproject.toml") + # If this repo is using Poetry return true + return true if filenames.include?("pyproject.toml") - filenames.include?("setup.py") - end + filenames.include?("setup.py") + end - def self.required_files_message - "Repo must contain a requirements.txt, setup.py, pyproject.toml, "\ - "or a Pipfile." - end + def self.required_files_message + "Repo must contain a requirements.txt, setup.py, pyproject.toml, "\ + "or a Pipfile." + end - private + private - def fetch_files - fetched_files = [] + def fetch_files + fetched_files = [] - fetched_files += pipenv_files - fetched_files += pyproject_files + fetched_files += pipenv_files + fetched_files += pyproject_files - fetched_files += requirements_in_files - fetched_files += requirement_files if requirements_txt_files.any? + fetched_files += requirements_in_files + fetched_files += requirement_files if requirements_txt_files.any? - fetched_files << setup_file if setup_file - fetched_files << setup_cfg if setup_cfg - fetched_files += path_setup_files - fetched_files << pip_conf if pip_conf - fetched_files << python_version if python_version + fetched_files << setup_file if setup_file + fetched_files << setup_cfg if setup_cfg + fetched_files += path_setup_files + fetched_files << pip_conf if pip_conf + fetched_files << python_version if python_version - check_required_files_present - fetched_files.uniq - end - - def pipenv_files - [pipfile, pipfile_lock].compact - end + check_required_files_present + fetched_files.uniq + end - def pyproject_files - [pyproject, pyproject_lock, poetry_lock].compact - end + def pipenv_files + [pipfile, pipfile_lock].compact + end - def requirement_files - [ - *requirements_txt_files, - *child_requirement_files, - *constraints_files - ] - end + def pyproject_files + [pyproject, pyproject_lock, poetry_lock].compact + end - def check_required_files_present - if requirements_txt_files.any? || setup_file || pipfile || pyproject - return - end + def requirement_files + [ + *requirements_txt_files, + *child_requirement_files, + *constraints_files + ] + end - path = Pathname.new(File.join(directory, "requirements.txt")). - cleanpath.to_path - raise Dependabot::DependencyFileNotFound, path + def check_required_files_present + if requirements_txt_files.any? || setup_file || pipfile || pyproject + return end - def setup_file - @setup_file ||= fetch_file_if_present("setup.py") - end + path = Pathname.new(File.join(directory, "requirements.txt")). + cleanpath.to_path + raise Dependabot::DependencyFileNotFound, path + end - def setup_cfg - @setup_cfg ||= fetch_file_if_present("setup.cfg") - end + def setup_file + @setup_file ||= fetch_file_if_present("setup.py") + end - def pip_conf - @pip_conf ||= fetch_file_if_present("pip.conf")&. - tap { |f| f.support_file = true } - end + def setup_cfg + @setup_cfg ||= fetch_file_if_present("setup.cfg") + end - def python_version - @python_version ||= fetch_file_if_present(".python-version")&. - tap { |f| f.support_file = true } - end + def pip_conf + @pip_conf ||= fetch_file_if_present("pip.conf")&. + tap { |f| f.support_file = true } + end - def pipfile - @pipfile ||= fetch_file_if_present("Pipfile") - end + def python_version + @python_version ||= fetch_file_if_present(".python-version")&. + tap { |f| f.support_file = true } + end - def pipfile_lock - @pipfile_lock ||= fetch_file_if_present("Pipfile.lock") - end + def pipfile + @pipfile ||= fetch_file_if_present("Pipfile") + end - def pyproject - @pyproject ||= fetch_file_if_present("pyproject.toml") - end + def pipfile_lock + @pipfile_lock ||= fetch_file_if_present("Pipfile.lock") + end - def pyproject_lock - @pyproject_lock ||= fetch_file_if_present("pyproject.lock") - end + def pyproject + @pyproject ||= fetch_file_if_present("pyproject.toml") + end - def poetry_lock - @poetry_lock ||= fetch_file_if_present("poetry.lock") - end + def pyproject_lock + @pyproject_lock ||= fetch_file_if_present("pyproject.lock") + end - def requirements_txt_files - req_txt_and_in_files.select { |f| f.name.end_with?(".txt") } - end + def poetry_lock + @poetry_lock ||= fetch_file_if_present("poetry.lock") + end - def requirements_in_files - req_txt_and_in_files.select { |f| f.name.end_with?(".in") } - end + def requirements_txt_files + req_txt_and_in_files.select { |f| f.name.end_with?(".txt") } + end - def parsed_pipfile - raise "No Pipfile" unless pipfile + def requirements_in_files + req_txt_and_in_files.select { |f| f.name.end_with?(".in") } + end - @parsed_pipfile ||= TomlRB.parse(pipfile.content) - rescue TomlRB::ParseError - raise Dependabot::DependencyFileNotParseable, pipfile.path - end + def parsed_pipfile + raise "No Pipfile" unless pipfile - def req_txt_and_in_files - return @req_txt_and_in_files if @req_txt_and_in_files + @parsed_pipfile ||= TomlRB.parse(pipfile.content) + rescue TomlRB::ParseError + raise Dependabot::DependencyFileNotParseable, pipfile.path + end - @req_txt_and_in_files = [] + def req_txt_and_in_files + return @req_txt_and_in_files if @req_txt_and_in_files - repo_contents. - select { |f| f.type == "file" }. - select { |f| f.name.end_with?(".txt", ".in") }. - map { |f| fetch_file_from_host(f.name) }. - select { |f| requirements_file?(f) }. - each { |f| @req_txt_and_in_files << f } + @req_txt_and_in_files = [] - repo_contents. - select { |f| f.type == "dir" }. - each { |f| @req_txt_and_in_files += req_files_for_dir(f) } + repo_contents. + select { |f| f.type == "file" }. + select { |f| f.name.end_with?(".txt", ".in") }. + map { |f| fetch_file_from_host(f.name) }. + select { |f| requirements_file?(f) }. + each { |f| @req_txt_and_in_files << f } - @req_txt_and_in_files - end + repo_contents. + select { |f| f.type == "dir" }. + each { |f| @req_txt_and_in_files += req_files_for_dir(f) } - def req_files_for_dir(requirements_dir) - dir = directory.gsub(%r{(^/|/$)}, "") - relative_reqs_dir = - requirements_dir.path.gsub(%r{^/?#{Regexp.escape(dir)}/?}, "") + @req_txt_and_in_files + end - repo_contents(dir: relative_reqs_dir). - select { |f| f.type == "file" }. - select { |f| f.name.end_with?(".txt", ".in") }. - map { |f| fetch_file_from_host("#{relative_reqs_dir}/#{f.name}") }. - select { |f| requirements_file?(f) } - end + def req_files_for_dir(requirements_dir) + dir = directory.gsub(%r{(^/|/$)}, "") + relative_reqs_dir = + requirements_dir.path.gsub(%r{^/?#{Regexp.escape(dir)}/?}, "") - def child_requirement_files - @child_requirement_files ||= - begin - fetched_files = requirements_txt_files.dup - requirements_txt_files.flat_map do |requirement_file| - child_files = fetch_child_requirement_files( - file: requirement_file, - previously_fetched_files: fetched_files - ) - - fetched_files += child_files - child_files - end + repo_contents(dir: relative_reqs_dir). + select { |f| f.type == "file" }. + select { |f| f.name.end_with?(".txt", ".in") }. + map { |f| fetch_file_from_host("#{relative_reqs_dir}/#{f.name}") }. + select { |f| requirements_file?(f) } + end + + def child_requirement_files + @child_requirement_files ||= + begin + fetched_files = requirements_txt_files.dup + requirements_txt_files.flat_map do |requirement_file| + child_files = fetch_child_requirement_files( + file: requirement_file, + previously_fetched_files: fetched_files + ) + + fetched_files += child_files + child_files end - end + end + end - def fetch_child_requirement_files(file:, previously_fetched_files:) - paths = file.content.scan(CHILD_REQUIREMENT_REGEX).flatten - current_dir = File.dirname(file.name) + def fetch_child_requirement_files(file:, previously_fetched_files:) + paths = file.content.scan(CHILD_REQUIREMENT_REGEX).flatten + current_dir = File.dirname(file.name) - paths.flat_map do |path| - path = File.join(current_dir, path) unless current_dir == "." - path = Pathname.new(path).cleanpath.to_path + paths.flat_map do |path| + path = File.join(current_dir, path) unless current_dir == "." + path = Pathname.new(path).cleanpath.to_path - next if previously_fetched_files.map(&:name).include?(path) - next if file.name == path + next if previously_fetched_files.map(&:name).include?(path) + next if file.name == path - fetched_file = fetch_file_from_host(path) - grandchild_requirement_files = fetch_child_requirement_files( - file: fetched_file, - previously_fetched_files: previously_fetched_files + [file] - ) - [fetched_file, *grandchild_requirement_files] - end.compact - end + fetched_file = fetch_file_from_host(path) + grandchild_requirement_files = fetch_child_requirement_files( + file: fetched_file, + previously_fetched_files: previously_fetched_files + [file] + ) + [fetched_file, *grandchild_requirement_files] + end.compact + end - def constraints_files - all_requirement_files = requirements_txt_files + - child_requirement_files + def constraints_files + all_requirement_files = requirements_txt_files + + child_requirement_files - constraints_paths = all_requirement_files.map do |req_file| - req_file.content.scan(CONSTRAINT_REGEX).flatten - end.flatten.uniq + constraints_paths = all_requirement_files.map do |req_file| + req_file.content.scan(CONSTRAINT_REGEX).flatten + end.flatten.uniq - constraints_paths.map { |path| fetch_file_from_host(path) } - end - - def path_setup_files - path_setup_files = [] - unfetchable_files = [] + constraints_paths.map { |path| fetch_file_from_host(path) } + end - path_setup_file_paths.each do |path| - path = Pathname.new(File.join(path, "setup.py")).cleanpath.to_path - next if path == "setup.py" && setup_file + def path_setup_files + path_setup_files = [] + unfetchable_files = [] - begin - path_setup_files << fetch_file_from_host(path). - tap { |f| f.support_file = true } - rescue Dependabot::DependencyFileNotFound - unfetchable_files << path - end + path_setup_file_paths.each do |path| + path = Pathname.new(File.join(path, "setup.py")).cleanpath.to_path + next if path == "setup.py" && setup_file - begin - cfg_path = path.gsub(/\.py$/, ".cfg") - path_setup_files << fetch_file_from_host(cfg_path). - tap { |f| f.support_file = true } - rescue Dependabot::DependencyFileNotFound - # Ignore lack of a setup.cfg - nil - end + begin + path_setup_files << fetch_file_from_host(path). + tap { |f| f.support_file = true } + rescue Dependabot::DependencyFileNotFound + unfetchable_files << path end - if unfetchable_files.any? - raise Dependabot::PathDependenciesNotReachable, unfetchable_files + begin + cfg_path = path.gsub(/\.py$/, ".cfg") + path_setup_files << fetch_file_from_host(cfg_path). + tap { |f| f.support_file = true } + rescue Dependabot::DependencyFileNotFound + # Ignore lack of a setup.cfg + nil end + end - path_setup_files + if unfetchable_files.any? + raise Dependabot::PathDependenciesNotReachable, unfetchable_files end - def requirements_file?(file) - return true if file.name.match?(/requirements/x) + path_setup_files + end - content = file.content. - gsub(CONSTRAINT_REGEX, ""). - gsub(CHILD_REQUIREMENT_REGEX, "") + def requirements_file?(file) + return true if file.name.match?(/requirements/x) - tmp_file = DependencyFile.new(name: file.name, content: content) - Dependabot::FileParsers::Python::Pip. - new(dependency_files: [tmp_file], source: source). - parse.any? - rescue Dependabot::DependencyFileNotEvaluatable - false - end + content = file.content. + gsub(CONSTRAINT_REGEX, ""). + gsub(CHILD_REQUIREMENT_REGEX, "") - def path_setup_file_paths - requirement_txt_path_setup_file_paths + pipfile_path_setup_file_paths - end + tmp_file = DependencyFile.new(name: file.name, content: content) + Dependabot::FileParsers::Python::Pip. + new(dependency_files: [tmp_file], source: source). + parse.any? + rescue Dependabot::DependencyFileNotEvaluatable + false + end - def requirement_txt_path_setup_file_paths - (requirements_txt_files + child_requirement_files).map do |req_file| - uneditable_reqs = - req_file.content. - scan(/^['"]?(?\..*?)(?=\[|#|'|"|$)/). - flatten. - map(&:strip). - reject { |p| p.include?("://") } - - editable_reqs = - req_file.content. - scan(/^(?:-e)\s+['"]?(?.*?)(?=\[|#|'|"|$)/). - flatten. - map(&:strip). - reject { |p| p.include?("://") } - - uneditable_reqs + editable_reqs - end.flatten.uniq - end + def path_setup_file_paths + requirement_txt_path_setup_file_paths + pipfile_path_setup_file_paths + end + + def requirement_txt_path_setup_file_paths + (requirements_txt_files + child_requirement_files).map do |req_file| + uneditable_reqs = + req_file.content. + scan(/^['"]?(?\..*?)(?=\[|#|'|"|$)/). + flatten. + map(&:strip). + reject { |p| p.include?("://") } + + editable_reqs = + req_file.content. + scan(/^(?:-e)\s+['"]?(?.*?)(?=\[|#|'|"|$)/). + flatten. + map(&:strip). + reject { |p| p.include?("://") } + + uneditable_reqs + editable_reqs + end.flatten.uniq + end - def pipfile_path_setup_file_paths - return [] unless pipfile + def pipfile_path_setup_file_paths + return [] unless pipfile - paths = [] - %w(packages dev-packages).each do |dep_type| - next unless parsed_pipfile[dep_type] + paths = [] + %w(packages dev-packages).each do |dep_type| + next unless parsed_pipfile[dep_type] - parsed_pipfile[dep_type].each do |_, req| - next unless req.is_a?(Hash) && req["path"] + parsed_pipfile[dep_type].each do |_, req| + next unless req.is_a?(Hash) && req["path"] - paths << req["path"] - end + paths << req["path"] end - - paths end + + paths end end end diff --git a/python/spec/dependabot/python/file_fetcher_spec.rb b/python/spec/dependabot/python/file_fetcher_spec.rb index f7f162a3479..cb256cf2dfb 100644 --- a/python/spec/dependabot/python/file_fetcher_spec.rb +++ b/python/spec/dependabot/python/file_fetcher_spec.rb @@ -1,9 +1,10 @@ # frozen_string_literal: true -require "dependabot/file_fetchers/python/pip" -require_relative "../shared_examples_for_file_fetchers" +require "spec_helper" +require "dependabot/python/file_fetcher" +require_common_spec "file_fetchers/shared_examples_for_file_fetchers" -RSpec.describe Dependabot::FileFetchers::Python::Pip do +RSpec.describe Dependabot::Python::FileFetcher do it_behaves_like "a dependency file fetcher" describe ".required_files_in?" do diff --git a/python/spec/spec_helper.rb b/python/spec/spec_helper.rb new file mode 100644 index 00000000000..08eeb477cec --- /dev/null +++ b/python/spec/spec_helper.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +def common_dir + @common_dir ||= Gem::Specification.find_by_name("dependabot-core").gem_dir +end + +def require_common_spec(path) + require "#{common_dir}/spec/dependabot/#{path}" +end + +require "#{common_dir}/spec/spec_helper.rb"