diff options
author | Mathieu Bridon <bochecha@fedoraproject.org> | 2014-07-04 17:59:44 +0200 |
---|---|---|
committer | Kevin Fenzi <kevin@scrye.com> | 2014-08-26 18:28:25 +0000 |
commit | fed72f7ba11ee89520f289498bd85aa23b87e69a (patch) | |
tree | b39c9f21f78eff928038af12ec10137b2a11d851 /roles/git/hooks/files/git.py | |
parent | b121d21d569d3cddf0a95183b24afb4be6eba690 (diff) | |
download | ansible-fed72f7ba11ee89520f289498bd85aa23b87e69a.tar.gz ansible-fed72f7ba11ee89520f289498bd85aa23b87e69a.tar.xz ansible-fed72f7ba11ee89520f289498bd85aa23b87e69a.zip |
Add a new git/hooks role
This will be needed to migrate Dist Git from puppet to ansible.
Diffstat (limited to 'roles/git/hooks/files/git.py')
-rw-r--r-- | roles/git/hooks/files/git.py | 211 |
1 files changed, 211 insertions, 0 deletions
diff --git a/roles/git/hooks/files/git.py b/roles/git/hooks/files/git.py new file mode 100644 index 000000000..72adff1f7 --- /dev/null +++ b/roles/git/hooks/files/git.py @@ -0,0 +1,211 @@ +# Utility functions for git +# +# Copyright (C) 2008 Owen Taylor +# Copyright (C) 2009 Red Hat, Inc +# +# 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; either version 2 +# of the License, or (at your option) any later version. +# +# 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, If not, see +# http://www.gnu.org/licenses/. +# +# (These are adapted from git-bz) + +import os +import re +from subprocess import Popen, PIPE +import sys + +from util import die + +# Clone of subprocess.CalledProcessError (not in Python 2.4) +class CalledProcessError(Exception): + def __init__(self, returncode, cmd): + self.returncode = returncode + self.cmd = cmd + + def __str__(self): + return "Command '%s' returned non-zero exit status %d" % (self.cmd, self.returncode) + +NULL_REVISION = "0000000000000000000000000000000000000000" + +# Run a git command +# Non-keyword arguments are passed verbatim as command line arguments +# Keyword arguments are turned into command line options +# <name>=True => --<name> +# <name>='<str>' => --<name>=<str> +# Special keyword arguments: +# _quiet: Discard all output even if an error occurs +# _interactive: Don't capture stdout and stderr +# _input=<str>: Feed <str> to stdinin of the command +# _outfile=<file): Use <file> as the output file descriptor +# _split_lines: Return an array with one string per returned line +# +def git_run(command, *args, **kwargs): + to_run = ['git', command.replace("_", "-")] + + interactive = False + quiet = False + input = None + interactive = False + outfile = None + do_split_lines = False + for (k,v) in kwargs.iteritems(): + if k == '_quiet': + quiet = True + elif k == '_interactive': + interactive = True + elif k == '_input': + input = v + elif k == '_outfile': + outfile = v + elif k == '_split_lines': + do_split_lines = True + elif v is True: + if len(k) == 1: + to_run.append("-" + k) + else: + to_run.append("--" + k.replace("_", "-")) + else: + to_run.append("--" + k.replace("_", "-") + "=" + v) + + to_run.extend(args) + + if outfile: + stdout = outfile + else: + if interactive: + stdout = None + else: + stdout = PIPE + + if interactive: + stderr = None + else: + stderr = PIPE + + if input != None: + stdin = PIPE + else: + stdin = None + + process = Popen(to_run, + stdout=stdout, stderr=stderr, stdin=stdin) + output, error = process.communicate(input) + if process.returncode != 0: + if not quiet and not interactive: + print >>sys.stderr, error, + print output, + raise CalledProcessError(process.returncode, " ".join(to_run)) + + if interactive or outfile: + return None + else: + if do_split_lines: + return output.strip().splitlines() + else: + return output.strip() + +# Wrapper to allow us to do git.<command>(...) instead of git_run() +class Git: + def __getattr__(self, command): + def f(*args, **kwargs): + return git_run(command, *args, **kwargs) + return f + +git = Git() + +class GitCommit: + def __init__(self, id, subject): + self.id = id + self.subject = subject + +# Takes argument like 'git.rev_list()' and returns a list of commit objects +def rev_list_commits(*args, **kwargs): + kwargs_copy = dict(kwargs) + kwargs_copy['pretty'] = 'format:%s' + kwargs_copy['_split_lines'] = True + lines = git.rev_list(*args, **kwargs_copy) + if (len(lines) % 2 != 0): + raise RuntimeException("git rev-list didn't return an even number of lines") + + result = [] + for i in xrange(0, len(lines), 2): + m = re.match("commit\s+([A-Fa-f0-9]+)", lines[i]) + if not m: + raise RuntimeException("Can't parse commit it '%s'", lines[i]) + commit_id = m.group(1) + subject = lines[i + 1] + result.append(GitCommit(commit_id, subject)) + + return result + +# Loads a single commit object by ID +def load_commit(commit_id): + return rev_list_commits(commit_id + "^!")[0] + +# Return True if the commit has multiple parents +def commit_is_merge(commit): + if isinstance(commit, basestring): + commit = load_commit(commit) + + parent_count = 0 + for line in git.cat_file("commit", commit.id, _split_lines=True): + if line == "": + break + if line.startswith("parent "): + parent_count += 1 + + return parent_count > 1 + +# Return a short one-line summary of the commit +def commit_oneline(commit): + if isinstance(commit, basestring): + commit = load_commit(commit) + + return commit.id[0:7]+"... " + commit.subject[0:59] + +# Return the directory name with .git stripped as a short identifier +# for the module +def get_module_name(): + try: + git_dir = git.rev_parse(git_dir=True, _quiet=True) + except CalledProcessError: + die("GIT_DIR not set") + + # Use the directory name with .git stripped as a short identifier + absdir = os.path.abspath(git_dir) + if absdir.endswith(os.sep + '.git'): + absdir = os.path.dirname(absdir) + projectshort = os.path.basename(absdir) + if projectshort.endswith(".git"): + projectshort = projectshort[:-4] + + return projectshort + +# Return the project description or '' if it is 'Unnamed repository;' +def get_project_description(): + try: + git_dir = git.rev_parse(git_dir=True, _quiet=True) + except CalledProcessError: + die("GIT_DIR not set") + + projectdesc = '' + description = os.path.join(git_dir, 'description') + if os.path.exists(description): + try: + projectdesc = open(description).read().strip() + except: + pass + if projectdesc.startswith('Unnamed repository;'): + projectdesc = '' + + return projectdesc |