init project
This commit is contained in:
71
cmd/ppilot/main.go
Normal file
71
cmd/ppilot/main.go
Normal file
@@ -0,0 +1,71 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"parch-pilot/internal/config"
|
||||
"parch-pilot/internal/deploy"
|
||||
"parch-pilot/internal/restore"
|
||||
)
|
||||
|
||||
func main() {
|
||||
deployFlag := flag.Bool("deploy", false, "Deploy a new system from YAML using pacstrap and related tools (assumes partitions are prepared and runs as root)")
|
||||
restoreFlag := flag.Bool("restore", false, "Apply YAML configuration on an already installed system (assumes root and tools like yay/flatpak installed)")
|
||||
validate := flag.Bool("validate", false, "Validate the YAML file for parsing errors")
|
||||
|
||||
flag.Parse()
|
||||
|
||||
if flag.NArg() != 1 {
|
||||
fmt.Println("Usage: ppilot [--deploy|--restore|--validate] /path/to/yaml")
|
||||
flag.PrintDefaults()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
yamlPath := flag.Arg(0)
|
||||
|
||||
count := 0
|
||||
if *deployFlag {
|
||||
count++
|
||||
}
|
||||
if *restoreFlag {
|
||||
count++
|
||||
}
|
||||
if *validate {
|
||||
count++
|
||||
}
|
||||
if count != 1 {
|
||||
fmt.Println("Exactly one of --deploy, --restore, or --validate must be specified")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
conf, err := config.ParseConfig(yamlPath)
|
||||
if err != nil {
|
||||
fmt.Printf("Error parsing YAML: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if *validate {
|
||||
fmt.Println("YAML configuration is valid.")
|
||||
return
|
||||
}
|
||||
|
||||
if *restoreFlag {
|
||||
if err := restore.RestoreConfig(conf); err != nil {
|
||||
fmt.Printf("Error during restore: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println("Restore completed successfully.")
|
||||
return
|
||||
}
|
||||
|
||||
if *deployFlag {
|
||||
if err := deploy.DeploySystem(conf, yamlPath); err != nil {
|
||||
fmt.Printf("Error during deploy: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println("Deploy completed. Boot into the new system and run 'ppilot --restore /root/parch-pilot.yaml' as root after installing ppilot (e.g., via 'go install').")
|
||||
return
|
||||
}
|
||||
}
|
||||
5
go.mod
Normal file
5
go.mod
Normal file
@@ -0,0 +1,5 @@
|
||||
module parch-pilot
|
||||
|
||||
go 1.25.1
|
||||
|
||||
require gopkg.in/yaml.v3 v3.0.1
|
||||
4
go.sum
Normal file
4
go.sum
Normal file
@@ -0,0 +1,4 @@
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
39
internal/config/config.go
Normal file
39
internal/config/config.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
PacmanPackages []string `yaml:"pacman_packages"`
|
||||
AURPackages []string `yaml:"aur_packages"`
|
||||
FlatpakPackages []string `yaml:"flatpak_packages"`
|
||||
CustomCommands []string `yaml:"custom_commands"`
|
||||
Services []string `yaml:"services"`
|
||||
Deploy struct {
|
||||
RootDevice string `yaml:"root_device"`
|
||||
EfiDevice string `yaml:"efi_device"`
|
||||
MountPoint string `yaml:"mount_point"`
|
||||
} `yaml:"deploy"`
|
||||
}
|
||||
|
||||
func ParseConfig(yamlPath string) (*Config, error) {
|
||||
data, err := os.ReadFile(yamlPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var conf Config
|
||||
err = yaml.Unmarshal(data, &conf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if conf.Deploy.MountPoint == "" {
|
||||
conf.Deploy.MountPoint = "/mnt"
|
||||
}
|
||||
|
||||
return &conf, nil
|
||||
}
|
||||
90
internal/deploy/deploy.go
Normal file
90
internal/deploy/deploy.go
Normal file
@@ -0,0 +1,90 @@
|
||||
package deploy
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
"parch-pilot/internal/config"
|
||||
"parch-pilot/internal/utils"
|
||||
)
|
||||
|
||||
func DeploySystem(conf *config.Config, yamlPath string) error {
|
||||
if conf.Deploy.RootDevice == "" {
|
||||
return fmt.Errorf("root_device is required for --deploy")
|
||||
}
|
||||
|
||||
mp := conf.Deploy.MountPoint
|
||||
|
||||
if err := utils.ExecCmd("mount", conf.Deploy.RootDevice, mp); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if conf.Deploy.EfiDevice != "" {
|
||||
bootDir := filepath.Join(mp, "boot")
|
||||
if err := os.MkdirAll(bootDir, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := utils.ExecCmd("mount", conf.Deploy.EfiDevice, bootDir); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
pacArgs := []string{mp, "base", "linux", "linux-firmware", "grub"} // Include grub for fallback, but use systemd-boot if EFI
|
||||
if len(conf.PacmanPackages) > 0 {
|
||||
pacArgs = append(pacArgs, conf.PacmanPackages...)
|
||||
}
|
||||
if err := utils.ExecCmd("pacstrap", pacArgs...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fstabPath := filepath.Join(mp, "etc/fstab")
|
||||
genCmd := exec.Command("genfstab", "-U", mp)
|
||||
output, err := genCmd.Output()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.WriteFile(fstabPath, output, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if conf.Deploy.EfiDevice != "" {
|
||||
if err := utils.ExecCmd("arch-chroot", mp, "bootctl", "--esp-path=/boot", "install"); err != nil {
|
||||
fmt.Println("Warning: Failed to install systemd-boot; falling back to GRUB.")
|
||||
if err := utils.ExecCmd("arch-chroot", mp, "grub-install", "--target=x86_64-efi", "--efi-directory=/boot", "--bootloader-id=GRUB"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := utils.ExecCmd("arch-chroot", mp, "grub-mkconfig", "-o", "/boot/grub/grub.cfg"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// BIOS GRUB
|
||||
disk := filepath.Dir(conf.Deploy.RootDevice) // assume /dev/sda from /dev/sda2
|
||||
if err := utils.ExecCmd("arch-chroot", mp, "grub-install", "--target=i386-pc", disk); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := utils.ExecCmd("arch-chroot", mp, "grub-mkconfig", "-o", "/boot/grub/grub.cfg"); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
copyPath := filepath.Join(mp, "root/parch-pilot.yaml")
|
||||
src, err := os.Open(yamlPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer src.Close()
|
||||
dst, err := os.Create(copyPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer dst.Close()
|
||||
if _, err := io.Copy(dst, src); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
44
internal/restore/restore.go
Normal file
44
internal/restore/restore.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package restore
|
||||
|
||||
import (
|
||||
"parch-pilot/internal/config"
|
||||
"parch-pilot/internal/utils"
|
||||
)
|
||||
|
||||
func RestoreConfig(conf *config.Config) error {
|
||||
if len(conf.PacmanPackages) > 0 {
|
||||
args := append([]string{"-S", "--needed", "--noconfirm"}, conf.PacmanPackages...)
|
||||
if err := utils.ExecCmd("sudo", append([]string{"pacman"}, args...)...); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(conf.AURPackages) > 0 {
|
||||
args := append([]string{"-S", "--needed", "--noconfirm"}, conf.AURPackages...)
|
||||
if err := utils.ExecCmd("yay", args...); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if len(conf.FlatpakPackages) > 0 {
|
||||
for _, pkg := range conf.FlatpakPackages {
|
||||
if err := utils.ExecCmd("flatpak", "install", "--assumeyes", "--noninteractive", pkg); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for _, cmdStr := range conf.CustomCommands {
|
||||
if err := utils.ExecCmd("sh", "-c", cmdStr); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, srv := range conf.Services {
|
||||
if err := utils.ExecCmd("systemctl", "enable", "--now", srv); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
13
internal/utils/utils.go
Normal file
13
internal/utils/utils.go
Normal file
@@ -0,0 +1,13 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
func ExecCmd(name string, args ...string) error {
|
||||
cmd := exec.Command(name, args...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
return cmd.Run()
|
||||
}
|
||||
25
sample/sample.yaml
Normal file
25
sample/sample.yaml
Normal file
@@ -0,0 +1,25 @@
|
||||
deploy:
|
||||
root_device: /dev/sda2 # Example: Root filesystem partition
|
||||
efi_device: /dev/sda1 # Example: EFI partition (optional for BIOS systems)
|
||||
mount_point: /mnt # Optional: Defaults to /mnt if not specified
|
||||
|
||||
pacman_packages:
|
||||
- vim
|
||||
- git
|
||||
- networkmanager
|
||||
- efibootmgr # Useful for EFI setups
|
||||
|
||||
aur_packages:
|
||||
- visual-studio-code-bin
|
||||
|
||||
flatpak_packages:
|
||||
- com.slack.Slack
|
||||
- org.signal.Signal
|
||||
|
||||
custom_commands:
|
||||
- echo "Custom setup complete"
|
||||
- timedatectl set-ntp true
|
||||
|
||||
services:
|
||||
- NetworkManager
|
||||
- sshd
|
||||
Reference in New Issue
Block a user