Skip to content

Commit edfcd74

Browse files
authored
Merge pull request #1 from mdgspace/phoenix/packages
feat: Generate script to reinstall packages
2 parents d5e5a76 + a17f635 commit edfcd74

File tree

8 files changed

+282
-42
lines changed

8 files changed

+282
-42
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
dist/

main.go

Lines changed: 3 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,8 @@
11
package main
22

3-
import (
4-
"fmt"
5-
"os"
6-
"runtime"
7-
"strings"
8-
)
3+
import "github.com/mdgspace/sysreplicate/system"
94

5+
// main is the entry point for the program.
106
func main() {
11-
osType := runtime.GOOS
12-
fmt.Println("Detected OS Type:",osType)
13-
14-
switch(osType){
15-
case "darwin":
16-
fmt.Println("MacOS is not supported")
17-
case "windows":
18-
fmt.Println("Windows is not supported")
19-
case "linux":
20-
distro, base_distro := fetchLinusDistro()
21-
if(distro=="unknown" && base_distro=="unknown"){fmt.Println("Failed to fetch the details of your distro")}
22-
fmt.Println("Distribution:", distro)
23-
fmt.Println("Built On:", base_distro)
24-
default:
25-
fmt.Println("OS not supported")
26-
27-
}
7+
system.Run()
288
}
29-
30-
func fetchLinusDistro() (string, string) {
31-
data, err := os.ReadFile("/etc/os-release")
32-
33-
if (err != nil) {return "unknown", "unknown"}
34-
var distro, base_distro string
35-
36-
lines:= strings.Split(string(data),"\n")
37-
for _, line := range lines {
38-
if strings.HasPrefix(line, "ID="){
39-
distro =strings.Trim(strings.SplitN(line,"=",2)[1],`"`)
40-
}
41-
if (strings.HasPrefix(line, "ID_LIKE=")){
42-
base_distro = strings.Trim(strings.SplitN(line, "=",2)[1],`"`)
43-
}
44-
}
45-
46-
return distro, base_distro
47-
}

system/output/json.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package output
2+
3+
import (
4+
"encoding/json"
5+
)
6+
7+
// BuildSystemJSON creates a well-structured JSON object for the system info and packages.
8+
func BuildSystemJSON(osType, distro, baseDistro string, packages []string) ([]byte, error) {
9+
type ArchPackages struct {
10+
Official []string `json:"official_packages"`
11+
AUR []string `json:"aur_packages"`
12+
}
13+
type SystemInfo struct {
14+
OS string `json:"os"`
15+
Distro string `json:"distro"`
16+
BaseDistro string `json:"base_distro"`
17+
Packages interface{} `json:"packages"`
18+
}
19+
if baseDistro == "arch" {
20+
official, aur := SplitArchPackages(packages)
21+
archPkgs := ArchPackages{Official: official, AUR: aur}
22+
info := SystemInfo{
23+
OS: osType,
24+
Distro: distro,
25+
BaseDistro: baseDistro,
26+
Packages: archPkgs,
27+
}
28+
return json.MarshalIndent(info, "", " ")
29+
}
30+
info := SystemInfo{
31+
OS: osType,
32+
Distro: distro,
33+
BaseDistro: baseDistro,
34+
Packages: packages,
35+
}
36+
return json.MarshalIndent(info, "", " ")
37+
}

system/output/script.go

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
package output
2+
3+
import (
4+
"fmt"
5+
"os"
6+
)
7+
8+
// splitArchPackages splits the combined package list into official and AUR packages for Arch-based distros.
9+
func SplitArchPackages(packages []string) (official, aur []string) {
10+
isAUR := false
11+
for _, pkg := range packages {
12+
if pkg == "YayPackages" {
13+
isAUR = true
14+
continue
15+
}
16+
if isAUR {
17+
if pkg != "" {
18+
aur = append(aur, pkg)
19+
}
20+
} else {
21+
if pkg != "" {
22+
official = append(official, pkg)
23+
}
24+
}
25+
}
26+
return
27+
}
28+
29+
// generateInstallScript creates a shell script to install all packages for the given distro.
30+
// Returns an error if the script cannot be created or written.
31+
func GenerateInstallScript(baseDistro string, packages []string, scriptPath string) error {
32+
f, err := os.Create(scriptPath)
33+
if err != nil {
34+
return err
35+
}
36+
defer f.Close()
37+
38+
_, err = f.WriteString("#!/bin/bash\nset -e\necho 'Starting package installation...'\n")
39+
if err != nil {
40+
return err
41+
}
42+
43+
var installCmd string
44+
switch baseDistro {
45+
case "debian":
46+
installCmd = "sudo apt-get install -y"
47+
case "arch":
48+
installCmd = "sudo pacman -S --noconfirm"
49+
case "rhel", "fedora":
50+
installCmd = "sudo dnf install -y"
51+
case "void":
52+
installCmd = "sudo xbps-install -y"
53+
default:
54+
_, _ = f.WriteString("echo 'Unsupported distro for script generation.'\n")
55+
return nil
56+
}
57+
58+
if baseDistro == "arch" {
59+
official, aur := SplitArchPackages(packages)
60+
_, err = f.WriteString("echo 'Installing official packages with pacman...'\n")
61+
if err != nil {
62+
return err
63+
}
64+
for _, pkg := range official {
65+
if pkg == "" {
66+
continue
67+
}
68+
_, err = f.WriteString(fmt.Sprintf("%s %s || true\n", installCmd, pkg))
69+
if err != nil {
70+
return err
71+
}
72+
}
73+
_, err = f.WriteString("if ! command -v yay >/dev/null; then\n echo 'yay not found, installing yay...'\n sudo pacman -S --noconfirm yay\nfi\n")
74+
if err != nil {
75+
return err
76+
}
77+
_, err = f.WriteString("echo 'Installing AUR packages with yay...'\n")
78+
if err != nil {
79+
return err
80+
}
81+
for _, pkg := range aur {
82+
if pkg == "" {
83+
continue
84+
}
85+
_, err = f.WriteString(fmt.Sprintf("yay -S --noconfirm %s || true\n", pkg))
86+
if err != nil {
87+
return err
88+
}
89+
}
90+
return nil
91+
}
92+
93+
_, err = f.WriteString(fmt.Sprintf("echo 'Installing packages with %s...'\n", installCmd))
94+
if err != nil {
95+
return err
96+
}
97+
for _, pkg := range packages {
98+
if pkg == "" {
99+
continue
100+
}
101+
_, err = f.WriteString(fmt.Sprintf("%s %s || true\n", installCmd, pkg))
102+
if err != nil {
103+
return err
104+
}
105+
}
106+
return nil
107+
}

system/run.go

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package system
2+
3+
import (
4+
"fmt"
5+
"log"
6+
"os"
7+
"runtime"
8+
"github.com/mdgspace/sysreplicate/system/output"
9+
"github.com/mdgspace/sysreplicate/system/utils"
10+
)
11+
12+
// Run is the entry point for the system orchestrator.
13+
func Run() {
14+
osType := runtime.GOOS
15+
fmt.Println("Detected OS Type:", osType)
16+
17+
switch osType {
18+
case "darwin":
19+
fmt.Println("MacOS is not supported")
20+
case "windows":
21+
fmt.Println("Windows is not supported")
22+
case "linux":
23+
distro, baseDistro := utils.DetectDistro()
24+
if distro == "unknown" && baseDistro == "unknown" {
25+
log.Println("Failed to fetch the details of your distro")
26+
}
27+
fmt.Println("Distribution:", distro)
28+
fmt.Println("Built On:", baseDistro)
29+
packages := utils.FetchPackages(baseDistro)
30+
jsonObj, err := output.BuildSystemJSON(osType, distro, baseDistro, packages)
31+
if err != nil {
32+
log.Println("Error marshalling JSON:", err)
33+
} else {
34+
if err := os.MkdirAll(outputSysDir, 0744); err != nil {
35+
log.Println("Error creating sys output directory:", err)
36+
}
37+
if err := os.WriteFile(jsonOutputPath, jsonObj, 0644); err != nil {
38+
log.Println("Error writing JSON output:", err)
39+
}
40+
if err := os.MkdirAll(outputScriptsDir, 0744); err != nil {
41+
log.Println("Error creating scripts output directory:", err)
42+
}
43+
if err := output.GenerateInstallScript(baseDistro, packages, scriptOutputPath); err != nil {
44+
log.Println("Error generating install script:", err)
45+
} else {
46+
fmt.Println("Script generated successfully at:", scriptOutputPath)
47+
}
48+
}
49+
default:
50+
fmt.Println("OS not supported")
51+
}
52+
}

system/settings.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package system
2+
3+
const (
4+
outputSysDir = "dist/sys-info"
5+
outputScriptsDir = "dist"
6+
jsonOutputPath = outputSysDir + "/package.json"
7+
scriptOutputPath = outputScriptsDir + "/setup.sh"
8+
)

system/utils/detect_distro.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package utils
2+
3+
import (
4+
"os"
5+
"strings"
6+
)
7+
8+
// DetectDistro returns the distro and base distro.
9+
func DetectDistro() (string, string) {
10+
data, err := os.ReadFile("/etc/os-release")
11+
if err != nil {
12+
return "unknown", "unknown"
13+
}
14+
var distro, baseDistro string
15+
lines := strings.Split(string(data), "\n")
16+
for _, line := range lines {
17+
if strings.HasPrefix(line, "ID=") {
18+
distro = strings.Trim(strings.SplitN(line, "=", 2)[1], `"`)
19+
}
20+
if strings.HasPrefix(line, "ID_LIKE=") {
21+
baseDistro = strings.Trim(strings.SplitN(line, "=", 2)[1], `"`)
22+
}
23+
}
24+
return distro, baseDistro
25+
}

system/utils/fetch_packages.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package utils
2+
3+
import (
4+
"log"
5+
"os/exec"
6+
"strings"
7+
)
8+
9+
// FetchPackages returns a list of installed packages for the given base distro.
10+
func FetchPackages(baseDistro string) []string {
11+
var cmd *exec.Cmd
12+
var cmdYay *exec.Cmd
13+
switch baseDistro {
14+
case "debian":
15+
cmd = exec.Command("dpkg", "--get-selections")
16+
case "arch":
17+
cmd = exec.Command("pacman", "-Qn")
18+
cmdYay = exec.Command("pacman", "-Qm")
19+
case "rhel", "fedora":
20+
cmd = exec.Command("rpm", "-qa")
21+
case "void":
22+
cmd = exec.Command("xbps-query", "-l")
23+
default:
24+
log.Println("Your distro is unsupported, cannot identify package manager!")
25+
return []string{"unknown"}
26+
}
27+
28+
if baseDistro != "arch" {
29+
output, err := cmd.CombinedOutput()
30+
if err != nil {
31+
log.Println("Error in retrieving packages:", err)
32+
}
33+
return strings.Split(strings.TrimSpace(string(output)), "\n")
34+
}
35+
36+
outputPacman, err := cmd.CombinedOutput()
37+
outPutYay, errYay := cmdYay.CombinedOutput()
38+
if err != nil {
39+
log.Println("Error in retrieving Pacman packages:", err)
40+
}
41+
if errYay != nil {
42+
log.Println("Error in retrieving Yay packages:", errYay)
43+
}
44+
pacmanPackages := strings.Split(strings.TrimSpace(string(outputPacman)), "\n")
45+
yayPackages := strings.Split(strings.TrimSpace(string(outPutYay)), "\n")
46+
// Mark the split between official and AUR packages
47+
yayPackages = append([]string{"YayPackages"}, yayPackages...)
48+
return append(pacmanPackages, yayPackages...)
49+
}

0 commit comments

Comments
 (0)