3 # Copyright (C) 2009-2012 Chris Ball <cjb@laptop.org>
4 # W. Trevor King <wking@tremily.us>
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 Free
10 # Software Foundation, either version 2 of the License, or (at your option) any
13 # Bugs Everywhere is distributed in the hope that it will be useful, but
14 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
18 # You should have received a copy of the GNU General Public License along with
19 # Bugs Everywhere. If not, see <http://www.gnu.org/licenses/>.
29 from libbe.util.subproc import invoke
32 INITIAL_COMMIT = '1bf1ec598b436f41ff27094eddf0b28c797e359d'
35 def validate_tag(tag):
37 >>> validate_tag('1.0.0')
38 >>> validate_tag('A.B.C-r7')
39 >>> validate_tag('A.B.C r7')
40 Traceback (most recent call last):
42 Exception: Invalid character ' ' in tag 'A.B.C r7'
44 Traceback (most recent call last):
46 Exception: Invalid character '"' in tag '"'
48 Traceback (most recent call last):
50 Exception: Invalid character ''' in tag '''
53 if char in string.digits:
55 elif char in string.letters:
57 elif char in ['.','-']:
59 raise Exception("Invalid character '%s' in tag '%s'" % (char, tag))
61 def pending_changes():
62 """Use `git diff`s output to detect change.
64 status,stdout,stderr = invoke(['git', 'diff', 'HEAD'])
69 def set_release_version(tag):
70 print "set libbe.version._VERSION = '%s'" % tag
71 invoke(['sed', '-i', "s/^[# ]*_VERSION *=.*/_VERSION = '%s'/" % tag,
72 os.path.join('libbe', 'version.py')])
74 def remove_makefile_libbe_version_dependencies(filename):
75 print "set %s LIBBE_VERSION :=" % filename
76 invoke(['sed', '-i', "s/^LIBBE_VERSION *:=.*/LIBBE_VERSION :=/",
79 def commit(commit_message):
80 print 'commit current status:', commit_message
81 invoke(['git', 'commit', '-a', '-m', commit_message])
84 print 'tag current revision', tag
85 invoke(['git', 'tag', '-s', '-m', 'version {}'.format(tag), tag])
87 def export(target_dir):
88 if not target_dir.endswith(os.path.sep):
89 target_dir += os.path.sep
90 print 'export current revision to', target_dir
91 status,stdout,stderr = invoke(
92 ['git', 'archive', '--prefix', target_dir, 'HEAD'],
94 status,stdout,stderr = invoke(['tar', '-xv'], stdin=stdout)
97 print 'generate libbe/_version.py'
98 invoke(['make', os.path.join('libbe', '_version.py')])
100 def make_changelog(filename, tag):
101 """Generate a ChangeLog from the git history.
103 Not the most ChangeLog-esque format, but iterating through commits
104 by hand is just too slow.
106 print 'generate ChangeLog file', filename, 'up to tag', tag
107 status,stdout,stderr = invoke(
108 ['git', 'log', '--no-merges', '{}..{}'.format(INITIAL_COMMIT, tag)])
109 with codecs.open(filename, 'w', 'utf-8') as f:
110 for line in stdout.splitlines():
111 f.write(line.rstrip())
114 def set_vcs_name(be_dir, vcs_name='None'):
115 """Exported directory is not a git repository, so set vcs_name to
116 something that will work.
117 vcs_name: new_vcs_name
119 for directory in os.listdir(be_dir):
120 if not os.path.isdir(os.path.join(be_dir, directory)):
122 filename = os.path.join(be_dir, directory, 'settings')
123 if os.path.exists(filename):
124 print 'set vcs_name in', filename, 'to', vcs_name
125 invoke(['sed', '-i', "s/^vcs_name:.*/vcs_name: %s/" % vcs_name,
129 """Generate .be/id-cache so users won't need to.
131 invoke([sys.executable, './be', 'list'])
133 def make_html_docs(docdir):
134 """Generate docs so users won't need to install Sphinx, etc.
136 print('generate HTML docs in {}'.format(docdir))
137 status,stdout,stderr = invoke(
138 ['make', 'SPHINXBUILD=sphinx-build-2.7', 'dirhtml'], cwd=docdir)
140 def create_tarball(tag):
141 release_name='be-%s' % tag
142 export_dir = release_name
145 remove_makefile_libbe_version_dependencies(
146 os.path.join(export_dir, 'Makefile'))
147 print 'copy libbe/_version.py to %s/libbe/_version.py' % export_dir
148 shutil.copy(os.path.join('libbe', '_version.py'),
149 os.path.join(export_dir, 'libbe', '_version.py'))
150 make_changelog(os.path.join(export_dir, 'ChangeLog'), tag)
152 make_html_docs(os.path.join(export_dir, 'doc'))
153 print 'copy .be/id-cache to %s/.be/id-cache' % export_dir
154 shutil.copy(os.path.join('.be', 'id-cache'),
155 os.path.join(export_dir, '.be', 'id-cache'))
156 set_vcs_name(os.path.join(export_dir, '.be'))
157 tarball_file = '%s.tar.gz' % release_name
158 print 'create tarball', tarball_file
159 invoke(['tar', '-czf', tarball_file, export_dir])
160 print 'remove', export_dir
161 shutil.rmtree(export_dir)
167 def main(*args, **kwargs):
168 usage = """%prog [options] TAG
170 Create a git tag and a release tarball from the current revision.
174 If you don't like what got committed, you can undo the release with
176 $ git reset --hard HEAD^
178 p = optparse.OptionParser(usage)
179 p.add_option('--test', dest='test', default=False,
180 action='store_true', help='Run internal tests and exit')
181 options,args = p.parse_args(*args, **kwargs)
183 if options.test == True:
187 assert len(args) == 1, '%d (!= 1) arguments: %s' % (len(args), args)
191 if pending_changes() == True:
192 print "Handle pending changes before releasing."
194 set_release_version(_tag)
195 print "Update copyright information..."
196 env = dict(os.environ)
197 pythonpath = os.path.abspath('update-copyright')
198 if 'PYTHONPATH' in env:
199 env['PYTHONPATH'] = '{}:{}'.format(pythonpath, env['PYTHONPATH'])
201 env['PYTHONPATH'] = pythonpath
202 status,stdout,stderr = invoke([
203 os.path.join('update-copyright', 'bin', 'update-copyright.py')],
205 commit("Bumped to version %s" % _tag)
210 if __name__ == '__main__':