done
This commit is contained in:
162
cmd/apply.go
Normal file
162
cmd/apply.go
Normal file
@@ -0,0 +1,162 @@
|
||||
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")
|
||||
}
|
171
cmd/find.go
Normal file
171
cmd/find.go
Normal file
@@ -0,0 +1,171 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"git.gostacks.org/iwasforcedtobehere/fastestmirror/internal/distro"
|
||||
"git.gostacks.org/iwasforcedtobehere/fastestmirror/internal/mirror"
|
||||
"github.com/fatih/color"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
verbose bool
|
||||
timeout int
|
||||
topCount int
|
||||
)
|
||||
|
||||
var findCmd = &cobra.Command{
|
||||
Use: "find",
|
||||
Short: "Find the fastest mirrors for your distribution",
|
||||
Long: `Find and test mirrors to determine which ones will make your package
|
||||
manager fly faster than a caffinated developer on Monday morning.
|
||||
|
||||
This command will:
|
||||
• Auto-detect your Linux distribution
|
||||
• Test multiple mirrors concurrently
|
||||
• Rank them by speed and reliability
|
||||
• Show you the results in glorious color`,
|
||||
RunE: runFind,
|
||||
}
|
||||
|
||||
func runFind(cmd *cobra.Command, args []string) error {
|
||||
// Detect distribution
|
||||
fmt.Println(color.CyanString("🔍 Detecting your Linux distribution..."))
|
||||
|
||||
distroInfo, err := distro.DetectDistribution()
|
||||
if err != nil {
|
||||
return fmt.Errorf("fuck me, couldn't detect your distro: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("📦 Found: %s\n", color.GreenString(distroInfo.String()))
|
||||
|
||||
if !distroInfo.IsSupported() {
|
||||
return fmt.Errorf("sorry, %s isn't supported yet - but hey, you're using something exotic! 🦄", distroInfo.ID)
|
||||
}
|
||||
|
||||
// Get mirrors for this distribution family
|
||||
fmt.Printf("🔧 Loading mirrors for %s family...\n", color.YellowString(distroInfo.Family))
|
||||
|
||||
mirrorList := mirror.NewMirrorList(distroInfo.Family)
|
||||
|
||||
// Load mirrors from official sources
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second*5)
|
||||
defer cancel()
|
||||
|
||||
err = mirrorList.LoadMirrors(ctx, distroInfo.Family)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load mirrors: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("⚡ Testing %d mirrors (timeout: %ds each)...\n",
|
||||
len(mirrorList.Mirrors), timeout)
|
||||
|
||||
err = mirrorList.TestMirrors(ctx, time.Duration(timeout)*time.Second)
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("mirror testing failed: %w", err)
|
||||
}
|
||||
|
||||
// Display results
|
||||
fmt.Println("\n" + color.GreenString("🎉 Testing complete! Here are your results:"))
|
||||
fmt.Println()
|
||||
|
||||
topMirrors := mirrorList.GetTop(topCount)
|
||||
allMirrors := mirrorList.GetAll()
|
||||
|
||||
// Show summary of all tests
|
||||
successCount := 0
|
||||
for _, mirror := range allMirrors {
|
||||
if mirror.Error == nil {
|
||||
successCount++
|
||||
}
|
||||
}
|
||||
|
||||
if verbose {
|
||||
fmt.Printf("📊 Test Summary: %d/%d mirrors responded successfully\n", successCount, len(allMirrors))
|
||||
fmt.Println()
|
||||
}
|
||||
if len(topMirrors) == 0 {
|
||||
fmt.Println(color.RedString("😱 No working mirrors found. Your internet might be fucked, or all mirrors are down."))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Display top mirrors in a nice table format
|
||||
fmt.Printf("%-4s %-50s %-12s %-10s %-8s\n",
|
||||
color.CyanString("Rank"),
|
||||
color.CyanString("Mirror URL"),
|
||||
color.CyanString("Latency"),
|
||||
color.CyanString("Speed"),
|
||||
color.CyanString("Score"))
|
||||
fmt.Println(color.HiBlackString("─────────────────────────────────────────────────────────────────────────────────"))
|
||||
|
||||
for i, m := range topMirrors {
|
||||
rank := color.YellowString("#%d", i+1)
|
||||
url := m.URL
|
||||
if len(url) > 48 {
|
||||
url = url[:45] + "..."
|
||||
}
|
||||
|
||||
latencyStr := color.GreenString("%dms", m.Latency.Milliseconds())
|
||||
if m.Latency > 500*time.Millisecond {
|
||||
latencyStr = color.RedString("%dms", m.Latency.Milliseconds())
|
||||
} else if m.Latency > 200*time.Millisecond {
|
||||
latencyStr = color.YellowString("%dms", m.Latency.Milliseconds())
|
||||
}
|
||||
|
||||
speedStr := color.GreenString("%.1f MB/s", m.Speed)
|
||||
if m.Speed < 1.0 {
|
||||
speedStr = color.RedString("%.1f MB/s", m.Speed)
|
||||
} else if m.Speed < 5.0 {
|
||||
speedStr = color.YellowString("%.1f MB/s", m.Speed)
|
||||
}
|
||||
|
||||
scoreStr := color.CyanString("%.1f", m.Score)
|
||||
|
||||
fmt.Printf("%-4s %-50s %-12s %-10s %-8s\n",
|
||||
rank, url, latencyStr, speedStr, scoreStr)
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
best := mirrorList.GetBest()
|
||||
if best != nil {
|
||||
fmt.Printf("🏆 Winner: %s\n", color.GreenString(best.URL))
|
||||
fmt.Printf("⚡ This bad boy clocks in at %.1f MB/s with %dms latency\n",
|
||||
best.Speed, best.Latency.Milliseconds())
|
||||
}
|
||||
|
||||
// Show failed mirrors in verbose mode
|
||||
if verbose {
|
||||
failedMirrors := make([]mirror.Mirror, 0)
|
||||
for _, m := range allMirrors {
|
||||
if m.Error != nil {
|
||||
failedMirrors = append(failedMirrors, m)
|
||||
}
|
||||
}
|
||||
|
||||
if len(failedMirrors) > 0 {
|
||||
fmt.Println()
|
||||
fmt.Printf("❌ Failed mirrors (%d):\n", len(failedMirrors))
|
||||
for _, m := range failedMirrors {
|
||||
fmt.Printf(" %s - %s\n", color.RedString(m.URL), m.Error.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
fmt.Printf("💡 To apply the fastest mirror, run: %s\n",
|
||||
color.YellowString("fastestmirror apply"))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(findCmd)
|
||||
|
||||
findCmd.Flags().BoolVarP(&verbose, "verbose", "v", false, "Show verbose output")
|
||||
findCmd.Flags().IntVarP(&timeout, "timeout", "t", 10, "Timeout for each mirror test in seconds")
|
||||
findCmd.Flags().IntVarP(&topCount, "top", "n", 5, "Number of top mirrors to display")
|
||||
}
|
31
cmd/root.go
Normal file
31
cmd/root.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "fastestmirror",
|
||||
Short: "Find and set the fastest package mirror for your Linux distribution",
|
||||
Long: `FastestMirror - Because waiting for slow mirrors is like watching paint dry in a hurricane.
|
||||
|
||||
This tool automatically detects your Linux distribution and finds the fastest
|
||||
package repository mirrors available. No more suffering through downloads that
|
||||
move slower than continental drift.
|
||||
|
||||
Features:
|
||||
• Automatic distribution detection
|
||||
• Concurrent mirror speed testing
|
||||
• Colorful output that doesn't hurt your eyes
|
||||
• Backup and restore functionality
|
||||
• Works with major distros (Debian, Ubuntu, Arch, Fedora, etc.)`,
|
||||
}
|
||||
|
||||
// Execute runs the root command
|
||||
func Execute() error {
|
||||
return rootCmd.Execute()
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Global flags and configuration initialization
|
||||
}
|
34
cmd/version.go
Normal file
34
cmd/version.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
version = "dev"
|
||||
commit = "unknown"
|
||||
buildTime = "unknown"
|
||||
)
|
||||
|
||||
var versionCmd = &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Show version information",
|
||||
Long: `Display version, build time, and other build information for FastestMirror.`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Printf("FastestMirror %s\n", version)
|
||||
fmt.Printf("Git Commit: %s\n", commit)
|
||||
fmt.Printf("Build Time: %s\n", buildTime)
|
||||
fmt.Printf("Go Version: %s\n", runtime.Version())
|
||||
fmt.Printf("OS/Arch: %s/%s\n", runtime.GOOS, runtime.GOARCH)
|
||||
fmt.Println()
|
||||
fmt.Println("Repository: https://git.gostacks.org/iwasforcedtobehere/fastestmirror")
|
||||
fmt.Println("Author: @iwasforcedtobehere")
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(versionCmd)
|
||||
}
|
Reference in New Issue
Block a user