Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add --dry-run option for publish command #2199

Merged
merged 1 commit into from
Mar 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@ It can also build the package if you pass it the `--build` option.
Should match a repository name set by the [`config`](#config) command.
* `--username (-u)`: The username to access the repository.
* `--password (-p)`: The password to access the repository.
* `--dry-run`: Perform all actions except upload the package.

## config

Expand Down
2 changes: 2 additions & 0 deletions poetry/console/commands/publish.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ class PublishCommand(Command):
flag=False,
),
option("build", None, "Build the package before publishing."),
option("dry-run", None, "Perform all actions except upload the package."),
]

help = """The publish command builds and uploads the package to a remote repository.
Expand Down Expand Up @@ -79,4 +80,5 @@ def handle(self):
self.option("password"),
cert,
client_cert,
self.option("dry-run"),
)
11 changes: 10 additions & 1 deletion poetry/publishing/publisher.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,15 @@ def __init__(self, poetry, io):
def files(self):
return self._uploader.files

def publish(self, repository_name, username, password, cert=None, client_cert=None):
def publish(
self,
repository_name,
username,
password,
cert=None,
client_cert=None,
dry_run=False,
):
if repository_name:
self._io.write_line(
"Publishing <c1>{}</c1> (<b>{}</b>) "
Expand Down Expand Up @@ -90,4 +98,5 @@ def publish(self, repository_name, username, password, cert=None, client_cert=No
url,
cert=cert or get_cert(self._poetry.config, repository_name),
client_cert=resolved_client_cert,
dry_run=dry_run,
)
36 changes: 20 additions & 16 deletions poetry/publishing/uploader.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@ def is_authenticated(self):
return self._username is not None and self._password is not None

def upload(
self, url, cert=None, client_cert=None
): # type: (str, Optional[Path], Optional[Path]) -> None
self, url, cert=None, client_cert=None, dry_run=False
): # type: (str, Optional[Path], Optional[Path], bool) -> None
session = self.make_session()

if cert:
Expand All @@ -106,7 +106,7 @@ def upload(
session.cert = str(client_cert)

try:
self._upload(session, url)
self._upload(session, url, dry_run)
finally:
session.close()

Expand Down Expand Up @@ -188,9 +188,9 @@ def post_data(self, file):

return data

def _upload(self, session, url):
def _upload(self, session, url, dry_run=False):
try:
self._do_upload(session, url)
self._do_upload(session, url, dry_run)
except HTTPError as e:
if (
e.response.status_code == 400
Expand All @@ -203,15 +203,16 @@ def _upload(self, session, url):

raise UploadError(e)

def _do_upload(self, session, url):
def _do_upload(self, session, url, dry_run=False):
for file in self.files:
# TODO: Check existence

resp = self._upload_file(session, url, file)
resp = self._upload_file(session, url, file, dry_run)

resp.raise_for_status()
if not dry_run:
resp.raise_for_status()

def _upload_file(self, session, url, file):
def _upload_file(self, session, url, file, dry_run=False):
data = self.post_data(file)
data.update(
{
Expand All @@ -238,14 +239,17 @@ def _upload_file(self, session, url, file):

bar.start()

resp = session.post(
url,
data=monitor,
allow_redirects=False,
headers={"Content-Type": monitor.content_type},
)
resp = None

if not dry_run:
resp = session.post(
url,
data=monitor,
allow_redirects=False,
headers={"Content-Type": monitor.content_type},
)

if resp.ok:
if dry_run or resp.ok:
bar.set_format(
" - Uploading <c1>{0}</c1> <fg=green>%percent%%</>".format(
file.name
Expand Down
21 changes: 19 additions & 2 deletions tests/console/commands/test_publish.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ def test_publish_with_cert(app_tester, mocker):
app_tester.execute("publish --cert path/to/ca.pem")

assert [
(None, None, None, Path("path/to/ca.pem"), None)
(None, None, None, Path("path/to/ca.pem"), None, False)
] == publisher_publish.call_args


Expand All @@ -36,5 +36,22 @@ def test_publish_with_client_cert(app_tester, mocker):

app_tester.execute("publish --client-cert path/to/client.pem")
assert [
(None, None, None, None, Path("path/to/client.pem"))
(None, None, None, None, Path("path/to/client.pem"), False)
] == publisher_publish.call_args


def test_publish_dry_run(app_tester, http):
http.register_uri(
http.POST, "https://upload.pypi.org/legacy/", status=403, body="Forbidden"
)

exit_code = app_tester.execute("publish --dry-run --username foo --password bar")

assert 0 == exit_code

output = app_tester.io.fetch_output()
error = app_tester.io.fetch_error()

assert "Publishing simple-project (1.2.3) to PyPI" in output
assert "- Uploading simple-project-1.2.3.tar.gz" in error
assert "- Uploading simple_project-1.2.3-py2.py3-none-any.whl" in error
12 changes: 6 additions & 6 deletions tests/publishing/test_publisher.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def test_publish_publishes_to_pypi_by_default(fixture_dir, mocker, config):
assert [("foo", "bar")] == uploader_auth.call_args
assert [
("https://upload.pypi.org/legacy/",),
{"cert": None, "client_cert": None},
{"cert": None, "client_cert": None, "dry_run": False},
] == uploader_upload.call_args


Expand All @@ -45,7 +45,7 @@ def test_publish_can_publish_to_given_repository(fixture_dir, mocker, config):
assert [("foo", "bar")] == uploader_auth.call_args
assert [
("http://foo.bar",),
{"cert": None, "client_cert": None},
{"cert": None, "client_cert": None, "dry_run": False},
] == uploader_upload.call_args


Expand Down Expand Up @@ -74,7 +74,7 @@ def test_publish_uses_token_if_it_exists(fixture_dir, mocker, config):
assert [("__token__", "my-token")] == uploader_auth.call_args
assert [
("https://upload.pypi.org/legacy/",),
{"cert": None, "client_cert": None},
{"cert": None, "client_cert": None, "dry_run": False},
] == uploader_upload.call_args


Expand All @@ -98,7 +98,7 @@ def test_publish_uses_cert(fixture_dir, mocker, config):
assert [("foo", "bar")] == uploader_auth.call_args
assert [
("https://foo.bar",),
{"cert": Path(cert), "client_cert": None},
{"cert": Path(cert), "client_cert": None, "dry_run": False},
] == uploader_upload.call_args


Expand All @@ -119,7 +119,7 @@ def test_publish_uses_client_cert(fixture_dir, mocker, config):

assert [
("https://foo.bar",),
{"cert": None, "client_cert": Path(client_cert)},
{"cert": None, "client_cert": Path(client_cert), "dry_run": False},
] == uploader_upload.call_args


Expand All @@ -137,5 +137,5 @@ def test_publish_read_from_environment_variable(fixture_dir, environ, mocker, co
assert [("bar", "baz")] == uploader_auth.call_args
assert [
("https://foo.bar",),
{"cert": None, "client_cert": None},
{"cert": None, "client_cert": None, "dry_run": False},
] == uploader_upload.call_args