ppp-sync/src/main.go
2023-09-05 12:52:11 +01:00

305 lines
6.7 KiB
Go

package main
import (
"bufio"
"compress/gzip"
"fmt"
"io"
"log"
"net/http"
"os"
"os/exec"
"strings"
"sync"
"github.com/ulikunitz/xz"
)
func main() {
if os.Args[1] == "sign" {
signFiles(os.Args[2])
return
}
config := config{
Source: os.Args[1],
Target: os.Args[2],
Download: false,
}
if len(os.Args) > 3 {
config.Download = true
config.DlUrl = os.Args[3]
config.Output = os.Args[4]
}
basePackages := processFile(config.Source)
targetPackages := processFile(config.Target)
changed := compare(basePackages, targetPackages, config.Download)
if config.Download {
download(changed, config.DlUrl, config.Output)
}
}
func processFile(url string) map[string]packageInfo {
resp, err := http.Get(url)
if err != nil {
panic(err)
}
defer resp.Body.Close()
rdr := io.Reader(resp.Body)
if strings.HasSuffix(url, ".xz") {
r, err := xz.NewReader(resp.Body)
if err != nil {
log.Fatalf("xz error %s", err)
}
rdr = r
}
if strings.HasSuffix(url, ".gz") {
r, err := gzip.NewReader(resp.Body)
if err != nil {
log.Fatalf("gzip error %s", err)
}
rdr = r
}
packages := make(map[string]packageInfo)
var currentPackage string
scanner := bufio.NewScanner(rdr)
for scanner.Scan() {
line := scanner.Text()
if line == "" {
currentPackage = ""
}
if currentPackage == "" {
if strings.HasPrefix(line, "Package: ") {
pkName := strings.TrimPrefix(line, "Package: ") + " "
_, broken := brokenPackages[pkName]
if !broken {
currentPackage = pkName
packages[currentPackage] = packageInfo{
Name: pkName,
}
} else {
currentPackage = ""
}
}
} else {
if strings.HasPrefix(line, "Version: ") {
packages[currentPackage] = packageInfo{
Name: currentPackage,
Version: strings.TrimPrefix(line, "Version: "),
}
}
if strings.HasPrefix(line, "Filename: ") {
packages[currentPackage] = packageInfo{
Name: currentPackage,
Version: packages[currentPackage].Version,
FilePath: strings.TrimPrefix(line, "Filename: "),
}
}
}
}
return packages
}
func compare(basePackages map[string]packageInfo, targetPackages map[string]packageInfo, download bool) map[string]packageInfo {
output := make(map[string]packageInfo)
for pack, info := range targetPackages {
if baseVersion, ok := basePackages[pack]; ok {
if baseVersion.Version != info.Version {
output[pack] = info
if !download {
os.Stdout.WriteString(pack)
}
}
} else {
output[pack] = info
if !download {
os.Stdout.WriteString(pack)
}
}
}
return output
}
func signFiles(path string) {
dir, err := os.Open(path)
if err != nil {
panic(err)
}
defer dir.Close()
files, err := dir.Readdirnames(-1)
if err != nil {
panic(err)
}
signQueue := make(chan string, 10)
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for {
select {
case path, ok := <-signQueue:
if !ok {
return
}
ch := make(chan bool)
go func() {
sign(ch, path)
}()
<-ch
default:
// No more files to sign, exit the goroutine
return
}
}
}()
}
count := 0
totalCount := len(files)
filePath := ""
for _, file := range files {
totalCount--
if count < 10 && totalCount > 0 {
count++
filePath = filePath + " " + path + file
} else {
count = 0
filePath = filePath + " " + path + file
signQueue <- filePath
}
}
close(signQueue)
wg.Wait()
}
func sign(ch chan bool, path string) {
if strings.HasSuffix(path, ".deb") {
fmt.Printf("Signing %s \n", path)
cmd := exec.Command("dpkg-sig", "--sign", "builder", path)
err := cmd.Run()
if err != nil {
panic(err)
}
}
ch <- true
}
func download(packages map[string]packageInfo, url string, output string) {
// Create a buffered channel to store the packages to be downloaded
packageQueue := make(chan packageInfo, 10)
// Create a worker pool with 10 workers
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for {
select {
case pack, ok := <-packageQueue:
if !ok {
return
}
fmt.Printf("Downloading %s \n", pack.Name)
resp, err := http.Get(url + pack.FilePath)
if err != nil {
fmt.Printf("Failed to download %s: %v \n", pack.Name, err)
continue
}
defer resp.Body.Close()
rdr := io.Reader(resp.Body)
path := output + strings.Split(pack.FilePath, "/")[len(strings.Split(pack.FilePath, "/"))-1]
file, err := os.Create(path)
if err != nil {
fmt.Printf("Failed to create file %s: %v \n", path, err)
continue
}
defer file.Close()
_, err = io.Copy(file, rdr)
if err != nil {
fmt.Printf("Failed to save file %s: %v \n", path, err)
continue
}
default:
// No more packages to download, exit the goroutine
return
}
}
}()
}
// Add the packages to the queue
for _, pack := range packages {
packageQueue <- pack
}
// Close the queue to signal the workers to stop
close(packageQueue)
// Wait for all the workers to finish
wg.Wait()
}
type config struct {
Source string
Target string
Download bool
DlUrl string
Output string
}
type packageInfo struct {
Name string
Version string
FilePath string
}
var brokenPackages = map[string]bool{
"libkpim5mbox-data ": true,
"libkpim5identitymanagement-data ": true,
"libkpim5libkdepim-data ": true,
"libkpim5imap-data ": true,
"libkpim5ldap-data ": true,
"libkpim5mailimporter-data ": true,
"libkpim5mailtransport-data ": true,
"libkpim5akonadimime-data ": true,
"libkpim5kontactinterface-data ": true,
"libkpim5ksieve-data ": true,
"libkpim5textedit-data ": true,
"libk3b-data ": true,
"libkpim5eventviews-data ": true,
"libkpim5incidenceeditor-data ": true,
"libkpim5calendarsupport-data ": true,
"libkpim5calendarutils-data ": true,
"libkpim5grantleetheme-data ": true,
"libkpim5pkpass-data ": true,
"libkpim5gapi-data ": true,
"libkpim5akonadisearch-data ": true,
"libkpim5gravatar-data ": true,
"libkpim5akonadicontact-data ": true,
"libkpim5akonadinotes-data ": true,
"libkpim5libkleo-data ": true,
"plasma-mobile-tweaks ": true,
"libkpim5mime-data ": true,
"libkf5textaddons-data ": true,
"libkpim5smtp-data ": true,
"libkpim5tnef-data ": true,
"libkpim5akonadicalendar-data ": true,
"libkpim5akonadi-data ": true,
"libnvidia-common-390 ": true,
"libnvidia-common-530 ": true,
}