Fuller example implementation of GitFileRepo

This commit is contained in:
Greg Neagle
2017-03-04 20:00:53 -08:00
parent ba0cb7c836
commit 8d6b468d9d
+125 -2
View File
@@ -1,10 +1,133 @@
import os
import pwd
import subprocess
import sys
from munkilib.munkirepo.FileRepo import FileRepo
from FileRepo import FileRepo
class MunkiGit(object):
"""A simple interface for some common interactions with the git binary"""
def __init__(self, repo):
self.cmd = '/usr/bin/git'
self.git_repo_dir = os.getcwd()
self.munki_repo_dir = repo.root
self.args = []
self.results = {}
def run_git(self, custom_args=None):
"""Executes the git command with the current set of arguments and
returns a dictionary with the keys 'output', 'error', and
'returncode'. You can optionally pass an array into customArgs to
override the self.args value without overwriting them."""
custom_args = self.args if custom_args == None else custom_args
proc = subprocess.Popen([self.cmd] + custom_args,
shell=False,
bufsize=-1,
cwd=self.git_repo_dir,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
(output, error) = proc.communicate()
self.results = {"output": output,
"error": error, "returncode": proc.returncode}
return self.results
def path_is_gitignored(self, a_path):
"""Returns True if path will be ignored by Git (usually due to being
in a .gitignore file)"""
self.git_repo_dir = os.path.dirname(a_path)
self.run_git(['check-ignore', a_path])
return self.results['returncode'] == 0
def path_is_in_git_repo(self, a_path):
"""Returns True if the path is in a Git repo, false otherwise."""
self.git_repo_dir = os.path.dirname(a_path)
self.run_git(['status', '-z', a_path])
return self.results['returncode'] == 0
def commit_file_at_path(self, a_path):
"""Commits the file at 'a_path'. This method will also automatically
generate the commit log appropriate for the status of a_path where
status would be 'modified', 'new file', or 'deleted'"""
# get the status of the file at a_path
self.git_repo_dir = os.path.dirname(a_path)
status_results = self.run_git(['status', a_path])
status_output = status_results['output']
if status_output.find("new file:") != -1:
action = 'created'
elif status_output.find("modified:") != -1:
action = 'modified'
elif status_output.find("deleted:") != -1:
action = 'deleted'
else:
action = 'did something with'
# determine the path relative to self.munki_repo_dir
# for the file at a_path
itempath = a_path
if a_path.startswith(self.munki_repo_dir):
itempath = a_path[len(self.munki_repo_dir)+1:]
username = pwd.getpwuid(os.getuid()).pw_name
# generate the log message
log_msg = ('%s %s \'%s\' via Munki command-line tools'
% (username, action, itempath))
print "Doing git commit: %s" % log_msg
self.run_git(['commit', '-m', log_msg])
if self.results['returncode'] != 0:
print >> sys.stderr, "Failed to commit changes to %s" % a_path
print >> sys.stderr, self.results['error']
return -1
return 0
def _add_remove_file_at_path(self, a_path, operation):
"""Git adds or removes a file at a_path. operation must be either
'add' or 'rm'"""
if self.path_is_in_git_repo(a_path):
if not self.path_is_gitignored(a_path):
self.git_repo_dir = os.path.dirname(a_path)
self.run_git([operation, a_path])
if self.results['returncode'] == 0:
self.commit_file_at_path(a_path)
else:
print >> sys.stderr, "Git error: %s" % self.results['error']
else:
print >> sys.stderr, "%s is not in a git repo." % a_path
def add_file_at_path(self, a_path):
"""Commits a file to the Git repo."""
self._add_remove_file_at_path(a_path, 'add')
def delete_file_at_path(self, a_path):
"""Deletes a file from the filesystem and Git repo."""
self._add_remove_file_at_path(a_path, 'rm')
class GitFileRepo(FileRepo):
'''A subclass of FileRepo that does git commits for pkginfo files'''
def remove(self, path):
'''Removes a file from the repo.'''
result = os.remove(os.path.join(self.root, path))
if result == 0:
MunkiGit(self).delete_file_at_path(os.path.join(self.root, path))
return result
def unlink(self, path):
'''Removes a file from the repo.'''
self.remove(path)
def put(self, src, dest):
'''Copies a local file to the repo.'''
cmd = ['/bin/cp', src, os.path.join(self.root, dest)]
result = subprocess.call(cmd)
if result == 0:
MunkiGit(self).add_file_at_path(os.path.join(self.root, dest))
return result
class RepoFile(object):
def __init__(self, repo, repo_path, mode):
self.repo = repo
@@ -15,7 +138,7 @@ class GitFileRepo(FileRepo):
def __del__(self):
if 'w' in self.repo_mode:
print "Pretending to do a git commit on %s" % self.repo_path
MunkiGit(self.repo).add_file_at_path(self.repo_path)
def read(self):
return self.file.read()