init
This commit is contained in:
88
src/github.rs
Normal file
88
src/github.rs
Normal file
@ -0,0 +1,88 @@
|
||||
use reqwest::{Body, Client};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{error::Error, fs::File};
|
||||
|
||||
#[derive(Serialize, Default, Debug, PartialEq)]
|
||||
pub struct Release {
|
||||
pub tag_name: String,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub name: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub body: Option<String>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub draft: Option<bool>,
|
||||
}
|
||||
|
||||
pub trait Releaser {
|
||||
fn release(
|
||||
&self,
|
||||
github_token: &str,
|
||||
github_repo: &str,
|
||||
release: Release,
|
||||
) -> Result<usize, Box<dyn Error>>;
|
||||
}
|
||||
|
||||
pub trait AssetUploader<A: Into<Body> = File> {
|
||||
fn upload(
|
||||
&self,
|
||||
github_token: &str,
|
||||
github_repo: &str,
|
||||
release_id: usize,
|
||||
mime: mime::Mime,
|
||||
asset: A,
|
||||
) -> Result<(), Box<dyn Error>>;
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct ReleaseResponse {
|
||||
id: usize,
|
||||
}
|
||||
|
||||
impl Releaser for Client {
|
||||
// https://developer.github.com/v3/repos/releases/#create-a-release
|
||||
fn release(
|
||||
&self,
|
||||
github_token: &str,
|
||||
github_repo: &str,
|
||||
release: Release,
|
||||
) -> Result<usize, Box<dyn Error>> {
|
||||
let response: ReleaseResponse = self
|
||||
.post(&format!(
|
||||
"https://api.github.com/repos/{}/releases",
|
||||
github_repo
|
||||
))
|
||||
.header("Authorization", format!("bearer {}", github_token))
|
||||
.json(&release)
|
||||
.send()?
|
||||
.json()?;
|
||||
Ok(response.id)
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: Into<Body>> AssetUploader<A> for Client {
|
||||
// https://developer.github.com/v3/repos/releases/#upload-a-release-asset
|
||||
fn upload(
|
||||
&self,
|
||||
github_token: &str,
|
||||
github_repo: &str,
|
||||
release_id: usize,
|
||||
mime: mime::Mime,
|
||||
asset: A,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
self.post(&format!(
|
||||
"http://uploads.github.com/repos/{}/releases/{}",
|
||||
github_repo, release_id
|
||||
))
|
||||
.header("Authorization", format!("bearer {}", github_token))
|
||||
.header("Content-Type", mime.to_string())
|
||||
.body(asset)
|
||||
.send()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn it_works() {}
|
||||
}
|
84
src/main.rs
Normal file
84
src/main.rs
Normal file
@ -0,0 +1,84 @@
|
||||
mod github;
|
||||
|
||||
use github::{AssetUploader, Release, Releaser};
|
||||
use reqwest::Client;
|
||||
use serde::Deserialize;
|
||||
use std::{error::Error, fs::File};
|
||||
|
||||
#[derive(Deserialize, Default)]
|
||||
struct Config {
|
||||
// provided
|
||||
github_token: String,
|
||||
github_ref: String, // refs/heads/..., ref/tags/...
|
||||
github_repository: String,
|
||||
// optional
|
||||
input_body: Option<String>,
|
||||
input_files: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
fn release(conf: &Config) -> Release {
|
||||
let Config {
|
||||
github_ref,
|
||||
input_body,
|
||||
..
|
||||
} = conf;
|
||||
Release {
|
||||
tag_name: github_ref.clone(),
|
||||
body: input_body.clone(),
|
||||
..Release::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn run(
|
||||
conf: Config,
|
||||
releaser: &dyn Releaser,
|
||||
uploader: &dyn AssetUploader,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
if !conf.github_ref.starts_with("refs/tags/") {
|
||||
log::error!("GH Releases require a tag");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let release_id = releaser.release(
|
||||
conf.github_token.as_str(),
|
||||
conf.github_repository.as_str(),
|
||||
release(&conf),
|
||||
)?;
|
||||
|
||||
if let Some(patterns) = conf.input_files {
|
||||
for pattern in patterns {
|
||||
for path in glob::glob(pattern.as_str())? {
|
||||
let resolved = path?;
|
||||
let mime =
|
||||
mime_guess::from_path(&resolved).first_or(mime::APPLICATION_OCTET_STREAM);
|
||||
uploader.upload(
|
||||
conf.github_token.as_str(),
|
||||
conf.github_repository.as_str(),
|
||||
release_id,
|
||||
mime,
|
||||
File::open(resolved)?,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
env_logger::init();
|
||||
let client = Client::new();
|
||||
run(envy::from_env()?, &client, &client)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn release_constructs_a_release_from_a_config() -> Result<(), Box<dyn Error>> {
|
||||
for (conf, expect) in vec![(Config::default(), Release::default())] {
|
||||
assert_eq!(release(&conf), expect);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user