octocrab
octocrab copied to clipboard
Add upload release asset endpoints
Do you have any recommendation on how to upload binaries to a release?
Thank you for your issue! I don't think those endpoints are currently implemented in the semantic API, so you'd need to use the HTTP API. If someone wants to add those, contributions are welcome.
Figured out how to do it with the current API, added the snippet below in case someone else need to do this.
let filename = file.file_name().unwrap().to_str().unwrap();
let mut new_url = base_url.clone();
new_url.set_query(Some(format!("{}={}", "name", filename).as_str()));
let file_size = std::fs::metadata(file)?.len();
let file = tokio::fs::File::open(file).await?;
let stream = tokio_util::codec::FramedRead::new(file, tokio_util::codec::BytesCodec::new());
let body = reqwest::Body::wrap_stream(stream);
let builder = octocrab.request_builder(new_url.as_str(), reqwest::Method::POST)
.header("Content-Type", "application/octet-stream")
.header("Content-Length", file_size.to_string());
let response = builder.body(body).send().await?;
I'm going to re-open this issue, because I don't really consider that to be a good enough solution. I want a semantic API solution that is easier to use.
Thanks for the snippet. Here is my take. I needed simple functions to call.
in Cargo.toml:
[dependencies]
octocrab = "0.12.0"
unwrap="1.2.1"
reqwest={version="0.11.4", features=["stream"]}
tokio-util = "0.6.7"
tokio = {version = "1.10.0", features = ["rt","rt-multi-thread","fs"]}
url="2.2.2"
/// create new release on Github
/// return release_id
async fn github_create_new_release(
owner: &str,
repo: &str,
version: &str,
name: &str,
branch: &str,
body_md_text: &str,
) -> String {
use octocrab::Octocrab;
let token = std::env::var("GITHUB_TOKEN").expect("GITHUB_TOKEN env variable is required");
let octocrab = unwrap!(Octocrab::builder().personal_token(token).build());
let new_release = unwrap!(
octocrab
.repos(owner, repo)
.releases()
.create(&format!("v{}", version))
.target_commitish(branch)
.name(name)
.body(body_md_text)
.draft(false)
.prerelease(false)
.send()
.await
);
new_release.id.to_string()
}
/// upload asset to github release
/// release_upload_url example: https://uploads.github.com/repos/owner/repo/releases/48127727/assets
async fn upload_asset_to_github_release(
owner: &str,
repo: &str,
release_id: &str,
path_to_file: &str,
) {
use octocrab::Octocrab;
let token = std::env::var("GITHUB_TOKEN").expect("GITHUB_TOKEN env variable is required");
let octocrab = unwrap!(Octocrab::builder().personal_token(token).build());
// println!("path_to_file: {}", path_to_file);
let file = std::path::Path::new(&path_to_file);
let file_name = file.file_name().unwrap().to_str().unwrap();
let release_upload_url = format!(
"https://uploads.github.com/repos/{owner}/{repo}/releases/{release_id}/assets",
owner = owner,
repo = repo,
release_id = release_id
);
let mut release_upload_url = unwrap!(url::Url::from_str(&release_upload_url));
release_upload_url.set_query(Some(format!("{}={}", "name", file_name).as_str()));
// println!("upload_url: {}", release_upload_url);
let file_size = unwrap!(std::fs::metadata(file)).len();
// println!(
"file_size: {}. It can take some time to upload. Wait...",
file_size
);
let file = unwrap!(tokio::fs::File::open(file).await);
let stream = tokio_util::codec::FramedRead::new(file, tokio_util::codec::BytesCodec::new());
let body = reqwest::Body::wrap_stream(stream);
let builder = octocrab
.request_builder(release_upload_url.as_str(), reqwest::Method::POST)
.header("Content-Type", "application/octet-stream")
.header("Content-Length", file_size.to_string());
let _response = unwrap!(builder.body(body).send().await);
}
Thank you for snippet, would you like to make a PR adding the upload_asset endpoint? That code looks good, except that in octocrab, we'd just take in a R: Read object rather than a file path.
Thanks for the snippet by @kenr, we are using it in Airshipper, which is licensed under GPLv3.
Thanks this issue's codes.
I want to use deleteAssets and uploadAssets with octocrab, which is more convenient
add delete fun but got original: Error("EOF while parsing a value", line: 1, column: 0)
/// Delete a single asset by its ID.
/// ```no_run
/// # async fn run() -> octocrab::Result<()> {
/// let asset = octocrab::instance()
/// .repos("owner", "repo")
/// .releases()
/// .delete_assets(42u64.into())
/// .await?;
/// # Ok(())
/// # }
/// ```
pub async fn delete_assets(&self, asset_id: AssetId) -> crate::Result<()> {
let url = format!(
"repos/{owner}/{repo}/releases/assets/{asset_id}",
owner = self.parent.owner,
repo = self.parent.repo,
asset_id = asset_id,
);
self.parent.crab.delete(url, None::<&()>).await
}
i thins blank text can;t parse to ().
use snafu::ResultExt;
/// A trait for mapping from a `reqwest::Response` to an another type.
#[async_trait::async_trait]
pub trait FromResponse: Sized {
async fn from_response(response: reqwest::Response) -> crate::Result<Self>;
}
#[async_trait::async_trait]
impl<T: serde::de::DeserializeOwned> FromResponse for T {
async fn from_response(response: reqwest::Response) -> crate::Result<Self> {
let text = response.text().await.context(crate::error::HttpSnafu)?;
let de = &mut serde_json::Deserializer::from_str(&text);
serde_path_to_error::deserialize(de).context(crate::error::JsonSnafu)
}
}
Should we add delete_and_got_blank() fn ?
This issue also happens with TeamRepoHandler::remove().
I'm going to make a new issue for this EOF thing, I made a test that demonstrates it for teams().repos().remove().
I submitted the EOF issue as #504. I made a test, too, to show how it's failing.