1 /++ dub.sdl: 2 dependency "requests" version="*" 3 +/ 4 5 module update_downloader; 6 7 import requests; 8 9 import fs = std.file; 10 import std.algorithm; 11 import std.array; 12 import std.digest; 13 import std.digest.sha; 14 import std.exception; 15 import std.json; 16 import std.process; 17 import std.stdio; 18 import std.string; 19 import std.traits; 20 21 enum ArchSuffix 22 { 23 linux_x86, 24 osx_x86, 25 osx_aarch64, 26 windows_x86, 27 } 28 29 void main() 30 { 31 auto rq = Request(); 32 rq.addHeaders(["User-Agent": "[update_downloader.d] https://github.com/rorm-orm/dorm"]); 33 34 auto output = File("download-map.txt", "w"); 35 output.write("# rorm-cli - "); 36 putDownloads(rq, "https://api.github.com/repos/rorm-orm/rorm-cli/releases", output); 37 output.writeln(); 38 output.write("# rorm-lib - "); 39 putDownloads(rq, "https://api.github.com/repos/rorm-orm/rorm-lib/releases", output); 40 } 41 42 void putDownloads(ref Request rq, string url, ref File output) 43 { 44 auto rormReleases = rq.get(url).responseBody.toString.parseJSON; 45 46 ArchSuffix[] allSuffixes = [EnumMembers!ArchSuffix]; 47 48 ArchSuffix[] missingAssetSuffixes = allSuffixes.dup; 49 50 string[ArchSuffix] downloadURLs; 51 52 auto release = rormReleases.array[0]; 53 54 output.writeln(release["tag_name"].str); 55 foreach (asset; release["assets"].array) 56 { 57 string assetName = asset["name"].str; 58 string downloadUrl = asset["browser_download_url"].str; 59 foreach_reverse (i, missingSuffix; missingAssetSuffixes) 60 { 61 if (matchesAsset(assetName, missingSuffix)) 62 { 63 downloadURLs[missingSuffix] = downloadUrl; 64 missingAssetSuffixes = missingAssetSuffixes.remove(i); 65 } 66 } 67 } 68 69 foreach (suffix; allSuffixes) 70 { 71 if (auto downloadUrl = suffix in downloadURLs) 72 { 73 string sigFilePath = "/tmp/verify-test.bin.sig"; 74 download(rq, *downloadUrl ~ ".sig", sigFilePath); 75 76 string downloadFilePath = "/tmp/verify-test.bin"; 77 auto downloadFile = File(downloadFilePath, "wb"); 78 SHA512 sha512; 79 sha512.start(); 80 auto data = rq.get(*downloadUrl).responseBody.data; 81 downloadFile.rawWrite(data); 82 sha512.put(data); 83 downloadFile.close(); 84 85 checkSignature(downloadFilePath, sigFilePath); 86 87 output.writeln(suffix, "\t", *downloadUrl, "\t", sha512.finish.toHexString); 88 } 89 else 90 { 91 stderr.writeln("Warning: no pre-built release for ", suffix, " - user will be prompted to compile manually!"); 92 } 93 } 94 } 95 96 bool matchesAsset(string asset, ArchSuffix suffix) 97 { 98 final switch (suffix) 99 { 100 case ArchSuffix.linux_x86: 101 return !!asset.endsWith("linux-x86_64.tar.gz"); 102 case ArchSuffix.osx_x86: 103 return !!asset.endsWith("apple-x86_64.tar.gz"); 104 case ArchSuffix.osx_aarch64: 105 return !!asset.endsWith("apple-aarch64.tar.gz"); 106 case ArchSuffix.windows_x86: 107 return !!asset.endsWith("windows-x86_64.zip"); 108 } 109 } 110 111 void download(ref Request rq, string url, string output) 112 { 113 auto rs = rq.get(url); 114 fs.write(output, rs.responseBody.data); 115 } 116 117 void checkSignature(string file, string sigFile) 118 { 119 enforce(spawnProcess([ 120 "gpg", "--verify", sigFile, file 121 ]).wait == 0, "Failed to verify " ~ file ~ " with signature " ~ sigFile); 122 }