This commit is contained in:
Jeffrey Paul 2024-05-22 13:31:22 -07:00
parent 0dd9505fb3
commit b168c1f7d2
6 changed files with 46 additions and 126 deletions

14
main.go
View File

@ -33,9 +33,11 @@ func CLIEntry() {
log.Fatal(err) log.Fatal(err)
} }
// Create sources.list.d directory if it doesn't exist oldMirrorURL, err := findMirrorURLInFile(sourcesFilePath)
if err := createSourcesListD(); err != nil {
log.Fatal(err) if err != nil {
// redundant
log.Fatalf("Failed to find mirror URL in file %s: %v", sourcesFilePath, err)
} }
// Fetch and find the fastest mirror // Fetch and find the fastest mirror
@ -48,8 +50,12 @@ func CLIEntry() {
// Display latency statistics // Display latency statistics
displayLatencyStatistics(latencies, fastestMirrorURL, fastestTime) displayLatencyStatistics(latencies, fastestMirrorURL, fastestTime)
if err != nil {
log.Fatalf("Failed to find mirror URL in file %s: %v", sourcesFilePath, err)
}
// Update sources file with the fastest mirror // Update sources file with the fastest mirror
if err := updateSourcesFile(sourcesFilePath, fastestMirrorURL, suites); err != nil { if err := updateSourcesFile(sourcesFilePath, oldMirrorURL, fastestMirrorURL); err != nil {
log.Fatal(err) log.Fatal(err)
} }
log.Println("Fastmirror CLI finished successfully") log.Println("Fastmirror CLI finished successfully")

View File

@ -8,7 +8,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/schollz/progressbar/v3" progressbar "github.com/schollz/progressbar/v3"
) )
func findFastestMirror(suites []string) (string, time.Duration, []time.Duration, error) { func findFastestMirror(suites []string) (string, time.Duration, []time.Duration, error) {
@ -33,7 +33,6 @@ func findFastestMirror(suites []string) (string, time.Duration, []time.Duration,
downCount := 0 downCount := 0
noIndexCount := 0 noIndexCount := 0
codename, err := getUbuntuCodename()
if err != nil { if err != nil {
return "", 0, nil, err return "", 0, nil, err
} }
@ -52,7 +51,7 @@ func findFastestMirror(suites []string) (string, time.Duration, []time.Duration,
if strings.HasPrefix(mirror, "https://") { if strings.HasPrefix(mirror, "https://") {
mirror = strings.TrimSuffix(mirror, "/") mirror = strings.TrimSuffix(mirror, "/")
startTime := time.Now() startTime := time.Now()
isValid := isValidMirror(httpClient, mirror, codename, suites) isValid := isValidMirror(httpClient, mirror, suites)
elapsedTime := time.Since(startTime) elapsedTime := time.Since(startTime)
if isValid { if isValid {
@ -80,7 +79,7 @@ func findFastestMirror(suites []string) (string, time.Duration, []time.Duration,
return fastestMirrorURL, fastestTime, latencies, nil return fastestMirrorURL, fastestTime, latencies, nil
} }
func isValidMirror(httpClient http.Client, mirrorURL, codename string, suites []string) bool { func isValidMirror(httpClient http.Client, mirrorURL string, suites []string) bool {
for _, suite := range suites { for _, suite := range suites {
uri := fmt.Sprintf("%s/dists/%s/Release", mirrorURL, suite) uri := fmt.Sprintf("%s/dists/%s/Release", mirrorURL, suite)
resp, err := httpClient.Get(uri) resp, err := httpClient.Get(uri)

View File

@ -4,19 +4,13 @@ import (
"bufio" "bufio"
_ "embed" _ "embed"
"fmt" "fmt"
"html/template"
"log" "log"
"os" "os"
"path/filepath" "path/filepath"
"regexp"
"strings" "strings"
) )
//go:embed sources_list_legacy.tmpl
var legacyTemplate string
//go:embed sources_list_modern.tmpl
var modernTemplate string
func findSourcesFilePath() (string, error) { func findSourcesFilePath() (string, error) {
const sourcesListPath = "/etc/apt/sources.list" const sourcesListPath = "/etc/apt/sources.list"
const sourcesListDPath = "/etc/apt/sources.list.d/" const sourcesListDPath = "/etc/apt/sources.list.d/"
@ -24,6 +18,7 @@ func findSourcesFilePath() (string, error) {
log.Printf("Found Ubuntu sources file: %s", sourcesListPath) log.Printf("Found Ubuntu sources file: %s", sourcesListPath)
return sourcesListPath, nil return sourcesListPath, nil
} }
files, err := os.ReadDir(sourcesListDPath) files, err := os.ReadDir(sourcesListDPath)
if err != nil { if err != nil {
return "", fmt.Errorf("failed to read directory %s: %v", sourcesListDPath, err) return "", fmt.Errorf("failed to read directory %s: %v", sourcesListDPath, err)
@ -43,14 +38,28 @@ func findSourcesFilePath() (string, error) {
return "", fmt.Errorf("no Ubuntu sources file found") return "", fmt.Errorf("no Ubuntu sources file found")
} }
func isUbuntuSourcesFile(filePath string) bool { var ubuntuMirrorURLRegex = regexp.MustCompile(`http://((?P<countrycode>[a-z]{2})\.)?(archive|ports)\.ubuntu\.com/(?P<path>.*)`)
content, err := os.ReadFile(filePath)
func findMirrorURLInFile(filePath string) (string, error) {
fileContent, err := os.ReadFile(filePath)
if err != nil { if err != nil {
log.Printf("Failed to read file %s: %v", filePath, err) return "", fmt.Errorf("failed to read file %s: %v", filePath, err)
}
match := ubuntuMirrorURLRegex.FindString(string(fileContent))
if match != "" {
return match, nil
}
return "", fmt.Errorf("no URL found in file %s", filePath)
}
func isUbuntuSourcesFile(filePath string) bool {
match, err := findMirrorURLInFile(filePath)
if err != nil {
log.Printf("Failed to find OEM mirror URL in file %s: %v", filePath, err)
return false return false
} }
log.Printf("Found OEM mirror URL in file %s: %s", filePath, match)
return strings.Contains(strings.ToLower(string(content)), "ubuntu.com") return true
} }
func createBackup(filePath string) error { func createBackup(filePath string) error {
@ -65,17 +74,6 @@ func createBackup(filePath string) error {
return nil return nil
} }
func createSourcesListD() error {
const sourcesListDPath = "/etc/apt/sources.list.d/"
if _, err := os.Stat(sourcesListDPath); os.IsNotExist(err) {
if err := os.Mkdir(sourcesListDPath, 0755); err != nil {
return fmt.Errorf("failed to create directory %s: %v", sourcesListDPath, err)
}
log.Printf("Created directory: %s", sourcesListDPath)
}
return nil
}
func extractSuites(filePath string) ([]string, error) { func extractSuites(filePath string) ([]string, error) {
content, err := os.ReadFile(filePath) content, err := os.ReadFile(filePath)
if err != nil { if err != nil {
@ -121,56 +119,24 @@ func extractSuites(filePath string) ([]string, error) {
return suites, nil return suites, nil
} }
func updateSourcesFile(filePath, mirrorURL string, suites []string) error { func updateSourcesFile(filePath, oldMirrorURL, newMirrorURL string) error {
codename, err := getUbuntuCodename() file, err := os.Open(filePath)
if err != nil { if err != nil {
return err return fmt.Errorf("failed to open sources file %s: %v", filePath, err)
} }
majorVersion, minorVersion, err := getUbuntuVersion() file.Close()
contentBytes, err := os.ReadFile(filePath)
if err != nil { if err != nil {
return err return fmt.Errorf("failed to read sources file %s: %v", filePath, err)
} }
suitesString := strings.Join(suites, " ") log.Printf("update: replacing %s with %s in %s", oldMirrorURL, newMirrorURL, filePath)
// replace old mirror URL with new mirror URL
content := string(contentBytes)
content = strings.ReplaceAll(content, oldMirrorURL, newMirrorURL)
var content string if err := os.WriteFile(filePath, []byte(content), 0644); err != nil {
data := struct { return fmt.Errorf("failed to write sources file %s: %v", filePath, err)
MirrorURL string
Codename string
Suites string
}{
MirrorURL: mirrorURL,
Codename: codename,
Suites: suitesString,
}
if majorVersion > 24 || (majorVersion == 24 && minorVersion >= 4) {
tmpl, err := template.New("modern").Parse(modernTemplate)
if err != nil {
return fmt.Errorf("failed to parse modern template: %v", err)
}
var buf strings.Builder
err = tmpl.Execute(&buf, data)
if err != nil {
return fmt.Errorf("failed to execute modern template: %v", err)
}
content = buf.String()
} else {
tmpl, err := template.New("legacy").Parse(legacyTemplate)
if err != nil {
return fmt.Errorf("failed to parse legacy template: %v", err)
}
var buf strings.Builder
err = tmpl.Execute(&buf, data)
if err != nil {
return fmt.Errorf("failed to execute legacy template: %v", err)
}
content = buf.String()
}
err = os.WriteFile(filePath, []byte(content), 0644)
if err != nil {
return fmt.Errorf("failed to update sources file %s: %v", filePath, err)
} }
log.Printf("Updated sources file: %s", filePath) log.Printf("Updated sources file: %s", filePath)
return nil return nil

View File

@ -1,5 +0,0 @@
deb {{.MirrorURL}} {{.Codename}} {{.Suites}}
deb {{.MirrorURL}} {{.Codename}}-updates {{.Suites}}
deb {{.MirrorURL}} {{.Codename}}-backports {{.Suites}}
deb {{.MirrorURL}} {{.Codename}}-security {{.Suites}}

View File

@ -1,14 +0,0 @@
Types: deb
URIs: {{.MirrorURL}}
Suites: {{.Codename}} {{.Codename}}-updates {{.Codename}}-backports
Components: {{.Suites}}
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg
## Ubuntu security updates. Aside from URIs and Suites,
## this should mirror your choices in the previous section.
Types: deb
URIs: {{.MirrorURL}}
Suites: {{.Codename}}-security
Components: {{.Suites}}
Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg

View File

@ -3,41 +3,9 @@ package fastmirror
import ( import (
"fmt" "fmt"
"os/exec" "os/exec"
"strconv"
"strings" "strings"
) )
func getUbuntuCodename() (string, error) {
cmd := exec.Command("lsb_release", "-cs")
output, err := cmd.Output()
if err != nil {
return "", fmt.Errorf("failed to run lsb_release: %v", err)
}
return strings.TrimSpace(string(output)), nil
}
func getUbuntuVersion() (int, int, error) {
cmd := exec.Command("lsb_release", "-rs")
output, err := cmd.Output()
if err != nil {
return 0, 0, fmt.Errorf("failed to run lsb_release: %v", err)
}
version := strings.TrimSpace(string(output))
parts := strings.Split(version, ".")
if len(parts) != 2 {
return 0, 0, fmt.Errorf("unexpected version format: %s", version)
}
majorVersion, err := strconv.Atoi(parts[0])
if err != nil {
return 0, 0, fmt.Errorf("failed to parse major version: %v", err)
}
minorVersion, err := strconv.Atoi(parts[1])
if err != nil {
return 0, 0, fmt.Errorf("failed to parse minor version: %v", err)
}
return majorVersion, minorVersion, nil
}
func isUbuntu() error { func isUbuntu() error {
cmd := exec.Command("lsb_release", "-is") cmd := exec.Command("lsb_release", "-is")
output, err := cmd.Output() output, err := cmd.Output()