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 }