From aa0de421d2948c6552770534381e58d6acae7997 Mon Sep 17 00:00:00 2001 From: Jordan Borean Date: Tue, 23 Jul 2019 07:52:30 +1000 Subject: [PATCH] Ignore collection build release files in the root collection directory (#59121) --- lib/ansible/galaxy/collection.py | 19 +++++++++----- test/units/galaxy/test_collection.py | 38 +++++++++++++++++++++++++--- 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/lib/ansible/galaxy/collection.py b/lib/ansible/galaxy/collection.py index df164c1305c..592aa70af90 100644 --- a/lib/ansible/galaxy/collection.py +++ b/lib/ansible/galaxy/collection.py @@ -358,7 +358,7 @@ def build_collection(collection_path, output_path, force): raise AnsibleError("The collection galaxy.yml path '%s' does not exist." % to_native(b_galaxy_path)) collection_meta = _get_galaxy_yml(b_galaxy_path) - file_manifest = _build_files_manifest(b_collection_path) + file_manifest = _build_files_manifest(b_collection_path, collection_meta['namespace'], collection_meta['name']) collection_manifest = _build_manifest(**collection_meta) collection_output = os.path.join(output_path, "%s-%s-%s.tar.gz" % (collection_meta['namespace'], @@ -587,9 +587,12 @@ def _get_galaxy_yml(b_galaxy_yml_path): return galaxy_yml -def _build_files_manifest(b_collection_path): - b_ignore_files = frozenset([b'*.pyc', b'*.retry']) - b_ignore_dirs = frozenset([b'CVS', b'.bzr', b'.hg', b'.git', b'.svn', b'__pycache__', b'.tox']) +def _build_files_manifest(b_collection_path, namespace, name): + # Contains tuple of (b_filename, only root) where 'only root' means to only ignore the file in the root dir + b_ignore_files = frozenset([(b'*.pyc', False), (b'*.retry', False), + (to_bytes('{0}-{1}-*.tar.gz'.format(namespace, name)), True)]) + b_ignore_dirs = frozenset([(b'CVS', False), (b'.bzr', False), (b'.hg', False), (b'.git', False), (b'.svn', False), + (b'__pycache__', False), (b'.tox', False)]) entry_template = { 'name': None, @@ -612,13 +615,16 @@ def _build_files_manifest(b_collection_path): } def _walk(b_path, b_top_level_dir): + is_root = b_path == b_top_level_dir + for b_item in os.listdir(b_path): b_abs_path = os.path.join(b_path, b_item) b_rel_base_dir = b'' if b_path == b_top_level_dir else b_path[len(b_top_level_dir) + 1:] rel_path = to_text(os.path.join(b_rel_base_dir, b_item), errors='surrogate_or_strict') if os.path.isdir(b_abs_path): - if b_item in b_ignore_dirs: + if any(b_item == b_path for b_path, root_only in b_ignore_dirs + if not root_only or root_only == is_root): display.vvv("Skipping '%s' for collection build" % to_text(b_abs_path)) continue @@ -640,7 +646,8 @@ def _build_files_manifest(b_collection_path): else: if b_item == b'galaxy.yml': continue - elif any(fnmatch.fnmatch(b_item, b_pattern) for b_pattern in b_ignore_files): + elif any(fnmatch.fnmatch(b_item, b_pattern) for b_pattern, root_only in b_ignore_files + if not root_only or root_only == is_root): display.vvv("Skipping '%s' for collection build" % to_text(b_abs_path)) continue diff --git a/test/units/galaxy/test_collection.py b/test/units/galaxy/test_collection.py index 02441baa185..2f7c498aff1 100644 --- a/test/units/galaxy/test_collection.py +++ b/test/units/galaxy/test_collection.py @@ -263,7 +263,7 @@ def test_build_ignore_files_and_folders(collection_input, monkeypatch): ignore_file.write('random') ignore_file.flush() - actual = collection._build_files_manifest(to_bytes(input_dir)) + actual = collection._build_files_manifest(to_bytes(input_dir), 'namespace', 'collection') assert actual['format'] == 1 for manifest_entry in actual['files']: @@ -278,6 +278,38 @@ def test_build_ignore_files_and_folders(collection_input, monkeypatch): assert mock_display.mock_calls[1][1][0] in expected_msgs +def test_build_ignore_older_release_in_root(collection_input, monkeypatch): + input_dir = collection_input[0] + + mock_display = MagicMock() + monkeypatch.setattr(Display, 'vvv', mock_display) + + # This is expected to be ignored because it is in the root collection dir. + release_file = os.path.join(input_dir, 'namespace-collection-0.0.0.tar.gz') + + # This is not expected to be ignored because it is not in the root collection dir. + fake_release_file = os.path.join(input_dir, 'plugins', 'namespace-collection-0.0.0.tar.gz') + + for filename in [release_file, fake_release_file]: + with open(filename, 'w+') as file_obj: + file_obj.write('random') + file_obj.flush() + + actual = collection._build_files_manifest(to_bytes(input_dir), 'namespace', 'collection') + assert actual['format'] == 1 + + plugin_release_found = False + for manifest_entry in actual['files']: + assert manifest_entry['name'] != 'namespace-collection-0.0.0.tar.gz' + if manifest_entry['name'] == 'plugins/namespace-collection-0.0.0.tar.gz': + plugin_release_found = True + + assert plugin_release_found + + assert mock_display.call_count == 1 + assert mock_display.mock_calls[0][1][0] == "Skipping '%s' for collection build" % to_text(release_file) + + def test_build_ignore_symlink_target_outside_collection(collection_input, monkeypatch): input_dir, outside_dir = collection_input @@ -287,7 +319,7 @@ def test_build_ignore_symlink_target_outside_collection(collection_input, monkey link_path = os.path.join(input_dir, 'plugins', 'connection') os.symlink(outside_dir, link_path) - actual = collection._build_files_manifest(to_bytes(input_dir)) + actual = collection._build_files_manifest(to_bytes(input_dir), 'namespace', 'collection') for manifest_entry in actual['files']: assert manifest_entry['name'] != 'plugins/connection' @@ -311,7 +343,7 @@ def test_build_copy_symlink_target_inside_collection(collection_input): os.symlink(roles_target, roles_link) - actual = collection._build_files_manifest(to_bytes(input_dir)) + actual = collection._build_files_manifest(to_bytes(input_dir), 'namespace', 'collection') linked_entries = [e for e in actual['files'] if e['name'].startswith('playbooks/roles/linked')] assert len(linked_entries) == 3