Class: Motion::Project::Sparkle
- Inherits:
-
Object
- Object
- Motion::Project::Sparkle
- Defined in:
- lib/motion/project/templates.rb,
lib/motion/project/version.rb,
lib/motion/project/sparkle.rb,
lib/motion/project/package.rb,
lib/motion/project/install.rb,
lib/motion/project/appcast.rb,
lib/motion/project/setup.rb
Defined Under Namespace
Classes: Appcast
Constant Summary collapse
- TEMPLATE_PATHS =
[ File.(File.join(__FILE__, '../appcast')) ]
- VERSION =
'0.0.6'- SPARKLE_ROOT =
"sparkle"- CONFIG_PATH =
"#{SPARKLE_ROOT}/config"- RELEASE_PATH =
"#{SPARKLE_ROOT}/release"
Instance Method Summary collapse
-
#add_to_gitignore ⇒ Object
File manipulation and certificates.
- #all_templates ⇒ Object
- #app_bundle_path ⇒ Object
- #app_file ⇒ Object
- #app_name ⇒ Object
- #app_release_path ⇒ Object
- #appcast ⇒ Object
- #appcast_xml ⇒ Object
- #certificates_ok?(silence = false) ⇒ Boolean
- #check_base_url ⇒ Object
- #check_feed_url ⇒ Object
- #check_public_key ⇒ Object
- #config_ok? ⇒ Boolean
- #copy_templates(force = false) ⇒ Object
- #copy_zipball ⇒ Object
- #create_appcast ⇒ Object
- #create_config_folder ⇒ Object
- #create_release_folder ⇒ Object
- #create_release_notes ⇒ Object
- #create_sparkle_folder ⇒ Object
- #create_zip_file ⇒ Object
- #dsa_param_path ⇒ Object
- #embed ⇒ Object
- #feed_url(url) ⇒ Object
- #generate_keys ⇒ Object
- #generate_public_key ⇒ Object
- #gitignore_path ⇒ Object
-
#initialize(config) ⇒ Sparkle
constructor
A new instance of Sparkle.
- #install ⇒ Object
- #install_and_embed ⇒ Object
- #installed? ⇒ Boolean
-
#openssl ⇒ Object
A few helpers.
- #package ⇒ Object
- #private_key_path ⇒ Object
- #project_path ⇒ Object
- #public_key(path_in_resources_folder) ⇒ Object
- #public_key_path ⇒ Object
- #publish(key, value) ⇒ Object (also: #release)
- #release_notes_content ⇒ Object
- #release_notes_content_path ⇒ Object
- #release_notes_html ⇒ Object
- #release_notes_path ⇒ Object
- #release_notes_template_path ⇒ Object
- #setup ⇒ Object
- #setup_ok? ⇒ Boolean
- #sign_package ⇒ Object
- #sparkle_config_path ⇒ Object
- #sparkle_distrib ⇒ Object
- #sparkle_path ⇒ Object
- #sparkle_release_path ⇒ Object
- #sparkle_zipball ⇒ Object
- #unzip ⇒ Object
- #vendor_path ⇒ Object
- #verify_installation ⇒ Object
- #version(vstring) ⇒ Object
- #version_string ⇒ Object
- #zip_file ⇒ Object
Constructor Details
#initialize(config) ⇒ Sparkle
Returns a new instance of Sparkle.
8 9 10 11 12 |
# File 'lib/motion/project/sparkle.rb', line 8 def initialize(config) @config = config publish :public_key, 'dsa_pub.pem' end |
Instance Method Details
#add_to_gitignore ⇒ Object
File manipulation and certificates
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/motion/project/sparkle.rb', line 60 def add_to_gitignore @ignorable = ['sparkle/release','sparkle/release/*','sparkle/config/dsa_priv.pem'] return unless File.exist?(gitignore_path) File.open(gitignore_path, 'r') do |f| f.each_line do |line| @ignorable.delete(line) if @ignorable.include?(line) end end File.open(gitignore_path, 'a') do |f| @ignorable.each do |i| f << "#{i}\n" end end if @ignorable.any? `cat #{gitignore_path}` end |
#all_templates ⇒ Object
11 12 13 14 15 16 17 18 19 |
# File 'lib/motion/project/templates.rb', line 11 def all_templates @all_templates ||= begin templates = {} TEMPLATE_PATHS.map { |path| Dir.glob(path + '/*') }.flatten.each do |template_path| templates[File.basename(template_path)] = template_path end templates end end |
#app_bundle_path ⇒ Object
162 163 164 |
# File 'lib/motion/project/sparkle.rb', line 162 def app_bundle_path Pathname.new @config.app_bundle_raw('MacOSX') end |
#app_file ⇒ Object
178 179 180 |
# File 'lib/motion/project/sparkle.rb', line 178 def app_file "#{app_name}.app" end |
#app_name ⇒ Object
170 171 172 |
# File 'lib/motion/project/sparkle.rb', line 170 def app_name File.basename(app_bundle_path, '.app') end |
#app_release_path ⇒ Object
166 167 168 |
# File 'lib/motion/project/sparkle.rb', line 166 def app_release_path app_bundle_path.parent.to_s end |
#appcast ⇒ Object
14 15 16 |
# File 'lib/motion/project/sparkle.rb', line 14 def appcast @appcast ||= Appcast.new end |
#appcast_xml ⇒ Object
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
# File 'lib/motion/project/appcast.rb', line 32 def appcast_xml rss = REXML::Element.new 'rss' rss.attributes['xmlns:atom'] = "http://www.w3.org/2005/Atom" rss.attributes['xmlns:sparkle'] = "http://www.andymatuschak.org/xml-namespaces/sparkle" rss.attributes['xmlns:version'] = "2.0" rss.attributes['xmlns:dc'] = "http://purl.org/dc/elements/1.1/" channel = rss.add_element 'channel' channel.add_element('title').text = @config.name channel.add_element('description').text = "#{@config.name} updates" channel.add_element('link').text = @config.info_plist["SUFeedURL"] channel.add_element('language').text = 'en' channel.add_element('pubDate').text = Time.now.strftime("%a, %d %b %Y %H:%M:%S %z") atom_link = channel.add_element('atom:link') atom_link.attributes['href'] = @config.info_plist["SUFeedURL"] atom_link.attributes['rel'] = 'self' atom_link.attributes['type'] = "application/rss+xml" item = channel.add_element 'item' item.add_element('title').text = "#{@config.name} #{@config.version}" item.add_element('pubDate').text = Time.now.strftime("%a, %d %b %Y %H:%M:%S %z") guid = item.add_element('guid') guid.text = "#{@config.name}-#{@config.version}" guid.attributes['isPermaLink'] = false item.add_element('sparkle:releaseNotesLink').text = "#{appcast.notes_url}" enclosure = item.add_element('enclosure') enclosure.attributes['url'] = "#{appcast.package_url}" enclosure.attributes['length'] = "#{@package_size}" enclosure.attributes['type'] = "application/octet-stream" enclosure.attributes['sparkle:version'] = @config.version enclosure.attributes['sparkle:dsaSignature'] = @package_signature rss end |
#certificates_ok?(silence = false) ⇒ Boolean
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/motion/project/setup.rb', line 40 def certificates_ok?(silence=false) unless File.exist?("./#{Sparkle::CONFIG_PATH}") if silence return false else App.fail "Missing `#{Sparkle::CONFIG_PATH}`. Run `rake sparkle:setup` to get started" end end unless File.exist?(private_key_path) if silence return false else App.fail "Missing `#{private_key_path}`. Please run `rake sparkle:setup_certificates` or check the docs to know where to put them." end end unless File.exist?(public_key_path) if silence return false else App.fail "Missing `#{public_key_path}`. Did you configure `release :public_key` correctly in the Rakefile? Advanced: recreate your public key with `rake sparkle:recreate_public_key`" end end true end |
#check_base_url ⇒ Object
15 16 17 18 19 20 21 |
# File 'lib/motion/project/setup.rb', line 15 def check_base_url base_url_check = appcast.base_url.to_s if base_url_check.nil? or base_url_check.empty? App.fail "Sparkle :base_url missing. Use `release :base_url, 'http://example.com/your_app_folder'` in your Rakefile's `app.sparkle` block" end true end |
#check_feed_url ⇒ Object
23 24 25 26 27 28 29 30 |
# File 'lib/motion/project/setup.rb', line 23 def check_feed_url feed_url_check = @config.info_plist['SUFeedURL'] feed_filename_check = appcast.feed_filename if feed_url_check.nil? or feed_url_check.empty? or feed_filename_check.nil? or feed_filename_check.empty? App.fail "Sparkle :feed_filename is nil or blank. Please check your Rakefile." end true end |
#check_public_key ⇒ Object
32 33 34 35 36 37 38 |
# File 'lib/motion/project/setup.rb', line 32 def check_public_key public_key_check = @config.info_plist['SUPublicDSAKeyFile'].to_s if public_key_check.nil? or public_key_check.empty? App.fail "Sparkle :public_key is nil or blank. Please check your Rakefile." end true end |
#config_ok? ⇒ Boolean
9 10 11 12 13 |
# File 'lib/motion/project/setup.rb', line 9 def config_ok? check_base_url check_feed_url check_public_key end |
#copy_templates(force = false) ⇒ Object
21 22 23 24 25 26 27 28 29 30 31 |
# File 'lib/motion/project/templates.rb', line 21 def copy_templates(force=false) all_templates.each_pair do |tmpl, path| result = "#{sparkle_config_path}/#{tmpl}" if File.exist?(result) and !force App.info 'Exists', result else FileUtils.cp(path, "#{sparkle_config_path}/") App.info 'Create', "./#{sparkle_config_path}/#{tmpl.to_s}" end end end |
#copy_zipball ⇒ Object
18 19 20 |
# File 'lib/motion/project/install.rb', line 18 def copy_zipball `cp #{sparkle_distrib} #{sparkle_zipball}` end |
#create_appcast ⇒ Object
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
# File 'lib/motion/project/appcast.rb', line 16 def create_appcast appcast_file = File.open("#{sparkle_release_path}/#{appcast.feed_filename}", 'w') do |f| xml_string = '' doc = REXML::Formatters::Pretty.new doc.write(appcast_xml, xml_string) f << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" f << xml_string f << "\n" end if appcast_file App.info "Create", "./#{sparkle_release_path}/#{appcast.feed_filename}" else App.info "Fail", "./#{sparkle_release_path}/#{appcast.feed_filename} not created" end end |
#create_config_folder ⇒ Object
81 82 83 |
# File 'lib/motion/project/sparkle.rb', line 81 def create_config_folder FileUtils.mkdir_p(sparkle_config_path) unless File.exist?(sparkle_config_path) end |
#create_release_folder ⇒ Object
85 86 87 |
# File 'lib/motion/project/sparkle.rb', line 85 def create_release_folder FileUtils.mkdir_p(sparkle_release_path) unless File.exist?(sparkle_release_path) end |
#create_release_notes ⇒ Object
4 5 6 7 8 9 10 11 12 13 14 |
# File 'lib/motion/project/appcast.rb', line 4 def create_release_notes if File.exist?(release_notes_template_path) File.open("#{release_notes_path}", "w") do |f| template = File.read(release_notes_template_path) f << ERB.new(template).result(binding) end App.info 'Create', "./#{release_notes_path}" else App.fail "Release notes template not found as expected at ./#{release_notes_template_path}" end end |
#create_sparkle_folder ⇒ Object
76 77 78 79 |
# File 'lib/motion/project/sparkle.rb', line 76 def create_sparkle_folder create_config_folder create_release_folder end |
#create_zip_file ⇒ Object
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
# File 'lib/motion/project/package.rb', line 19 def create_zip_file unless File.exist?(app_bundle_path) App.fail "You need to build your app with the Release target to use Sparkle" end if File.exist?("#{sparkle_release_path}/#{zip_file}") App.fail "Release already exists at ./#{sparkle_release_path}/#{zip_file} (remove it manually with `rake sparkle:clean`)" end FileUtils.cd(app_release_path) do `zip -r --symlinks "#{zip_file}" "#{app_file}"` end FileUtils.mv "#{app_release_path}/#{zip_file}", "./#{sparkle_release_path}/" App.info "Create", "./#{sparkle_release_path}/#{zip_file}" @package_file = zip_file @package_size = File.size "./#{sparkle_release_path}/#{zip_file}" end |
#dsa_param_path ⇒ Object
149 150 151 |
# File 'lib/motion/project/sparkle.rb', line 149 def dsa_param_path sparkle_config_path + "dsaparam.pem" end |
#embed ⇒ Object
36 37 38 |
# File 'lib/motion/project/install.rb', line 36 def @config. << sparkle_path end |
#feed_url(url) ⇒ Object
50 51 52 |
# File 'lib/motion/project/sparkle.rb', line 50 def feed_url(url) @config.info_plist['SUFeedURL'] = url end |
#generate_keys ⇒ Object
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/motion/project/sparkle.rb', line 89 def generate_keys return false unless config_ok? unless File.exist?(sparkle_config_path) FileUtils.mkdir_p sparkle_config_path end [dsa_param_path, private_key_path, public_key_path].each do |file| if File.exist? file App.info "Sparkle", "Error: file exists. There's already a '#{file}'. Be careful not to override or lose your certificates. \n Delete this file if you're sure. \n Aborting (no action performed) " return end end `#{openssl} dsaparam 1024 < /dev/urandom > #{dsa_param_path}` `#{openssl} gendsa #{dsa_param_path} -out #{private_key_path}` generate_public_key `rm #{dsa_param_path}` App.info "Sparkle", "Generated private and public certificates. Details: * Private certificate: ./#{private_key_path} * Public certificate: ./#{public_key_path} Warning: ADD YOUR PRIVATE CERTIFICATE TO YOUR `.gitignore` OR EQUIVALENT AND BACK IT UP! KEEP IT PRIVATE AND SAFE! If you lose it, your users will be unable to upgrade. " end |
#generate_public_key ⇒ Object
119 120 121 |
# File 'lib/motion/project/sparkle.rb', line 119 def generate_public_key `#{openssl} dsa -in #{private_key_path} -pubout -out #{public_key_path}` end |
#gitignore_path ⇒ Object
137 138 139 |
# File 'lib/motion/project/sparkle.rb', line 137 def gitignore_path project_path + ".gitignore" end |
#install ⇒ Object
31 32 33 34 |
# File 'lib/motion/project/install.rb', line 31 def install copy_zipball unzip end |
#install_and_embed ⇒ Object
40 41 42 43 |
# File 'lib/motion/project/install.rb', line 40 def install unless installed? end |
#installed? ⇒ Boolean
27 28 29 |
# File 'lib/motion/project/install.rb', line 27 def installed? File.directory?(sparkle_path) end |
#openssl ⇒ Object
A few helpers
125 126 127 |
# File 'lib/motion/project/sparkle.rb', line 125 def openssl "/usr/bin/openssl" end |
#package ⇒ Object
4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# File 'lib/motion/project/package.rb', line 4 def package return unless setup_ok? create_release_folder @config.build_mode = :release return unless create_zip_file App.info "Release", version_string App.info "Version", @config.version App.info "Build", @config.short_version || 'unspecified in Rakefile' App.info "Size", @package_size.to_s sign_package create_appcast create_release_notes `open #{sparkle_release_path}` end |
#private_key_path ⇒ Object
153 154 155 |
# File 'lib/motion/project/sparkle.rb', line 153 def private_key_path sparkle_config_path + "dsa_priv.pem" end |
#project_path ⇒ Object
129 130 131 |
# File 'lib/motion/project/sparkle.rb', line 129 def project_path @project_path ||= Pathname.new(@config.project_dir) end |
#public_key(path_in_resources_folder) ⇒ Object
54 55 56 |
# File 'lib/motion/project/sparkle.rb', line 54 def public_key(path_in_resources_folder) @config.info_plist['SUPublicDSAKeyFile'] = path_in_resources_folder end |
#public_key_path ⇒ Object
157 158 159 160 |
# File 'lib/motion/project/sparkle.rb', line 157 def public_key_path pub_key_file = @config.info_plist['SUPublicDSAKeyFile'] project_path + "resources/#{pub_key_file}" end |
#publish(key, value) ⇒ Object Also known as: release
18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
# File 'lib/motion/project/sparkle.rb', line 18 def publish(key, value) case key when :public_key public_key value when :base_url appcast.base_url = value feed_url appcast.feed_url when :feed_base_url appcast.feed_base_url = value feed_url appcast.feed_url when :feed_filename appcast.feed_filename = value feed_url appcast.feed_url when :version version value when :notes_base_url, :package_base_url, :notes_filename, :package_filename appcast.send "#{key}=", value else raise "Unknown Sparkle config option #{key}" end end |
#release_notes_content ⇒ Object
76 77 78 79 80 81 82 |
# File 'lib/motion/project/appcast.rb', line 76 def release_notes_content if File.exist?(release_notes_content_path) File.read(release_notes_content_path) else App.fail "Missing #{release_notes_content_path}" end end |
#release_notes_content_path ⇒ Object
68 69 70 |
# File 'lib/motion/project/appcast.rb', line 68 def release_notes_content_path sparkle_config_path + "release_notes.content.html" end |
#release_notes_html ⇒ Object
84 85 86 |
# File 'lib/motion/project/appcast.rb', line 84 def release_notes_html release_notes_content end |
#release_notes_path ⇒ Object
72 73 74 |
# File 'lib/motion/project/appcast.rb', line 72 def release_notes_path sparkle_release_path + appcast.notes_filename.to_s end |
#release_notes_template_path ⇒ Object
64 65 66 |
# File 'lib/motion/project/appcast.rb', line 64 def release_notes_template_path sparkle_config_path + "release_notes.template.erb" end |
#setup ⇒ Object
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 |
# File 'lib/motion/project/setup.rb', line 65 def setup verify_installation create_sparkle_folder add_to_gitignore copy_templates if config_ok? App.info "Sparkle", "Config found" else return false end silence = true if certificates_ok?(silence) App.info "Sparkle", "Certificates found" else App.info "Sparkle", "Certificates not found Please generate your private and public keys with `rake sparkle:setup_certificates` If you already have your certificates and only need to include them in the project, follow these steps: 1. Rename your private key to `./#{private_key_path}` 2. Place your public key in `./#{public_key_path}` 3. If you wish to use a different name or location for your public key within the resources dir, make sure you add `publish :public_key, 'folder/new_name.pem'` to the sparkle config in your Rakefile " return false end App.info "Sparkle", "Setup OK. After `rake build:release`, you can now run `rake sparkle:package`." end |
#setup_ok? ⇒ Boolean
4 5 6 7 |
# File 'lib/motion/project/setup.rb', line 4 def setup_ok? config_ok? certificates_ok? end |
#sign_package ⇒ Object
35 36 37 38 39 40 |
# File 'lib/motion/project/package.rb', line 35 def sign_package package = "./#{sparkle_release_path}/#{zip_file}" @package_signature = `#{openssl} dgst -sha1 -binary < "#{package}" | #{openssl} dgst -dss1 -sign "#{private_key_path}" | #{openssl} enc -base64` @package_signature = @package_signature.strip App.info "Signature", "\"#{@package_signature}\"" end |
#sparkle_config_path ⇒ Object
145 146 147 |
# File 'lib/motion/project/sparkle.rb', line 145 def sparkle_config_path project_path + CONFIG_PATH end |
#sparkle_distrib ⇒ Object
4 5 6 7 8 |
# File 'lib/motion/project/install.rb', line 4 def sparkle_distrib file_path = Pathname.new File.dirname(__FILE__) distrib_path = 'vendor/Sparkle.framework.zip' (file_path.parent.parent.parent + distrib_path).to_s end |
#sparkle_path ⇒ Object
10 11 12 |
# File 'lib/motion/project/install.rb', line 10 def sparkle_path Pathname.new(vendor_path + 'Sparkle.framework') end |
#sparkle_release_path ⇒ Object
141 142 143 |
# File 'lib/motion/project/sparkle.rb', line 141 def sparkle_release_path project_path + RELEASE_PATH end |
#sparkle_zipball ⇒ Object
14 15 16 |
# File 'lib/motion/project/install.rb', line 14 def sparkle_zipball Pathname.new(vendor_path + 'Sparkle.framework.zip') end |
#unzip ⇒ Object
22 23 24 25 |
# File 'lib/motion/project/install.rb', line 22 def unzip `unzip #{sparkle_zipball.to_s} -d #{vendor_path.to_s}` `rm #{sparkle_zipball}` end |
#vendor_path ⇒ Object
133 134 135 |
# File 'lib/motion/project/sparkle.rb', line 133 def vendor_path @vendor_path ||= Pathname.new(project_path + 'vendor/') end |
#verify_installation ⇒ Object
45 46 47 48 49 50 51 52 53 54 55 |
# File 'lib/motion/project/install.rb', line 45 def verify_installation if installed? App.info "Sparkle", "Framework installed in #{sparkle_path.to_s}" else App.fail "Sparkle framework not correctly copied to #{sparkle_path.to_s} Run `rake sparkle:install` manually or, if the problem persists, please explain your setup and problem as an issue on GitHub at: https://github.com/webcracy/motion-sparkle/issues " end end |
#version(vstring) ⇒ Object
41 42 43 44 |
# File 'lib/motion/project/sparkle.rb', line 41 def version(vstring) @config.version = vstring.to_s @config.short_version = vstring.to_s end |
#version_string ⇒ Object
46 47 48 |
# File 'lib/motion/project/sparkle.rb', line 46 def version_string "#{@config.version} (#{@config.short_version})" end |
#zip_file ⇒ Object
174 175 176 |
# File 'lib/motion/project/sparkle.rb', line 174 def zip_file appcast.package_filename || "#{app_name}.zip" end |