github.com/rajveermalviya/gamen@v0.1.2-0.20220930195403-9be15877c1aa/internal/common/cmd/download-rpms/download-rpms.go (about)

     1  package main
     2  
     3  import (
     4  	"compress/gzip"
     5  	"encoding/xml"
     6  	"fmt"
     7  	"io"
     8  	"net/http"
     9  	"os"
    10  	"sort"
    11  	"strconv"
    12  	"strings"
    13  	"sync"
    14  
    15  	"golang.org/x/exp/slices"
    16  )
    17  
    18  func main() {
    19  	if len(os.Args) < 2 {
    20  		panic("specify package names")
    21  	}
    22  
    23  	// serverUrl := getFedoraMirrorServer()
    24  	// fmt.Println(serverUrl)
    25  	serverUrl := "https://download-cc-rdu01.fedoraproject.org/pub/fedora/linux/releases/36/Everything/x86_64/os/repodata/repomd.xml"
    26  	primaryXmlUrl := getPrimaryXmlUrl(serverUrl)
    27  	metadata := getPackageXmlMetadata(primaryXmlUrl)
    28  	packageUrls := getPackageUrls(
    29  		serverUrl, metadata,
    30  		os.Args[1:]...,
    31  	)
    32  
    33  	var wg sync.WaitGroup
    34  
    35  	for pkg, pkgUrl := range packageUrls {
    36  		pkg, pkgUrl := pkg, pkgUrl
    37  
    38  		wg.Add(1)
    39  		go func() {
    40  			defer wg.Done()
    41  
    42  			fmt.Println("Downloading: " + pkgUrl)
    43  
    44  			res := mustV(http.Get(pkgUrl))
    45  			defer res.Body.Close()
    46  
    47  			f := mustV(os.Create(pkg + ".rpm"))
    48  			defer f.Close()
    49  
    50  			_ = mustV(io.Copy(f, res.Body))
    51  
    52  			fmt.Println("Done downloading: " + pkgUrl)
    53  		}()
    54  	}
    55  
    56  	wg.Wait()
    57  }
    58  
    59  func getFedoraMirrorServer() string {
    60  	const FedoraReleaseEver = "36"
    61  	const FedoraRepoUrl = "https://mirrors.fedoraproject.org/metalink?repo=fedora-" + FedoraReleaseEver + "&arch=x86_64"
    62  
    63  	type rUrl struct {
    64  		Text       string `xml:",chardata"`
    65  		Protocol   string `xml:"protocol,attr"`
    66  		Preference string `xml:"preference,attr"`
    67  	}
    68  
    69  	var r struct {
    70  		XMLName xml.Name `xml:"metalink"`
    71  		Files   struct {
    72  			File []struct {
    73  				Name      string `xml:"name,attr"`
    74  				Resources struct {
    75  					URL []rUrl `xml:"url"`
    76  				} `xml:"resources"`
    77  			} `xml:"file"`
    78  		} `xml:"files"`
    79  	}
    80  
    81  	fmt.Println("Downloading: " + FedoraRepoUrl)
    82  	res := mustV(http.Get(FedoraRepoUrl))
    83  	defer res.Body.Close()
    84  	fmt.Println("Done downloading: " + FedoraRepoUrl)
    85  
    86  	must(xml.NewDecoder(res.Body).Decode(&r))
    87  
    88  	urls := []rUrl{}
    89  	for _, file := range r.Files.File {
    90  		if file.Name == "repomd.xml" {
    91  			for _, url := range file.Resources.URL {
    92  				if url.Protocol == "https" {
    93  					urls = append(urls, url)
    94  				}
    95  			}
    96  			break
    97  		}
    98  	}
    99  	sort.Slice(urls, func(i, j int) bool {
   100  		return mustV(strconv.ParseFloat(urls[i].Preference, 64)) >
   101  			mustV(strconv.ParseFloat(urls[j].Preference, 64))
   102  	})
   103  
   104  	if len(urls) == 0 {
   105  		panic("getFedoraMirrorServer failed")
   106  	}
   107  
   108  	return strings.TrimSpace(urls[0].Text)
   109  }
   110  
   111  func getPrimaryXmlUrl(serverUrl string) string {
   112  	fmt.Println("Downloading: " + serverUrl)
   113  	res := mustV(http.Get(serverUrl))
   114  	defer res.Body.Close()
   115  	fmt.Println("Done downloading: " + serverUrl)
   116  
   117  	var r struct {
   118  		XMLName xml.Name `xml:"repomd"`
   119  		Data    []struct {
   120  			Type     string `xml:"type,attr"`
   121  			Location struct {
   122  				Text string `xml:",chardata"`
   123  				Href string `xml:"href,attr"`
   124  			} `xml:"location"`
   125  		} `xml:"data"`
   126  	}
   127  
   128  	must(xml.NewDecoder(res.Body).Decode(&r))
   129  
   130  	var primaryXmlUrl string
   131  	for _, data := range r.Data {
   132  		if data.Type == "primary" {
   133  			primaryXmlUrl = strings.TrimSpace(data.Location.Href)
   134  			break
   135  		}
   136  	}
   137  	if primaryXmlUrl == "" {
   138  		panic("getPrimaryXmlUrl failed")
   139  	}
   140  
   141  	return strings.TrimSuffix(serverUrl, "repodata/repomd.xml") + primaryXmlUrl
   142  }
   143  
   144  func getPackageUrls(serverUrl string, metadata PackageXmlMetadata, packages ...string) map[string]string {
   145  	serverUrlPrefix := strings.TrimSuffix(serverUrl, "repodata/repomd.xml")
   146  
   147  	packageUrls := map[string]string{}
   148  	for _, pkg := range metadata.Package {
   149  		if pkg.Type == "rpm" && (pkg.Arch == "x86_64" || pkg.Arch == "noarch") && slices.Contains(packages, pkg.Name) {
   150  			pkgUrl := serverUrlPrefix + pkg.Location.Href
   151  			packageUrls[pkg.Name] = pkgUrl
   152  		}
   153  	}
   154  
   155  	if len(packageUrls) != len(packages) {
   156  		notFound := []string{}
   157  		for _, pkg := range packages {
   158  			if _, ok := packageUrls[pkg]; !ok {
   159  				notFound = append(notFound, pkg)
   160  			}
   161  		}
   162  
   163  		panic(fmt.Sprintf("some packages not found: %v", notFound))
   164  	}
   165  
   166  	return packageUrls
   167  }
   168  
   169  type PackageXmlMetadata struct {
   170  	XMLName  xml.Name `xml:"metadata"`
   171  	Packages string   `xml:"packages,attr"`
   172  	Package  []struct {
   173  		Type     string `xml:"type,attr"`
   174  		Name     string `xml:"name"`
   175  		Arch     string `xml:"arch"`
   176  		Location struct {
   177  			Href string `xml:"href,attr"`
   178  		} `xml:"location"`
   179  	} `xml:"package"`
   180  }
   181  
   182  func getPackageXmlMetadata(primaryXmlUrl string) PackageXmlMetadata {
   183  	fmt.Println("Downloading: " + primaryXmlUrl)
   184  	res := mustV(http.Get(primaryXmlUrl))
   185  	defer res.Body.Close()
   186  	fmt.Println("Done downloading: " + primaryXmlUrl)
   187  
   188  	var metadata PackageXmlMetadata
   189  
   190  	if strings.HasSuffix(primaryXmlUrl, ".gz") {
   191  		gzipReader := mustV(gzip.NewReader(res.Body))
   192  		defer gzipReader.Close()
   193  
   194  		must(
   195  			xml.NewDecoder(gzipReader).
   196  				Decode(&metadata),
   197  		)
   198  	} else {
   199  		must(
   200  			xml.NewDecoder(res.Body).
   201  				Decode(&metadata),
   202  		)
   203  	}
   204  
   205  	return metadata
   206  }
   207  
   208  func must(err error) {
   209  	if err != nil {
   210  		panic(err)
   211  	}
   212  }
   213  
   214  func mustV[T any](r T, err error) T {
   215  	if err != nil {
   216  		panic(err)
   217  	}
   218  	return r
   219  }