From 754b05ad991d4876574995e673d110f09149bd26 Mon Sep 17 00:00:00 2001 From: Ilya Kulakov Date: Mon, 13 Aug 2018 15:25:49 -0700 Subject: [PATCH 01/18] Remove debug lines from .travis.yml --- .travis.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 876a061..29b1d4c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -186,8 +186,6 @@ matrix: python: nightly install: - - which python - - ls env/bin - curl ${PIP_URL} | python - ${PIP} install -r requirements.txt From 6b7134ddd485f7e510a67a9701693f7ac1af196f Mon Sep 17 00:00:00 2001 From: Ilya Kulakov Date: Mon, 13 Aug 2018 15:32:19 -0700 Subject: [PATCH 02/18] Use pytest.param directly. --- test_git_archive_all.py | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/test_git_archive_all.py b/test_git_archive_all.py index 5214a3d..dfae7c8 100644 --- a/test_git_archive_all.py +++ b/test_git_archive_all.py @@ -18,13 +18,6 @@ def makedirs(p): raise -def pytest_param(*values, **kwargs): - try: - return pytest.param(*values, **kwargs) - except AttributeError: - return values - - @pytest.fixture def git_env(tmpdir_factory): """ @@ -141,49 +134,49 @@ def archive(self, path): } ignore_in_root = deepcopy(base) -ignore_in_root['.gitattributes'] = FileRecord('\ntests/__init__.py export-ignore') +ignore_in_root['.gitattributes'] = FileRecord('tests/__init__.py export-ignore') ignore_in_root['tests'] = DirRecord({ '__init__.py': FileRecord('#Complex is better than complicated.', excluded=True) }) ignore_in_submodule = deepcopy(base) -ignore_in_submodule['lib']['.gitattributes'] = FileRecord('\ntests/__init__.py export-ignore') +ignore_in_submodule['lib']['.gitattributes'] = FileRecord('tests/__init__.py export-ignore') ignore_in_submodule['lib']['tests'] = DirRecord({ '__init__.py': FileRecord('#Flat is better than nested.', excluded=True) }) ignore_in_nested_submodule = deepcopy(base) -ignore_in_nested_submodule['lib']['extra']['.gitattributes'] = FileRecord('\ntests/__init__.py export-ignore') +ignore_in_nested_submodule['lib']['extra']['.gitattributes'] = FileRecord('tests/__init__.py export-ignore') ignore_in_nested_submodule['lib']['extra']['tests'] = DirRecord({ '__init__.py': FileRecord('#Sparse is better than dense.', excluded=True) }) ignore_in_submodule_from_root = deepcopy(base) -ignore_in_submodule_from_root['.gitattributes'] = FileRecord('\nlib/tests/__init__.py export-ignore') +ignore_in_submodule_from_root['.gitattributes'] = FileRecord('lib/tests/__init__.py export-ignore') ignore_in_submodule_from_root['lib']['tests'] = DirRecord({ '__init__.py': FileRecord('#Readability counts.', excluded=True) }) ignore_in_nested_submodule_from_root = deepcopy(base) -ignore_in_nested_submodule_from_root['.gitattributes'] = FileRecord('\nlib/extra/tests/__init__.py export-ignore') +ignore_in_nested_submodule_from_root['.gitattributes'] = FileRecord('lib/extra/tests/__init__.py export-ignore') ignore_in_nested_submodule_from_root['lib']['extra']['tests'] = DirRecord({ '__init__.py': FileRecord('#Special cases aren\'t special enough to break the rules.', excluded=True) }) ignore_in_nested_submodule_from_submodule = deepcopy(base) -ignore_in_nested_submodule_from_submodule['lib']['.gitattributes'] = FileRecord('\nextra/tests/__init__.py export-ignore') +ignore_in_nested_submodule_from_submodule['lib']['.gitattributes'] = FileRecord('extra/tests/__init__.py export-ignore') ignore_in_nested_submodule_from_submodule['lib']['extra']['tests'] = DirRecord({ '__init__.py': FileRecord('#Although practicality beats purity.', excluded=True) }) @pytest.mark.parametrize('contents', [ - pytest_param(base, id='No Ignore'), - pytest_param(ignore_in_root, id='Ignore in Root'), - pytest_param(ignore_in_submodule, id='Ignore in Submodule'), - pytest_param(ignore_in_nested_submodule, id='Ignore in Nested Submodule'), - pytest_param(ignore_in_submodule_from_root, id='Ignore in Submodule from Root'), - pytest_param(ignore_in_nested_submodule_from_root, id='Ignore in Nested Submodule from Root'), + pytest.param(base, id='No Ignore'), + pytest.param(ignore_in_root, id='Ignore in Root'), + pytest.param(ignore_in_submodule, id='Ignore in Submodule'), + pytest.param(ignore_in_nested_submodule, id='Ignore in Nested Submodule'), + pytest.param(ignore_in_submodule_from_root, id='Ignore in Submodule from Root'), + pytest.param(ignore_in_nested_submodule_from_root, id='Ignore in Nested Submodule from Root'), ]) def test_ignore(contents, tmpdir, git_env): """ From d32b1cac9d0dfa44c3f3c25721781cb710d4d808 Mon Sep 17 00:00:00 2001 From: Ilya Kulakov Date: Mon, 13 Aug 2018 15:32:32 -0700 Subject: [PATCH 03/18] Add test case for -export-ignore. --- test_git_archive_all.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test_git_archive_all.py b/test_git_archive_all.py index dfae7c8..6fcbe92 100644 --- a/test_git_archive_all.py +++ b/test_git_archive_all.py @@ -169,6 +169,12 @@ def archive(self, path): '__init__.py': FileRecord('#Although practicality beats purity.', excluded=True) }) +unset_export_ignore = deepcopy(base) +unset_export_ignore['.gitattributes'] = FileRecord('.* export-ignore\n*.htaccess -export-ignore', excluded=True) +unset_export_ignore['.a'] = FileRecord('Errors should never pass silently.', excluded=True) +unset_export_ignore['.b'] = FileRecord('Unless explicitly silenced.', excluded=True) +unset_export_ignore['.htaccess'] = FileRecord('In the face of ambiguity, refuse the temptation to guess.') + @pytest.mark.parametrize('contents', [ pytest.param(base, id='No Ignore'), @@ -177,6 +183,7 @@ def archive(self, path): pytest.param(ignore_in_nested_submodule, id='Ignore in Nested Submodule'), pytest.param(ignore_in_submodule_from_root, id='Ignore in Submodule from Root'), pytest.param(ignore_in_nested_submodule_from_root, id='Ignore in Nested Submodule from Root'), + pytest.param(unset_export_ignore, id='-export-ignore') ]) def test_ignore(contents, tmpdir, git_env): """ From fdede70dffb080906766e53316249459e3fe8a7c Mon Sep 17 00:00:00 2001 From: Ilya Kulakov Date: Mon, 13 Aug 2018 18:39:52 -0700 Subject: [PATCH 04/18] List only export-ignore attribute with check-attr. --- git_archive_all.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/git_archive_all.py b/git_archive_all.py index 0edf480..45621be 100755 --- a/git_archive_all.py +++ b/git_archive_all.py @@ -167,14 +167,14 @@ def is_file_excluded(self, repo_abspath, repo_file_path): @param repo_abspath: Absolute path to the git repository. @type repo_abspath: str - @param repo_file_path: Path to a file within repo_abspath. + @param repo_file_path: Path to a file relative to repo_abspath. @type repo_file_path: str @return: True if file should be excluded. Otherwise False. @rtype: bool """ out = self.run_git_shell( - 'git check-attr -a -- %s' % repo_file_path, + 'git check-attr export-ignore -- %s' % repo_file_path, cwd=repo_abspath ) return 'export-ignore: set' in out From 7a84efaee7c71e6246e0eb56013ce527b62fac0e Mon Sep 17 00:00:00 2001 From: Ilya Kulakov Date: Mon, 13 Aug 2018 18:40:16 -0700 Subject: [PATCH 05/18] Use single quotes for shell commands. --- git_archive_all.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/git_archive_all.py b/git_archive_all.py index 45621be..378b820 100755 --- a/git_archive_all.py +++ b/git_archive_all.py @@ -210,7 +210,7 @@ def walk_git_files(self, repo_path=''): """ repo_abspath = path.join(self.main_repo_abspath, repo_path) repo_file_paths = self.run_git_shell( - "git ls-files --cached --full-name --no-empty-directory", + 'git ls-files --cached --full-name --no-empty-directory', repo_abspath ).splitlines() @@ -230,8 +230,8 @@ def walk_git_files(self, repo_path=''): yield main_repo_file_path if self.force_sub: - self.run_git_shell("git submodule init", repo_abspath) - self.run_git_shell("git submodule update", repo_abspath) + self.run_git_shell('git submodule init', repo_abspath) + self.run_git_shell('git submodule update', repo_abspath) try: repo_gitmodules_abspath = path.join(repo_abspath, ".gitmodules") From 43431c40efd5c7f778ac086ad31309cb9dc70359 Mon Sep 17 00:00:00 2001 From: Ilya Kulakov Date: Mon, 13 Aug 2018 18:40:56 -0700 Subject: [PATCH 06/18] Better support for complicated file names. --- git_archive_all.py | 6 ++++-- test_git_archive_all.py | 39 ++++++++++++++++++++++++++++++--------- 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/git_archive_all.py b/git_archive_all.py index 378b820..f404ffd 100755 --- a/git_archive_all.py +++ b/git_archive_all.py @@ -215,8 +215,10 @@ def walk_git_files(self, repo_path=''): ).splitlines() for repo_file_path in repo_file_paths: - # Git puts path in quotes if file path has unicode characters. - repo_file_path = repo_file_path.strip('"') # file path relative to current repo + # Git quotes output if it contains non-ASCII or otherwise problematic characters as ". + if repo_file_path.startswith('"') and repo_file_path.endswith('"'): + repo_file_path = repo_file_path[1:-1] + repo_file_abspath = path.join(repo_abspath, repo_file_path) # absolute file path main_repo_file_path = path.join(repo_path, repo_file_path) # file path relative to the main repo diff --git a/test_git_archive_all.py b/test_git_archive_all.py index 6fcbe92..4aafdc0 100644 --- a/test_git_archive_all.py +++ b/test_git_archive_all.py @@ -133,6 +133,12 @@ def archive(self, path): }) } +base_quoted = deepcopy(base) +base_quoted['data'] = DirRecord({ + '\"hello world.dat\"': FileRecord('Special cases aren\'t special enough to break the rules.'), + '\'hello world.dat\'': FileRecord('Although practicality beats purity.') +}) + ignore_in_root = deepcopy(base) ignore_in_root['.gitattributes'] = FileRecord('tests/__init__.py export-ignore') ignore_in_root['tests'] = DirRecord({ @@ -142,48 +148,63 @@ def archive(self, path): ignore_in_submodule = deepcopy(base) ignore_in_submodule['lib']['.gitattributes'] = FileRecord('tests/__init__.py export-ignore') ignore_in_submodule['lib']['tests'] = DirRecord({ - '__init__.py': FileRecord('#Flat is better than nested.', excluded=True) + '__init__.py': FileRecord('#Complex is better than complicated.', excluded=True) }) ignore_in_nested_submodule = deepcopy(base) ignore_in_nested_submodule['lib']['extra']['.gitattributes'] = FileRecord('tests/__init__.py export-ignore') ignore_in_nested_submodule['lib']['extra']['tests'] = DirRecord({ - '__init__.py': FileRecord('#Sparse is better than dense.', excluded=True) + '__init__.py': FileRecord('#Complex is better than complicated.', excluded=True) }) ignore_in_submodule_from_root = deepcopy(base) ignore_in_submodule_from_root['.gitattributes'] = FileRecord('lib/tests/__init__.py export-ignore') ignore_in_submodule_from_root['lib']['tests'] = DirRecord({ - '__init__.py': FileRecord('#Readability counts.', excluded=True) + '__init__.py': FileRecord('#Complex is better than complicated.', excluded=True) }) ignore_in_nested_submodule_from_root = deepcopy(base) ignore_in_nested_submodule_from_root['.gitattributes'] = FileRecord('lib/extra/tests/__init__.py export-ignore') ignore_in_nested_submodule_from_root['lib']['extra']['tests'] = DirRecord({ - '__init__.py': FileRecord('#Special cases aren\'t special enough to break the rules.', excluded=True) + '__init__.py': FileRecord('#Complex is better than complicated.', excluded=True) }) ignore_in_nested_submodule_from_submodule = deepcopy(base) ignore_in_nested_submodule_from_submodule['lib']['.gitattributes'] = FileRecord('extra/tests/__init__.py export-ignore') ignore_in_nested_submodule_from_submodule['lib']['extra']['tests'] = DirRecord({ - '__init__.py': FileRecord('#Although practicality beats purity.', excluded=True) + '__init__.py': FileRecord('#Complex is better than complicated.', excluded=True) }) unset_export_ignore = deepcopy(base) unset_export_ignore['.gitattributes'] = FileRecord('.* export-ignore\n*.htaccess -export-ignore', excluded=True) -unset_export_ignore['.a'] = FileRecord('Errors should never pass silently.', excluded=True) -unset_export_ignore['.b'] = FileRecord('Unless explicitly silenced.', excluded=True) -unset_export_ignore['.htaccess'] = FileRecord('In the face of ambiguity, refuse the temptation to guess.') +unset_export_ignore['.a'] = FileRecord('Flat is better than nested.', excluded=True) +unset_export_ignore['.b'] = FileRecord('Sparse is better than dense.', excluded=True) +unset_export_ignore['.htaccess'] = FileRecord('Readability counts.') + +unicode_base = deepcopy(base) +unicode_base['data'] = DirRecord({ + 'مرحبا بالعالم.dat': FileRecord('Special cases aren\'t special enough to break the rules.') +}) + +unicode_quoted = deepcopy(base) +unicode_quoted['data'] = DirRecord({ + '\"مرحبا بالعالم.dat\"': FileRecord('Special cases aren\'t special enough to break the rules.'), + '\'привет мир.dat\'': FileRecord('Although practicality beats purity.') +}) @pytest.mark.parametrize('contents', [ pytest.param(base, id='No Ignore'), + pytest.param(base_quoted, id='No Ignore (Quoted)'), pytest.param(ignore_in_root, id='Ignore in Root'), pytest.param(ignore_in_submodule, id='Ignore in Submodule'), pytest.param(ignore_in_nested_submodule, id='Ignore in Nested Submodule'), pytest.param(ignore_in_submodule_from_root, id='Ignore in Submodule from Root'), pytest.param(ignore_in_nested_submodule_from_root, id='Ignore in Nested Submodule from Root'), - pytest.param(unset_export_ignore, id='-export-ignore') + pytest.param(ignore_in_nested_submodule_from_submodule, id='Ignore in Nested Submodule from Submodule'), + pytest.param(unset_export_ignore, id='-export-ignore'), + pytest.param(unicode_base, id='No Ignore (Unicode)'), + pytest.param(unicode_quoted, id='No Ignore (Quoted Unicode)') ]) def test_ignore(contents, tmpdir, git_env): """ From 645c30afee595dd000228e7eeee3ba111a777fd6 Mon Sep 17 00:00:00 2001 From: Ilya Kulakov Date: Mon, 13 Aug 2018 18:41:14 -0700 Subject: [PATCH 07/18] Better naming of paths. --- git_archive_all.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/git_archive_all.py b/git_archive_all.py index f404ffd..5c0ffd2 100755 --- a/git_archive_all.py +++ b/git_archive_all.py @@ -245,18 +245,18 @@ def walk_git_files(self, repo_path=''): m = re.match("^\s*path\s*=\s*(.*)\s*$", l) if m: - submodule_path = m.group(1) - submodule_abspath = path.join(repo_path, submodule_path) + repo_submodule_path = m.group(1) # relative to repo_path + main_repo_submodule_path = path.join(repo_path, repo_submodule_path) # relative to main_repo_abspath - if self.is_file_excluded(repo_abspath, submodule_path): + if self.is_file_excluded(repo_abspath, repo_submodule_path): continue - for submodule_file_path in self.walk_git_files(submodule_abspath): - rel_file_path = submodule_file_path.replace(repo_path, "", 1).strip("/") - if self.is_file_excluded(repo_abspath, rel_file_path): + for main_repo_submodule_file_path in self.walk_git_files(main_repo_submodule_path): + repo_submodule_file_path = main_repo_submodule_file_path.replace(repo_path, "", 1).strip("/") + if self.is_file_excluded(repo_abspath, repo_submodule_file_path): continue - yield submodule_file_path + yield main_repo_submodule_file_path except IOError: pass From b2ce25703f77f52ada632d0ddc33e9f3acd3f901 Mon Sep 17 00:00:00 2001 From: Ilya Kulakov Date: Mon, 13 Aug 2018 18:45:20 -0700 Subject: [PATCH 08/18] Use the -z option to avoid extra quotes in git's output. --- git_archive_all.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/git_archive_all.py b/git_archive_all.py index 5c0ffd2..46a1c33 100755 --- a/git_archive_all.py +++ b/git_archive_all.py @@ -210,15 +210,11 @@ def walk_git_files(self, repo_path=''): """ repo_abspath = path.join(self.main_repo_abspath, repo_path) repo_file_paths = self.run_git_shell( - 'git ls-files --cached --full-name --no-empty-directory', + 'git ls-files -z --cached --full-name --no-empty-directory', repo_abspath - ).splitlines() + ).split('\0')[:-1] for repo_file_path in repo_file_paths: - # Git quotes output if it contains non-ASCII or otherwise problematic characters as ". - if repo_file_path.startswith('"') and repo_file_path.endswith('"'): - repo_file_path = repo_file_path[1:-1] - repo_file_abspath = path.join(repo_abspath, repo_file_path) # absolute file path main_repo_file_path = path.join(repo_path, repo_file_path) # file path relative to the main repo From d0cf63e9a9d5676bb071e2faa7f383181900f7fd Mon Sep 17 00:00:00 2001 From: Ilya Kulakov Date: Mon, 13 Aug 2018 18:47:09 -0700 Subject: [PATCH 09/18] Add CHANGES.rst --- CHANGES.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 CHANGES.rst diff --git a/CHANGES.rst b/CHANGES.rst new file mode 100644 index 0000000..a0aea8d --- /dev/null +++ b/CHANGES.rst @@ -0,0 +1,10 @@ +CHANGES +======= + +1.18.0 (2018-08-xx) +------------------- + +- Add **CHANGES.rst** to track further changes +- Add tests +- Use `git check-attr` to test against export-ignore +- Better support for complicated file names (with unicode or From ddd602889e5bb6efa9bf8058c2421515b611660f Mon Sep 17 00:00:00 2001 From: Ilya Kulakov Date: Mon, 13 Aug 2018 20:10:11 -0700 Subject: [PATCH 10/18] Make test_git_archive_all importable by Python 2. --- test_git_archive_all.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test_git_archive_all.py b/test_git_archive_all.py index 4aafdc0..2a406b1 100644 --- a/test_git_archive_all.py +++ b/test_git_archive_all.py @@ -1,3 +1,5 @@ +# -*- coding: utf-8 -*- + from copy import deepcopy import errno from functools import partial @@ -183,13 +185,13 @@ def archive(self, path): unicode_base = deepcopy(base) unicode_base['data'] = DirRecord({ - 'مرحبا بالعالم.dat': FileRecord('Special cases aren\'t special enough to break the rules.') + u'مرحبا بالعالم.dat': FileRecord('Special cases aren\'t special enough to break the rules.') }) unicode_quoted = deepcopy(base) unicode_quoted['data'] = DirRecord({ - '\"مرحبا بالعالم.dat\"': FileRecord('Special cases aren\'t special enough to break the rules.'), - '\'привет мир.dat\'': FileRecord('Although practicality beats purity.') + u'\"مرحبا بالعالم.dat\"': FileRecord('Special cases aren\'t special enough to break the rules.'), + u'\'привет мир.dat\'': FileRecord('Although practicality beats purity.') }) From ce7a6efd616cd775efedc1de68e15a09c5fbeaa8 Mon Sep 17 00:00:00 2001 From: Ilya Kulakov Date: Mon, 13 Aug 2018 20:18:46 -0700 Subject: [PATCH 11/18] Fix pycodestyle warnings. --- git_archive_all.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/git_archive_all.py b/git_archive_all.py index 46a1c33..3df7eae 100755 --- a/git_archive_all.py +++ b/git_archive_all.py @@ -238,7 +238,7 @@ def walk_git_files(self, repo_path=''): lines = f.readlines() for l in lines: - m = re.match("^\s*path\s*=\s*(.*)\s*$", l) + m = re.match("^\\s*path\\s*=\\s*(.*)\\s*$", l) if m: repo_submodule_path = m.group(1) # relative to repo_path @@ -347,7 +347,7 @@ def main(): output_name = path.basename(output_file_path) output_name = re.sub( - '(\.zip|\.tar|\.tgz|\.txz|\.gz|\.bz2|\.xz|\.tar\.gz|\.tar\.bz2|\.tar\.xz)$', + '(\\.zip|\\.tar|\\.tgz|\\.txz|\\.gz|\\.bz2|\\.xz|\\.tar\\.gz|\\.tar\\.bz2|\\.tar\\.xz)$', '', output_name ) or "Archive" From c9d7ebac69e4eb734af8f9d9a3ee97df3ecf2bdc Mon Sep 17 00:00:00 2001 From: Ilya Kulakov Date: Mon, 13 Aug 2018 20:27:51 -0700 Subject: [PATCH 12/18] Migrate from pep8 to pycodestyle. --- Makefile | 7 +++---- requirements.txt | 1 + 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index 9e4c319..345a077 100644 --- a/Makefile +++ b/Makefile @@ -8,10 +8,9 @@ all: @echo " make uninstall" @echo " test" -test: test_pep - -test_pep: - pep8 --max-line-length=240 git_archive_all.py +test: + pycodestyle --config=. --max-line-length=240 git_archive_all.py + python setup.py test install: install -d -m 0755 $(TARGET_DIR) diff --git a/requirements.txt b/requirements.txt index 1695968..a77637b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,4 @@ pytest==3.7.1; python_version>"2.6" pytest==3.2.5; python_version<="2.6" pytest-cov==2.5.1 codecov==2.0.15 +pycodestyle==2.4.0 From d18eddaa16d190d8fd47178b8c007573ef16c0cc Mon Sep 17 00:00:00 2001 From: Ilya Kulakov Date: Mon, 13 Aug 2018 20:28:09 -0700 Subject: [PATCH 13/18] Use makefile to run tests on travis. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 29b1d4c..9185f1c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -190,7 +190,7 @@ install: - ${PIP} install -r requirements.txt script: - - python setup.py test + - make test after_success: - python -m codecov From 48740c6c07bcb97f01335e0ad2247793a99d18d2 Mon Sep 17 00:00:00 2001 From: Ilya Kulakov Date: Mon, 13 Aug 2018 20:44:22 -0700 Subject: [PATCH 14/18] Run pycodestyle with pytest. --- Makefile | 1 - test_git_archive_all.py | 7 +++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 345a077..a866e2b 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,6 @@ all: @echo " test" test: - pycodestyle --config=. --max-line-length=240 git_archive_all.py python setup.py test install: diff --git a/test_git_archive_all.py b/test_git_archive_all.py index 2a406b1..7bb5c60 100644 --- a/test_git_archive_all.py +++ b/test_git_archive_all.py @@ -7,6 +7,7 @@ from subprocess import check_call from tarfile import TarFile +import pycodestyle import pytest from git_archive_all import GitArchiver @@ -249,3 +250,9 @@ def make_actual(tar_file): actual = make_actual(repo_tar) assert actual == expected + + +def test_pycodestyle(): + style = pycodestyle.StyleGuide(repeat=True, max_line_length=240) + report = style.check_files(['git_archive_all.py']) + assert report.total_errors == 0, "Found code style errors (and warnings)." From eb7b3ceb4b859ac62d0471ddb5f8cc1a9c749e71 Mon Sep 17 00:00:00 2001 From: Ilya Kulakov Date: Mon, 13 Aug 2018 20:58:19 -0700 Subject: [PATCH 15/18] Prefer unicode_literals to the u prefix. --- test_git_archive_all.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/test_git_archive_all.py b/test_git_archive_all.py index 7bb5c60..7c99058 100644 --- a/test_git_archive_all.py +++ b/test_git_archive_all.py @@ -1,5 +1,8 @@ # -*- coding: utf-8 -*- +from __future__ import print_function +from __future__ import unicode_literals + from copy import deepcopy import errno from functools import partial @@ -186,13 +189,13 @@ def archive(self, path): unicode_base = deepcopy(base) unicode_base['data'] = DirRecord({ - u'مرحبا بالعالم.dat': FileRecord('Special cases aren\'t special enough to break the rules.') + 'مرحبا بالعالم.dat': FileRecord('Special cases aren\'t special enough to break the rules.') }) unicode_quoted = deepcopy(base) unicode_quoted['data'] = DirRecord({ - u'\"مرحبا بالعالم.dat\"': FileRecord('Special cases aren\'t special enough to break the rules.'), - u'\'привет мир.dat\'': FileRecord('Although practicality beats purity.') + '\"مرحبا بالعالم.dat\"': FileRecord('Special cases aren\'t special enough to break the rules.'), + '\'привет мир.dat\'': FileRecord('Although practicality beats purity.') }) From 3d8df3fe020c6128b80c1a72de4678850771d42f Mon Sep 17 00:00:00 2001 From: Ilya Kulakov Date: Mon, 13 Aug 2018 21:09:42 -0700 Subject: [PATCH 16/18] Enable deploy to pypi and github. --- .travis.yml | 386 +++++++++++++++++++++++++++------------------------- 1 file changed, 203 insertions(+), 183 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9185f1c..1e3b7a3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,196 +1,216 @@ env: global: - - PIP_URL="https://bootstrap.pypa.io/get-pip.py" - - PIP="python -m pip" + - PIP_URL="https://bootstrap.pypa.io/get-pip.py" + - PIP="python -m pip" matrix: fast_finish: true include: - - os: osx - language: generic - env: - - NAME="Python 2.6" - - PIP_URL="https://bootstrap.pypa.io/2.6/get-pip.py" - - PIP=pip - before_install: - - brew update - - brew upgrade pyenv || brew install pyenv || true - - pyenv install 2.6.9 - - pyenv local 2.6.9 - - eval "$(pyenv init -)" - - pip install virtualenv - - python2.6 -m virtualenv env - - pyenv local --unset - - source env/bin/activate - - os: osx - language: generic - env: NAME="Python 2.7" - before_install: - - brew update - - brew upgrade python2 || true - - python2.7 -m pip install virtualenv - - python2.7 -m virtualenv env - - source env/bin/activate - - os: osx - language: generic - env: NAME="Python pypy" - before_install: - - brew update - - brew upgrade pypy || brew install pypy || true - - pypy -m pip install virtualenv - - pypy -m virtualenv env - - source env/bin/activate - - os: osx - language: generic - env: NAME="Python 3.4" - before_install: - - brew update - - brew upgrade pyenv || brew install pyenv || true - - pyenv install 3.4.9 - - pyenv local 3.4.9 - - eval "$(pyenv init -)" - - python3.4 -m venv env - - pyenv local --unset - - source env/bin/activate - - os: osx - language: generic - env: NAME="Python 3.5" - before_install: - - brew update - - brew upgrade pyenv || brew install pyenv || true - - pyenv install 3.5.6 - - pyenv local 3.5.6 - - eval "$(pyenv init -)" - - python3.5 -m venv env - - pyenv local --unset - - source env/bin/activate - - os: osx - language: generic - env: NAME="Python 3.6" - before_install: - - brew update - - brew upgrade pyenv || brew install pyenv || true - - pyenv install 3.6.6 - - pyenv local 3.6.6 - - eval "$(pyenv init -)" - - python3.6 -m venv env - - pyenv local --unset - - source env/bin/activate - - os: osx - language: generic - env: NAME="Python 3.7" - before_install: - - brew update - - brew upgrade python || true - - python3.7 -m venv env - - source env/bin/activate - - os: osx - language: generic - env: NAME="Python HEAD" - before_install: - - brew update - - brew unlink python - - brew install python --HEAD - - python3 -m venv env - - source env/bin/activate - - os: osx - language: generic - env: NAME="Python pypy3" - before_install: - - brew update - - brew upgrade pypy3 || brew install pypy3 || true - - pypy3 -m venv env - - source env/bin/activate - - os: linux - env: - - PIP_URL="https://bootstrap.pypa.io/2.6/get-pip.py" - - PIP=env/bin/pip - language: python - python: 2.6 - before_install: - - pip install virtualenv - - python2.6 -m virtualenv env - - source env/bin/activate - - os: linux - language: python - python: 2.7 - before_install: - - pip install virtualenv - - python2.7 -m virtualenv env - - source env/bin/activate - - os: linux - language: python - python: pypy - before_install: - - pip install virtualenv - - pypy -m virtualenv env - - source env/bin/activate - - os: linux - language: python - python: 3.4 - before_install: - - python3.4 -m venv env - - source env/bin/activate - - os: linux - language: python - python: 3.5 - before_install: - - python3.5 -m venv env - - source env/bin/activate - - os: linux - language: python - python: 3.6 - before_install: - - python3.6 -m venv env - - source env/bin/activate - - os: linux - language: python - python: &python_major_ver 3.7 - dist: xenial - sudo: true - before_install: - - python3.7 -m venv env - - source env/bin/activate - - os: linux - language: python - python: 3.6-dev - before_install: - - python3.6 -m venv env - - source env/bin/activate - - os: linux - language: python - python: 3.7-dev - before_install: - - python3.7 -m venv env - - source env/bin/activate - - os: linux - language: python - python: nightly - before_install: - - python3 -m venv env - - source env/bin/activate - - os: linux - language: python - python: pypy3 - before_install: - - pypy3 -m venv env - - source env/bin/activate + - os: osx + language: generic + env: + - NAME="Python 2.6" + - PIP_URL="https://bootstrap.pypa.io/2.6/get-pip.py" + - PIP=pip + before_install: + - brew update + - brew upgrade pyenv || brew install pyenv || true + - pyenv install 2.6.9 + - pyenv local 2.6.9 + - eval "$(pyenv init -)" + - pip install virtualenv + - python2.6 -m virtualenv env + - pyenv local --unset + - source env/bin/activate + - os: osx + language: generic + env: NAME="Python 2.7" + before_install: + - brew update + - brew upgrade python2 || true + - python2.7 -m pip install virtualenv + - python2.7 -m virtualenv env + - source env/bin/activate + - os: osx + language: generic + env: NAME="Python pypy" + before_install: + - brew update + - brew upgrade pypy || brew install pypy || true + - pypy -m pip install virtualenv + - pypy -m virtualenv env + - source env/bin/activate + - os: osx + language: generic + env: NAME="Python 3.4" + before_install: + - brew update + - brew upgrade pyenv || brew install pyenv || true + - pyenv install 3.4.9 + - pyenv local 3.4.9 + - eval "$(pyenv init -)" + - python3.4 -m venv env + - pyenv local --unset + - source env/bin/activate + - os: osx + language: generic + env: NAME="Python 3.5" + before_install: + - brew update + - brew upgrade pyenv || brew install pyenv || true + - pyenv install 3.5.6 + - pyenv local 3.5.6 + - eval "$(pyenv init -)" + - python3.5 -m venv env + - pyenv local --unset + - source env/bin/activate + - os: osx + language: generic + env: NAME="Python 3.6" + before_install: + - brew update + - brew upgrade pyenv || brew install pyenv || true + - pyenv install 3.6.6 + - pyenv local 3.6.6 + - eval "$(pyenv init -)" + - python3.6 -m venv env + - pyenv local --unset + - source env/bin/activate + - os: osx + language: generic + env: NAME="Python 3.7" + before_install: + - brew update + - brew upgrade python || true + - python3.7 -m venv env + - source env/bin/activate + - os: osx + language: generic + env: NAME="Python HEAD" + before_install: + - brew update + - brew unlink python + - brew install python --HEAD + - python3 -m venv env + - source env/bin/activate + - os: osx + language: generic + env: NAME="Python pypy3" + before_install: + - brew update + - brew upgrade pypy3 || brew install pypy3 || true + - pypy3 -m venv env + - source env/bin/activate + - os: linux + env: + - PIP_URL="https://bootstrap.pypa.io/2.6/get-pip.py" + - PIP=env/bin/pip + language: python + python: 2.6 + before_install: + - pip install virtualenv + - python2.6 -m virtualenv env + - source env/bin/activate + - os: linux + language: python + python: 2.7 + before_install: + - pip install virtualenv + - python2.7 -m virtualenv env + - source env/bin/activate + - os: linux + language: python + python: pypy + before_install: + - pip install virtualenv + - pypy -m virtualenv env + - source env/bin/activate + - os: linux + language: python + python: 3.4 + before_install: + - python3.4 -m venv env + - source env/bin/activate + - os: linux + language: python + python: 3.5 + before_install: + - python3.5 -m venv env + - source env/bin/activate + - os: linux + language: python + python: 3.6 + before_install: + - python3.6 -m venv env + - source env/bin/activate + - os: linux + language: python + python: 3.7 + dist: xenial + sudo: true + before_install: + - python3.7 -m venv env + - source env/bin/activate + - os: linux + language: python + python: 3.6-dev + before_install: + - python3.6 -m venv env + - source env/bin/activate + - os: linux + language: python + python: 3.7-dev + before_install: + - python3.7 -m venv env + - source env/bin/activate + - os: linux + language: python + python: nightly + before_install: + - python3 -m venv env + - source env/bin/activate + - os: linux + language: python + python: pypy3 + before_install: + - pypy3 -m venv env + - source env/bin/activate allow_failures: - - os: osx - env: NAME="Python HEAD" - - os: linux - python: 3.6-dev - - os: linux - python: 3.7-dev - - os: linux - python: nightly + - os: osx + env: NAME="Python HEAD" + - os: linux + python: 3.6-dev + - os: linux + python: 3.7-dev + - os: linux + python: nightly install: - - curl ${PIP_URL} | python - - ${PIP} install -r requirements.txt +- curl ${PIP_URL} | python +- ${PIP} install -r requirements.txt script: - - make test +- make test after_success: - - python -m codecov +- python -m codecov + +deploy: +- provider: pypi + user: Ilya.Kulakov + password: + secure: "VlZYEwVXqGknNEfz6vCo26JWtcbXfBY0ihG+Co6PO7JQn2jEoogKEsMhi3f/rs22nlsH124AQa7bugmH1TkgBn8ODsKLZWGAQKSm9tQlYD2idsTYqCDk5bCAWpjX/RJqZZeMnDmL9BFJkaubrTfnFWdMxI1xXi/G9wC4NaSXSJc=" + distributions: "sdist bdist_wheel" + on: + tags: true + branch: master + python: 3.7 +- provider: releases + api_key: + secure: "QHn7vzWo7rbgemP37qdNU4h+q7Xb2CQ7HxPFfa7yTsxFd8V4+sQLVrnaQtzYTM8dJWvRgi8PVHVGl2VGnQAiRM4Nd/NE/3HL9aHQIfWRtZ6XHfNVQ55bxJzLfZZy2M+32b8W268ELj3ty4C3Mo7TuOTv4svQoRDrLzGozJCpu+w=" + file_glob: true + file: dist/* + on: + tags: true + branch: master + python: 3.7 From 3a4ed6330b477a2588f3a81b41b3f6a02efd726f Mon Sep 17 00:00:00 2001 From: Ilya Kulakov Date: Tue, 14 Aug 2018 15:11:54 -0700 Subject: [PATCH 17/18] Fix unicode tests on Python 2. --- test_git_archive_all.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/test_git_archive_all.py b/test_git_archive_all.py index 7c99058..16d8d18 100644 --- a/test_git_archive_all.py +++ b/test_git_archive_all.py @@ -8,7 +8,8 @@ from functools import partial import os from subprocess import check_call -from tarfile import TarFile +import sys +from tarfile import TarFile, PAX_FORMAT import pycodestyle import pytest @@ -224,7 +225,7 @@ def test_ignore(contents, tmpdir, git_env): repo_tar_path = os.path.join(str(tmpdir), 'repo.tar') repo.archive(repo_tar_path) - repo_tar = TarFile(repo_tar_path) + repo_tar = TarFile(repo_tar_path, format=PAX_FORMAT, encoding='utf-8') def make_expected(contents): e = {} @@ -243,7 +244,12 @@ def make_actual(tar_file): for m in tar_file.getmembers(): if m.isfile(): - a[m.name] = tar_file.extractfile(m).read().decode() + name = m.name + + if sys.version_info < (3,): + name = m.name.decode('utf-8') + + a[name] = tar_file.extractfile(m).read().decode() else: raise NotImplementedError From 6fe4ace58f39481375f5b3b78d7aa3152aef145d Mon Sep 17 00:00:00 2001 From: Ilya Kulakov Date: Tue, 14 Aug 2018 15:49:56 -0700 Subject: [PATCH 18/18] Use the -z option for check-attr to make test unambiguous. --- git_archive_all.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/git_archive_all.py b/git_archive_all.py index 3df7eae..9dbadc8 100755 --- a/git_archive_all.py +++ b/git_archive_all.py @@ -174,10 +174,14 @@ def is_file_excluded(self, repo_abspath, repo_file_path): @rtype: bool """ out = self.run_git_shell( - 'git check-attr export-ignore -- %s' % repo_file_path, + 'git check-attr -z export-ignore -- %s' % repo_file_path, cwd=repo_abspath - ) - return 'export-ignore: set' in out + ).split('\0') + + try: + return out[2] == 'set' + except IndexError: + return False def archive_all_files(self, archiver): """