1
1
module Motion ::Project
2
2
class Sparkle
3
- def create_release_notes
4
- if File . exist? ( release_notes_template_path )
5
- File . open ( release_notes_path . to_s , 'w' ) do |f |
6
- template = File . read ( release_notes_template_path )
7
- f << ERB . new ( template ) . result ( binding )
8
- end
9
- App . info 'Create' , "./#{ release_notes_path } "
10
- else
11
- App . fail "Release notes template not found as expected at ./#{ release_notes_template_path } "
3
+ # Generate the appcast.
4
+ # Note: We do not support the old DSA keys, only the newer EdDSA keys.
5
+ # See https://sparkle-project.org/documentation/eddsa-migration
6
+ def generate_appcast
7
+ generate_appcast_app = "#{ vendored_sparkle_path } /bin/generate_appcast"
8
+ path = ( project_path + archive_folder ) . realpath
9
+ appcast_filename = ( path + appcast . feed_filename )
10
+
11
+ args = [ ]
12
+
13
+ FileUtils . mkdir_p ( path ) unless File . exist? ( path )
14
+
15
+ App . info ( 'Sparkle' , "Generating appcast using `#{ generate_appcast_app } `" )
16
+ puts "from files in `#{ path } `..." . indent ( 11 )
17
+
18
+ if appcast . use_exported_private_key && File . exist? ( private_key_path )
19
+ # -s <private-EdDSA-key> The private EdDSA string (128 characters). If not
20
+ # specified, the private EdDSA key will be read from
21
+ # the Keychain instead.
22
+ private_key = File . read ( private_key_path )
23
+ args << "-s=#{ private_key } "
12
24
end
13
- end
14
25
15
- def create_appcast
16
- create_release_folder
17
- appcast_file = File . open ( "#{ sparkle_release_path } /#{ appcast . feed_filename } " , 'w' ) do |f |
18
- xml_string = ''
19
- doc = REXML ::Formatters ::Pretty . new
20
- doc . write ( appcast_xml , xml_string )
21
- f << "<?xml version=\" 1.0\" encoding=\" utf-8\" ?>\n "
22
- f << xml_string
23
- f << "\n "
26
+ # --download-url-prefix <url> A URL that will be used as prefix for the URL from
27
+ # where updates will be downloaded.
28
+ args << "--download-url-prefix=#{ appcast . package_url } " if appcast . package_url . present?
29
+
30
+ # --release-notes-url-prefix <url> A URL that will be used as prefix for constructing
31
+ # URLs for release notes.
32
+ args << "--release-notes-url-prefix=#{ appcast . notes_url } " if appcast . notes_url . present?
33
+
34
+ # --link <link> A URL to the application's website which Sparkle may
35
+ # use for directing users to if they cannot download a
36
+ # new update from within the application. This will be
37
+ # used for new generated update items. By default, no
38
+ # product link is used.
39
+
40
+ # --versions <versions> An optional comma delimited list of application
41
+ # versions (specified by CFBundleVersion) to generate
42
+ # new update items for. By default, new update items
43
+ # are inferred from the available archives and are only
44
+ # generated if they are in the latest 5 updates in the
45
+ # appcast.
46
+
47
+ # --maximum-deltas <maximum-deltas>
48
+ # The maximum number of delta items to create for the
49
+ # latest update for each minimum required operating
50
+ # system. (default: 5)
51
+
52
+ # --channel <channel-name>
53
+ # The Sparkle channel name that will be used for
54
+ # generating new updates. By default, no channel is
55
+ # used. Old applications need to be using Sparkle 2 to
56
+ # use this feature.
57
+
58
+ # --major-version <major-version>
59
+ # The last major or minimum autoupdate sparkle:version
60
+ # that will be used for generating new updates. By
61
+ # default, no last major version is used.
62
+
63
+ # --phased-rollout-interval <phased-rollout-interval>
64
+ # The phased rollout interval in seconds that will be
65
+ # used for generating new updates. By default, no
66
+ # phased rollout interval is used.
67
+
68
+ # --critical-update-version <critical-update-version>
69
+ # The last critical update sparkle:version that will be
70
+ # used for generating new updates. An empty string
71
+ # argument will treat this update as critical coming
72
+ # from any application version. By default, no last
73
+ # critical update version is used. Old applications
74
+ # need to be using Sparkle 2 to use this feature.
75
+
76
+ # --informational-update-versions <informational-update-versions>
77
+ # A comma delimited list of application
78
+ # sparkle:version's that will see newly generated
79
+ # updates as being informational only. An empty string
80
+ # argument will treat this update as informational
81
+ # coming from any application version. By default,
82
+ # updates are not informational only. --link must also
83
+ # be provided. Old applications need to be using
84
+ # Sparkle 2 to use this feature.
85
+
86
+ # -o <output-path> Path to filename for the generated appcast (allowed
87
+ # when only one will be created).
88
+
89
+ # -f <private-dsa-key-file> Path to the private DSA key file. Only use this
90
+ # option for transitioning to EdDSA from older updates.
91
+ # Note: only for supporting a legacy app that used DSA keys. Check if the
92
+ # default DSA key exists in `sparkle/config/dsa_priv.pem` and if it does,
93
+ # add it to the command.
94
+ if File . exist? ( legacy_private_key_path )
95
+ App . info 'Sparkle' , "Also signing with legacy DSA key at #{ legacy_private_key_path } "
96
+ args << "-f=#{ legacy_private_key_path } "
24
97
end
25
- if appcast_file
26
- App . info 'Create' , "./#{ sparkle_release_path } /#{ appcast . feed_filename } "
27
- else
28
- App . info 'Fail' , "./#{ sparkle_release_path } /#{ appcast . feed_filename } not created"
98
+
99
+ args << "-o=#{ appcast_filename } " if appcast_filename . present?
100
+
101
+ App . info 'Executing' , [ generate_appcast_app , *args , path . to_s ] . join ( ' ' )
102
+
103
+ results , status = Open3 . capture2e ( generate_appcast_app , *args , path . to_s )
104
+
105
+ App . info ( 'Sparkle' , "Saved appcast to `#{ appcast_filename } `" ) if status . success?
106
+ puts results . indent ( 11 )
107
+
108
+ if status . success?
109
+ puts
110
+ puts "SUFeedURL : #{ feed_url } " . indent ( 11 )
111
+ puts "SUPublicEDKey : #{ public_EdDSA_key } " . indent ( 11 )
29
112
end
30
113
end
31
114
32
- def appcast_xml
33
- rss = REXML ::Element . new 'rss'
34
- rss . attributes [ 'xmlns:atom' ] = 'http://www.w3.org/2005/Atom'
35
- rss . attributes [ 'xmlns:sparkle' ] = 'http://www.andymatuschak.org/xml-namespaces/sparkle'
36
- rss . attributes [ 'xmlns:version' ] = '2.0'
37
- rss . attributes [ 'xmlns:dc' ] = 'http://purl.org/dc/elements/1.1/'
38
- channel = rss . add_element 'channel'
39
- channel . add_element ( 'title' ) . text = @config . name
40
- channel . add_element ( 'description' ) . text = "#{ @config . name } updates"
41
- channel . add_element ( 'link' ) . text = @config . info_plist [ 'SUFeedURL' ]
42
- channel . add_element ( 'language' ) . text = 'en'
43
- channel . add_element ( 'pubDate' ) . text = Time . now . strftime ( '%a, %d %b %Y %H:%M:%S %z' )
44
- atom_link = channel . add_element ( 'atom:link' )
45
- atom_link . attributes [ 'href' ] = @config . info_plist [ 'SUFeedURL' ]
46
- atom_link . attributes [ 'rel' ] = 'self'
47
- atom_link . attributes [ 'type' ] = 'application/rss+xml'
48
- item = channel . add_element 'item'
49
- item . add_element ( 'title' ) . text = "#{ @config . name } #{ @config . short_version } "
50
- item . add_element ( 'pubDate' ) . text = Time . now . strftime ( '%a, %d %b %Y %H:%M:%S %z' )
51
- guid = item . add_element ( 'guid' )
52
- guid . text = "#{ @config . name } -#{ @config . short_version } "
53
- guid . attributes [ 'isPermaLink' ] = false
54
- item . add_element ( 'sparkle:releaseNotesLink' ) . text = appcast . notes_url . to_s
55
- enclosure = item . add_element ( 'enclosure' )
56
- enclosure . attributes [ 'url' ] = appcast . package_url . to_s
57
- enclosure . attributes [ 'length' ] = @package_size . to_s
58
- enclosure . attributes [ 'type' ] = 'application/octet-stream'
59
- enclosure . attributes [ 'sparkle:version' ] = @config . version
60
- enclosure . attributes [ 'sparkle:shortVersionString' ] = @config . short_version
61
- enclosure . attributes [ 'sparkle:dsaSignature' ] = @package_signature
62
- rss
115
+ def generate_appcast_help
116
+ generate_appcast_app = "#{ vendored_sparkle_path } /bin/generate_appcast"
117
+ results , _status = Open3 . capture2e ( generate_appcast_app , '--help' )
118
+ puts results
119
+ end
120
+
121
+ def create_release_notes
122
+ App . fail "Release notes template not found as expected at ./#{ release_notes_template_path } " unless File . exist? ( release_notes_template_path )
123
+
124
+ create_release_folder
125
+
126
+ File . open ( release_notes_path . to_s , 'w' ) do |f |
127
+ template = File . read ( release_notes_template_path )
128
+ f << ERB . new ( template ) . result ( binding )
129
+ end
130
+
131
+ App . info 'Create' , "./#{ release_notes_path } "
63
132
end
64
133
65
134
def release_notes_template_path
@@ -71,7 +140,7 @@ def release_notes_content_path
71
140
end
72
141
73
142
def release_notes_path
74
- sparkle_release_path + appcast . notes_filename . to_s
143
+ sparkle_release_path + ( appcast . notes_filename || " #{ app_name } . #{ @config . short_version } .html" )
75
144
end
76
145
77
146
def release_notes_content
@@ -94,29 +163,31 @@ class Appcast
94
163
:notes_filename ,
95
164
:package_base_url ,
96
165
:package_filename ,
97
- :archive_folder
166
+ :archive_folder ,
167
+ :use_exported_private_key
98
168
99
169
def initialize
100
170
@feed_base_url = nil
101
171
@feed_filename = 'releases.xml'
102
172
@notes_base_url = nil
103
- @notes_filename = 'release_notes.html'
173
+ @notes_filename = nil
104
174
@package_base_url = nil
105
175
@package_filename = nil
106
176
@base_url = nil
107
177
@archive_folder = nil
178
+ @use_exported_private_key = false
108
179
end
109
180
110
181
def feed_url
111
- "#{ feed_base_url || base_url } / #{ feed_filename } "
182
+ "#{ feed_base_url || base_url } #{ feed_filename } "
112
183
end
113
184
114
185
def notes_url
115
- " #{ notes_base_url || base_url } / #{ notes_filename } "
186
+ notes_base_url || base_url
116
187
end
117
188
118
189
def package_url
119
- " #{ package_base_url || base_url } / #{ package_filename } "
190
+ package_base_url || base_url
120
191
end
121
192
end
122
193
end
0 commit comments