Clone of https://github.com/NixOS/nixpkgs.git (to stress-test knotserver)
at devShellTools-shell 262 lines 9.6 kB view raw
1#! /usr/bin/env nix-shell 2/* 3#! nix-shell -i zx -p zx 4*/ 5 6cd(__dirname) 7const nixpkgs = (await $`git rev-parse --show-toplevel`).stdout.trim() 8const $nixpkgs = $({ 9 cwd: nixpkgs 10}) 11 12const dummy_hash = 'sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=' 13 14const lockfile_file = './info.json' 15const lockfile_initial = fs.readJsonSync(lockfile_file) 16function flush_to_file() { 17 fs.writeJsonSync(lockfile_file, lockfile, { spaces: 2 }) 18} 19const flush_to_file_proxy = { 20 get(obj, prop) { 21 const value = obj[prop] 22 return typeof value == 'object' ? new Proxy(value, flush_to_file_proxy) : value 23 }, 24 25 set(obj, prop, value) { 26 obj[prop] = value 27 flush_to_file() 28 return true 29 }, 30} 31const lockfile = new Proxy(structuredClone(lockfile_initial), flush_to_file_proxy) 32const ungoogled_rev = argv['ungoogled-chromium-rev'] 33 34for (const attr_path of Object.keys(lockfile)) { 35 const ungoogled = attr_path === 'ungoogled-chromium' 36 37 if (!argv[attr_path] && !(ungoogled && ungoogled_rev)) { 38 console.log(`[${attr_path}] Skipping ${attr_path}. Pass --${attr_path} as argument to update.`) 39 continue 40 } 41 42 const version_nixpkgs = !ungoogled ? lockfile[attr_path].version : lockfile[attr_path].deps['ungoogled-patches'].rev 43 const version_upstream = !ungoogled ? await get_latest_chromium_release('linux') : 44 ungoogled_rev ?? await get_latest_ungoogled_release() 45 46 console.log(`[${attr_path}] ${chalk.red(version_nixpkgs)} (nixpkgs)`) 47 console.log(`[${attr_path}] ${chalk.green(version_upstream)} (upstream)`) 48 49 if (ungoogled_rev || version_greater_than(version_upstream, version_nixpkgs)) { 50 console.log(`[${attr_path}] ${chalk.green(version_upstream)} from upstream is newer than our ${chalk.red(version_nixpkgs)}...`) 51 52 let ungoogled_patches = ungoogled ? await fetch_ungoogled(version_upstream) : undefined 53 54 // For ungoogled-chromium we need to remove the patch revision (e.g. 130.0.6723.116-1 -> 130.0.6723.116) 55 // by just using the chromium_version.txt content from the patches checkout (to also work with commit revs). 56 const version_chromium = ungoogled_patches?.chromium_version ?? version_upstream 57 58 const chromium_rev = await chromium_resolve_tag_to_rev(version_chromium) 59 60 lockfile[attr_path] = { 61 version: version_chromium, 62 chromedriver: !ungoogled ? await fetch_chromedriver_binaries(await get_latest_chromium_release('mac')) : undefined, 63 deps: { 64 depot_tools: {}, 65 gn: {}, 66 'ungoogled-patches': !ungoogled ? undefined : { 67 rev: ungoogled_patches.rev, 68 hash: ungoogled_patches.hash, 69 }, 70 npmHash: dummy_hash, 71 }, 72 DEPS: {}, 73 } 74 75 const depot_tools = await fetch_depot_tools(chromium_rev, lockfile_initial[attr_path].deps.depot_tools) 76 lockfile[attr_path].deps.depot_tools = { 77 rev: depot_tools.rev, 78 hash: depot_tools.hash, 79 } 80 81 const gn = await fetch_gn(chromium_rev, lockfile_initial[attr_path].deps.gn) 82 lockfile[attr_path].deps.gn = { 83 rev: gn.rev, 84 hash: gn.hash, 85 } 86 87 // DEPS update loop 88 lockfile[attr_path].DEPS = await resolve_DEPS(depot_tools.out, chromium_rev) 89 for (const [path, value] of Object.entries(lockfile[attr_path].DEPS)) { 90 delete value.fetcher 91 delete value.postFetch 92 93 if (value.url === 'https://chromium.googlesource.com/chromium/src.git') { 94 value.recompress = true 95 } 96 97 const cache_hit = (() => { 98 for (const attr_path in lockfile_initial) { 99 const cache = lockfile_initial[attr_path].DEPS[path] 100 const hits_cache = 101 cache !== undefined && 102 value.url === cache.url && 103 value.rev === cache.rev && 104 value.recompress === cache.recompress && 105 cache.hash !== undefined && 106 cache.hash !== '' && 107 cache.hash !== dummy_hash 108 109 if (hits_cache) { 110 cache.attr_path = attr_path 111 return cache; 112 } 113 } 114 })(); 115 116 if (cache_hit) { 117 console.log(`[${chalk.green(path)}] Reusing hash from previous info.json for ${cache_hit.url}@${cache_hit.rev} from ${cache_hit.attr_path}`) 118 value.hash = cache_hit.hash 119 continue 120 } 121 122 console.log(`[${chalk.red(path)}] FOD prefetching ${value.url}@${value.rev}...`) 123 value.hash = await prefetch_FOD('-A', `${attr_path}.browser.passthru.chromiumDeps."${path}"`) 124 console.log(`[${chalk.green(path)}] FOD prefetching successful`) 125 } 126 127 lockfile[attr_path].deps.npmHash = await prefetch_FOD('-A', `${attr_path}.browser.passthru.npmDeps`) 128 129 console.log(chalk.green(`[${attr_path}] Done updating ${attr_path} from ${version_nixpkgs} to ${version_upstream}!`)) 130 } 131} 132 133 134async function fetch_gn(chromium_rev, gn_previous) { 135 const DEPS_file = await get_gitiles_file('https://chromium.googlesource.com/chromium/src', chromium_rev, 'DEPS') 136 const gn_rev = /^\s+'gn_version': 'git_revision:(?<rev>.+)',$/m.exec(DEPS_file).groups.rev 137 const hash = gn_rev === gn_previous.rev ? gn_previous.hash : '' 138 139 return await prefetch_gitiles('https://gn.googlesource.com/gn', gn_rev, hash) 140} 141 142 143async function fetch_chromedriver_binaries(version) { 144 // https://developer.chrome.com/docs/chromedriver/downloads/version-selection 145 const prefetch = async (url) => { 146 const expr = [`(import ./. {}).fetchzip { url = "${url}"; hash = ""; }`] 147 const derivation = await $nixpkgs`nix-instantiate --expr ${expr}` 148 return await prefetch_FOD(derivation) 149 } 150 151 // if the URL ever changes, the URLs in the chromedriver derivations need updating as well! 152 const url = (platform) => `https://storage.googleapis.com/chrome-for-testing-public/${version}/${platform}/chromedriver-${platform}.zip` 153 return { 154 version, 155 hash_darwin: await prefetch(url('mac-x64')), 156 hash_darwin_aarch64: await prefetch(url('mac-arm64')), 157 } 158} 159 160 161async function chromium_resolve_tag_to_rev(tag) { 162 const url = `https://chromium.googlesource.com/chromium/src/+/refs/tags/${tag}?format=json` 163 const response = await (await fetch(url)).text() 164 const json = JSON.parse(response.replace(`)]}'\n`, '')) 165 return json.commit 166} 167 168 169async function resolve_DEPS(depot_tools_checkout, chromium_rev) { 170 const { stdout } = await $`./depot_tools.py ${depot_tools_checkout} ${chromium_rev}` 171 const deps = JSON.parse(stdout) 172 return Object.fromEntries(Object.entries(deps).map(([k, { url, rev, hash }]) => [k, { url, rev, hash }])) 173} 174 175 176async function get_latest_chromium_release(platform) { 177 const url = `https://versionhistory.googleapis.com/v1/chrome/platforms/${platform}/channels/stable/versions/all/releases?` + new URLSearchParams({ 178 order_by: 'version desc', 179 filter: 'endtime=none,fraction>=0.5' 180 }) 181 182 const response = await (await fetch(url)).json() 183 return response.releases[0].version 184} 185 186 187async function get_latest_ungoogled_release() { 188 const ungoogled_tags = await (await fetch('https://api.github.com/repos/ungoogled-software/ungoogled-chromium/tags')).json() 189 const chromium_releases = await (await fetch('https://versionhistory.googleapis.com/v1/chrome/platforms/linux/channels/stable/versions/all/releases')).json() 190 const chromium_release_map = chromium_releases.releases.map((x) => x.version) 191 return ungoogled_tags.find((x) => chromium_release_map.includes(x.name.split('-')[0])).name 192} 193 194 195async function fetch_ungoogled(rev) { 196 const expr = (hash) => [`(import ./. {}).fetchFromGitHub { owner = "ungoogled-software"; repo = "ungoogled-chromium"; rev = "${rev}"; hash = "${hash}"; }`] 197 const hash = await prefetch_FOD('--expr', expr('')) 198 199 const checkout = await $nixpkgs`nix-build --expr ${expr(hash)}` 200 const checkout_path = checkout.stdout.trim() 201 202 await fs.copy(path.join(checkout_path, 'flags.gn'), './ungoogled-flags.toml') 203 204 const chromium_version = (await fs.readFile(path.join(checkout_path, 'chromium_version.txt'))).toString().trim() 205 206 console.log(`[ungoogled-chromium] ${chalk.green(rev)} patch revision resolves to chromium version ${chalk.green(chromium_version)}`) 207 208 return { 209 rev, 210 hash, 211 chromium_version, 212 } 213} 214 215 216function version_greater_than(greater, than) { 217 return greater.localeCompare(than, undefined, { numeric: true, sensitivity: 'base' }) === 1 218} 219 220 221async function get_gitiles_file(repo, rev, path) { 222 const base64 = await (await fetch(`${repo}/+/${rev}/${path}?format=TEXT`)).text() 223 return Buffer.from(base64, 'base64').toString('utf-8') 224} 225 226 227async function fetch_depot_tools(chromium_rev, depot_tools_previous) { 228 const depot_tools_rev = await get_gitiles_file('https://chromium.googlesource.com/chromium/src', chromium_rev, 'third_party/depot_tools') 229 const hash = depot_tools_rev === depot_tools_previous.rev ? depot_tools_previous.hash : '' 230 return await prefetch_gitiles('https://chromium.googlesource.com/chromium/tools/depot_tools', depot_tools_rev, hash) 231} 232 233 234async function prefetch_gitiles(url, rev, hash = '') { 235 const expr = () => [`(import ./. {}).fetchFromGitiles { url = "${url}"; rev = "${rev}"; hash = "${hash}"; }`] 236 237 if (hash === '') { 238 hash = await prefetch_FOD('--expr', expr()) 239 } 240 241 const { stdout } = await $nixpkgs`nix-build --expr ${expr()}` 242 243 return { 244 url, 245 rev, 246 hash, 247 out: stdout.trim(), 248 } 249} 250 251 252async function prefetch_FOD(...args) { 253 const { stderr } = await $nixpkgs`nix-build ${args}`.nothrow() 254 const hash = /\s+got:\s+(?<hash>.+)$/m.exec(stderr)?.groups?.hash 255 256 if (hash == undefined) { 257 throw new Error(chalk.red('Expected to find hash in nix-build stderr output:') + stderr) 258 } 259 260 return hash 261} 262