From 367b0c114f38d5c332f5ee971ad13dd69e302dec Mon Sep 17 00:00:00 2001 From: tilpner Date: Mon, 15 Jun 2020 09:53:06 +0200 Subject: WIP towards module based configuration --- nix/lib.nix | 307 +++++++++++++++++++++++++++++++------------------------- nix/options.nix | 66 ++++++++++++ 2 files changed, 234 insertions(+), 139 deletions(-) create mode 100644 nix/options.nix (limited to 'nix') diff --git a/nix/lib.nix b/nix/lib.nix index 179223b..0ca057d 100644 --- a/nix/lib.nix +++ b/nix/lib.nix @@ -1,147 +1,176 @@ { pkgs, lib }: with lib; -rec { - mozlz4 = pkgs.callPackage ./mozlz4.nix {}; - - flattenAttrs = set: - let recurse = k: v: - if isAttrs v - then mapAttrsToList (k': recurse "${k}.${k'}") v - else nameValuePair k v; - in listToAttrs (flatten (mapAttrsToList recurse set)); - - mkValueString = v: - if isInt v then toString v - else if isString v then "\"${v}\"" - else if true == v then "true" - else if false == v then "false" - else abort "unsupported value type: ${builtins.typeOf v}"; - - mkPrefs = settings: pkgs.writeText "prefs.js" - ("// dummy line\n" + - (lib.concatStringsSep "\n" - (lib.mapAttrsToList (k: v: "pref(\"${k}\", ${mkValueString v}, locked);") - (flattenAttrs settings)))); - - toSearchConfig = settings: pkgs.runCommand "search.json.mozlz4" {} '' - ${mozlz4.compress} < ${pkgs.writeText "search.json" (builtins.toJSON settings)} > $out - ''; - - profile = { preferences, search }: pkgs.runCommand "profile" {} '' - mkdir $out - ln -s ${toUserPrefs preferences} $out/user.js - # ln -s ${toSearchConfig search} $out/search.json.mozlz4 - cp ${../search-with-qj.json.mozlz4} $out/search.json.mozlz4 - ''; - - mkPolicies = policies: pkgs.writeText "policies.json" (builtins.toJSON { - inherit policies; - }); - - mergeProfiles = profiles: - let - sanitise = args: { - policies = args.policies or {}; - preferences = args.preferences or {}; +let + importProfiles = dir: + mapAttrs' + (name: type: + let + name' = builtins.replaceStrings [".nix"] [""] name; + in if type == "regular" then + nameValuePair name' (import (dir + ("/" + name))) + else if type == "directory" then + nameValuePair name' (pkgs.callPackage (dir + ("/" + name)) {}) + else + builtins.throw "Unexpected file type: ${type}") + (builtins.readDir dir); + + self = rec { + mozlz4 = pkgs.callPackage ./mozlz4.nix {}; + directory = pkgs.callPackage ./directory.nix {}; + + profileList = attrValues (directory.pathDirectory ../profiles); + + flattenAttrs = set: + let recurse = k: v: + if isAttrs v + then mapAttrsToList (k': recurse "${k}.${k'}") v + else nameValuePair k v; + in listToAttrs (flatten (mapAttrsToList recurse set)); + + mkValueString = v: + if isInt v then toString v + else if isString v then "\"${v}\"" + else if true == v then "true" + else if false == v then "false" + else abort "unsupported value type: ${builtins.typeOf v}"; + + mkPrefs = settings: pkgs.writeText "prefs.js" + ("// dummy line\n" + + (lib.concatStringsSep "\n" + (lib.mapAttrsToList (k: v: "pref(\"${k}\", ${mkValueString v}, locked);") + (flattenAttrs settings)))); + + toSearchConfig = settings: pkgs.runCommand "search.json.mozlz4" {} '' + ${mozlz4.compress} < ${pkgs.writeText "search.json" (builtins.toJSON settings)} > $out + ''; + + profile = { preferences, search }: pkgs.runCommand "profile" {} '' + mkdir $out + ln -s ${toUserPrefs preferences} $out/user.js + # ln -s ${toSearchConfig search} $out/search.json.mozlz4 + cp ${../search-with-qj.json.mozlz4} $out/search.json.mozlz4 + ''; + + mkPolicies = policies: pkgs.writeText "policies.json" (builtins.toJSON { + inherit policies; + }); + + optionsModule = import ./options.nix; + eval = root: evalModules { + modules = profileList ++ [ + optionsModule + root + ]; + + specialArgs = { + ff = self; }; - sanitised = map sanitise profiles; - final = lib.foldl lib.recursiveUpdate {} sanitised; - in final; - - patchOmniJa = src: script: pkgs.runCommand "omni.ja" { } '' - ${pkgs.unzip}/bin/unzip -q ${src} - - ${script} - - ${pkgs.zip}/bin/zip -qr9XD $out . - ''; - - bundle = { policies ? {}, preferences ? {}, patchOmniJaCommand ? "" }: - let - firefox = pkgs.firefox-unwrapped; - policies' = mkPolicies policies; - preferences' = mkPrefs preferences; - patchedOmniJa = patchOmniJa "${firefox}/lib/firefox/browser/omni.ja" patchOmniJaCommand; - patched = pkgs.runCommand "firefox-bundle" { - nativeBuildInputs = [ pkgs.nix ]; - disallowedReferences = [ firefox ]; - } '' - cp -r ${firefox} $out - chmod -R +w $out - # correct argv[0], which is used to locate distribution and defaults - substituteInPlace $out/bin/firefox \ - --replace ${firefox} $out - - mkdir $out/lib/firefox/distribution - cp ${policies'} $out/lib/firefox/distribution/policies.json - cp ${preferences'} $out/lib/firefox/defaults/pref/99-custom.js - cp ${patchedOmniJa} $out/lib/firefox/browser/omni.ja + }; + + docs = options: + let + optionsDoc = pkgs.nixosOptionsDoc { inherit options; }; + in pkgs.runCommand "manual.html" { nativeBuildInputs = [ pkgs.asciidoctor ]; } '' + asciidoctor \ + -o $out \ + ${builtins.toFile "manual.asciidoc" optionsDoc.optionsAsciiDoc} ''; - wrapped = (pkgs.wrapFirefox patched { - browserName = "firefox"; - version = "custom"; - }) // { inherit policies preferences; }; - in wrapped; - - # This attempts to provide a start for deploying Nix-configured profiles to - # systems which do not have Nix installed, and where /nix/store is undesired. - # It does this by bundling all referenced store items into $out, and rewriting - # references to those store items. - export = { selfPath, policies ? {}, preferences ? {} }: pkgs.stdenv.mkDerivation rec { - name = "firefox-profile-export"; - nativeBuildInputs = [ pkgs.jq ]; - - allowedRequisites = []; - - buildCommand = + + patchOmniJa = src: script: pkgs.runCommand "omni.ja" { } '' + ${pkgs.unzip}/bin/unzip -q ${src} + + ${script} + + ${pkgs.zip}/bin/zip -qr9XD $out . + ''; + + bundle = { policies ? {}, preferences ? {}, patchOmniJaCommand ? "" }: let - policyFile = mkPolicies policies; - prefsFile = mkPrefs preferences; - - closure = pkgs.closureInfo { - rootPaths = [ - policyFile prefsFile - ]; - }; - in '' - mkdir $out $out/store - - storePaths=$(cat ${closure}/store-paths) - for p in $storePaths; do - cp -a "$p" $out/store/"$(basename "$p" | sed -e 's|\([a-z0-9]\{32\}\)-||')" - done - - mv $out/store/prefs.js $out/ - jq < $out/store/policies.json > $out/policies.json - - find $out -type f -print0 | - xargs -0I{} -- sed -i -e "s|$NIX_STORE/\\([a-z0-9]\{32\}\\)-|${selfPath}/store/|g" "{}" + firefox = pkgs.firefox-unwrapped; + policies' = mkPolicies policies; + preferences' = mkPrefs preferences; + patchedOmniJa = patchOmniJa "${firefox}/lib/firefox/browser/omni.ja" patchOmniJaCommand; + patched = pkgs.runCommand "firefox-bundle" { + nativeBuildInputs = [ pkgs.nix ]; + disallowedReferences = [ firefox ]; + } '' + cp -r ${firefox} $out + chmod -R +w $out + # correct argv[0], which is used to locate distribution and defaults + substituteInPlace $out/bin/firefox \ + --replace ${firefox} $out + + mkdir $out/lib/firefox/distribution + cp ${policies'} $out/lib/firefox/distribution/policies.json + cp ${preferences'} $out/lib/firefox/defaults/pref/99-custom.js + cp ${patchedOmniJa} $out/lib/firefox/browser/omni.ja + ''; + wrapped = (pkgs.wrapFirefox patched { + browserName = "firefox"; + version = "custom"; + }) // { inherit policies preferences; }; + in wrapped; + + # This attempts to provide a start for deploying Nix-configured profiles to + # systems which do not have Nix installed, and where /nix/store is undesired. + # It does this by bundling all referenced store items into $out, and rewriting + # references to those store items. + export = { selfPath, policies ? {}, preferences ? {} }: pkgs.stdenv.mkDerivation rec { + name = "firefox-profile-export"; + nativeBuildInputs = [ pkgs.jq ]; + + allowedRequisites = []; + + buildCommand = + let + policyFile = mkPolicies policies; + prefsFile = mkPrefs preferences; + + closure = pkgs.closureInfo { + rootPaths = [ + policyFile prefsFile + ]; + }; + in '' + mkdir $out $out/store + + storePaths=$(cat ${closure}/store-paths) + for p in $storePaths; do + cp -a "$p" $out/store/"$(basename "$p" | sed -e 's|\([a-z0-9]\{32\}\)-||')" + done + + mv $out/store/prefs.js $out/ + jq < $out/store/policies.json > $out/policies.json + + find $out -type f -print0 | + xargs -0I{} -- sed -i -e "s|$NIX_STORE/\\([a-z0-9]\{32\}\\)-|${selfPath}/store/|g" "{}" + ''; + }; + + launcher = firefox: pkgs.writeShellScriptBin "firefox" '' + # FF doesn't accept ro profiles, tries to create lockfile + TMP_PROFILE="$(mktemp -d)" + echo "$TMP_PROFILE" + + function finish() { + echo deleting profile + # rm -rv "$TMP_PROFILE" + } + trap finish EXIT + # cp -r ''${profile} "$TMP_PROFILE" + chmod u+rwx -R "$TMP_PROFILE" + + export MOZ_LOG=nsHttp:1 + export MOZ_LOG_FILE=ff + # Make FF pay attention to services.settings.server + # https://bugzilla.mozilla.org/show_bug.cgi?id=1598562 + export XPCSHELL_TEST_PROFILE_DIR=1 + + ${firefox}/bin/firefox \ + --no-remote \ + --profile "$TMP_PROFILE" \ + "$@" ''; }; - - launcher = firefox: pkgs.writeShellScriptBin "firefox" '' - # FF doesn't accept ro profiles, tries to create lockfile - TMP_PROFILE="$(mktemp -d)" - echo "$TMP_PROFILE" - - function finish() { - echo deleting profile - # rm -rv "$TMP_PROFILE" - } - trap finish EXIT - # cp -r ''${profile} "$TMP_PROFILE" - chmod u+rwx -R "$TMP_PROFILE" - - export MOZ_LOG=nsHttp:1 - export MOZ_LOG_FILE=ff - # Make FF pay attention to services.settings.server - # https://bugzilla.mozilla.org/show_bug.cgi?id=1598562 - export XPCSHELL_TEST_PROFILE_DIR=1 - - ${firefox}/bin/firefox \ - --no-remote \ - --profile "$TMP_PROFILE" \ - "$@" - ''; -} +in self diff --git a/nix/options.nix b/nix/options.nix new file mode 100644 index 0000000..cbd21a5 --- /dev/null +++ b/nix/options.nix @@ -0,0 +1,66 @@ +{ options, pkgs, lib, ... }: + +with lib; +let + # https://github.com/NixOS/nixpkgs/pull/75584 + json = with lib.types; let + valueType = nullOr (oneOf [ + bool + int + float + str + (attrsOf valueType) + (listOf valueType) + ]) // { + description = "Boolean, Integer, Float, String, or lists or attribute sets of these types"; + }; + in valueType; +in { + options = { + meta.description = mkOption { + type = types.lines; + default = ""; + internal = true; + }; + + preferences = mkOption { + type = json; + default = {}; + + description = '' + These nested values are translated directly into Firefox preferences, as can be + found in user.js, prefs.js, or about:config. + ''; + + example = { + }; + }; + + policies = mkOption { + type = json; + default = {}; + + description = '' + These nested values must follow the pattern of https://github.com/mozilla/policy-templates/, + but compliance is not validated immediately. + ''; + + example = { + FirefoxHome = { + Pocket = false; + Locked = true; + }; + NewTabPage = false; + }; + }; + + omnija.browser.patchCommand = mkOption { + type = types.lines; + default = ""; + + description = '' + This is a shell script to be executed for patching omni.ja + ''; + }; + }; +} -- cgit v1.2.3