diff --git a/ebcli/core/fileoperations.py b/ebcli/core/fileoperations.py index 04f07155d..3a2f88c0a 100644 --- a/ebcli/core/fileoperations.py +++ b/ebcli/core/fileoperations.py @@ -110,6 +110,11 @@ def _get_option(config, section, key, default): def is_git_directory_present(): return os.path.isdir('.git') +def is_parent_directory_in_ignore_list(filepath, ignore_list): + for directory in ignore_list: + if os.path.abspath(filepath).startswith(os.path.abspath(directory)): + return True + return False def clean_up(): cwd = os.getcwd() @@ -423,7 +428,6 @@ def zip_up_project(location, ignore_list=None): finally: os.chdir(cwd) - def _zipdir(path, zipf, ignore_list=None): if ignore_list is None: ignore_list = {'.gitignore'} @@ -447,7 +451,10 @@ def _zipdir(path, zipf, ignore_list=None): zipInfo.external_attr = 2716663808 else: zipInfo.external_attr = long(2716663808) - zipf.writestr(zipInfo, os.readlink(cur_dir)) + if not is_parent_directory_in_ignore_list(cur_dir, ignore_list): + zipf.writestr(zipInfo, os.readlink(cur_dir)) + else: + io.log_info(' -skipping: {}'.format(cur_dir)) for f in files: cur_file = os.path.join(root, f) @@ -455,6 +462,7 @@ def _zipdir(path, zipf, ignore_list=None): cur_file.endswith('~') or cur_file in ignore_list or not _validate_file_for_archive(cur_file) + or is_parent_directory_in_ignore_list(cur_file, ignore_list) ): # Ignore editor backup files (like file.txt~) # Ignore anything in the .ebignore file @@ -812,7 +820,8 @@ def get_ebignore_list(): with codecs.open(location, 'r', encoding='utf-8') as f: spec = PathSpec.from_lines('gitwildmatch', f) - ignore_list = {f for f in spec.match_tree(get_project_root())} + matches = [f for f in spec.match_tree_entries(get_project_root())] + ignore_list = {match.path for match in matches} ignore_list.add('.ebignore') return ignore_list diff --git a/requirements.txt b/requirements.txt index fba1f9310..335116526 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ botocore>1.23.41,<1.27.80 cement==2.8.2 colorama>=0.2.5,<0.4.4 # use the same range that 'docker-compose' uses future>=0.16.0,<0.17.0 -pathspec==0.9.0 +pathspec==0.10.1 python-dateutil>=2.1,<3.0.0 # use the same range that 'botocore' uses requests>=2.20.1,<=2.26 setuptools >= 20.0 diff --git a/tests/unit/core/test_fileoperations.py b/tests/unit/core/test_fileoperations.py index acf3edf29..b15bc9693 100644 --- a/tests/unit/core/test_fileoperations.py +++ b/tests/unit/core/test_fileoperations.py @@ -717,24 +717,33 @@ def test_delete_app_file(self): def test_zip_up_project(self, _validate_file_for_archive_mock): _validate_file_for_archive_mock.side_effect = lambda f: not f.endswith('.sock') shutil.rmtree('home', ignore_errors=True) + os.mkdir('ignore-me') + os.mkdir('linkme') os.mkdir('src') os.mkdir(os.path.join('src', 'lib')) open(os.path.join('src', 'lib', 'app.py'), 'w').write('import os') open(os.path.join('src', 'lib', 'app.py~'), 'w').write('import os') open(os.path.join('src', 'lib', 'ignore-this-file.py'), 'w').write('import os') open(os.path.join('src', 'lib', 'test.sock'), 'w').write('mock socket file') + open(os.path.join('ignore-me', 'text.txt'), 'w').write('import os') + open(os.path.join('linkme', 'foobar.txt'), 'w').write('import os linkme') os.symlink( - os.path.join('src', 'lib', 'app.py'), - os.path.join('src', 'lib', 'app.py-copy') + os.path.abspath(os.path.join('src', 'lib', 'app.py')), + os.path.abspath(os.path.join('src', 'lib', 'app.py-copy')) ) os.mkdir(os.path.join('src', 'lib', 'api')) if sys.version_info > (3, 0): os.symlink( - os.path.join('src', 'lib', 'api'), - os.path.join('src', 'lib', 'api-copy'), + os.path.abspath(os.path.join('src', 'lib', 'api')), + os.path.abspath(os.path.join('src', 'lib', 'api-copy')), + target_is_directory=True + ) + os.symlink( + os.path.abspath(os.path.join('linkme')), + os.path.abspath(os.path.join('ignore-me','linkme')), target_is_directory=True ) else: @@ -747,7 +756,7 @@ def test_zip_up_project(self, _validate_file_for_archive_mock): fileoperations.zip_up_project( 'app.zip', - ignore_list=[os.path.join('src', 'lib', 'ignore-this-file.py')] + ignore_list=[os.path.join('src', 'lib', 'ignore-this-file.py'), os.path.join('ignore-me/')] ) os.mkdir('tmp') @@ -760,6 +769,7 @@ def test_zip_up_project(self, _validate_file_for_archive_mock): self.assertFalse(os.path.exists(os.path.join('tmp', 'src', 'lib', 'app.py~'))) self.assertFalse(os.path.exists(os.path.join('tmp', 'src', 'lib', 'ignore-this-file.py'))) self.assertFalse(os.path.exists(os.path.join('tmp', 'src', 'lib', 'test.sock'))) + self.assertFalse(os.path.exists(os.path.join('tmp','ignore-me', 'link-me'))) def test_delete_app_versions(self): os.mkdir(os.path.join('.elasticbeanstalk', 'app_versions')) diff --git a/tests/unit/operations/test_ebignore.py b/tests/unit/operations/test_ebignore.py index bfa400791..dd299a3a5 100644 --- a/tests/unit/operations/test_ebignore.py +++ b/tests/unit/operations/test_ebignore.py @@ -140,6 +140,7 @@ def test_get_ebignore_list__includes_directories_in_the_ebignore_list( self.assertEqual( { 'directory_1{}.gitkeep'.format(os.path.sep), + 'directory_1', 'directory_2{}.gitkeep'.format(os.path.sep), '.ebignore' }, @@ -206,7 +207,7 @@ def test_get_ebignore_list__includes_filenames_with_spaces_in_ignore_list( open('file 1', 'w').close() with open('.ebignore', 'w') as file: - ebignore_file_contents = """ + ebignore_file_contents = r""" file\ 1 """ @@ -234,7 +235,7 @@ def test_get_ebignore_list__includes_filenames_with_exclamations_in_ignore_list( open('!file_1', 'w').close() with open('.ebignore', 'w') as file: - ebignore_file_contents = """ + ebignore_file_contents = r""" \!file_1 """ @@ -265,7 +266,7 @@ def test_get_ebignore_list__includes_files_with_unicode_names( open('ändrar något i databasen', 'w').close() with open('.ebignore', 'wb') as file: - ebignore_file_contents = """ + ebignore_file_contents = r""" 哈哈 昨夜のコンサートは最高でした。 ändrar\ något\ i\ databasen