summaryrefslogtreecommitdiffstats
path: root/src/lib/Bcfg2/Server/Plugins/Git.py
blob: d0ae010fac3a5a0b25c4b967aa5bdf658df6fd2c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
""" The Git plugin provides a revision interface for Bcfg2 repos using
git. """

import sys
import Bcfg2.Options
from Bcfg2.Server.Plugin import Version, PluginExecutionError

try:
    import git
    HAS_GITPYTHON = True
except ImportError:
    from Bcfg2.Utils import Executor
    HAS_GITPYTHON = False


class Git(Version):
    """ The Git plugin provides a revision interface for Bcfg2 repos
    using git. """
    __author__ = 'bcfg-dev@mcs.anl.gov'
    __vcs_metadata_path__ = ".git"
    if HAS_GITPYTHON:
        __rmi__ = Version.__rmi__ + ['Update']

    def __init__(self, core):
        Version.__init__(self, core)
        if HAS_GITPYTHON:
            self.repo = git.Repo(Bcfg2.Options.setup.vcs_root)
            self.cmd = None
        else:
            self.logger.debug("Git: GitPython not found, using CLI interface "
                              "to Git")
            self.repo = None
            self.cmd = Executor()
        self.logger.debug("Initialized git plugin with git directory %s" %
                          self.vcs_path)

    def _log_git_cmd(self, output):
        """ Send output from a GitPython command to the debug log """
        for line in output.strip().splitlines():
            self.debug_log("Git: %s" % line)

    def get_revision(self):
        """Read git revision information for the Bcfg2 repository."""
        if HAS_GITPYTHON:
            return self.repo.head.commit.hexsha
        else:
            cmd = ["git", "--git-dir", self.vcs_path,
                   "--work-tree", Bcfg2.Options.setup.vcs_root,
                   "rev-parse", "HEAD"]
            self.debug_log("Git: Running %s" % cmd)
            result = self.cmd.run(cmd)
            if not result.success:
                raise PluginExecutionError(result.stderr)
            return result.stdout

    def Update(self, ref=None):
        """ Git.Update() => True|False
        Update the working copy against the upstream repository
        """
        self.logger.info("Git: Git.Update(ref='%s')" % ref)
        self.debug_log("Git: Performing garbage collection on repo at %s" %
                       Bcfg2.Options.setup.vcs_root)
        try:
            self._log_git_cmd(self.repo.git.gc('--auto'))
        except git.GitCommandError:
            self.logger.warning("Git: Failed to perform garbage collection: %s"
                                % sys.exc_info()[1])

        self.debug_log("Git: Fetching all refs for repo at %s" %
                       Bcfg2.Options.setup.vcs_root)
        try:
            self._log_git_cmd(self.repo.git.fetch('--all'))
        except git.GitCommandError:
            self.logger.warning("Git: Failed to fetch refs: %s" %
                                sys.exc_info()[1])

        if ref:
            self.debug_log("Git: Checking out %s" % ref)
            try:
                self._log_git_cmd(self.repo.git.checkout('-f', ref))
            except git.GitCommandError:
                raise PluginExecutionError("Git: Failed to checkout %s: %s" %
                                           (ref, sys.exc_info()[1]))

        # determine if we should try to pull to get the latest commit
        # on this head
        tracking = None
        if not self.repo.head.is_detached:
            self.debug_log("Git: Determining if %s is a tracking branch" %
                           self.repo.head.ref.name)
            tracking = self.repo.head.ref.tracking_branch()

        if tracking is not None:
            self.debug_log("Git: %s is a tracking branch, pulling from %s" %
                           (self.repo.head.ref.name, tracking))
            try:
                self._log_git_cmd(self.repo.git.pull("--rebase"))
            except git.GitCommandError:
                raise PluginExecutionError("Git: Failed to pull from "
                                           "upstream: %s" % sys.exc_info()[1])

        self.logger.info("Git: Repo at %s updated to %s" %
                         (Bcfg2.Options.setup.vcs_root, self.get_revision()))
        return True