summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Cargo.toml1
-rw-r--r--src/cli.rs137
-rw-r--r--src/commands/release.rs71
3 files changed, 167 insertions, 42 deletions
diff --git a/Cargo.toml b/Cargo.toml
index 8a05423..2ca07fe 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -22,6 +22,7 @@ colored = "2"
config = "0.10"
csv = "1.1"
daggy = { version = "0.7", features = [ "serde" ] }
+dialoguer = "0.7"
diesel = { version = "1.4", features = ["postgres", "chrono", "uuid", "serde_json"] }
env_logger = "0.8"
filters = "0.4.0"
diff --git a/src/cli.rs b/src/cli.rs
index 113280e..63a22e3 100644
--- a/src/cli.rs
+++ b/src/cli.rs
@@ -662,51 +662,106 @@ pub fn cli<'a>() -> App<'a> {
)
.subcommand(App::new("release")
- .about("Release artifacts")
- .arg(Arg::new("submit_uuid")
- .required(true)
- .multiple(false)
- .index(1)
- .value_name("SUBMIT")
- .about("The submit uuid from which to release a package")
- )
- .arg(Arg::new("release_store_name")
- .required(true)
- .multiple(false)
- .long("to")
- .value_name("RELEASE_STORE_NAME")
- .about("Release store name to release to")
+ .subcommand(App::new("rm")
+ .about("Remove release artifacts")
.long_about(indoc::indoc!(r#"
- Butido can release to different release stores, based on this CLI flag.
- The release stores that are available must be listed in the configuration.
+ Removes a released artifact from the release store and deletes the according database entry.
+
+ This command asks interactively whether you want to delete data.
+ This can't be turned off.
"#))
+ .arg(Arg::new("release_store_name")
+ .required(true)
+ .multiple(false)
+ .long("from")
+ .value_name("RELEASE_STORE_NAME")
+ .about("Release store name to remove release from")
+ )
+
+ .arg(Arg::new("package_name")
+ .required(false)
+ .multiple(false)
+ .index(1)
+ .value_name("PKG")
+ .about("The name of the package")
+ .conflicts_with("artifact_path")
+ .requires("package_version")
+ )
+
+ .arg(Arg::new("package_version")
+ .required(false)
+ .multiple(false)
+ .index(2)
+ .value_name("VERSION")
+ .about("The exact version of the package (string match)")
+ .conflicts_with("artifact_path")
+ .requires("package_name")
+ )
+
+ .arg(Arg::new("artifact_path")
+ .required(false)
+ .multiple(false)
+ .long("path")
+ .value_name("PATH")
+ .about("Path to a released artifact")
+ .conflicts_with_all(&["package_name", "package_version"])
+ )
+
+ .group(ArgGroup::new("artifact_or_package")
+ .args(&["package_name", "package_version", "artifact_path"])
+ .multiple(true)
+ .required(true)
+ )
)
- .arg(Arg::new("package_name")
- .required(false)
- .multiple(false)
- .index(2)
- .value_name("PKG")
- .about("The name of the package")
- .conflicts_with("all-packages")
- )
- .arg(Arg::new("all-packages")
- .required(false)
- .multiple(false)
- .long("all")
- .about("Release all packages")
- .conflicts_with("package_name")
- )
- .group(ArgGroup::new("package")
- .args(&["package_name", "all-packages"])
- .required(true) // one of these is required
- )
- .arg(Arg::new("package_version")
- .required(false)
- .multiple(false)
- .index(3)
- .value_name("VERSION")
- .about("The exact version of the package (string match)")
+
+ .subcommand(App::new("new")
+ .about("Release artifacts")
+ .arg(Arg::new("submit_uuid")
+ .required(true)
+ .multiple(false)
+ .index(1)
+ .value_name("SUBMIT")
+ .about("The submit uuid from which to release a package")
+ )
+ .arg(Arg::new("release_store_name")
+ .required(true)
+ .multiple(false)
+ .long("to")
+ .value_name("RELEASE_STORE_NAME")
+ .about("Release store name to release to")
+ .long_about(indoc::indoc!(r#"
+ Butido can release to different release stores, based on this CLI flag.
+ The release stores that are available must be listed in the configuration.
+ "#))
+ )
+ .arg(Arg::new("package_name")
+ .required(false)
+ .multiple(false)
+ .index(2)
+ .value_name("PKG")
+ .about("The name of the package")
+ .conflicts_with("all-packages")
+ )
+ .arg(Arg::new("all-packages")
+ .required(false)
+ .multiple(false)
+ .long("all")
+ .about("Release all packages")
+ .conflicts_with("package_name")
+ )
+ .group(ArgGroup::new("package")
+ .args(&["package_name", "all-packages"])
+ .required(true) // one of these is required
+ )
+ .arg(Arg::new("package_version")
+ .required(false)
+ .multiple(false)
+ .index(3)
+ .value_name("VERSION")
+ .about("The exact version of the package (string match)")
+ )
)
+
)
.subcommand(App::new("lint")
diff --git a/src/commands/release.rs b/src/commands/release.rs
index 51ecd89..a3d53f0 100644
--- a/src/commands/release.rs
+++ b/src/commands/release.rs
@@ -8,6 +8,7 @@
// SPDX-License-Identifier: EPL-2.0
//
+use std::io::Write;
use std::path::PathBuf;
use anyhow::anyhow;
@@ -15,7 +16,7 @@ use anyhow::Error;
use anyhow::Result;
use clap::ArgMatches;
use diesel::prelude::*;
-use log::{debug, trace};
+use log::{debug, info, trace};
use tokio_stream::StreamExt;
use crate::config::Configuration;
@@ -28,6 +29,20 @@ pub async fn release(
config: &Configuration,
matches: &ArgMatches,
) -> Result<()> {
+ match matches.subcommand() {
+ Some(("new", matches)) => new_release(db_connection_config, config, matches).await,
+ Some(("rm", matches)) => rm_release(db_connection_config, config, matches).await,
+ Some((other, _matches)) => Err(anyhow!("Unknown subcommand: {}", other)),
+ None => Err(anyhow!("Missing subcommand")),
+ }
+}
+
+
+async fn new_release(
+ db_connection_config: DbConnectionConfig,
+ config: &Configuration,
+ matches: &ArgMatches,
+) -> Result<()> {
let release_store_name = matches.value_of("release_store_name").unwrap(); // safe by clap
if !(config.releases_directory().exists() && config.releases_directory().is_dir()) {
return Err(anyhow!(
@@ -155,3 +170,57 @@ pub async fn release(
Ok(())
})
}
+
+pub async fn rm_release(
+ db_connection_config: DbConnectionConfig,
+ config: &Configuration,
+ matches: &ArgMatches,
+) -> Result<()> {
+ let release_store_name = matches.value_of("release_store_name").unwrap(); // safe by clap
+ if !(config.releases_directory().exists() && config.releases_directory().is_dir()) {
+ return Err(anyhow!(
+ "Release directory does not exist or does not point to directory: {}",
+ config.releases_directory().display()
+ ));
+ }
+
+ let pname = matches.value_of("package_name").map(String::from).unwrap(); // TODO: FIXME
+ let pvers = matches.value_of("package_version").map(String::from).unwrap(); // TODO: FIXME
+ debug!("Remove Release called for: {:?} {:?}", pname, pvers);
+
+ let conn = crate::db::establish_connection(db_connection_config)?;
+
+ let (release, artifact) = crate::schema::jobs::table
+ .inner_join(crate::schema::packages::table)
+ .inner_join(crate::schema::artifacts::table)
+ .inner_join(crate::schema::releases::table
+ .on(crate::schema::releases::artifact_id.eq(crate::schema::artifacts::id)))
+ .inner_join(crate::schema::release_stores::table
+ .on(crate::schema::release_stores::id.eq(crate::schema::releases::release_store_id)))
+ .filter(crate::schema::packages::dsl::name.eq(&pname)
+ .and(crate::schema::packages::dsl::version.eq(&pvers)))
+ .filter(crate::schema::release_stores::dsl::store_name.eq(&release_store_name))
+ .order(crate::schema::releases::dsl::release_date.desc())
+ .select((crate::schema::releases::all_columns, crate::schema::artifacts::all_columns))
+ .first::<(crate::db::models::Release, crate::db::models::Artifact)>(&conn)?;
+
+ let artifact_path = config.releases_directory().join(release_store_name).join(&artifact.path);
+ if !artifact_path.is_file() {
+ return Err(anyhow!("Not a file: {}", artifact_path.display()))
+ }
+
+ writeln!(std::io::stderr(), "Going to delete: {}", artifact_path.display())?;
+ writeln!(std::io::stderr(), "Going to remove from database: Release with ID {} from {}", release.id, release.release_date)?;
+ if !dialoguer::Confirm::new().with_prompt("Continue?").interact()? {
+ return Ok(())
+ }
+
+ tokio::fs::remove_file(&artifact_path).await?;
+ info!("File removed");
+
+ diesel::delete(&release).execute(&conn)?;
+ info!("Release deleted from database");
+
+ Ok(())
+}
+