motion-sparkle-sandbox 2.0.0 → 2.0.1
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.
- checksums.yaml +4 -4
- data/.gitignore +4 -0
- data/.rspec +2 -0
- data/.rubocop.yml +7 -0
- data/.rubocop_todo.yml +183 -0
- data/.ruby-version +1 -0
- data/.travis.yml +37 -0
- data/Gemfile +13 -3
- data/README.md +73 -71
- data/Rakefile +2 -7
- data/bin/bundle +103 -0
- data/bin/byebug +29 -0
- data/bin/coderay +29 -0
- data/bin/fuzzy_match +29 -0
- data/bin/htmldiff +29 -0
- data/bin/httpclient +29 -0
- data/bin/ldiff +29 -0
- data/bin/pod +29 -0
- data/bin/pry +29 -0
- data/bin/rake +29 -0
- data/bin/rspec +29 -0
- data/bin/rubocop +29 -0
- data/bin/ruby-parse +29 -0
- data/bin/ruby-rewrite +29 -0
- data/bin/sandbox-pod +29 -0
- data/bin/xcodeproj +29 -0
- data/lib/motion/project/appcast.rb +177 -104
- data/lib/motion/project/indent_string.rb +18 -0
- data/lib/motion/project/install.rb +22 -66
- data/lib/motion/project/package.rb +52 -36
- data/lib/motion/project/project.rb +41 -51
- data/lib/motion/project/rake_tasks.rb +28 -32
- data/lib/motion/project/setup.rb +67 -73
- data/lib/motion/project/sparkle.rb +244 -148
- data/lib/motion/project/templates.rb +24 -22
- data/lib/motion-sparkle-sandbox/version.rb +5 -0
- data/lib/motion-sparkle-sandbox.rb +19 -3
- data/motion-sparkle-sandbox.gemspec +26 -16
- data/sample-app/.gitignore +9 -0
- data/sample-app/Gemfile +8 -0
- data/sample-app/README.md +87 -0
- data/sample-app/Rakefile +44 -0
- data/sample-app/app/app_delegate.rb +15 -0
- data/sample-app/app/menu.rb +155 -0
- data/sample-app/resources/Assets.xcassets/AppIcon.appiconset/1024x1024.png +0 -0
- data/sample-app/resources/Assets.xcassets/AppIcon.appiconset/Contents.json +68 -0
- data/sample-app/resources/Assets.xcassets/AppIcon.appiconset/Icon_128x128.png +0 -0
- data/sample-app/resources/Assets.xcassets/AppIcon.appiconset/Icon_128x128@2x.png +0 -0
- data/sample-app/resources/Assets.xcassets/AppIcon.appiconset/Icon_16x16.png +0 -0
- data/sample-app/resources/Assets.xcassets/AppIcon.appiconset/Icon_16x16@2x.png +0 -0
- data/sample-app/resources/Assets.xcassets/AppIcon.appiconset/Icon_256x256.png +0 -0
- data/sample-app/resources/Assets.xcassets/AppIcon.appiconset/Icon_256x256@2x.png +0 -0
- data/sample-app/resources/Assets.xcassets/AppIcon.appiconset/Icon_32x32.png +0 -0
- data/sample-app/resources/Assets.xcassets/AppIcon.appiconset/Icon_32x32@2x.png +0 -0
- data/sample-app/resources/Assets.xcassets/AppIcon.appiconset/Icon_512x512.png +0 -0
- data/sample-app/resources/Assets.xcassets/AppIcon.appiconset/Icon_512x512@2x.png +0 -0
- data/sample-app/resources/Assets.xcassets/Contents.json +6 -0
- data/sample-app/resources/Credits.rtf +29 -0
- data/sample-app/spec/main_spec.rb +11 -0
- data/spec/setup_spec.rb +67 -0
- data/spec/sparkle_spec.rb +113 -65
- data/spec/spec_helper.rb +94 -29
- data/spec/spec_utils.rb +32 -0
- data/vendor/.git_keep +0 -0
- metadata +78 -14
- data/vendor/README.md +0 -34
- data/vendor/Sparkle.zip +0 -0
- data/vendor/codesign_embedded_executable +0 -46
- data/vendor/generate_appcast +0 -0
@@ -1,127 +1,200 @@
|
|
1
|
-
|
2
|
-
|
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
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
71
|
-
|
72
|
-
end
|
109
|
+
App.info('Sparkle', "Saved appcast to `#{appcast_filename}`") if status.success?
|
110
|
+
puts results.indent(11)
|
73
111
|
|
74
|
-
|
75
|
-
sparkle_release_path + appcast.notes_filename.to_s
|
76
|
-
end
|
112
|
+
return unless status.success?
|
77
113
|
|
78
|
-
|
79
|
-
|
80
|
-
|
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
|
-
|
118
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
85
119
|
|
86
|
-
|
87
|
-
|
88
|
-
|
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
|
-
|
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
|
113
|
-
|
139
|
+
def release_notes_template_path
|
140
|
+
sparkle_config_path.join('release_notes.template.erb')
|
114
141
|
end
|
115
142
|
|
116
|
-
def
|
117
|
-
|
143
|
+
def release_notes_content_path
|
144
|
+
sparkle_config_path.join('release_notes.content.html')
|
118
145
|
end
|
119
146
|
|
120
|
-
def
|
121
|
-
"#{
|
147
|
+
def release_notes_path
|
148
|
+
sparkle_release_path + (appcast.notes_filename || "#{app_name}.#{@config.short_version}.html")
|
122
149
|
end
|
123
150
|
|
124
|
-
|
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
|
-
|
2
|
-
class Sparkle
|
1
|
+
# frozen_string_literal: true
|
3
2
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
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
|
-
|
48
|
-
|
49
|
-
|
50
|
-
unzip
|
51
|
-
end
|
10
|
+
def vendored_sparkle_framework_path
|
11
|
+
vendored_sparkle_path.join('Sparkle.framework')
|
12
|
+
end
|
52
13
|
|
53
|
-
|
54
|
-
|
55
|
-
|
14
|
+
def vendored_sparkle_xpc_path
|
15
|
+
vendored_sparkle_path.join('XPCServices')
|
16
|
+
end
|
56
17
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
end
|
18
|
+
def installed?
|
19
|
+
File.directory?(vendored_sparkle_framework_path)
|
20
|
+
end
|
61
21
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
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
|
-
|
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
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
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
|
-
|
27
|
-
|
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
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
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
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
variable :sparkle
|
3
|
+
require 'open3'
|
5
4
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
end
|
11
|
-
@sparkle
|
12
|
-
end
|
13
|
-
end
|
5
|
+
module Motion
|
6
|
+
module Project
|
7
|
+
class Config
|
8
|
+
variable :sparkle
|
14
9
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
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
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
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
|
-
|
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
|
-
|
49
|
-
|
24
|
+
build_without_sparkle(platform, opts)
|
25
|
+
end
|
50
26
|
|
51
|
-
|
52
|
-
|
27
|
+
alias_method 'build_without_sparkle', 'build'
|
28
|
+
alias_method 'build', 'build_with_sparkle'
|
29
|
+
end
|
30
|
+
end
|
53
31
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
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
|
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
|