summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Maw <richard.maw@codethink.co.uk>2013-01-24 15:49:10 +0000
committerRichard Maw <richard.maw@codethink.co.uk>2013-01-28 13:04:26 +0000
commit6d50cfe8df892b10dd5c1312e013d8e228648bc7 (patch)
treeea62bcff16e3e423835d4ad8776526ec68214574
parent0a6ab406c667d8b542ee6008349423a83896297b (diff)
downloadmorph-6d50cfe8df892b10dd5c1312e013d8e228648bc7.tar.gz
Make morph get its version from git.
When morph is built, it writes various version information from git into morphlib. When morphlib is loaded it attempts to read this version information. if it cannot be found then it checks whether morphlib is being run from inside a git checkout, if it is then it reads the information that way. If it isn't in a git checkout then it raises an exception as builds made in such a fashion are not reproducible. The git version information retained is: 1. The output of git describe This is a relatively human-friendly way of knowing a version and gives a reasonably short output string. This will end with `-unreproducible` if there were uncommitted changes. 2. The commit sha1, so the exact part of Morph's history can be found 3. The tree sha1, so if the branch has been rebased rather than merged such that the commit is lost, you may still be able to find it, though it requires a git-wizard to check it out 4. The branch of morph, so that it's easier to see if the Further possible changes to increase reproducibility include: 1. Not allowing `python setup.py build` if there are uncommitted changes 2. Failing to run with uncommitted changes (recommended against since it will just annoy developers who are making changes to morph, and make them commit just to shut it up, then destroy the history later) Requiring an extra flag to build in this case may work better. 3. Reading the uncommitted changes into a tree object and including that would allow it to be recovered if the tree was later committed. 4. Checking whether the commit has been pushed upstream as well. Too annoying to work.
-rw-r--r--morphlib/__init__.py5
-rw-r--r--morphlib/gitversion.py57
-rw-r--r--setup.py53
-rw-r--r--without-test-modules1
4 files changed, 109 insertions, 7 deletions
diff --git a/morphlib/__init__.py b/morphlib/__init__.py
index 213241d8..2d2f68a2 100644
--- a/morphlib/__init__.py
+++ b/morphlib/__init__.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2011-2012 Codethink Limited
+# Copyright (C) 2011-2013 Codethink Limited
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -19,8 +19,9 @@
import cliapp
+import gitversion
-__version__ = '0.1'
+__version__ = gitversion.version
class Error(cliapp.AppException):
diff --git a/morphlib/gitversion.py b/morphlib/gitversion.py
new file mode 100644
index 00000000..b1f82da6
--- /dev/null
+++ b/morphlib/gitversion.py
@@ -0,0 +1,57 @@
+# Copyright (C) 2013 Codethink Limited
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; version 2 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+
+'''Version information retrieved either from the package data, or the
+ git repository the library is being run from.
+
+ It is an error to run morph without this version information, since
+ it makes it impossible to reproduce any Systems that are built.
+'''
+
+
+import subprocess
+import os
+
+import cliapp
+
+
+try:
+ import pkgutil
+ version = pkgutil.get_data('morphlib', 'version')
+ commit = pkgutil.get_data('morphlib', 'commit')
+ tree = pkgutil.get_data('morphlib', 'tree')
+ ref = pkgutil.get_data('morphlib', 'ref')
+except IOError, e:
+ from os.path import dirname
+ def run_git(*args):
+ command = ['git'] + list(args)
+ p = subprocess.Popen(command,
+ cwd=os.path.dirname(__file__),
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
+ o = p.communicate()
+ if p.returncode:
+ raise subprocess.CalledProcessError(p.returncode,
+ command)
+ return o[0].strip()
+
+ try:
+ version = run_git('describe', '--always', '--dirty=-unreproducible')
+ commit = run_git('rev-parse', 'HEAD^{commit}')
+ tree = run_git('rev-parse', 'HEAD^{tree}')
+ ref = run_git('rev-parse', '--symbolic-full-name', 'HEAD')
+ except cliapp.AppException:
+ raise cliapp.AppException("morphlib version could not be determined")
diff --git a/setup.py b/setup.py
index eceb705b..6e8c4c4b 100644
--- a/setup.py
+++ b/setup.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2011, 2012 Codethink Limited
+# Copyright (C) 2011, 2012, 2013 Codethink Limited
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -23,23 +23,58 @@ from distutils.command.build import build
from distutils.command.clean import clean
import glob
import os
+import os.path
import shutil
import subprocess
+import cliapp
+
import morphlib
-class GenerateManpage(build):
+class GenerateResources(build):
def run(self):
+ if not self.dry_run:
+ self.generate_manpages()
+ self.generate_version()
build.run(self)
- print 'building manpages'
+
+ def generate_manpages(self):
+ self.announce('building manpages')
for x in ['morph']:
with open('%s.1' % x, 'w') as f:
subprocess.check_call(['python', x,
'--generate-manpage=%s.1.in' % x,
'--output=%s.1' % x], stdout=f)
+ def generate_version(self):
+ target_dir = os.path.join(self.build_lib, 'morphlib')
+
+ self.mkpath(target_dir)
+
+ def save_git_info(filename, *args):
+ path = os.path.join(target_dir, filename)
+ command = ['git'] + list(args)
+
+ self.announce('generating %s with %s' %
+ (path, ' '.join(command)))
+
+ with open(os.path.join(target_dir, filename), 'w') as f:
+ p = subprocess.Popen(command,
+ cwd=os.path.dirname(__file__),
+ stdout=subprocess.PIPE,
+ stderr=subprocess.STDOUT)
+ o = p.communicate()
+ if p.returncode:
+ raise subprocess.CalledProcessError(p.returncode, command)
+ f.write(o[0].strip())
+
+ save_git_info('version', 'describe', '--always',
+ '--dirty=-unreproducible')
+ save_git_info('commit', 'rev-parse', 'HEAD^{commit}')
+ save_git_info('tree', 'rev-parse', 'HEAD^{tree}')
+ save_git_info('ref', 'rev-parse', '--symbolic-full-name', 'HEAD')
class Clean(clean):
@@ -104,10 +139,18 @@ FIXME
url='http://www.baserock.org/',
scripts=['morph'],
packages=['morphlib'],
- package_data={'morphlib': ['plugins/*_plugin.py']},
+ package_data={
+ 'morphlib': [
+ 'plugins/*_plugin.py',
+ 'version',
+ 'commit',
+ 'tree',
+ 'ref',
+ ]
+ },
data_files=[('share/man/man1', glob.glob('*.[1-8]'))],
cmdclass={
- 'build': GenerateManpage,
+ 'build': GenerateResources,
'check': Check,
'clean': Clean,
})
diff --git a/without-test-modules b/without-test-modules
index f1ea523d..cb0302c8 100644
--- a/without-test-modules
+++ b/without-test-modules
@@ -16,5 +16,6 @@ morphlib/plugins/trebuchet_plugin.py
morphlib/plugins/branch_and_merge_plugin.py
morphlib/buildcommand.py
morphlib/plugins/build_plugin.py
+morphlib/gitversion.py
morphlib/plugins/expand_repo_plugin.py