Files
fastestmirror/cmd/apply.go
2025-09-13 02:47:20 +03:00

163 lines
4.9 KiB
Go
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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("\n⚠ This 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")
}