Loading [MathJax]/extensions/tex2jax.js
The Gaudi Framework  v31r0 (aeb156f0)
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
prepare_gaudi_release.py
Go to the documentation of this file.
1 #!/usr/bin/env python
2 '''
3 Script to prepare the release of Gaudi.
4 
5 @author Marco Clemencic
6 '''
7 
8 import os
9 import sys
10 import logging
11 import re
12 from subprocess import check_output, CalledProcessError
13 
14 
16  '''
17  Ensure we have a usable version of Git (>= 1.7.9.1).
18 
19  See:
20  * https://raw.githubusercontent.com/git/git/master/Documentation/RelNotes/1.7.9.1.txt
21  * https://github.com/git/git/commit/36ed1913e1d5de0930e59db6eeec3ccb2bd58bd9
22  '''
23  version = check_output(['git', '--version']).split()[-1]
24  if versionKey(version) < versionKey('1.7.9.1'):
25  raise RuntimeError(
26  'bad version of git found: %s (1.7.9.1 required)' % version)
27 
28 
29 _VK_RE = re.compile(r'(\d+|\D+)')
30 
31 
32 def versionKey(x):
33  '''
34  Key function to be passes to list.sort() to sort strings
35  as version numbers.
36  '''
37  return [
38  int(i) if i[0] in '0123456789' else i for i in _VK_RE.split(x) if i
39  ]
40 
41 
43  '''
44  Return the latest Gaudi tag (of the format "v*r*...").
45  '''
46  logging.info('looking for latest tag')
47  cmd = ['git', 'tag']
48  logging.debug('using command %r', cmd)
49  output = check_output(cmd)
50  vers_exp = re.compile(r'^v\d+r\d+(p\d+)?$')
51  tags = [tag for tag in output.splitlines() if vers_exp.match(tag)]
52  if tags:
53  tags.sort(key=versionKey)
54  logging.info('found %s', tags[-1])
55  return tags[-1]
56  logging.info('no valid tag found')
57 
58 
59 def releaseNotes(path=os.curdir, from_tag=None, branch=None):
60  '''
61  Return the release notes (in the old LHCb format) extracted from git
62  commits for a given path.
63  '''
64  cmd = [
65  'git', 'log', '--first-parent', '--date=short',
66  '--pretty=format:! %ad - commit %h%n%n%w(80,1,3)- %s%n%n%b%n'
67  ]
68  if from_tag:
69  cmd.append('{0}..{1}'.format(from_tag, branch or ''))
70  elif branch:
71  cmd.append(branch)
72  cmd.append('--')
73  cmd.append(path)
74  logging.info('preparing release notes for %s%s', path,
75  ' since ' + from_tag if from_tag else '')
76  logging.debug('using command %r', cmd)
77  # remove '\r' characters from commit messages
78  out = check_output(cmd).replace('\r', '')
79 
80  # replace ' - commit 123abc' with ' - Contributor Name (commit 123abc)'
81  def add_contributors(match):
82  try:
83  authors = set(
84  check_output([
85  'git',
86  'log',
87  '--pretty=format:%aN',
88  '{0}^1..{0}^2'.format(match.group(1)),
89  ]).splitlines())
90  return ' - {0} (commit {1})'.format(', '.join(sorted(authors)),
91  match.group(1))
92  except CalledProcessError:
93  return match.group(0)
94 
95  out = re.sub(r' - commit ([0-9a-f]+)', add_contributors, out)
96  return out
97 
98 
99 def updateReleaseNotes(path, notes):
100  '''
101  Smartly prepend the content of notes to the release.notes file in path.
102  '''
103  notes_filename = os.path.join(path, 'doc', 'release.notes')
104  logging.info('updating %s', notes_filename)
105  from itertools import takewhile, dropwhile
106 
107  def dropuntil(predicate, iterable):
108  return dropwhile(lambda x: not predicate(x), iterable)
109 
110  with open(notes_filename) as notes_file:
111  orig_data = iter(list(notes_file))
112  header = takewhile(str.strip, orig_data)
113  with open(notes_filename, 'w') as notes_file:
114  notes_file.writelines(header)
115  notes_file.write('\n')
116  notes_file.writelines(l.rstrip() + '\n' for l in notes.splitlines())
117  notes_file.write('\n')
118  notes_file.writelines(
119  dropuntil(re.compile(r'^!?============').match, orig_data))
120 
121 
122 def tag_bar(pkg, version=None):
123  title = ' %s %s ' % (pkg, version)
124  letf_chars = (78 - len(title)) / 2
125  right_chars = 78 - letf_chars - len(title)
126  separator = ('=' * letf_chars) + title + ('=' * right_chars)
127  return separator
128 
129 
130 def main():
131  logging.basicConfig(level=logging.DEBUG)
132  os.chdir(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
133  # Find the version of HEPTools (LCG)
134  for l in open('toolchain.cmake'):
135  m = re.match(r'^\s*set\(\s*heptools_version\s+(\S*)\s*\)', l)
136  if m:
137  HEPToolsVers = m.group(1)
138  print "Using HEPTools", HEPToolsVers
139  break
140  else:
141  logging.error('Cannot find HEPTools version')
142  sys.exit(1)
143 
144  # Collect all the packages in the project with their directory
145  def all_subdirs():
146  for dirpath, dirnames, filenames in os.walk(os.curdir):
147  if 'CMakeLists.txt' in filenames and dirpath != os.curdir:
148  dirnames[:] = []
149  yield dirpath
150  else:
151  dirnames[:] = [
152  dirname for dirname in dirnames
153  if (not dirname.startswith('build.') and dirname != 'cmake'
154  )
155  ]
156 
157  # Ask for the version of the project
158  latest_tag = findLatestTag()
159 
160  old_version = latest_tag.split('_')[-1]
161  new_version = raw_input(("The old version of the project is %s, "
162  "which is the new one? ") % old_version)
163 
164  release_notes = {}
165  # for each package in the project update the release.notes
166  for pkgdir in all_subdirs():
168  pkgdir,
169  tag_bar('Gaudi', new_version) + '\n\n' + releaseNotes(
170  pkgdir, latest_tag, 'master'))
171 
172  # update the global CMakeLists.txt
173  out = []
174  for l in open('CMakeLists.txt'):
175  if l.strip().startswith('gaudi_project'):
176  l = 'gaudi_project(Gaudi %s)\n' % new_version
177  out.append(l)
178  open('CMakeLists.txt', "w").writelines(out)
179 
180 
181 if __name__ == '__main__':
183  main()
GAUDI_API std::string format(const char *,...)
MsgStream format utility "a la sprintf(...)".
Definition: MsgStream.cpp:109
def releaseNotes(path=os.curdir, from_tag=None, branch=None)
def tag_bar(pkg, version=None)
def updateReleaseNotes(path, notes)