Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CLI automatic answers to questions #668

Merged
merged 12 commits into from
Jul 18, 2023
9 changes: 7 additions & 2 deletions doc/cli_guidelines.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,11 @@ There are pending questions. Please, answer questions first: agama questions.

## Non Interactive Mode

Commands should offer a `--non-interactive` option to make scripting possible. The non interactive mode should allow answering questions automatically.
There is a `questions` subcommand that support `mode` and `answers` subcommands.
The `mode` subcommand allows to set `interactive` and `non-interactive` modes.
The `answers` allows to pass a file with predefined answers.

TBD: Non interactive mode will be defined later in the next iteration.
~~~
agama questions mode non-interactive
agama questions answers /tmp/answers.json
~~~
1 change: 1 addition & 0 deletions rust/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions rust/agama-cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ thiserror = "1.0.39"
convert_case = "0.6.0"
console = "0.15.7"
anyhow = "1.0.71"
log = "0.4"

[[bin]]
name = "agama"
Expand Down
4 changes: 4 additions & 0 deletions rust/agama-cli/src/commands.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::config::ConfigCommands;
use crate::profile::ProfileCommands;
use crate::questions::QuestionsCommands;
use clap::Subcommand;

#[derive(Subcommand, Debug)]
Expand All @@ -20,4 +21,7 @@ pub enum Commands {
/// Autoinstallation profile handling
#[command(subcommand)]
Profile(ProfileCommands),
/// Questions handling
#[command(subcommand)]
Questions(QuestionsCommands),
}
3 changes: 3 additions & 0 deletions rust/agama-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ mod error;
mod printers;
mod profile;
mod progress;
mod questions;

use crate::error::CliError;
use agama_lib::error::ServiceError;
Expand All @@ -17,6 +18,7 @@ use config::run as run_config_cmd;
use printers::Format;
use profile::run as run_profile_cmd;
use progress::InstallerProgress;
use questions::run as run_questions_cmd;
use std::{
process::{ExitCode, Termination},
thread::sleep,
Expand Down Expand Up @@ -128,6 +130,7 @@ async fn run_command(cli: Cli) -> anyhow::Result<()> {
let manager = build_manager().await?;
block_on(install(&manager, 3))
}
Commands::Questions(subcommand) => block_on(run_questions_cmd(subcommand)),
_ => unimplemented!(),
}
}
Expand Down
48 changes: 48 additions & 0 deletions rust/agama-cli/src/questions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
use agama_lib::connection;
use agama_lib::proxies::Questions1Proxy;
use anyhow::{Context, Ok};
use clap::{Args, Subcommand, ValueEnum};

#[derive(Subcommand, Debug)]
pub enum QuestionsCommands {
/// Set mode for answering questions.
Mode(ModesArgs),
}

#[derive(Args, Debug)]
pub struct ModesArgs {
#[arg(value_enum)]
value: Modes,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
pub enum Modes {
Interactive,
NonInteractive,
}
// TODO when more commands is added, refactor and add it to agama-lib and share a bit of functionality
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Beware that the code which is specific to the CLI should live in agama-cli. That's the main intention of this package.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well, I mean here code that handles questions, do connection, use Question API and so on. But lets see how future will look like. For rust it is nice that strict separation of public and private, so it is easy to refactor internal stuff.

async fn set_mode(value: Modes) -> anyhow::Result<()> {
match value {
Modes::NonInteractive => {
let connection = connection().await?;
let proxy = Questions1Proxy::new(&connection)
.await
.context("Failed to connect to Questions service")?;

// TODO: how to print dbus error in that anyhow?
proxy
.use_default_answer()
.await
.context("Failed to set default answer")?;
}
Modes::Interactive => log::info!("not implemented"), //TODO do it
}

Ok(())
}

pub async fn run(subcommand: QuestionsCommands) -> anyhow::Result<()> {
match subcommand {
QuestionsCommands::Mode(value) => set_mode(value.value).await,
}
}
28 changes: 15 additions & 13 deletions rust/agama-lib/src/proxies.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,30 +111,32 @@ trait Locale1 {
fn set_vconsole_keyboard(&self, value: &str) -> zbus::Result<()>;
}

#[dbus_proxy(
interface = "org.opensuse.Agama.Questions1",
default_service = "org.opensuse.Agama.Questions1",
default_path = "/org/opensuse/Agama/Questions1"
)]
#[dbus_proxy(interface = "org.opensuse.Agama.Questions1", assume_defaults = true)]
trait Questions1 {
/// Delete method
fn delete(&self, question: &zbus::zvariant::ObjectPath<'_>) -> zbus::Result<()>;

/// New method
#[dbus_proxy(name = "New")]
fn create(
fn new_generic(
&self,
class: &str,
text: &str,
options: &[&str],
default_option: &[&str],
default_option: &str,
data: std::collections::HashMap<&str, &str>,
) -> zbus::Result<zbus::zvariant::OwnedObjectPath>;

/// NewLuksActivation method
fn new_luks_activation(
/// NewWithPassword method
fn new_with_password(
&self,
device: &str,
label: &str,
size: &str,
attempt: u8,
class: &str,
text: &str,
options: &[&str],
default_option: &str,
data: std::collections::HashMap<&str, &str>,
) -> zbus::Result<zbus::zvariant::OwnedObjectPath>;

/// UseDefaultAnswer method
fn use_default_answer(&self) -> zbus::Result<()>;
}
18 changes: 18 additions & 0 deletions rust/agama-lib/src/questions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,24 @@ impl GenericQuestion {
}
}

/// Gets object path of given question. It is useful as parameter
/// for deleting it.
///
/// # Examples
///
/// ```
/// use std::collections::HashMap;
/// use agama_lib::questions::GenericQuestion;
/// let question = GenericQuestion::new(
/// 2,
/// "test_class".to_string(),
/// "Really?".to_string(),
/// vec!["Yes".to_string(), "No".to_string()],
/// "No".to_string(),
/// HashMap::new()
/// );
/// assert_eq!(question.object_path(), "/org/opensuse/Agama/Questions1/2".to_string());
/// ```
pub fn object_path(&self) -> String {
format!("/org/opensuse/Agama/Questions1/{}", self.id)
}
Expand Down
6 changes: 6 additions & 0 deletions rust/package/agama-cli.changes
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
-------------------------------------------------------------------
Tue Jul 18 13:32:04 UTC 2023 - Josef Reidinger <[email protected]>

- Add to CLI "questions" subcommand with mode option to set
interactive and non-interactive mode (gh#openSUSE/agama#668)

-------------------------------------------------------------------
Mon Jul 17 13:36:56 UTC 2023 - Imobach Gonzalez Sosa <[email protected]>

Expand Down
Loading