diff --git a/packages/python-packages/doc-warden/warden/enforce_changelog_content.py b/packages/python-packages/doc-warden/warden/enforce_changelog_content.py index a7cc4df601..4e3bfdc25d 100644 --- a/packages/python-packages/doc-warden/warden/enforce_changelog_content.py +++ b/packages/python-packages/doc-warden/warden/enforce_changelog_content.py @@ -12,7 +12,7 @@ import pathlib import logging -from .index_packages import get_python_package_info, get_js_package_info, get_java_package_info, get_net_packages_info +from .index_packages import get_python_package_info, get_js_package_info, get_java_package_info, get_net_package_info # entry point def verify_changelog_content(config, pkg_list): diff --git a/packages/python-packages/doc-warden/warden/enforce_target_file_presence.py b/packages/python-packages/doc-warden/warden/enforce_target_file_presence.py index d161fa465a..cd36e53061 100644 --- a/packages/python-packages/doc-warden/warden/enforce_target_file_presence.py +++ b/packages/python-packages/doc-warden/warden/enforce_target_file_presence.py @@ -9,7 +9,7 @@ import fnmatch import zipfile from pathlib import Path -from .warden_common import get_java_package_roots, get_net_packages, get_python_package_roots, get_js_package_roots, find_alongside_file +from .warden_common import get_java_package_roots, get_net_package, get_python_package_roots, get_swift_package_roots, get_js_package_roots, find_alongside_file # python 3 transitioned StringIO to be part of `io` module. # python 2 needs the old version however @@ -29,7 +29,8 @@ def find_missing_target_files(configuration): 'python': check_python_target_files, 'js': check_js_target_files, 'java': check_java_target_files, - 'net': check_net_target_files + 'net': check_net_target_files, + 'swift': check_swift_target_files } missing_target_file_paths = [] ignored_missing_target_file_paths = [] @@ -52,6 +53,18 @@ def check_repo_root(configuration): return any(x in [f.lower() for f in present_files] for x in configuration.target_files) return true +def check_swift_target_files(configuration): + expected_target_files, omitted_target_files = get_swift_package_roots(configuration) + missing_expected_target_file_locations = [] + + for expected_location in expected_target_files: + result = False + for target_file in configuration.target_files: + result = result or find_alongside_file(expected_location, target_file) + if not result: + missing_expected_target_file_locations.append(os.path.dirname(expected_location)) + return missing_expected_target_file_locations + # return all missing target_files for a PYTHON repostiroy def check_python_target_files(configuration): expected_target_files, omitted_target_files = get_python_package_roots(configuration) @@ -80,7 +93,7 @@ def check_js_target_files(configuration): # return all missing target_files for a .NET repostory def check_net_target_files(configuration): - expected_target_files, omitted_target_files = get_net_packages(configuration) + expected_target_files, omitted_target_files = get_net_package(configuration) missing_expected_target_file_locations = [] for expected_location in expected_target_files: diff --git a/packages/python-packages/doc-warden/warden/index_packages.py b/packages/python-packages/doc-warden/warden/index_packages.py index 45300d30ce..31ab6e9b25 100644 --- a/packages/python-packages/doc-warden/warden/index_packages.py +++ b/packages/python-packages/doc-warden/warden/index_packages.py @@ -3,7 +3,7 @@ from __future__ import print_function -from .warden_common import check_match, walk_directory_for_pattern, get_omitted_files, get_java_package_roots, get_net_packages, get_python_package_roots, get_js_package_roots, find_alongside_file, find_below_file, parse_pom, parse_csproj, is_java_pom_package_pom, find_above_file +from .warden_common import check_match, walk_directory_for_pattern, get_omitted_files, get_java_package_roots, get_net_package, get_swift_package_roots, get_python_package_roots, get_js_package_roots, find_alongside_file, find_below_file, parse_pom, parse_csproj, is_java_pom_package_pom, find_above_file from .PackageInfo import PackageInfo import json import os @@ -13,6 +13,7 @@ import textwrap import re import fnmatch +import pathlib # python 3 transitioned StringIO to be part of `io` module. # python 2 needs the old version however @@ -66,11 +67,65 @@ def index_packages(configuration): 'python': get_python_package_info, 'js': get_js_package_info, 'java': get_java_package_info, - 'net': get_net_packages_info + 'net': get_net_package_info, + 'swift': get_swift_package_info } return language_selector.get(configuration.scan_language.lower(), unrecognized_option)(configuration) +def get_swift_package_id_from_directory(directory): + package_id = pathlib.Path(directory).name + return package_id + +def get_swift_package_info(config): + pkg_list = [] + pkg_locations, ignored_pkg_locations = get_swift_package_roots(config) + + for pkg_file in (pkg_locations + ignored_pkg_locations): + + pkg_id = get_swift_package_id_from_directory(pkg_file) + + changelog = os.path.join(pkg_file, 'CHANGELOG.md') + changelog_relpath = webify_relative_path(os.path.relpath(changelog, config.target_directory)) + + readme = os.path.join(pkg_file, 'README.md') + readme_relpath = webify_relative_path(os.path.relpath(readme, config.target_directory)) + + pkg_location = webify_relative_path(os.path.relpath(pkg_file, config.target_directory)) + + # The way I am determining the package version is by lifting the marketing version + # from the project.pbxproj file. There isn't really a strong notion of semantic version + # in XCode projects beyond this marketing version from what I can tell. Perhaps if + # SwiftPM becomes more dominant and gets tooling support then that might change. + pbxproj_file_path = '{}/{}.xcodeproj/project.pbxproj'.format(pkg_file, pkg_id) + with open(pbxproj_file_path) as pbxproj_file: + pbxproj_file_contents = pbxproj_file.read() + + # This is a pretty simple expression, it grabs the strings that are + # in the form: + # + # MARKETING_VERSION = "1.0.0-beta.1" + # + # It then uses a capture group name to zero in on the version. It + # doesn't attempt to validate the format of the version, it just takes + # the value between the quotes. + version_match_expression = 'MARKETING_VERSION = \"(?P(.*))\"' + search_result = re.search(version_match_expression, pbxproj_file_contents) + if search_result is None: + continue + else: + version = search_result.group(2) + + if(pkg_id not in config.package_indexing_exclusion_list): + pkg_list.append(PackageInfo( + package_id = pkg_id, + package_version = version, + relative_package_location = pkg_location, + relative_readme_location = readme_relpath or '', + relative_changelog_location = changelog_relpath or '', + repository_args = [] + )) + # leverages python AST to parse arguments to `setup.py` and return a list of all indexed packages # within the target directory def get_python_package_info(config): @@ -218,9 +273,9 @@ def get_java_package_info(config): # finds .net packages (non-test CSProjs) and attempts to correlate the packageinfo details # returns a list of all `packages` found within the target directory. -def get_net_packages_info(config): +def get_net_package_info(config): pkg_list = [] - pkg_locations, ignored_pkg_locations = get_net_packages(config) + pkg_locations, ignored_pkg_locations = get_net_package(config) for pkg_file in (pkg_locations + ignored_pkg_locations): pkg_version = parse_csproj(pkg_file) @@ -276,6 +331,7 @@ def webify_relative_path(path): # entrypoint for rendering the packages.md # handles the template selection and execution def render(config, pkg_list): + language_selector = { 'python': OUTPUT_TEMPLATE, 'js': OUTPUT_TEMPLATE, diff --git a/packages/python-packages/doc-warden/warden/warden_common.py b/packages/python-packages/doc-warden/warden/warden_common.py index fa9343756b..96486074d2 100644 --- a/packages/python-packages/doc-warden/warden/warden_common.py +++ b/packages/python-packages/doc-warden/warden/warden_common.py @@ -5,6 +5,7 @@ import fnmatch import re import xml.etree.ElementTree as ET +import pathlib # python 3 transitioned StringIO to be part of `io` module. # python 2 needs the old version however @@ -17,6 +18,7 @@ PYTHON_PACKAGE_DISCOVERY_PATTERN = '*/setup.py' NET_PACKAGE_DISCOVERY_PATTERN = '*.csproj' JAVA_PACKAGE_DISCOVERY_PATTERN = '*/pom.xml' +SWIFT_PACKAGE_DISCOVERY_PATTERN = '*/project.pbxproj' # we want to walk the files as few times as possible. as such, for omitted_files, we provide a SET # of patterns that we want to omit. This function simply checks @@ -32,7 +34,7 @@ def get_java_package_roots(configuration): return file_set -def get_net_packages(configuration): +def get_net_package(configuration): file_set = get_file_sets(configuration, NET_PACKAGE_DISCOVERY_PATTERN, is_net_csproj_package) if configuration.verbose_output: @@ -40,6 +42,29 @@ def get_net_packages(configuration): return file_set +def get_project_roots_from_pbxproj_paths(pbxproj_file_set): + project_roots = [] + for pbxproj_file in pbxproj_file_set: + pbxproj_file_path = pathlib.Path(pbxproj_file) + project_root_path = pbxproj_file_path.parents[1] + project_roots.append(str(project_root_path)) + + return project_roots + + +def get_swift_package_roots(configuration): + project_files, omitted_project_files = get_file_sets(configuration, SWIFT_PACKAGE_DISCOVERY_PATTERN) + + project_roots = get_project_roots_from_pbxproj_paths(project_files) + omitted_project_roots = get_project_roots_from_pbxproj_paths(omitted_project_files) + + file_set = project_roots, omitted_project_roots + + if configuration.verbose_output: + print(file_set) + + return file_set + def get_python_package_roots(configuration): file_set = get_file_sets(configuration, PYTHON_PACKAGE_DISCOVERY_PATTERN)