Skip to content

Commit cfae44e

Browse files
authored
Merge pull request #3 from lfir/feat/parallel-actions
Allow multiple PostInstallUpdater instances to exist
2 parents 4cfa6a8 + de702d3 commit cfae44e

File tree

6 files changed

+263
-165
lines changed

6 files changed

+263
-165
lines changed

pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>cf.maybelambda</groupId>
88
<artifactId>fedora-setup-script</artifactId>
9-
<version>3.0.4</version>
9+
<version>3.0.5</version>
1010

1111
<properties>
1212
<maven.compiler.source>21</maven.compiler.source>
@@ -51,7 +51,7 @@
5151
<archive>
5252
<manifest>
5353
<addClasspath>true</addClasspath>
54-
<mainClass>cf.maybelambda.fedora.PostInstallUpdater</mainClass>
54+
<mainClass>cf.maybelambda.fedora.Main</mainClass>
5555
</manifest>
5656
</archive>
5757
</configuration>
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package cf.maybelambda.fedora;
2+
3+
import static cf.maybelambda.fedora.ConsoleIOHelper.GREEN;
4+
import static cf.maybelambda.fedora.ConsoleIOHelper.RED;
5+
import static cf.maybelambda.fedora.ConsoleIOHelper.color;
6+
import static cf.maybelambda.fedora.ConsoleIOHelper.confirm;
7+
import static cf.maybelambda.fedora.ConsoleIOHelper.promptForExclusions;
8+
import static java.util.Arrays.asList;
9+
10+
import java.util.List;
11+
import java.util.Scanner;
12+
13+
public class Main {
14+
static List<String> CMD_RPM_IMPORT = asList("sudo", "rpm", "--import");
15+
static List<String> CMD_DNF_INST_REPOS = asList("sudo", "dnf", "install", "-y");
16+
static List<String> CMD_DNF_INST = asList("sudo", "dnf", "--refresh", "install", "-y");
17+
static List<String> CMD_DNF_RM = asList("sudo", "dnf", "remove", "-y", "--noautoremove");
18+
static List<String> CMD_DNF_MARK = asList("sudo", "dnf", "mark", "user", "flatpak"); // single arg appended to cmd
19+
static List<String> CMD_DNF_AUTORM = asList("sudo", "dnf", "autoremove", "-y");
20+
static List<String> CMD_FLATPAK_REMOTE_ADD = asList("sudo", "flatpak", "remote-add", "--if-not-exists");
21+
static List<String> CMD_FLATPAK_INST = asList("flatpak", "install", "-y");
22+
static List<String> CMD_GETENT = asList("getent", "group");
23+
static List<String> CMD_ADD_GROUP = asList("sudo", "groupadd");
24+
static List<String> CMD_ADD_USER_TO_GROUP = asList("sudo", "usermod", "-aG");
25+
static List<String> CMD_SYSTEMCTL_ENABLE = asList("sudo", "systemctl", "enable", "--now", "cockpit.socket"); // single arg appended to cmd
26+
27+
public static void main(String[] args) {
28+
run(args, new PostInstallUpdater());
29+
}
30+
31+
static void run(String[] args, PostInstallUpdater updater) {
32+
if (asList(args).contains("-h") || asList(args).contains("--help")) {
33+
ConsoleIOHelper.printHelp();
34+
return;
35+
}
36+
37+
System.out.println(color("]|I{•------» Fedora Setup Script «------•}I|[\n", GREEN));
38+
39+
List<String> dnfInstallPackages = ConfigManager.getDnfInstallPackages();
40+
List<String> dnfRemovePackages = ConfigManager.getDnfRemovePackages();
41+
List<String> flatpakInstallPackages = ConfigManager.getFlatpakInstallPackages();
42+
Scanner scanner = new Scanner(System.in);
43+
44+
updater.setDryRun(asList(args).contains("--dry-run"));
45+
if (updater.isDryRun()) {
46+
System.out.println(color("---[Dry Run Mode] Shell Commands will not be executed.---\n", RED));
47+
}
48+
49+
if (confirm(scanner, "Install RPMFusion repos?")) {
50+
for (String key : ConfigManager.getRPMFusionGpgKeys()) {
51+
updater.runCommand(CMD_RPM_IMPORT, asList(key));
52+
}
53+
54+
List<String> repos = ConfigManager.getRPMFusionRepos();
55+
updater.runCommand(CMD_DNF_INST_REPOS, repos);
56+
}
57+
58+
if (confirm(scanner, "Install additional packages with DNF?")) {
59+
List<String> filtered = promptForExclusions(dnfInstallPackages, scanner);
60+
updater.runCommand(CMD_DNF_INST, filtered);
61+
}
62+
63+
if (confirm(scanner, "Remove all DNF packages marked for removal?")) {
64+
List<String> filtered = promptForExclusions(dnfRemovePackages, scanner);
65+
updater.runCommand(CMD_DNF_RM, filtered);
66+
updater.runCommand(CMD_DNF_MARK, asList());
67+
updater.runCommand(CMD_DNF_AUTORM, asList());
68+
}
69+
70+
if (confirm(scanner, "Install Flatpak apps?")) {
71+
String name = ConfigManager.getFlatpakRemoteName();
72+
String url = ConfigManager.getFlatpakRemoteUrl();
73+
updater.runCommand(CMD_FLATPAK_REMOTE_ADD, asList(name, url));
74+
75+
List<String> filtered = promptForExclusions(flatpakInstallPackages, scanner);
76+
filtered.addFirst(name);
77+
updater.runCommand(CMD_FLATPAK_INST, filtered);
78+
}
79+
80+
if (confirm(scanner, "Ensure admin groups exist and add current user to them?")) {
81+
String user = System.getProperty("user.name");
82+
for (String group : ConfigManager.getAdminGroups()) {
83+
int exit = updater.runCommand(CMD_GETENT, asList(group));
84+
boolean groupExists = (exit == 0);
85+
if (!groupExists) {
86+
System.out.println("Group '" + group + "' does not exist. Creating...");
87+
updater.runCommand(CMD_ADD_GROUP, asList(group));
88+
}
89+
updater.runCommand(CMD_ADD_USER_TO_GROUP, asList(group, user));
90+
}
91+
}
92+
93+
if (confirm(scanner, "Enable and start cockpit.socket service?")) {
94+
updater.runCommand(CMD_SYSTEMCTL_ENABLE, asList());
95+
}
96+
97+
System.out.println(color("\n.o0×X×0o. All actions completed. Goodbye. .o0×X×0o.", GREEN));
98+
}
99+
}

src/main/java/cf/maybelambda/fedora/PostInstallUpdater.java

Lines changed: 12 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -1,135 +1,33 @@
11
package cf.maybelambda.fedora;
22

3+
import static cf.maybelambda.fedora.ConsoleIOHelper.BLUE;
4+
import static cf.maybelambda.fedora.ConsoleIOHelper.YELLOW;
5+
import static cf.maybelambda.fedora.ConsoleIOHelper.color;
6+
import static java.util.stream.Stream.concat;
7+
38
import java.io.BufferedReader;
49
import java.io.IOException;
510
import java.io.InputStreamReader;
6-
import java.util.Arrays;
711
import java.util.List;
812
import java.util.NoSuchElementException;
9-
import java.util.Scanner;
10-
11-
import static cf.maybelambda.fedora.ConsoleIOHelper.BLUE;
12-
import static cf.maybelambda.fedora.ConsoleIOHelper.GREEN;
13-
import static cf.maybelambda.fedora.ConsoleIOHelper.RED;
14-
import static cf.maybelambda.fedora.ConsoleIOHelper.YELLOW;
15-
import static cf.maybelambda.fedora.ConsoleIOHelper.color;
16-
import static cf.maybelambda.fedora.ConsoleIOHelper.confirm;
17-
import static cf.maybelambda.fedora.ConsoleIOHelper.promptForExclusions;
1813

1914
public class PostInstallUpdater {
20-
private static boolean dryRun;
21-
22-
public static void main(String[] args) {
23-
if (Arrays.asList(args).contains("-h") || Arrays.asList(args).contains("--help")) {
24-
ConsoleIOHelper.printHelp();
25-
return;
26-
}
27-
28-
List<String> dnfInstallPackages = ConfigManager.getDnfInstallPackages();
29-
List<String> dnfRemovePackages = ConfigManager.getDnfRemovePackages();
30-
List<String> flatpakInstallPackages = ConfigManager.getFlatpakInstallPackages();
31-
Scanner scanner = new Scanner(System.in);
32-
33-
System.out.println(color("]|I{•------» Fedora Setup Script «------•}I|[\n", GREEN));
34-
setDryRun(Arrays.asList(args).contains("--dry-run"));
35-
if (isDryRun()) {
36-
System.out.println(color("---[Dry Run Mode] Shell Commands will not be executed.---\n", RED));
37-
}
38-
39-
if (confirm(scanner, "Install RPMFusion repos?")) {
40-
for (String key : ConfigManager.getRPMFusionGpgKeys()) {
41-
runCommand(new String[]{"sudo", "rpm", "--import", key});
42-
}
43-
44-
List<String> repos = ConfigManager.getRPMFusionRepos();
45-
String[] cmd = new String[repos.size() + 4];
46-
cmd[0] = "sudo";
47-
cmd[1] = "dnf";
48-
cmd[2] = "install";
49-
cmd[3] = "-y";
50-
for (int i = 0; i < repos.size(); i++) {
51-
cmd[4 + i] = repos.get(i);
52-
}
53-
runCommand(cmd);
54-
}
55-
56-
if (confirm(scanner, "Install additional packages with DNF?")) {
57-
List<String> filtered = promptForExclusions(dnfInstallPackages, scanner);
58-
String[] cmd = new String[filtered.size() + 5];
59-
cmd[0] = "sudo";
60-
cmd[1] = "dnf";
61-
cmd[2] = "--refresh";
62-
cmd[3] = "install";
63-
cmd[4] = "-y";
64-
for (int i = 0; i < filtered.size(); i++) {
65-
cmd[5 + i] = filtered.get(i);
66-
}
67-
runCommand(cmd);
68-
}
69-
70-
if (confirm(scanner, "Remove all DNF packages marked for removal?")) {
71-
List<String> filtered = promptForExclusions(dnfRemovePackages, scanner);
72-
String[] cmd = new String[filtered.size() + 4];
73-
cmd[0] = "sudo";
74-
cmd[1] = "dnf";
75-
cmd[2] = "remove";
76-
cmd[3] = "-y";
77-
for (int i = 0; i < filtered.size(); i++) {
78-
cmd[4 + i] = filtered.get(i);
79-
}
80-
runCommand(cmd);
81-
runCommand(new String[]{"sudo", "dnf", "autoremove", "-y"});
82-
}
83-
84-
if (confirm(scanner, "Install Flatpak apps?")) {
85-
String name = ConfigManager.getFlatpakRemoteName();
86-
String url = ConfigManager.getFlatpakRemoteUrl();
87-
runCommand(new String[]{"sudo", "flatpak", "remote-add", "--if-not-exists", name, url});
88-
List<String> filtered = promptForExclusions(flatpakInstallPackages, scanner);
89-
String[] cmd = new String[filtered.size() + 4];
90-
cmd[0] = "flatpak";
91-
cmd[1] = "install";
92-
cmd[2] = "-y";
93-
cmd[3] = name;
94-
for (int i = 0; i < filtered.size(); i++) {
95-
cmd[4 + i] = filtered.get(i);
96-
}
97-
runCommand(cmd);
98-
}
99-
100-
if (confirm(scanner, "Ensure admin groups exist and add current user to them?")) {
101-
String user = System.getProperty("user.name");
102-
for (String group : ConfigManager.getAdminGroups()) {
103-
int exit = runCommand(new String[]{"getent", "group", group});
104-
boolean groupExists = (exit == 0);
105-
if (!groupExists) {
106-
System.out.println("Group '" + group + "' does not exist. Creating...");
107-
runCommand(new String[]{"sudo", "groupadd", group});
108-
}
109-
runCommand(new String[]{"sudo", "usermod", "-aG", group, user});
110-
}
111-
}
112-
113-
if (confirm(scanner, "Enable and start cockpit.socket service?")) {
114-
runCommand(new String[]{"sudo", "systemctl", "enable", "--now", "cockpit.socket"});
115-
}
116-
117-
System.out.println(color("\n.o0×X×0o. All actions completed. Goodbye. .o0×X×0o.", GREEN));
118-
}
15+
private boolean dryRun;
11916

120-
static boolean isDryRun() {
17+
boolean isDryRun() {
12118
return dryRun;
12219
}
12320

124-
static void setDryRun(boolean dryRun) {
125-
PostInstallUpdater.dryRun = dryRun;
21+
void setDryRun(boolean dryRun) {
22+
this.dryRun = dryRun;
12623
}
12724

128-
static ProcessBuilder createProcessBuilder(String[] cmd) {
25+
ProcessBuilder createProcessBuilder(String[] cmd) {
12926
return new ProcessBuilder(cmd);
13027
}
13128

132-
static int runCommand(String[] command) {
29+
int runCommand(List<String> baseCmd, List<String> args) {
30+
String[] command = concat(baseCmd.stream(), args.stream()).toArray(String[]::new);
13331
System.out.println("Executing shell command: " + color(String.join(" ", command), BLUE));
13432
if (isDryRun()) {
13533
System.out.println(color("Dry-run: command not executed.", YELLOW));

src/main/resources/dnf-remove.cf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ krdc
1313
krfb
1414
ktnef
1515
neochat
16+
plasma-discover
1617
plasma-welcome
1718
skanpage
1819
spectacle
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package cf.maybelambda.fedora;
2+
3+
import static cf.maybelambda.fedora.ConfigManager.getAdminGroups;
4+
import static cf.maybelambda.fedora.ConfigManager.getDnfInstallPackages;
5+
import static cf.maybelambda.fedora.ConfigManager.getDnfRemovePackages;
6+
import static cf.maybelambda.fedora.ConfigManager.getFlatpakInstallPackages;
7+
import static cf.maybelambda.fedora.ConfigManager.getFlatpakRemoteName;
8+
import static cf.maybelambda.fedora.ConfigManager.getFlatpakRemoteUrl;
9+
import static cf.maybelambda.fedora.ConfigManager.getRPMFusionGpgKeys;
10+
import static cf.maybelambda.fedora.ConfigManager.getRPMFusionRepos;
11+
import static cf.maybelambda.fedora.Main.CMD_ADD_USER_TO_GROUP;
12+
import static cf.maybelambda.fedora.Main.CMD_DNF_AUTORM;
13+
import static cf.maybelambda.fedora.Main.CMD_DNF_INST;
14+
import static cf.maybelambda.fedora.Main.CMD_DNF_INST_REPOS;
15+
import static cf.maybelambda.fedora.Main.CMD_DNF_MARK;
16+
import static cf.maybelambda.fedora.Main.CMD_DNF_RM;
17+
import static cf.maybelambda.fedora.Main.CMD_FLATPAK_INST;
18+
import static cf.maybelambda.fedora.Main.CMD_FLATPAK_REMOTE_ADD;
19+
import static cf.maybelambda.fedora.Main.CMD_GETENT;
20+
import static cf.maybelambda.fedora.Main.CMD_RPM_IMPORT;
21+
import static cf.maybelambda.fedora.Main.CMD_SYSTEMCTL_ENABLE;
22+
import static org.junit.jupiter.api.Assertions.assertEquals;
23+
import static org.junit.jupiter.api.Assertions.assertTrue;
24+
import static org.mockito.ArgumentMatchers.any;
25+
import static org.mockito.Mockito.mock;
26+
import static org.mockito.Mockito.mockStatic;
27+
import static org.mockito.Mockito.when;
28+
29+
import java.io.ByteArrayInputStream;
30+
import java.nio.charset.StandardCharsets;
31+
import java.util.List;
32+
33+
import org.junit.jupiter.api.Test;
34+
import org.mockito.ArgumentCaptor;
35+
import org.mockito.MockedStatic;
36+
import org.mockito.Mockito;
37+
38+
class MainTests {
39+
private PostInstallUpdater mockUpdater = mock(PostInstallUpdater.class);
40+
41+
private void setupConfigManager(MockedStatic<ConfigManager> cfg) {
42+
cfg.when(ConfigManager::getDnfInstallPackages).thenReturn(List.of("pkg1"));
43+
cfg.when(ConfigManager::getDnfRemovePackages).thenReturn(List.of("pkg2"));
44+
cfg.when(ConfigManager::getFlatpakInstallPackages).thenReturn(List.of("flatpak1"));
45+
cfg.when(ConfigManager::getRPMFusionGpgKeys).thenReturn(List.of("key1"));
46+
cfg.when(ConfigManager::getRPMFusionRepos).thenReturn(List.of("repo1"));
47+
cfg.when(ConfigManager::getFlatpakRemoteName).thenReturn("flathub");
48+
cfg.when(ConfigManager::getFlatpakRemoteUrl).thenReturn("https://flathub");
49+
cfg.when(ConfigManager::getAdminGroups).thenReturn(List.of("wheel"));
50+
}
51+
52+
private void simulateUserInput() {
53+
// Simulate user input: answers to 9 prompts (y/y/empty/y/empty/y/empty/y/y)
54+
String input = String.join("\n", "y", "y", "", "y", "", "y", "", "y", "y") + "\n";
55+
System.setIn(new ByteArrayInputStream(input.getBytes(StandardCharsets.UTF_8)));
56+
}
57+
58+
@Test
59+
void runExecutesCommandStructureAndSequenceInCorrectOrdering() {
60+
try (MockedStatic<ConfigManager> cfg = mockStatic(ConfigManager.class)) {
61+
setupConfigManager(cfg);
62+
simulateUserInput();
63+
when(mockUpdater.runCommand(any(List.class), any(List.class))).thenReturn(0);
64+
65+
Main.run(new String[]{}, mockUpdater);
66+
67+
ArgumentCaptor<List<String>> captorPrefix = ArgumentCaptor.forClass(List.class);
68+
ArgumentCaptor<List<String>> captorArgs = ArgumentCaptor.forClass(List.class);
69+
Mockito.verify(mockUpdater, Mockito.atLeastOnce()).runCommand(captorPrefix.capture(), captorArgs.capture());
70+
List<List<String>> prefixes = captorPrefix.getAllValues();
71+
List<List<String>> args = captorArgs.getAllValues();
72+
int i = 0;
73+
74+
assertEquals(CMD_RPM_IMPORT, prefixes.get(i));
75+
assertEquals(getRPMFusionGpgKeys(), args.get(i));
76+
i++;
77+
assertEquals(CMD_DNF_INST_REPOS, prefixes.get(i));
78+
assertEquals(getRPMFusionRepos(), args.get(i));
79+
i++;
80+
assertEquals(CMD_DNF_INST, prefixes.get(i));
81+
assertEquals(getDnfInstallPackages(), args.get(i));
82+
i++;
83+
assertEquals(CMD_DNF_RM, prefixes.get(i));
84+
assertEquals(getDnfRemovePackages(), args.get(i));
85+
i++;
86+
assertEquals(CMD_DNF_MARK, prefixes.get(i));
87+
assertTrue(args.get(i).isEmpty());
88+
i++;
89+
assertEquals(CMD_DNF_AUTORM, prefixes.get(i));
90+
assertTrue(args.get(i).isEmpty());
91+
i++;
92+
assertEquals(CMD_FLATPAK_REMOTE_ADD, prefixes.get(i));
93+
assertEquals(List.of(getFlatpakRemoteName(), getFlatpakRemoteUrl()), args.get(i));
94+
i++;
95+
assertEquals(CMD_FLATPAK_INST, prefixes.get(i));
96+
assertEquals(List.of(getFlatpakRemoteName(), getFlatpakInstallPackages().getFirst()), args.get(i));
97+
i++;
98+
assertEquals(CMD_GETENT, prefixes.get(i));
99+
assertEquals(getAdminGroups(), args.get(i));
100+
i++;
101+
assertEquals(CMD_ADD_USER_TO_GROUP, prefixes.get(i));
102+
assertTrue(args.get(i).containsAll(getAdminGroups()));
103+
i++;
104+
assertEquals(CMD_SYSTEMCTL_ENABLE, prefixes.get(i));
105+
assertTrue(args.get(i).isEmpty());
106+
}
107+
}
108+
109+
@Test
110+
void helpOptionDisplaysHelpTextAndExits() {
111+
try (var filesMock = mockStatic(ConfigManager.class)) {
112+
filesMock.when(() -> ConfigManager.readResourceLines(any(String.class)))
113+
.thenReturn(List.of("Usage instructions go here"));
114+
115+
Main.main(new String[]{"--help"});
116+
Main.main(new String[]{"-h"});
117+
118+
filesMock.verify(() -> ConfigManager.getHelpText(), Mockito.times(2));
119+
}
120+
}
121+
}

0 commit comments

Comments
 (0)