4 Usage: AppcastReplaceItem path-to-appcast version-to-replace version-to-replace-with path-to-dmg
6 Example: AppcastReplaceItem appcast-release.xml 1.1.4 1.2 Release/build/Adium_1.2.dmg
9 # Configurable variables.
11 changelog_fmt
= 'http://www.adiumx.com/changelogs/%(version)s.html'
12 enclosure_fmt
= ' <enclosure sparkle:md5Sum="%(md5)s" sparkle:version="%(version)s" url="%(url)s" length="%(file_size)s" type="application/octet-stream"/>\n'
13 # End of configurable variables.
15 import xml
.etree
.cElementTree
as ElementTree
22 args
= dict(zip(('appcast_path', 'old_version', 'version', 'dmg_pathname'), sys
.argv
[1:]))
24 appcast_path
= args
['appcast_path']
25 old_version
= args
['old_version']
26 version
= args
['version']
27 dmg_pathname
= args
['dmg_pathname']
29 print >>sys
.stderr
, __doc__
31 args
['app_name'] = app_name
33 # Get the length and modification time of the dmg file.
34 sb
= os
.stat(dmg_pathname
)
35 file_size
= args
['file_size'] = sb
[ST_SIZE
]
36 dmg_mod_time
= time
.localtime(sb
[ST_MTIME
])
38 # Suffix for the day of the month.
39 th
= ['st', 'nd', 'rd'] + ['th'] * (31 - 3)
41 # GMT offset in hours.
42 gmt_offset
= '%+i' % (-int(time
.timezone
/ 3600),)
44 # Format, which we must fill in with the above items first.
45 time_fmt
= '%A, %B %dth, %Y %H:%M:%S GMT+0'.replace('th', th
[dmg_mod_time
.tm_mday
]).replace('+0', gmt_offset
)
46 dmg_mod_date
= args
['dmg_mod_date'] = time
.strftime(time_fmt
, dmg_mod_time
)
48 openssl_md5
= subprocess
.Popen(['openssl', 'md5', dmg_pathname
], stdout
=subprocess
.PIPE
)
50 openssl_md5
.stdout
.read(len('MD5(') + len(dmg_pathname
) + len(')= '))
51 md5
= args
['md5'] = openssl_md5
.stdout
.read().strip()
52 exit_status
= openssl_md5
.wait()
53 if exit_status
!= 0: sys
.exit('openssl md5 exited with status ' + str(exit_status
))
54 # An MD5 hash is 16 bytes, which is 32 digits hexadecimal.
55 assert len(md5
) == 32, 'MD5 sum is %u bytes' % (len(md5
),)
57 dmg_filename
= os
.path
.split(dmg_pathname
)[1]
58 url
= args
['url'] = 'http://adiumx.cachefly.net/' + dmg_filename
60 # Because XML parsing with the standard library is a PITA, we're going to do it the hackish way.
61 xmlfile
= file(appcast_path
)
64 is_correct_item
= False
65 found_correct_item
= False
73 is_correct_item
= False
74 elif '<title>' in line
:
75 if '>%(app_name)s %(old_version)s<' % args
in line
:
76 line
= line
.replace(old_version
, version
)
77 is_correct_item
= found_correct_item
= True
79 if'<pubDate>' in line
:
80 line
= ' <pubDate>%(dmg_mod_date)s</pubDate>\n' % args
81 elif '<sparkle:releaseNotesLink>' in line
:
82 line
= ' <sparkle:releaseNotesLink>%s</sparkle:releaseNotesLink>\n' % (changelog_fmt
% args
,)
83 elif '<enclosure' in line
:
84 line
= enclosure_fmt
% args
87 if not found_correct_item
:
88 sys
.exit('No item found for version %(old_version)s' % args
)
90 xmlfile
= file(appcast_path
, 'w')
91 xmlfile
.writelines(lines
)