motion-sparkle-sandbox 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,22 @@
1
+ <h1>Your app - version x.yz</h1>
2
+
3
+ <h2>The greatest update ever</h2>
4
+
5
+ <dl>
6
+ <dt>An interface to drool for</dt>
7
+ <dd>I won't say too much (wouldn't want to spoil the surprise)</dd>
8
+ <dt>Record-breaking performance</dt>
9
+ <dd>
10
+ <p>
11
+ We keep doing it every time and you start to be less impressed, but we're still very proud of ourselves! We could write about our performance for hours, and make you read it for days!
12
+ </p>
13
+ <p>
14
+ <strong>What for?</strong>
15
+ <br>
16
+ This does serve a purpose, however, and it is to show you how a longer text would look like. It'll help you figure out your style.
17
+ </p>
18
+ <p>
19
+ Just do the same for your app now :)
20
+ </p>
21
+ </dd>
22
+ </dl>
@@ -0,0 +1,19 @@
1
+ <html>
2
+ <head>
3
+ <title>Release Notes for <%= @config.name %></title>
4
+ <style>
5
+ * { font-family: "Helvetica Neue", "Helvetica", "Arial", "Sans";}
6
+ h1 { background-color: #ABABFF; padding: 10px;}
7
+ h1 span { font-size: 75%; font-weight: normal; float: right;}
8
+ dt { font-size: 105%; font-weight: bold; margin: 5px 0;}
9
+ dd { margin: 0; padding: 5px 0 0 5px; }
10
+ dd p {margin: 2px 0; padding: 0;}
11
+ </style>
12
+ </head>
13
+
14
+ <body>
15
+
16
+ <%= release_notes_html %>
17
+
18
+ </body>
19
+ </html>
@@ -0,0 +1,75 @@
1
+ module Motion::Project
2
+ class Sparkle
3
+
4
+ SPARKLE_ZIP_FILE = 'Sparkle.zip'
5
+
6
+ def sparkle_distrib
7
+ file_path = Pathname.new File.dirname(__FILE__)
8
+ distrib_path = "vendor/#{SPARKLE_ZIP_FILE}"
9
+ (file_path.parent.parent.parent + distrib_path).to_s
10
+ Pathname.new(sparkle_vendor_path + SPARKLE_ZIP_FILE)
11
+ end
12
+
13
+ def sparkle_vendor_path
14
+ file_path = Pathname.new File.dirname(__FILE__)
15
+ (file_path.parent.parent.parent + 'vendor/').to_s
16
+ end
17
+
18
+ def sparkle_path
19
+ Pathname.new(vendor_path + 'Sparkle')
20
+ end
21
+
22
+ def sparkle_framework_path
23
+ Pathname.new(vendor_path + 'Sparkle/Sparkle.framework')
24
+ end
25
+
26
+ def sparkle_xpc_path
27
+ Pathname.new(vendor_path + 'Sparkle/XPCServices')
28
+ end
29
+
30
+ def sparkle_zipball
31
+ Pathname.new(vendor_path + SPARKLE_ZIP_FILE)
32
+ end
33
+
34
+ def copy_zipball
35
+ `cp #{sparkle_distrib} #{sparkle_zipball}`
36
+ end
37
+
38
+ def unzip
39
+ `unzip #{sparkle_zipball.to_s} -d #{vendor_path.to_s}`
40
+ `rm #{sparkle_zipball}`
41
+ end
42
+
43
+ def installed?
44
+ File.directory?(sparkle_framework_path)
45
+ end
46
+
47
+ def install
48
+ FileUtils.rm_rf(sparkle_path) if File.directory?(sparkle_path) # force clean install
49
+ copy_zipball
50
+ unzip
51
+ end
52
+
53
+ def embed
54
+ @config.embedded_frameworks << sparkle_framework_path
55
+ end
56
+
57
+ def install_and_embed
58
+ install unless installed?
59
+ embed
60
+ end
61
+
62
+ def verify_installation
63
+ if installed?
64
+ App.info "Sparkle", "Framework installed in #{sparkle_framework_path.to_s}"
65
+ else
66
+ App.fail "Sparkle framework not correctly copied to #{sparkle_framework_path.to_s}
67
+ Run `rake sparkle:install` manually or, if the problem persists,
68
+ please explain your setup and problem as an issue on GitHub at:
69
+ https://github.com/digitalmoksha/motion-sparkle-sandbox/issues
70
+ "
71
+ end
72
+ end
73
+
74
+ end
75
+ end
@@ -0,0 +1,44 @@
1
+ module Motion::Project
2
+ class Sparkle
3
+
4
+ def package
5
+ return unless setup_ok?
6
+ create_release_folder
7
+ @config.build_mode = :release
8
+ return unless create_zip_file
9
+ App.info "Release", version_string
10
+ App.info "Version", @config.short_version
11
+ App.info "Build", @config.version || 'unspecified in Rakefile'
12
+ App.info "Size", @package_size.to_s
13
+ sign_package
14
+ create_appcast
15
+ create_release_notes
16
+ `open #{sparkle_release_path}`
17
+ end
18
+
19
+ def create_zip_file
20
+ unless File.exist?(app_bundle_path)
21
+ App.fail "You need to build your app with the Release target to use Sparkle"
22
+ end
23
+ if File.exist?("#{sparkle_release_path}/#{zip_file}")
24
+ App.fail "Release already exists at ./#{sparkle_release_path}/#{zip_file} (remove it manually with `rake sparkle:clean`)"
25
+ end
26
+ FileUtils.cd(app_release_path) do
27
+ `zip -r --symlinks "#{zip_file}" "#{app_file}"`
28
+ end
29
+ FileUtils.mv "#{app_release_path}/#{zip_file}", "./#{sparkle_release_path}/"
30
+ App.info "Create", "./#{sparkle_release_path}/#{zip_file}"
31
+ @package_file = zip_file
32
+ @package_size = File.size "./#{sparkle_release_path}/#{zip_file}"
33
+ end
34
+
35
+ def sign_package
36
+ package = "./#{sparkle_release_path}/#{zip_file}"
37
+ @package_signature = `#{openssl} dgst -sha1 -binary < "#{package}" | #{openssl} dgst -dss1 -sign "#{private_key_path}" | #{openssl} enc -base64`
38
+ @package_signature = @package_signature.strip
39
+ App.info "Signature", "\"#{@package_signature}\""
40
+ end
41
+
42
+
43
+ end
44
+ end
@@ -0,0 +1,59 @@
1
+ module Motion::Project
2
+
3
+ class Config
4
+ variable :sparkle
5
+
6
+ def sparkle(&block)
7
+ @sparkle ||= Motion::Project::Sparkle.new(self)
8
+ if block
9
+ @sparkle.instance_eval &block
10
+ end
11
+ @sparkle
12
+ end
13
+ end
14
+
15
+ class App
16
+ class << self
17
+ def build_with_sparkle(platform, opts = {})
18
+ if App.config.sparkle.setup_ok?
19
+ App.info "Sparkle", "Setup OK"
20
+ else
21
+ exit 1
22
+ end
23
+ build_without_sparkle(platform, opts)
24
+ end
25
+
26
+ alias_method "build_without_sparkle", "build"
27
+ alias_method "build", "build_with_sparkle"
28
+ end
29
+ end
30
+
31
+ class Builder
32
+
33
+ # if we're using the sandboxed version of Sparkle, then we need to copy the
34
+ # xpc services to the proper folder and sign them. This has to be done
35
+ # before we sign the app itself
36
+ #------------------------------------------------------------------------------
37
+ def codesign_with_sparkle(config, platform)
38
+ if App.config.embedded_frameworks.any? {|item| item.to_s.include?('Sparkle.framework')}
39
+ bundle_path = App.config.app_bundle('MacOSX')
40
+ if File.directory?(App.config.sparkle.sparkle_xpc_path)
41
+ xpc_path = File.join(bundle_path, "XPCServices")
42
+ App.info 'Sparkle', "Copying XPCServices to #{xpc_path}"
43
+ FileUtils.mkdir_p(xpc_path)
44
+ `cp -R #{App.config.sparkle.sparkle_xpc_path}/*.xpc "#{xpc_path}"`
45
+
46
+ Dir.glob("#{xpc_path}/*.xpc").each do |path|
47
+ App.info 'Codesign', path
48
+ results = `#{App.config.sparkle.sparkle_vendor_path}/codesign_xpc "#{App.config.codesign_certificate}" "#{File.expand_path(path)}" 2>&1`
49
+ end
50
+ end
51
+ end
52
+ codesign_without_sparkle(config, platform)
53
+ end
54
+
55
+ alias_method "codesign_without_sparkle", "codesign"
56
+ alias_method "codesign", "codesign_with_sparkle"
57
+ end
58
+
59
+ end
@@ -0,0 +1,89 @@
1
+ # Rake tasks
2
+ namespace :sparkle do
3
+
4
+ task :install do
5
+ sparkle = App.config.sparkle
6
+ sparkle.install
7
+ end
8
+
9
+ desc "Setup Sparkle configuration"
10
+ task :setup do
11
+ sparkle = App.config.sparkle
12
+ sparkle.setup
13
+ end
14
+
15
+ desc "Create a ZIP file with you application .app release build"
16
+ task :package do
17
+ App.config_without_setup.build_mode = :release
18
+ sparkle = App.config.sparkle
19
+ sparkle.package
20
+ end
21
+
22
+ task :setup_certificates do
23
+ sparkle = App.config.sparkle
24
+ sparkle.generate_keys
25
+ end
26
+
27
+ desc "Sign the ZIP file with appropriate certificates"
28
+ task :sign do
29
+ App.config_without_setup.build_mode = :release
30
+ sparkle = App.config.sparkle
31
+ sparkle.sign_package
32
+ end
33
+
34
+ task :recreate_public_key do
35
+ sparkle = App.config.sparkle
36
+ sparkle.generate_public_key
37
+ end
38
+
39
+ task :copy_release_notes_templates do
40
+ App.config_without_setup.build_mode = :release
41
+ sparkle = App.config.sparkle
42
+ sparkle.copy_templates(force = true)
43
+ end
44
+
45
+ desc "Generate the appcast xml feed"
46
+ task :feed do
47
+ App.config_without_setup.build_mode = :release
48
+ sparkle = App.config.sparkle
49
+ sparkle.create_appcast
50
+ end
51
+
52
+ desc "Generate the appcast using Sparkle's `generate_appcast`"
53
+ task :generate_appcast do
54
+ App.config_without_setup.build_mode = :release
55
+ sparkle = App.config.sparkle
56
+ results = `#{sparkle.sparkle_vendor_path}/generate_appcast "#{sparkle.private_key_path}" "#{sparkle.archive_folder}"`
57
+ end
58
+
59
+ desc "Update the release notes of this build"
60
+ task :release_notes do
61
+ App.config_without_setup.build_mode = :release
62
+ sparkle = App.config.sparkle
63
+ sparkle.create_release_notes
64
+ end
65
+
66
+ desc "Upload to configured location"
67
+ task :upload do
68
+ end
69
+
70
+ desc "Clean the Sparkle release folder"
71
+ task :clean do
72
+ dir = Motion::Project::Sparkle::RELEASE_PATH
73
+ if File.exist?("./#{dir}")
74
+ App.info 'Delete', "./#{dir}"
75
+ rm_rf dir
76
+ end
77
+ end
78
+ end
79
+
80
+ namespace :clean do
81
+ # Delete Sparkle release folder when cleaning all
82
+ task :all do
83
+ dir = Motion::Project::Sparkle::RELEASE_PATH
84
+ if File.exist?("./#{dir}")
85
+ App.info 'Delete', "./#{dir}"
86
+ rm_rf dir
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,96 @@
1
+ module Motion::Project
2
+ class Sparkle
3
+
4
+ def setup_ok?
5
+ config_ok?
6
+ certificates_ok?
7
+ end
8
+
9
+ def config_ok?
10
+ check_base_url
11
+ check_feed_url
12
+ check_public_key
13
+ end
14
+
15
+ def check_base_url
16
+ base_url_check = appcast.base_url.to_s
17
+ if base_url_check.nil? or base_url_check.empty?
18
+ App.fail "Sparkle :base_url missing. Use `release :base_url, 'http://example.com/your_app_folder'` in your Rakefile's `app.sparkle` block"
19
+ end
20
+ true
21
+ end
22
+
23
+ def check_feed_url
24
+ feed_url_check = @config.info_plist['SUFeedURL']
25
+ feed_filename_check = appcast.feed_filename
26
+ if feed_url_check.nil? or feed_url_check.empty? or feed_filename_check.nil? or feed_filename_check.empty?
27
+ App.fail "Sparkle :feed_filename is nil or blank. Please check your Rakefile."
28
+ end
29
+ true
30
+ end
31
+
32
+ def check_public_key
33
+ public_key_check = @config.info_plist['SUPublicDSAKeyFile'].to_s
34
+ if public_key_check.nil? or public_key_check.empty?
35
+ App.fail "Sparkle :public_key is nil or blank. Please check your Rakefile."
36
+ end
37
+ true
38
+ end
39
+
40
+ def certificates_ok?(silence=false)
41
+ unless File.exist?("./#{Sparkle::CONFIG_PATH}")
42
+ if silence
43
+ return false
44
+ else
45
+ App.fail "Missing `#{Sparkle::CONFIG_PATH}`. Run `rake sparkle:setup` to get started"
46
+ end
47
+ end
48
+ unless File.exist?(private_key_path)
49
+ if silence
50
+ return false
51
+ else
52
+ App.fail "Missing `#{private_key_path}`. Please run `rake sparkle:setup_certificates` or check the docs to know where to put them."
53
+ end
54
+ end
55
+ unless File.exist?(public_key_path)
56
+ if silence
57
+ return false
58
+ else
59
+ 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`"
60
+ end
61
+ end
62
+ true
63
+ end
64
+
65
+ def setup
66
+ verify_installation
67
+ create_sparkle_folder
68
+ add_to_gitignore
69
+ copy_templates
70
+
71
+ if config_ok?
72
+ App.info "Sparkle", "Config found"
73
+ else
74
+ return false
75
+ end
76
+
77
+ silence = true
78
+ if certificates_ok?(silence)
79
+ App.info "Sparkle", "Certificates found"
80
+ else
81
+ App.info "Sparkle", "Certificates not found
82
+ Please generate your private and public keys with
83
+ `rake sparkle:setup_certificates`
84
+ If you already have your certificates and only need to include them in the project, follow these steps:
85
+ 1. Rename your private key to `./#{private_key_path}`
86
+ 2. Place your public key in `./#{public_key_path}`
87
+ 3. If you wish to use a different name or location for your public key within the resources dir,
88
+ make sure you add `publish :public_key, 'folder/new_name.pem'` to the sparkle config in your Rakefile
89
+ "
90
+ return false
91
+ end
92
+ App.info "Sparkle", "Setup OK. After `rake build:release`, you can now run `rake sparkle:package`."
93
+ end
94
+
95
+ end
96
+ end
@@ -0,0 +1,189 @@
1
+ module Motion::Project
2
+ class Sparkle
3
+
4
+ SPARKLE_ROOT = "sparkle"
5
+ CONFIG_PATH = "#{SPARKLE_ROOT}/config"
6
+ RELEASE_PATH = "#{SPARKLE_ROOT}/release"
7
+
8
+ def initialize(config)
9
+ @config = config
10
+ publish :public_key, 'dsa_pub.pem'
11
+ install_and_embed
12
+ end
13
+
14
+ def appcast
15
+ @appcast ||= Appcast.new
16
+ end
17
+
18
+ def publish(key, value)
19
+ case key
20
+ when :public_key
21
+ public_key value
22
+ when :base_url
23
+ appcast.base_url = value
24
+ feed_url appcast.feed_url
25
+ when :feed_base_url
26
+ appcast.feed_base_url = value
27
+ feed_url appcast.feed_url
28
+ when :feed_filename
29
+ appcast.feed_filename = value
30
+ feed_url appcast.feed_url
31
+ when :version
32
+ version value
33
+ when :notes_base_url, :package_base_url, :notes_filename, :package_filename
34
+ appcast.send "#{key}=", value
35
+ when :archive_folder
36
+ appcast.archive_folder = value
37
+ else
38
+ raise "Unknown Sparkle config option #{key}"
39
+ end
40
+ end
41
+ alias_method :release, :publish
42
+
43
+ def version(vstring)
44
+ @config.version = vstring.to_s
45
+ @config.short_version = vstring.to_s
46
+ end
47
+
48
+ def version_string
49
+ "#{@config.short_version} (#{@config.version})"
50
+ end
51
+
52
+ def feed_url(url)
53
+ @config.info_plist['SUFeedURL'] = url
54
+ end
55
+
56
+ def public_key(path_in_resources_folder)
57
+ @config.info_plist['SUPublicDSAKeyFile'] = path_in_resources_folder
58
+ end
59
+
60
+ # File manipulation and certificates
61
+
62
+ def add_to_gitignore
63
+ @ignorable = ['sparkle/release','sparkle/release/*','sparkle/config/dsa_priv.pem']
64
+ return unless File.exist?(gitignore_path)
65
+ File.open(gitignore_path, 'r') do |f|
66
+ f.each_line do |line|
67
+ @ignorable.delete(line) if @ignorable.include?(line)
68
+ end
69
+ end
70
+ File.open(gitignore_path, 'a') do |f|
71
+ @ignorable.each do |i|
72
+ f << "#{i}\n"
73
+ end
74
+ end if @ignorable.any?
75
+ `cat #{gitignore_path}`
76
+ end
77
+
78
+ def create_sparkle_folder
79
+ create_config_folder
80
+ create_release_folder
81
+ end
82
+
83
+ def create_config_folder
84
+ FileUtils.mkdir_p(sparkle_config_path) unless File.exist?(sparkle_config_path)
85
+ end
86
+
87
+ def create_release_folder
88
+ FileUtils.mkdir_p(sparkle_release_path) unless File.exist?(sparkle_release_path)
89
+ end
90
+
91
+ def generate_keys
92
+ return false unless config_ok?
93
+ unless File.exist?(sparkle_config_path)
94
+ FileUtils.mkdir_p sparkle_config_path
95
+ end
96
+ [dsa_param_path, private_key_path, public_key_path].each do |file|
97
+ if File.exist? file
98
+ App.info "Sparkle", "Error: file exists.
99
+ There's already a '#{file}'. Be careful not to override or lose your certificates. \n
100
+ Delete this file if you're sure. \n
101
+ Aborting (no action performed)
102
+ "
103
+ return
104
+ end
105
+ end
106
+ `#{openssl} dsaparam 2048 < /dev/urandom > #{dsa_param_path}`
107
+ `#{openssl} gendsa #{dsa_param_path} -out #{private_key_path}`
108
+ generate_public_key
109
+ `rm #{dsa_param_path}`
110
+ App.info "Sparkle", "Generated private and public certificates.
111
+ Details:
112
+ * Private certificate: ./#{private_key_path}
113
+ * Public certificate: ./#{public_key_path}
114
+ Warning:
115
+ ADD YOUR PRIVATE CERTIFICATE TO YOUR `.gitignore` OR EQUIVALENT AND BACK IT UP!
116
+ KEEP IT PRIVATE AND SAFE!
117
+ If you lose it, your users will be unable to upgrade.
118
+ "
119
+ end
120
+
121
+ def generate_public_key
122
+ `#{openssl} dsa -in #{private_key_path} -pubout -out #{public_key_path}`
123
+ end
124
+
125
+ # A few helpers
126
+
127
+ def openssl
128
+ "/usr/bin/openssl"
129
+ end
130
+
131
+ def project_path
132
+ @project_path ||= Pathname.new(@config.project_dir)
133
+ end
134
+
135
+ def vendor_path
136
+ @vendor_path ||= Pathname.new(project_path + 'vendor/')
137
+ end
138
+
139
+ def gitignore_path
140
+ project_path + ".gitignore"
141
+ end
142
+
143
+ def sparkle_release_path
144
+ project_path + RELEASE_PATH
145
+ end
146
+
147
+ def sparkle_config_path
148
+ project_path + CONFIG_PATH
149
+ end
150
+
151
+ def dsa_param_path
152
+ sparkle_config_path + "dsaparam.pem"
153
+ end
154
+
155
+ def private_key_path
156
+ sparkle_config_path + "dsa_priv.pem"
157
+ end
158
+
159
+ def public_key_path
160
+ pub_key_file = @config.info_plist['SUPublicDSAKeyFile']
161
+ project_path + "resources/#{pub_key_file}"
162
+ end
163
+
164
+ def app_bundle_path
165
+ Pathname.new @config.app_bundle_raw('MacOSX')
166
+ end
167
+
168
+ def app_release_path
169
+ app_bundle_path.parent.to_s
170
+ end
171
+
172
+ def app_name
173
+ File.basename(app_bundle_path, '.app')
174
+ end
175
+
176
+ def zip_file
177
+ appcast.package_filename || "#{app_name}.zip"
178
+ end
179
+
180
+ def archive_folder
181
+ appcast.archive_folder
182
+ end
183
+
184
+ def app_file
185
+ "#{app_name}.app"
186
+ end
187
+
188
+ end
189
+ end