Migration
This is my new public git source viewer. Source coming soon. Please contact with concerns/bugs.
Contact me
users/ryan/modules/zen/extensions.nix
b7d807c39733ce101c55286d6428c19fb31c1ef3
· 3.7 KB · 89 lines
raw
| 1 | # version = null -> whatever the lock holds; `lock.sh <attr>` bumps it |
| 2 | # version = "x.y.z" -> hard pin; lock.sh resolves exactly that version and |
| 3 | # eval fails if the lock disagrees |
| 4 | { config, lib, pkgs, ... }: |
| 5 | let |
| 6 | inherit (lib) mapAttrs' mapAttrsToList nameValuePair optionalAttrs; |
| 7 | cfg = config.ryan.zen; |
| 8 | exts = lib.filterAttrs (_: e: e.enable) cfg.extensions; |
| 9 | zen = config.programs.zen-browser; |
| 10 | |
| 11 | lockFile = ./extensions.lock.json; |
| 12 | lock = lib.importJSON lockFile; |
| 13 | |
| 14 | # Validated lock entry. Throws (not assertions) so the message surfaces no |
| 15 | # matter which consumer forces the policy first (HM wrapper, darwin defaults). |
| 16 | locked = name: e: |
| 17 | let |
| 18 | l = lock.${name} or (throw |
| 19 | "zen extension '${name}' has no entry in extensions.lock.json; run modules/zen/lock.sh"); |
| 20 | in |
| 21 | if l.id != e.id then throw |
| 22 | "zen extension '${name}': lock has id ${l.id}, config has ${e.id}; re-run lock.sh" |
| 23 | else if e.version != null && l.version != e.version then throw |
| 24 | "zen extension '${name}': pinned ${e.version}, lock has ${l.version}; re-run lock.sh" |
| 25 | else l; |
| 26 | |
| 27 | xpi = name: e: |
| 28 | let l = locked name e; in |
| 29 | pkgs.fetchurl { |
| 30 | name = "zen-ext-${name}-${l.version}.xpi"; |
| 31 | inherit (l) url hash; |
| 32 | }; |
| 33 | |
| 34 | settingsFor = name: e: |
| 35 | { |
| 36 | install_url = "file://${xpi name e}"; |
| 37 | installation_mode = e.mode; |
| 38 | updates_disabled = true; |
| 39 | } |
| 40 | // optionalAttrs (e.privateBrowsing != null) { private_browsing = e.privateBrowsing; } |
| 41 | // e.extraSettings; |
| 42 | |
| 43 | pins = pkgs.writeText "zen-extension-pins.json" (builtins.toJSON |
| 44 | (mapAttrsToList (name: e: { inherit (e) id; version = (locked name e).version; }) exts)); |
| 45 | |
| 46 | jq = lib.getExe pkgs.jq; |
| 47 | sort = "${pkgs.coreutils}/bin/sort"; |
| 48 | in |
| 49 | { |
| 50 | config = lib.mkIf (cfg.enable && exts != { }) { |
| 51 | programs.zen-browser.policies = { |
| 52 | # Locks extensions.update.enabled=false; belt to updates_disabled's braces. |
| 53 | ExtensionUpdate = false; |
| 54 | ExtensionSettings = |
| 55 | mapAttrs' (name: e: nameValuePair e.id (settingsFor name e)) exts |
| 56 | // optionalAttrs cfg.exclusive { "*".installation_mode = "blocked"; }; |
| 57 | }; |
| 58 | |
| 59 | # Explicit GC root for every pinned XPI, independent of how the policy |
| 60 | # gets serialized on each platform. Also a handy place to inspect them. |
| 61 | home.file.".local/share/zen-extensions".source = pkgs.linkFarm "zen-extensions" |
| 62 | (mapAttrsToList (name: e: { name = "${name}.xpi"; path = xpi name e; }) exts); |
| 63 | |
| 64 | # The policy engine refuses downgrades (installed > pinned is cancelled in |
| 65 | # PoliciesHelpers installAddonFromURL). For exactly that case, delete the |
| 66 | # installed XPI while Zen is closed; the next start drops it from the DB |
| 67 | # and the policy installs the pin. Upgrades are left to the policy engine. |
| 68 | programs.zen-browser.activationFragments.default = [{ |
| 69 | requiresLock = true; |
| 70 | skipSubject = "zen extension downgrade check"; |
| 71 | text = '' |
| 72 | prof="${zen.profilesPath}/${zen.profiles.default.path}" |
| 73 | if [ -f "$prof/extensions.json" ]; then |
| 74 | while IFS=$'\t' read -r id want have; do |
| 75 | [ -n "$have" ] && [ "$have" != "$want" ] || continue |
| 76 | newest="$(printf '%s\n%s\n' "$want" "$have" | ${sort} -V | tail -n1)" |
| 77 | [ "$newest" = "$have" ] || continue |
| 78 | echo "zen-extensions: $id installed $have > pinned $want; removing installed XPI" |
| 79 | [[ -v DRY_RUN ]] || rm -f "$prof/extensions/$id.xpi" |
| 80 | done < <(${jq} -r --slurpfile ej "$prof/extensions.json" ' |
| 81 | .[] | . as $p |
| 82 | | [ $p.id, $p.version, |
| 83 | ([ $ej[0].addons[] | select(.id == $p.id and .location == "app-profile") | .version ][0] // "") ] |
| 84 | | @tsv' ${pins}) |
| 85 | fi |
| 86 | ''; |
| 87 | }]; |
| 88 | }; |
| 89 | } |