motion-sparkle-sandbox 0.8.0 → 2.1.0

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/LICENSE +1 -0
  10. data/README.md +72 -72
  11. data/Rakefile +2 -7
  12. data/bin/bundle +105 -0
  13. data/bin/byebug +29 -0
  14. data/bin/coderay +29 -0
  15. data/bin/fuzzy_match +29 -0
  16. data/bin/htmldiff +29 -0
  17. data/bin/httpclient +29 -0
  18. data/bin/ldiff +29 -0
  19. data/bin/pod +29 -0
  20. data/bin/pry +29 -0
  21. data/bin/rake +29 -0
  22. data/bin/rspec +29 -0
  23. data/bin/rubocop +29 -0
  24. data/bin/ruby-parse +29 -0
  25. data/bin/ruby-rewrite +29 -0
  26. data/bin/sandbox-pod +29 -0
  27. data/bin/xcodeproj +29 -0
  28. data/lib/motion/project/appcast.rb +177 -104
  29. data/lib/motion/project/indent_string.rb +18 -0
  30. data/lib/motion/project/install.rb +22 -66
  31. data/lib/motion/project/package.rb +52 -36
  32. data/lib/motion/project/project.rb +41 -51
  33. data/lib/motion/project/rake_tasks.rb +28 -32
  34. data/lib/motion/project/setup.rb +67 -73
  35. data/lib/motion/project/sparkle.rb +244 -148
  36. data/lib/motion/project/templates.rb +24 -22
  37. data/lib/motion-sparkle-sandbox/version.rb +5 -0
  38. data/lib/motion-sparkle-sandbox.rb +20 -3
  39. data/motion-sparkle-sandbox.gemspec +26 -16
  40. data/sample-app/.gitignore +9 -0
  41. data/sample-app/Gemfile +8 -0
  42. data/sample-app/README.md +87 -0
  43. data/sample-app/Rakefile +44 -0
  44. data/sample-app/app/app_delegate.rb +15 -0
  45. data/sample-app/app/menu.rb +155 -0
  46. data/sample-app/resources/Assets.xcassets/AppIcon.appiconset/1024x1024.png +0 -0
  47. data/sample-app/resources/Assets.xcassets/AppIcon.appiconset/Contents.json +68 -0
  48. data/sample-app/resources/Assets.xcassets/AppIcon.appiconset/Icon_128x128.png +0 -0
  49. data/sample-app/resources/Assets.xcassets/AppIcon.appiconset/Icon_128x128@2x.png +0 -0
  50. data/sample-app/resources/Assets.xcassets/AppIcon.appiconset/Icon_16x16.png +0 -0
  51. data/sample-app/resources/Assets.xcassets/AppIcon.appiconset/Icon_16x16@2x.png +0 -0
  52. data/sample-app/resources/Assets.xcassets/AppIcon.appiconset/Icon_256x256.png +0 -0
  53. data/sample-app/resources/Assets.xcassets/AppIcon.appiconset/Icon_256x256@2x.png +0 -0
  54. data/sample-app/resources/Assets.xcassets/AppIcon.appiconset/Icon_32x32.png +0 -0
  55. data/sample-app/resources/Assets.xcassets/AppIcon.appiconset/Icon_32x32@2x.png +0 -0
  56. data/sample-app/resources/Assets.xcassets/AppIcon.appiconset/Icon_512x512.png +0 -0
  57. data/sample-app/resources/Assets.xcassets/AppIcon.appiconset/Icon_512x512@2x.png +0 -0
  58. data/sample-app/resources/Assets.xcassets/Contents.json +6 -0
  59. data/sample-app/resources/Credits.rtf +29 -0
  60. data/sample-app/spec/main_spec.rb +11 -0
  61. data/spec/setup_spec.rb +42 -0
  62. data/spec/sparkle_spec.rb +92 -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 +78 -15
  67. data/vendor/README.md +0 -24
  68. data/vendor/Sparkle.zip +0 -0
  69. data/vendor/codesign_xpc +0 -46
  70. data/vendor/generate_appcast +0 -0
@@ -1,127 +1,200 @@
1
- module Motion::Project
2
- class Sparkle
1
+ # frozen_string_literal: true
2
+
3
+ module Motion
4
+ module Project
5
+ class Sparkle
6
+ # Generate the appcast.
7
+ # Note: We do not support the old DSA keys, only the newer EdDSA keys.
8
+ # See https://sparkle-project.org/documentation/eddsa-migration
9
+ # rubocop:disable Metrics/CyclomaticComplexity
10
+ def generate_appcast
11
+ generate_appcast_app = "#{vendored_sparkle_path}/bin/generate_appcast"
12
+ path = (project_path + archive_folder).realpath
13
+ appcast_filename = (path + appcast.feed_filename)
14
+
15
+ args = []
16
+
17
+ FileUtils.mkdir_p(path) unless File.exist?(path)
18
+
19
+ App.info('Sparkle', "Generating appcast using `#{generate_appcast_app}`")
20
+ puts "from files in `#{path}`...".indent(11)
21
+
22
+ if appcast.use_exported_private_key && File.exist?(private_key_path)
23
+ # -s <private-EdDSA-key> The private EdDSA string (128 characters). If not
24
+ # specified, the private EdDSA key will be read from
25
+ # the Keychain instead.
26
+ private_key = File.read(private_key_path)
27
+ args << "-s=#{private_key}"
28
+ end
3
29
 
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)
30
+ # --download-url-prefix <url> A URL that will be used as prefix for the URL from
31
+ # where updates will be downloaded.
32
+ args << "--download-url-prefix=#{appcast.package_base_url}" if appcast.package_base_url.present?
33
+
34
+ # --release-notes-url-prefix <url> A URL that will be used as prefix for constructing
35
+ # URLs for release notes.
36
+ args << "--release-notes-url-prefix=#{appcast.notes_base_url}" if appcast.notes_base_url.present?
37
+
38
+ # --link <link> A URL to the application's website which Sparkle may
39
+ # use for directing users to if they cannot download a
40
+ # new update from within the application. This will be
41
+ # used for new generated update items. By default, no
42
+ # product link is used.
43
+
44
+ # --versions <versions> An optional comma delimited list of application
45
+ # versions (specified by CFBundleVersion) to generate
46
+ # new update items for. By default, new update items
47
+ # are inferred from the available archives and are only
48
+ # generated if they are in the latest 5 updates in the
49
+ # appcast.
50
+
51
+ # --maximum-deltas <maximum-deltas>
52
+ # The maximum number of delta items to create for the
53
+ # latest update for each minimum required operating
54
+ # system. (default: 5)
55
+
56
+ # --channel <channel-name>
57
+ # The Sparkle channel name that will be used for
58
+ # generating new updates. By default, no channel is
59
+ # used. Old applications need to be using Sparkle 2 to
60
+ # use this feature.
61
+
62
+ # --major-version <major-version>
63
+ # The last major or minimum autoupdate sparkle:version
64
+ # that will be used for generating new updates. By
65
+ # default, no last major version is used.
66
+
67
+ # --phased-rollout-interval <phased-rollout-interval>
68
+ # The phased rollout interval in seconds that will be
69
+ # used for generating new updates. By default, no
70
+ # phased rollout interval is used.
71
+
72
+ # --critical-update-version <critical-update-version>
73
+ # The last critical update sparkle:version that will be
74
+ # used for generating new updates. An empty string
75
+ # argument will treat this update as critical coming
76
+ # from any application version. By default, no last
77
+ # critical update version is used. Old applications
78
+ # need to be using Sparkle 2 to use this feature.
79
+
80
+ # --informational-update-versions <informational-update-versions>
81
+ # A comma delimited list of application
82
+ # sparkle:version's that will see newly generated
83
+ # updates as being informational only. An empty string
84
+ # argument will treat this update as informational
85
+ # coming from any application version. By default,
86
+ # updates are not informational only. --link must also
87
+ # be provided. Old applications need to be using
88
+ # Sparkle 2 to use this feature.
89
+
90
+ # -o <output-path> Path to filename for the generated appcast (allowed
91
+ # when only one will be created).
92
+
93
+ # -f <private-dsa-key-file> Path to the private DSA key file. Only use this
94
+ # option for transitioning to EdDSA from older updates.
95
+ # Note: only for supporting a legacy app that used DSA keys. Check if the
96
+ # default DSA key exists in `sparkle/config/dsa_priv.pem` and if it does,
97
+ # add it to the command.
98
+ if File.exist?(legacy_private_key_path)
99
+ App.info 'Sparkle', "Also signing with legacy DSA key at #{legacy_private_key_path}"
100
+ args << "-f=#{legacy_private_key_path}"
9
101
  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
15
102
 
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
103
+ args << "-o=#{appcast_filename}" if appcast_filename.present?
32
104
 
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
105
+ App.info 'Executing', [generate_appcast_app, *args, path.to_s].join(' ')
65
106
 
66
- def release_notes_template_path
67
- sparkle_config_path + "release_notes.template.erb"
68
- end
107
+ results, status = Open3.capture2e(generate_appcast_app, *args, path.to_s)
69
108
 
70
- def release_notes_content_path
71
- sparkle_config_path + "release_notes.content.html"
72
- end
109
+ App.info('Sparkle', "Saved appcast to `#{appcast_filename}`") if status.success?
110
+ puts results.indent(11)
73
111
 
74
- def release_notes_path
75
- sparkle_release_path + appcast.notes_filename.to_s
76
- end
112
+ return unless status.success?
77
113
 
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}"
114
+ puts
115
+ puts "SUFeedURL : #{feed_url}".indent(11)
116
+ puts "SUPublicEDKey : #{public_EdDSA_key}".indent(11)
83
117
  end
84
- end
118
+ # rubocop:enable Metrics/CyclomaticComplexity
85
119
 
86
- def release_notes_html
87
- release_notes_content
88
- end
120
+ def generate_appcast_help
121
+ generate_appcast_app = "#{vendored_sparkle_path}/bin/generate_appcast"
122
+ results, _status = Open3.capture2e(generate_appcast_app, '--help')
123
+ puts results
124
+ end
125
+
126
+ def create_release_notes
127
+ App.fail "Release notes template not found as expected at ./#{release_notes_template_path}" unless File.exist?(release_notes_template_path)
128
+
129
+ create_release_folder
89
130
 
131
+ File.open(release_notes_path.to_s, 'w') do |f|
132
+ template = File.read(release_notes_template_path)
133
+ f << ERB.new(template).result(binding)
134
+ end
90
135
 
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
136
+ App.info 'Create', "./#{release_notes_path}"
110
137
  end
111
138
 
112
- def feed_url
113
- "#{feed_base_url || base_url}/#{feed_filename}"
139
+ def release_notes_template_path
140
+ sparkle_config_path.join('release_notes.template.erb')
114
141
  end
115
142
 
116
- def notes_url
117
- "#{notes_base_url || base_url}/#{notes_filename}"
143
+ def release_notes_content_path
144
+ sparkle_config_path.join('release_notes.content.html')
118
145
  end
119
146
 
120
- def package_url
121
- "#{package_base_url || base_url}/#{package_filename}"
147
+ def release_notes_path
148
+ sparkle_release_path + (appcast.notes_filename || "#{app_name}.#{@config.short_version}.html")
122
149
  end
123
150
 
124
- end
151
+ def release_notes_content
152
+ if File.exist?(release_notes_content_path)
153
+ File.read(release_notes_content_path)
154
+ else
155
+ App.fail "Missing #{release_notes_content_path}"
156
+ end
157
+ end
158
+
159
+ def release_notes_html
160
+ release_notes_content
161
+ end
125
162
 
163
+ class Appcast
164
+ attr_accessor :base_url,
165
+ :feed_base_url,
166
+ :feed_filename,
167
+ :notes_filename,
168
+ :package_filename,
169
+ :archive_folder,
170
+ :use_exported_private_key
171
+ attr_writer :notes_base_url,
172
+ :package_base_url
173
+
174
+ def initialize
175
+ @feed_base_url = nil
176
+ @feed_filename = 'releases.xml'
177
+ @notes_base_url = nil
178
+ @notes_filename = nil
179
+ @package_base_url = nil
180
+ @package_filename = nil
181
+ @base_url = nil
182
+ @archive_folder = nil
183
+ @use_exported_private_key = false
184
+ end
185
+
186
+ def feed_url
187
+ "#{feed_base_url || base_url}#{feed_filename}"
188
+ end
189
+
190
+ def notes_base_url
191
+ @notes_base_url || base_url
192
+ end
193
+
194
+ def package_base_url
195
+ @package_base_url || base_url
196
+ end
197
+ end
198
+ end
126
199
  end
127
200
  end
@@ -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
@@ -1,67 +1,57 @@
1
- module Motion::Project
1
+ # frozen_string_literal: true
2
2
 
3
- class Config
4
- variable :sparkle
3
+ require 'open3'
5
4
 
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
5
+ module Motion
6
+ module Project
7
+ class Config
8
+ variable :sparkle
14
9
 
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)
10
+ def sparkle(&block)
11
+ @sparkle ||= Motion::Project::Sparkle.new(self)
12
+ @sparkle.instance_eval(&block) if block
13
+ @sparkle
24
14
  end
25
-
26
- alias_method "build_without_sparkle", "build"
27
- alias_method "build", "build_with_sparkle"
28
15
  end
29
- end
30
16
 
31
- class Builder
32
- # if we're using the sandboxed version of Sparkle, then we need to copy the
33
- # xpc services to the proper folder and sign them. This has to be done
34
- # before we sign the app itself
35
- #------------------------------------------------------------------------------
36
- def codesign_with_sparkle(config, platform)
37
- if App.config.embedded_frameworks.any? {|item| item.to_s.include?('Sparkle.framework')}
38
- bundle_path = App.config.app_bundle('MacOSX')
17
+ class App
18
+ class << self
19
+ def build_with_sparkle(platform, opts = {})
20
+ App.fail 'Sparkle not setup correctly' unless App.config.sparkle.setup_ok?
39
21
 
40
- if File.directory?(App.config.sparkle.sparkle_xpc_path)
41
- App.info 'Sparkle', 'Removing unnecessary executables...'
42
- sparkle_path = File.join(bundle_path, "Frameworks/Sparkle.framework")
43
- ['Resources/Autoupdate', 'Resources/Updater.app'].each do |item|
44
- bundle = File.join(sparkle_path, item)
45
- FileUtils.rm_r(bundle) if File.exist?(bundle)
46
- end
22
+ App.info 'Sparkle', 'Setup OK'
47
23
 
48
- xpc_path = File.join(bundle_path, "XPCServices")
49
- App.info 'Sparkle', "Copying XPCServices to #{xpc_path}"
24
+ build_without_sparkle(platform, opts)
25
+ end
50
26
 
51
- FileUtils.mkdir_p(xpc_path)
52
- `cp -R #{App.config.sparkle.sparkle_xpc_path}/*.xpc "#{xpc_path}"`
27
+ alias_method 'build_without_sparkle', 'build'
28
+ alias_method 'build', 'build_with_sparkle'
29
+ end
30
+ end
53
31
 
54
- Dir.glob("#{xpc_path}/*.xpc").each do |path|
55
- App.info 'Codesign', path
56
- results = `#{App.config.sparkle.sparkle_vendor_path}/codesign_xpc "#{App.config.codesign_certificate}" "#{File.expand_path(path)}" 2>&1`
57
- end
32
+ class Builder
33
+ # The XPC services are already residing in the Sparkle package. But we need
34
+ # to re-sign the entire package to ensure all executables have the hardened
35
+ # runtime and correct certificate.
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
+ sparkle_path = File.join(bundle_path, 'Frameworks', 'Sparkle.framework')
41
+
42
+ `/usr/bin/codesign -f -s "#{config.codesign_certificate}" -o runtime "#{sparkle_path}/Versions/B/Autoupdate"`
43
+ `/usr/bin/codesign -f -s "#{config.codesign_certificate}" -o runtime "#{sparkle_path}/Versions/B/Updater.app"`
44
+ `/usr/bin/codesign -f -s "#{config.codesign_certificate}" -o runtime "#{sparkle_path}/Versions/B/XPCServices/org.sparkle-project.InstallerLauncher.xpc"`
45
+ `/usr/bin/codesign -f -s "#{config.codesign_certificate}" -o runtime --entitlements "./vendor/Pods/Sparkle/Entitlements/org.sparkle-project.Downloader.entitlements" "#{sparkle_path}/Versions/B/XPCServices/org.sparkle-project.Downloader.xpc"` # rubocop:disable Layout/LineLength
46
+
47
+ `/usr/bin/codesign -f -s "#{config.codesign_certificate}" -o runtime "#{sparkle_path}"`
58
48
  end
49
+
50
+ codesign_without_sparkle(config, platform)
59
51
  end
60
52
 
61
- codesign_without_sparkle(config, platform)
53
+ alias_method 'codesign_without_sparkle', 'codesign'
54
+ alias_method 'codesign', 'codesign_with_sparkle'
62
55
  end
63
-
64
- alias_method "codesign_without_sparkle", "codesign"
65
- alias_method "codesign", "codesign_with_sparkle"
66
56
  end
67
57
  end