diff --git a/changelogs/fragments/66067-git-archive_prefix-option.yaml b/changelogs/fragments/66067-git-archive_prefix-option.yaml new file mode 100644 index 00000000000..85d55b6fcf6 --- /dev/null +++ b/changelogs/fragments/66067-git-archive_prefix-option.yaml @@ -0,0 +1,2 @@ +minor_changes: + - git - added an ``archive_prefix`` option to set a prefix to add to each file path in archive diff --git a/lib/ansible/modules/source_control/git.py b/lib/ansible/modules/source_control/git.py index 29529e56c5e..ef940da8cc8 100644 --- a/lib/ansible/modules/source_control/git.py +++ b/lib/ansible/modules/source_control/git.py @@ -163,6 +163,12 @@ options: all git servers support git archive. version_added: "2.4" + archive_prefix: + description: + - Specify a prefix to add to each file path in archive. Requires C(archive) to be specified. + version_added: "2.10" + type: str + separate_git_dir: description: - The path to place the cloned repository. If specified, Git repository @@ -972,10 +978,12 @@ def git_version(git_path, module): return LooseVersion(rematch.groups()[0]) -def git_archive(git_path, module, dest, archive, archive_fmt, version): +def git_archive(git_path, module, dest, archive, archive_fmt, archive_prefix, version): """ Create git archive in given source directory """ - cmd = "%s archive --format=%s --output=%s %s" \ - % (git_path, archive_fmt, archive, version) + cmd = [git_path, 'archive', '--format', archive_fmt, '--output', archive, version] + if archive_prefix is not None: + cmd.insert(-1, '--prefix') + cmd.insert(-1, archive_prefix) (rc, out, err) = module.run_command(cmd, cwd=dest) if rc != 0: module.fail_json(msg="Failed to perform archive operation", @@ -985,7 +993,7 @@ def git_archive(git_path, module, dest, archive, archive_fmt, version): return rc, out, err -def create_archive(git_path, module, dest, archive, version, repo, result): +def create_archive(git_path, module, dest, archive, archive_prefix, version, repo, result): """ Helper function for creating archive using git_archive """ all_archive_fmt = {'.zip': 'zip', '.gz': 'tar.gz', '.tar': 'tar', '.tgz': 'tgz'} @@ -1007,7 +1015,7 @@ def create_archive(git_path, module, dest, archive, version, repo, result): 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) + git_archive(git_path, module, dest, new_archive, archive_fmt, archive_prefix, version) # filecmp is supposed to be efficient than md5sum checksum if filecmp.cmp(new_archive, archive): @@ -1029,7 +1037,7 @@ def create_archive(git_path, module, dest, archive, version, repo, result): % to_text(e)) else: # Perform archive from local directory - git_archive(git_path, module, dest, archive, archive_fmt, version) + git_archive(git_path, module, dest, archive, archive_fmt, archive_prefix, version) result.update(changed=True) @@ -1059,9 +1067,11 @@ def main(): track_submodules=dict(default='no', type='bool'), umask=dict(default=None, type='raw'), archive=dict(type='path'), + archive_prefix=dict(), separate_git_dir=dict(type='path'), ), mutually_exclusive=[('separate_git_dir', 'bare')], + required_by={'archive_prefix': ['archive']}, supports_check_mode=True ) @@ -1083,6 +1093,7 @@ def main(): ssh_opts = module.params['ssh_opts'] umask = module.params['umask'] archive = module.params['archive'] + archive_prefix = module.params['archive_prefix'] separate_git_dir = module.params['separate_git_dir'] result = dict(changed=False, warnings=list()) @@ -1186,7 +1197,7 @@ def main(): result.update(changed=True) module.exit_json(**result) - create_archive(git_path, module, dest, archive, version, repo, result) + create_archive(git_path, module, dest, archive, archive_prefix, version, repo, result) module.exit_json(**result) else: @@ -1260,7 +1271,7 @@ def main(): result.update(changed=True) module.exit_json(**result) - create_archive(git_path, module, dest, archive, version, repo, result) + create_archive(git_path, module, dest, archive, archive_prefix, version, repo, result) module.exit_json(**result) diff --git a/test/integration/targets/git/handlers/main.yml b/test/integration/targets/git/handlers/main.yml index a96627adc98..592ea394d0b 100644 --- a/test/integration/targets/git/handlers/main.yml +++ b/test/integration/targets/git/handlers/main.yml @@ -1,3 +1,5 @@ +# TODO remove everything we'd installed (see git_required_packages), not just git +# problem is that we should not remove what we hadn't installed - name: remove git package: name: git diff --git a/test/integration/targets/git/tasks/archive.yml b/test/integration/targets/git/tasks/archive.yml index c0d125cbeb8..8c5e5ebcab1 100644 --- a/test/integration/targets/git/tasks/archive.yml +++ b/test/integration/targets/git/tasks/archive.yml @@ -74,3 +74,62 @@ when: - "ansible_os_family == 'RedHat'" - ansible_distribution_major_version is version('7', '>=') + +- name: ARCHIVE | Inspect archive file + command: + cmd: "{{ git_list_commands[item] }} {{ checkout_dir }}/test_role.{{ item }}" + warn: no + register: archive_content + with_items: "{{ git_archive_extensions[ansible_os_family ~ ansible_distribution_major_version | default('default') ] | default(git_archive_extensions.default) }}" + +# Does not work on RedHat6 (jinja2 too old?) +- name: ARCHIVE | Ensure archive content is correct + assert: + that: + - item.stdout_lines | sort | first == 'defaults/' + with_items: "{{ archive_content.results }}" + when: + - ansible_os_family ~ ansible_distribution_major_version != 'RedHat6' + +- name: ARCHIVE | Clear checkout_dir + file: + state: absent + path: "{{ checkout_dir }}" + +- name: ARCHIVE | Generate an archive prefix + set_fact: + git_archive_prefix: '{{ range(2 ** 31, 2 ** 32) | random }}' # Generate some random archive prefix + +- name: ARCHIVE | Archive repo using various archival format and with an archive prefix + git: + repo: '{{ repo_format1 }}' + dest: '{{ checkout_dir }}' + archive: '{{ checkout_dir }}/test_role.{{ item }}' + archive_prefix: '{{ git_archive_prefix }}/' + register: git_archive + with_items: "{{ git_archive_extensions[ansible_os_family ~ ansible_distribution_major_version | default('default') ] | default(git_archive_extensions.default) }}" + +- name: ARCHIVE | Prepare the target for archive(s) extraction + file: + state: directory + path: '{{ checkout_dir }}/{{ git_archive_prefix }}.{{ item }}' + with_items: "{{ git_archive_extensions[ansible_os_family ~ ansible_distribution_major_version | default('default') ] | default(git_archive_extensions.default) }}" + +- name: ARCHIVE | Extract the archive(s) into that target + unarchive: + src: '{{ checkout_dir }}/test_role.{{ item }}' + dest: '{{ checkout_dir }}/{{ git_archive_prefix }}.{{ item }}' + with_items: "{{ git_archive_extensions[ansible_os_family ~ ansible_distribution_major_version | default('default') ] | default(git_archive_extensions.default) }}" + +- name: ARCHIVE | Check if prefix directory exists in what's extracted + find: + path: '{{ checkout_dir }}/{{ git_archive_prefix }}.{{ item }}' + patterns: '{{ git_archive_prefix }}' + file_type: directory + register: archive_check + with_items: "{{ git_archive_extensions[ansible_os_family ~ ansible_distribution_major_version | default('default') ] | default(git_archive_extensions.default) }}" + +- name: ARCHIVE | Assert that prefix directory is found + assert: + that: '{{ item.matched == 1 }}' + with_items: "{{ archive_check.results }}" diff --git a/test/integration/targets/git/tasks/setup.yml b/test/integration/targets/git/tasks/setup.yml index 0e56e8b0175..109798e3844 100644 --- a/test/integration/targets/git/tasks/setup.yml +++ b/test/integration/targets/git/tasks/setup.yml @@ -10,11 +10,12 @@ - name: SETUP | install git package: - name: git + name: '{{ item }}' when: ansible_distribution != "MacOSX" notify: - remove git - remove git from FreeBSD + with_items: "{{ git_required_packages[ansible_os_family | default('default') ] | default(git_required_packages.default) }}" - name: SETUP | verify that git is installed so this test can continue shell: which git diff --git a/test/integration/targets/git/vars/main.yml b/test/integration/targets/git/vars/main.yml index ea9dae268c4..a5bae5ba7f2 100644 --- a/test/integration/targets/git/vars/main.yml +++ b/test/integration/targets/git/vars/main.yml @@ -8,6 +8,24 @@ git_archive_extensions: - tar - zip +git_required_packages: + default: + - git + - gzip + - tar + - unzip + - zip + FreeBSD: + - git + - gzip + - unzip + - zip + +git_list_commands: + tar.gz: tar -tf + tar: tar -tf + tgz: tar -tf + zip: unzip -Z1 checkout_dir: '{{ output_dir }}/git' repo_dir: '{{ output_dir }}/local_repos'