From 4ee9cc4b3f0d2fccfa7eca8aa59ba39a37c5b0f3 Mon Sep 17 00:00:00 2001 From: Dag Wieers Date: Thu, 11 Aug 2016 19:27:32 +0200 Subject: [PATCH] Add diff mode support to git module (#3364) * Add diffmode support to git module This patch adds missing diffmode support to the git module. * Remodel get_diff() and calls to it As proposed by @abadger * Ensure we fetch the required object before performing a diff Also we handle the return code ourselves, so don't leave this up to run_command(). --- lib/ansible/modules/source_control/git.py | 78 +++++++++++++++++------ 1 file changed, 58 insertions(+), 20 deletions(-) diff --git a/lib/ansible/modules/source_control/git.py b/lib/ansible/modules/source_control/git.py index 6bcba63a990..acc52b28fdc 100644 --- a/lib/ansible/modules/source_control/git.py +++ b/lib/ansible/modules/source_control/git.py @@ -375,6 +375,25 @@ def reset(git_path, module, dest): cmd = "%s reset --hard HEAD" % (git_path,) return module.run_command(cmd, check_rc=True, cwd=dest) +def get_diff(module, git_path, dest, repo, remote, depth, bare, before, after): + ''' Return the difference between 2 versions ''' + if before == None: + return { 'prepared': '>> Newly checked out %s' % after } + elif before != after: + # Ensure we have the object we are referring to during git diff ! + fetch(git_path, module, repo, dest, after, remote, depth, bare, '') + cmd = '%s diff %s %s' % (git_path, before, after) + (rc, out, err) = module.run_command(cmd, cwd=dest) + if rc == 0 and out: + return { 'prepared': out } + elif rc == 0: + return { 'prepared': '>> No visual differences between %s and %s' % (before, after) } + elif err: + return { 'prepared': '>> Failed to get proper diff between %s and %s:\n>> %s' % (before, after, err) } + else: + return { 'prepared': '>> Failed to get proper diff between %s and %s' % (before, after) } + return {} + def get_remote_head(git_path, module, dest, version, remote, bare): cloning = False cwd = None @@ -761,8 +780,7 @@ def main(): key_file = module.params['key_file'] ssh_opts = module.params['ssh_opts'] - return_values = {} - return_values['warnings'] = [] + result = dict( warnings=list() ) # We screenscrape a huge amount of git commands so use C locale anytime we # call run_command() @@ -796,13 +814,13 @@ def main(): git_version_used = git_version(git_path, module) if depth is not None and git_version_used < LooseVersion('1.9.1'): - return_values['warnings'].append("Your git version is too old to fully support the depth argument. Falling back to full checkouts.") + result['warnings'].append("Your git version is too old to fully support the depth argument. Falling back to full checkouts.") depth = None recursive = module.params['recursive'] track_submodules = module.params['track_submodules'] - before = None + result.update(before=None) local_mods = False repo_updated = None if (dest and not os.path.exists(gitconfig)) or (not dest and not allow_clone): @@ -812,7 +830,12 @@ def main(): # In those cases we do an ls-remote if module.check_mode or not allow_clone: remote_head = get_remote_head(git_path, module, dest, version, repo, bare) - module.exit_json(changed=True, before=before, after=remote_head, **return_values) + result.update(changed=True, after=remote_head) + if module._diff: + diff = get_diff(module, git_path, dest, repo, remote, depth, bare, result['before'], result['after']) + if diff: + result['diff'] = diff + module.exit_json(**result) # there's no git config, so clone clone(git_path, module, repo, dest, remote, depth, version, bare, reference, refspec, verify_commit) repo_updated = True @@ -820,16 +843,17 @@ def main(): # Just return having found a repo already in the dest path # this does no checking that the repo is the actual repo # requested. - before = get_version(module, git_path, dest) - module.exit_json(changed=False, before=before, after=before, **return_values) + result['before'] = get_version(module, git_path, dest) + result.update(changed=False, after=result['before']) + module.exit_json(**result) else: # else do a pull local_mods = has_local_mods(module, git_path, dest, bare) - before = get_version(module, git_path, dest) + result['before'] = get_version(module, git_path, dest) if local_mods: # failure should happen regardless of check mode if not force: - module.fail_json(msg="Local modifications exist in repository (force=no).", **return_values) + module.fail_json(msg="Local modifications exist in repository (force=no).", **result) # if force and in non-check mode, do a reset if not module.check_mode: reset(git_path, module, dest) @@ -837,10 +861,14 @@ def main(): # exit if already at desired sha version set_remote_url(git_path, module, repo, dest, remote) remote_head = get_remote_head(git_path, module, dest, version, remote, bare) - if before == remote_head: + if result['before'] == remote_head: if local_mods: - module.exit_json(changed=True, before=before, after=remote_head, - msg="Local modifications exist", **return_values) + result.update(changed=True, after=remote_head, msg='Local modifications exist') + if module._diff: + diff = get_diff(module, git_path, dest, repo, remote, depth, bare, result['before'], result['after']) + if diff: + result['diff'] = diff + module.exit_json(**result) elif version == 'HEAD': # If the remote and local match and we're using the default of # HEAD (It's not a real tag) then exit early @@ -856,7 +884,12 @@ def main(): if repo_updated is None: if module.check_mode: - module.exit_json(changed=True, before=before, after=remote_head, **return_values) + result.update(changed=(result['before']!=remote_head), after=remote_head) + if module._diff: + diff = get_diff(module, git_path, dest, repo, remote, depth, bare, result['before'], result['after']) + if diff: + result['diff'] = diff + module.exit_json(**result) fetch(git_path, module, repo, dest, version, remote, depth, bare, refspec) repo_updated = True @@ -872,20 +905,25 @@ def main(): if module.check_mode: if submodules_updated: - module.exit_json(changed=True, before=before, after=remote_head, submodules_changed=True, **return_values) + result.update(changed=True, after=remote_head, submodules_changed=True) else: - module.exit_json(changed=False, before=before, after=remote_head, **return_values) + result.update(changed=False, after=remote_head) + module.exit_json(**result) if submodules_updated: # Switch to version specified submodule_update(git_path, module, dest, track_submodules) # determine if we changed anything - after = get_version(module, git_path, dest) + result['after'] = get_version(module, git_path, dest) - changed = False - if before != after or local_mods or submodules_updated: - changed = True + result.update(changed=False) + if result['before'] != result['after'] or local_mods or submodules_updated: + result.update(changed=True) + if module._diff: + diff = get_diff(module, git_path, dest, repo, remote, depth, bare, result['before'], result['after']) + if diff: + result['diff'] = diff # cleanup the wrapper script if ssh_wrapper: @@ -895,7 +933,7 @@ def main(): # No need to fail if the file already doesn't exist pass - module.exit_json(changed=changed, before=before, after=after, **return_values) + module.exit_json(**result) # import module snippets from ansible.module_utils.basic import *