From 43c87f45def23479774b8e604345cc9833618e4e Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Sun, 15 Nov 2020 17:09:54 +0000 Subject: [PATCH 01/19] drop explicit py3.2, 3.3, 3.4 support --- setup.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/setup.py b/setup.py index bc52819..a62cd55 100644 --- a/setup.py +++ b/setup.py @@ -101,9 +101,6 @@ def cythonize(*args, **kwargs): 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.2', - 'Programming Language :: Python :: 3.3', - 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', From 3f98b8893d4c2881db47dc1cc0e79f317db3faa4 Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Sun, 15 Nov 2020 17:23:24 +0000 Subject: [PATCH 02/19] CI: update action post-upstream-merge --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5c26a02..a7dcafb 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -50,7 +50,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Coveralls Finished - uses: exoplanet-dev/coveralls-python-action@develop # https://github.com/AndreMiras/coveralls-python-action/pull/5 + uses: AndreMiras/coveralls-python-action@develop with: parallel-finished: true deploy: From 49a190ba46af9fcac64f729d9b4c4e54378830fd Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Sun, 15 Nov 2020 17:30:45 +0000 Subject: [PATCH 03/19] badge logos --- README.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index e911fc9..9393e92 100644 --- a/README.rst +++ b/README.rst @@ -280,7 +280,7 @@ We are grateful for all |GitHub-Contributions|. .. |Build-Status| image:: https://img.shields.io/github/workflow/status/casperdcl/git-fame/Test/master?logo=GitHub :target: https://github.com/casperdcl/git-fame/actions?query=workflow%3ATest -.. |Coverage-Status| image:: https://coveralls.io/repos/casperdcl/git-fame/badge.svg?branch=master +.. |Coverage-Status| image:: https://img.shields.io/coveralls/github/casperdcl/git-fame/master?logo=coveralls :target: https://coveralls.io/github/casperdcl/git-fame .. |Branch-Coverage-Status| image:: https://codecov.io/gh/casperdcl/git-fame/branch/master/graph/badge.svg :target: https://codecov.io/gh/casperdcl/git-fame @@ -304,7 +304,7 @@ We are grateful for all |GitHub-Contributions|. :target: https://github.com/casperdcl/git-fame/pulse .. |Gift-Casper| image:: https://img.shields.io/badge/gift-donate-dc10ff.svg :target: https://caspersci.uk.to/donate -.. |PyPI-Status| image:: https://img.shields.io/pypi/v/git-fame.svg +.. |PyPI-Status| image:: https://img.shields.io/pypi/v/git-fame.svg?logo=python&logoColor=white :target: https://pypi.org/project/git-fame .. |PyPI-Downloads| image:: https://img.shields.io/pypi/dm/git-fame.svg?label=pypi%20downloads&logo=python&logoColor=white :target: https://pypi.org/project/git-fame From 2d34d847e74357834f209c9dbe7bf559b8f85f46 Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Wed, 18 Nov 2020 02:43:09 +0000 Subject: [PATCH 04/19] add --churn=surviving|insertions|deletions - fixes #50 --- .github/workflows/test.yml | 3 +- git-fame_completion.bash | 5 +- gitfame/_gitfame.py | 102 ++++++++++++++++++++++++------------- gitfame/git-fame.1 | 9 +++- 4 files changed, 80 insertions(+), 39 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a7dcafb..61c2430 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -33,8 +33,7 @@ jobs: with: python-version: ${{ matrix.python }} - name: Install - run: | - pip install -U tox setuptools_scm + run: pip install -U tox setuptools_scm - name: Test run: tox -e py${PYVER/./} env: diff --git a/git-fame_completion.bash b/git-fame_completion.bash index 88db9e0..2c3c15b 100644 --- a/git-fame_completion.bash +++ b/git-fame_completion.bash @@ -15,6 +15,9 @@ _git_fame() --cost) COMPREPLY=($(compgen -W 'months cocomo hours commits' -- ${cur})) ;; + --churn) + COMPREPLY=($(compgen -W 'surviving insertions deletions' -- ${cur})) + ;; --format) COMPREPLY=($(compgen -W 'pipe markdown yaml json csv tsv tabulate' -- ${cur})) ;; @@ -32,7 +35,7 @@ _git_fame() ;; *) if [ ${COMP_WORDS[1]} == fame ]; then - COMPREPLY=($(compgen -dW '-h --help -v --version --cost --branch --since --sort --incl --excl -n --no-regex -s --silent-progress --warn-binary -t --bytype -w --ignore-whitespace -e --show-email --enum -M -C --format --manpath --log' -- ${cur})) + COMPREPLY=($(compgen -dW '-h --help -v --version --cost --branch --since --sort --churn --incl --excl -n --no-regex -s --silent-progress --warn-binary -t --bytype -w --ignore-whitespace -e --show-email --enum -M -C --format --manpath --log' -- ${cur})) fi ;; esac diff --git a/gitfame/_gitfame.py b/gitfame/_gitfame.py index a5419e7..bd32dd1 100755 --- a/gitfame/_gitfame.py +++ b/gitfame/_gitfame.py @@ -12,6 +12,8 @@ -v, --version Print module version and exit. --branch= Branch or tag [default: HEAD] up to which to check. --sort= [default: loc]|commits|files|hours|months. + --churn= [default: surviving]|ins(ertions)|del(etions) + What `loc` represents. Use 'ins,del' to count both. --excl= Excluded files (default: None). In no-regex mode, may be a comma-separated list. Escape (\,) for a literal comma (may require \\, in shell). @@ -24,7 +26,7 @@ May be multiple comma-separated values. -n, --no-regex Assume are comma-separated exact matches rather than regular expressions [default: False]. - NB: if regex is enabled `,` is equivalent to `|`. + NB: if regex is enabled ',' is equivalent to '|'. -s, --silent-progress Suppress `tqdm` [default: False]. --warn-binary Don't silently skip files which appear to be binary data [default: False]. @@ -91,6 +93,8 @@ def get_version_dist(name=__name__): RE_AUTHS = re.compile( r'^\w+ \d+ \d+ (\d+)\nauthor (.+?)$.*?\ncommitter-time (\d+)', flags=re.M | re.DOTALL) +RE_AUTHS_LOG = re.compile( + r'^aN(.+?) ct(\d+)\n\n(?:.*? (\d+) insert)?(?:.*? (\d+) del)?', flags=re.M) # finds all non-escaped commas # NB: does not support escaping of escaped character RE_CSPILT = re.compile(r'(?": {"loc": int, "files": {}, "commits": int, "ctimes": [int]}}""" since = ["--since", since] if since else [] @@ -228,20 +238,47 @@ def _get_auth_stats( if include_files.search(i) if not (exclude_files and exclude_files.search(i))] log.log(logging.NOTSET, "files:\n" + '\n'.join(file_list)) + churn = set(churn.lower().split(',')) + + if churn & CHURN_SLOC: + base_cmd = git_cmd + ["blame", "--line-porcelain"] + since + else: + base_cmd = git_cmd + ["log", "--format=aN%aN ct%ct", "--shortstat"] + since + + if ignore_whitespace: + base_cmd.append("-w") + if M: + base_cmd.append("-M") + if C: + base_cmd.extend(["-C", "-C"]) # twice to include file creation auth_stats = {} + + def stats_append(fname, auth, loc, tstamp): + auth = _str(auth) + tstamp = int(tstamp) + try: + auth_stats[auth]["loc"] += loc + except KeyError: + auth_stats[auth] = {"loc": loc, "files": set([fname]), "ctimes": []} + else: + auth_stats[auth]["files"].add(fname) + auth_stats[auth]["ctimes"].append(tstamp) + + if bytype: + fext_key = ("." + fext(fname)) if fext(fname) else "._None_ext" + # auth_stats[auth].setdefault(fext_key, 0) + try: + auth_stats[auth][fext_key] += loc + except KeyError: + auth_stats[auth][fext_key] = loc + for fname in tqdm(file_list, desc=gitdir if prefix_gitdir else "Processing", disable=silent_progress, unit="file"): - git_blame_cmd = git_cmd + ["blame", "--line-porcelain", branch, fname] + \ - since + + git_blame_cmd = base_cmd + [branch, fname] if prefix_gitdir: fname = path.join(gitdir, fname) - if ignore_whitespace: - git_blame_cmd.append("-w") - if M: - git_blame_cmd.append("-M") - if C: - git_blame_cmd.extend(["-C", "-C"]) # twice to include file creation try: blame_out = check_output(git_blame_cmd, stderr=subprocess.STDOUT) except Exception as e: @@ -249,31 +286,25 @@ def _get_auth_stats( continue log.log(logging.NOTSET, blame_out) - # Strip boundary messages, - # preventing user with nearest commit to boundary owning the LOC - blame_out = RE_BLAME_BOUNDS.sub('', blame_out) - loc_auth_times = RE_AUTHS.findall(blame_out) + if churn & CHURN_SLOC: + # Strip boundary messages, + # preventing user with nearest commit to boundary owning the LOC + blame_out = RE_BLAME_BOUNDS.sub('', blame_out) + loc_auth_times = RE_AUTHS.findall(blame_out) - for loc, auth, tstamp in loc_auth_times: # for each chunk - loc = int(loc) - auth = _str(auth) - tstamp = int(tstamp) - try: - auth_stats[auth]["loc"] += loc - except KeyError: - auth_stats[auth] = {"loc": loc, "files": set([fname]), "ctimes": []} - else: - auth_stats[auth]["files"].add(fname) - auth_stats[auth]["ctimes"].append(tstamp) + for loc, auth, tstamp in loc_auth_times: # for each chunk + loc = int(loc) + stats_append(fname, auth, loc, tstamp) + else: + auth_time_ins_dels = RE_AUTHS_LOG.findall(blame_out) - if bytype: - fext_key = ("." + fext(fname)) if fext(fname) else "._None_ext" - # auth_stats[auth].setdefault(fext_key, 0) - try: - auth_stats[auth][fext_key] += loc - except KeyError: - auth_stats[auth][fext_key] = loc + for auth, tstamp, inss, dels in auth_time_ins_dels: # for each chunk + loc = ( + int(inss) if churn & CHURN_INS and inss else 0) + ( + int(dels) if churn & CHURN_DEL and dels else 0) + stats_append(fname, auth, loc, tstamp) + # quickly count commits (even if no surviving loc) log.log(logging.NOTSET, "authors:" + '; '.join(auth_stats.keys())) auth_commits = check_output( git_cmd + ["shortlog", "-s", "-e", branch] + since) @@ -355,7 +386,8 @@ def run(args): silent_progress=args.silent_progress, ignore_whitespace=args.ignore_whitespace, M=args.M, C=args.C, warn_binary=args.warn_binary, bytype=args.bytype, - show_email=args.show_email, prefix_gitdir=len(gitdirs) > 1) + show_email=args.show_email, prefix_gitdir=len(gitdirs) > 1, + churn=args.churn) # concurrent multi-repo processing if len(gitdirs) > 1: diff --git a/gitfame/git-fame.1 b/gitfame/git-fame.1 index a16a99e..64465fe 100644 --- a/gitfame/git-fame.1 +++ b/gitfame/git-fame.1 @@ -59,6 +59,13 @@ Branch or tag [default: HEAD] up to which to check. .RS .RE .TP +.B \-\-churn=\f[I]c\f[] +[default: surviving]|ins(ertions)|del(etions) What \f[C]loc\f[] +represents. +Use \[aq]ins,del\[aq] to count both. +.RS +.RE +.TP .B \-\-excl=\f[I]f\f[] Excluded files (default: None). In no\-regex mode, may be a comma\-separated list. @@ -89,7 +96,7 @@ May be multiple comma\-separated values. .B \-n, \-\-no\-regex Assume are comma\-separated exact matches rather than regular expressions [default: False]. -NB: if regex is enabled \f[C],\f[] is equivalent to \f[C]|\f[]. +NB: if regex is enabled \[aq],\[aq] is equivalent to \[aq]|\[aq]. .RS .RE .TP From f539ca4ac294b027f1334e6802f7bf406e47c8c7 Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Wed, 18 Nov 2020 02:56:30 +0000 Subject: [PATCH 05/19] rename --churn => --loc --- README.rst | 4 +++- git-fame_completion.bash | 4 ++-- gitfame/_gitfame.py | 4 ++-- gitfame/git-fame.1 | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/README.rst b/README.rst index 9393e92..f41cdcc 100644 --- a/README.rst +++ b/README.rst @@ -170,6 +170,8 @@ Documentation -v, --version Print module version and exit. --branch= Branch or tag [default: HEAD] up to which to check. --sort= [default: loc]|commits|files|hours|months. + --loc= [default: surviving]|ins(ertions)|del(etions) + What `loc` represents. Use 'ins,del' to count both. --excl= Excluded files (default: None). In no-regex mode, may be a comma-separated list. Escape (\,) for a literal comma (may require \\, in shell). @@ -182,7 +184,7 @@ Documentation May be multiple comma-separated values. -n, --no-regex Assume are comma-separated exact matches rather than regular expressions [default: False]. - NB: if regex is enabled `,` is equivalent to `|`. + NB: if regex is enabled ',' is equivalent to '|'. -s, --silent-progress Suppress `tqdm` [default: False]. --warn-binary Don't silently skip files which appear to be binary data [default: False]. diff --git a/git-fame_completion.bash b/git-fame_completion.bash index 2c3c15b..067e24b 100644 --- a/git-fame_completion.bash +++ b/git-fame_completion.bash @@ -15,7 +15,7 @@ _git_fame() --cost) COMPREPLY=($(compgen -W 'months cocomo hours commits' -- ${cur})) ;; - --churn) + --loc) COMPREPLY=($(compgen -W 'surviving insertions deletions' -- ${cur})) ;; --format) @@ -35,7 +35,7 @@ _git_fame() ;; *) if [ ${COMP_WORDS[1]} == fame ]; then - COMPREPLY=($(compgen -dW '-h --help -v --version --cost --branch --since --sort --churn --incl --excl -n --no-regex -s --silent-progress --warn-binary -t --bytype -w --ignore-whitespace -e --show-email --enum -M -C --format --manpath --log' -- ${cur})) + COMPREPLY=($(compgen -dW '-h --help -v --version --cost --branch --since --sort --loc --incl --excl -n --no-regex -s --silent-progress --warn-binary -t --bytype -w --ignore-whitespace -e --show-email --enum -M -C --format --manpath --log' -- ${cur})) fi ;; esac diff --git a/gitfame/_gitfame.py b/gitfame/_gitfame.py index bd32dd1..76c4268 100755 --- a/gitfame/_gitfame.py +++ b/gitfame/_gitfame.py @@ -12,7 +12,7 @@ -v, --version Print module version and exit. --branch= Branch or tag [default: HEAD] up to which to check. --sort= [default: loc]|commits|files|hours|months. - --churn= [default: surviving]|ins(ertions)|del(etions) + --loc= [default: surviving]|ins(ertions)|del(etions) What `loc` represents. Use 'ins,del' to count both. --excl= Excluded files (default: None). In no-regex mode, may be a comma-separated list. @@ -387,7 +387,7 @@ def run(args): ignore_whitespace=args.ignore_whitespace, M=args.M, C=args.C, warn_binary=args.warn_binary, bytype=args.bytype, show_email=args.show_email, prefix_gitdir=len(gitdirs) > 1, - churn=args.churn) + churn=args.loc) # concurrent multi-repo processing if len(gitdirs) > 1: diff --git a/gitfame/git-fame.1 b/gitfame/git-fame.1 index 64465fe..312ebf7 100644 --- a/gitfame/git-fame.1 +++ b/gitfame/git-fame.1 @@ -59,7 +59,7 @@ Branch or tag [default: HEAD] up to which to check. .RS .RE .TP -.B \-\-churn=\f[I]c\f[] +.B \-\-loc=\f[I]type\f[] [default: surviving]|ins(ertions)|del(etions) What \f[C]loc\f[] represents. Use \[aq]ins,del\[aq] to count both. From 302b3b8ccc251ffa4269e838f44c7b853420849e Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Wed, 18 Nov 2020 10:21:52 +0000 Subject: [PATCH 06/19] fix logging --- gitfame/_utils.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/gitfame/_utils.py b/gitfame/_utils.py index 3f2d209..e9ba0cc 100644 --- a/gitfame/_utils.py +++ b/gitfame/_utils.py @@ -4,7 +4,7 @@ import subprocess import logging -from tqdm import tqdm +from tqdm import tqdm as tqdm_std from tqdm.utils import _screen_shape_wrapper try: @@ -22,10 +22,10 @@ try: from threading import RLock except ImportError: - pass + tqdm = tqdm_std else: - tqdm.set_lock(RLock()) - tqdm = partial(tqdm, lock_args=(False,)) + tqdm_std.set_lock(RLock()) + tqdm = partial(tqdm_std, lock_args=(False,)) __author__ = "Casper da Costa-Luis " __date__ = "2016-2020" @@ -45,7 +45,7 @@ class TqdmStream(object): @classmethod def write(cls, msg): - tqdm.write(msg, end='') + tqdm_std.write(msg, end='') def check_output(*a, **k): From b05101e34f0af87bd391bf6d8b3fffd729c59df8 Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Wed, 18 Nov 2020 10:33:31 +0000 Subject: [PATCH 07/19] add --loc=ins,del support for all files in history --- gitfame/_gitfame.py | 79 +++++++++++++++++++++++++++++---------------- 1 file changed, 51 insertions(+), 28 deletions(-) diff --git a/gitfame/_gitfame.py b/gitfame/_gitfame.py index 76c4268..1d17cc0 100755 --- a/gitfame/_gitfame.py +++ b/gitfame/_gitfame.py @@ -90,19 +90,22 @@ def get_version_dist(name=__name__): __license__ = __licence__ # weird foreign language log = logging.getLogger(__name__) -RE_AUTHS = re.compile( +# processing `blame --line-porcelain` +RE_AUTHS_BLAME = re.compile( r'^\w+ \d+ \d+ (\d+)\nauthor (.+?)$.*?\ncommitter-time (\d+)', flags=re.M | re.DOTALL) -RE_AUTHS_LOG = re.compile( - r'^aN(.+?) ct(\d+)\n\n(?:.*? (\d+) insert)?(?:.*? (\d+) del)?', flags=re.M) -# finds all non-escaped commas -# NB: does not support escaping of escaped character -RE_CSPILT = re.compile(r'(?\s*$', flags=re.M) -# finds "boundary" line-porcelain messages RE_BLAME_BOUNDS = re.compile( r'^\w+\s+\d+\s+\d+(\s+\d+)?\s*$[^\t]*?^boundary\s*$[^\t]*?^\t.*?$\r?\n', flags=re.M | re.DOTALL) +# processing `log --format="aN%aN ct%ct" --numstat` +RE_AUTHS_LOG = re.compile(r"^aN(.+?) ct(\d+)\n\n", flags=re.M) +RE_STAT_BINARY = re.compile(r"^\s*?-\s*-.*?\n", flags=re.M) +RE_RENAME = re.compile(r"\{.+? => (.+?)\}") +# finds all non-escaped commas +# NB: does not support escaping of escaped character +RE_CSPILT = re.compile(r'(? Date: Wed, 18 Nov 2020 11:35:29 +0000 Subject: [PATCH 08/19] upgrade build framework --- .github/workflows/test.yml | 29 ++++---- .gitignore | 16 +++-- .meta/.snapcraft.yml | 7 +- MANIFEST.in | 3 +- Makefile | 5 +- gitfame/_gitfame.py | 26 ++------ pyproject.toml | 7 ++ setup.cfg | 93 +++++++++++++++++++++++++- setup.py | 131 ++++++------------------------------- tox.ini | 12 ++-- 10 files changed, 162 insertions(+), 167 deletions(-) create mode 100644 pyproject.toml mode change 100644 => 100755 setup.py diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 61c2430..e4a24a0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -6,34 +6,40 @@ on: - cron: '3 2 1 * *' # M H d m w (monthly at 2:03) jobs: check: + if: github.event_name != 'pull_request' || github.head_ref != 'devel' name: Check runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + with: + fetch-depth: 0 - uses: actions/setup-python@v2 with: python-version: '3.x' - name: Install - run: | - pip install -U tox - pip install -U . + run: pip install -U tox - name: Test run: tox env: TOXENV: 'flake8,setup.py,nodeps' + - name: Self install + run: pip install -U . test: + if: github.event_name != 'pull_request' || github.head_ref != 'devel' strategy: matrix: - python: [2.7, 3.5, 3.6, 3.7, 3.8] + python: [2.7, 3.5, 3.6, 3.7, 3.8, 3.9] name: Python ${{ matrix.python }} runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + with: + fetch-depth: 0 - uses: actions/setup-python@v2 with: python-version: ${{ matrix.python }} - name: Install - run: pip install -U tox setuptools_scm + run: pip install -U tox - name: Test run: tox -e py${PYVER/./} env: @@ -44,6 +50,7 @@ jobs: with: parallel: true finish: + if: github.event_name != 'pull_request' || github.head_ref != 'devel' name: Coverage needs: test runs-on: ubuntu-latest @@ -53,19 +60,20 @@ jobs: with: parallel-finished: true deploy: + if: github.event_name != 'pull_request' || github.head_ref != 'devel' name: Deploy needs: [check, test, finish] runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 + with: + fetch-depth: 0 - uses: actions/setup-python@v2 with: python-version: '3.x' - name: Install run: | sudo apt-get install -yqq pandoc - pip install setuptools_scm - git fetch --unshallow --tags pip install .[dev] make build .dockerignore snapcraft.yaml - if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') @@ -76,7 +84,6 @@ jobs: skip_existing: true - id: collect_assets name: Collect assets - shell: bash run: | echo "::set-output name=asset_path::$(ls dist/*.whl)" echo "::set-output name=asset_name::$(basename dist/*.whl)" @@ -126,7 +133,6 @@ jobs: with: use_lxd: true - name: Snap build - shell: bash run: | export SNAPCRAFT_IMAGE_INFO='{"build_url": "https://github.com/casperdcl/git-fame/actions/runs/'$GITHUB_RUN_ID'"}' sg lxd -c 'snapcraft --use-lxd' @@ -140,7 +146,6 @@ jobs: snapcraft_token: ${{ secrets.SNAP_TOKEN }} - if: github.event_name == 'push' && steps.collect_assets.outputs.snap_channel name: Snap deploy - shell: bash run: | if [ -n "$(ls git-fame*.snap 2>/dev/null)" ]; then sudo snapcraft upload git-fame*.snap --release $CHANNEL @@ -150,7 +155,7 @@ jobs: - name: Docker build push uses: elgohr/Publish-Docker-Github-Action@master with: - name: ${{ github.actor }}/git-fame + name: ${{ github.repository }} tags: ${{ steps.collect_assets.outputs.docker_tags }} password: ${{ secrets.DOCKER_PWD }} username: ${{ secrets.DOCKER_USR }} @@ -158,7 +163,7 @@ jobs: - name: Docker push GitHub uses: elgohr/Publish-Docker-Github-Action@master with: - name: ${{ github.actor }}/git-fame/git-fame + name: ${{ github.repository }}/git-fame tags: ${{ steps.collect_assets.outputs.docker_tags }} password: ${{ github.token }} username: ${{ github.actor }} diff --git a/.gitignore b/.gitignore index fad3293..9a0f108 100644 --- a/.gitignore +++ b/.gitignore @@ -4,17 +4,19 @@ *.so # Packages -*.egg-info -build/ -dist/ -snapcraft.yaml -git-fame_*_amd64.snap -.dockerignore +/gitfame/_dist_ver.py +/.eggs/ +/*.egg-info +/build/ +/dist/ +/snapcraft.yaml +/git-fame_*_amd64.snap +/.dockerignore # Unit test / coverage reports .tox/ .coverage -__pycache__ +__pycache__/ nosetests.xml # Translations diff --git a/.meta/.snapcraft.yml b/.meta/.snapcraft.yml index c108e7b..24f1e9f 100644 --- a/.meta/.snapcraft.yml +++ b/.meta/.snapcraft.yml @@ -1,7 +1,7 @@ name: git-fame summary: Pretty-print `git` repository collaborators sorted by contributions +description: https://github.com/casperdcl/git-fame version: '{version}' -adopt-info: git-fame grade: stable confinement: strict base: core18 @@ -9,11 +9,12 @@ license: MPL-2.0 parts: git-fame: plugin: python - python-packages: [tqdm, pyyaml, setuptools_scm] + python-packages: [pyyaml, setuptools>=42, 'setuptools_scm[toml]>=3.4'] + python-version: python3 stage-packages: [git] source: {source} source-commit: '{commit}' - parse-info: [setup.py] + build-packages: [git] override-build: | snapcraftctl build cp $SNAPCRAFT_PART_BUILD/git-fame_completion.bash $SNAPCRAFT_PART_INSTALL/completion.sh diff --git a/MANIFEST.in b/MANIFEST.in index 24816d6..16074aa 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,9 +1,7 @@ # Misc include .coveragerc -include LICENCE include Makefile include tox.ini -include git-fame_completion.bash # Test suite recursive-include gitfame/tests *.py @@ -11,3 +9,4 @@ recursive-include gitfame/tests *.py # Examples/Documentation include README.rst include gitfame/git-fame.1 +include git-fame_completion.bash \ No newline at end of file diff --git a/Makefile b/Makefile index 1a56398..ad93bf5 100644 --- a/Makefile +++ b/Makefile @@ -77,8 +77,7 @@ snapcraft.yaml: .meta/.snapcraft.yml -e 's/{source}/./g' > "$@" .dockerignore: - echo '*' > $@ - echo '!dist/*.whl' >> $@ + @+python -c "fd=open('.dockerignore', 'w'); fd.write('*\n!dist/*.whl\n')" distclean: @+make coverclean @@ -88,6 +87,8 @@ prebuildclean: @+python -c "import shutil; shutil.rmtree('build', True)" @+python -c "import shutil; shutil.rmtree('dist', True)" @+python -c "import shutil; shutil.rmtree('git_fame.egg-info', True)" + @+python -c "import shutil; shutil.rmtree('.eggs', True)" + @+python -c "import os; os.remove('gitfame/_dist_ver.py') if os.path.exists('gitfame/_dist_ver.py') else None" coverclean: @+python -c "import os; os.remove('.coverage') if os.path.exists('.coverage') else None" @+python -c "import shutil; shutil.rmtree('gitfame/__pycache__', True)" diff --git a/gitfame/_gitfame.py b/gitfame/_gitfame.py index 1d17cc0..545fa0d 100755 --- a/gitfame/_gitfame.py +++ b/gitfame/_gitfame.py @@ -59,29 +59,15 @@ check_output, tqdm, TqdmStream, print_unicode, Str, string_types, \ merge_stats - -def get_version_dist(name=__name__): - from pkg_resources import DistributionNotFound, get_distribution - - try: - return get_distribution(name).version - except DistributionNotFound: - return "UNKNOWN" - - +# version detector. Precedence: installed dist, git, 'UNKNOWN' try: - from setuptools_scm import get_version + from ._dist_ver import __version__ except ImportError: - ROOT = path.abspath(path.dirname(path.dirname(__file__))) - if path.exists(path.join(ROOT, ".git")): - __version__ = "UNKNOWN - please install setuptools_scm" - else: - __version__ = get_version_dist() -else: try: - __version__ = get_version(root="..", relative_to=__file__) - except LookupError: - __version__ = get_version_dist() + from setuptools_scm import get_version + __version__ = get_version(root='..', relative_to=__file__) + except (ImportError, LookupError): + __version__ = "UNKNOWN" __author__ = "Casper da Costa-Luis " __date__ = "2016-2020" __licence__ = "[MPLv2.0](https://mozilla.org/MPL/2.0/)" diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..ca661d3 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,7 @@ +[build-system] +requires = ["setuptools>=42", "wheel", "setuptools_scm[toml]>=3.4"] +build-backend = "setuptools.build_meta" + +[tool.setuptools_scm] +write_to = "gitfame/_dist_ver.py" +write_to_template = "__version__ = '{version}'\n" diff --git a/setup.cfg b/setup.cfg index b9bffee..bc9384b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -2,6 +2,93 @@ universal = 1 [flake8] -max-line-length=80 -ignore=E111,E114 -exclude=.eggs,.tox,build +ignore = E111,E114 +max_line_length = 80 +exclude = .eggs,.tox,dist,build,dist,.git,__pycache__ + +[metadata] +name = git-fame +url = https://github.com/casperdcl/git-fame +project_urls = + Changelog = https://github.com/casperdcl/git-fame/releases + Documentation = https://github.com/casperdcl/git-fame/#git-fame +license_file = LICENCE +description = Pretty-print `git` repository collaborators sorted by contributions +long_description = file: README.rst +long_description_content_type = text/x-rst +keywords = git, blame, git-blame, git-log, code-analysis, cost, loc, author, commit, shortlog, ls-files +platforms = any +provides = gitfame +# Trove classifiers (https://pypi.org/pypi?%3Aaction=list_classifiers) +classifiers = + Development Status :: 5 - Production/Stable + Environment :: Console + Environment :: MacOS X + Environment :: Other Environment + Environment :: Win32 (MS Windows) + Environment :: X11 Applications + Framework :: IPython + Intended Audience :: Developers + Intended Audience :: Education + Intended Audience :: End Users/Desktop + Intended Audience :: Other Audience + Intended Audience :: System Administrators + License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0) + Operating System :: MacOS + Operating System :: MacOS :: MacOS X + Operating System :: Microsoft + Operating System :: Microsoft :: MS-DOS + Operating System :: Microsoft :: Windows + Operating System :: POSIX + Operating System :: POSIX :: BSD + Operating System :: POSIX :: BSD :: FreeBSD + Operating System :: POSIX :: Linux + Operating System :: POSIX :: SunOS/Solaris + Operating System :: Unix + Programming Language :: Python + Programming Language :: Python :: 2 + Programming Language :: Python :: 2.7 + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.5 + Programming Language :: Python :: 3.6 + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 + Programming Language :: Python :: Implementation + Programming Language :: Python :: Implementation :: IronPython + Programming Language :: Python :: Implementation :: PyPy + Programming Language :: Unix Shell + Topic :: Desktop Environment + Topic :: Education :: Computer Aided Instruction (CAI) + Topic :: Education :: Testing + Topic :: Office/Business + Topic :: Other/Nonlisted Topic + Topic :: Software Development :: Build Tools + Topic :: Software Development :: Libraries + Topic :: Software Development :: Libraries :: Python Modules + Topic :: Software Development :: Pre-processors + Topic :: Software Development :: User Interfaces + Topic :: System :: Installation/Setup + Topic :: System :: Logging + Topic :: System :: Monitoring + Topic :: System :: Shells + Topic :: Terminals + Topic :: Utilities + +[options] +setup_requires = setuptools>=42; setuptools_scm[toml]>=3.4 +install_requires = argopt>=0.3.5; tabulate; tqdm>=4.44.0 +python_requires = >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.* +tests_require = nose; flake8; coverage +include_package_data = True +packages = find: +[options.extras_require] +yaml = pyyaml +tabulate = +full = pyyaml +dev = pyyaml; py-make>=0.1.0; twine; wheel +[options.entry_points] +console_scripts = + git-fame = gitfame:main +[options.package_data] +gitfame = git-fame.1 diff --git a/setup.py b/setup.py old mode 100644 new mode 100755 index a62cd55..7ae7b17 --- a/setup.py +++ b/setup.py @@ -2,132 +2,41 @@ # -*- coding: utf-8 -*- from ast import literal_eval from io import open as io_open -import os -try: - from setuptools import setup -except ImportError: - from distutils.core import setup +from os import path +from setuptools import setup import sys -try: - if '--cython' in sys.argv: - sys.argv.remove('--cython') - from Cython.Build import cythonize - else: - raise ImportError('--cython') -except ImportError: - def cythonize(*args, **kwargs): - return [] +src_dir = path.abspath(path.dirname(__file__)) +if sys.argv[1].lower().strip() == 'make': # exec Makefile commands + import pymake + fpath = path.join(src_dir, 'Makefile') + pymake.main(['-f', fpath] + sys.argv[2:]) + # Stop to avoid setup.py raising non-standard command error + sys.exit(0) +ext_modules = [] +if '--cython' in sys.argv: + sys.argv.remove('--cython') + try: + from Cython.Build import cythonize + ext_modules = cythonize([ + "gitfame/_gitfame.py", "gitfame/_utils.py"], nthreads=2) + except ImportError: + pass __author__ = None __licence__ = None -src_dir = os.path.abspath(os.path.dirname(__file__)) -main_file = os.path.join(src_dir, 'gitfame', '_gitfame.py') -for line in io_open(main_file, mode='r'): +for line in io_open(path.join(src_dir, 'gitfame', '_gitfame.py'), mode='r'): if line.startswith('__author__'): __author__ = literal_eval(line.split('=', 1)[1].strip()) elif line.startswith('__licence__'): __licence__ = literal_eval(line.split('=', 1)[1].strip()) -# Executing makefile commands if specified -if sys.argv[1].lower().strip() == 'make': - import pymake - # Filename of the makefile - fpath = os.path.join(src_dir, 'Makefile') - pymake.main(['-f', fpath] + sys.argv[2:]) - # Stop to avoid setup.py raising non-standard command error - sys.exit(0) - -extras_require = dict(yaml=['pyyaml'], tabulate=[]) -extras_require['full'] = list(set(sum( - extras_require.values(), []))) -extras_require['dev'] = list(set( - extras_require['full'] + ['py-make>=0.1.0', 'twine', 'wheel'])) - -README_rst = '' -fndoc = os.path.join(src_dir, 'README.rst') -with io_open(fndoc, mode='r', encoding='utf-8') as fd: - README_rst = fd.read() setup( - name='git-fame', use_scm_version=True, - setup_requires=["setuptools_scm"], - description='Pretty-print `git` repository collaborators' - ' sorted by contributions', - long_description=README_rst, license=__licence__.lstrip('[').split(']')[0], author=__author__.split('<')[0].strip(), author_email=__author__.split('<')[1][:-1], - url='https://github.com/casperdcl/git-fame', - platforms=['any'], - packages=['gitfame'], - provides=['gitfame'], - install_requires=['argopt>=0.3.5', 'tabulate', 'tqdm>=4.44.0'], - extras_require=extras_require, - entry_points={'console_scripts': ['git-fame=gitfame:main'], }, - package_data={'gitfame': ['LICENCE', 'git-fame.1']}, - ext_modules=cythonize(["gitfame/_gitfame.py", "gitfame/_utils.py"], - nthreads=2), - python_requires='>=2.7, !=3.0.*, !=3.1.*', - classifiers=[ - # Trove classifiers - # (https://pypi.org/pypi?%3Aaction=list_classifiers) - 'Development Status :: 5 - Production/Stable', - 'Environment :: Console', - 'Environment :: MacOS X', - 'Environment :: Other Environment', - 'Environment :: Win32 (MS Windows)', - 'Environment :: X11 Applications', - 'Framework :: IPython', - 'Intended Audience :: Developers', - 'Intended Audience :: Education', - 'Intended Audience :: End Users/Desktop', - 'Intended Audience :: Other Audience', - 'Intended Audience :: System Administrators', - 'License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)', - 'Operating System :: MacOS', - 'Operating System :: MacOS :: MacOS X', - 'Operating System :: Microsoft', - 'Operating System :: Microsoft :: MS-DOS', - 'Operating System :: Microsoft :: Windows', - 'Operating System :: POSIX', - 'Operating System :: POSIX :: BSD', - 'Operating System :: POSIX :: BSD :: FreeBSD', - 'Operating System :: POSIX :: Linux', - 'Operating System :: POSIX :: SunOS/Solaris', - 'Operating System :: Unix', - 'Programming Language :: Python', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: Implementation', - 'Programming Language :: Python :: Implementation :: IronPython', - 'Programming Language :: Python :: Implementation :: PyPy', - 'Programming Language :: Unix Shell', - 'Topic :: Desktop Environment', - 'Topic :: Education :: Computer Aided Instruction (CAI)', - 'Topic :: Education :: Testing', - 'Topic :: Office/Business', - 'Topic :: Other/Nonlisted Topic', - 'Topic :: Software Development :: Build Tools', - 'Topic :: Software Development :: Libraries', - 'Topic :: Software Development :: Libraries :: Python Modules', - 'Topic :: Software Development :: Pre-processors', - 'Topic :: Software Development :: User Interfaces', - 'Topic :: System :: Installation/Setup', - 'Topic :: System :: Logging', - 'Topic :: System :: Monitoring', - 'Topic :: System :: Shells', - 'Topic :: Terminals', - 'Topic :: Utilities' - ], - keywords='git blame git-blame git-log code-analysis cost loc' - ' author commit shortlog ls-files', test_suite='nose.collector', - tests_require=['nose', 'flake8', 'coverage'], + ext_modules=ext_modules, ) diff --git a/tox.ini b/tox.ini index 4a8bd77..a10cd76 100644 --- a/tox.ini +++ b/tox.ini @@ -5,13 +5,12 @@ [tox] # deprecation warning: py{26,32,33,34} -envlist = py{27,33,34,35,36,37,38,py,py3}, flake8, setup.py, nodeps +envlist = py{27,35,36,37,38,39,py,py3}, flake8, setup.py, nodeps +isolated_build = True [core] -deps = - nose -commands = - nosetests -d -v gitfame/ +deps = nose +commands = nosetests -d -v gitfame/ [coverage] deps = @@ -52,8 +51,7 @@ deps = {[extra]deps} [testenv:flake8] deps = flake8 -commands = - flake8 -j 8 --count --statistics . +commands = flake8 -j 8 --count --statistics . [testenv:setup.py] deps = From d2bf15ef3376f8bd149970320e8523684739826d Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Wed, 18 Nov 2020 12:56:30 +0000 Subject: [PATCH 09/19] CI: add reviewdog flake8 --- .github/workflows/test.yml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e4a24a0..4df2a5b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,9 +21,17 @@ jobs: - name: Test run: tox env: - TOXENV: 'flake8,setup.py,nodeps' + TOXENV: 'setup.py,nodeps' - name: Self install run: pip install -U . + - uses: reviewdog/action-setup@v1 + - name: flake8 + run: | + pip install -U flake8 + set -o pipefail + flake8 -j8 --count --statistics . | reviewdog -f=pep8 -name=Format -tee -reporter=github-check -filter-mode nofilter + env: + REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} test: if: github.event_name != 'pull_request' || github.head_ref != 'devel' strategy: From 73721574dd4f26775c65c1a663ba532a7783f358 Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Wed, 18 Nov 2020 16:46:14 +0000 Subject: [PATCH 10/19] some tidy --- .github/workflows/comment-bot.yml | 8 ++------ .meta/.snapcraft.yml | 3 +-- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/.github/workflows/comment-bot.yml b/.github/workflows/comment-bot.yml index 7b05266..37ec46d 100644 --- a/.github/workflows/comment-bot.yml +++ b/.github/workflows/comment-bot.yml @@ -1,9 +1,9 @@ name: Comment Bot on: issue_comment: - types: [created, edited] + types: [created] pull_request_review_comment: - types: [created, edited] + types: [created] jobs: tag: # /tag @@ -50,7 +50,3 @@ jobs: post({ owner: context.repo.owner, repo: context.repo.repo, comment_id: context.payload.comment.id, content: "rocket"}) - always: - runs-on: ubuntu-latest - steps: - - run: echo prevent failure when other jobs are skipped diff --git a/.meta/.snapcraft.yml b/.meta/.snapcraft.yml index 24f1e9f..82f93f5 100644 --- a/.meta/.snapcraft.yml +++ b/.meta/.snapcraft.yml @@ -9,8 +9,7 @@ license: MPL-2.0 parts: git-fame: plugin: python - python-packages: [pyyaml, setuptools>=42, 'setuptools_scm[toml]>=3.4'] - python-version: python3 + python-packages: [pyyaml] stage-packages: [git] source: {source} source-commit: '{commit}' From e4412fbf2b6247e92f390e6387a5c98287e55956 Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Sun, 29 Nov 2020 20:48:02 +0000 Subject: [PATCH 11/19] slightly more parallel CI - mostly testing https://github.com/AndreMiras/coveralls-python-action/issues/7 --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4df2a5b..f4c8e1b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -70,7 +70,7 @@ jobs: deploy: if: github.event_name != 'pull_request' || github.head_ref != 'devel' name: Deploy - needs: [check, test, finish] + needs: [check, test] runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 From 1eb53423ded123696cbebeb6a248f97698866a76 Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Wed, 2 Dec 2020 18:44:39 +0000 Subject: [PATCH 12/19] minor build upgrades --- .gitattributes | 7 ------- .github/workflows/comment-bot.yml | 2 -- .github/workflows/test.yml | 28 ++++++---------------------- LICENCE | 2 +- MANIFEST.in | 12 ------------ setup.cfg | 19 ++++++++++--------- setup.py | 19 +------------------ 7 files changed, 18 insertions(+), 71 deletions(-) delete mode 100644 .gitattributes delete mode 100644 MANIFEST.in diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index f345201..0000000 --- a/.gitattributes +++ /dev/null @@ -1,7 +0,0 @@ -[attr]py-file eol=lf -*.py py-file - -.git* export-ignore -MANIFEST.in export-ignore -codecov.yml export-ignore -.git-fame.1.md export-ignore diff --git a/.github/workflows/comment-bot.yml b/.github/workflows/comment-bot.yml index 37ec46d..4451632 100644 --- a/.github/workflows/comment-bot.yml +++ b/.github/workflows/comment-bot.yml @@ -4,7 +4,6 @@ on: types: [created] pull_request_review_comment: types: [created] - jobs: tag: # /tag if: startsWith(github.event.comment.body, '/tag ') @@ -21,7 +20,6 @@ jobs: post = (context.eventName == "issue_comment" ? github.reactions.createForIssueComment : github.reactions.createForPullRequestReviewComment) - if (!["admin", "write"].includes(perm.data.permission)){ post({ owner: context.repo.owner, repo: context.repo.repo, diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f4c8e1b..c9dfc50 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -136,30 +136,14 @@ jobs: asset_path: ${{ steps.collect_assets.outputs.asset_path_sig }} asset_name: ${{ steps.collect_assets.outputs.asset_name_sig }} asset_content_type: text/plain - - name: Snap install - uses: samuelmeuli/action-snapcraft@v1 - with: - use_lxd: true - - name: Snap build - run: | - export SNAPCRAFT_IMAGE_INFO='{"build_url": "https://github.com/casperdcl/git-fame/actions/runs/'$GITHUB_RUN_ID'"}' - sg lxd -c 'snapcraft --use-lxd' - env: - SNAPCRAFT_BUILD_INFO: 1 # https://snapcraft.io/blog/introducing-developer-notifications-for-snap-security-updates + - uses: snapcore/action-build@v1 + id: snap_build - if: github.event_name == 'push' && steps.collect_assets.outputs.snap_channel - name: Snap login - uses: samuelmeuli/action-snapcraft@v1 + uses: snapcore/action-publish@v1 with: - skip_install: true - snapcraft_token: ${{ secrets.SNAP_TOKEN }} - - if: github.event_name == 'push' && steps.collect_assets.outputs.snap_channel - name: Snap deploy - run: | - if [ -n "$(ls git-fame*.snap 2>/dev/null)" ]; then - sudo snapcraft upload git-fame*.snap --release $CHANNEL - fi - env: - CHANNEL: ${{ steps.collect_assets.outputs.snap_channel }} + store_login: ${{ secrets.SNAP_TOKEN }} + snap: ${{ steps.snap_build.outputs.snap }} + release: ${{ steps.collect_assets.outputs.snap_channel }} - name: Docker build push uses: elgohr/Publish-Docker-Github-Action@master with: diff --git a/LICENCE b/LICENCE index 8202689..f0b17f7 100644 --- a/LICENCE +++ b/LICENCE @@ -1,5 +1,5 @@ * files: * - MPLv2.0 2016-2019 (c) Casper da Costa-Luis + MPLv2.0 2016-2020 (c) Casper da Costa-Luis [casperdcl](https://github.com/casperdcl). diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 16074aa..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,12 +0,0 @@ -# Misc -include .coveragerc -include Makefile -include tox.ini - -# Test suite -recursive-include gitfame/tests *.py - -# Examples/Documentation -include README.rst -include gitfame/git-fame.1 -include git-fame_completion.bash \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index bc9384b..7330cde 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,21 +1,16 @@ -[bdist_wheel] -universal = 1 - -[flake8] -ignore = E111,E114 -max_line_length = 80 -exclude = .eggs,.tox,dist,build,dist,.git,__pycache__ - [metadata] name = git-fame url = https://github.com/casperdcl/git-fame project_urls = Changelog = https://github.com/casperdcl/git-fame/releases Documentation = https://github.com/casperdcl/git-fame/#git-fame +licence = MPL 2.0 license_file = LICENCE description = Pretty-print `git` repository collaborators sorted by contributions long_description = file: README.rst long_description_content_type = text/x-rst +author = Casper da Costa-Luis +author_email = casper.dcl@physics.org keywords = git, blame, git-blame, git-log, code-analysis, cost, loc, author, commit, shortlog, ls-files platforms = any provides = gitfame @@ -74,7 +69,6 @@ classifiers = Topic :: System :: Shells Topic :: Terminals Topic :: Utilities - [options] setup_requires = setuptools>=42; setuptools_scm[toml]>=3.4 install_requires = argopt>=0.3.5; tabulate; tqdm>=4.44.0 @@ -92,3 +86,10 @@ console_scripts = git-fame = gitfame:main [options.package_data] gitfame = git-fame.1 +[bdist_wheel] +universal = 1 + +[flake8] +ignore = E111,E114 +max_line_length = 88 +exclude = .eggs,.tox,dist,build,dist,.git,__pycache__ diff --git a/setup.py b/setup.py index 7ae7b17..6045051 100755 --- a/setup.py +++ b/setup.py @@ -1,7 +1,5 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- -from ast import literal_eval -from io import open as io_open from os import path from setuptools import setup import sys @@ -24,19 +22,4 @@ except ImportError: pass -__author__ = None -__licence__ = None -for line in io_open(path.join(src_dir, 'gitfame', '_gitfame.py'), mode='r'): - if line.startswith('__author__'): - __author__ = literal_eval(line.split('=', 1)[1].strip()) - elif line.startswith('__licence__'): - __licence__ = literal_eval(line.split('=', 1)[1].strip()) - -setup( - use_scm_version=True, - license=__licence__.lstrip('[').split(']')[0], - author=__author__.split('<')[0].strip(), - author_email=__author__.split('<')[1][:-1], - test_suite='nose.collector', - ext_modules=ext_modules, -) +setup(use_scm_version=True, test_suite='nose.collector', ext_modules=ext_modules) From 3a944eac1f97bd86ed024eb3d660f02ed3aed36f Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Wed, 2 Dec 2020 21:33:44 +0000 Subject: [PATCH 13/19] sanitise and split snap build --- .github/workflows/test.yml | 40 ++++++++++++++++++-------- .gitignore | 1 - Makefile | 8 ------ .meta/.snapcraft.yml => snapcraft.yaml | 15 +++++++--- 4 files changed, 39 insertions(+), 25 deletions(-) rename .meta/.snapcraft.yml => snapcraft.yaml (52%) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c9dfc50..c933ca7 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -83,7 +83,7 @@ jobs: run: | sudo apt-get install -yqq pandoc pip install .[dev] - make build .dockerignore snapcraft.yaml + make build .dockerignore - if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') uses: casperdcl/deploy-pypi@v1 with: @@ -99,13 +99,10 @@ jobs: echo "::set-output name=asset_name_sig::$(basename dist/*.whl.asc 2>/dev/null)" if [[ $GITHUB_REF == refs/tags/v* ]]; then echo ::set-output name=docker_tags::latest,${GITHUB_REF/refs\/tags\/v/} - echo ::set-output name=snap_channel::stable elif [[ $GITHUB_REF == refs/heads/master ]]; then echo ::set-output name=docker_tags::master - echo ::set-output name=snap_channel::candidate elif [[ $GITHUB_REF == refs/heads/devel ]]; then echo ::set-output name=docker_tags::devel - echo ::set-output name=snap_channel::edge fi git log --pretty='format:%d%n- %s%n%b---' $(git tag --sort=v:refname | tail -n2 | head -n1)..HEAD > _CHANGES.md - if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') @@ -136,14 +133,6 @@ jobs: asset_path: ${{ steps.collect_assets.outputs.asset_path_sig }} asset_name: ${{ steps.collect_assets.outputs.asset_name_sig }} asset_content_type: text/plain - - uses: snapcore/action-build@v1 - id: snap_build - - if: github.event_name == 'push' && steps.collect_assets.outputs.snap_channel - uses: snapcore/action-publish@v1 - with: - store_login: ${{ secrets.SNAP_TOKEN }} - snap: ${{ steps.snap_build.outputs.snap }} - release: ${{ steps.collect_assets.outputs.snap_channel }} - name: Docker build push uses: elgohr/Publish-Docker-Github-Action@master with: @@ -161,3 +150,30 @@ jobs: username: ${{ github.actor }} registry: docker.pkg.github.com no_push: ${{ steps.collect_assets.outputs.docker_tags == '' }} + deploy-snap: + if: github.event_name != 'pull_request' || github.head_ref != 'devel' + name: Deploy Snap + needs: [check, test] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + with: + fetch-depth: 0 + - id: snap_channel + name: Snap channel + run: | + if [[ $GITHUB_REF == refs/tags/v* ]]; then + echo ::set-output name=release::stable,candidate + elif [[ $GITHUB_REF == refs/heads/master ]]; then + echo ::set-output name=release::candidate + elif [[ $GITHUB_REF == refs/heads/devel ]]; then + echo ::set-output name=release::edge + fi + - id: snap_build + uses: snapcore/action-build@v1 + - if: github.event_name == 'push' && steps.snap_channel.outputs.release + uses: snapcore/action-publish@v1 + with: + store_login: ${{ secrets.SNAP_TOKEN }} + snap: ${{ steps.snap_build.outputs.snap }} + release: ${{ steps.snap_channel.outputs.release }} diff --git a/.gitignore b/.gitignore index 9a0f108..76836fb 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,6 @@ /*.egg-info /build/ /dist/ -/snapcraft.yaml /git-fame_*_amd64.snap /.dockerignore diff --git a/Makefile b/Makefile index ad93bf5..a8b0165 100644 --- a/Makefile +++ b/Makefile @@ -71,11 +71,6 @@ gitfame/git-fame.1: .meta/.git-fame.1.md gitfame/_gitfame.py cat "$<" - |\ pandoc -o "$@" -s -t man -snapcraft.yaml: .meta/.snapcraft.yml - cat "$<" | sed -e "s/{version}/$$(python -m gitfame --version 2>&1)/g" \ - -e "s/{commit}/$$(git describe --always)/g" \ - -e 's/{source}/./g' > "$@" - .dockerignore: @+python -c "fd=open('.dockerignore', 'w'); fd.write('*\n!dist/*.whl\n')" @@ -123,9 +118,6 @@ buildupload: @make build @make pypi -snap: - @make -B snapcraft.yaml - snapcraft docker: @make build @make .dockerignore diff --git a/.meta/.snapcraft.yml b/snapcraft.yaml similarity index 52% rename from .meta/.snapcraft.yml rename to snapcraft.yaml index 82f93f5..93f2067 100644 --- a/.meta/.snapcraft.yml +++ b/snapcraft.yaml @@ -1,7 +1,7 @@ name: git-fame summary: Pretty-print `git` repository collaborators sorted by contributions description: https://github.com/casperdcl/git-fame -version: '{version}' +adopt-info: git-fame grade: stable confinement: strict base: core18 @@ -10,13 +10,20 @@ parts: git-fame: plugin: python python-packages: [pyyaml] - stage-packages: [git] - source: {source} - source-commit: '{commit}' + source: . + build-snaps: + - snapd build-packages: [git] + stage-packages: [git] override-build: | snapcraftctl build + # prevent user site packages interfering with this snap - reference: + # https://github.com/snapcore/snapcraft/blob/19393ef36cd773a28131cec10cc0bfb3bf9c7e77/tools/snapcraft-override-build.sh#L18 + sed -ri 's/^(ENABLE_USER_SITE = )None$/\1False/' $SNAPCRAFT_PART_INSTALL/usr/lib/python*/site.py cp $SNAPCRAFT_PART_BUILD/git-fame_completion.bash $SNAPCRAFT_PART_INSTALL/completion.sh + override-stage: | + snapcraftctl stage + snapcraftctl set-version $(usr/bin/python3 -m gitfame --version) apps: git-fame: command: bin/git-fame From f464a0fe44df1fafdab45655ba51cb6aa3a0dab3 Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Wed, 2 Dec 2020 21:56:55 +0000 Subject: [PATCH 14/19] add pre-commit hooks --- .github/workflows/test.yml | 27 +++++++++++++++++++-------- .pre-commit-config.yaml | 28 ++++++++++++++++++++++++++++ setup.cfg | 6 +++++- 3 files changed, 52 insertions(+), 9 deletions(-) create mode 100644 .pre-commit-config.yaml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c933ca7..1a35d3a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -16,22 +16,33 @@ jobs: - uses: actions/setup-python@v2 with: python-version: '3.x' - - name: Install - run: pip install -U tox + - name: set PYSHA + run: echo "PYSHA=$(python -VV | sha256sum | cut -d' ' -f1)" >> $GITHUB_ENV + - uses: actions/cache@v1 + with: + path: ~/.cache/pre-commit + key: pre-commit|${{ env.PYSHA }}|${{ hashFiles('.pre-commit-config.yaml') }} - name: Test - run: tox + run: | + pip install -U tox + tox env: TOXENV: 'setup.py,nodeps' - name: Self install - run: pip install -U . + run: pip install -U .[dev] + - name: Build + run: | + python setup.py sdist bdist_wheel + twine check dist/* - uses: reviewdog/action-setup@v1 - - name: flake8 + - if: github.event_name != 'schedule' + name: flake8 run: | - pip install -U flake8 - set -o pipefail - flake8 -j8 --count --statistics . | reviewdog -f=pep8 -name=Format -tee -reporter=github-check -filter-mode nofilter + pre-commit run -a flake8 | reviewdog -f=pep8 -name=Format -tee -reporter=github-check -filter-mode nofilter env: REVIEWDOG_GITHUB_API_TOKEN: ${{ secrets.GITHUB_TOKEN }} + - name: Lint + run: pre-commit run -a --show-diff-on-failure test: if: github.event_name != 'pull_request' || github.head_ref != 'devel' strategy: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..c6c7843 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,28 @@ +default_language_version: + python: python3 +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v3.3.0 + hooks: + - id: check-added-large-files + - id: check-case-conflict + - id: check-docstring-first + - id: check-executables-have-shebangs + - id: check-toml + - id: check-yaml + - id: end-of-file-fixer + - id: mixed-line-ending + - id: trailing-whitespace +- hooks: + - id: flake8 + additional_dependencies: + - flake8-bugbear + - flake8-comprehensions + - flake8-debugger + - flake8-string-format + repo: https://gitlab.com/pycqa/flake8 + rev: 3.8.4 +- hooks: + - id: isort + repo: https://github.com/timothycrosley/isort + rev: 5.6.4 diff --git a/setup.cfg b/setup.cfg index 7330cde..e6a68fa 100644 --- a/setup.cfg +++ b/setup.cfg @@ -80,7 +80,7 @@ packages = find: yaml = pyyaml tabulate = full = pyyaml -dev = pyyaml; py-make>=0.1.0; twine; wheel +dev = pyyaml; py-make>=0.1.0; twine; wheel; pre-commit [options.entry_points] console_scripts = git-fame = gitfame:main @@ -93,3 +93,7 @@ universal = 1 ignore = E111,E114 max_line_length = 88 exclude = .eggs,.tox,dist,build,dist,.git,__pycache__ + +[isort] +profile = black +known_first_party = gitfame,tests From 692a02aa71812a87499f4e6f5d4f4fde4c5b79fc Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Wed, 2 Dec 2020 21:58:06 +0000 Subject: [PATCH 15/19] lint --- gitfame/__init__.py | 16 +++++++----- gitfame/__main__.py | 1 + gitfame/_gitfame.py | 47 ++++++++++++++++++++++------------ gitfame/_utils.py | 7 ++--- gitfame/tests/tests_gitfame.py | 16 +++++++----- gitfame/tests/tests_utils.py | 1 + setup.py | 3 ++- 7 files changed, 56 insertions(+), 35 deletions(-) diff --git a/gitfame/__init__.py b/gitfame/__init__.py index 732cdcf..72cf18a 100644 --- a/gitfame/__init__.py +++ b/gitfame/__init__.py @@ -1,10 +1,12 @@ -from ._gitfame import __author__ -from ._gitfame import __date__ -from ._gitfame import __licence__ -from ._gitfame import __copyright__ -from ._gitfame import __version__ -from ._gitfame import __license__ -from ._gitfame import main +from ._gitfame import ( + __author__, + __copyright__, + __date__, + __licence__, + __license__, + __version__, + main, +) __all__ = ['main', '__author__', '__date__', '__licence__', '__copyright__', '__version__', '__license__'] diff --git a/gitfame/__main__.py b/gitfame/__main__.py index 938f954..7d70827 100644 --- a/gitfame/__main__.py +++ b/gitfame/__main__.py @@ -1,2 +1,3 @@ from ._gitfame import main # pragma: no cover + main() # pragma: no cover diff --git a/gitfame/_gitfame.py b/gitfame/_gitfame.py index 545fa0d..073e2d6 100755 --- a/gitfame/_gitfame.py +++ b/gitfame/_gitfame.py @@ -45,19 +45,30 @@ --manpath= Directory in which to install git-fame man pages. --log= FATAL|CRITICAL|ERROR|WARN(ING)|[default: INFO]|DEBUG|NOTSET. """ -from __future__ import print_function -from __future__ import division -# from __future__ import absolute_import -from functools import partial +from __future__ import division, print_function + import logging import os -from os import path import re import subprocess -from ._utils import TERM_WIDTH, int_cast_or_len, fext, _str, \ - check_output, tqdm, TqdmStream, print_unicode, Str, string_types, \ - merge_stats +# from __future__ import absolute_import +from functools import partial +from os import path + +from ._utils import ( + TERM_WIDTH, + Str, + TqdmStream, + _str, + check_output, + fext, + int_cast_or_len, + merge_stats, + print_unicode, + string_types, + tqdm, +) # version detector. Precedence: installed dist, git, 'UNKNOWN' try: @@ -164,9 +175,8 @@ def tabulate( if backend in ['yaml', 'yml', 'json', 'csv', 'tsv']: tab = [i[:-1] + [float(pc.strip()) for pc in i[-1].split('/')] for i in tab] - tab = dict( - total=stats_tot, data=tab, - columns=COL_NAMES[:-1] + ['%' + i for i in COL_NAMES[-4:-1]]) + tab = {"total": stats_tot, "data": tab, + "columns": COL_NAMES[:-1] + ['%' + i for i in COL_NAMES[-4:-1]]} if backend in ['yaml', 'yml']: log.debug("backend:yaml") from yaml import safe_dump as tabber @@ -178,6 +188,7 @@ def tabulate( elif backend in ['csv', 'tsv']: log.debug("backend:csv") from csv import writer as tabber + from ._utils import StringIO res = StringIO() t = tabber(res, delimiter=',' if backend == 'csv' else '\t') @@ -249,7 +260,7 @@ def stats_append(fname, auth, loc, tstamp): try: auth_stats[auth]["loc"] += loc except KeyError: - auth_stats[auth] = {"loc": loc, "files": set([fname]), "ctimes": []} + auth_stats[auth] = {"loc": loc, "files": {fname}, "ctimes": []} else: auth_stats[auth]["files"].add(fname) auth_stats[auth]["ctimes"].append(tstamp) @@ -328,7 +339,7 @@ def stats_append(fname, auth, loc, tstamp): auth_stats[auth]["commits"] += int(ncom) except KeyError: auth_stats[auth] = {"loc": 0, - "files": set([]), + "files": set(), "commits": int(ncom), "ctimes": []} if show_email: @@ -338,7 +349,7 @@ def stats_append(fname, auth, loc, tstamp): auth_stats = {} for auth, stats in getattr(old, 'iteritems', old.items)(): i = auth_stats.setdefault(auth2em[auth], {"loc": 0, - "files": set([]), + "files": set(), "commits": 0, "ctimes": []}) i["loc"] += stats["loc"] @@ -402,6 +413,7 @@ def run(args): if len(gitdirs) > 1: try: from concurrent.futures import ThreadPoolExecutor # NOQA + from tqdm.contrib.concurrent import thread_map mapper = partial(thread_map, desc="Repos", unit="repo", miniters=1, disable=args.silent_progress or len(gitdirs) <= 1) @@ -417,7 +429,7 @@ def run(args): else: auth_stats[auth] = stats - stats_tot = dict((k, 0) for stats in auth_stats.values() for k in stats) + stats_tot = {k: 0 for stats in auth_stats.values() for k in stats} log.debug(stats_tot) for k in stats_tot: stats_tot[k] = sum(int_cast_or_len(stats.get(k, 0)) @@ -452,10 +464,11 @@ def main(args=None): log.debug(args) if args.manpath is not None: + import sys from os import path from shutil import copyfile - from pkg_resources import resource_filename, Requirement - import sys + + from pkg_resources import Requirement, resource_filename fi = resource_filename(Requirement.parse('git-fame'), 'gitfame/git-fame.1') fo = path.join(args.manpath, 'git-fame.1') copyfile(fi, fo) diff --git a/gitfame/_utils.py b/gitfame/_utils.py index e9ba0cc..0dcf92e 100644 --- a/gitfame/_utils.py +++ b/gitfame/_utils.py @@ -1,8 +1,9 @@ from __future__ import print_function -from functools import partial -import sys -import subprocess + import logging +import subprocess +import sys +from functools import partial from tqdm import tqdm as tqdm_std from tqdm.utils import _screen_shape_wrapper diff --git a/gitfame/tests/tests_gitfame.py b/gitfame/tests/tests_gitfame.py index 618bc95..d6f3c1c 100644 --- a/gitfame/tests/tests_gitfame.py +++ b/gitfame/tests/tests_gitfame.py @@ -4,26 +4,28 @@ from os import path from shutil import rmtree from tempfile import mkdtemp +from textwrap import dedent + # import re # from nose import with_setup from nose.plugins.skip import SkipTest + +from gitfame import _gitfame, main + # from io import IOBase # to support unicode strings from gitfame._utils import StringIO -from gitfame import _gitfame -from gitfame import main -from textwrap import dedent # test data auth_stats = { - u'Not Committed Yet': {'files': set([ + u'Not Committed Yet': {'files': { 'gitfame/_gitfame.py', 'gitfame/_utils.py', 'Makefile', 'MANIFEST.in' - ]), + }, 'loc': 75, 'ctimes': [], 'commits': 0}, - u'Casper da Costa-Luis': {'files': set([ + u'Casper da Costa-Luis': {'files': { 'gitfame/_utils.py', 'gitfame/__main__.py', 'setup.cfg', 'gitfame/_gitfame.py', 'gitfame/__init__.py', 'git-fame_completion.bash', 'Makefile', 'MANIFEST.in', '.gitignore', - 'setup.py']), 'loc': 538, 'ctimes': [ + 'setup.py'}, 'loc': 538, 'ctimes': [ 1510942009, 1517426360, 1532103452, 1543323944, 1548030670, 1459558286, 1510942009, 1459559144, 1481150373, 1510942009, 1548030670, 1517178199, 1481150379, 1517426360, 1548030670, 1459625059, 1510942009, 1517426360, diff --git a/gitfame/tests/tests_utils.py b/gitfame/tests/tests_utils.py index 1c1491e..cce5d3d 100644 --- a/gitfame/tests/tests_utils.py +++ b/gitfame/tests/tests_utils.py @@ -1,4 +1,5 @@ from __future__ import unicode_literals + from gitfame import _utils diff --git a/setup.py b/setup.py index 6045051..dc57a78 100755 --- a/setup.py +++ b/setup.py @@ -1,8 +1,9 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- +import sys from os import path + from setuptools import setup -import sys src_dir = path.abspath(path.dirname(__file__)) if sys.argv[1].lower().strip() == 'make': # exec Makefile commands From e2a91bf11ff55953135dcd4a8df2c8dadb4eed96 Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Wed, 2 Dec 2020 22:29:25 +0000 Subject: [PATCH 16/19] add `--loc=auto` --- git-fame_completion.bash | 2 +- gitfame/_gitfame.py | 20 ++++++++++++++++---- gitfame/tests/tests_gitfame.py | 7 +++---- 3 files changed, 20 insertions(+), 9 deletions(-) diff --git a/git-fame_completion.bash b/git-fame_completion.bash index 067e24b..c85a8aa 100644 --- a/git-fame_completion.bash +++ b/git-fame_completion.bash @@ -16,7 +16,7 @@ _git_fame() COMPREPLY=($(compgen -W 'months cocomo hours commits' -- ${cur})) ;; --loc) - COMPREPLY=($(compgen -W 'surviving insertions deletions' -- ${cur})) + COMPREPLY=($(compgen -W 'auto surviving insertions deletions' -- ${cur})) ;; --format) COMPREPLY=($(compgen -W 'pipe markdown yaml json csv tsv tabulate' -- ${cur})) diff --git a/gitfame/_gitfame.py b/gitfame/_gitfame.py index 073e2d6..406b776 100755 --- a/gitfame/_gitfame.py +++ b/gitfame/_gitfame.py @@ -12,8 +12,9 @@ -v, --version Print module version and exit. --branch= Branch or tag [default: HEAD] up to which to check. --sort= [default: loc]|commits|files|hours|months. - --loc= [default: surviving]|ins(ertions)|del(etions) + --loc= [default: auto]|surviving|ins(ertions)|del(etions) What `loc` represents. Use 'ins,del' to count both. + 'auto' means 'surviving' unless `--cost` is specified. --excl= Excluded files (default: None). In no-regex mode, may be a comma-separated list. Escape (\,) for a literal comma (may require \\, in shell). @@ -24,6 +25,8 @@ person-hours (based on commit times). Methods: month(s)|cocomo|hour(s)|commit(s). May be multiple comma-separated values. + Alters meaning of `--loc='auto'` to mean 'ins' (COCOMO) or + 'ins,del' (hours). -n, --no-regex Assume are comma-separated exact matches rather than regular expressions [default: False]. NB: if regex is enabled ',' is equivalent to '|'. @@ -145,7 +148,6 @@ def tabulate( ))).replace('/100.0/', '/ 100/')] for (auth, s) in it_as()] if cost: - cost = set(cost.lower().split(',')) stats_tot = dict(stats_tot) if cost & COST_MONTHS: COL_NAMES.insert(1, 'mths') @@ -398,6 +400,16 @@ def run(args): include_files = re.compile(args.incl) # include_files = re.compile(args.incl, flags=re.M) + cost = set(args.cost.lower().split(',')) if args.cost else set() + churn = args.loc + if churn == "auto": + if cost & COST_HOURS: + churn = "ins,del" + elif cost & COST_MONTHS: + churn = "ins" + else: + churn = "surviving" + auth_stats = {} statter = partial( _get_auth_stats, @@ -407,7 +419,7 @@ def run(args): ignore_whitespace=args.ignore_whitespace, M=args.M, C=args.C, warn_binary=args.warn_binary, bytype=args.bytype, show_email=args.show_email, prefix_gitdir=len(gitdirs) > 1, - churn=args.loc) + churn=churn) # concurrent multi-repo processing if len(gitdirs) > 1: @@ -445,7 +457,7 @@ def run(args): print_unicode(tabulate( auth_stats, stats_tot, - args.sort, args.bytype, args.format, args.cost, args.enum)) + args.sort, args.bytype, args.format, cost, args.enum)) def get_main_parser(): diff --git a/gitfame/tests/tests_gitfame.py b/gitfame/tests/tests_gitfame.py index d6f3c1c..8b4ea0b 100644 --- a/gitfame/tests/tests_gitfame.py +++ b/gitfame/tests/tests_gitfame.py @@ -1,6 +1,7 @@ from __future__ import unicode_literals import sys +from json import loads from os import path from shutil import rmtree from tempfile import mkdtemp @@ -53,8 +54,8 @@ def test_tabulate(): def test_tabulate_cost(): """Test cost estimates""" - assert (_gitfame.tabulate( - auth_stats, stats_tot, cost="hours,COCOMO") == dedent("""\ + assert (_gitfame.tabulate(auth_stats, stats_tot, cost={"hours", "months"}) == dedent( + """\ Total commits: 35 Total files: 14 Total hours: 5.5 @@ -116,7 +117,6 @@ def test_tabulate_yaml(): def test_tabulate_json(): """Test JSON tabulate""" - from json import loads res = loads(_gitfame.tabulate(auth_stats, stats_tot, backend='json')) assert (res == loads(dedent("""\ {"total": {"files": 14, "loc": 613, "commits": 35}, @@ -151,7 +151,6 @@ def test_tabulate_tabulate(): def test_tabulate_enum(): """Test --enum tabulate""" - from json import loads res = loads(_gitfame.tabulate( auth_stats, stats_tot, backend='json', row_nums=True)) assert res['columns'][0] == '#' From 911f39095b0a12c1f4dc06604710106740a77db3 Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Wed, 2 Dec 2020 23:02:04 +0000 Subject: [PATCH 17/19] test coverage --- gitfame/tests/tests_gitfame.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/gitfame/tests/tests_gitfame.py b/gitfame/tests/tests_gitfame.py index 8b4ea0b..57c36f5 100644 --- a/gitfame/tests/tests_gitfame.py +++ b/gitfame/tests/tests_gitfame.py @@ -217,6 +217,10 @@ def test_main(): ['--no-regex'], ['--no-regex', '--incl', 'setup.py,README.rst'], ['--excl', r'.*\.py'], + ['--loc', 'ins,del'], + ['--cost', 'hour'], + ['--cost', 'month'], + ['-e'], ['-w'], ['-M'], ['-C'], From 742ebd430f08bc9b0a0cd0800755f1f6ddb48ac3 Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Wed, 2 Dec 2020 23:25:36 +0000 Subject: [PATCH 18/19] replace `--loc=auto` with unspecified --- git-fame_completion.bash | 2 +- gitfame/_gitfame.py | 24 ++++++++++++++---------- gitfame/tests/tests_gitfame.py | 1 + 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/git-fame_completion.bash b/git-fame_completion.bash index c85a8aa..067e24b 100644 --- a/git-fame_completion.bash +++ b/git-fame_completion.bash @@ -16,7 +16,7 @@ _git_fame() COMPREPLY=($(compgen -W 'months cocomo hours commits' -- ${cur})) ;; --loc) - COMPREPLY=($(compgen -W 'auto surviving insertions deletions' -- ${cur})) + COMPREPLY=($(compgen -W 'surviving insertions deletions' -- ${cur})) ;; --format) COMPREPLY=($(compgen -W 'pipe markdown yaml json csv tsv tabulate' -- ${cur})) diff --git a/gitfame/_gitfame.py b/gitfame/_gitfame.py index 406b776..e963bdd 100755 --- a/gitfame/_gitfame.py +++ b/gitfame/_gitfame.py @@ -12,9 +12,9 @@ -v, --version Print module version and exit. --branch= Branch or tag [default: HEAD] up to which to check. --sort= [default: loc]|commits|files|hours|months. - --loc= [default: auto]|surviving|ins(ertions)|del(etions) + --loc= surviving|ins(ertions)|del(etions) What `loc` represents. Use 'ins,del' to count both. - 'auto' means 'surviving' unless `--cost` is specified. + defaults to 'surviving' unless `--cost` is specified. --excl= Excluded files (default: None). In no-regex mode, may be a comma-separated list. Escape (\,) for a literal comma (may require \\, in shell). @@ -25,7 +25,7 @@ person-hours (based on commit times). Methods: month(s)|cocomo|hour(s)|commit(s). May be multiple comma-separated values. - Alters meaning of `--loc='auto'` to mean 'ins' (COCOMO) or + Alters `--loc` default to imply 'ins' (COCOMO) or 'ins,del' (hours). -n, --no-regex Assume are comma-separated exact matches rather than regular expressions [default: False]. @@ -223,7 +223,7 @@ def _get_auth_stats( gitdir, branch="HEAD", since=None, include_files=None, exclude_files=None, silent_progress=False, ignore_whitespace=False, M=False, C=False, warn_binary=False, bytype=False, show_email=False, - prefix_gitdir=False, churn="surviving"): + prefix_gitdir=False, churn=None): """Returns dict: {"": {"loc": int, "files": {}, "commits": int, "ctimes": [int]}}""" since = ["--since", since] if since else [] @@ -240,7 +240,7 @@ def _get_auth_stats( if include_files.search(i) if not (exclude_files and exclude_files.search(i))] log.log(logging.NOTSET, "files:\n" + '\n'.join(file_list)) - churn = set(churn.lower().split(',')) + churn = churn or set() if churn & CHURN_SLOC: base_cmd = git_cmd + ["blame", "--line-porcelain"] + since @@ -401,14 +401,18 @@ def run(args): # include_files = re.compile(args.incl, flags=re.M) cost = set(args.cost.lower().split(',')) if args.cost else set() - churn = args.loc - if churn == "auto": + churn = set(args.loc.lower().split(',')) if args.loc else set() + if not churn: if cost & COST_HOURS: - churn = "ins,del" + churn = CHURN_INS | CHURN_DEL elif cost & COST_MONTHS: - churn = "ins" + churn = CHURN_INS else: - churn = "surviving" + churn = CHURN_SLOC + + if churn & (CHURN_INS | CHURN_DEL) and args.excl: + log.warn("--loc=ins,del includes historical files" + " which may need to be added to --excl") auth_stats = {} statter = partial( diff --git a/gitfame/tests/tests_gitfame.py b/gitfame/tests/tests_gitfame.py index 57c36f5..0a55913 100644 --- a/gitfame/tests/tests_gitfame.py +++ b/gitfame/tests/tests_gitfame.py @@ -220,6 +220,7 @@ def test_main(): ['--loc', 'ins,del'], ['--cost', 'hour'], ['--cost', 'month'], + ['--cost', 'month', '--excl', r'.*\.py'], ['-e'], ['-w'], ['-M'], From ad7e1fcae45e0831be56088a167047a5039a4d3d Mon Sep 17 00:00:00 2001 From: Casper da Costa-Luis Date: Wed, 2 Dec 2020 23:37:26 +0000 Subject: [PATCH 19/19] update pkg_resources & deps --- gitfame/_gitfame.py | 4 ++-- setup.cfg | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/gitfame/_gitfame.py b/gitfame/_gitfame.py index e963bdd..afa46c8 100755 --- a/gitfame/_gitfame.py +++ b/gitfame/_gitfame.py @@ -484,8 +484,8 @@ def main(args=None): from os import path from shutil import copyfile - from pkg_resources import Requirement, resource_filename - fi = resource_filename(Requirement.parse('git-fame'), 'gitfame/git-fame.1') + from pkg_resources import resource_filename + fi = resource_filename(__name__, 'git-fame.1') fo = path.join(args.manpath, 'git-fame.1') copyfile(fi, fo) log.info("written:" + fo) diff --git a/setup.cfg b/setup.cfg index e6a68fa..17554b6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -71,7 +71,7 @@ classifiers = Topic :: Utilities [options] setup_requires = setuptools>=42; setuptools_scm[toml]>=3.4 -install_requires = argopt>=0.3.5; tabulate; tqdm>=4.44.0 +install_requires = argopt>=0.3.5; setuptools; tabulate; tqdm>=4.44.0 python_requires = >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.* tests_require = nose; flake8; coverage include_package_data = True