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

aeb9107982a669bb3d2f270a6d8e78eb021f6501 · 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 }