Files
pkgs/pkgs/by-name/he/helium-browser/update.mjs
2025-10-07 13:27:00 +04:00

291 lines
10 KiB
JavaScript
Executable File

#! /usr/bin/env nix-shell
/*
#! nix-shell -i zx -p zx
*/
console.log(process.cwd())
const custompkgs = (await $`git rev-parse --show-toplevel`).stdout.trim()
const $custompkgs = $({
cwd:custompkgs
})
const nixpkgs = "<nixpkgs>"
const dummy_hash = 'sha256-AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA='
const lockfile_file = './info.json'
const lockfile_initial = fs.readJsonSync(lockfile_file)
function flush_to_file() {
fs.writeJsonSync(lockfile_file, lockfile, { spaces: 2 })
}
const flush_to_file_proxy = {
get(obj, prop) {
const value = obj[prop]
return typeof value == 'object' ? new Proxy(value, flush_to_file_proxy) : value
},
set(obj, prop, value) {
obj[prop] = value
flush_to_file()
return true
},
}
const lockfile = new Proxy(structuredClone(lockfile_initial), flush_to_file_proxy)
const ungoogled_rev = argv['ungoogled-chromium-rev']
for (const attr_path of Object.keys(lockfile)) {
const ungoogled = attr_path === 'ungoogled-chromium'
if (!argv[attr_path] && !(ungoogled && ungoogled_rev)) {
console.log(`[${attr_path}] Skipping ${attr_path}. Pass --${attr_path} as argument to update.`)
continue
}
const version_nixpkgs = !ungoogled ? lockfile[attr_path].version : lockfile[attr_path].deps['ungoogled-patches'].rev
const version_upstream = !ungoogled ? await get_latest_chromium_release('linux') :
ungoogled_rev ?? await get_latest_ungoogled_release()
console.log(`[${attr_path}] ${chalk.red(version_nixpkgs)} (nixpkgs)`)
console.log(`[${attr_path}] ${chalk.green(version_upstream)} (upstream)`)
if (ungoogled_rev || version_greater_than(version_upstream, version_nixpkgs)) {
console.log(`[${attr_path}] ${chalk.green(version_upstream)} from upstream is newer than our ${chalk.red(version_nixpkgs)}...`)
let ungoogled_patches = ungoogled ? await fetch_ungoogled(version_upstream) : undefined
// For ungoogled-chromium we need to remove the patch revision (e.g. 130.0.6723.116-1 -> 130.0.6723.116)
// by just using the chromium_version.txt content from the patches checkout (to also work with commit revs).
const version_chromium = ungoogled_patches?.chromium_version ?? version_upstream
const chromium_rev = await chromium_resolve_tag_to_rev(version_chromium)
lockfile[attr_path] = {
version: version_chromium,
chromedriver: !ungoogled ? await fetch_chromedriver_binaries(await get_latest_chromium_release('mac')) : undefined,
deps: {
depot_tools: {},
gn: await fetch_gn(chromium_rev, lockfile_initial[attr_path].deps.gn),
'ungoogled-patches': !ungoogled ? undefined : {
rev: ungoogled_patches.rev,
hash: ungoogled_patches.hash,
},
npmHash: dummy_hash,
},
DEPS: {},
}
const depot_tools = await fetch_depot_tools(chromium_rev, lockfile_initial[attr_path].deps.depot_tools)
lockfile[attr_path].deps.depot_tools = {
rev: depot_tools.rev,
hash: depot_tools.hash,
}
// DEPS update loop
lockfile[attr_path].DEPS = await resolve_DEPS(depot_tools.out, chromium_rev)
for (const [path, value] of Object.entries(lockfile[attr_path].DEPS)) {
delete value.fetcher
delete value.postFetch
if (value.url === 'https://chromium.googlesource.com/chromium/src.git') {
value.recompress = true
}
const cache_hit = (() => {
for (const attr_path in lockfile_initial) {
const cache = lockfile_initial[attr_path].DEPS[path]
const hits_cache =
cache !== undefined &&
value.url === cache.url &&
value.rev === cache.rev &&
value.recompress === cache.recompress &&
cache.hash !== undefined &&
cache.hash !== '' &&
cache.hash !== dummy_hash
if (hits_cache) {
cache.attr_path = attr_path
return cache;
}
}
})();
if (cache_hit) {
console.log(`[${chalk.green(path)}] Reusing hash from previous info.json for ${cache_hit.url}@${cache_hit.rev} from ${cache_hit.attr_path}`)
value.hash = cache_hit.hash
continue
}
console.log(`[${chalk.red(path)}] FOD prefetching ${value.url}@${value.rev}...`)
// value.hash = await prefetch_FOD('./package.nix', '-A', `${attr_path}.browser.passthru.chromiumDeps."${path}"`)
value.hash = await prefetch_flake_FOD(`.#helium-browser.browser.passthru.chromiumDeps."${path}"`)
// console.log(`[${chalk.green(path)}] FOD prefetching successful`)
}
// lockfile[attr_path].deps.npmHash = await prefetch_FOD('./package.nix', '-A', `${attr_path}.browser.passthru.npmDeps`)
lockfile[attr_path].deps.npmHash = await prefetch_flake_FOD(`.#helium-browser.browser.passthru.npmDeps`)
console.log(chalk.green(`[${attr_path}] Done updating ${attr_path} from ${version_nixpkgs} to ${version_upstream}!`))
}
}
async function fetch_gn(chromium_rev, gn_previous) {
const DEPS_file = await get_gitiles_file('https://chromium.googlesource.com/chromium/src', chromium_rev, 'DEPS')
const { rev } = /^\s+'gn_version': 'git_revision:(?<rev>.+)',$/m.exec(DEPS_file).groups
const cache_hit = rev === gn_previous.rev;
if (cache_hit) {
return gn_previous
}
const commit_date = await get_gitiles_commit_date('https://gn.googlesource.com/gn', rev)
const version = `0-unstable-${commit_date}`
const expr = [`(import ${nixpkgs} {}).gn.override { version = "${version}"; rev = "${rev}"; hash = ""; }`]
const derivation = await $`nix-instantiate --expr ${expr}`
return {
version,
rev,
hash: await prefetch_FOD(derivation),
}
}
async function get_gitiles_commit_date(base_url, rev) {
const url = `${base_url}/+/${rev}?format=json`
const response = await (await fetch(url)).text()
const json = JSON.parse(response.replace(`)]}'\n`, ''))
const date = new Date(json.committer.time)
return date.toISOString().split("T")[0]
}
async function fetch_chromedriver_binaries(version) {
// https://developer.chrome.com/docs/chromedriver/downloads/version-selection
const prefetch = async (url) => {
const expr = [`(import ${nixpkgs} {}).fetchzip { url = "${url}"; hash = ""; }`]
const derivation = await $`nix-instantiate --expr ${expr}`
return await prefetch_FOD(derivation)
}
// if the URL ever changes, the URLs in the chromedriver derivations need updating as well!
const url = (platform) => `https://storage.googleapis.com/chrome-for-testing-public/${version}/${platform}/chromedriver-${platform}.zip`
return {
version,
hash_darwin: await prefetch(url('mac-x64')),
hash_darwin_aarch64: await prefetch(url('mac-arm64')),
}
}
async function chromium_resolve_tag_to_rev(tag) {
const url = `https://chromium.googlesource.com/chromium/src/+/refs/tags/${tag}?format=json`
const response = await (await fetch(url)).text()
const json = JSON.parse(response.replace(`)]}'\n`, ''))
return json.commit
}
async function resolve_DEPS(depot_tools_checkout, chromium_rev) {
const { stdout } = await $`./depot_tools.py ${depot_tools_checkout} ${chromium_rev}`
const deps = JSON.parse(stdout)
return Object.fromEntries(Object.entries(deps).map(([k, { url, rev, hash }]) => [k, { url, rev, hash }]))
}
async function get_latest_chromium_release(platform) {
const url = `https://versionhistory.googleapis.com/v1/chrome/platforms/${platform}/channels/stable/versions/all/releases?` + new URLSearchParams({
order_by: 'version desc',
filter: 'endtime=none,fraction>=0.5'
})
const response = await (await fetch(url)).json()
return response.releases[0].version
}
async function get_latest_ungoogled_release() {
const ungoogled_tags = await (await fetch('https://api.github.com/repos/imputnet/helium/tags')).json()
// console.log(ungoogled_tags)
// const chromium_releases = await (await fetch('https://versionhistory.googleapis.com/v1/chrome/platforms/linux/channels/stable/versions/all/releases')).json()
// const chromium_release_map = chromium_releases.releases.map((x) => x.version)
// return "0.5.2" // TODO: figure out how to actually properly do this stuff
return ungoogled_tags[0].name
}
async function fetch_ungoogled(rev) {
const expr = (hash) => [`(import ${nixpkgs} {}).fetchFromGitHub { owner = "imputnet"; repo = "helium"; rev = "${rev}"; hash = "${hash}"; }`]
const hash = await prefetch_FOD('--expr', expr(''))
const checkout = await $`nix-build --expr ${expr(hash)}`
const checkout_path = checkout.stdout.trim()
await fs.copy(path.join(checkout_path, 'flags.gn'), './ungoogled-flags.toml')
const chromium_version = (await fs.readFile(path.join(checkout_path, 'chromium_version.txt'))).toString().trim()
console.log(`[ungoogled-chromium] ${chalk.green(rev)} patch revision resolves to chromium version ${chalk.green(chromium_version)}`)
return {
rev,
hash,
chromium_version,
}
}
function version_greater_than(greater, than) {
return greater.localeCompare(than, undefined, { numeric: true, sensitivity: 'base' }) === 1
}
async function get_gitiles_file(repo, rev, path) {
const base64 = await (await fetch(`${repo}/+/${rev}/${path}?format=TEXT`)).text()
return Buffer.from(base64, 'base64').toString('utf-8')
}
async function fetch_depot_tools(chromium_rev, depot_tools_previous) {
const depot_tools_rev = await get_gitiles_file('https://chromium.googlesource.com/chromium/src', chromium_rev, 'third_party/depot_tools')
const hash = depot_tools_rev === depot_tools_previous.rev ? depot_tools_previous.hash : ''
return await prefetch_gitiles('https://chromium.googlesource.com/chromium/tools/depot_tools', depot_tools_rev, hash)
}
async function prefetch_gitiles(url, rev, hash = '') {
const expr = () => [`(import ${nixpkgs} {}).fetchFromGitiles { url = "${url}"; rev = "${rev}"; hash = "${hash}"; }`]
if (hash === '') {
hash = await prefetch_FOD('--expr', expr())
}
const { stdout } = await $`nix-build --expr ${expr()}`
return {
url,
rev,
hash,
out: stdout.trim(),
}
}
async function prefetch_FOD(...args) {
const { stderr } = await $`nix-build ${args}`.nothrow()
const hash = /\s+got:\s+(?<hash>.+)$/m.exec(stderr)?.groups?.hash
if (hash == undefined) {
throw new Error(chalk.red('Expected to find hash in nix-build stderr output:') + stderr)
}
return hash
}
async function prefetch_flake_FOD(...args) {
const { stderr } = await $custompkgs`nix build ${args}`.nothrow()
const hash = /\s+got:\s+(?<hash>.+)$/m.exec(stderr)?.groups?.hash
if (hash == undefined) {
throw new Error(chalk.red('Expected to find hash in nix build stderr output:') + stderr)
}
return hash
}