gitlint: Binary .local/bin/gitlint missing after update from 0.16.0 to 0.17.0
After upgrading to the latest release 0.17.0 the executable is gone. Uninstalling and installing again does not help. When installing 0.16.0 all is fine.
After installing 0.17.0 …
$ pip3 install --user -U gitlint
Collecting gitlint
Using cached gitlint-0.17.0-py2.py3-none-any.whl (2.7 kB)
Requirement already satisfied: gitlint-core[trusted-deps]==0.17.0 in ./.local/lib/python3.6/site-packages (from gitlint) (0.17.0)
Requirement already satisfied: arrow>=1 in ./.local/lib/python3.6/site-packages (from gitlint-core[trusted-deps]==0.17.0->gitlint) (1.2.1)
Requirement already satisfied: Click>=8 in ./.local/lib/python3.6/site-packages (from gitlint-core[trusted-deps]==0.17.0->gitlint) (8.0.3)
Requirement already satisfied: sh>=1.13.0 in ./.local/lib/python3.6/site-packages (from gitlint-core[trusted-deps]==0.17.0->gitlint) (1.14.2)
Requirement already satisfied: python-dateutil>=2.7.0 in ./.local/lib/python3.6/site-packages (from arrow>=1->gitlint-core[trusted-deps]==0.17.0->gitlint) (2.8.0)
Requirement already satisfied: typing-extensions in ./.local/lib/python3.6/site-packages (from arrow>=1->gitlint-core[trusted-deps]==0.17.0->gitlint) (3.7.4.3)
Requirement already satisfied: importlib-metadata in ./.local/lib/python3.6/site-packages (from Click>=8->gitlint-core[trusted-deps]==0.17.0->gitlint) (4.8.1)
Requirement already satisfied: six>=1.5 in /usr/lib/python3.6/site-packages (from python-dateutil>=2.7.0->arrow>=1->gitlint-core[trusted-deps]==0.17.0->gitlint) (1.11.0)
Requirement already satisfied: zipp>=0.5 in ./.local/lib/python3.6/site-packages (from importlib-metadata->Click>=8->gitlint-core[trusted-deps]==0.17.0->gitlint) (3.6.0)
Installing collected packages: gitlint
Successfully installed gitlint-0.17.0
WARNING: You are using pip version 21.0.1; however, version 21.3.1 is available.
You should consider upgrading via the '/usr/bin/python3 -m pip install --upgrade pip' command.
… no executable to be found
$ find .local -name *gitlint*
.local/lib/python3.6/site-packages/gitlint_core-0.17.0.dist-info
.local/lib/python3.6/site-packages/gitlint-0.17.0.dist-info
After installing 0.16.0 …
$ pip3 install --user -U gitlint==0.16.0
...
… everything is fine
$ find .local -name *gitlint*
.local/lib/python3.6/site-packages/gitlint_core-0.17.0.dist-info
.local/lib/python3.6/site-packages/gitlint
.local/lib/python3.6/site-packages/gitlint/files/gitlint
.local/lib/python3.6/site-packages/qa/test_gitlint.py
.local/lib/python3.6/site-packages/qa/__pycache__/test_gitlint.cpython-36.pyc
.local/lib/python3.6/site-packages/gitlint-0.16.0.dist-info
.local/bin/gitlint
$ pip3 --version
pip 21.0.1 from /usr/lib/python3.6/site-packages/pip (python 3.6)
About this issue
- Original URL
- State: open
- Created 3 years ago
- Comments: 18 (9 by maintainers)
Commits related to this issue
- requirements[dev_helper]: Upgrade gitlint from >=0.10 to >=0.17. This may require extra development documentation due to jorisroovers/gitlint#250, which applies for upgrades. — committed to neiljp/zulip-terminal by neiljp 2 years ago
- requirements[dev_helper]: Upgrade gitlint from >=0.10 to >=0.17. This may require extra development documentation due to jorisroovers/gitlint#250, which applies for upgrades. — committed to zulip/zulip-terminal by neiljp 2 years ago
More precisely, it should be
pip uninstall gitlint gitlint-core; pip install gitlint
which would also take care of a semi-broken install.At Ansible, we’ve opted for documenting this in the FAQ: https://docs.ansible.com/ansible/latest/porting_guides/porting_guide_4.html#other / https://docs.ansible.com/ansible/latest/porting_guides/porting_guide_2.10.html#known-issues. Maybe,
gitlint
could do the same? Probably, we could change the issue label todocs
at this point.@jorisroovers I wish I saw this issue earlier. I know exactly what happens. We’ve seen this when we were splitting the project under ansible/ansible that was previously published as just
ansible
on PyPI but got renamed toansible-base
, and thenansible-core
. Theansible
distribution is also published but with different contents.TL;DR this is a limitation of pip that is described at https://github.com/pypa/pip/issues/8509.
When you rename a distribution package (the name published on PyPI), but keep that importable package (the folder with Python files that’s installed under site-packages), there are two different packages that provide the same files (or partially the same). There used to be a
gitlint/
directory installed intosite-packages/
ingitlint==0.16.0
. Now, there’s none, but a folder with the same namegitlint/
is shipped as a part ofgitlint-core
which is pulled in bygitlint
. When you do a clean install, pip will resolve the deptree and unpack the dists properly.When you have
gitlint==0.16.0
, there’s alreadysite-packages/gitlint/
on disk. And pip knows about each file belonging to the said package. During the dependency resolution, it knows that it needs to installgitlint==0.17.0
andgitlint-core==0.17.0
but because of one being a dependency of the other, it knows thatgitlint-core
should go in first. So pip goes ahead and unpacks the contents ofgitlint-core
intosite-packages/gitlint/
, overwriting the existing files. At this point, pip knows that the files belong togitlint==0.16.0
and it needs to uninstall it before writing the newer version to disk, it removes these files which got installed fromgitlint-core
because thegitlint==0.16.0
’s metadata still says that they belong to this package. And then, it goes ahead and installsgitlint==0.17.0
. This is how you end up with a broken installation. It is specific to pip upgrades, but not fresh installs. The issue I linked above has a number of suggestions that pip could implement to address this bug (including transactions, similar to other packaging ecosystems), but so far, nobody’s working on that.On the uninstall: I think we’ve all mentioned that
gitlint-core
needs to also be uninstalled for full cleanup. We can update docs to point this out or further investigate if that can be fixed insetup.py
. Separate issue IMO.Ok, I couldn’t resist. I booted an SUSE Linux Enterprise Server 15 SP2 instance on AWS, and still can’t reproduce this, see below.
Now I’m really out of time for today 😃 Appreciate if you can provide the steps to reproduce.
@webknjaz thanks for providing that extra context here, that’s insightful!
At this point, I think we should close this particular issue as the root cause and workaround is known, this probably only affects a (very) small fraction of our users and we can’t really solve this on our end.
To summarize:
gitlint <= 0.16
pip uninstall gitlint; pip install gitlint
Thanks for digging into this @omarkohl, this helped. I’ve been able to reproduce this in a virtualenv on macOS. I am now convinced this is indeed a gitlint installation bug and not specific to any OS or python/pip version.
Using the verbose flag on pip
-v
helped me to confirm that this is because pip removesbin/gitlint
as part of the gitlint 0.16.0 uninstallation. You can easily see in the output (below), this happens after installation ofgitlint
andgitlint-core
.As @sigmavirus24 suggested, I also think this happens because we moved the
gitlint
“binary” (really theentry_point
script) togitlint-core
as part of 0.17.0. From pip’s point of view, there’s a conflict there: 2 different packages are trying to own the same file.I’m not a pip expert, but I’m guessing pip just uninstalls everything it finds in
site-packages/gitlint-0.16.0.dist-info/RECORD,
unless referenced from newer versions (and same file checksum or something). Since gitlint 0.17.0 installation record is not referencingbin/gitlint
(again, it’s part ofgitlint-core
now), pip will just remove it. Maybe this is not exactly how it works, but I think this is conceptually what’s going on.I think if we were to release
0.17.1
, this problem would not occur when upgrading from0.17.0
to0.17.1
(or beyond). However, this problem will continue to exist for upgrading from0.16.0
(and before). Maybe there’s ways to fix that by adding special installation steps insetup.py
, but IMO we can also just add a note to the Changelog and/or pin a GH issue asking folks to do a clean re-install when upgrading from before 0.16.0 and leave it at that.For the record, I haven’t actually tried this. Appreciate any help getting this confirmed (or new insights 😃 )
@jorisroovers thanks for taking the time to look into it! I will try to report more data. (sidenote: I just had a look at your blog, very nice 👍)
Hmm, ok. I don’t have more time right now though, appreciate if you can help getting to a reproducible scenario, maybe on OpenSUSE (preferably using Vagrant).
I’m intrigued because we did change how we do packaging in 0.17.0, so it’s possible that affects installation for some users.