class PackageCloud::CLI::Entry

Public Instance Methods

promote(source_repo_desc, package_name, dest_repo_desc) click to toggle source
# File lib/package_cloud/cli/entry.rb, line 28
def promote(source_repo_desc, package_name, dest_repo_desc)
  repo_name = source_repo_desc.split("/")[0..1].join("/")
  dist = expand_dist_shortcut(source_repo_desc.split("/")[2..3].join("/"))

  dest_repo_name = expand_dist_shortcut(dest_repo_desc.split("/")[0..1].join("/"))

  if dist == "" && package_name =~ /\.gem$/
    dist = "gems"
  end

  print "Looking for source repository at #{repo_name}... "
  src_repo = client.repository(source_repo_desc)
  print "success!\n"

  print "Looking for destination repository at #{dest_repo_name}... "
  repo = client.repository(dest_repo_desc)
  print "success!\n"

  if package_name.include?('@')
    scope, unscoped_package_name = package_scope(package_name)

    print "Attempting to promote scoped package #{scope}/#{unscoped_package_name} from #{repo_name}/#{dist} to #{dest_repo_name}..."
    packages = src_repo.promote(dist, unscoped_package_name, dest_repo_name, scope)
  else
    print "Attempting to promote #{repo_name}/#{dist}/#{package_name} to #{dest_repo_name}..."
    packages = src_repo.promote(dist, package_name, dest_repo_name)
  end
  puts "done!".color(:green)

  if dist == "node/1"
    puts "WARNING: This Node.js package will NOT be downloadable by clients until a dist tag is created. Read more: https://packagecloud.io/docs/#node_promote".color(:yellow)
  end
end
push(repo, package_file, *package_files) click to toggle source
# File lib/package_cloud/cli/entry.rb, line 112
def push(repo, package_file, *package_files)
  total_time = Benchmark.measure do
    ARGV.clear # otherwise gets explodes
    package_files << package_file

    exts = package_files.map { |f| f.split(".").last }.uniq

    if package_files.length > 1 && exts.length > 1
      abort("You can't push multiple packages of different types at the same time.\nFor example, use *.deb to push all your debs at once.".color(:red))
    end

    invalid_packages = package_files.select do |f|
      !SUPPORTED_EXTS.include?(f.split(".").last.downcase)
    end

    if JAVA_EXTS.include?(exts.first.downcase) && options.has_key?("coordinates")
      puts "Using coordinates #{options["coordinates"].color(:yellow)}"
    end

    if !options.has_key?("skip-file-ext-validation") && invalid_packages.any?
      message = "I don't know how to push these packages:\n\n".color(:red)
      invalid_packages.each do |p|
        message << "  #{p}\n"
      end
      message << "\npackage_cloud only supports node.js, deb, gem, java, python, "\
                 "rpm, alpine, helm, anyfile (.zip, .asc) packages".color(:red)
      abort(message)
    end

    if !options.has_key?("yes") && exts.first == "gem" && package_files.length > 1
      answer = get_valid("Are you sure you want to push #{package_files.length} packages? (y/n)") do |s|
        s == "y" || s == "n"
      end

      if answer != "y"
        abort("Aborting...".color(:red))
      end
    end

    validator = Validator.new(client)
    if PY_EXTS.include?(exts.first.downcase)
      dist_id   = validator.distribution_id(repo, package_files, 'py')
    elsif ANYFILE_EXTS.include?(exts.first.downcase)
      dist_id   = validator.distribution_id(repo, package_files, 'anyfile')
    elsif NODE_EXTS.include?(exts.first.downcase)
      dist_id   = validator.distribution_id(repo, package_files, 'node')
    elsif JAVA_EXTS.include?(exts.first.downcase)
      abort_if_snapshot!(package_files)
      dist_id   = validator.distribution_id(repo, package_files, 'jar')
    elsif AMBIGUOUS_EXTS.include?(exts.first.downcase)
      dist_id   = validator.distribution_id_from_repo_url(repo, package_files)
    else
      dist_id   = validator.distribution_id(repo, package_files, exts.first)
    end

    # strip os/dist
    split_repo = repo.split("/")[0..1].join("/")

    print "Looking for repository at #{split_repo}... "
    client_repo = nil
    measurement = Benchmark.measure do
      client_repo = client.repository(split_repo)
    end
    print "success!\n"
    $logger.debug("repository lookup request timing: #{measurement}")

    package_files.each do |f|
      files = nil
      ext = f.split(".").last

      if ext == "dsc"
        print "Checking source package #{f}... "
        files = parse_and_verify_dsc(client_repo, f, dist_id)
      end

      print "Pushing #{f}... "
      measurement = Benchmark.measure do
        if options.has_key?("skip-errors")
          create_package_skip_errors(client_repo, f, dist_id, files, ext, options["coordinates"])
        elsif options.has_key?("skip-duplicates")
          create_package_skip_duplicates(client_repo, f, dist_id, files, ext, options["coordinates"])
        else
          create_package(client_repo, f, dist_id, files, ext, options["coordinates"])
        end
      end
      $logger.debug("create package request timing: #{measurement}")
    end
  end
  $logger.debug("push command total timing: #{total_time}")
end
version() click to toggle source
# File lib/package_cloud/cli/entry.rb, line 205
def version
  puts "package_cloud CLI #{VERSION}\nSee https://packagecloud.io/docs#cli for more details."
end
yank(repo_desc, package_name) click to toggle source
# File lib/package_cloud/cli/entry.rb, line 64
def yank(repo_desc, package_name)
  ARGV.clear # otherwise gets explodes

  # strip os/dist
  repo_name = repo_desc.split("/")[0..1].join("/")
  dist = expand_dist_shortcut(repo_desc.split("/")[2..3].join("/"))

  if dist == "" && package_name =~ /\.gem$/
    dist = "gems"
  end

  print "Looking for repository at #{repo_name}... "
  repo = client.repository(repo_desc)
  print "success!\n"

  if package_name.include?('@')
    scope, unscoped_package_name = package_scope(package_name)
    print "Attempting to yank scoped package at #{repo_name}/#{dist} #{package_name}..."
    repo.yank(dist, unscoped_package_name, scope)
  else
    print "Attempting to yank package at #{repo_name}/#{dist}/#{package_name}..."
    repo.yank(dist, package_name)
  end
  puts "done!".color(:green)

  if dist == "node/1"
    puts "WARNING: Deleting Node.js packages can have unexpected side effects with dist tags. Read more: https://packagecloud.io/docs/#node_delete".color(:yellow)
  end
end

Private Instance Methods

abort_if_snapshot!(files) click to toggle source
# File lib/package_cloud/cli/entry.rb, line 235
def abort_if_snapshot!(files)
  if files.any? { |file| file.include?("-SNAPSHOT")  }
    abort("SNAPSHOT uploads are not supported by the CLI, please use Maven instead: https://packagecloud.io/docs#wagon-instructions")
  end
end
create_package(client_repo, f, dist_id, files, ext, coordinates=nil) click to toggle source
# File lib/package_cloud/cli/entry.rb, line 276
def create_package(client_repo, f, dist_id, files, ext, coordinates=nil)
  begin
    client_repo.create_package(f, dist_id, files, ext, coordinates)
  rescue RestClient::UnprocessableEntity => e
    print "error:\n".color(:red)
    json = JSON.parse(e.response)
    json.each do |k,v|
      if v.is_a? String
        puts "\n\t#{k}: #{v}\n"
      elsif v.is_a? Array
        puts "\n\t#{k}: #{v.join(", ")}\n"
      end
    end
    puts ""
    exit(1)
  end
end
create_package_skip_duplicates(client_repo, f, dist_id, files, ext, coordinates=nil) click to toggle source
# File lib/package_cloud/cli/entry.rb, line 254
def create_package_skip_duplicates(client_repo, f, dist_id, files, ext, coordinates=nil)
  begin
    client_repo.create_package(f, dist_id, files, ext, coordinates=nil)
  rescue RestClient::UnprocessableEntity => e
    json = JSON.parse(e.response)
    if json == { "filename" => ["has already been taken"] }
      print "skipping, already exists.\n".color(:yellow)
    else
      print "error:\n".color(:red)
      json.each do |k,v|
        if v.is_a? String
          puts "\n\t#{k}: #{v}\n"
        elsif v.is_a? Array
          puts "\n\t#{k}: #{v.join(", ")}\n"
        end
      end
      puts ""
      exit(1)
    end
  end
end
create_package_skip_errors(client_repo, f, dist_id, files, ext, coordinates=nil) click to toggle source
# File lib/package_cloud/cli/entry.rb, line 241
def create_package_skip_errors(client_repo, f, dist_id, files, ext, coordinates=nil)
  begin
    client_repo.create_package(f, dist_id, files, ext, coordinates=nil)
  rescue RestClient::UnprocessableEntity => e
    print "error (skipping):\n".color(:yellow)
    json = JSON.parse(e.response)
    json.each do |k,v|
      puts "\n\t#{k}: #{v.join(", ")}\n"
    end
    puts ""
  end
end
expand_dist_shortcut(dist) click to toggle source
# File lib/package_cloud/cli/entry.rb, line 210
def expand_dist_shortcut(dist)
  case dist
  when 'java'
    'java/maven2'
  when 'python'
    'python/1'
  when 'anyfile'
    'anyfile/1'
  when 'node'
    'node/1'
  else
    dist
  end
end
find_and_verify(dir, files) click to toggle source
# File lib/package_cloud/cli/entry.rb, line 300
def find_and_verify(dir, files)
  file_paths = []
  files.each do |f|
    filepath = File.join(dir, f["filename"])
    if !File.exist?(filepath)
      print "Unable to find file name: #{f["filename"]} for source package: #{filepath}\n".color(:red)
      abort("Aborting...".color(:red))
    end

    disk_size = File.stat(filepath).size
    if disk_size != f["size"]
      print "File #{f["filename"]} has size: #{disk_size}, expected: #{f["size"]}\n".color(:red)
      abort("Aborting...".color(:red))
    end

    print "Found DSC package file #{f["filename"]} for upload\n"

    file_paths << filepath
  end
  file_paths
end
get_valid(prompt) click to toggle source
# File lib/package_cloud/cli/entry.rb, line 322
def get_valid(prompt)
  selection = ""
  times = 0
  until yield(selection)
    if times > 0
      puts "#{selection} is not a valid selection."
    end
    print "#{prompt}: "
    selection = ::Kernel.gets.chomp
    times += 1
  end

  selection
end
package_scope(package_name) click to toggle source
# File lib/package_cloud/cli/entry.rb, line 225
def package_scope(package_name)
  name_parts = package_name.split('/')
  if name_parts.size != 2 || !package_name.include?('@')
    abort("Could not determine scope for '#{package_name}', it should look like: @my-scope/package-1.0.tgz".color(:red))
  end
  scope = name_parts[0]
  unscoped_package_name = name_parts[1]
  [scope, unscoped_package_name]
end
parse_and_verify_dsc(repo, f, dist_id) click to toggle source
# File lib/package_cloud/cli/entry.rb, line 294
def parse_and_verify_dsc(repo, f, dist_id)
  files = repo.parse_dsc(f, dist_id)
  dirname = File.dirname(f)
  find_and_verify(dirname, files)
end