class Buildr::MavenCentral

Attributes

password[W]
user_agent[W]
username[W]

Public Class Methods

buildr_release(project, profile_name, username, password) click to toggle source
# File lib/buildr/java/publish.rb, line 78
def buildr_release(project, profile_name, username, password)
  release_to_url = Buildr.repositories.release_to[:url]
  release_to_username = Buildr.repositories.release_to[:username]
  release_to_password = Buildr.repositories.release_to[:password]

  begin
    Buildr.repositories.release_to[:url] = 'https://oss.sonatype.org/service/local/staging/deploy/maven2'
    Buildr.repositories.release_to[:username] = username
    Buildr.repositories.release_to[:password] = password

    project.task(':package').invoke

    r = MavenCentral.new
    r.username = username
    r.password = password
    r.user_agent = "Buildr-#{Buildr::VERSION}"
    while r.get_staging_repositories(profile_name, false).size != 0
      puts 'Another project currently staging. Waiting for other repository to complete. Please visit the website https://oss.sonatype.org/index.html#stagingRepositories to view the other staging attempts.'
      sleep 1
    end
    puts "Beginning upload to staging repository #{profile_name}"

    project.task(':upload').invoke

    r.release_sole_auto_staging(profile_name)
  ensure
    Buildr.repositories.release_to[:url] = release_to_url
    Buildr.repositories.release_to[:username] = release_to_username
    Buildr.repositories.release_to[:password] = release_to_password
  end
end
define_publish_tasks(options = {}) click to toggle source
# File lib/buildr/java/publish.rb, line 22
def define_publish_tasks(options = {})
  candidate_branches = options[:branches] || %w(master)
  desc 'Publish release on maven central'
  task 'mcrt:publish' do
    project = options[:project] || Buildr.projects[0].root_project
    profile_name = options[:profile_name] || (raise ':profile_name not specified when defining tasks')
    username = options[:username] || (raise ':username name not specified when defining tasks')
    password = options[:password] || ENV['MAVEN_CENTRAL_PASSWORD'] || (raise "Unable to locate environment variable with name 'MAVEN_CENTRAL_PASSWORD'")
    MavenCentral.buildr_release(project, profile_name, username, password)
  end

  desc 'Publish release to maven central iff current HEAD is a tag'
  task 'mcrt:publish_if_tagged' do
    tag = MavenCentral.get_head_tag_if_any
    if tag.nil?
      puts 'Current HEAD is not a tag. Skipping publish step.'
    else
      puts "Current HEAD is a tag: #{tag}"
      if MavenCentral.is_tag_on_candidate_branches?(tag, candidate_branches)
        task('mcrt:publish').invoke
      end
    end
  end
end
get_head_tag_if_any() click to toggle source
# File lib/buildr/java/publish.rb, line 47
def get_head_tag_if_any
  version = `git describe --exact-match --tags 2>&1`
  if 0 == $?.exitstatus && version =~ /^v[0-9]/ && (ENV['TRAVIS_BUILD_ID'].nil? || ENV['TRAVIS_TAG'].to_s != '')
    version.strip
  else
    nil
  end
end
is_tag_on_branch?(tag, branch) click to toggle source
# File lib/buildr/java/publish.rb, line 56
def is_tag_on_branch?(tag, branch)
  output = `git tag --merged #{branch} 2>&1`
  tags = output.split
  tags.include?(tag)
end
is_tag_on_candidate_branches?(tag, branches) click to toggle source
# File lib/buildr/java/publish.rb, line 62
def is_tag_on_candidate_branches?(tag, branches)
  sh 'git fetch origin'
  branches.each do |branch|
    if is_tag_on_branch?(tag, branch)
      puts "Tag #{tag} is on branch: #{branch}"
      return true
    elsif is_tag_on_branch?(tag, "origin/#{branch}")
      puts "Tag #{tag} is on branch: origin/#{branch}"
      return true
    else
      puts "Tag #{tag} is not on branches: #{branch} or origin/#{branch}"
    end
  end
  false
end

Public Instance Methods

close_repository(repository_id, description) click to toggle source
# File lib/buildr/java/publish.rb, line 174
def close_repository(repository_id, description)
  post_request('https://oss.sonatype.org/service/local/staging/bulk/close',
               JSON.pretty_generate('data' => { 'description' => description, 'stagedRepositoryIds' => [repository_id] }))
end
drop_repository(repository_id, description) click to toggle source
# File lib/buildr/java/publish.rb, line 186
def drop_repository(repository_id, description)
  post_request('https://oss.sonatype.org/service/local/staging/bulk/drop',
               JSON.pretty_generate('data' => { 'description' => description, 'stagedRepositoryIds' => [repository_id] }))
end
get_my_ip_addresses() click to toggle source
# File lib/buildr/java/publish.rb, line 141
def get_my_ip_addresses
  addresses = Socket.ip_address_list.collect { |a| a.ip_address.to_s }
  commands = [
    "dig +short myip.opendns.com @resolver1.opendns.com",
    "curl ifconfig.me",
    "curl icanhazip.com",
    "curl ipecho.net/plain",
    "curl ifconfig.co",
    "dig TXT +short o-o.myaddr.l.google.com @ns1.google.com | awk -F'\"' '{ print $2 }'"
  ]
  commands.each do |cmd|
    begin
      addresses << `#{cmd}`.strip
    rescue Exception
      # ignored
    end
  end
  urls = %w[http://www.myexternalip.com/raw https://diagnostic.opendns.com/myip]
  urls.each do |url|
    begin
      addresses << Net::HTTP.get(URI(url)).strip
    rescue Exception
      # ignored
    end
  end
  begin
    addresses << JSON.parse(Net::HTTP.get(URI('https://api.ipify.org?format=json')))['ip']
  rescue Exception
    # ignored
  end
  addresses.sort.uniq
end
get_staging_repositories(profile_name, ignore_transitioning_repositories = true) click to toggle source
# File lib/buildr/java/publish.rb, line 129
def get_staging_repositories(profile_name, ignore_transitioning_repositories = true)
  result = get_request('https://oss.sonatype.org/service/local/staging/profile_repositories')
  result = JSON.parse(result)
  result['data'].select do |repo|
    repo['profileName'] == profile_name &&
      repo['userId'] == self.username &&
      repo['userAgent'] == self.user_agent &&
      (!ignore_transitioning_repositories || !repo['transitioning']) &&
      get_my_ip_addresses.any? { |a| a == repo['ipAddress'] }
  end
end
password() click to toggle source
# File lib/buildr/java/publish.rb, line 119
def password
  @password || (raise 'Password not yet specified')
end
promote_repository(repository_id, description) click to toggle source
# File lib/buildr/java/publish.rb, line 179
def promote_repository(repository_id, description)
  post_request('https://oss.sonatype.org/service/local/staging/bulk/promote',
               JSON.pretty_generate('data' => { 'autoDropAfterRelease' => true,
                                                'description' => description,
                                                'stagedRepositoryIds' => [repository_id] }))
end
release_sole_auto_staging(profile_name) click to toggle source
# File lib/buildr/java/publish.rb, line 191
def release_sole_auto_staging(profile_name)
  candidates = get_staging_repositories(profile_name)
  if candidates.empty?
    raise 'Release process unable to find any staging repositories.'
  elsif 1 != candidates.size
    raise 'Release process found multiple staging repositories that could be the release just uploaded. Please visit the website https://oss.sonatype.org/index.html#stagingRepositories and manually complete the release.'
  else
    candidate = candidates[0]
    puts "Requesting close of staging repository #{profile_name}:#{candidate['repositoryId']}"
    begin
      close_repository(candidate['repositoryId'], "Closing repository for #{profile_name}")
    rescue Exception => e
      puts "#{e.class.name}: #{e.message}"
      puts e.backtrace.join("\n")
      raise 'Failed to close repository. It is likely that the release does not conform to Maven Central release requirements. Please visit the website https://oss.sonatype.org/index.html#stagingRepositories and manually complete the release.'
    end
    while get_staging_repositories(profile_name).size == 0
      puts 'Waiting for repository to close...'
      sleep 1
    end
    puts "Requesting promotion of staging repository #{profile_name}:#{candidate['repositoryId']}"
    begin
      promote_repository(candidate['repositoryId'], "Promoting repository for #{profile_name}")
    rescue Exception => e
      puts "#{e.class.name}: #{e.message}"
      puts e.backtrace.join("\n")
      raise 'Failed to promote repository. Please visit the website https://oss.sonatype.org/index.html#stagingRepositories and manually complete the release.'
    end
    repositories = get_staging_repositories(profile_name, false)
    while repositories.size == 1
      puts 'Waiting for repository to be promoted...'
      sleep 1
      if repositories[0]['notifications'] != 0
        raise 'Failed to promote repository. Please visit the website https://oss.sonatype.org/index.html#stagingRepositories and manually complete the release.'
      end
      repositories = get_staging_repositories(profile_name, false)
    end
  end
end
user_agent() click to toggle source
# File lib/buildr/java/publish.rb, line 125
def user_agent
  @user_agent || "Ruby-#{RUBY_VERSION}"
end
username() click to toggle source
# File lib/buildr/java/publish.rb, line 113
def username
  @username || (raise 'Username not yet specified')
end

Private Instance Methods

create_http(uri) click to toggle source
# File lib/buildr/java/publish.rb, line 233
def create_http(uri)
  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = true
  http.verify_mode = OpenSSL::SSL::VERIFY_PEER
  http
end
get_request(url) click to toggle source
# File lib/buildr/java/publish.rb, line 246
def get_request(url)
  uri = URI.parse(url)
  request = Net::HTTP::Get.new(uri.request_uri)
  setup_standard_request(request)
  create_http(uri).request(request).body
end
post_request(url, content) click to toggle source
# File lib/buildr/java/publish.rb, line 253
def post_request(url, content)
  uri = URI.parse(url)
  request = Net::HTTP::Post.new(uri.request_uri)
  setup_standard_request(request)
  request.add_field('Content-Type', 'application/json')
  request.body = content
  create_http(uri).request(request).body
end
setup_standard_request(request) click to toggle source
# File lib/buildr/java/publish.rb, line 240
def setup_standard_request(request)
  request['Accept'] = 'application/json,application/vnd.siesta-error-v1+json,application/vnd.siesta-validation-errors-v1+json'
  request.basic_auth(self.username, self.password)
  request.add_field('User-Agent', self.user_agent)
end