Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

<groupId>cf.maybelambda</groupId>
<artifactId>fedora-setup-script</artifactId>
<version>3.0.4</version>
<version>3.0.5</version>

<properties>
<maven.compiler.source>21</maven.compiler.source>
Expand Down Expand Up @@ -51,7 +51,7 @@
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>cf.maybelambda.fedora.PostInstallUpdater</mainClass>
<mainClass>cf.maybelambda.fedora.Main</mainClass>
</manifest>
</archive>
</configuration>
Expand Down
99 changes: 99 additions & 0 deletions src/main/java/cf/maybelambda/fedora/Main.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package cf.maybelambda.fedora;

import static cf.maybelambda.fedora.ConsoleIOHelper.GREEN;
import static cf.maybelambda.fedora.ConsoleIOHelper.RED;
import static cf.maybelambda.fedora.ConsoleIOHelper.color;
import static cf.maybelambda.fedora.ConsoleIOHelper.confirm;
import static cf.maybelambda.fedora.ConsoleIOHelper.promptForExclusions;
import static java.util.Arrays.asList;

import java.util.List;
import java.util.Scanner;

public class Main {
static List<String> CMD_RPM_IMPORT = asList("sudo", "rpm", "--import");
static List<String> CMD_DNF_INST_REPOS = asList("sudo", "dnf", "install", "-y");
static List<String> CMD_DNF_INST = asList("sudo", "dnf", "--refresh", "install", "-y");
static List<String> CMD_DNF_RM = asList("sudo", "dnf", "remove", "-y", "--noautoremove");
static List<String> CMD_DNF_MARK = asList("sudo", "dnf", "mark", "user", "flatpak"); // single arg appended to cmd
static List<String> CMD_DNF_AUTORM = asList("sudo", "dnf", "autoremove", "-y");
static List<String> CMD_FLATPAK_REMOTE_ADD = asList("sudo", "flatpak", "remote-add", "--if-not-exists");
static List<String> CMD_FLATPAK_INST = asList("flatpak", "install", "-y");
static List<String> CMD_GETENT = asList("getent", "group");
static List<String> CMD_ADD_GROUP = asList("sudo", "groupadd");
static List<String> CMD_ADD_USER_TO_GROUP = asList("sudo", "usermod", "-aG");
static List<String> CMD_SYSTEMCTL_ENABLE = asList("sudo", "systemctl", "enable", "--now", "cockpit.socket"); // single arg appended to cmd

public static void main(String[] args) {
run(args, new PostInstallUpdater());
}

static void run(String[] args, PostInstallUpdater updater) {
if (asList(args).contains("-h") || asList(args).contains("--help")) {
ConsoleIOHelper.printHelp();
return;
}

System.out.println(color("]|I{•------» Fedora Setup Script «------•}I|[\n", GREEN));

List<String> dnfInstallPackages = ConfigManager.getDnfInstallPackages();
List<String> dnfRemovePackages = ConfigManager.getDnfRemovePackages();
List<String> flatpakInstallPackages = ConfigManager.getFlatpakInstallPackages();
Scanner scanner = new Scanner(System.in);

updater.setDryRun(asList(args).contains("--dry-run"));
if (updater.isDryRun()) {
System.out.println(color("---[Dry Run Mode] Shell Commands will not be executed.---\n", RED));
}

if (confirm(scanner, "Install RPMFusion repos?")) {
for (String key : ConfigManager.getRPMFusionGpgKeys()) {
updater.runCommand(CMD_RPM_IMPORT, asList(key));
}

List<String> repos = ConfigManager.getRPMFusionRepos();
updater.runCommand(CMD_DNF_INST_REPOS, repos);
}

if (confirm(scanner, "Install additional packages with DNF?")) {
List<String> filtered = promptForExclusions(dnfInstallPackages, scanner);
updater.runCommand(CMD_DNF_INST, filtered);
}

if (confirm(scanner, "Remove all DNF packages marked for removal?")) {
List<String> filtered = promptForExclusions(dnfRemovePackages, scanner);
updater.runCommand(CMD_DNF_RM, filtered);
updater.runCommand(CMD_DNF_MARK, asList());
updater.runCommand(CMD_DNF_AUTORM, asList());
}

if (confirm(scanner, "Install Flatpak apps?")) {
String name = ConfigManager.getFlatpakRemoteName();
String url = ConfigManager.getFlatpakRemoteUrl();
updater.runCommand(CMD_FLATPAK_REMOTE_ADD, asList(name, url));

List<String> filtered = promptForExclusions(flatpakInstallPackages, scanner);
filtered.addFirst(name);
updater.runCommand(CMD_FLATPAK_INST, filtered);
}

if (confirm(scanner, "Ensure admin groups exist and add current user to them?")) {
String user = System.getProperty("user.name");
for (String group : ConfigManager.getAdminGroups()) {
int exit = updater.runCommand(CMD_GETENT, asList(group));
boolean groupExists = (exit == 0);
if (!groupExists) {
System.out.println("Group '" + group + "' does not exist. Creating...");
updater.runCommand(CMD_ADD_GROUP, asList(group));
}
updater.runCommand(CMD_ADD_USER_TO_GROUP, asList(group, user));
}
}

if (confirm(scanner, "Enable and start cockpit.socket service?")) {
updater.runCommand(CMD_SYSTEMCTL_ENABLE, asList());
}

System.out.println(color("\n.o0×X×0o. All actions completed. Goodbye. .o0×X×0o.", GREEN));
}
}
126 changes: 12 additions & 114 deletions src/main/java/cf/maybelambda/fedora/PostInstallUpdater.java
Original file line number Diff line number Diff line change
@@ -1,135 +1,33 @@
package cf.maybelambda.fedora;

import static cf.maybelambda.fedora.ConsoleIOHelper.BLUE;
import static cf.maybelambda.fedora.ConsoleIOHelper.YELLOW;
import static cf.maybelambda.fedora.ConsoleIOHelper.color;
import static java.util.stream.Stream.concat;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Scanner;

import static cf.maybelambda.fedora.ConsoleIOHelper.BLUE;
import static cf.maybelambda.fedora.ConsoleIOHelper.GREEN;
import static cf.maybelambda.fedora.ConsoleIOHelper.RED;
import static cf.maybelambda.fedora.ConsoleIOHelper.YELLOW;
import static cf.maybelambda.fedora.ConsoleIOHelper.color;
import static cf.maybelambda.fedora.ConsoleIOHelper.confirm;
import static cf.maybelambda.fedora.ConsoleIOHelper.promptForExclusions;

public class PostInstallUpdater {
private static boolean dryRun;

public static void main(String[] args) {
if (Arrays.asList(args).contains("-h") || Arrays.asList(args).contains("--help")) {
ConsoleIOHelper.printHelp();
return;
}

List<String> dnfInstallPackages = ConfigManager.getDnfInstallPackages();
List<String> dnfRemovePackages = ConfigManager.getDnfRemovePackages();
List<String> flatpakInstallPackages = ConfigManager.getFlatpakInstallPackages();
Scanner scanner = new Scanner(System.in);

System.out.println(color("]|I{•------» Fedora Setup Script «------•}I|[\n", GREEN));
setDryRun(Arrays.asList(args).contains("--dry-run"));
if (isDryRun()) {
System.out.println(color("---[Dry Run Mode] Shell Commands will not be executed.---\n", RED));
}

if (confirm(scanner, "Install RPMFusion repos?")) {
for (String key : ConfigManager.getRPMFusionGpgKeys()) {
runCommand(new String[]{"sudo", "rpm", "--import", key});
}

List<String> repos = ConfigManager.getRPMFusionRepos();
String[] cmd = new String[repos.size() + 4];
cmd[0] = "sudo";
cmd[1] = "dnf";
cmd[2] = "install";
cmd[3] = "-y";
for (int i = 0; i < repos.size(); i++) {
cmd[4 + i] = repos.get(i);
}
runCommand(cmd);
}

if (confirm(scanner, "Install additional packages with DNF?")) {
List<String> filtered = promptForExclusions(dnfInstallPackages, scanner);
String[] cmd = new String[filtered.size() + 5];
cmd[0] = "sudo";
cmd[1] = "dnf";
cmd[2] = "--refresh";
cmd[3] = "install";
cmd[4] = "-y";
for (int i = 0; i < filtered.size(); i++) {
cmd[5 + i] = filtered.get(i);
}
runCommand(cmd);
}

if (confirm(scanner, "Remove all DNF packages marked for removal?")) {
List<String> filtered = promptForExclusions(dnfRemovePackages, scanner);
String[] cmd = new String[filtered.size() + 4];
cmd[0] = "sudo";
cmd[1] = "dnf";
cmd[2] = "remove";
cmd[3] = "-y";
for (int i = 0; i < filtered.size(); i++) {
cmd[4 + i] = filtered.get(i);
}
runCommand(cmd);
runCommand(new String[]{"sudo", "dnf", "autoremove", "-y"});
}

if (confirm(scanner, "Install Flatpak apps?")) {
String name = ConfigManager.getFlatpakRemoteName();
String url = ConfigManager.getFlatpakRemoteUrl();
runCommand(new String[]{"sudo", "flatpak", "remote-add", "--if-not-exists", name, url});
List<String> filtered = promptForExclusions(flatpakInstallPackages, scanner);
String[] cmd = new String[filtered.size() + 4];
cmd[0] = "flatpak";
cmd[1] = "install";
cmd[2] = "-y";
cmd[3] = name;
for (int i = 0; i < filtered.size(); i++) {
cmd[4 + i] = filtered.get(i);
}
runCommand(cmd);
}

if (confirm(scanner, "Ensure admin groups exist and add current user to them?")) {
String user = System.getProperty("user.name");
for (String group : ConfigManager.getAdminGroups()) {
int exit = runCommand(new String[]{"getent", "group", group});
boolean groupExists = (exit == 0);
if (!groupExists) {
System.out.println("Group '" + group + "' does not exist. Creating...");
runCommand(new String[]{"sudo", "groupadd", group});
}
runCommand(new String[]{"sudo", "usermod", "-aG", group, user});
}
}

if (confirm(scanner, "Enable and start cockpit.socket service?")) {
runCommand(new String[]{"sudo", "systemctl", "enable", "--now", "cockpit.socket"});
}

System.out.println(color("\n.o0×X×0o. All actions completed. Goodbye. .o0×X×0o.", GREEN));
}
private boolean dryRun;

static boolean isDryRun() {
boolean isDryRun() {
return dryRun;
}

static void setDryRun(boolean dryRun) {
PostInstallUpdater.dryRun = dryRun;
void setDryRun(boolean dryRun) {
this.dryRun = dryRun;
}

static ProcessBuilder createProcessBuilder(String[] cmd) {
ProcessBuilder createProcessBuilder(String[] cmd) {
return new ProcessBuilder(cmd);
}

static int runCommand(String[] command) {
int runCommand(List<String> baseCmd, List<String> args) {
String[] command = concat(baseCmd.stream(), args.stream()).toArray(String[]::new);
System.out.println("Executing shell command: " + color(String.join(" ", command), BLUE));
if (isDryRun()) {
System.out.println(color("Dry-run: command not executed.", YELLOW));
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/dnf-remove.cf
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ krdc
krfb
ktnef
neochat
plasma-discover
plasma-welcome
skanpage
spectacle
121 changes: 121 additions & 0 deletions src/test/java/cf/maybelambda/fedora/MainTests.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package cf.maybelambda.fedora;

import static cf.maybelambda.fedora.ConfigManager.getAdminGroups;
import static cf.maybelambda.fedora.ConfigManager.getDnfInstallPackages;
import static cf.maybelambda.fedora.ConfigManager.getDnfRemovePackages;
import static cf.maybelambda.fedora.ConfigManager.getFlatpakInstallPackages;
import static cf.maybelambda.fedora.ConfigManager.getFlatpakRemoteName;
import static cf.maybelambda.fedora.ConfigManager.getFlatpakRemoteUrl;
import static cf.maybelambda.fedora.ConfigManager.getRPMFusionGpgKeys;
import static cf.maybelambda.fedora.ConfigManager.getRPMFusionRepos;
import static cf.maybelambda.fedora.Main.CMD_ADD_USER_TO_GROUP;
import static cf.maybelambda.fedora.Main.CMD_DNF_AUTORM;
import static cf.maybelambda.fedora.Main.CMD_DNF_INST;
import static cf.maybelambda.fedora.Main.CMD_DNF_INST_REPOS;
import static cf.maybelambda.fedora.Main.CMD_DNF_MARK;
import static cf.maybelambda.fedora.Main.CMD_DNF_RM;
import static cf.maybelambda.fedora.Main.CMD_FLATPAK_INST;
import static cf.maybelambda.fedora.Main.CMD_FLATPAK_REMOTE_ADD;
import static cf.maybelambda.fedora.Main.CMD_GETENT;
import static cf.maybelambda.fedora.Main.CMD_RPM_IMPORT;
import static cf.maybelambda.fedora.Main.CMD_SYSTEMCTL_ENABLE;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.when;

import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;

import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.MockedStatic;
import org.mockito.Mockito;

class MainTests {
private PostInstallUpdater mockUpdater = mock(PostInstallUpdater.class);

private void setupConfigManager(MockedStatic<ConfigManager> cfg) {
cfg.when(ConfigManager::getDnfInstallPackages).thenReturn(List.of("pkg1"));
cfg.when(ConfigManager::getDnfRemovePackages).thenReturn(List.of("pkg2"));
cfg.when(ConfigManager::getFlatpakInstallPackages).thenReturn(List.of("flatpak1"));
cfg.when(ConfigManager::getRPMFusionGpgKeys).thenReturn(List.of("key1"));
cfg.when(ConfigManager::getRPMFusionRepos).thenReturn(List.of("repo1"));
cfg.when(ConfigManager::getFlatpakRemoteName).thenReturn("flathub");
cfg.when(ConfigManager::getFlatpakRemoteUrl).thenReturn("https://flathub");
cfg.when(ConfigManager::getAdminGroups).thenReturn(List.of("wheel"));
}

private void simulateUserInput() {
// Simulate user input: answers to 9 prompts (y/y/empty/y/empty/y/empty/y/y)
String input = String.join("\n", "y", "y", "", "y", "", "y", "", "y", "y") + "\n";
System.setIn(new ByteArrayInputStream(input.getBytes(StandardCharsets.UTF_8)));
}

@Test
void runExecutesCommandStructureAndSequenceInCorrectOrdering() {
try (MockedStatic<ConfigManager> cfg = mockStatic(ConfigManager.class)) {
setupConfigManager(cfg);
simulateUserInput();
when(mockUpdater.runCommand(any(List.class), any(List.class))).thenReturn(0);

Main.run(new String[]{}, mockUpdater);

ArgumentCaptor<List<String>> captorPrefix = ArgumentCaptor.forClass(List.class);
ArgumentCaptor<List<String>> captorArgs = ArgumentCaptor.forClass(List.class);
Mockito.verify(mockUpdater, Mockito.atLeastOnce()).runCommand(captorPrefix.capture(), captorArgs.capture());
List<List<String>> prefixes = captorPrefix.getAllValues();
List<List<String>> args = captorArgs.getAllValues();
int i = 0;

assertEquals(CMD_RPM_IMPORT, prefixes.get(i));
assertEquals(getRPMFusionGpgKeys(), args.get(i));
i++;
assertEquals(CMD_DNF_INST_REPOS, prefixes.get(i));
assertEquals(getRPMFusionRepos(), args.get(i));
i++;
assertEquals(CMD_DNF_INST, prefixes.get(i));
assertEquals(getDnfInstallPackages(), args.get(i));
i++;
assertEquals(CMD_DNF_RM, prefixes.get(i));
assertEquals(getDnfRemovePackages(), args.get(i));
i++;
assertEquals(CMD_DNF_MARK, prefixes.get(i));
assertTrue(args.get(i).isEmpty());
i++;
assertEquals(CMD_DNF_AUTORM, prefixes.get(i));
assertTrue(args.get(i).isEmpty());
i++;
assertEquals(CMD_FLATPAK_REMOTE_ADD, prefixes.get(i));
assertEquals(List.of(getFlatpakRemoteName(), getFlatpakRemoteUrl()), args.get(i));
i++;
assertEquals(CMD_FLATPAK_INST, prefixes.get(i));
assertEquals(List.of(getFlatpakRemoteName(), getFlatpakInstallPackages().getFirst()), args.get(i));
i++;
assertEquals(CMD_GETENT, prefixes.get(i));
assertEquals(getAdminGroups(), args.get(i));
i++;
assertEquals(CMD_ADD_USER_TO_GROUP, prefixes.get(i));
assertTrue(args.get(i).containsAll(getAdminGroups()));
i++;
assertEquals(CMD_SYSTEMCTL_ENABLE, prefixes.get(i));
assertTrue(args.get(i).isEmpty());
}
}

@Test
void helpOptionDisplaysHelpTextAndExits() {
try (var filesMock = mockStatic(ConfigManager.class)) {
filesMock.when(() -> ConfigManager.readResourceLines(any(String.class)))
.thenReturn(List.of("Usage instructions go here"));

Main.main(new String[]{"--help"});
Main.main(new String[]{"-h"});

filesMock.verify(() -> ConfigManager.getHelpText(), Mockito.times(2));
}
}
}
Loading