#!/System/Library/Frameworks/Ruby.framework/Versions/Current/usr/bin/ruby -w
# == Synopsis
#
# upload [-k «keyfile»] [-d «destination»] [-m «json»] «file»
#
# == Usage
#
# --help:
#    show help.
#
# --keyfile/-k:
#    Keyfile used for signing.
#
# --destination/-d:
#    Which GitHub ‘«user»/«repository»’ we should upload to.
#
# --merge/-m:
#    JSON that should be placed in the result dictionary.
#
# --description/-s:
#    Optional description.
#
require 'getoptlong'
require "rubygems"
require "net/netrc"
require "json"
require 'base64'
require 'openssl'
require 'digest/sha1'

def sign_file(path, keyfile, password)
  # %x{openssl dgst -dss1 -sign '#{keyfile}' -passin 'pass:#{password}' '#{path}'|openssl enc -base64}.chomp

  key       = OpenSSL::PKey::DSA.new(File.read(keyfile), password) or abort "*** error reading keyfile: ‘#{keyfile}’."
  digest    = Digest::SHA1.digest(File.read(path))
  signature = key.syssign(digest)
  Base64.encode64(signature).gsub("\n", '')
end

def create_release(path, repository, tag, description, content_type)
  name = tag.sub(/^v(.*?)-(.*?)\.(.*)$/, 'TextMate \1 (\2 \3)')
  basename = File::basename(path)
  payload = { 'name' => name, 'tag_name' => tag, 'draft' => false, 'prerelease' => true }
  payload['body'] = description unless description.nil?
  open("|curl -snd '#{payload.to_json}' https://api.github.com/repos/#{repository}/releases") do |response|
    github = JSON.parse(response.read)

    if github.include?('errors')
      err = github['errors'].first
      if err['code'] == 'already_exists' && err['field'] == 'tag_name'
        open("|curl -sn https://api.github.com/repos/#{repository}/releases") do |io|
          github = JSON.parse(io.read)
          github = github.find { |e| e['tag_name'] == tag }
          STDERR << "Upload to existing release: #{github['html_url']}\n"
        end
      else
        abort "GitHub error: #{github['errors'].inspect} for #{repository}" if github.include?('errors')
      end
    end

    if url = github['upload_url']
      url.sub!(/\{\?.*?\}/, '')
      upload_url = "|curl -nH'Content-Type: #{content_type}' --data-binary @'#{path}' '#{url}?name=#{basename}'"
      open(upload_url) do |io|
        res = JSON.parse(io.read)
        return "https://github.com/#{repository}/releases/download/#{tag}/#{basename}" if res.include?('url')
        STDERR << "Unexpected response (asset): #{res.inspect}\n"
      end
    else
      STDERR << "Unexpected response (release): #{github.inspect}\n"
    end
    open("|curl -snX DELETE #{github['url']}") { |io| STDERR << "Removing release: #{github['url']}\n" }
  end
  abort
end

if __FILE__ == $PROGRAM_NAME
  opts = GetoptLong.new(
    [ '--help',        '-h', GetoptLong::NO_ARGUMENT       ],
    [ '--keyfile',     '-k', GetoptLong::REQUIRED_ARGUMENT ],
    [ '--destination', '-d', GetoptLong::REQUIRED_ARGUMENT ],
    [ '--tag',         '-t', GetoptLong::REQUIRED_ARGUMENT ],
    [ '--merge',       '-m', GetoptLong::REQUIRED_ARGUMENT ],
    [ '--description', '-s', GetoptLong::REQUIRED_ARGUMENT ]
  )

  keyfile     = nil
  destination = 'textmate/textmate'
  tag         = nil
  base_info   = { }
  description = nil
  netinfo     = Net::Netrc.locate("sign.textmate.org") or abort "*** missing passphrase in ~/.netrc."

  opts.each do |opt, arg|
    case opt
      when '--help'        then RDoc::usage
      when '--keyfile'     then keyfile     = arg
      when '--destination' then destination = arg
      when '--tag'         then tag         = arg
      when '--merge'       then base_info   = JSON.parse(arg)
      when '--description' then description = arg
    end
  end

  abort 'No signing key provided' if keyfile.nil?
  abort "No tag specified"        if tag.nil?

  if path = ARGV.shift
    signature = sign_file(path, keyfile, netinfo.password)
    info = base_info.merge({
      'url'       => create_release(path, destination, tag, description, 'application/x-bzip2'),
      'signature' => signature,
      'signee'    => netinfo.login
    })
    STDOUT << info.to_json
  end
end
