motion-sparkle-sandbox 2.0.1 → 2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -15,29 +15,22 @@ module Motion
15
15
  # verify_installation
16
16
  end
17
17
 
18
+ def after_initialize
19
+ self.feed_url = appcast.feed_url
20
+ end
21
+
18
22
  def appcast
19
- @appcast ||= Appcast.new
23
+ @appcast ||= Appcast.new(self)
20
24
  end
21
25
 
22
26
  def publish(key, value)
27
+ return if appcast.process_option(key, value)
28
+
23
29
  case key
24
30
  when :public_key
25
- self.public_EdDSA_key = value
26
- when :base_url
27
- appcast.base_url = value
28
- self.feed_url = appcast.feed_url
29
- when :feed_base_url
30
- appcast.feed_base_url = value
31
- self.feed_url = appcast.feed_url
32
- when :feed_filename
33
- appcast.feed_filename = value
34
- self.feed_url = appcast.feed_url
31
+ self.public_ed_dsa_key = value
35
32
  when :version
36
- version value
37
- when :package_base_url, :package_filename, :notes_base_url, :notes_filename, :use_exported_private_key
38
- appcast.send "#{key}=", value
39
- when :archive_folder
40
- appcast.archive_folder = value
33
+ version(value)
41
34
  else
42
35
  raise "Unknown Sparkle config option #{key}"
43
36
  end
@@ -61,15 +54,13 @@ module Motion
61
54
  @config.info_plist['SUFeedURL'] = url
62
55
  end
63
56
 
64
- # rubocop:disable Naming/MethodName
65
- def public_EdDSA_key
57
+ def public_ed_dsa_key
66
58
  @config.info_plist['SUPublicEDKey']
67
59
  end
68
60
 
69
- def public_EdDSA_key=(key)
61
+ def public_ed_dsa_key=(key)
70
62
  @config.info_plist['SUPublicEDKey'] = key
71
63
  end
72
- # rubocop:enable Naming/MethodName
73
64
 
74
65
  # File manipulation and certificates
75
66
 
@@ -109,7 +100,7 @@ module Motion
109
100
 
110
101
  if appcast.use_exported_private_key && File.exist?(private_key_path)
111
102
  App.info 'Sparkle', "Private key already exported at `#{private_key_path}` and will be used."
112
- if public_EdDSA_key.present?
103
+ if public_ed_dsa_key.present?
113
104
  App.info '', <<~EXISTS
114
105
  SUPublicEDKey already set
115
106
 
@@ -139,12 +130,12 @@ module Motion
139
130
  return
140
131
  end
141
132
 
142
- results, status = Open3.capture2e(generate_keys_app, '-p')
133
+ results, status = Open3.capture2e(generate_keys_app, '-p', '--account', appcast.cli_options[:account])
143
134
 
144
135
  if status.success?
145
- App.info 'Sparkle', 'Public/private keys found in the keychain'
136
+ App.info 'Sparkle', "Public/private keys found in the keychain for account #{appcast.cli_options[:account]}"
146
137
 
147
- if results.strip == public_EdDSA_key
138
+ if results.strip == public_ed_dsa_key
148
139
  App.info 'Sparkle', 'Keychain public key matches `SUPublicEDKey`'
149
140
 
150
141
  if appcast.use_exported_private_key && !File.exist?(private_key_path)
@@ -155,7 +146,7 @@ module Motion
155
146
  Keychain public key DOES NOT match `SUPublicEDKey`
156
147
 
157
148
  Keychain public key: #{results.strip}
158
- SUPublicEDKey public key: #{public_EdDSA_key}
149
+ SUPublicEDKey public key: #{public_ed_dsa_key}
159
150
 
160
151
  NOT_MATCHED
161
152
  .indent(11, skip_first_line: true)
@@ -173,7 +164,7 @@ module Motion
173
164
  def create_private_key
174
165
  App.info 'Sparkle',
175
166
  'Generating a new signing key into the Keychain. This may take a moment, depending on your machine.'
176
- results, status = Open3.capture2e(generate_keys_app)
167
+ results, status = Open3.capture2e(generate_keys_app, '--account', appcast.cli_options[:account])
177
168
 
178
169
  App.fail 'Sparkle could not generate keys' unless status.success?
179
170
 
@@ -181,7 +172,7 @@ module Motion
181
172
  puts results.lines[1..].join.indent(11)
182
173
 
183
174
  # Extract the public key so we can use it in message
184
- results, status = Open3.capture2e(generate_keys_app, '-p')
175
+ results, status = Open3.capture2e(generate_keys_app, '-p', '--account', appcast.cli_options[:account])
185
176
 
186
177
  App.fail 'Unable to read public key' unless status.success?
187
178
 
@@ -199,7 +190,7 @@ module Motion
199
190
 
200
191
  # Export the private key from the keychain
201
192
  def export_private_key
202
- _results, status = Open3.capture2e(generate_keys_app, '-x', private_key_path.to_s)
193
+ _results, status = Open3.capture2e(generate_keys_app, '-x', private_key_path.to_s, '--account', appcast.cli_options[:account])
203
194
 
204
195
  App.fail 'Unable to export private key' unless status.success?
205
196
 
@@ -216,6 +207,92 @@ module Motion
216
207
  .indent(11)
217
208
  end
218
209
 
210
+ # copy the release notes and zip archive into the releases_folder,
211
+ # where the appcast will get built
212
+ def copy_to_release
213
+ destination_path = (project_path + releases_folder).realpath
214
+ zip_file_path = (sparkle_release_path + zip_file)
215
+
216
+ [release_notes_path, zip_file_path].each do |file|
217
+ FileUtils.cp(file, "#{destination_path}/")
218
+ App.info 'Copied', "#{destination_path}/#{file}"
219
+ end
220
+ end
221
+
222
+ # Generate the appcast.
223
+ # Note: We do not support the old DSA keys, only the newer EdDSA keys.
224
+ # See https://sparkle-project.org/documentation/eddsa-migration
225
+ def generate_appcast
226
+ generate_appcast_app = "#{vendored_sparkle_path}/bin/generate_appcast"
227
+ path = (project_path + releases_folder).realpath
228
+ appcast_filename = (path + appcast.feed_filename)
229
+ appcast.cli_options[:output_path] = appcast_filename
230
+
231
+ FileUtils.mkdir_p(path) unless File.exist?(path)
232
+
233
+ App.info('Sparkle', "Generating appcast using `#{generate_appcast_app}`")
234
+ puts "from files in `#{path}`...".indent(11)
235
+
236
+ args = appcast.prepare_args
237
+
238
+ App.info 'Executing', [generate_appcast_app, *args, path.to_s].join(' ')
239
+
240
+ results, status = Open3.capture2e(generate_appcast_app, *args, path.to_s)
241
+
242
+ App.info('Sparkle', "Saved appcast to `#{appcast_filename}`") if status.success?
243
+ puts results.indent(11)
244
+
245
+ return unless status.success?
246
+
247
+ puts
248
+ puts "SUFeedURL : #{feed_url}".indent(11)
249
+ puts "SUPublicEDKey : #{public_ed_dsa_key}".indent(11)
250
+ end
251
+
252
+ def generate_appcast_help
253
+ generate_appcast_app = "#{vendored_sparkle_path}/bin/generate_appcast"
254
+ results, _status = Open3.capture2e(generate_appcast_app, '--help')
255
+
256
+ puts results
257
+ end
258
+
259
+ def create_release_notes
260
+ App.fail "Release notes template not found as expected at ./#{release_notes_template_path}" unless File.exist?(release_notes_template_path)
261
+
262
+ create_release_folder
263
+
264
+ File.open(release_notes_path.to_s, 'w') do |f|
265
+ template = File.read(release_notes_template_path)
266
+ f << ERB.new(template).result(binding)
267
+ end
268
+
269
+ App.info 'Create', "./#{release_notes_path}"
270
+ end
271
+
272
+ def release_notes_template_path
273
+ sparkle_config_path.join('release_notes.template.erb')
274
+ end
275
+
276
+ def release_notes_content_path
277
+ sparkle_config_path.join('release_notes.content.html')
278
+ end
279
+
280
+ def release_notes_path
281
+ sparkle_release_path + (appcast.notes_filename || "#{app_name}.#{@config.short_version}.html")
282
+ end
283
+
284
+ def release_notes_content
285
+ if File.exist?(release_notes_content_path)
286
+ File.read(release_notes_content_path)
287
+ else
288
+ App.fail "Missing #{release_notes_content_path}"
289
+ end
290
+ end
291
+
292
+ def release_notes_html
293
+ release_notes_content
294
+ end
295
+
219
296
  # A few helpers
220
297
 
221
298
  def project_path
@@ -242,10 +319,6 @@ module Motion
242
319
  sparkle_config_path.join(EDDSA_PRIV_KEY)
243
320
  end
244
321
 
245
- def legacy_private_key_path
246
- sparkle_config_path.join(DSA_PRIV_KEY)
247
- end
248
-
249
322
  def app_bundle_path
250
323
  Pathname.new(@config.app_bundle_raw('MacOSX'))
251
324
  end
@@ -262,8 +335,8 @@ module Motion
262
335
  appcast.package_filename || "#{app_name}.#{@config.short_version}.zip"
263
336
  end
264
337
 
265
- def archive_folder
266
- appcast.archive_folder
338
+ def releases_folder
339
+ appcast.releases_folder
267
340
  end
268
341
 
269
342
  def app_file
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MotionSparkleSandbox
4
- VERSION = '2.0.1'
4
+ VERSION = '2.2'
5
5
  end
@@ -16,7 +16,7 @@ require 'motion/project/indent_string'
16
16
 
17
17
  lib_dir_path = File.dirname(File.expand_path(__FILE__))
18
18
 
19
- POD_VERSION = '~> 2.0.0'
19
+ POD_VERSION = '~> 2.2.0'
20
20
 
21
21
  unless @running_specs
22
22
  Motion::Project::App.setup do |app|
@@ -7,3 +7,4 @@ vendor
7
7
 
8
8
  sparkle/release
9
9
  sparkle/config
10
+ sparkle/config/eddsa_priv.key
data/sample-app/Rakefile CHANGED
@@ -25,8 +25,8 @@ Motion::Project::App.setup do |app|
25
25
 
26
26
  app.sparkle do
27
27
  publish :base_url, 'http://example.com/your_app_folder/releases/'
28
- publish :public_key, '06Iq4HGJFToJrA8lqWWG0SCqqtGXLuEN1Wgy/CVJBnI='
29
- publish :archive_folder, '../published/releases/v2.2/'
28
+ publish :public_key, '1/eJupmw4JH+iFQIlh99nS2qvwYealJsNzFN3LL6FFE=x'
29
+ publish :releases_folder, 'tmp/releases/v2.2/'
30
30
 
31
31
  # Appcast Feed
32
32
  # publish :feed_base_url, 'http://downloads.example.com/releases' # defaults to base_url
@@ -40,5 +40,8 @@ Motion::Project::App.setup do |app|
40
40
  # publish :package_base_url, 'http://downloads.example.com/releases/' # defaults to base_url
41
41
 
42
42
  # publish :use_exported_private_key, true
43
+
44
+ publish :account, 'motion-sparkle-sandbox.sample-app'
45
+ publish :full_release_notes_url, 'http://example.com/full_release_notes_changed_again.html'
43
46
  end
44
47
  end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require File.expand_path('spec_utils', __dir__)
4
+
5
+ describe 'Appcast' do
6
+ describe 'arguments' do
7
+ before do
8
+ @appcast = Motion::Project::Sparkle::Appcast.new(nil)
9
+ end
10
+
11
+ it 'defaults to empty' do
12
+ args = @appcast.prepare_args
13
+
14
+ expect(args).to eq ['--account=ed25519']
15
+ end
16
+
17
+ it 'sets all command line options' do
18
+ expected_results = [
19
+ '--account=test1',
20
+ '--ed-key-file=test2',
21
+ '--download-url-prefix=test3',
22
+ '--release-notes-url-prefix=test4',
23
+ '--full-release-notes-url=test5',
24
+ '--link=test6',
25
+ '--versions=test7',
26
+ '--maximum-deltas=test8',
27
+ '--delta-compression=test9',
28
+ '--delta-compression-level=test10',
29
+ '--channel=test11',
30
+ '--major-version=test12',
31
+ '--ignore-skipped-upgrades-below-version=test13',
32
+ '--phased-rollout-interval=test14',
33
+ '--critical-update-version=test15',
34
+ '--informational-update-versions=test16',
35
+ '-o=test17'
36
+ ]
37
+
38
+ @appcast.process_option(:account, 'test1')
39
+ @appcast.process_option(:private_ed_key_file, 'test2')
40
+ @appcast.process_option(:download_url_prefix, 'test3')
41
+ @appcast.process_option(:release_notes_url_prefix, 'test4')
42
+ @appcast.process_option(:full_release_notes_url, 'test5')
43
+ @appcast.process_option(:link, 'test6')
44
+ @appcast.process_option(:versions, 'test7')
45
+ @appcast.process_option(:maximum_deltas, 'test8')
46
+ @appcast.process_option(:delta_compression, 'test9')
47
+ @appcast.process_option(:delta_compression_level, 'test10')
48
+ @appcast.process_option(:channel, 'test11')
49
+ @appcast.process_option(:major_version, 'test12')
50
+ @appcast.process_option(:ignore_skipped_upgrades_below_version, 'test13')
51
+ @appcast.process_option(:phased_rollout_interval, 'test14')
52
+ @appcast.process_option(:critical_update_version, 'test15')
53
+ @appcast.process_option(:informational_update_versions, 'test16')
54
+ @appcast.process_option(:output_path, 'test17')
55
+ args = @appcast.prepare_args
56
+
57
+ expect(args).to match_array expected_results
58
+ end
59
+ end
60
+ end
data/spec/setup_spec.rb CHANGED
@@ -2,66 +2,41 @@
2
2
 
3
3
  require File.expand_path('spec_utils', __dir__)
4
4
 
5
- module Motion
6
- module Project
7
- class Config
8
- attr_writer :project_dir
9
- end
10
- end
11
- end
12
-
13
5
  describe 'Sparkle setup' do
14
6
  before(:all) do
15
- SpecUtils::TemporaryDirectory.teardown
16
- SpecUtils::TemporaryDirectory.setup
17
-
18
- FileUtils.mkdir_p("#{SpecUtils::TemporaryDirectory.directory}resources")
19
- FileUtils.mkdir_p("#{SpecUtils::TemporaryDirectory.directory}vendor")
20
- FileUtils.touch("#{SpecUtils::TemporaryDirectory.directory}.gitignore")
21
- end
22
-
23
- context 'something' do
24
- before do
25
- @config = App.config
26
- @config.project_dir = SpecUtils::TemporaryDirectory.directory.to_s
27
- @config.instance_eval do
28
- pods do
29
- pod 'Sparkle', POD_VERSION
30
- end
31
-
32
- sparkle do
33
- release :base_url, 'http://example.com/'
34
- # release :public_key, 'public_key.pem'
35
- publish :public_key, '<YOUR-EDDSA-PUBLIC-KEY>'
36
- release :version, '1.0'
37
-
38
- # Optional config options
39
- release :feed_base_url, 'http://rss.example.com/'
40
- release :feed_filename, 'example.xml'
41
- release :notes_base_url, 'http://www.example.com/'
42
- release :notes_filename, 'example.html'
43
- release :package_base_url, 'http://download.example.com/'
44
- release :package_filename, 'example.zip'
45
- # publish :use_exported_private_key, true
46
- end
7
+ @config = App.config
8
+ @config.project_dir = SpecUtils::TemporaryDirectory.directory.to_s
9
+ @config.instance_eval do
10
+ sparkle do
11
+ release :base_url, 'http://example.com/'
12
+ # release :public_key, 'public_key.pem'
13
+ publish :public_key, '<YOUR-EDDSA-PUBLIC-KEY>'
14
+ release :version, '1.0'
15
+
16
+ # Optional config options
17
+ release :feed_base_url, 'http://rss.example.com/'
18
+ release :feed_filename, 'example.xml'
19
+ release :notes_base_url, 'http://www.example.com/'
20
+ release :notes_filename, 'example.html'
21
+ release :package_base_url, 'http://download.example.com/'
22
+ release :package_filename, 'example.zip'
23
+ # publish :use_exported_private_key, true
47
24
  end
48
-
49
- Rake::Task['pod:install'].invoke
50
- Rake::Task['sparkle:setup'].invoke
51
- # Rake::Task['sparkle:setup_certificates'].invoke
52
- end
53
-
54
- it 'should create private certificate' do
55
- expect(File.exist?(@config.sparkle.private_key_path.to_s)).to be_truthy
56
25
  end
57
26
 
58
- it 'should create public certificate' do
59
- expect(File.exist?(@config.sparkle.public_key_path.to_s)).to be_truthy
60
- end
27
+ Rake::Task['sparkle:setup'].invoke
28
+ end
61
29
 
62
- it 'should add files to gitignore' do
63
- a = `cat .gitignore`
64
- expect(a.strip).not_to eq ''
65
- end
30
+ # it 'should create private certificate' do
31
+ # expect(File.exist?(@config.sparkle.private_key_path.to_s)).to be_truthy
32
+ # end
33
+ #
34
+ # it 'should create public certificate' do
35
+ # expect(File.exist?(@config.sparkle.public_key_path.to_s)).to be_truthy
36
+ # end
37
+
38
+ it 'should add files to gitignore' do
39
+ a = `cat .gitignore`
40
+ expect(a.strip).not_to eq ''
66
41
  end
67
42
  end
data/spec/sparkle_spec.rb CHANGED
@@ -2,37 +2,20 @@
2
2
 
3
3
  require File.expand_path('spec_utils', __dir__)
4
4
 
5
- module Motion
6
- module Project
7
- class Config
8
- attr_writer :project_dir
9
- end
10
- end
11
- end
12
-
13
5
  # rubocop:disable Metrics/BlockLength
14
6
  describe 'motion-sparkle-sandbox' do
15
7
  before(:all) do
16
- SpecUtils::TemporaryDirectory.teardown
17
- SpecUtils::TemporaryDirectory.setup
18
-
19
- FileUtils.mkdir_p("#{SpecUtils::TemporaryDirectory.directory}resources")
20
- FileUtils.mkdir_p("#{SpecUtils::TemporaryDirectory.directory}vendor")
21
- FileUtils.touch("#{SpecUtils::TemporaryDirectory.directory}.gitignore")
22
- end
23
-
24
- context 'configuration' do
25
- before do
26
- @config = App.config
27
- @config.sparkle = nil
28
- @config.project_dir = SpecUtils::TemporaryDirectory.directory.to_s
29
- @config.instance_eval do
30
- sparkle do
31
- release :base_url, 'http://example.com/'
32
- end
8
+ @config = App.config
9
+ @config.sparkle = nil
10
+ @config.project_dir = SpecUtils::TemporaryDirectory.directory.to_s
11
+ @config.instance_eval do
12
+ sparkle do
13
+ release :base_url, 'http://example.com/'
33
14
  end
34
15
  end
16
+ end
35
17
 
18
+ context 'configuration' do
36
19
  describe 'base url' do
37
20
  it 'base url should be set correctly' do
38
21
  expect(@config.sparkle.appcast.base_url).to eq 'http://example.com/'
@@ -46,6 +29,7 @@ describe 'motion-sparkle-sandbox' do
46
29
 
47
30
  it 'uses feed_base_url' do
48
31
  @config.sparkle.publish(:feed_base_url, 'http://rss.example.com/')
32
+ @config.sparkle.after_initialize
49
33
 
50
34
  expect(@config.info_plist['SUFeedURL']).to eq 'http://rss.example.com/releases.xml'
51
35
  end
@@ -53,6 +37,7 @@ describe 'motion-sparkle-sandbox' do
53
37
  it 'uses feed_filename' do
54
38
  @config.sparkle.publish(:feed_base_url, 'http://rss.example.com/')
55
39
  @config.sparkle.publish(:feed_filename, 'example.xml')
40
+ @config.sparkle.after_initialize
56
41
 
57
42
  expect(@config.info_plist['SUFeedURL']).to eq 'http://rss.example.com/example.xml'
58
43
  end
@@ -115,22 +100,10 @@ describe 'motion-sparkle-sandbox' do
115
100
  end
116
101
 
117
102
  context 'cocoapod' do
118
- before do
119
- @config = App.config
120
- @config.project_dir = SpecUtils::TemporaryDirectory.directory.to_s
121
- @config.instance_eval do
122
- pods do
123
- pod 'Sparkle', POD_VERSION
124
- end
125
- end
126
-
127
- Rake::Task['pod:install'].invoke
128
- end
129
-
130
103
  it 'Sparkle framework pod should be embedded' do
131
104
  sparkle_framework_path = 'vendor/Pods/Sparkle/Sparkle.framework'
132
- @config.pods.pods_libraries
133
105
 
106
+ @config.pods.pods_libraries
134
107
  expect(@config.embedded_frameworks.first.end_with?(sparkle_framework_path)).to be_truthy
135
108
  end
136
109
  end
data/spec/spec_helper.rb CHANGED
@@ -97,4 +97,12 @@ RSpec.configure do |config|
97
97
  # # test failures related to randomization by passing the same `--seed` value
98
98
  # # as the one that triggered the failure.
99
99
  # Kernel.srand config.seed
100
+
101
+ config.before(:suite) do
102
+ SpecUtils::SparkleSetup.initial_install
103
+ end
104
+
105
+ config.after(:suite) do
106
+ SpecUtils::SparkleSetup.final_deinstall
107
+ end
100
108
  end
data/spec/spec_utils.rb CHANGED
@@ -13,7 +13,43 @@ $:.unshift("#{ROOT}lib".to_s)
13
13
  require 'motion/project/template/osx'
14
14
  require 'motion-sparkle-sandbox'
15
15
 
16
+ # necessary for us to be able to overwrite the `project_dir`
17
+ module Motion
18
+ module Project
19
+ class Config
20
+ attr_writer :project_dir
21
+ end
22
+ end
23
+ end
24
+
16
25
  module SpecUtils
26
+ module SparkleSetup
27
+ # run from a before(:suite)
28
+ def self.initial_install
29
+ SpecUtils::TemporaryDirectory.setup
30
+
31
+ FileUtils.mkdir_p("#{SpecUtils::TemporaryDirectory.directory}/resources")
32
+ FileUtils.mkdir_p("#{SpecUtils::TemporaryDirectory.directory}/vendor")
33
+ FileUtils.touch("#{SpecUtils::TemporaryDirectory.directory}/.gitignore")
34
+
35
+ @config = App.config
36
+ @config.sparkle = nil
37
+ @config.project_dir = SpecUtils::TemporaryDirectory.directory.to_s
38
+ @config.instance_eval do
39
+ pods do
40
+ pod 'Sparkle', POD_VERSION
41
+ end
42
+ end
43
+
44
+ Rake::Task['pod:install'].invoke
45
+ end
46
+
47
+ # run from an after(:suite)
48
+ def self.final_deinstall
49
+ SpecUtils::TemporaryDirectory.teardown
50
+ end
51
+ end
52
+
17
53
  module TemporaryDirectory
18
54
  TEMPORARY_DIRECTORY = ROOT + 'tmp' # rubocop:disable Style/StringConcatenation
19
55
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: motion-sparkle-sandbox
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.1
4
+ version: '2.2'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brett Walker
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2022-05-27 00:00:00.000000000 Z
12
+ date: 2022-07-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: motion-cocoapods
@@ -95,6 +95,7 @@ files:
95
95
  - sample-app/resources/Assets.xcassets/Contents.json
96
96
  - sample-app/resources/Credits.rtf
97
97
  - sample-app/spec/main_spec.rb
98
+ - spec/appcast_spec.rb
98
99
  - spec/setup_spec.rb
99
100
  - spec/sparkle_spec.rb
100
101
  - spec/spec_helper.rb
@@ -124,6 +125,7 @@ signing_key:
124
125
  specification_version: 4
125
126
  summary: Sparkle (sandboxed) integration for Rubymotion projects
126
127
  test_files:
128
+ - spec/appcast_spec.rb
127
129
  - spec/spec_helper.rb
128
130
  - spec/sparkle_spec.rb
129
131
  - spec/setup_spec.rb