3 # Copyright (C) 2009-2011 Chris Ball <cjb@laptop.org>
4 # W. Trevor King <wking@drexel.edu>
6 # This file is part of Bugs Everywhere.
8 # Bugs Everywhere is free software; you can redistribute it and/or modify it
9 # under the terms of the GNU General Public License as published by the
10 # Free Software Foundation, either version 2 of the License, or (at your
11 # option) any later version.
13 # Bugs Everywhere is distributed in the hope that it will be useful, but
14 # WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 # General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with Bugs Everywhere. If not, see <http://www.gnu.org/licenses/>.
27 from libbe.util.subproc import Pipe, invoke
28 from update_copyright import update_authors, update_files
31 INITIAL_COMMIT = '1bf1ec598b436f41ff27094eddf0b28c797e359d'
34 def validate_tag(tag):
36 >>> validate_tag('1.0.0')
37 >>> validate_tag('A.B.C-r7')
38 >>> validate_tag('A.B.C r7')
39 Traceback (most recent call last):
41 Exception: Invalid character ' ' in tag 'A.B.C r7'
43 Traceback (most recent call last):
45 Exception: Invalid character '"' in tag '"'
47 Traceback (most recent call last):
49 Exception: Invalid character ''' in tag '''
52 if char in string.digits:
54 elif char in string.letters:
56 elif char in ['.','-']:
58 raise Exception("Invalid character '%s' in tag '%s'" % (char, tag))
60 def pending_changes():
61 """Use `git diff`s output to detect change.
63 status,stdout,stderr = invoke(['git', 'diff', 'HEAD'])
68 def set_release_version(tag):
69 print "set libbe.version._VERSION = '%s'" % tag
70 invoke(['sed', '-i', "s/^# *_VERSION *=.*/_VERSION = '%s'/" % tag,
71 os.path.join('libbe', 'version.py')])
73 def remove_makefile_libbe_version_dependencies(filename):
74 print "set %s LIBBE_VERSION :=" % filename
75 invoke(['sed', '-i', "s/^LIBBE_VERSION *:=.*/LIBBE_VERSION :=/",
78 def commit(commit_message):
79 print 'commit current status:', commit_message
80 invoke(['git', 'commit', '-a', '-m', commit_message])
83 print 'tag current revision', tag
84 invoke(['git', 'tag', tag])
86 def export(target_dir):
87 if not target_dir.endswith(os.path.sep):
88 target_dir += os.path.sep
89 print 'export current revision to', target_dir
90 p = Pipe([['git', 'archive', '--prefix', target_dir, 'HEAD'],
92 assert p.status == 0, p.statuses
95 print 'generate libbe/_version.py'
96 invoke(['make', os.path.join('libbe', '_version.py')])
98 def make_changelog(filename, tag):
99 """Generate a ChangeLog from the git history.
101 Not the most ChangeLog-esque format, but iterating through commits
102 by hand is just too slow.
104 print 'generate ChangeLog file', filename, 'up to tag', tag
105 invoke(['git', 'log', '--no-merges',
106 '%s..%s' % (INITIAL_COMMIT, tag)],
107 stdout=open(filename, 'w')),
109 def set_vcs_name(be_dir, vcs_name='None'):
110 """Exported directory is not a git repository, so set vcs_name to
111 something that will work.
112 vcs_name: new_vcs_name
114 for directory in os.listdir(be_dir):
115 if not os.path.isdir(os.path.join(be_dir, directory)):
117 filename = os.path.join(be_dir, directory, 'settings')
118 if os.path.exists(filename):
119 print 'set vcs_name in', filename, 'to', vcs_name
120 invoke(['sed', '-i', "s/^vcs_name:.*/vcs_name: %s/" % vcs_name,
124 """Generate .be/id-cache so users won't need to.
126 invoke(['./be', 'list'])
128 def create_tarball(tag):
129 release_name='be-%s' % tag
130 export_dir = release_name
133 remove_makefile_libbe_version_dependencies(
134 os.path.join(export_dir, 'Makefile'))
135 print 'copy libbe/_version.py to %s/libbe/_version.py' % export_dir
136 shutil.copy(os.path.join('libbe', '_version.py'),
137 os.path.join(export_dir, 'libbe', '_version.py'))
138 make_changelog(os.path.join(export_dir, 'ChangeLog'), tag)
140 print 'copy .be/id-cache to %s/.be/id-cache' % export_dir
141 shutil.copy(os.path.join('.be', 'id-cache'),
142 os.path.join(export_dir, '.be', 'id-cache'))
143 set_vcs_name(os.path.join(export_dir, '.be'))
144 os.remove(os.path.join(export_dir, 'update_copyright.py'))
145 tarball_file = '%s.tar.gz' % release_name
146 print 'create tarball', tarball_file
147 invoke(['tar', '-czf', tarball_file, export_dir])
148 print 'remove', export_dir
149 shutil.rmtree(export_dir)
155 if __name__ == '__main__':
157 usage = """%prog [options] TAG
159 Create a git tag and a release tarball from the current revision.
163 If you don't like what got committed, you can undo the release with
165 $ git reset --hard HEAD^
167 p = optparse.OptionParser(usage)
168 p.add_option('--test', dest='test', default=False,
169 action='store_true', help='Run internal tests and exit')
170 options,args = p.parse_args()
172 if options.test == True:
176 assert len(args) == 1, '%d (!= 1) arguments: %s' % (len(args), args)
180 if pending_changes() == True:
181 print "Handle pending changes before releasing."
183 set_release_version(_tag)
184 print "Update copyright information..."
187 commit("Bumped to version %s" % _tag)