Skip to content

Commit

Permalink
Add --skip-existing option to publish
Browse files Browse the repository at this point in the history
  • Loading branch information
messense committed Feb 24, 2021
1 parent c39a595 commit 6980e6e
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 13 deletions.
1 change: 1 addition & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* Change manylinux default version based on target arch by messense in [#424](https://github.com/PyO3/maturin/pull/424)
* Support local path dependencies in source distribution (i.e. you can now package a workspace into an sdist)
* Set a more reasonable LC_ID_DYLIB entry on macOS by messense [#433](https://github.com/PyO3/maturin/pull/433)
* Add `--skip-existing` option to publish by messense [#444](https://github.com/PyO3/maturin/pull/444)

## 0.9.4 - 2021-02-18

Expand Down
4 changes: 4 additions & 0 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,10 @@ FLAGS:
--skip-auditwheel
Don't check for manylinux compliance
--skip-existing
Continue uploading files if one already exists. (Only valid when uploading to PyPI. Other implementations
may not support this.)
--universal2
Control whether to build universal2 wheel for macOS or not. Only applies to macOS targets, do nothing
otherwise
Expand Down
18 changes: 17 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,10 @@ struct PublishOpt {
/// Do not strip the library for minimum file size
#[structopt(long = "no-strip")]
no_strip: bool,
/// Continue uploading files if one already exists.
/// (Only valid when uploading to PyPI. Other implementations may not support this.)
#[structopt(long = "skip-existing")]
skip_existing: bool,
}

#[derive(Debug, StructOpt)]
Expand Down Expand Up @@ -423,12 +427,24 @@ fn upload_ui(build: BuildOptions, publish: &PublishOpt, no_sdist: bool) -> Resul
bail!("Username and/or password are wrong");
}
Err(err) => {
let filename = wheel_path.file_name().unwrap_or(&wheel_path.as_os_str());
match err {
UploadError::FileExistsError(_) => {
if publish.skip_existing {
eprintln!(
"⚠ Skipping {:?} because it appears to already exist",
filename
);
continue;
}
}
_ => {}
}
let filesize = fs::metadata(&wheel_path)
.map(|x| ByteSize(x.len()).to_string())
.unwrap_or_else(|e| {
format!("Failed to get the filesize of {:?}: {}", &wheel_path, e)
});
let filename = wheel_path.file_name().unwrap_or(&wheel_path.as_os_str());
return Err(err)
.context(format!("💥 Failed to upload {:?} ({})", filename, filesize));
}
Expand Down
45 changes: 33 additions & 12 deletions src/upload.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ pub enum UploadError {
/// The registry returned something else than 200
#[error("Failed to upload the wheel with status {0}: {1}")]
StatusCodeError(String, String),
/// File already exists
#[error("File already exists: {0}")]
FileExistsError(String),
}

impl From<io::Error> for UploadError {
Expand Down Expand Up @@ -95,18 +98,36 @@ pub fn upload(
.basic_auth(registry.username.clone(), Some(registry.password.clone()))
.send()?;

if response.status().is_success() {
Ok(())
} else if response.status() == StatusCode::FORBIDDEN {
Err(UploadError::AuthenticationError)
let status = response.status();
if status.is_success() {
return Ok(());
}
let err_text = response.text().unwrap_or_else(|e| {
format!(
"The registry should return some text, even in case of an error, but didn't ({})",
e
)
});
if status == StatusCode::FORBIDDEN {
if err_text.contains("overwrite artifact") {
// Artifactory (https://jfrog.com/artifactory/)
Err(UploadError::FileExistsError(err_text))
} else {
Err(UploadError::AuthenticationError)
}
} else {
let status_string = response.status().to_string();
let err_text = response.text().unwrap_or_else(|e| {
format!(
"The registry should return some text, even in case of an error, but didn't ({})",
e
)
});
Err(UploadError::StatusCodeError(status_string, err_text))
let status_string = status.to_string();
if status == StatusCode::CONFLICT // pypiserver (https://pypi.org/project/pypiserver)
// PyPI / TestPyPI
|| (status == StatusCode::BAD_REQUEST && err_text.contains("already exists"))
// Nexus Repository OSS (https://www.sonatype.com/nexus-repository-oss)
|| (status == StatusCode::BAD_REQUEST && err_text.contains("updating asset"))
// # Gitlab Enterprise Edition (https://about.gitlab.com)
|| (status == StatusCode::BAD_REQUEST && err_text.contains("already been taken"))
{
Err(UploadError::FileExistsError(err_text))
} else {
Err(UploadError::StatusCodeError(status_string, err_text))
}
}
}

0 comments on commit 6980e6e

Please sign in to comment.