motion-sparkle-sandbox 2.0.0 → 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +7 -0
  5. data/.rubocop_todo.yml +183 -0
  6. data/.ruby-version +1 -0
  7. data/.travis.yml +37 -0
  8. data/Gemfile +13 -3
  9. data/README.md +72 -72
  10. data/Rakefile +2 -7
  11. data/bin/bundle +114 -0
  12. data/bin/byebug +29 -0
  13. data/bin/coderay +29 -0
  14. data/bin/fuzzy_match +29 -0
  15. data/bin/htmldiff +29 -0
  16. data/bin/httpclient +29 -0
  17. data/bin/ldiff +29 -0
  18. data/bin/pod +29 -0
  19. data/bin/pry +29 -0
  20. data/bin/rake +29 -0
  21. data/bin/rspec +29 -0
  22. data/bin/rubocop +29 -0
  23. data/bin/ruby-parse +29 -0
  24. data/bin/ruby-rewrite +29 -0
  25. data/bin/sandbox-pod +29 -0
  26. data/bin/xcodeproj +29 -0
  27. data/lib/motion/project/appcast.rb +294 -111
  28. data/lib/motion/project/indent_string.rb +18 -0
  29. data/lib/motion/project/install.rb +22 -66
  30. data/lib/motion/project/package.rb +52 -36
  31. data/lib/motion/project/project.rb +42 -51
  32. data/lib/motion/project/rake_tasks.rb +38 -28
  33. data/lib/motion/project/setup.rb +67 -73
  34. data/lib/motion/project/sparkle.rb +317 -148
  35. data/lib/motion/project/templates.rb +24 -22
  36. data/lib/motion-sparkle-sandbox/version.rb +5 -0
  37. data/lib/motion-sparkle-sandbox.rb +19 -3
  38. data/motion-sparkle-sandbox.gemspec +26 -16
  39. data/sample-app/.gitignore +10 -0
  40. data/sample-app/Gemfile +8 -0
  41. data/sample-app/README.md +87 -0
  42. data/sample-app/Rakefile +47 -0
  43. data/sample-app/app/app_delegate.rb +15 -0
  44. data/sample-app/app/menu.rb +155 -0
  45. data/sample-app/resources/Assets.xcassets/AppIcon.appiconset/1024x1024.png +0 -0
  46. data/sample-app/resources/Assets.xcassets/AppIcon.appiconset/Contents.json +68 -0
  47. data/sample-app/resources/Assets.xcassets/AppIcon.appiconset/Icon_128x128.png +0 -0
  48. data/sample-app/resources/Assets.xcassets/AppIcon.appiconset/Icon_128x128@2x.png +0 -0
  49. data/sample-app/resources/Assets.xcassets/AppIcon.appiconset/Icon_16x16.png +0 -0
  50. data/sample-app/resources/Assets.xcassets/AppIcon.appiconset/Icon_16x16@2x.png +0 -0
  51. data/sample-app/resources/Assets.xcassets/AppIcon.appiconset/Icon_256x256.png +0 -0
  52. data/sample-app/resources/Assets.xcassets/AppIcon.appiconset/Icon_256x256@2x.png +0 -0
  53. data/sample-app/resources/Assets.xcassets/AppIcon.appiconset/Icon_32x32.png +0 -0
  54. data/sample-app/resources/Assets.xcassets/AppIcon.appiconset/Icon_32x32@2x.png +0 -0
  55. data/sample-app/resources/Assets.xcassets/AppIcon.appiconset/Icon_512x512.png +0 -0
  56. data/sample-app/resources/Assets.xcassets/AppIcon.appiconset/Icon_512x512@2x.png +0 -0
  57. data/sample-app/resources/Assets.xcassets/Contents.json +6 -0
  58. data/sample-app/resources/Credits.rtf +29 -0
  59. data/sample-app/spec/main_spec.rb +11 -0
  60. data/spec/appcast_spec.rb +60 -0
  61. data/spec/setup_spec.rb +42 -0
  62. data/spec/sparkle_spec.rb +94 -73
  63. data/spec/spec_helper.rb +100 -27
  64. data/spec/spec_utils.rb +68 -0
  65. data/vendor/.git_keep +0 -0
  66. metadata +80 -14
  67. data/vendor/README.md +0 -34
  68. data/vendor/Sparkle.zip +0 -0
  69. data/vendor/codesign_embedded_executable +0 -46
  70. data/vendor/generate_appcast +0 -0
@@ -1,127 +1,310 @@
1
- module Motion::Project
2
- class Sparkle
3
-
4
- def create_release_notes
5
- if File.exist?(release_notes_template_path)
6
- File.open("#{release_notes_path}", "w") do |f|
7
- template = File.read(release_notes_template_path)
8
- f << ERB.new(template).result(binding)
9
- end
10
- App.info 'Create', "./#{release_notes_path}"
11
- else
12
- App.fail "Release notes template not found as expected at ./#{release_notes_template_path}"
13
- end
14
- end
1
+ # frozen_string_literal: true
15
2
 
16
- def create_appcast
17
- create_release_folder
18
- appcast_file = File.open("#{sparkle_release_path}/#{appcast.feed_filename}", 'w') do |f|
19
- xml_string = ''
20
- doc = REXML::Formatters::Pretty.new
21
- doc.write(appcast_xml, xml_string)
22
- f << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
23
- f << xml_string
24
- f << "\n"
25
- end
26
- if appcast_file
27
- App.info "Create", "./#{sparkle_release_path}/#{appcast.feed_filename}"
28
- else
29
- App.info "Fail", "./#{sparkle_release_path}/#{appcast.feed_filename} not created"
30
- end
31
- end
3
+ # rubocop:disable Metrics/ClassLength
4
+ module Motion
5
+ module Project
6
+ class Sparkle
7
+ class Appcast
8
+ PARAMS = %i[
9
+ package_base_url
10
+ package_filename
11
+ notes_base_url
12
+ notes_filename
13
+ use_exported_private_key
14
+ base_url
15
+ releases_folder
16
+ feed_base_url
17
+ feed_filename
18
+ ].freeze
32
19
 
33
- def appcast_xml
34
- rss = REXML::Element.new 'rss'
35
- rss.attributes['xmlns:atom'] = "http://www.w3.org/2005/Atom"
36
- rss.attributes['xmlns:sparkle'] = "http://www.andymatuschak.org/xml-namespaces/sparkle"
37
- rss.attributes['xmlns:version'] = "2.0"
38
- rss.attributes['xmlns:dc'] = "http://purl.org/dc/elements/1.1/"
39
- channel = rss.add_element 'channel'
40
- channel.add_element('title').text = @config.name
41
- channel.add_element('description').text = "#{@config.name} updates"
42
- channel.add_element('link').text = @config.info_plist["SUFeedURL"]
43
- channel.add_element('language').text = 'en'
44
- channel.add_element('pubDate').text = Time.now.strftime("%a, %d %b %Y %H:%M:%S %z")
45
- atom_link = channel.add_element('atom:link')
46
- atom_link.attributes['href'] = @config.info_plist["SUFeedURL"]
47
- atom_link.attributes['rel'] = 'self'
48
- atom_link.attributes['type'] = "application/rss+xml"
49
- item = channel.add_element 'item'
50
- item.add_element('title').text = "#{@config.name} #{@config.short_version}"
51
- item.add_element('pubDate').text = Time.now.strftime("%a, %d %b %Y %H:%M:%S %z")
52
- guid = item.add_element('guid')
53
- guid.text = "#{@config.name}-#{@config.short_version}"
54
- guid.attributes['isPermaLink'] = false
55
- item.add_element('sparkle:releaseNotesLink').text = "#{appcast.notes_url}"
56
- enclosure = item.add_element('enclosure')
57
- enclosure.attributes['url'] = "#{appcast.package_url}"
58
- enclosure.attributes['length'] = "#{@package_size}"
59
- enclosure.attributes['type'] = "application/octet-stream"
60
- enclosure.attributes['sparkle:version'] = @config.version
61
- enclosure.attributes['sparkle:shortVersionString'] = @config.short_version
62
- enclosure.attributes['sparkle:dsaSignature'] = @package_signature
63
- rss
64
- end
20
+ CLI_OPTIONS = {
21
+ account: '--account',
22
+ private_eddsa_key: '-s',
23
+ download_url_prefix: '--download-url-prefix',
24
+ release_notes_url_prefix: '--release-notes-url-prefix',
25
+ full_release_notes_url: '--full-release-notes-url',
26
+ link: '--link',
27
+ versions: '--versions',
28
+ maximum_deltas: '--maximum-deltas',
29
+ delta_compression: '--delta-compression',
30
+ delta_compression_level: '--delta-compression-level',
31
+ channel: '--channel',
32
+ major_version: '--major-version',
33
+ ignore_skipped_upgrades_below_version: '--ignore-skipped-upgrades-below-version',
34
+ phased_rollout_interval: '--phased-rollout-interval',
35
+ critical_update_version: '--critical-update-version',
36
+ informational_update_versions: '--informational-update-versions',
37
+ output_path: '-o'
38
+ }.freeze
65
39
 
66
- def release_notes_template_path
67
- sparkle_config_path + "release_notes.template.erb"
68
- end
40
+ attr_accessor :base_url,
41
+ :feed_base_url,
42
+ :feed_filename,
43
+ :notes_filename,
44
+ :package_filename,
45
+ :releases_folder,
46
+ :use_exported_private_key,
47
+ :cli_options
48
+ attr_writer :notes_base_url,
49
+ :package_base_url
69
50
 
70
- def release_notes_content_path
71
- sparkle_config_path + "release_notes.content.html"
72
- end
51
+ def initialize(sparkle_object)
52
+ @sparkle = sparkle_object
53
+ @cli_options = {
54
+ account: 'ed25519' # Sparkle's default account
55
+ }
73
56
 
74
- def release_notes_path
75
- sparkle_release_path + appcast.notes_filename.to_s
76
- end
57
+ @feed_base_url = nil
58
+ @feed_filename = 'releases.xml'
59
+ @notes_base_url = nil
60
+ @notes_filename = nil
61
+ @package_base_url = nil
62
+ @package_filename = nil
63
+ @base_url = nil
64
+ @releases_folder = nil
65
+ @use_exported_private_key = false
66
+ end
77
67
 
78
- def release_notes_content
79
- if File.exist?(release_notes_content_path)
80
- File.read(release_notes_content_path)
81
- else
82
- App.fail "Missing #{release_notes_content_path}"
83
- end
84
- end
68
+ def process_option(key, value)
69
+ if CLI_OPTIONS.keys.include?(key)
70
+ cli_options[key] = value
71
+ elsif PARAMS.include?(key)
72
+ send("#{key}=", value)
73
+ else
74
+ return false
75
+ end
85
76
 
86
- def release_notes_html
87
- release_notes_content
88
- end
77
+ true
78
+ end
89
79
 
80
+ def feed_url
81
+ "#{feed_base_url || base_url}#{feed_filename}"
82
+ end
90
83
 
91
- class Appcast
92
- attr_accessor :base_url,
93
- :feed_base_url,
94
- :feed_filename,
95
- :notes_base_url,
96
- :notes_filename,
97
- :package_base_url,
98
- :package_filename,
99
- :archive_folder
100
-
101
- def initialize
102
- @feed_base_url = nil
103
- @feed_filename = 'releases.xml'
104
- @notes_base_url = nil
105
- @notes_filename = 'release_notes.html'
106
- @package_base_url = nil
107
- @package_filename = nil
108
- @base_url = nil
109
- @archive_folder = nil
110
- end
84
+ def notes_base_url
85
+ @notes_base_url || base_url
86
+ end
111
87
 
112
- def feed_url
113
- "#{feed_base_url || base_url}/#{feed_filename}"
114
- end
88
+ def package_base_url
89
+ @package_base_url || base_url
90
+ end
115
91
 
116
- def notes_url
117
- "#{notes_base_url || base_url}/#{notes_filename}"
118
- end
92
+ def prepare_args
93
+ args = []
119
94
 
120
- def package_url
121
- "#{package_base_url || base_url}/#{package_filename}"
122
- end
95
+ account(args)
96
+ private_eddsa_key(args)
97
+ download_url_prefix(args)
98
+ release_notes_url_prefix(args)
99
+ full_release_notes_url(args)
100
+ link(args)
101
+ versions(args)
102
+ maximum_deltas(args)
103
+ delta_compression(args)
104
+ delta_compression_level(args)
105
+ channel(args)
106
+ major_version(args)
107
+ ignore_skipped_upgrades_below_version(args)
108
+ phased_rollout_interval(args)
109
+ critical_update_version(args)
110
+ informational_update_versions(args)
111
+ output_path(args)
123
112
 
124
- end
113
+ args
114
+ end
115
+
116
+ private
117
+
118
+ # --account <account> The account name in your keychain associated with
119
+ # your private EdDSA (ed25519) key to use for signing
120
+ # new updates. (default: ed25519)
121
+ def account(args)
122
+ return unless cli_options[:account].present?
123
+
124
+ args << "--account=#{cli_options[:account]}"
125
+ end
126
+
127
+ # -s <private-EdDSA-key> The private EdDSA string (128 characters). If not
128
+ # specified, the private EdDSA key will be read from
129
+ # the Keychain instead.
130
+ def private_eddsa_key(args)
131
+ if cli_options[:private_eddsa_key].present?
132
+ args << "-s=#{cli_options[:private_eddsa_key]}"
133
+ elsif use_exported_private_key && File.exist?(private_key_path)
134
+ private_key = File.read(private_key_path)
135
+ args << "-s=#{private_key}"
136
+ end
137
+ end
138
+
139
+ # --download-url-prefix <url> A URL that will be used as prefix for the URL from
140
+ # where updates will be downloaded.
141
+ def download_url_prefix(args)
142
+ if cli_options[:download_url_prefix].present?
143
+ args << "--download-url-prefix=#{cli_options[:download_url_prefix]}"
144
+ elsif package_base_url.present?
145
+ args << "--download-url-prefix=#{package_base_url}"
146
+ end
147
+ end
148
+
149
+ # --release-notes-url-prefix <url> A URL that will be used as prefix for constructing
150
+ # URLs for release notes.
151
+ def release_notes_url_prefix(args)
152
+ if cli_options[:release_notes_url_prefix].present?
153
+ args << "--release-notes-url-prefix=#{cli_options[:release_notes_url_prefix]}"
154
+ elsif notes_base_url.present?
155
+ args << "--release-notes-url-prefix=#{notes_base_url}"
156
+ end
157
+ end
158
+
159
+ # --full-release-notes-url <url>
160
+ # A URL that will be used for the full release notes.
161
+ def full_release_notes_url(args)
162
+ return unless cli_options[:full_release_notes_url].present?
163
+
164
+ args << "--full-release-notes-url=#{cli_options[:full_release_notes_url]}"
165
+ end
166
+
167
+ # --link <link> A URL to the application's website which Sparkle may
168
+ # use for directing users to if they cannot download a
169
+ # new update from within the application. This will be
170
+ # used for new generated update items. By default, no
171
+ # product link is used.
172
+ def link(args)
173
+ return unless cli_options[:link].present?
125
174
 
175
+ args << "--link=#{cli_options[:link]}"
176
+ end
177
+
178
+ # --versions <versions> An optional comma delimited list of application
179
+ # versions (specified by CFBundleVersion) to generate
180
+ # new update items for. By default, new update items
181
+ # are inferred from the available archives and are only
182
+ # generated if they are in the latest 5 updates in the
183
+ # appcast.
184
+ def versions(args)
185
+ return unless cli_options[:versions].present?
186
+
187
+ args << "--versions=#{cli_options[:versions]}"
188
+ end
189
+
190
+ # --maximum-deltas <maximum-deltas>
191
+ # The maximum number of delta items to create for the
192
+ # latest update for each minimum required operating
193
+ # system. (default: 5)
194
+ def maximum_deltas(args)
195
+ return unless cli_options[:maximum_deltas].present?
196
+
197
+ args << "--maximum-deltas=#{cli_options[:maximum_deltas]}"
198
+ end
199
+
200
+ # --delta-compression <delta-compression>
201
+ # The compression method to use for generating delta
202
+ # updates. Supported methods for version 3 delta files
203
+ # are 'lzma', 'bzip2', 'zlib', 'lzfse', 'lz4', 'none',
204
+ # and 'default'. Note that version 2 delta files only
205
+ # support 'bzip2', 'none', and 'default' so other
206
+ # methods will be ignored if version 2 files are being
207
+ # generated. The 'default' compression for version 3
208
+ # delta files is currently lzma. (default: default)
209
+ def delta_compression(args)
210
+ return unless cli_options[:delta_compression].present?
211
+
212
+ args << "--delta-compression=#{cli_options[:delta_compression]}"
213
+ end
214
+
215
+ # --delta-compression-level <delta-compression-level>
216
+ # The compression level to use for generating delta
217
+ # updates. This only applies if the compression method
218
+ # used is bzip2 which accepts values from 1 - 9. A
219
+ # special value of 0 will use the default compression
220
+ # level. (default: 0)
221
+ def delta_compression_level(args)
222
+ return unless cli_options[:delta_compression_level].present?
223
+
224
+ args << "--delta-compression-level=#{cli_options[:delta_compression_level]}"
225
+ end
226
+
227
+ # --channel <channel-name>
228
+ # The Sparkle channel name that will be used for
229
+ # generating new updates. By default, no channel is
230
+ # used. Old applications need to be using Sparkle 2 to
231
+ # use this feature.
232
+ def channel(args)
233
+ return unless cli_options[:channel].present?
234
+
235
+ args << "--channel=#{cli_options[:channel]}"
236
+ end
237
+
238
+ # --major-version <major-version>
239
+ # The last major or minimum autoupdate sparkle:version
240
+ # that will be used for generating new updates. By
241
+ # default, no last major version is used.
242
+ def major_version(args)
243
+ return unless cli_options[:major_version].present?
244
+
245
+ args << "--major-version=#{cli_options[:major_version]}"
246
+ end
247
+
248
+ # --ignore-skipped-upgrades-below-version <below-version>
249
+ # Ignore skipped major upgrades below this specified
250
+ # version. Only applicable for major upgrades.
251
+ def ignore_skipped_upgrades_below_version(args)
252
+ return unless cli_options[:ignore_skipped_upgrades_below_version].present?
253
+
254
+ args << "--ignore-skipped-upgrades-below-version=#{cli_options[:ignore_skipped_upgrades_below_version]}"
255
+ end
256
+
257
+ # --phased-rollout-interval <phased-rollout-interval>
258
+ # The phased rollout interval in seconds that will be
259
+ # used for generating new updates. By default, no
260
+ # phased rollout interval is used.
261
+ def phased_rollout_interval(args)
262
+ return unless cli_options[:phased_rollout_interval].present?
263
+
264
+ args << "--phased-rollout-interval=#{cli_options[:phased_rollout_interval]}"
265
+ end
266
+
267
+ # --critical-update-version <critical-update-version>
268
+ # The last critical update sparkle:version that will be
269
+ # used for generating new updates. An empty string
270
+ # argument will treat this update as critical coming
271
+ # from any application version. By default, no last
272
+ # critical update version is used. Old applications
273
+ # need to be using Sparkle 2 to use this feature.
274
+ def critical_update_version(args)
275
+ return unless cli_options[:critical_update_version].present?
276
+
277
+ args << "--critical-update-version=#{cli_options[:critical_update_version]}"
278
+ end
279
+
280
+ # --informational-update-versions <informational-update-versions>
281
+ # A comma delimited list of application
282
+ # sparkle:version's that will see newly generated
283
+ # updates as being informational only. An empty string
284
+ # argument will treat this update as informational
285
+ # coming from any application version. Prefix a version
286
+ # string with '<' to indicate (eg "<2.5") to indicate
287
+ # older versions than the one specified should treat
288
+ # the update as informational only. By default, updates
289
+ # are not informational only. --link must also be
290
+ # provided. Old applications need to be using Sparkle 2
291
+ # to use this feature, and 2.1 or later to use the '<'
292
+ # upper bound feature.
293
+ def informational_update_versions(args)
294
+ return unless cli_options[:informational_update_versions].present?
295
+
296
+ args << "--informational-update-versions=#{cli_options[:informational_update_versions]}"
297
+ end
298
+
299
+ # -o <output-path> Path to filename for the generated appcast (allowed
300
+ # when only one will be created).
301
+ def output_path(args)
302
+ return unless cli_options[:output_path].present?
303
+
304
+ args << "-o=#{cli_options[:output_path]}"
305
+ end
306
+ end
307
+ end
126
308
  end
127
309
  end
310
+ # rubocop:enable Metrics/ClassLength
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ # https://makandracards.com/makandra/6087-ruby-indent-a-string
4
+ String.class_eval do
5
+ def indent(count, char = ' ', skip_first_line: false)
6
+ gsub(/([^\n]*)(\n|$)/) do |_match|
7
+ last_iteration = (Regexp.last_match(1) == '' && Regexp.last_match(2) == '')
8
+ line = String.new
9
+ line << (char * count) unless last_iteration || skip_first_line
10
+ line << Regexp.last_match(1)
11
+ line << Regexp.last_match(2)
12
+
13
+ skip_first_line = false
14
+
15
+ line
16
+ end
17
+ end
18
+ end
@@ -1,75 +1,31 @@
1
- module Motion::Project
2
- class Sparkle
1
+ # frozen_string_literal: true
3
2
 
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
3
+ module Motion
4
+ module Project
5
+ class Sparkle
6
+ def vendored_sparkle_path
7
+ vendor_path.join('Pods/Sparkle')
8
+ end
46
9
 
47
- def install
48
- FileUtils.rm_rf(sparkle_path) if File.directory?(sparkle_path) # force clean install
49
- copy_zipball
50
- unzip
51
- end
10
+ def vendored_sparkle_framework_path
11
+ vendored_sparkle_path.join('Sparkle.framework')
12
+ end
52
13
 
53
- def embed
54
- @config.embedded_frameworks << sparkle_framework_path
55
- end
14
+ def vendored_sparkle_xpc_path
15
+ vendored_sparkle_path.join('XPCServices')
16
+ end
56
17
 
57
- def install_and_embed
58
- install unless installed?
59
- embed
60
- end
18
+ def installed?
19
+ File.directory?(vendored_sparkle_framework_path)
20
+ end
61
21
 
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
- "
22
+ def verify_installation
23
+ if installed?
24
+ App.info 'Sparkle', "Framework installed in #{vendored_sparkle_framework_path}"
25
+ else
26
+ App.fail "Sparkle Cocoapod not correctly installed to #{vendored_sparkle_path}. Run `rake pod:install`."
27
+ end
71
28
  end
72
29
  end
73
-
74
30
  end
75
31
  end
@@ -1,44 +1,60 @@
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
1
+ # frozen_string_literal: true
18
2
 
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`)"
3
+ module Motion
4
+ module Project
5
+ class Sparkle
6
+ def package
7
+ return unless setup_ok?
8
+
9
+ create_release_folder
10
+ @config.build_mode = :release
11
+ return unless create_zip_file
12
+
13
+ App.info 'Release', version_string
14
+ App.info 'Version', @config.short_version
15
+ App.info 'Build', @config.version || 'unspecified in Rakefile'
16
+ App.info 'Size', @package_size.to_s
17
+
18
+ sign_package
19
+ create_release_notes
20
+
21
+ `open #{sparkle_release_path}`
25
22
  end
26
- FileUtils.cd(app_release_path) do
27
- `zip -r --symlinks "#{zip_file}" "#{app_file}"`
23
+
24
+ def create_zip_file
25
+ App.fail 'You need to build your app with the Release target to use Sparkle' unless File.exist?(app_bundle_path)
26
+
27
+ App.info 'Create', "./#{sparkle_release_path}/#{zip_file}"
28
+
29
+ if File.exist?("#{sparkle_release_path}/#{zip_file}")
30
+ App.fail "Release already exists at ./#{sparkle_release_path}/#{zip_file} (remove it manually with `rake sparkle:clean`)"
31
+ end
32
+
33
+ FileUtils.cd(app_release_path) do
34
+ `zip -r --symlinks "#{zip_file}" "#{app_file}"`
35
+ end
36
+
37
+ FileUtils.mv "#{app_release_path}/#{zip_file}", "./#{sparkle_release_path}/"
38
+
39
+ @package_file = zip_file
40
+ @package_size = File.size "./#{sparkle_release_path}/#{zip_file}"
28
41
  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
42
 
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
43
+ def sign_package
44
+ package = "./#{sparkle_release_path}/#{zip_file}"
45
+ sign_update_app = "#{vendored_sparkle_path}/bin/sign_update"
46
+ args = []
47
+
48
+ if appcast.use_exported_private_key && File.exist?(private_key_path)
49
+ # -s <private-key> The private EdDSA (ed25519) key
50
+ private_key = File.read(private_key_path)
51
+ args << "-s=#{private_key}"
52
+ end
41
53
 
54
+ results, _status = Open3.capture2e(sign_update_app, *args, package)
42
55
 
56
+ App.info 'Signature', results
57
+ end
58
+ end
43
59
  end
44
60
  end