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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
|
""" The Git plugin provides a revision interface for Bcfg2 repos using
git. """
import os
import sys
import Bcfg2.Server.Plugin
class GitAPIBase(object):
""" Base class for the various Git APIs (dulwich, GitPython,
subprocesses) """
def __init__(self, path):
self.path = path
def revision(self):
""" Get the current revision of the git repo as a string """
raise NotImplementedError
def pull(self):
""" Pull the latest version of the upstream git repo and
rebase against it. """
raise NotImplementedError
try:
from dulwich.client import get_transport_and_path
from dulwich.repo import Repo
from dulwich.file import GitFile, ensure_dir_exists
class GitAPI(GitAPIBase):
""" API for :class:`Git` using :mod:`dulwich` """
def __init__(self, path):
GitAPIBase.__init__(self, path)
self.repo = Repo(self.path)
self.client, self.origin_path = get_transport_and_path(
self.repo.get_config().get(("remote", "origin"),
"url"))
def revision(self):
return self.repo.head()
def pull(self):
try:
remote_refs = self.client.fetch(
self.origin_path, self.repo,
determine_wants=self.repo.object_store.determine_wants_all)
except KeyError:
etype, err = sys.exc_info()[:2]
# try to work around bug
# https://bugs.launchpad.net/dulwich/+bug/1025886
try:
# pylint: disable=W0212
self.client._fetch_capabilities.remove('thin-pack')
# pylint: enable=W0212
except KeyError:
raise etype(err)
remote_refs = self.client.fetch(
self.origin_path, self.repo,
determine_wants=self.repo.object_store.determine_wants_all)
tree_id = self.repo[remote_refs['HEAD']].tree
# iterate over tree content, giving path and blob sha.
for entry in self.repo.object_store.iter_tree_contents(tree_id):
entry_in_path = entry.in_path(self.repo.path)
ensure_dir_exists(os.path.split(entry_in_path.path)[0])
GitFile(entry_in_path.path,
'wb').write(self.repo[entry.sha].data)
except ImportError:
try:
import git
class GitAPI(GitAPIBase):
""" API for :class:`Git` using :mod:`git` (GitPython) """
def __init__(self, path):
GitAPIBase.__init__(self, path)
self.repo = git.Repo(path)
def revision(self):
return self.repo.head.commit.hexsha
def pull(self):
self.repo.git.pull("--rebase")
except ImportError:
from subprocess import Popen, PIPE
try:
Popen(["git"], stdout=PIPE, stderr=PIPE).wait()
class GitAPI(GitAPIBase):
""" API for :class:`Git` using subprocess to run git
commands """
def revision(self):
proc = Popen(["git", "--work-tree",
os.path.join(self.path, ".git"),
"rev-parse", "HEAD"], stdout=PIPE,
stderr=PIPE)
rv, err = proc.communicate()
if proc.wait():
raise Exception("Git: Error getting revision from %s: "
"%s" % (self.path, err))
return rv.strip() # pylint: disable=E1103
def pull(self):
proc = Popen(["git", "--work-tree",
os.path.join(self.path, ".git"),
"pull", "--rebase"], stdout=PIPE,
stderr=PIPE)
err = proc.communicate()[1].strip()
if proc.wait():
raise Exception("Git: Error pulling: %s" % err)
except OSError:
raise ImportError("Could not import dulwich or GitPython "
"libraries, and no 'git' command found in PATH")
class Git(Bcfg2.Server.Plugin.Version):
""" The Git plugin provides a revision interface for Bcfg2 repos
using git. """
__author__ = 'bcfg-dev@mcs.anl.gov'
__vcs_metadata_path__ = ".git"
__rmi__ = Bcfg2.Server.Plugin.Version.__rmi__ + ['Update']
def __init__(self, core, datastore):
Bcfg2.Server.Plugin.Version.__init__(self, core, datastore)
self.repo = GitAPI(self.vcs_root)
self.logger.debug("Initialized git plugin with git directory %s" %
self.vcs_path)
def get_revision(self):
"""Read git revision information for the Bcfg2 repository."""
try:
return self.repo.revision()
except:
err = sys.exc_info()[1]
msg = "Failed to read git repository: %s" % err
self.logger.error(msg)
raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
def Update(self):
""" Git.Update() => True|False
Update the working copy against the upstream repository
"""
try:
self.repo.pull()
self.logger.info("Git repo at %s updated to %s" %
(self.vcs_root, self.get_revision()))
return True
except: # pylint: disable=W0702
err = sys.exc_info()[1]
msg = "Failed to pull from git repository: %s" % err
self.logger.error(msg)
raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
|