163 lines
4.9 KiB
Go
163 lines
4.9 KiB
Go
package cmd
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"os"
|
||
"path/filepath"
|
||
"strings"
|
||
"time"
|
||
|
||
"git.gostacks.org/iwasforcedtobehere/fastestmirror/internal/config"
|
||
"git.gostacks.org/iwasforcedtobehere/fastestmirror/internal/distro"
|
||
"git.gostacks.org/iwasforcedtobehere/fastestmirror/internal/mirror"
|
||
"github.com/fatih/color"
|
||
"github.com/spf13/cobra"
|
||
)
|
||
|
||
var applyCmd = &cobra.Command{
|
||
Use: "apply",
|
||
Short: "Apply the fastest mirror to your system configuration",
|
||
Long: `Apply the fastest mirror found to your system's package manager configuration.
|
||
|
||
This will:
|
||
• Backup your current configuration (because we're not savages)
|
||
• Update your package manager to use the fastest mirror
|
||
• Test the new configuration
|
||
|
||
WARNING: This modifies system files and requires root privileges.
|
||
Don't blame us if you break something - you've been warned! 🔥`,
|
||
RunE: runApply,
|
||
}
|
||
|
||
var (
|
||
force bool
|
||
dryRun bool
|
||
backupPath string
|
||
)
|
||
|
||
func runApply(cmd *cobra.Command, args []string) error {
|
||
// Check if running as root
|
||
if os.Geteuid() != 0 && !dryRun {
|
||
binaryName := "fastestmirror"
|
||
if len(os.Args) > 0 {
|
||
binaryName = filepath.Base(os.Args[0])
|
||
}
|
||
return fmt.Errorf("this command requires root privileges. Try: sudo %s apply", binaryName)
|
||
}
|
||
|
||
// Detect distribution
|
||
fmt.Println("Detecting distribution...")
|
||
distroInfo, err := distro.DetectDistribution()
|
||
if err != nil {
|
||
return fmt.Errorf("could not detect distribution: %w", err)
|
||
}
|
||
|
||
if !distroInfo.IsSupported() {
|
||
return fmt.Errorf("distribution %s is not supported yet", distroInfo.ID)
|
||
}
|
||
|
||
configFile := distroInfo.GetConfigFile()
|
||
if configFile == "" {
|
||
return fmt.Errorf("no configuration file defined for %s", distroInfo.Family)
|
||
}
|
||
|
||
if dryRun {
|
||
fmt.Printf("🧪 DRY RUN: Would modify %s\n", configFile)
|
||
fmt.Println("🧪 DRY RUN: Would backup existing configuration")
|
||
fmt.Println("🧪 DRY RUN: Would test fastest mirror and apply changes")
|
||
return nil
|
||
}
|
||
|
||
// Create configuration manager
|
||
configManager := config.NewConfigManager(distroInfo)
|
||
|
||
// First, find the fastest mirror
|
||
fmt.Printf("Finding fastest mirror for %s...\n", color.YellowString(distroInfo.Family))
|
||
|
||
mirrorList := mirror.NewMirrorList(distroInfo.Family)
|
||
|
||
ctx, cancel := context.WithTimeout(context.Background(), 120*time.Second)
|
||
defer cancel()
|
||
|
||
err = mirrorList.LoadMirrors(ctx, distroInfo.Family)
|
||
if err != nil {
|
||
return fmt.Errorf("failed to load mirrors: %w", err)
|
||
}
|
||
|
||
err = mirrorList.TestMirrors(ctx, 10*time.Second)
|
||
if err != nil {
|
||
return fmt.Errorf("failed to test mirrors: %w", err)
|
||
}
|
||
|
||
bestMirror := mirrorList.GetBest()
|
||
if bestMirror == nil {
|
||
return fmt.Errorf("no working mirrors found - your connection might be fucked")
|
||
}
|
||
|
||
fmt.Printf("Best mirror found: %s\n", color.GreenString(bestMirror.URL))
|
||
fmt.Printf("Speed: %.1f MB/s, Latency: %dms\n", bestMirror.Speed, bestMirror.Latency.Milliseconds())
|
||
|
||
// Ask for confirmation unless forced
|
||
if !force {
|
||
fmt.Printf("\nThis will modify %s\n", color.YellowString(configFile))
|
||
fmt.Print("Do you want to continue? [y/N]: ")
|
||
|
||
var response string
|
||
fmt.Scanln(&response)
|
||
|
||
if strings.ToLower(response) != "y" && strings.ToLower(response) != "yes" {
|
||
fmt.Println("Operation cancelled.")
|
||
return nil
|
||
}
|
||
}
|
||
|
||
// Create backup
|
||
fmt.Println("Creating backup...")
|
||
backupPath, err := configManager.BackupConfig()
|
||
if err != nil {
|
||
return fmt.Errorf("failed to create backup: %w", err)
|
||
}
|
||
fmt.Printf("Backup created: %s\n", color.GreenString(backupPath))
|
||
|
||
// Apply the fastest mirror
|
||
fmt.Println("Applying fastest mirror configuration...")
|
||
err = configManager.ApplyMirror(bestMirror.URL)
|
||
if err != nil {
|
||
// Try to restore backup on failure
|
||
fmt.Printf("Failed to apply configuration: %v\n", err)
|
||
fmt.Println("<22> Attempting to restore backup...")
|
||
|
||
if restoreErr := configManager.RestoreBackup(backupPath); restoreErr != nil {
|
||
return fmt.Errorf("configuration failed AND backup restore failed: %v (original error: %v)", restoreErr, err)
|
||
}
|
||
|
||
fmt.Println("Backup restored successfully")
|
||
return fmt.Errorf("configuration application failed, backup restored: %w", err)
|
||
}
|
||
|
||
fmt.Printf("Successfully applied fastest mirror!\n")
|
||
fmt.Printf("New mirror: %s\n", color.GreenString(bestMirror.URL))
|
||
fmt.Println()
|
||
fmt.Printf("Don't forget to run your package manager's update command:\n")
|
||
|
||
switch distroInfo.Family {
|
||
case "debian":
|
||
fmt.Printf(" %s\n", color.CyanString("sudo apt update"))
|
||
case "arch":
|
||
fmt.Printf(" %s\n", color.CyanString("sudo pacman -Sy"))
|
||
case "fedora":
|
||
fmt.Printf(" %s\n", color.CyanString("sudo dnf clean all && sudo dnf makecache"))
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
func init() {
|
||
rootCmd.AddCommand(applyCmd)
|
||
|
||
applyCmd.Flags().BoolVarP(&force, "force", "f", false, "Force application without confirmation")
|
||
applyCmd.Flags().BoolVarP(&dryRun, "dry-run", "d", false, "Show what would be done without making changes")
|
||
applyCmd.Flags().StringVarP(&backupPath, "backup", "b", "", "Custom backup file path")
|
||
}
|