diff --git a/CHANGELOG.md b/CHANGELOG.md index a09650516..d73359ff0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - a@e check_auth will now try to refresh tokens 5 minutes before expiration instead of waiting for it to expire - `runway test` will now return a non-zero exit code if any non-required tests failed - `static-react` sample uses npm instead of yarn +- `yamllint` is now invoked using `runpy` instead of using `runway run-python` + +### Fixed +- issue where `yamllint` and `cfnlint` could not be imported/executed from the Pyinstaller executables ## [1.8.0] - 2020-05-16 ### Fixed diff --git a/runway.file.spec b/runway.file.spec index 2302e9232..848c2df1e 100644 --- a/runway.file.spec +++ b/runway.file.spec @@ -66,12 +66,13 @@ data_files = [ ] data_files.append(('{}/yamllint/conf'.format(get_distribution('yamllint').location), 'yamllint/conf/')) -data_files.append(('{}/cfnlint/data'.format(get_distribution('cfn-lint').location), - 'cfnlint/data/')) +data_files.append(('{}/cfnlint/rules'.format(get_distribution('cfn-lint').location), + 'cfnlint/rules/')) data_files.append(('{}/botocore/data'.format(get_distribution('botocore').location), 'botocore/data/')) data_files.append(('{}/awscli/data'.format(get_distribution('awscli').location), 'awscli/data/')) +data_files.extend(collect_data_files('cfnlint')) data_files.extend(collect_data_files('distutils')) data_files.extend(collect_data_files('pip')) data_files.extend(collect_data_files('wheel')) @@ -90,6 +91,8 @@ import awscli # noqa import botocore # noqa import pip # noqa import wheel # noqa +import yamllint # noqa +import cfnlint # noqa hiddenimports.extend(get_submodules(runway)) hiddenimports.extend(get_submodules(troposphere)) hiddenimports.extend(get_submodules(awacs)) @@ -98,6 +101,8 @@ hiddenimports.extend(get_submodules(botocore)) hiddenimports.extend(get_submodules(pip)) hiddenimports.extend(get_submodules(wheel)) hiddenimports.extend(get_submodules(distutils)) +hiddenimports.extend(get_submodules(yamllint)) +hiddenimports.extend(get_submodules(cfnlint)) # needed due to pkg_resources dropping python2 support # can be removed on the next pyinstaller release # https://github.com/pypa/setuptools/issues/1963#issuecomment-582084099 diff --git a/runway.folder.spec b/runway.folder.spec index 96b8a14c6..5e9fae022 100644 --- a/runway.folder.spec +++ b/runway.folder.spec @@ -60,19 +60,19 @@ def Entrypoint(dist, group, name, **kwargs): # noqa # files that are not explicitly imported but consumed at runtime # need to be included as data_files. data_files = [ - (os.path.join(CLI_PATH, 'templates'), './runway/templates/'), - (os.path.join(CLI_PATH, 'blueprints'), './runway/blueprints/'), - (os.path.join(CLI_PATH, 'hooks'), './runway/hooks/') + (os.path.join(CLI_PATH, 'templates'), './runway/templates'), + (os.path.join(CLI_PATH, 'blueprints'), './runway/blueprints'), + (os.path.join(CLI_PATH, 'hooks'), './runway/hooks') ] - data_files.append(('{}/yamllint/conf'.format(get_distribution('yamllint').location), 'yamllint/conf/')) -data_files.append(('{}/cfnlint/data'.format(get_distribution('cfn-lint').location), - 'cfnlint/data/')) +data_files.append(('{}/cfnlint/rules'.format(get_distribution('cfn-lint').location), + 'cfnlint/rules/')) data_files.append(('{}/botocore/data'.format(get_distribution('botocore').location), 'botocore/data/')) data_files.append(('{}/awscli/data'.format(get_distribution('awscli').location), 'awscli/data/')) +data_files.extend(collect_data_files('cfnlint')) data_files.extend(collect_data_files('distutils')) data_files.extend(collect_data_files('pip')) data_files.extend(collect_data_files('wheel')) @@ -91,6 +91,8 @@ import awscli # noqa import botocore # noqa import pip # noqa import wheel # noqa +import yamllint # noqa +import cfnlint # noqa hiddenimports.extend(get_submodules(runway)) hiddenimports.extend(get_submodules(troposphere)) hiddenimports.extend(get_submodules(awacs)) @@ -99,6 +101,8 @@ hiddenimports.extend(get_submodules(botocore)) hiddenimports.extend(get_submodules(pip)) hiddenimports.extend(get_submodules(wheel)) hiddenimports.extend(get_submodules(distutils)) +hiddenimports.extend(get_submodules(yamllint)) +hiddenimports.extend(get_submodules(cfnlint)) # needed due to pkg_resources dropping python2 support # can be removed on the next pyinstaller release # https://github.com/pypa/setuptools/issues/1963#issuecomment-582084099 diff --git a/runway/tests/handlers/yaml_lint.py b/runway/tests/handlers/yaml_lint.py index fd9c4fb17..73a84fd59 100644 --- a/runway/tests/handlers/yaml_lint.py +++ b/runway/tests/handlers/yaml_lint.py @@ -3,13 +3,11 @@ import glob import logging import os -import sys -import tempfile -from typing import Dict, Any, List # pylint: disable=unused-import +import runpy +from typing import Any, Dict, List # pylint: disable=unused-import from runway.tests.handlers.base import TestHandler -from runway.tests.handlers.script import ScriptHandler -from runway.util import change_dir +from runway.util import argv TYPE_NAME = 'yamllint' LOGGER = logging.getLogger('runway') @@ -31,23 +29,13 @@ def get_yaml_files_at_path(provided_path): return yaml_files + yml_files @classmethod - def get_yamllint_options(cls, path, quote_paths=True): - # type: (str, bool) -> List[str] + def get_yamllint_options(cls, path): + # type: (str) -> List[str] """Return yamllint option list.""" - dirs_to_scan = cls.get_dirs(path) - files_at_base = cls.get_yaml_files_at_path(path) yamllint_options = [] - if dirs_to_scan: - yamllint_options.extend( - ["\"%s\"" % x if quote_paths else x for x in dirs_to_scan] - ) - if files_at_base: - yamllint_options.extend( - ["\"%s\"" % x if quote_paths else x for x in files_at_base] - ) - - return yamllint_options + return yamllint_options + cls.get_dirs(path) + \ + cls.get_yaml_files_at_path(path) @classmethod def handle(cls, name, args): @@ -69,41 +57,7 @@ def handle(cls, name, args): ) yamllint_options = ["--config-file=%s" % yamllint_config] - yamllint_options.extend(cls.get_yamllint_options(base_dir, - not getattr(sys, 'frozen', False))) - - if getattr(sys, 'frozen', False): - # running in pyinstaller single-exe, so sys.executable will - # be the all-in-one Runway binary + yamllint_options.extend(cls.get_yamllint_options(base_dir)) - # This would be a little more natural if yamllint was imported - # directly, but that has unclear license implications so instead - # we'll generate the setuptools invocation script here and shell - # out to it - yamllint_invocation_script = ( - "import sys;" - "from yamllint.cli import run;" - "sys.argv = [%s];" - "sys.exit(run());" % ','.join( - "'%s'" % i for i in ['yamllint'] + yamllint_options - ) - ) - - temp_fd, temp_path = tempfile.mkstemp(prefix='yamllint') - os.close(temp_fd) - with open(temp_path, 'w') as fileobj: - fileobj.write(yamllint_invocation_script) - - yl_cmd = sys.executable + ' run-python ' + temp_path - else: - # traditional python execution - yl_cmd = "yamllint " + ' '.join(yamllint_options) - with change_dir(base_dir): - try: - ScriptHandler().handle( - 'yamllint', - {'commands': [yl_cmd]} - ) - finally: - if getattr(sys, 'frozen', False): - os.remove(temp_path) + with argv(*['yamllint'] + yamllint_options): + runpy.run_module('yamllint', run_name='__main__')