304 lines
9.1 KiB
Rust
304 lines
9.1 KiB
Rust
use std::thread::available_parallelism;
|
|
|
|
use clap::{CommandFactory, Parser};
|
|
use feddy::*;
|
|
use ini::Ini;
|
|
|
|
fn main() {
|
|
configure_logger();
|
|
|
|
let cli = Cli::parse();
|
|
let mut cmd = Cli::command();
|
|
|
|
if let Some(generator) = cli.generator {
|
|
log::info!("Generating completion file for {generator:?}...");
|
|
print_completions(generator, &mut cmd);
|
|
return;
|
|
}
|
|
|
|
if let Some(command) = cli.command {
|
|
match command {
|
|
Commands::ConfigureDnf => {
|
|
if let Err(e) = configure_dnf() {
|
|
log::error!("Error configuring dnf: {}", e);
|
|
}
|
|
}
|
|
Commands::AddRepo(repo_command) => {
|
|
if let Err(e) = add_repo(repo_command) {
|
|
log::error!("Error adding repository: {}", e);
|
|
}
|
|
}
|
|
Commands::InstallRpmFusion => {
|
|
if let Err(e) = install_rpm_fusion() {
|
|
log::error!("Error installing RPM Fusion: {}", e);
|
|
}
|
|
}
|
|
Commands::ConfigureGroups(configure_groups_command) => {
|
|
if let Err(e) = configure_groups(configure_groups_command) {
|
|
log::error!("Error configuring groups: {}", e);
|
|
}
|
|
}
|
|
Commands::Package(package_command) => {
|
|
if let Err(e) = manage_package(package_command) {
|
|
log::error!("Error executing package command: {}", e);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
cmd.print_help().unwrap();
|
|
}
|
|
|
|
success!("Bye :)");
|
|
}
|
|
|
|
fn configure_groups(configure_groups_command: ConfigureGroupsCommand) -> Result<()> {
|
|
let groups = include_str!("../data/user_groups.txt").to_string();
|
|
|
|
if configure_groups_command.print {
|
|
log::info!("Printing groups...");
|
|
println!("{}", groups);
|
|
return Ok(());
|
|
}
|
|
|
|
let groups = groups
|
|
.lines()
|
|
.map(|line| line.trim())
|
|
.filter(|line| !line.is_empty())
|
|
.filter(|group| {
|
|
let group_exists = std::process::Command::new("getent")
|
|
.arg("group")
|
|
.arg(group)
|
|
.output()
|
|
.map(|output| output.status.success())
|
|
.unwrap_or(false);
|
|
if !group_exists {
|
|
log::warn!("Group {} does not exist.", group);
|
|
}
|
|
group_exists
|
|
})
|
|
.collect::<Vec<_>>();
|
|
|
|
if groups.is_empty() {
|
|
log::warn!("No valid groups found to add user to.");
|
|
return Ok(());
|
|
}
|
|
|
|
let groups = groups.join(",");
|
|
|
|
let user = configure_groups_command.user.unwrap_or_else(|| {
|
|
log::info!("No user specified, using current user");
|
|
whoami::username()
|
|
});
|
|
|
|
log::info!("Adding user {} to groups: {}", user, groups);
|
|
|
|
let mut cmd = std::process::Command::new("usermod");
|
|
cmd.arg("-aG");
|
|
cmd.arg(groups);
|
|
cmd.arg(&user);
|
|
|
|
log::debug!("Executing command: {:?}", cmd);
|
|
|
|
cmd.status()?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn add_repo(repo_command: AddRepoCommand) -> Result<()> {
|
|
match repo_command.command {
|
|
AddRepoSubCommand::Vscode => {
|
|
add_repo_common(
|
|
"vscode",
|
|
"Visual Studio Code",
|
|
"https://packages.microsoft.com/keys/microsoft.asc",
|
|
"https://packages.microsoft.com/yumrepos/vscode",
|
|
"/etc/yum.repos.d/vscode.repo",
|
|
)?;
|
|
}
|
|
AddRepoSubCommand::Mullvad => {
|
|
add_repo_common(
|
|
"mullvad-stable",
|
|
"Mullvad VPN",
|
|
"https://repository.mullvad.net/rpm/mullvad-keyring.asc",
|
|
"https://repository.mullvad.net/rpm/stable/$basearch",
|
|
"/etc/yum.repos.d/mullvad.repo",
|
|
)?;
|
|
}
|
|
AddRepoSubCommand::Vivaldi => {
|
|
add_repo_common(
|
|
"vivaldi",
|
|
"Vivaldi",
|
|
"https://repo.vivaldi.com/archive/linux_signing_key.pub",
|
|
"https://repo.vivaldi.com/archive/rpm/x86_64",
|
|
"/etc/yum.repos.d/vivaldi.repo",
|
|
)?;
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn add_repo_common(
|
|
name: &str,
|
|
display_name: &str,
|
|
gpg_key_url: &str,
|
|
baseurl: &str,
|
|
repo_file_path: &str,
|
|
) -> Result<()> {
|
|
log::info!("Importing {} GPG key...", display_name);
|
|
std::process::Command::new("rpm")
|
|
.arg("--import")
|
|
.arg(gpg_key_url)
|
|
.status()?;
|
|
|
|
let mut conf = Ini::new();
|
|
conf.with_section(Some(name))
|
|
.set("name", display_name)
|
|
.set("enabled", "1")
|
|
.set("gpgcheck", "1")
|
|
.set("autorefresh", "1")
|
|
.set("baseurl", baseurl)
|
|
.set("gpgkey", gpg_key_url);
|
|
|
|
log::info!("Adding {} repository...", display_name);
|
|
conf.write_to_file(repo_file_path)?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn install_rpm_fusion() -> Result<()> {
|
|
let fedora_version_vec = std::process::Command::new("rpm")
|
|
.arg("-E")
|
|
.arg("%fedora")
|
|
.output()?
|
|
.stdout;
|
|
let fedora_version = String::from_utf8_lossy(&fedora_version_vec);
|
|
let fedora_version = fedora_version.trim();
|
|
log::info!("Installing RPM Fusion for Fedora {}", fedora_version);
|
|
|
|
log::info!("Enabling the openh264 library...");
|
|
std::process::Command::new("dnf")
|
|
.arg("config-manager")
|
|
.arg("setopt")
|
|
.arg("fedora-cisco-openh264.enabled=1")
|
|
.status()?;
|
|
|
|
log::info!("Installing RPM Fusion free and non-free repositories...");
|
|
std::process::Command::new("dnf")
|
|
.arg("install")
|
|
.arg(format!(
|
|
"https://mirrors.rpmfusion.org/free/fedora/rpmfusion-free-release-{}.noarch.rpm",
|
|
fedora_version
|
|
))
|
|
.arg(format!(
|
|
"https://mirrors.rpmfusion.org/nonfree/fedora/rpmfusion-nonfree-release-{}.noarch.rpm",
|
|
fedora_version
|
|
))
|
|
.status()?;
|
|
|
|
log::info!("Installing RPM Fusion additional packages...");
|
|
std::process::Command::new("dnf")
|
|
.arg("install")
|
|
.arg("rpmfusion-free-appstream-data")
|
|
.arg("rpmfusion-nonfree-appstream-data")
|
|
.arg("rpmfusion-free-release-tainted")
|
|
.arg("rpmfusion-nonfree-release-tainted")
|
|
.status()?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn manage_package(package_command: PackageCommand) -> Result<()> {
|
|
let list = match &package_command.command {
|
|
PackageSubCommand::CommonList => include_str!("../data/common_list.txt").to_string(),
|
|
PackageSubCommand::AmdList => include_str!("../data/amd_list.txt").to_string(),
|
|
PackageSubCommand::IntelList => include_str!("../data/intel_list.txt").to_string(),
|
|
PackageSubCommand::GnomeExtraList => {
|
|
include_str!("../data/gnome_extra_list.txt").to_string()
|
|
}
|
|
PackageSubCommand::FirmwareList => include_str!("../data/firmware_list.txt").to_string(),
|
|
PackageSubCommand::CustomList(custom_list_command) => {
|
|
log::info!("Using custom list from {}", custom_list_command.file);
|
|
read_file!(&custom_list_command.file)?
|
|
}
|
|
};
|
|
|
|
if package_command.print {
|
|
log::info!("Printing package list...");
|
|
println!("{}", list);
|
|
return Ok(());
|
|
}
|
|
|
|
manage_list(list, package_command.remove)?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn manage_list(list: String, remove: bool) -> Result<()> {
|
|
process_packages(&list, remove, '+')?;
|
|
process_packages(&list, remove, '-')?;
|
|
Ok(())
|
|
}
|
|
|
|
fn process_packages(list: &String, remove: bool, prefix: char) -> Result<()> {
|
|
let packages = list
|
|
.lines()
|
|
.map(|line| line.trim())
|
|
.filter(|line| !line.is_empty() && line.starts_with(prefix))
|
|
.map(|line| line.trim_start_matches(prefix).trim())
|
|
.collect::<Vec<_>>();
|
|
|
|
if packages.is_empty() {
|
|
return Ok(());
|
|
}
|
|
|
|
let mut dnf_cmd = std::process::Command::new("dnf");
|
|
if remove {
|
|
if prefix == '+' {
|
|
log::info!("Removing wanted list's packages...");
|
|
dnf_cmd.arg("remove");
|
|
} else {
|
|
log::info!("Re-installing unwanted list's packages...");
|
|
dnf_cmd.arg("install").arg("--allowerasing");
|
|
}
|
|
} else {
|
|
if prefix == '+' {
|
|
log::info!("Installing wanted list's packages...");
|
|
dnf_cmd.arg("install").arg("--allowerasing");
|
|
} else {
|
|
log::info!("Removing unwanted list's packages...");
|
|
dnf_cmd.arg("remove");
|
|
}
|
|
}
|
|
|
|
packages.iter().for_each(|package| {
|
|
dnf_cmd.arg(package);
|
|
});
|
|
dnf_cmd.status()?;
|
|
Ok(())
|
|
}
|
|
|
|
fn configure_dnf() -> Result<()> {
|
|
log::info!("Tweaking dnf configuration...");
|
|
let mut conf = Ini::load_from_file("/etc/dnf/dnf.conf")?;
|
|
|
|
let max_parallel_downloads =
|
|
std::cmp::min(20, std::cmp::max(available_parallelism()?.get() / 2, 3));
|
|
|
|
log::info!(
|
|
"Setting max_parallel_downloads to {}",
|
|
max_parallel_downloads
|
|
);
|
|
log::info!("Setting defaultyes to True");
|
|
|
|
conf.with_section(Some("main"))
|
|
.set("defaultyes", "True")
|
|
.set("max_parallel_downloads", max_parallel_downloads.to_string());
|
|
|
|
// Write the changes back to the file
|
|
log::info!("Writing changes to /etc/dnf/dnf.conf");
|
|
conf.write_to_file("/etc/dnf/dnf.conf")?;
|
|
|
|
Ok(())
|
|
}
|