Skip to content
Open
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
116 changes: 116 additions & 0 deletions src/main/java/cf/maybelambda/fedora/Main.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
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 {
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(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);
}
updater.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);
}
updater.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);
}
updater.runCommand(cmd);
updater.runCommand(new String[]{"sudo", "dnf", "autoremove", "-y"});
}

if (confirm(scanner, "Install Flatpak apps?")) {
String name = ConfigManager.getFlatpakRemoteName();
String url = ConfigManager.getFlatpakRemoteUrl();
updater.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);
}
updater.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 = updater.runCommand(new String[]{"getent", "group", group});
boolean groupExists = (exit == 0);
if (!groupExists) {
System.out.println("Group '" + group + "' does not exist. Creating...");
updater.runCommand(new String[]{"sudo", "groupadd", group});
}
updater.runCommand(new String[]{"sudo", "usermod", "-aG", group, user});
}
}

if (confirm(scanner, "Enable and start cockpit.socket service?")) {
updater.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));
}
}
125 changes: 10 additions & 115 deletions src/main/java/cf/maybelambda/fedora/PostInstallUpdater.java
Original file line number Diff line number Diff line change
@@ -1,135 +1,30 @@
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 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(String[] command) {
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
88 changes: 88 additions & 0 deletions src/test/java/cf/maybelambda/fedora/MainTests.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package cf.maybelambda.fedora;

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 static final List<String> EXPECTED_COMMAND_PARTS = List.of(
"rpm --import",
"dnf install -y repo1",
"dnf --refresh install -y pkg1",
"dnf remove -y pkg2",
"dnf autoremove",
"flatpak remote-add",
"flatpak install -y flathub flatpak1",
"getent group wheel",
"usermod -aG wheel",
"systemctl enable --now cockpit.socket"
);

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)));
}

private void assertCommandSequence(List<String> actualCommands) {
for (int i = 0; i < EXPECTED_COMMAND_PARTS.size(); i++) {
assertTrue(actualCommands.get(i).contains(EXPECTED_COMMAND_PARTS.get(i)),
"Command at index " + i + " should contain '" + EXPECTED_COMMAND_PARTS.get(i) + "'");
}
}

@Test
void runExecutesAllActionsInExpectedOrder() {
try (var cfg = mockStatic(ConfigManager.class)) {
setupConfigManager(cfg);
simulateUserInput();
when(mockUpdater.runCommand(any(String[].class))).thenReturn(0);

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

ArgumentCaptor<String[]> captor = ArgumentCaptor.forClass(String[].class);
Mockito.verify(mockUpdater, Mockito.atLeastOnce()).runCommand(captor.capture());
List<String> joined = captor.getAllValues().stream()
.map(arr -> String.join(" ", arr))
.toList();

assertCommandSequence(joined);
}
}

@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