Skip to content

Commit

Permalink
Make --skip-existing work for Artifactory indexes (#363)
Browse files Browse the repository at this point in the history
Artifactory repositories seem to return HTTP 403 when you try to upload
a package that already exists (and your user doesn't have overwrite
permissions).
Extend skip_upload to support this.
  • Loading branch information
mulmschneider authored and theacodes committed Sep 21, 2018
1 parent 0191a22 commit 0c7f38f
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 5 deletions.
15 changes: 15 additions & 0 deletions tests/test_upload.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,21 @@ def test_skip_existing_skips_files_already_on_pypiserver(monkeypatch):
package=pkg) is True


def test_skip_existing_skips_files_already_on_artifactory(monkeypatch):
# Artifactory (https://jfrog.com/artifactory/) responds with 403
# when the file already exists.
response = pretend.stub(
status_code=403,
text="Not enough permissions to overwrite artifact "
"'pypi-local:twine/1.5.0/twine-1.5.0-py2.py3-none-any.whl'"
"(user 'twine-deployer' needs DELETE permission).")

pkg = package.PackageFile.from_filename(WHEEL_FIXTURE, None)
assert upload.skip_upload(response=response,
skip_existing=True,
package=pkg) is True


def test_skip_upload_respects_skip_existing(monkeypatch):
response = pretend.stub(
status_code=400,
Expand Down
15 changes: 10 additions & 5 deletions twine/commands/upload.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,18 +28,23 @@ def skip_upload(response, skip_existing, package):
filename = package.basefilename
# NOTE(sigmavirus24): Old PyPI returns the first message while Warehouse
# returns the latter. This papers over the differences.
msg = ('A file named "{0}" already exists for'.format(filename),
'File already exists')
msg_400 = ('A file named "{0}" already exists for'.format(filename),
'File already exists')
msg_403 = 'Not enough permissions to overwrite artifact'
# NOTE(sigmavirus24): PyPI presently returns a 400 status code with the
# error message in the reason attribute. Other implementations return a
# 409 status code. We only want to skip an upload if:
# 409 or 403 status code. We only want to skip an upload if:
# 1. The user has told us to skip existing packages (skip_existing is
# True) AND
# 2. a) The response status code is 409 OR
# 2. b) The response status code is 400 AND it has a reason that matches
# what we expect PyPI to return to us.
# what we expect PyPI to return to us. OR
# 2. c) The response status code is 403 AND the text matches what we
# expect Artifactory to return to us.
return (skip_existing and (response.status_code == 409 or
(response.status_code == 400 and response.reason.startswith(msg))))
(response.status_code == 400 and
response.reason.startswith(msg_400)) or
(response.status_code == 403 and msg_403 in response.text)))


def upload(upload_settings, dists):
Expand Down

0 comments on commit 0c7f38f

Please sign in to comment.