Files
textmate/bin/upload
Allan Odgaard 1293828e79 Upload to existing release if it already exists
Creating a release with attachment is non-atomic in that the release is first created and then we upload to that release but our build file has it as a single action, so this action should be able to handle partial success from a previous execution.
2013-12-12 14:11:26 +07:00

119 lines
4.1 KiB
Ruby
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby -wKU
# == 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)
accept = "-H'Accept: application/vnd.github.manifold-preview'"
payload = { 'name' => name, 'tag_name' => tag, 'draft' => false, 'prerelease' => true }
payload['body'] = description unless description.nil?
open("|curl #{accept} -snd '#{payload.to_json}' https://api.github.com/repos/#{repository}/releases") do |io|
github = JSON.parse(io.read)
if github.include?('errors')
err = github['errors'].first
if err['code'] == 'already_exists' && err['field'] == 'tag_name'
open("|curl #{accept} -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!(/\{\?name\}/, '')
auth = "-H'Authorization: token b8babd96eb93a0baa4a1cda59f5b886060e6c21e'"
upload_url = "|curl #{accept} -s #{auth} -H'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 #{accept} -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
info = base_info.merge({
'url' => create_release(path, destination, tag, description, 'application/x-bzip2'),
'signature' => sign_file(path, keyfile, netinfo.password),
'signee' => netinfo.login
})
STDOUT << info.to_json
end
end