class Dependabot::Dep::FileUpdater::LockfileUpdater

Attributes

credentials[R]
dependencies[R]
dependency_files[R]

Public Class Methods

new(dependencies:, dependency_files:, credentials:) click to toggle source
# File lib/dependabot/dep/file_updater/lockfile_updater.rb, line 14
def initialize(dependencies:, dependency_files:, credentials:)
  @dependencies = dependencies
  @dependency_files = dependency_files
  @credentials = credentials
end

Public Instance Methods

updated_lockfile_content() click to toggle source
# File lib/dependabot/dep/file_updater/lockfile_updater.rb, line 20
def updated_lockfile_content
  deps = dependencies.select { |d| appears_in_lockfile(d) }
  return lockfile.content if deps.none?

  base_directory = File.join("src", "project",
                             dependency_files.first.directory)
  base_parts = base_directory.split("/").length
  updated_content =
    SharedHelpers.in_a_temporary_directory(base_directory) do |dir|
      write_temporary_dependency_files

      SharedHelpers.with_git_configured(credentials: credentials) do
        # Shell out to dep, which handles everything for us.
        # Note: We are currently doing a full install here (we're not
        # passing no-vendor) because dep needs to generate the digests
        # for each project.
        command = "dep ensure -update #{deps.map(&:name).join(' ')}"
        dir_parts = dir.realpath.to_s.split("/")
        gopath = File.join(dir_parts[0..-(base_parts + 1)])
        run_shell_command(command, "GOPATH" => gopath)
      end

      File.read("Gopkg.lock")
    end

  updated_content
end

Private Instance Methods

add_fsnotify_override(overrides) click to toggle source

Work around a dep bug that results in a panic

# File lib/dependabot/dep/file_updater/lockfile_updater.rb, line 153
def add_fsnotify_override(overrides)
  overrides ||= []
  dep_name = "gopkg.in/fsnotify.v1"

  override = overrides.find { |s| s["name"] == dep_name }
  if override.nil?
    override = { "name" => dep_name }
    overrides << override
  end

  override["source"] = "gopkg.in/fsnotify/fsnotify.v1" unless override["source"]

  overrides
end
appears_in_lockfile(dep) click to toggle source
# File lib/dependabot/dep/file_updater/lockfile_updater.rb, line 197
def appears_in_lockfile(dep)
  !parsed_file(lockfile)["projects"]&.
    find { |p| p["name"] == dep.name }.nil?
end
create_constraint!(parsed_manifest, dep) click to toggle source

Used to lock the version when updating a subdependency

# File lib/dependabot/dep/file_updater/lockfile_updater.rb, line 131
def create_constraint!(parsed_manifest, dep)
  details = { "name" => dep.name }

  # Fetch the details from the lockfile to check whether this
  # sub-dependency needs a git revision or a version.
  original_details =
    parsed_file(lockfile).fetch("projects").
    find { |p| p["name"] == dep.name }

  details["source"] = original_details["source"] if original_details["source"]

  if original_details["version"]
    details["version"] = dep.version
  else
    details["revision"] = dep.version
  end

  parsed_manifest["constraint"] ||= []
  parsed_manifest["constraint"] << details
end
dummy_app_content() click to toggle source
# File lib/dependabot/dep/file_updater/lockfile_updater.rb, line 168
def dummy_app_content
  base = "package main\n\n"\
         "import \"fmt\"\n\n"

  packages_to_import.each { |nm| base += "import \"#{nm}\"\n\n" }

  base + "func main() {\n  fmt.Printf(\"hello, world\\n\")\n}"
end
lockfile() click to toggle source
# File lib/dependabot/dep/file_updater/lockfile_updater.rb, line 211
def lockfile
  @lockfile ||= dependency_files.find { |f| f.name == "Gopkg.lock" }
end
manifest() click to toggle source
# File lib/dependabot/dep/file_updater/lockfile_updater.rb, line 207
def manifest
  @manifest ||= dependency_files.find { |f| f.name == "Gopkg.toml" }
end
packages_to_import() click to toggle source
# File lib/dependabot/dep/file_updater/lockfile_updater.rb, line 177
def packages_to_import
  parsed_lockfile = TomlRB.parse(lockfile.content)

  # If the lockfile was created using dep v0.5.0+ then it will tell us
  # exactly which packages to import
  if parsed_lockfile.dig("solve-meta", "input-imports")
    return parsed_lockfile.dig("solve-meta", "input-imports")
  end

  # Otherwise we have no way of knowing, so import everything in the
  # lockfile that isn't marked as internal
  parsed_lockfile.fetch("projects").flat_map do |dep|
    dep["packages"].map do |package|
      next if package.start_with?("internal")

      package == "." ? dep["name"] : File.join(dep["name"], package)
    end.compact
  end
end
parsed_file(file) click to toggle source
# File lib/dependabot/dep/file_updater/lockfile_updater.rb, line 202
def parsed_file(file)
  @parsed_file ||= {}
  @parsed_file[file.name] ||= TomlRB.parse(file.content)
end
prepared_manifest() click to toggle source
# File lib/dependabot/dep/file_updater/lockfile_updater.rb, line 81
def prepared_manifest
  DependencyFile.new(
    name: manifest.name,
    content: prepared_manifest_content
  )
end
prepared_manifest_content() click to toggle source
# File lib/dependabot/dep/file_updater/lockfile_updater.rb, line 88
def prepared_manifest_content
  parsed_manifest = TomlRB.parse(manifest.content)

  parsed_manifest["override"] =
    add_fsnotify_override(parsed_manifest["override"])

  dependencies.each do |dep|
    req = dep.requirements.find { |r| r[:file] == manifest.name }
    next unless appears_in_lockfile(dep)

    if req
      update_constraint!(parsed_manifest, dep)
    else
      create_constraint!(parsed_manifest, dep)
    end
  end

  TomlRB.dump(parsed_manifest)
end
run_shell_command(command, env = {}) click to toggle source
# File lib/dependabot/dep/file_updater/lockfile_updater.rb, line 52
def run_shell_command(command, env = {})
  start = Time.now
  command = SharedHelpers.escape_command(command)
  stdout, process = Open3.capture2e(env, command)
  time_taken = Time.now - start

  # Raise an error with the output from the shell session if dep
  # returns a non-zero status
  return if process.success?

  raise SharedHelpers::HelperSubprocessFailed.new(
    message: stdout,
    error_context: {
      command: command,
      time_taken: time_taken,
      process_exit_value: process.to_s
    }
  )
end
update_constraint!(parsed_manifest, dep) click to toggle source

Used to lock the version when updating a top-level dependency

# File lib/dependabot/dep/file_updater/lockfile_updater.rb, line 109
def update_constraint!(parsed_manifest, dep)
  details =
    parsed_manifest.
    values_at(*Dep::FileParser::REQUIREMENT_TYPES).
    flatten.compact.find { |d| d["name"] == dep.name }

  req = dep.requirements.find { |r| r[:file] == manifest.name }

  if req.fetch(:source).fetch(:type) == "git" && !details["branch"]
    # NOTE: we don't try to update to a specific revision if the
    # branch was previously specified because the change in
    # specification type would be persisted in the lockfile
    details["revision"] = dep.version if details["revision"]
    details["version"] = dep.version if details["version"]
  elsif req.fetch(:source).fetch(:type) == "default"
    details.delete("branch")
    details.delete("revision")
    details["version"] = "=#{dep.version}"
  end
end
write_temporary_dependency_files() click to toggle source
# File lib/dependabot/dep/file_updater/lockfile_updater.rb, line 72
def write_temporary_dependency_files
  File.write(lockfile.name, lockfile.content)

  # Overwrite the manifest with our custom prepared one
  File.write(prepared_manifest.name, prepared_manifest.content)

  File.write("hello.go", dummy_app_content)
end