Skip to content

Commit 475c671

Browse files
committed
Rewrite/simplify Sparkle distribution build script so it works again and generates edDSA sigs
1 parent 120e0ab commit 475c671

File tree

5 files changed

+64
-160
lines changed

5 files changed

+64
-160
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
.Trashes
33
*.swp
44

5+
release/
56
version-header.h
67

78
*~.nib

Info.plist

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,8 @@
4545
<string>public.app-category.productivity</string>
4646
<key>LSHasLocalizedDisplayName</key>
4747
<true/>
48-
<key>LSMinimumSystemVersionByArchitecture</key>
49-
<dict>
50-
<key>i386</key>
51-
<string>10.5</string>
52-
<key>x86_64</key>
53-
<string>10.6</string>
54-
</dict>
48+
<key>LSMinimumSystemVersion</key>
49+
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
5550
<key>NSAppleScriptEnabled</key>
5651
<true/>
5752
<key>NSHumanReadableCopyright</key>

SelfControl.xcodeproj/project.pbxproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1029,7 +1029,7 @@
10291029
);
10301030
runOnlyForDeploymentPostprocessing = 0;
10311031
shellPath = /bin/sh;
1032-
shellScript = "ruby ./distribution-build.rb";
1032+
shellScript = "ruby ./distribution-build.rb\n";
10331033
};
10341034
CBFF218A3108765AAB5DE542 /* [CP] Copy Pods Resources */ = {
10351035
isa = PBXShellScriptBuildPhase;

config.yml

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
1-
build_now: 'YES'
2-
min_system_version: '10.8'
31
download_base_url: 'https://downloads.selfcontrolapp.com/'
42
web_base_url: 'https://selfcontrolapp.com'
5-
appcast_basefolder: '/Users/charlie/dev/selfcontrol/'
6-
appcast_xml_name: 'SelfControlAppcast.xml'
3+
appcast_xml_name: 'appcast-snippet.xml'
74
keychain_privkey_name: 'SelfControl Sparkle Private Key'
8-
css_file_name: 'rnotes.css'

distribution-build.rb

Lines changed: 59 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
# #
77
# author: Craig Williams #
88
# created: 2009-01-09 #
9+
# half rewritten by Charlie Stigler from 2009-present :) #
910
# #
1011
#################################################################################
1112
# #
@@ -25,39 +26,28 @@
2526
#################################################################################
2627

2728
class AppCast
28-
require 'rubygems'
2929
require 'yaml'
30-
require 'tmpdir'
31-
require 'fileutils'
32-
require 'openssl'
33-
require 'nokogiri'
34-
require 'base64'
30+
require 'plist'
3531

3632
MESSAGE_HEADER = 'RUN SCRIPT DURING BUILD MESSAGE'
3733
YAML_FOLDER_PATH = "#{ENV['HOME']}/dev/selfcontrol/"
34+
SRCROOT = ENV['SRCROOT'].chomp
3835

3936
def initialize
4037
@signature = ''
4138
require_release_build
39+
parse_project_settings
4240
project_setup
4341
load_config
4442

45-
# the build_now setting in the config.yml file
46-
# determines whether you want to perform this script
47-
# set to 'NO' until you are ready to publish
48-
exit_unless_build
49-
base_folder
5043
appcast_setup
5144
end
5245

5346
def execute!
5447
create_appcast_folder_and_files
5548
remove_old_zip_create_new_zip
5649
file_stats
57-
create_appcast_xml
58-
puts "created appcast xml"
59-
copy_archive_to_appcast_path
60-
puts "copied archive to appcast path"
50+
create_appcast_xml_snippet
6151
end
6252

6353
# Only works for Release builds
@@ -78,91 +68,72 @@ def load_config
7868
end
7969
@config = YAML.load_file(config_file_path)
8070
end
81-
82-
def exit_unless_build
83-
unless @config['build_now'] == 'YES'
84-
log_message("The 'build_now' setting in 'config.yml' set to 'NO'\nIf you are wanting to include this script in\nthe build process change this setting to 'YES'")
85-
exit
86-
end
71+
72+
def parse_project_settings
73+
plist = Plist.parse_xml("#{SRCROOT}/Info.plist")
74+
75+
@version = plist['CFBundleShortVersionString']
8776
end
88-
77+
8978
def project_setup
9079
@proj_dir = ENV['BUILT_PRODUCTS_DIR']
9180
@proj_name = ENV['PROJECT_NAME']
92-
@version = "2.3.2"
93-
@build_number = "2.3.2"
9481
@archive_filename = "#{@proj_name}-#{@version.chomp}.zip" # underline character added
95-
@archive_path = "#{@proj_dir}/#{@archive_filename}"
82+
@archive_path = "#{SRCROOT}/release/#{@archive_filename}".chomp
9683
end
97-
84+
9885
def appcast_setup
9986
@appcast_xml_name = @config['appcast_xml_name'].chomp
100-
@appcast_proj_folder = "#{@config['appcast_basefolder']}/#{@proj_name}-#{@version}".chomp
101-
@appcast_xml_path = "#{@appcast_proj_folder}/#{@appcast_xml_name}"
102-
@min_system_version = @config['min_system_version']
87+
@appcast_release_folder = "#{SRCROOT}/release".chomp
88+
@appcast_xml_path = "#{@appcast_release_folder}/#{@appcast_xml_name}"
89+
@min_system_version = ENV['MACOSX_DEPLOYMENT_TARGET']
10390
@download_base_url = @config['download_base_url']
10491
@web_base_url = @config['web_base_url']
105-
@css_file_name = @config['css_file_name']
10692
@releasenotes_url = "#{@web_base_url}/releasenotes.html#collapse-#{@version.chomp.gsub('.', '-')}"
10793
@download_url = "#{@download_base_url}#{@archive_filename}"
10894
@appcast_download_url = "#{@web_base_url}#{@appcast_xml_name}"
10995
end
110-
111-
def base_folder
112-
@appcast_basefolder = @config['appcast_basefolder'].chomp
113-
File.expand_path(@appcast_basefolder) if @appcast_basefolder.start_with?("~")
114-
end
115-
96+
11697
def remove_old_zip_create_new_zip
11798
puts @proj_dir
11899
Dir.chdir(@proj_dir)
119100
`rm -f #{@proj_name}*.zip`
120-
`ditto -ck --keepParent --rsrc --sequesterRsrc "#{@proj_name}.app" "#{@archive_filename}"`
121-
end
122-
123-
def copy_archive_to_appcast_path
124-
begin
125-
FileUtils.cp(@archive_path, @appcast_proj_folder)
126-
rescue
127-
log_message("There was an error coplogying the zip file to appcast folder\nError: #{$!}")
128-
end
101+
`ditto -ck --keepParent --rsrc --sequesterRsrc "#{@proj_name}.app" "#{@archive_path}"`
129102
end
130-
103+
131104
def file_stats
132-
@size = File.size(@archive_filename)
133105
@pubdate = `date +"%a, %d %b %G %T %z"`
134106
end
135107

136-
def get_signature
137-
puts "Generating signature for archive at path #{@archive_path}"
138-
puts "Command: /usr/local/bin/sparkle/sign_update \"#{@archive_path}\" /Volumes/SelfControl\ Keys\ and\ Secrets/Sparkle\ Signing\ Keys/dsa_priv.pem"
139-
return `/usr/local/bin/sparkle/sign_update \"#{@archive_path}\" \"/Volumes/SelfControl\ Keys\ and\ Secrets/Sparkle\ Signing\ Keys/dsa_priv.pem\"`.chomp
108+
def get_dsa_signature
109+
puts "Generating DSA signature for archive at path #{@archive_path}"
110+
puts "Command: #{SRCROOT}/Pods/Sparkle/bin/old_dsa_scripts/sign_update \"#{@archive_path}\" /Volumes/SelfControl\ Keys\ and\ Secrets/Sparkle\ Signing\ Keys/dsa_priv.pem"
111+
return `#{SRCROOT}/Pods/Sparkle/bin/old_dsa_scripts/sign_update \"#{@archive_path}\" \"/Volumes/SelfControl\ Keys\ and\ Secrets/Sparkle\ Signing\ Keys/dsa_priv.pem\"`.chomp
112+
end
113+
114+
def get_eddsa_signature_and_length_parameters
115+
puts "Generating edDSA signature parameters for archive at path #{@archive_path}"
116+
puts "Command: #{SRCROOT}/Pods/Sparkle/bin/sign_update \"#{@archive_path}\""
117+
return `#{SRCROOT}/Pods/Sparkle/bin/sign_update \"#{@archive_path}\"`.chomp
140118
end
141119

142-
def create_appcast_xml
120+
def create_appcast_xml_snippet
143121
appcast_xml =
144-
"<?xml version=\"1.0\" encoding=\"utf-8\"?>
145-
<rss version=\"2.0\" xmlns:sparkle=\"http://www.andymatuschak.org/xml-namespaces/sparkle\" xmlns:dc=\"http://purl.org/dc/elements/1.1/\">
146-
<title>#{@proj_name}</title>
147-
<link>#{@appcast_download_url}</link>
148-
<description>Most recent changes with links to updates.</description>
149-
<language>en</language>
150-
<item>
122+
" <item>
151123
<title>Version #{@version.chomp}</title>
152124
<sparkle:releaseNotesLink>
153125
#{@releasenotes_url}
154126
</sparkle:releaseNotesLink>
155127
<pubDate>#{@pubdate.chomp}</pubDate>
156128
<enclosure url=\"#{@download_url.chomp}\"
157-
length=\"#{@size}\"
158129
sparkle:version=\"#{@version.chomp}\"
159130
sparkle:shortVersionString=\"#{@version.chomp}\"
160-
sparkle:dsaSignature=\"#{get_signature}\"
131+
sparkle:dsaSignature=\"#{get_dsa_signature}\"
132+
#{get_eddsa_signature_and_length_parameters}
161133
type=\"application/octet-stream\"
162134
/>
163135
<sparkle:minimumSystemVersion>#{@min_system_version}</sparkle:minimumSystemVersion>
164-
</item>
165-
</rss>"
136+
</item>"
166137

167138
File.open(@appcast_xml_path, 'w') { |f| f.puts appcast_xml }
168139
end
@@ -171,19 +142,14 @@ def create_appcast_xml
171142
# or is accidently moved or deleted
172143
# Creates an html file with generic note template if it does not exist
173144
# This way the notes file is named correctly as well
174-
# Creates a css file named from yml file with default css
175145
def create_appcast_folder_and_files
176-
base_folder = @appcast_basefolder
177-
project_folder = @appcast_proj_folder
146+
project_folder = @appcast_release_folder
178147

179-
notes_file = "#{project_folder}/#{File.basename(@releasenotes_url.chomp)}"
180-
css_file_path = "#{project_folder}/#{@css_file_name}"
148+
notes_file = "#{project_folder}/releasenotes-#{@version}.html"
181149

182-
Dir.mkdir(base_folder) unless File.exist?(base_folder)
183150
Dir.mkdir(project_folder) unless File.exist?(project_folder)
184-
185-
File.open(notes_file, 'w') { |f| f.puts release_notes_generic_text } unless File.exist?(notes_file)
186-
File.open(css_file_path, 'w') { |f| f.puts decompressed_css } unless File.exist?(css_file_path)
151+
152+
File.open(notes_file, 'w') { |f| f.puts release_notes_html_snippet } unless File.exist?(notes_file)
187153
end
188154

189155
def log_message(msg)
@@ -192,83 +158,29 @@ def log_message(msg)
192158
puts msg
193159
puts "----------------------------------------------\n\n"
194160
end
195-
196-
def decompressed_css
197-
return css_text.gsub(/\{\s+/, "{\n\t").gsub(/;/, ";\n\t").gsub(/^\s+\}/, "}").gsub(/^\s+/, "\t")
198-
end
199-
200-
def release_notes_generic_text
201-
return "
202-
<html>
203-
<head>
204-
<meta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\">
205-
<title>What's new in #{@proj_name}?</title>
206-
<meta name=\"robots\" content=\"anchors\">
207-
<link href=\"rnotes.css\" type=\"text/css\" rel=\"stylesheet\" media=\"all\">
208-
</head>
209161

210-
<body>
211-
<br />
212-
<table class=\"dots\" width=\"100%\" border=\"0\" cellspacing=\"0\" cellpadding=\"0\" summary=\"Two column table with heading\">
213-
<tr>
214-
<td class=\"blue\" colspan=\"2\">
215-
<h3>#{@proj_name} #{@version.chomp} Release Notes</h3>
216-
</td>
217-
</tr>
218-
<tr>
219-
<td valign=\"top\">
220-
<p>
221-
<ul>
222-
<li>DESCRIPTION</li>
223-
</ul>
224-
</p>
225-
</td>
226-
</tr>
227-
</table>
228-
<br>
229-
</body>
230-
231-
</html>"
232-
end
233-
234-
# This css will be expanded to a normal, easily editable form when written to file
235-
def css_text
162+
def release_notes_html_snippet
163+
@releasenotes_url = "#{@web_base_url}/releasenotes.html#collapse-#{@version.chomp.gsub('.', '-')}"
164+
url_friendly_version = @version.chomp.gsub('.', '-')
165+
236166
return "
237-
body { margin: 2px 12px 12px; }
238-
h1 h2 h3 p ol ul a a:hover { font-family: \"Lucida Grande\", Arial, sans-serif; }
239-
h1 { font-size: 11pt; margin-bottom: 0; }
240-
h2 { font-size: 9pt; margin-top: 0; margin-bottom: -10px; }
241-
h3 { font-size: 9pt; font-weight: bold; margin-top: -4px; margin-bottom: -4px; }
242-
p { font-size: 9pt; line-height: 12pt; text-decoration: none; }
243-
ol { font-size: 9pt; line-height: 12pt; list-style-position: outside; margin-top: 12px; margin-bottom: 12px; margin-left: -18px; padding-left: 40px; }
244-
ol li { margin-top: 6px; margin-bottom: 6px; }
245-
ol p { margin-top: 6px; margin-bottom: 6px; }
246-
ul { font-size: 9pt; line-height: 12pt; list-style-type: square; list-style-position: outside; margin-top: 12px; margin-bottom: 12px; margin-left: -24px; padding-left: 40px; }
247-
ul li { margin-top: 6px; margin-bottom: 6px; }
248-
ul p { margin-top: 6px; margin-bottom: 6px; }
249-
a { color: #00f; font-size: 9pt; line-height: 12pt; text-decoration: none; }
250-
a:hover { color: #00f; text-decoration: underline; }
251-
hr { text-decoration: none; border: solid 1px #bfbfbf; }
252-
td { padding: 6px; }
253-
#banner { background-color: #f2f2f2; background-repeat: no-repeat; padding: -2px 6px 0; position: fixed; top: 0; left: 0; width: 100%; height: 1.2em; float: left; border: solid 1px #bfbfbf; }
254-
#caticon { margin-top: 3px; margin-bottom: -3px; margin-right: 5px; float: left; }
255-
#pagetitle { margin-top: 12px; margin-bottom: 0px; margin-left: 40px; width: 88%; border: solid 1px #fff; }
256-
#mainbox { margin-top: 2349px; padding-right: 6px; }
257-
#taskbox { background-color: #e6edff; list-style-type: decimal; list-style-position: outside; margin: 12px 0; padding: 2px 12px; border: solid 1px #bfbfbf; }
258-
#taskbox h2 { margin-top: 8; margin-bottom: -4px; }
259-
#machelp { position: absolute; top: 2px; left: 10px ; }
260-
#index { background-color: #f2f2f2; padding-right: 25px; top: 2px; right: 12px; width: auto; float: right; }
261-
#next { position: absolute; top: 49px; left: 88%; }
262-
#asindent { margin-left: 22px; font-size: 9pt; font-family: Verdana, Courier, sans-serif; }
263-
.bread { color: #00f; font-size: 8pt; margin: -9px 0 -6px; }
264-
.leftborder { color: #00f; font-size: 8pt; margin: -9px 0 -6px; padding-top: 2px; padding-bottom: 3px; padding-left: 8px; border-left: 1px solid #bfbfbf; }
265-
.mult { margin-top: -8px; }
266-
.blue { background-color: #e6edff; margin-top: -3px; margin-bottom: -3px; padding-top: -3px; padding-bottom: -3px; }
267-
.rightfloater { float: right; margin-left: 15px; }
268-
.rules { border-bottom: 1px dotted #ccc; }
269-
.dots { border: dotted 1px #ccc; }
270-
.seealso { margin-top: 4px; margin-bottom: 4px; }
271-
code { color: black; font-size: 9pt; font-family: Verdana, Courier, sans-serif; }"
167+
<div class=\"accordion-group\">
168+
<div class=\"accordion-heading\">
169+
<a href=\"#collapse-#{url_friendly_version}\" class=\"accordion-toggle\" data-toggle=\"collapse\" data-parent=\"#releasesAccordion\">
170+
<h1 id=\"version-#{url_friendly_version}\">Version #{@version}</h1>
171+
</a>
172+
</div>
173+
<div id=\"collapse-#{url_friendly_version}\" class=\"accordion-body collapse\">
174+
<div class=\"accordion-inner\">
175+
<h3>For Mac OS X #{@min_system_version}+ only. Please don't upgrade while a block is running.</h3>
176+
<h3>Improvements</h3>
177+
<ul>
178+
<li>First great feature</li>
179+
<li>Second great feature</li>
180+
</ul>
181+
</div>
182+
</div>
183+
</div>"
272184
end
273185
end
274186

0 commit comments

Comments
 (0)