use gt_tools::structs::release::CreateReleaseOption; use gt_tools::{structs::release::Release, cli::Args}; use reqwest::multipart::Part; use std::collections::HashMap; use std::{ env, fs }; use clap::Parser; use reqwest::Error; use reqwest::header::{ACCEPT, USER_AGENT}; const API_RELEASE_FRONT: &'static str = "/api/v1/repos/"; const API_RELEASE_BACK: &'static str = "/releases"; #[tokio::main] async fn main() -> Result<(), Error> { let args = Args::parse(); let client = reqwest::Client::new(); match args.command { gt_tools::cli::Commands::ListReleases => { let releases = do_list_releases(&client, &args.gitea_url, &args.repo).await?; for release in releases { println!("{:?}", release); } } gt_tools::cli::Commands::CreateRelease { name, body, draft, prerelease, tag_name, target_commitish, } => { let submission = CreateReleaseOption { body, draft, name, prerelease, tag_name, target_commitish, }; do_create_release(&client, &args.gitea_url, &args.repo, submission).await?; } gt_tools::cli::Commands::UploadRelease { tag_name, create, files } => { println!("Uploading files to a release!"); println!("Release Tag: {tag_name}"); println!("Creating?: {create}"); println!("Files..."); for file in &files { println!("--- {file}"); } // TODO: Pre-create the release, if it doesn't exist. // TODO: Find an existing release and use it's ID, if it does do_upload_release( &client, &args.gitea_url, &args.repo, 52usize, files ).await?; } } Ok(()) } async fn do_list_releases( client: &reqwest::Client, gitea_url: &str, repo: &str, ) -> Result, Error> { let request_url = format!( "{gitea_url}{front}{repo}{back}", front = API_RELEASE_FRONT, back = API_RELEASE_BACK ); let response = client .get(request_url) .header(USER_AGENT, "gt-tools-test-agent") .header(ACCEPT, "application/json") .send() .await?; // TODO: Handle case with no releases. // afaict: Serde tries to unpack an empty list, can't decide what struct it's unpacking, // and emits an error. Desired behavior: empty Vec. let body_text: Vec = response.json().await?; return Ok(body_text); } #[must_use] async fn do_create_release( client: &reqwest::Client, gitea_url: &str, repo: &str, submission: CreateReleaseOption, ) -> Result<(), Error> { let token = env::var("RELEASE_KEY_GITEA").expect( "You must set the RELEASE_KEY_GITEA environment variable so the Gitea API can be used.", ); let request_url = format!( "{gitea_url}{front}{repo}{back}", front = API_RELEASE_FRONT, back = API_RELEASE_BACK ); let response = client .post(request_url) .header(USER_AGENT, "gt-tools-test-agent") .header(ACCEPT, "application/json") .header("Authorization", format!("token {}", token)) .json(&submission) .send() .await?; println!("HTTP Response: {}", response.status()); let result: gt_tools::CreateResult = response.json().await?; println!("{:?}", result); Ok(()) } async fn do_upload_release( client: &reqwest::Client, gitea_url: &str, repo: &str, release_id: usize, files: Vec, ) -> Result<(), Error> { let token = env::var("RELEASE_KEY_GITEA").expect( "You must set the RELEASE_KEY_GITEA environment variable so the Gitea API can be used.", ); let request_url = format!( "{gitea_url}{front}{repo}{back}/{id}/assets", front = API_RELEASE_FRONT, back = API_RELEASE_BACK, id = release_id ); // Setup a "partial request". I'll need to loop over the files, so I'll // clone this a bunch of times for each one let partial_request = client .post(request_url) .header(USER_AGENT, "gt-tools-test-agent") .header(ACCEPT, "application/json") .header("Authorization", format!("token {}", token)); // Ensure all files exists before starting the uploads for file in &files { assert!(fs::exists(file) .expect(&format!("Can't check existence of {file}")) ); } for file in files { if let Some(request) = partial_request.try_clone() { println!("Uploading file {}", &file); let data = reqwest::multipart::Part::stream(fs::read(&file).unwrap()) .file_name("attachment") .mime_str("text/plain")?; let form= reqwest::multipart::Form::new() .part("attachment", data); let request = request .multipart(form) .query(&[("name", file.split("/").last())]) .send() .await?; } else { panic!("Failed to clone the RequestBuilder during file upload loop."); } } Ok(()) }