From ab76916b14784f92fbd7aee6e8d6fc5ee84b0e75 Mon Sep 17 00:00:00 2001 From: Maxwell G Date: Tue, 29 Nov 2022 09:12:47 -0600 Subject: galaxy: Add license_file to manifest directives (#79420) * galaxy: Add license_file to manifest directives * ag collection build: Test license handling This adds tests to ensure that - REUSE licensing files: .reuse/dep5, LICENSES/*, anyfile.license - galaxy.yml license_file are always included in the manifest. --- changelogs/fragments/79420-galaxy-manifest-license.yaml | 4 ++++ lib/ansible/galaxy/collection/__init__.py | 17 ++++++++++++----- .../ansible-galaxy-collection-cli/files/expected.txt | 6 ++++++ .../ansible-galaxy-collection-cli/files/galaxy.yml | 1 + .../files/make_collection_dir.py | 4 ++++ test/units/galaxy/test_collection.py | 10 +++++----- 6 files changed, 32 insertions(+), 10 deletions(-) create mode 100644 changelogs/fragments/79420-galaxy-manifest-license.yaml diff --git a/changelogs/fragments/79420-galaxy-manifest-license.yaml b/changelogs/fragments/79420-galaxy-manifest-license.yaml new file mode 100644 index 0000000000..3c95cda425 --- /dev/null +++ b/changelogs/fragments/79420-galaxy-manifest-license.yaml @@ -0,0 +1,4 @@ +--- +minor_changes: + - galaxy - include ``license_file`` in the default manifest directives + (https://github.com/ansible/ansible/pull-request/79420) diff --git a/lib/ansible/galaxy/collection/__init__.py b/lib/ansible/galaxy/collection/__init__.py index 1a0720c0b9..ffeba77dea 100644 --- a/lib/ansible/galaxy/collection/__init__.py +++ b/lib/ansible/galaxy/collection/__init__.py @@ -479,6 +479,7 @@ def build_collection(u_collection_path, u_output_path, force): collection_meta['name'], # type: ignore[arg-type] collection_meta['build_ignore'], # type: ignore[arg-type] collection_meta['manifest'], # type: ignore[arg-type] + collection_meta['license_file'], # type: ignore[arg-type] ) artifact_tarball_file_name = '{ns!s}-{name!s}-{ver!s}.tar.gz'.format( @@ -1062,8 +1063,9 @@ def _make_entry(name, ftype, chksum_type='sha256', chksum=None): } -def _build_files_manifest(b_collection_path, namespace, name, ignore_patterns, manifest_control): - # type: (bytes, str, str, list[str], dict[str, t.Any]) -> FilesManifestType +def _build_files_manifest(b_collection_path, namespace, name, ignore_patterns, + manifest_control, license_file): + # type: (bytes, str, str, list[str], dict[str, t.Any], t.Optional[str]) -> FilesManifestType if ignore_patterns and manifest_control is not Sentinel: raise AnsibleError('"build_ignore" and "manifest" are mutually exclusive') @@ -1073,14 +1075,15 @@ def _build_files_manifest(b_collection_path, namespace, name, ignore_patterns, m namespace, name, manifest_control, + license_file, ) return _build_files_manifest_walk(b_collection_path, namespace, name, ignore_patterns) -def _build_files_manifest_distlib(b_collection_path, namespace, name, manifest_control): - # type: (bytes, str, str, dict[str, t.Any]) -> FilesManifestType - +def _build_files_manifest_distlib(b_collection_path, namespace, name, manifest_control, + license_file): + # type: (bytes, str, str, dict[str, t.Any], t.Optional[str]) -> FilesManifestType if not HAS_DISTLIB: raise AnsibleError('Use of "manifest" requires the python "distlib" library') @@ -1124,6 +1127,9 @@ def _build_files_manifest_distlib(b_collection_path, namespace, name, manifest_c 'recursive-include plugins */**.py */**.license', ]) + if license_file: + directives.append(f'include {license_file}') + plugins = set(l.package.split('.')[-1] for d, l in get_all_plugin_loaders()) for plugin in sorted(plugins): if plugin in ('modules', 'module_utils'): @@ -1585,6 +1591,7 @@ def install_src(collection, b_collection_path, b_collection_output_path, artifac collection_meta['namespace'], collection_meta['name'], collection_meta['build_ignore'], collection_meta['manifest'], + collection_meta['license_file'], ) collection_output_path = _build_collection_dir( diff --git a/test/integration/targets/ansible-galaxy-collection-cli/files/expected.txt b/test/integration/targets/ansible-galaxy-collection-cli/files/expected.txt index 110009e385..6921829050 100644 --- a/test/integration/targets/ansible-galaxy-collection-cli/files/expected.txt +++ b/test/integration/targets/ansible-galaxy-collection-cli/files/expected.txt @@ -1,6 +1,11 @@ MANIFEST.json FILES.json README.rst +GPL +LICENSES/ +LICENSES/MIT.txt +.reuse/ +.reuse/dep5 changelogs/ docs/ playbooks/ @@ -88,6 +93,7 @@ plugins/test/bar.yml plugins/test/baz.yaml plugins/test/test.py plugins/vars/bar.yml +plugins/vars/bar.yml.license plugins/vars/baz.yaml plugins/vars/test.py roles/foo/ diff --git a/test/integration/targets/ansible-galaxy-collection-cli/files/galaxy.yml b/test/integration/targets/ansible-galaxy-collection-cli/files/galaxy.yml index 8f0ada0b82..140bf2a73a 100644 --- a/test/integration/targets/ansible-galaxy-collection-cli/files/galaxy.yml +++ b/test/integration/targets/ansible-galaxy-collection-cli/files/galaxy.yml @@ -2,6 +2,7 @@ namespace: ns name: col version: 1.0.0 readme: README.rst +license_file: GPL authors: - Ansible manifest: diff --git a/test/integration/targets/ansible-galaxy-collection-cli/files/make_collection_dir.py b/test/integration/targets/ansible-galaxy-collection-cli/files/make_collection_dir.py index 913a6f79e8..60c43cc70f 100644 --- a/test/integration/targets/ansible-galaxy-collection-cli/files/make_collection_dir.py +++ b/test/integration/targets/ansible-galaxy-collection-cli/files/make_collection_dir.py @@ -5,8 +5,12 @@ paths = [ 'ns-col-1.0.0.tar.gz', 'foo.txt', 'README.rst', + 'GPL', + 'LICENSES/MIT.txt', + '.reuse/dep5', 'artifacts/.gitkeep', 'plugins/vars/bar.yml', + 'plugins/vars/bar.yml.license', 'plugins/vars/baz.yaml', 'plugins/vars/test.py', 'plugins/vars/docs.md', diff --git a/test/units/galaxy/test_collection.py b/test/units/galaxy/test_collection.py index 28a69b2814..fa319835fb 100644 --- a/test/units/galaxy/test_collection.py +++ b/test/units/galaxy/test_collection.py @@ -596,7 +596,7 @@ def test_build_ignore_files_and_folders(collection_input, monkeypatch): tests_file.write('random') tests_file.flush() - actual = collection._build_files_manifest(to_bytes(input_dir), 'namespace', 'collection', [], Sentinel) + actual = collection._build_files_manifest(to_bytes(input_dir), 'namespace', 'collection', [], Sentinel, None) assert actual['format'] == 1 for manifest_entry in actual['files']: @@ -632,7 +632,7 @@ def test_build_ignore_older_release_in_root(collection_input, monkeypatch): file_obj.write('random') file_obj.flush() - actual = collection._build_files_manifest(to_bytes(input_dir), 'namespace', 'collection', [], Sentinel) + actual = collection._build_files_manifest(to_bytes(input_dir), 'namespace', 'collection', [], Sentinel, None) assert actual['format'] == 1 plugin_release_found = False @@ -660,7 +660,7 @@ def test_build_ignore_patterns(collection_input, monkeypatch): actual = collection._build_files_manifest(to_bytes(input_dir), 'namespace', 'collection', ['*.md', 'plugins/action', 'playbooks/*.j2'], - Sentinel) + Sentinel, None) assert actual['format'] == 1 expected_missing = [ @@ -711,7 +711,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), 'namespace', 'collection', [], Sentinel) + actual = collection._build_files_manifest(to_bytes(input_dir), 'namespace', 'collection', [], Sentinel, None) for manifest_entry in actual['files']: assert manifest_entry['name'] != 'plugins/connection' @@ -735,7 +735,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), 'namespace', 'collection', [], Sentinel) + actual = collection._build_files_manifest(to_bytes(input_dir), 'namespace', 'collection', [], Sentinel, None) linked_entries = [e for e in actual['files'] if e['name'].startswith('playbooks/roles/linked')] assert len(linked_entries) == 1 -- cgit v1.2.1