├── .envrc ├── .github ├── dependabot.yml └── workflows │ └── bump.yml ├── .gitignore ├── CODEOWNERS ├── README.md ├── flake.lock ├── flake.nix └── google-chrome ├── default.nix ├── get-commit-message.py ├── update.py └── upstream-info.nix /.envrc: -------------------------------------------------------------------------------- 1 | export NIXPKGS_ALLOW_UNFREE=1 2 | use flake 3 | 4 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | timezone: Australia/Brisbane 8 | open-pull-requests-limit: 3 9 | reviewers: 10 | - r-k-b 11 | -------------------------------------------------------------------------------- /.github/workflows/bump.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | schedule: 4 | # ~6am AEST 5 | - cron: "19 19 * * *" 6 | jobs: 7 | bump_versions: 8 | name: Bump Browser Versions 9 | runs-on: 10 | ubuntu-latest 11 | env: 12 | # https://stackoverflow.com/a/71158878/2014893 13 | BRANCH_NAME: ${{ github.head_ref || github.ref_name }} 14 | steps: 15 | - name: git checkout 16 | uses: actions/checkout@v4 17 | - name: Install Nix via Cachix 18 | if: env.using_self_hosted_runner_with_nix != 'true' 19 | uses: cachix/install-nix-action@v31 20 | with: 21 | install_url: "https://releases.nixos.org/nix/nix-2.21.0/install" 22 | - name: Run the Magic Nix Cache # https://determinate.systems/posts/magic-nix-cache 23 | uses: DeterminateSystems/magic-nix-cache-action@v9 24 | - name: Check Nix flake inputs 25 | uses: DeterminateSystems/flake-checker-action@v10 26 | with: 27 | fail-mode: false 28 | - name: pull dev environment 29 | run: | 30 | nix develop --command \ 31 | echo dev environment ready 32 | - name: prep git 33 | run: | 34 | set -e 35 | git fetch 36 | git config --global user.name 'BumpBot' 37 | git config --global user.email 'r-k-b@users.noreply.github.com' 38 | - name: update google-chrome 39 | run: | 40 | echo branch_name is "$BRANCH_NAME" 41 | if [[ "$BRANCH_NAME" == "main" ]]; then 42 | nix develop --command \ 43 | ./google-chrome/update.py --commit 44 | else 45 | echo "not on branch 'main'; skipping update.py." 46 | fi 47 | - name: check build outputs 48 | env: 49 | NIXPKGS_ALLOW_UNFREE: "1" 50 | run: | 51 | nix flake check --impure 52 | - name: push to origin 53 | run: git push 54 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .direnv 2 | .result 3 | result 4 | .idea 5 | browser-previews.iml 6 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @r-k-b 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # browser-previews flake for NixOS users 2 | 3 | This flake provides the latest Chrome stable, beta and dev releases. The flake is updated regularly to ensure fast and up to date releases, which would otherwise not be possible in [nixpkgs](https://github.com/NixOS/nixpkgs). 4 | 5 | ## Why? 6 | 7 | `google-chrome-beta` and `google-chrome-dev` were not getting maintained in nixpkgs, 8 | and were dropped in [NixOS/nixpkgs#261870](https://github.com/NixOS/nixpkgs/pull/261870). 9 | 10 | ## Available packages 11 | 12 | Run `nix flake show github:nix-community/browser-previews` for the up-to-date list. 13 | 14 | ## How? 15 | 16 | ### To directly run latest `google-chrome` from this flake 17 | 18 | ```bash 19 | NIXPKGS_ALLOW_UNFREE=1 nix run github:nix-community/browser-previews#google-chrome --impure 20 | ``` 21 | 22 | Likewise for google-chrome-beta or google-chrome-dev: 23 | 24 | ```bash 25 | NIXPKGS_ALLOW_UNFREE=1 nix run github:nix-community/browser-previews#google-chrome-beta --impure 26 | ``` 27 | 28 | ```bash 29 | NIXPKGS_ALLOW_UNFREE=1 nix run github:nix-community/browser-previews#google-chrome-dev --impure 30 | ``` 31 | 32 | ### To install a package from this flake 33 | 34 | - First you must add it as a input to your `flake.nix`: 35 | 36 | ```nix 37 | inputs.browser-previews = { url = "github:nix-community/browser-previews"; 38 | inputs.nixpkgs.follows = "nixpkgs"; }; 39 | ``` 40 | 41 | - Pass `inputs` to your modules using `specialArgs`. 42 | 43 | - Then in `configuration.nix`, use it like this: 44 | 45 | ```nix 46 | { config, lib, pkgs, inputs, ... }: 47 | { 48 | environment.systemPackages = with inputs.browser-previews.packages.${pkgs.system}; [ 49 | google-chrome # Stable Release 50 | google-chrome-beta # Beta Release 51 | google-chrome-dev # Dev Release 52 | ]; 53 | } 54 | ``` 55 | 56 | (Must have flakes enabled.) 57 | 58 | ## Why not include chromium? 59 | 60 | The chromium build takes more resources than I'm willing to spend. 61 | -------------------------------------------------------------------------------- /flake.lock: -------------------------------------------------------------------------------- 1 | { 2 | "nodes": { 3 | "flake-utils": { 4 | "inputs": { 5 | "systems": [ 6 | "systems" 7 | ] 8 | }, 9 | "locked": { 10 | "lastModified": 1731533236, 11 | "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", 12 | "owner": "numtide", 13 | "repo": "flake-utils", 14 | "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", 15 | "type": "github" 16 | }, 17 | "original": { 18 | "owner": "numtide", 19 | "repo": "flake-utils", 20 | "type": "github" 21 | } 22 | }, 23 | "nixpkgs": { 24 | "locked": { 25 | "lastModified": 1735834308, 26 | "narHash": "sha256-dklw3AXr3OGO4/XT1Tu3Xz9n/we8GctZZ75ZWVqAVhk=", 27 | "owner": "NixOS", 28 | "repo": "nixpkgs", 29 | "rev": "6df24922a1400241dae323af55f30e4318a6ca65", 30 | "type": "github" 31 | }, 32 | "original": { 33 | "id": "nixpkgs", 34 | "ref": "nixos-unstable", 35 | "type": "indirect" 36 | } 37 | }, 38 | "root": { 39 | "inputs": { 40 | "flake-utils": "flake-utils", 41 | "nixpkgs": "nixpkgs", 42 | "systems": "systems" 43 | } 44 | }, 45 | "systems": { 46 | "locked": { 47 | "lastModified": 1680978846, 48 | "narHash": "sha256-Gtqg8b/v49BFDpDetjclCYXm8mAnTrUzR0JnE2nv5aw=", 49 | "owner": "nix-systems", 50 | "repo": "x86_64-linux", 51 | "rev": "2ecfcac5e15790ba6ce360ceccddb15ad16d08a8", 52 | "type": "github" 53 | }, 54 | "original": { 55 | "owner": "nix-systems", 56 | "repo": "x86_64-linux", 57 | "type": "github" 58 | } 59 | } 60 | }, 61 | "root": "root", 62 | "version": 7 63 | } 64 | -------------------------------------------------------------------------------- /flake.nix: -------------------------------------------------------------------------------- 1 | { 2 | description = "fresh browser previews (dev, beta)"; 3 | 4 | inputs = { 5 | flake-utils = { 6 | url = "github:numtide/flake-utils"; 7 | inputs.systems.follows = "systems"; 8 | }; 9 | nixpkgs.url = "nixpkgs/nixos-unstable"; 10 | systems.url = "github:nix-systems/x86_64-linux"; 11 | }; 12 | 13 | outputs = 14 | { 15 | self, 16 | nixpkgs, 17 | flake-utils, 18 | ... 19 | }: 20 | flake-utils.lib.eachDefaultSystem ( 21 | system: 22 | let 23 | pkgs = import nixpkgs { 24 | system = "x86_64-linux"; 25 | config.allowUnfree = true; 26 | }; 27 | google-chrome = channel: pkgs.callPackage ./google-chrome { inherit channel; }; 28 | in 29 | { 30 | checks = { 31 | google-chrome = google-chrome "stable"; 32 | google-chrome-beta = google-chrome "beta"; 33 | google-chrome-dev = google-chrome "dev"; 34 | }; 35 | devShells = { 36 | default = pkgs.mkShell { 37 | name = "fresh-browser-previews-shell"; 38 | 39 | buildInputs = with pkgs; [ 40 | nix 41 | nix-prefetch-git 42 | nixfmt-rfc-style 43 | (python3.withPackages ( 44 | p3pkgs: with p3pkgs; [ 45 | feedparser 46 | looseversion 47 | requests 48 | ] 49 | )) 50 | ]; 51 | }; 52 | }; 53 | packages = { 54 | default = google-chrome "stable"; 55 | google-chrome = google-chrome "stable"; 56 | google-chrome-beta = google-chrome "beta"; 57 | google-chrome-dev = google-chrome "dev"; 58 | }; 59 | } 60 | ); 61 | } 62 | -------------------------------------------------------------------------------- /google-chrome/default.nix: -------------------------------------------------------------------------------- 1 | # Based on: https://github.com/NixOS/nixpkgs/blob/2c106c1e7c0794927c3b889de51e3c2cdd9130ba/pkgs/applications/networking/browsers/google-chrome/default.nix 2 | { 3 | fetchurl, 4 | lib, 5 | stdenv, 6 | patchelf, 7 | makeWrapper, 8 | 9 | # Linked dynamic libraries. 10 | glib, 11 | fontconfig, 12 | freetype, 13 | pango, 14 | cairo, 15 | libX11, 16 | libXi, 17 | atk, 18 | nss, 19 | nspr, 20 | libXcursor, 21 | libXext, 22 | libXfixes, 23 | libXrender, 24 | libXScrnSaver, 25 | libXcomposite, 26 | libxcb, 27 | alsa-lib, 28 | libXdamage, 29 | libXtst, 30 | libXrandr, 31 | libxshmfence, 32 | expat, 33 | cups, 34 | dbus, 35 | gtk3, 36 | gtk4, 37 | gdk-pixbuf, 38 | gcc-unwrapped, 39 | at-spi2-atk, 40 | at-spi2-core, 41 | libkrb5, 42 | libdrm, 43 | libglvnd, 44 | libgbm, 45 | libxkbcommon, 46 | pipewire, 47 | wayland, # ozone/wayland 48 | 49 | # Command line programs 50 | coreutils, 51 | 52 | # command line arguments which are always set e.g "--disable-gpu" 53 | commandLineArgs ? "", 54 | 55 | # Will crash without. 56 | systemd, 57 | 58 | # Loaded at runtime. 59 | libexif, 60 | pciutils, 61 | 62 | # Additional dependencies according to other distros. 63 | ## Ubuntu 64 | liberation_ttf, 65 | curl, 66 | util-linux, 67 | xdg-utils, 68 | wget, 69 | ## Arch Linux. 70 | flac, 71 | harfbuzz, 72 | icu, 73 | libpng, 74 | libopus, 75 | snappy, 76 | speechd, 77 | ## Gentoo 78 | bzip2, 79 | libcap, 80 | 81 | # Which distribution channel to use. 82 | channel ? "stable", 83 | 84 | # Necessary for USB audio devices. 85 | pulseSupport ? true, 86 | libpulseaudio, 87 | 88 | gsettings-desktop-schemas, 89 | adwaita-icon-theme, 90 | 91 | # For video acceleration via VA-API (--enable-features=VaapiVideoDecoder) 92 | libvaSupport ? true, 93 | libva, 94 | 95 | # For Vulkan support (--enable-features=Vulkan) 96 | addDriverRunpath, 97 | }: 98 | 99 | let 100 | opusWithCustomModes = libopus.override { withCustomModes = true; }; 101 | 102 | upstream-info = (import ./upstream-info.nix); 103 | 104 | version = upstream-info.${channel}.version; 105 | 106 | deps = 107 | [ 108 | glib 109 | fontconfig 110 | freetype 111 | pango 112 | cairo 113 | libX11 114 | libXi 115 | atk 116 | nss 117 | nspr 118 | libXcursor 119 | libXext 120 | libXfixes 121 | libXrender 122 | libXScrnSaver 123 | libXcomposite 124 | libxcb 125 | alsa-lib 126 | libXdamage 127 | libXtst 128 | libXrandr 129 | libxshmfence 130 | expat 131 | cups 132 | dbus 133 | gdk-pixbuf 134 | gcc-unwrapped.lib 135 | systemd 136 | libexif 137 | pciutils 138 | liberation_ttf 139 | curl 140 | util-linux 141 | wget 142 | flac 143 | harfbuzz 144 | icu 145 | libpng 146 | opusWithCustomModes 147 | snappy 148 | speechd 149 | bzip2 150 | libcap 151 | at-spi2-atk 152 | at-spi2-core 153 | libkrb5 154 | libdrm 155 | libglvnd 156 | libgbm 157 | coreutils 158 | libxkbcommon 159 | pipewire 160 | wayland 161 | ] 162 | ++ lib.optional pulseSupport libpulseaudio 163 | ++ lib.optional libvaSupport libva 164 | ++ [ 165 | gtk3 166 | gtk4 167 | ]; 168 | 169 | suffix = lib.optionalString (channel != "stable") "-${channel}"; 170 | 171 | crashpadHandlerBinary = 172 | if lib.versionAtLeast version "94" then "chrome_crashpad_handler" else "crashpad_handler"; 173 | 174 | pkgSuffix = 175 | if channel == "dev" then 176 | "unstable" 177 | else 178 | (if channel == "ungoogled-chromium" then "stable" else channel); 179 | 180 | pkgName = "google-chrome-${pkgSuffix}"; 181 | in 182 | stdenv.mkDerivation { 183 | inherit version; 184 | 185 | name = "google-chrome${suffix}-${version}"; 186 | 187 | # chromeSrc 188 | src = 189 | let 190 | # Use the latest stable Chrome version if necessary: 191 | version = upstream-info.${channel}.version; 192 | hash = upstream-info.${channel}.hash_deb_amd64; 193 | in 194 | fetchurl { 195 | urls = map (repo: "${repo}/${pkgName}/${pkgName}_${version}-1_amd64.deb") [ 196 | "https://dl.google.com/linux/chrome/deb/pool/main/g" 197 | "http://95.31.35.30/chrome/pool/main/g" 198 | "http://mirror.pcbeta.com/google/chrome/deb/pool/main/g" 199 | "http://repo.fdzh.org/chrome/deb/pool/main/g" 200 | ]; 201 | inherit hash; 202 | }; 203 | 204 | nativeBuildInputs = [ 205 | patchelf 206 | makeWrapper 207 | ]; 208 | buildInputs = [ 209 | # needed for GSETTINGS_SCHEMAS_PATH 210 | gsettings-desktop-schemas 211 | glib 212 | gtk3 213 | 214 | # needed for XDG_ICON_DIRS 215 | adwaita-icon-theme 216 | ]; 217 | 218 | unpackPhase = '' 219 | ar x $src 220 | tar xf data.tar.xz 221 | ''; 222 | 223 | rpath = lib.makeLibraryPath deps + ":" + lib.makeSearchPathOutput "lib" "lib64" deps; 224 | binpath = lib.makeBinPath deps; 225 | 226 | installPhase = '' 227 | runHook preInstall 228 | 229 | case ${channel} in 230 | beta) appname=chrome-beta dist=beta ;; 231 | dev) appname=chrome-unstable dist=unstable ;; 232 | *) appname=chrome dist=stable ;; 233 | esac 234 | 235 | exe=$out/bin/google-chrome-$dist 236 | 237 | mkdir -p $out/bin $out/share 238 | 239 | cp -a opt/* $out/share 240 | cp -a usr/share/* $out/share 241 | 242 | 243 | substituteInPlace $out/share/google/$appname/google-$appname \ 244 | --replace 'CHROME_WRAPPER' 'WRAPPER' 245 | substituteInPlace $out/share/applications/google-$appname.desktop \ 246 | --replace /usr/bin/google-chrome-$dist $exe 247 | substituteInPlace $out/share/gnome-control-center/default-apps/google-$appname.xml \ 248 | --replace /opt/google/$appname/google-$appname $exe 249 | substituteInPlace $out/share/menu/google-$appname.menu \ 250 | --replace /opt $out/share \ 251 | --replace $out/share/google/$appname/google-$appname $exe 252 | 253 | for icon_file in $out/share/google/chrome*/product_logo_[0-9]*.png; do 254 | num_and_suffix="''${icon_file##*logo_}" 255 | if [ $dist = "stable" ]; then 256 | icon_size="''${num_and_suffix%.*}" 257 | else 258 | icon_size="''${num_and_suffix%_*}" 259 | fi 260 | logo_output_prefix="$out/share/icons/hicolor" 261 | logo_output_path="$logo_output_prefix/''${icon_size}x''${icon_size}/apps" 262 | mkdir -p "$logo_output_path" 263 | mv "$icon_file" "$logo_output_path/google-$appname.png" 264 | done 265 | 266 | makeWrapper "$out/share/google/$appname/google-$appname" "$exe" \ 267 | --prefix LD_LIBRARY_PATH : "$rpath" \ 268 | --prefix PATH : "$binpath" \ 269 | --suffix PATH : "${lib.makeBinPath [ xdg-utils ]}" \ 270 | --prefix XDG_DATA_DIRS : "$XDG_ICON_DIRS:$GSETTINGS_SCHEMAS_PATH:${addDriverRunpath.driverLink}/share" \ 271 | --set CHROME_WRAPPER "google-chrome-$dist" \ 272 | --add-flags "\''${NIXOS_OZONE_WL:+\''${WAYLAND_DISPLAY:+--ozone-platform-hint=auto --enable-features=WaylandWindowDecorations}}" \ 273 | --add-flags ${lib.escapeShellArg commandLineArgs} 274 | 275 | for elf in $out/share/google/$appname/{chrome,chrome-sandbox,${crashpadHandlerBinary}}; do 276 | patchelf --set-rpath $rpath $elf 277 | patchelf --set-interpreter "$(cat $NIX_CC/nix-support/dynamic-linker)" $elf 278 | done 279 | 280 | runHook postInstall 281 | ''; 282 | 283 | meta = { 284 | description = "A freeware web browser developed by Google"; 285 | homepage = "https://www.google.com/chrome/browser/"; 286 | license = lib.licenses.unfree; 287 | sourceProvenance = with lib.sourceTypes; [ binaryNativeCode ]; 288 | platforms = [ "x86_64-linux" ]; 289 | mainProgram = if (channel == "dev") then "google-chrome-unstable" else "google-chrome-${channel}"; 290 | }; 291 | } 292 | -------------------------------------------------------------------------------- /google-chrome/get-commit-message.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # This script prints the Git commit message for stable channel updates. 4 | # Usage: ./get-commit-message.py [version] 5 | 6 | # Based on 7 | # https://github.com/NixOS/nixpkgs/blob/92559b7330a56778dde383225bae47e225de4861/pkgs/applications/networking/browsers/chromium/get-commit-message.py 8 | 9 | import re 10 | import sys 11 | import textwrap 12 | 13 | from collections import OrderedDict 14 | 15 | import feedparser 16 | import requests 17 | from builtins import len, list, print 18 | 19 | 20 | releasesBlogFeed = 'https://chromereleases.googleblog.com/feeds/posts/default' 21 | 22 | 23 | def stderr(s): 24 | sys.stderr.write(f'{s}\n') 25 | 26 | 27 | stderr(f'Fetching feed from <{releasesBlogFeed}>...') 28 | feed = feedparser.parse(releasesBlogFeed) 29 | stderr(f'fetched {len(feed.entries)} articles.') 30 | html_tags = re.compile(r'<[^>]+>') 31 | target_version = sys.argv[1] if len(sys.argv) == 2 else None 32 | 33 | stderr(f'Looking for posts mentioning target version "{target_version}"...') 34 | 35 | for entry in feed.entries: 36 | stderr(f'\nChecking entry link <{entry.link}>...') 37 | url = requests.get(entry.link).url.split('?')[0] 38 | if entry.title != 'Stable Channel Update for Desktop': 39 | if target_version and entry.title == '': 40 | # Workaround for a special case (Chrome Releases bug?): 41 | if 'the-stable-channel-has-been-updated-to' not in url: 42 | stderr('Not the "blank title" special case; skipping.') 43 | continue 44 | else: 45 | stderr('Not "Stable Channel Update for Desktop"; skipping.') 46 | continue 47 | content = entry.content[0].value 48 | content = html_tags.sub('', content) # Remove any HTML tags 49 | if re.search(r'Linux', content) is None: 50 | stderr('No mention of Linux; skipping.') 51 | continue 52 | # print(url) # For debugging purposes 53 | version = re.search(r'\d+(\.\d+){3}', content).group(0) 54 | if target_version: 55 | if version != target_version: 56 | stderr(f'Entry version "{version} does not match target; skipping." ') 57 | continue 58 | else: 59 | print('chromium: TODO -> ' + version + '\n') 60 | print(url) 61 | if fixes := re.search(r'This update includes .+ security fix(es)?\.', content): 62 | fixes = fixes.group(0) 63 | if zero_days := re.search(r'Google is aware( of reports)? th(e|at) .+ in the wild\.', content): 64 | fixes += " " + zero_days.group(0) 65 | print('\n' + '\n'.join(textwrap.wrap(fixes, width=72))) 66 | if cve_list := re.findall(r'CVE-[^: ]+', content): 67 | cve_list = list(OrderedDict.fromkeys(cve_list)) # Remove duplicates but preserve the order 68 | cve_string = ' '.join(cve_list) 69 | print("\nCVEs:\n" + '\n'.join(textwrap.wrap(cve_string, width=72))) 70 | sys.exit(0) # We only care about the most recent stable channel update 71 | 72 | stderr("Error: No match.") 73 | sys.exit(1) 74 | -------------------------------------------------------------------------------- /google-chrome/update.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | 3 | # Based on 4 | # https://github.com/NixOS/nixpkgs/blob/59719f787e94f39e64e9086d08eaedd8a9e61b22/pkgs/applications/networking/browsers/chromium/update.py 5 | 6 | """This script automatically updates google-chrome via upstream-info.nix.""" 7 | # Usage: ./update.py [--commit] 8 | 9 | import base64 10 | import json 11 | import re 12 | import subprocess 13 | import sys 14 | 15 | from collections import OrderedDict 16 | 17 | from builtins import iter, len, open, print, sorted 18 | from looseversion import LooseVersion 19 | from os.path import abspath, dirname 20 | from urllib.request import urlopen 21 | 22 | RELEASES_URL = 'https://versionhistory.googleapis.com/v1/chrome/platforms/linux/channels/all/versions/all/releases' 23 | DEB_URL = 'https://dl.google.com/linux/chrome/deb/pool/main/g' 24 | 25 | PIN_PATH = dirname(abspath(__file__)) + '/upstream-info.nix' 26 | COMMIT_MESSAGE_SCRIPT = dirname(abspath(__file__)) + '/get-commit-message.py' 27 | NIXPKGS_PATH = subprocess.check_output(["git", "rev-parse", "--show-toplevel"], cwd=dirname(PIN_PATH)).strip() 28 | 29 | 30 | def load_as_json(path): 31 | """Loads the given nix file as JSON.""" 32 | out = subprocess.check_output(['nix-instantiate', '--eval', '--strict', '--json', path]) 33 | return json.loads(out) 34 | 35 | 36 | def save_dict_as_nix(path, dict_input): 37 | """Saves the given dict/JSON as nix file.""" 38 | json_string = json.dumps(dict_input) 39 | nix = subprocess.check_output( 40 | ['nix-instantiate', '--eval', '--expr', '{ json }: builtins.fromJSON json', '--argstr', 'json', json_string]) 41 | formatted = subprocess.check_output(['nixfmt'], input=nix) 42 | with open(path, 'w') as out: 43 | out.write(formatted.decode()) 44 | 45 | 46 | def prefetch_src_sri_hash(attr_path, version): 47 | """Prefetches the fixed-output-derivation source tarball and returns its SRI-Hash.""" 48 | print(f'nix-build (FOD prefetch) {attr_path} {version}') 49 | out = subprocess.run( 50 | ["nix-build", "--expr", 51 | f'(import ./. {{}}).{attr_path}.browser.passthru.recompressTarball {{ version = "{version}"; }}'], 52 | cwd=NIXPKGS_PATH, 53 | stderr=subprocess.PIPE 54 | ).stderr.decode() 55 | 56 | for line in iter(out.split("\n")): 57 | match = re.match(r"\s+got:\s+(.+)$", line) 58 | if match: 59 | print(f'Hash: {match.group(1)}') 60 | return match.group(1) 61 | print(f'{out}\n\nError: Expected hash in nix-build stderr output.', file=sys.stderr) 62 | sys.exit(1) 63 | 64 | 65 | def nix_prefetch_url(url, algo='sha256'): 66 | """Prefetches the content of the given URL.""" 67 | print(f'nix store prefetch-file {url}') 68 | out = subprocess.check_output(['nix', 'store', 'prefetch-file', '--json', '--hash-type', algo, url]) 69 | return json.loads(out)['hash'] 70 | 71 | 72 | def nix_prefetch_git(url, rev): 73 | """Prefetches the requested Git revision of the given repository URL.""" 74 | print(f'nix-prefetch-git {url} {rev}') 75 | out = subprocess.check_output(['nix-prefetch-git', '--quiet', '--url', url, '--rev', rev]) 76 | return json.loads(out) 77 | 78 | 79 | def get_file_revision(revision, file_path): 80 | """Fetches the requested Git revision of the given Chromium file.""" 81 | url = f'https://chromium.googlesource.com/chromium/src/+/refs/tags/{revision}/{file_path}?format=TEXT' 82 | with urlopen(url) as http_response: 83 | response = http_response.read() 84 | return base64.b64decode(response) 85 | 86 | 87 | def get_chromedriver(cd_channel): 88 | """Get the latest chromedriver builds given a channel""" 89 | # See https://chromedriver.chromium.org/downloads/version-selection#h.4wiyvw42q63v 90 | chromedriver_versions_url = \ 91 | f'https://googlechromelabs.github.io/chrome-for-testing/last-known-good-versions-with-downloads.json' 92 | print(f'GET {chromedriver_versions_url}') 93 | with urlopen(chromedriver_versions_url) as http_response: 94 | chromedrivers = json.load(http_response) 95 | cd_channel = chromedrivers['channels'][cd_channel] 96 | downloads = cd_channel['downloads']['chromedriver'] 97 | 98 | def get_chromedriver_url(platform): 99 | for download in downloads: 100 | if download['platform'] == platform: 101 | return download['url'] 102 | 103 | return { 104 | 'version': cd_channel['version'], 105 | 'hash_linux': nix_prefetch_url(get_chromedriver_url('linux64')), 106 | 'hash_darwin': nix_prefetch_url(get_chromedriver_url('mac-x64')), 107 | 'hash_darwin_aarch64': nix_prefetch_url(get_chromedriver_url('mac-arm64')) 108 | } 109 | 110 | 111 | def channel_name_to_attr_name(channel_name): 112 | if channel_name == 'stable': 113 | return 'chrome' 114 | if channel_name == 'beta': 115 | return 'chromeBeta' 116 | if channel_name == 'dev': 117 | return 'chromeDev' 118 | print(f'Error: Unexpected channel: {channel_name}', file=sys.stderr) 119 | sys.exit(1) 120 | 121 | 122 | def get_channel_key(item): 123 | """Orders Chromium channels by their name.""" 124 | channel_name = item[0] 125 | if channel_name == 'stable': 126 | return 0 127 | if channel_name == 'beta': 128 | return 1 129 | if channel_name == 'dev': 130 | return 2 131 | if channel_name == 'ungoogled-chromium': 132 | return 3 133 | print(f'Error: Unexpected channel: {channel_name}', file=sys.stderr) 134 | sys.exit(1) 135 | 136 | 137 | def print_updates(channels_old, channels_new): 138 | """Print a summary of the updates.""" 139 | print('Updates:') 140 | for channel_name in channels_old: 141 | version_old = channels_old[channel_name]["version"] 142 | version_new = channels_new[channel_name]["version"] 143 | if LooseVersion(version_old) < LooseVersion(version_new): 144 | attr_name = channel_name_to_attr_name(channel_name) 145 | print(f'- {attr_name}: {version_old} -> {version_new}') 146 | 147 | 148 | def main(): 149 | channels = {} 150 | last_channels = load_as_json(PIN_PATH) 151 | 152 | print(f'GET {RELEASES_URL}', file=sys.stderr) 153 | with urlopen(RELEASES_URL) as resp: 154 | releases = json.load(resp)['releases'] 155 | 156 | for release in releases: 157 | channel_name = re.findall("chrome/platforms/linux/channels/(.*)/versions/", release['name'])[0] 158 | 159 | # If we've already found a newer release for this channel, we're 160 | # no longer interested in it. 161 | if channel_name in channels: 162 | continue 163 | 164 | # We only look for channels that are listed in our version pin file. 165 | if channel_name not in last_channels: 166 | continue 167 | 168 | # If we're back at the last release we used, we don't need to 169 | # keep going -- there's no new version available, and we can 170 | # just reuse the info from last time. 171 | if release['version'] == last_channels[channel_name]['version']: 172 | channels[channel_name] = last_channels[channel_name] 173 | continue 174 | 175 | channel = {'version': release['version']} 176 | if channel_name == 'dev': 177 | google_chrome_suffix = 'unstable' 178 | elif channel_name == 'ungoogled-chromium': 179 | google_chrome_suffix = 'stable' 180 | else: 181 | google_chrome_suffix = channel_name 182 | 183 | try: 184 | # channel['hash'] = prefetch_src_sri_hash( 185 | # channel_name_to_attr_name(channel_name), 186 | # release["version"] 187 | # ) 188 | channel['hash_deb_amd64'] = nix_prefetch_url( 189 | f'{DEB_URL}/google-chrome-{google_chrome_suffix}/' + 190 | f'google-chrome-{google_chrome_suffix}_{release["version"]}-1_amd64.deb') 191 | except subprocess.CalledProcessError: 192 | # This release isn't actually available yet. Continue to 193 | # the next one. 194 | print( 195 | f'Release not available yet: google-chrome-{google_chrome_suffix}_{release["version"]}-1_amd64.deb' 196 | ) 197 | continue 198 | 199 | if channel_name == 'stable': 200 | channel['chromedriver'] = get_chromedriver('Stable') 201 | 202 | channels[channel_name] = channel 203 | 204 | sorted_channels = OrderedDict(sorted(channels.items(), key=get_channel_key)) 205 | if len(sys.argv) == 2 and sys.argv[1] == '--commit': 206 | for channel_name in sorted_channels.keys(): 207 | version_old = last_channels[channel_name]['version'] 208 | version_new = sorted_channels[channel_name]['version'] 209 | print(f'Creating commits for {channel_name} using "Old" version ({version_old}) and "New" version ({version_new})...') 210 | if LooseVersion(version_old) < LooseVersion(version_new): 211 | last_channels[channel_name] = sorted_channels[channel_name] 212 | save_dict_as_nix(PIN_PATH, last_channels) 213 | attr_name = channel_name_to_attr_name(channel_name) 214 | commit_message = f'{attr_name}: {version_old} -> {version_new}' 215 | if channel_name == 'stable': 216 | body = subprocess.check_output([COMMIT_MESSAGE_SCRIPT, version_new]).decode('utf-8') 217 | commit_message += '\n\n' + body 218 | # `JSON_PATH`? 219 | subprocess.run(['git', 'add', PIN_PATH], check=True) 220 | subprocess.run(['git', 'commit', '--file=-'], input=commit_message.encode(), check=True) 221 | else: 222 | save_dict_as_nix(PIN_PATH, sorted_channels) 223 | print_updates(last_channels, sorted_channels) 224 | 225 | 226 | main() 227 | -------------------------------------------------------------------------------- /google-chrome/upstream-info.nix: -------------------------------------------------------------------------------- 1 | { 2 | beta = { 3 | hash_deb_amd64 = "sha256-8X+F1hp2S30GjGAWjLo/bBWsRf4YhGP6H1BJd2oNeFM="; 4 | version = "138.0.7204.15"; 5 | }; 6 | dev = { 7 | hash_deb_amd64 = "sha256-DVvlHHyNQdePet88lm+CKfVBro+ISpJhFAVeWFXEL30="; 8 | version = "139.0.7219.3"; 9 | }; 10 | stable = { 11 | chromedriver = { 12 | hash_darwin = "sha256-82lAakjKmO38GEXjkAvgOatMk96r6UEohRS7QCYxpcE="; 13 | hash_darwin_aarch64 = "sha256-vCNyg+/u2wKgZTYevbSZ2Z7DQ08ZwDkA3MYTMBpM3/0="; 14 | hash_linux = "sha256-BeNWs91Jeib140EsZ7WZdoU0WPGzqlsrn2Bs7N+SJYY="; 15 | version = "137.0.7151.68"; 16 | }; 17 | hash_deb_amd64 = "sha256-TBJ6XOnxrP/+Gnd2Y3/aYbRG5N+b7dKS/u80ZK4E+MM="; 18 | version = "137.0.7151.68"; 19 | }; 20 | } 21 | --------------------------------------------------------------------------------