Creating Sparkle Appcast via Rake Tasks
I have a RubyCocoa application that self-updates using Sparkle. To do so, you need to create an "appcast" file which contains the version and download information for your application, as well as creating the zip file that holds your app. Then, you of course have to upload this to the server and location that you have specified in the SUFeedURL key value in your Info.plist file of your app. For general instructions on using Sparkle and setting it up, see their Basic Instructions page.
- Your app root directory
- Rakefile
- appcast
- version_info.yml
- build
- Your app zip files go here (e.g. Linker_0.8.zip, Linker_0.9.zip, etc.)
- Rake task will create the linker_appcast.xml file here
linker-04:
title: Linker 0.4
filename: Linker_0.4.zip
description: Added Sparkle updating mechanism.linker-05:
title: Linker 0.5
filename: Linker_0.5.zip
description: Added help (see Help menu). Added bookmarklet support/custom URL protocol handling. See the new help for information on how to use the bookmarklet.
Note that you can put HTML into the "description" field, and my Rake task will deal with and preserve that. Finally, I have two Rake tasks, one for building the appcast, and the other for uploading it and the latest zip file to the server. These each are simply one liners that call a parallel Ruby method within the Rakefile:
namespace :appcast do
desc "Create/update the appcast file"
task :build do
make_appcast
end desc "Upload the appcast file to the server"
task :upload do
upload_appcast
end
end
The two methods rely on you defining a couple of variables in your Rakefile, adjust these as desired:
APPCAST_SERVER = 'your_appcast_server.com'
APPCAST_URL = "http://#{APPCAST_SERVER}"
APPCAST_FILENAME = 'linker_appcast.xml'
Here are the methods, first the one that builds the appcast, which you'll need to modify for your app:
def make_appcast
begin
versions = YAML.load_file("appcast/version_info.yml")
rescue Exception => e
raise StandardError, "appcast/version_info.yml could not be loaded: #{e.message}"
end appcast = File.open("appcast/build/#{APPCAST_FILENAME}", 'w') xml = Builder::XmlMarkup.new(:target => appcast, :indent => 2) xml.instruct!
xml.rss('xmlns:atom' => "http://www.w3.org/2005/Atom",
'xmlns:sparkle' => "http://www.andymatuschak.org/xml-namespaces/sparkle",
:version => "2.0") do
xml.channel do
xml.title('BWA Linker')
xml.link(APPCAST_URL)
xml.description('Linker app updates')
xml.language('en')
xml.pubDate(Time.now.rfc822)
xml.lastBuildDate(Time.now.rfc822)
xml.atom(:link, :href => "#{APPCAST_URL}/#{APPCAST_FILENAME}",
:rel => "self", :type => "application/rss+xml") versions.each do |version|
guid = version.first
items = version[1]
file = "appcast/build/#{items['filename']}" xml.item do
xml.title(items['title'])
xml.description { xml xml.pubDate(File.mtime(file))
xml.enclosure(:url => "#{APPCAST_URL}/#{items['filename']}",
:length => "#{File.size(file)}", :type => "application/zip")
xml.guid(guid, :isPermaLink => "false")
end
end
end
end
end
Looking through that above, you'll want to modify the title and description at least. Now on to the uploader method:
def upload_appcast
remote_dir = "/var/www/apps/bwa/shared/public/updaters/" Net::SSH.start( APPCAST_SERVER, 'deploy' ) do |session|
cwd = Dir.pwd
Dir.chdir('appcast/build') shell = session.shell.sync begin
out = shell.cd remote_dir
raise "Failed to change to proper remote directory." unless out.status == 0 out = shell.ls("-1")
raise "Failed to get directory listing." unless out.status == 0 files = Array.new
out.stdout.each { |file| files
# Look through the list of files and see what we need to upload, as
# compared to what we have locally - but always upload the appcast itself
local_files = Dir.glob('*')
files.delete(APPCAST_FILENAME) # we always upload this
local_files.each do |local_file|
unless files.include?(local_file)
print "Uploading: #{local_file}... "
`scp #{local_file} deploy@#{APPCAST_SERVER}:#{remote_dir}`
puts $?.exitstatus == 0 ? "done." : "FAILED!"
end
end
rescue => e
puts "Failed: #{e.message}"
ensure
Dir.chdir(cwd)
shell.exit
end
end
end
You will of course want to modify the
remote_dir, and the login credentials towards the bottom (where it does the scp command). This also relies on you having your SSH keys set up, so you don't have to enter a password when it does the scp.You could further generalize this obviously, but this is what I have, it works fine, and I haven't needed to extract anything out further. Posting here as per a request, and hopefully it saves someone else a few minutes.
Comments [0]