Add archive option in git plugin (#23332)

* Add archive option in git plugin

This fix will add archive option in git plugin

Fixes https://github.com/ansible/ansible/issues/22943

Signed-off-by: Abhijeet Kasurde <akasurde@redhat.com>

* Update code as per code review comments

Signed-off-by: Abhijeet Kasurde <akasurde@redhat.com>
This commit is contained in:
Abhijeet Kasurde 2017-05-15 20:16:25 +05:30 committed by Sloane Hertel
parent 91969b8c67
commit e9672748cc

View file

@ -186,6 +186,15 @@ options:
to be installed. The commit MUST be signed and the public key MUST to be installed. The commit MUST be signed and the public key MUST
be trusted in the GPG trustdb. be trusted in the GPG trustdb.
archive:
required: false
version_added: "2.4"
description:
- Specify archive file path with extension. If specified, creates an
archive file of the specified format containing the tree structure
for the source tree.
Allowed archive formats ["zip", "tar.gz", "tar", "tgz"]
requirements: requirements:
- git>=1.7.1 (the command line tool) - git>=1.7.1 (the command line tool)
@ -228,6 +237,13 @@ EXAMPLES = '''
repo: https://github.com/ansible/ansible-examples.git repo: https://github.com/ansible/ansible-examples.git
dest: /src/ansible-examples dest: /src/ansible-examples
refspec: '+refs/pull/*:refs/heads/*' refspec: '+refs/pull/*:refs/heads/*'
# Example Create git archive from repo
- git:
repo: https://github.com/ansible/ansible-examples.git
dest: /src/ansible-examples
archive: /tmp/ansible-examples.zip
''' '''
RETURN = ''' RETURN = '''
@ -253,15 +269,18 @@ warnings:
sample: Your git version is too old to fully support the depth argument. Falling back to full checkouts. sample: Your git version is too old to fully support the depth argument. Falling back to full checkouts.
''' '''
import filecmp
import os import os
import re import re
import shlex import shlex
import stat import stat
import sys import sys
import shutil
import tempfile import tempfile
from distutils.version import LooseVersion from distutils.version import LooseVersion
from ansible.module_utils.basic import AnsibleModule, get_module_path from ansible.module_utils.basic import AnsibleModule, get_module_path
from ansible.module_utils.basic import get_exception
from ansible.module_utils.known_hosts import add_git_host_key from ansible.module_utils.known_hosts import add_git_host_key
from ansible.module_utils.six import b, string_types from ansible.module_utils.six import b, string_types
from ansible.module_utils._text import to_native from ansible.module_utils._text import to_native
@ -901,6 +920,68 @@ def git_version(git_path, module):
return LooseVersion(rematch.groups()[0]) return LooseVersion(rematch.groups()[0])
def git_archive(git_path, module, dest, archive, archive_fmt, version):
""" Create git archive in given source directory """
cmd = "%s archive --format=%s --output=%s %s" \
% (git_path, archive_fmt, archive, version)
(rc, out, err) = module.run_command(cmd, cwd=dest)
if rc != 0:
module.fail_json(msg="Failed to perform archive operation",
details="Git archive command failed to create "
"archive %s using %s directory."
"Error: %s" % (archive, dest, err))
return rc, out, err
def create_archive(git_path, module, dest, archive, version, repo, result):
""" Helper function for creating archive using git_archive """
all_archive_fmt = {'.zip': 'zip', '.gz': 'tar.gz', '.tar': 'tar',
'.tgz': 'tgz'}
_, archive_ext = os.path.splitext(archive)
archive_fmt = all_archive_fmt.get(archive_ext, None)
if archive_fmt is None:
module.fail_json(msg="Unable to get file extension from "
"archive file name : %s" % archive,
details="Please specify archive as filename with "
"extension. File extension can be one "
"of ['tar', 'tar.gz', 'zip', 'tgz']")
repo_name = repo.split("/")[-1].replace(".git", "")
if os.path.exists(archive):
# If git archive file exists, then compare it with new git archive file.
# if match, do nothing
# if does not match, then replace existing with temp archive file.
tempdir = tempfile.mkdtemp()
new_archive_dest = os.path.join(tempdir, repo_name)
new_archive = new_archive_dest + '.' + archive_fmt
git_archive(git_path, module, dest, new_archive, archive_fmt, version)
# filecmp is supposed to be efficient than md5sum checksum
if filecmp.cmp(new_archive, archive):
result.update(changed=False)
# Cleanup before exiting
try:
shutil.remove(tempdir)
except OSError:
pass
else:
try:
shutil.move(new_archive, archive)
shutil.remove(tempdir)
result.update(changed=True)
except OSError:
exception = get_exception()
module.fail_json(msg="Failed to move %s to %s" %
(new_archive, archive),
details="Error occured while moving : %s"
% exception)
else:
# Perform archive from local directory
git_archive(git_path, module, dest, archive, archive_fmt, version)
result.update(changed=True)
# =========================================== # ===========================================
def main(): def main():
@ -925,6 +1006,7 @@ def main():
recursive=dict(default='yes', type='bool'), recursive=dict(default='yes', type='bool'),
track_submodules=dict(default='no', type='bool'), track_submodules=dict(default='no', type='bool'),
umask=dict(default=None, type='raw'), umask=dict(default=None, type='raw'),
archive=dict(type='path'),
), ),
supports_check_mode=True supports_check_mode=True
) )
@ -945,6 +1027,7 @@ def main():
key_file = module.params['key_file'] key_file = module.params['key_file']
ssh_opts = module.params['ssh_opts'] ssh_opts = module.params['ssh_opts']
umask = module.params['umask'] umask = module.params['umask']
archive = module.params['archive']
result = dict(changed=False, warnings=list()) result = dict(changed=False, warnings=list())
@ -1003,6 +1086,7 @@ def main():
track_submodules = module.params['track_submodules'] track_submodules = module.params['track_submodules']
result.update(before=None) result.update(before=None)
local_mods = False local_mods = False
need_fetch = True need_fetch = True
if (dest and not os.path.exists(gitconfig)) or (not dest and not allow_clone): if (dest and not os.path.exists(gitconfig)) or (not dest and not allow_clone):
@ -1092,6 +1176,15 @@ def main():
if diff: if diff:
result['diff'] = diff result['diff'] = diff
if archive:
# Git archive is not supported by all git servers, so
# we will first clone and perform git archive from local directory
if module.check_mode:
result.update(changed=True)
module.exit_json(**result)
create_archive(git_path, module, dest, archive, version, repo, result)
# cleanup the wrapper script # cleanup the wrapper script
if ssh_wrapper: if ssh_wrapper:
try: try: