firefox-extensions: refactor

remove the custom scope-level logic, and create the package set same way i do with normal pkgs/
This commit is contained in:
2025-04-27 03:35:44 +00:00
parent a8a6cd2973
commit 6693333a84
31 changed files with 462 additions and 339 deletions

View File

@@ -1,20 +0,0 @@
{ stdenv
, open-in-mpv
, zip
}:
stdenv.mkDerivation {
pname = "open-in-mpv-firefox";
inherit (open-in-mpv) version src;
nativeBuildInputs = [ zip ];
installPhase = ''
runHook preInstall
install build/firefox.zip $out
runHook postInstall
'';
makeFlags = [ "build/firefox.zip" ];
passthru.extid = "{d66c8515-1e0d-408f-82ee-2682f2362726}";
}

View File

@@ -1,258 +0,0 @@
{
callPackage,
concatTextFile,
fetchurl,
genericUpdater,
jq,
lib,
newScope,
nix-update-script,
runCommand,
static-nix-shell,
stdenv,
strip-nondeterminism,
unzip,
writers,
writeShellScript,
zip,
}:
let
addon-version-lister = static-nix-shell.mkYsh {
pname = "addon-version-lister";
pkgs = [ "common-updater-scripts" "coreutils" "curl" ];
srcRoot = ./.;
};
wrapAddon = addon: args:
let
extid = addon.passthru.extid;
# merge our requirements into the derivation args
args' = args // {
passthru = (builtins.removeAttrs addon.passthru ["updateScript"]) // (args.passthru or {});
nativeBuildInputs = [
jq
strip-nondeterminism
unzip
zip
] ++ (args.nativeBuildInputs or []);
};
in (stdenv.mkDerivation ({
# heavily borrows from <repo:nixos/nixpkgs:pkgs/build-support/fetchfirefoxaddon/default.nix>
name = "${addon.name}-wrapped";
unpackPhase = ''
echo "patching firefox addon $name into $out/${extid}.xpi"
mkdir build
cd build
# extract the XPI into the build directory
# it could be already wrapped, or a raw fetchurl result
unzip -q "${addon}/${extid}.xpi" -d . || \
unzip -q "${addon}" -d .
'';
patchPhase = ''
runHook prePatch
# firefox requires addons to have an id field when sideloading:
# - <https://extensionworkshop.com/documentation/publish/distribute-sideloading/>
for m in manifest.json manifest_v2.json manifest_v3.json; do
if test -e "$m"; then
NEW_MANIFEST=$(jq '. + {"applications": { "gecko": { "id": "${extid}" }}, "browser_specific_settings":{"gecko":{"id": "${extid}"}}}' "$m")
echo "$NEW_MANIFEST" > "$m"
fi
done
runHook postPatch
'';
installPhase = ''
runHook preInstall
# repackage the XPI
mkdir "$out"
zip -r -q -FS "$out/${extid}.xpi" ./*
strip-nondeterminism "$out/${extid}.xpi"
runHook postInstall
'';
} // args')).overrideAttrs (final: upstream: {
passthru = (upstream.passthru or {}) // {
unwrapped = addon;
withAttrs = attrs: wrapAddon addon (args // attrs);
withPostPatch = postPatch: final.passthru.withAttrs { inherit postPatch; };
withPassthru = passthru: (wrapAddon addon args).overrideAttrs (base: {
passthru = base.passthru // passthru;
});
# given an addon, repackage it without some `perm`ission
withoutPermission = perm: final.passthru.withPostPatch ''
for m in manifest.json manifest_v2.json manifest_v3.json; do
if test -e "$m"; then
NEW_MANIFEST=$(jq 'del(.permissions[] | select(. == "${perm}"))' "$m")
echo "$NEW_MANIFEST" > "$m"
fi
done
'';
};
});
# fetchAddon: fetch an addon directly from the mozilla store.
# prefer NOT to use this, because moz store doesn't offer versioned release access
# which breaks caching/reproducibility and such.
# (maybe the `latest.xpi` URL redirects to a versioned URI visible if i used curl?)
# fetchAddon = name: extid: hash: fetchurl {
# inherit name hash;
# url = "https://addons.mozilla.org/firefox/downloads/latest/${name}/latest.xpi";
# # extid can be found by unar'ing the above xpi, and copying browser_specific_settings.gecko.id field
# passthru = { inherit extid; };
# };
fetchVersionedAddon = { extid, version, url, hash ? "", pname ? extid }: stdenv.mkDerivation {
inherit pname version;
src = fetchurl {
inherit url hash;
};
dontUnpack = true;
installPhase = ''
cp $src $out
'';
passthru.updateScript = genericUpdater {
versionLister = writeShellScript "${pname}-version-lister" ''
${lib.getExe addon-version-lister} --verbose --old-version "$UPDATE_NIX_OLD_VERSION" ${url}
'';
ignoredVersions = "(b|rc)[0-9]*$";
};
passthru.extid = extid;
};
firefox-extensions = (lib.makeScope newScope (self: with self; {
inherit addon-version-lister;
unwrapped = lib.recurseIntoAttrs {
# get names from:
# - ~/ref/nix-community/nur-combined/repos/rycee/pkgs/firefox-addons/generated-firefox-addons.nix
# `wget ...xpi`; `unar ...xpi`; `cat */manifest.json | jq '.browser_specific_settings.gecko.id'`
browserpass-extension = callPackage ./browserpass-extension { };
bypass-paywalls-clean = callPackage ./bypass-paywalls-clean { };
ctrl-shift-c-should-copy = callPackage ./ctrl-shift-c-should-copy { };
default-zoom = callPackage ./default-zoom { };
firefox-xdg-open = callPackage ./firefox-xdg-open { };
i-still-dont-care-about-cookies = callPackage ./i-still-dont-care-about-cookies { };
# open-in-mpv = callPackage ./open-in-mpv { };
passff = callPackage ./passff { };
privacypass-extension = callPackage ./privacypass-extension { };
sidebery = callPackage ./sidebery { };
# ether-metamask = fetchVersionedAddon rec {
# extid = "webextension@metamask.io";
# pname = "ether-metamask";
# url = "https://github.com/MetaMask/metamask-extension/releases/download/v${version}/metamask-firefox-${version}.zip";
# version = "12.6.0";
# hash = "sha256-6A8xsXDjX2dOYUbcxezr0tJsUMwzDf64n2PaJ55HPcM=";
# };
# fx_cast = fetchVersionedAddon rec {
# extid = "fx_cast@matt.tf";
# pname = "fx_cast";
# url = "https://github.com/hensm/fx_cast/releases/download/v${version}/fx_cast-${version}.xpi";
# version = "0.3.1";
# hash = "sha256-zaYnUJpJkRAPSCpM3S20PjMS4aeBtQGhXB2wgdlFkSQ=";
# };
# i2p-in-private-browsing = fetchVersionedAddon rec {
# extid = "i2ppb@eyedeekay.github.io";
# pname = "i2p-in-private-browsing";
# url = "https://github.com/eyedeekay/I2P-in-Private-Browsing-Mode-Firefox/releases/download/${version}/i2ppb@eyedeekay.github.io.xpi";
# version = "2.7.1";
# hash = "sha256-e68O9S2hgG6ob9GR10BCLk0G0z0o/mjRBxWhLes5+Kk=";
# };
sponsorblock = fetchVersionedAddon rec {
extid = "sponsorBlocker@ajay.app";
pname = "sponsorblock";
url = "https://github.com/ajayyy/SponsorBlock/releases/download/${version}/FirefoxSignedInstaller.xpi";
version = "5.12.1";
hash = "sha256-boZskrdec2qh1cfe3NkjWKPk00m5qEzrtEqs2v+V1ag=";
};
ublacklist = fetchVersionedAddon rec {
extid = "@ublacklist";
pname = "ublacklist";
url = "https://github.com/iorate/ublacklist/releases/download/v${version}/ublacklist-v${version}-firefox.zip";
version = "8.9.2";
hash = "sha256-b+MBSeh+YC/qT88pupElPU531YyfvYe1aC1icHXfK3A=";
};
ublock-origin = fetchVersionedAddon rec {
extid = "uBlock0@raymondhill.net";
pname = "ublock-origin";
# N.B.: the release process seems to be to first release an unsigned .xpi,
# then sign it a few days later,
# and then REMOVE THE UNSIGNED RELEASE.
# therefore, only grab signed releases, to avoid having the artifact disappear out from under us :(
# url = "https://github.com/gorhill/uBlock/releases/download/${version}/uBlock0_${version}.firefox.xpi";
url = "https://github.com/gorhill/uBlock/releases/download/${version}/uBlock0_${version}.firefox.signed.xpi";
version = "1.63.2";
hash = "sha256-2TF2zvTcBC5BulAKoqkOXVe1vndEnL1SIRFYXjoM0Vg=";
};
};
}) ).overrideScope (self: super:
let
wrapped = lib.mapAttrs (name: _value: wrapAddon self.unwrapped."${name}" {}) super.unwrapped;
in wrapped // {
browserpass-extension = wrapped.browserpass-extension.withoutPermission "notifications";
sponsorblock = wrapped.sponsorblock.withPostPatch ''
# patch sponsorblock to not show the help tab on first launch.
#
# XXX: i tried to build sponsorblock from source and patch this *before* it gets webpack'd,
# but web shit is absolutely cursed and building from source requires a fucking PhD
# (if you have one, feel free to share your nix package)
#
# NB: in source this is `alreadyInstalled: false`, but the build process hates Booleans or something
# TODO(2024/03/23): this is broken (replacement doesn't match). but maybe not necessary anymore?
substituteInPlace js/*.js \
--replace 'alreadyInstalled:!1' 'alreadyInstalled:!0'
'';
ublock-origin = wrapped.ublock-origin.withPassthru {
# `makeConfig` produces a .json file meant to go at
# ~/.mozilla/managed-storage/uBlock0@raymondhill.net.json
# this is not formally documented anywhere, but is referenced from a few places:
# - <https://github.com/gorhill/uBlock/issues/2986#issuecomment-364035002>
# - <https://www.reddit.com/r/uBlockOrigin/comments/16bzb11/configuring_ublock_origin_for_nix_users_just_in/>
# - <https://www.reddit.com/r/sysadmin/comments/8lwmbo/guide_deploying_ublock_origin_with_preset/>
#
# a large part of why i do this is to configure the filters statically,
# so that they don't have to be fetched on every boot.
makeConfig = { filterFiles }: let
mergedFilters = concatTextFile {
name = "ublock-origin-filters-merged.txt";
files = filterFiles;
destination = "/share/filters/ublock-origin-filters-merged.txt";
};
baseConfig = writers.writeJSON "uBlock0@raymondhill.net.json" {
name = "uBlock0@raymondhill.net";
description = "ignored";
type = "storage";
data = {
adminSettings = {
#^ adminSettings dictionary uses the same schema as the "backup to file" option in settings.
userSettings = {
# default settings are found: <repo:gorhill/uBlock:src/js/background.js> (userSettingsDefault)
advancedUserEnabled = true;
autoUpdate = false;
# don't block page load when waiting for filter load
suspendUntilListsAreLoaded = false;
};
selectedFilterLists = [ "user-filters" ];
# there's an array version of this field too, if preferable
filters = ""; #< WILL BE SUBSTITUTED DURING BUILD
};
};
};
in runCommand "ublock-origin-config" {
preferLocalBuild = true;
nativeBuildInputs = [ jq ];
} ''
cat ${baseConfig} | jq 'setpath(["data", "adminSettings", "userFilters"]; $filterText)' --rawfile filterText ${mergedFilters}/share/filters/ublock-origin-filters-merged.txt > $out
'';
};
}
);
in
lib.recurseIntoAttrs firefox-extensions

View File

@@ -53,6 +53,14 @@ let
};
});
### FIREFOX EXTENSIONS
# build like `nix-build -A firefox-extensions.default-zoom`.
# doesn't *need* to be its own scope, but this style of organization makes it easier to track.
firefox-extensions = lib.filesystem.packagesFromDirectoryRecursive {
inherit callPackage newScope;
directory = ./firefox-extensions;
};
### aliases
inherit (trivial-builders)
copyIntoOwnPackage

View File

@@ -0,0 +1,13 @@
{
addon-version-lister,
genericUpdater,
lib,
writeShellScript,
}:
genericUpdater {
versionLister = writeShellScript "version-lister" ''
VERSION_LISTER_URL=$(nix-instantiate --eval . -A "''${UPDATE_NIX_ATTR_PATH}.src.url")
${lib.getExe addon-version-lister} --verbose --old-version "$UPDATE_NIX_OLD_VERSION" "$VERSION_LISTER_URL"
'';
ignoredVersions = "(b|rc)[0-9]*$";
}

View File

@@ -0,0 +1,8 @@
{
static-nix-shell
}:
static-nix-shell.mkYsh {
pname = "addon-version-lister";
pkgs = [ "common-updater-scripts" "coreutils" "curl" ];
srcRoot = ./.;
}

View File

@@ -1,10 +1,14 @@
{ stdenv
, fetchFromGitHub
, fetchFromGitea
, fetchYarnDeps
, mkYarnModules
, nodejs
, zip
{
addon-git-updater,
fetchFromGitHub,
fetchFromGitea,
fetchYarnDeps,
lib,
mkYarnModules,
nodejs,
stdenv,
wrapFirefoxAddonsHook,
zip,
}:
let
@@ -37,7 +41,11 @@ let
in stdenv.mkDerivation {
inherit pname src version;
nativeBuildInputs = [ nodejs zip ];
nativeBuildInputs = [
nodejs
wrapFirefoxAddonsHook
zip
];
postPatch = ''
# dependencies are built separately: skip the yarn install
@@ -59,12 +67,28 @@ in stdenv.mkDerivation {
installPhase = ''
pushd firefox
zip -r $out ./*
mkdir $out
zip -r $out/$extid.xpi ./*
popd
'';
extid = "browserpass@maximbaz.com";
keepFirefoxPermissions = [
"activeTab"
"alarms"
"tabs"
"clipboardRead"
"clipboardWrite"
"nativeMessaging"
# "notifications" #< remove `notifications` perm, else it spams info for where to file bug reports, etc, on first launch
"webRequest"
"webRequestBlocking"
"http://*/*"
"https://*/*"
];
passthru = {
yarn-modules = browserpass-extension-yarn-modules;
extid = "browserpass@maximbaz.com";
updateScript = addon-git-updater;
};
}

View File

@@ -1,8 +1,9 @@
{ lib
, fetchFromGitLab
, gitUpdater
, stdenv
, zip
{
lib,
fetchFromGitLab,
stdenv,
wrapFirefoxAddonsHook,
zip,
}:
stdenv.mkDerivation rec {
@@ -30,14 +31,21 @@ stdenv.mkDerivation rec {
--replace-fail ' fetch(' ' false && fetch('
'';
nativeBuildInputs = [ zip ];
nativeBuildInputs = [
wrapFirefoxAddonsHook
zip
];
installPhase = ''
zip -r $out ./*
runHook preInstall
mkdir $out
zip -r $out/$extid.xpi ./*
runHook postInstall
'';
extid = "magnolia@12.34";
passthru = {
extid = "magnolia@12.34";
# XXX: disabled because the upstream repo has disappeared, and gitlab auth hangs the updater
# updateScript = gitUpdater {
# rev-prefix = "v";

View File

@@ -1,7 +1,10 @@
{ stdenv, lib
, fetchFromGitHub
, unstableGitUpdater
, zip
{
fetchFromGitHub,
lib,
stdenv,
unstableGitUpdater,
wrapFirefoxAddonsHook,
zip,
}:
stdenv.mkDerivation {
@@ -15,18 +18,27 @@ stdenv.mkDerivation {
hash = "sha256-8v/b8nft7WmPOKwOR27DPG/Z9rAEPKBP4YODM+Wg8Rk=";
};
nativeBuildInputs = [ zip ];
nativeBuildInputs = [
wrapFirefoxAddonsHook
zip
];
buildPhase = ''
runHook preBuild
zip -r extension.zip ./*
runHook postBuild
'';
installPhase = ''
install extension.zip $out
runHook preInstall
mkdir $out
install extension.zip $out/$extid.xpi
runHook postInstall
'';
extid = "ctrl-shift-c-copy@jeffersonscher.com";
passthru = {
extid = "ctrl-shift-c-copy@jeffersonscher.com";
updateScript = unstableGitUpdater { };
};

View File

@@ -1,6 +1,7 @@
# inlined/simplified version of <https://github.com/jamielinux/default-zoom>
{
stdenvNoCC,
wrapFirefoxAddonsHook,
zip,
}:
stdenvNoCC.mkDerivation {
@@ -8,7 +9,10 @@ stdenvNoCC.mkDerivation {
version = "0.1";
src = ./.;
nativeBuildInputs = [ zip ];
nativeBuildInputs = [
wrapFirefoxAddonsHook
zip
];
buildPhase = ''
runHook preBuild
@@ -19,9 +23,10 @@ stdenvNoCC.mkDerivation {
installPhase = ''
runHook preInstall
install firefox.zip $out
mkdir $out
install firefox.zip $out/$extid.xpi
runHook postInstall
'';
passthru.extid = "default-zoom@uninsane.org";
extid = "default-zoom@uninsane.org";
}

View File

@@ -3,6 +3,7 @@
makeDesktopItem,
static-nix-shell,
stdenvNoCC,
wrapFirefoxAddonsHook,
zip,
}:
stdenvNoCC.mkDerivation {
@@ -10,7 +11,10 @@ stdenvNoCC.mkDerivation {
version = "0.1";
src = ./.;
nativeBuildInputs = [ zip ];
nativeBuildInputs = [
wrapFirefoxAddonsHook
zip
];
buildPhase = ''
runHook preBuild
@@ -21,11 +25,13 @@ stdenvNoCC.mkDerivation {
installPhase = ''
runHook preInstall
install firefox.zip $out
mkdir $out
install firefox.zip $out/$extid.xpi
runHook postInstall
'';
passthru.extid = "@firefox-xdg-open";
extid = "@firefox-xdg-open";
passthru.systemComponent = static-nix-shell.mkBash {
pname = "xdg-open-scheme-handler";
src = ./.;

View File

@@ -1,8 +1,10 @@
{ stdenv
, lib
, fetchFromGitHub
, gitUpdater
, zip
{
fetchFromGitHub,
gitUpdater,
lib,
stdenv,
wrapFirefoxAddonsHook,
zip,
}:
stdenv.mkDerivation rec {
pname = "i-still-dont-care-about-cookies";
@@ -14,7 +16,10 @@ stdenv.mkDerivation rec {
hash = "sha256-bs9Looh2fKmsT0/3rS5Ldta4wlOUc75DpGxwBc7yRmg=";
};
nativeBuildInputs = [ zip ];
nativeBuildInputs = [
wrapFirefoxAddonsHook
zip
];
postPatch = ''
# firefox claims to support manifest v3, but actually it won't load unless i point it to v2.
@@ -22,16 +27,18 @@ stdenv.mkDerivation rec {
'';
installPhase = ''
runHook preInstall
pushd src
zip -r $out ./*
mkdir $out
zip -r $out/$extid.xpi ./*
popd
runHook postInstall
'';
passthru = {
extid = "idcac-pub@guus.ninja";
updateScript = gitUpdater {
rev-prefix = "v";
};
extid = "idcac-pub@guus.ninja";
passthru.updateScript = gitUpdater {
rev-prefix = "v";
};
meta = {

View File

@@ -0,0 +1,29 @@
{
pkgs, # for outer `open-in-mpv`
stdenv,
wrapFirefoxAddonsHook,
zip,
}:
stdenv.mkDerivation {
pname = "open-in-mpv-firefox";
inherit (pkgs.open-in-mpv) version src;
nativeBuildInputs = [
wrapFirefoxAddonsHook
zip
];
installPhase = ''
runHook preInstall
mkdir $out
install build/firefox.zip $out/$extid.xpi
runHook postInstall
'';
makeFlags = [
"BUILD_DIR=build"
"build/firefox.zip"
];
extid = "{d66c8515-1e0d-408f-82ee-2682f2362726}";
}

View File

@@ -2,6 +2,7 @@
fetchFromGitea,
gitUpdater,
stdenv,
wrapFirefoxAddonsHook,
zip,
}:
stdenv.mkDerivation rec {
@@ -15,7 +16,10 @@ stdenv.mkDerivation rec {
hash = "sha256-XtKrVrXpvsz/7XaGiYW0dxRZr7wLGNK+C6c9BHqY7Gw=";
};
nativeBuildInputs = [ zip ];
nativeBuildInputs = [
wrapFirefoxAddonsHook
zip
];
makeFlags = [
"VERSION=${version}"
@@ -23,10 +27,12 @@ stdenv.mkDerivation rec {
installPhase = ''
runHook preInstall
install bin/$version/passff.xpi $out
mkdir $out
install bin/$version/passff.xpi $out/$extid.xpi
runHook postInstall
'';
extid = "passff@invicem.pro";
passthru.updateScript = gitUpdater { };
passthru.extid = "passff@invicem.pro";
}

View File

@@ -9,6 +9,7 @@
jq,
stdenv,
wasm-pack,
wrapFirefoxAddonsHook,
zip,
}:
stdenv.mkDerivation rec {
@@ -29,6 +30,7 @@ stdenv.mkDerivation rec {
cargo
jq
wasm-pack
wrapFirefoxAddonsHook
zip
];
@@ -43,7 +45,8 @@ stdenv.mkDerivation rec {
installPhase = ''
runHook preInstall
install build/firefox/kagi_privacypass_firefox_1.0.5.xpi $out
mkdir $out
install build/firefox/kagi_privacypass_firefox_1.0.5.xpi $out/$extid.xpi
runHook postInstall
'';
@@ -61,8 +64,7 @@ stdenv.mkDerivation rec {
doCheck = true;
passthru = {
extid = "privacypass@kagi.com";
updateScript = gitUpdater {};
};
extid = "privacypass@kagi.com";
passthru.updateScript = gitUpdater {};
}

View File

@@ -1,7 +1,9 @@
{ lib
, buildNpmPackage
, fetchFromGitHub
, gitUpdater
{
buildNpmPackage,
fetchFromGitHub,
gitUpdater,
lib,
wrapFirefoxAddonsHook,
}:
buildNpmPackage rec {
pname = "sidebery";
@@ -15,19 +17,23 @@ buildNpmPackage rec {
npmDepsHash = "sha256-YRfKI61RPvRUdgUElGgPolYNiUmd7S7uV2Fyb+ThOCM=";
nativeBuildInputs = [
wrapFirefoxAddonsHook
];
postBuild = ''
npm run build.ext
'';
installPhase = ''
cp dist/* "$out"
mkdir $out
cp dist/* $out/$extid.xpi
'';
passthru = {
extid = "{3c078156-979c-498b-8990-85f7987dd929}";
updateScript = gitUpdater {
rev-prefix = "v";
};
extid = "{3c078156-979c-498b-8990-85f7987dd929}";
passthru.updateScript = gitUpdater {
rev-prefix = "v";
};
meta = {

View File

@@ -0,0 +1,52 @@
{
addon-git-updater,
fetchurl,
lib,
stdenv,
unzip,
wrapFirefoxAddonsHook,
zip,
}:
stdenv.mkDerivation rec {
pname = "sponsorblock";
version = "5.12.1";
src = fetchurl {
url = "https://github.com/ajayyy/SponsorBlock/releases/download/${version}/FirefoxSignedInstaller.xpi";
hash = "sha256-boZskrdec2qh1cfe3NkjWKPk00m5qEzrtEqs2v+V1ag=";
name = "FirefoxSignedInstaller.zip";
};
# .zip file has everything in the top-level; stdenv needs it to be extracted into a subdir:
sourceRoot = ".";
preUnpack = ''
mkdir src && cd src
'';
postPatch = ''
# patch sponsorblock to not show the help tab on first launch.
#
# XXX: i tried to build sponsorblock from source and patch this *before* it gets webpack'd,
# but web shit is absolutely cursed and building from source requires a fucking PhD
# (if you have one, feel free to share your nix package)
#
# NB: in source this is `alreadyInstalled: false`, but the build process hates Booleans or something
# TODO(2024/03/23): this is broken (replacement doesn't match). but maybe not necessary anymore?
substituteInPlace js/*.js \
--replace 'alreadyInstalled:!1' 'alreadyInstalled:!0'
'';
installPhase = ''
runHook preInstall
mkdir $out
zip -r -FS $out/$extid.xpi .
runHook postInstall
'';
nativeBuildInputs = [
unzip # for unpackPhase
wrapFirefoxAddonsHook
zip
];
extid = "sponsorBlocker@ajay.app";
passthru.updateScript = addon-git-updater;
}

View File

@@ -0,0 +1,37 @@
{
addon-git-updater,
fetchurl,
stdenv,
unzip,
wrapFirefoxAddonsHook,
zip,
}:
stdenv.mkDerivation rec {
pname = "ublacklist";
version = "8.9.2";
src = fetchurl {
url = "https://github.com/iorate/ublacklist/releases/download/v${version}/ublacklist-v${version}-firefox.zip";
hash = "sha256-b+MBSeh+YC/qT88pupElPU531YyfvYe1aC1icHXfK3A=";
};
# .zip file has everything in the top-level; stdenv needs it to be extracted into a subdir:
sourceRoot = ".";
preUnpack = ''
mkdir src && cd src
'';
installPhase = ''
runHook preInstall
mkdir $out
zip -r -FS $out/$extid.xpi .
runHook postInstall
'';
nativeBuildInputs = [
unzip # for unpackPhase
wrapFirefoxAddonsHook
zip
];
extid = "@ublacklist";
passthru.updateScript = addon-git-updater;
}

View File

@@ -0,0 +1,92 @@
{
addon-git-updater,
concatTextFile,
fetchurl,
jq,
runCommand,
stdenv,
unzip,
wrapFirefoxAddonsHook,
writers,
zip,
}:
stdenv.mkDerivation rec {
pname = "ublock-origin";
version = "1.63.2";
src = fetchurl {
# N.B.: the release process seems to be to first release an unsigned .xpi,
# then sign it a few days later,
# and then REMOVE THE UNSIGNED RELEASE.
# therefore, only grab signed releases, to avoid having the artifact disappear out from under us :(
# url = "https://github.com/gorhill/uBlock/releases/download/${version}/uBlock0_${version}.firefox.xpi";
url = "https://github.com/gorhill/uBlock/releases/download/${version}/uBlock0_${version}.firefox.signed.xpi";
hash = "sha256-2TF2zvTcBC5BulAKoqkOXVe1vndEnL1SIRFYXjoM0Vg=";
name = "uBlock0_${version}.firefox.signed.zip";
};
# .zip file has everything in the top-level; stdenv needs it to be extracted into a subdir:
sourceRoot = ".";
preUnpack = ''
mkdir src && cd src
'';
installPhase = ''
runHook preInstall
mkdir $out
zip -r -FS $out/$extid.xpi .
runHook postInstall
'';
nativeBuildInputs = [
unzip # for unpackPhase
wrapFirefoxAddonsHook
zip
];
extid = "uBlock0@raymondhill.net";
passthru = {
updateScript = addon-git-updater;
# `makeConfig` produces a .json file meant to go at
# ~/.mozilla/managed-storage/uBlock0@raymondhill.net.json
# this is not formally documented anywhere, but is referenced from a few places:
# - <https://github.com/gorhill/uBlock/issues/2986#issuecomment-364035002>
# - <https://www.reddit.com/r/uBlockOrigin/comments/16bzb11/configuring_ublock_origin_for_nix_users_just_in/>
# - <https://www.reddit.com/r/sysadmin/comments/8lwmbo/guide_deploying_ublock_origin_with_preset/>
#
# a large part of why i do this is to configure the filters statically,
# so that they don't have to be fetched on every boot.
makeConfig = { filterFiles }: let
mergedFilters = concatTextFile {
name = "ublock-origin-filters-merged.txt";
files = filterFiles;
destination = "/share/filters/ublock-origin-filters-merged.txt";
};
baseConfig = writers.writeJSON "uBlock0@raymondhill.net.json" {
name = "uBlock0@raymondhill.net";
description = "ignored";
type = "storage";
data = {
adminSettings = {
#^ adminSettings dictionary uses the same schema as the "backup to file" option in settings.
userSettings = {
# default settings are found: <repo:gorhill/uBlock:src/js/background.js> (userSettingsDefault)
advancedUserEnabled = true;
autoUpdate = false;
# don't block page load when waiting for filter load
suspendUntilListsAreLoaded = false;
};
selectedFilterLists = [ "user-filters" ];
# there's an array version of this field too, if preferable
filters = ""; #< WILL BE SUBSTITUTED DURING BUILD
};
};
};
in runCommand "ublock-origin-config" {
preferLocalBuild = true;
nativeBuildInputs = [ jq ];
} ''
cat ${baseConfig} | jq 'setpath(["data", "adminSettings", "userFilters"]; $filterText)' --rawfile filterText ${mergedFilters}/share/filters/ublock-origin-filters-merged.txt > $out
'';
};
}

View File

@@ -0,0 +1,19 @@
{
findutils,
jq,
lib,
makeSetupHook,
strip-nondeterminism,
unzip,
zip,
}:
makeSetupHook {
name = "wrap-firefox-addons-hook";
substitutions = {
find = lib.getExe' findutils "find";
jq = lib.getExe jq;
strip_nondeterminism = lib.getExe strip-nondeterminism;
unzip = lib.getExe unzip;
zip = lib.getExe zip;
};
} ./wrap-firefox-addons-hook.sh

View File

@@ -0,0 +1,67 @@
patchManifest() {
local m=$1
echo "patching manifest at $m to use extid $extid"
local newManifest=$(@jq@ '. + {"applications": { "gecko": { "id": "'"$extid"'" }}, "browser_specific_settings":{"gecko":{"id": "'"$extid"'"}}}' "$m")
echo "$newManifest" > "$m"
echo "updating permissions for $m"
echo " old perms: $(@jq@ '.permissions' "$m")"
# if the user omits any specification, then keep all perms
# N.B.: `${X:-Y}` defaults to Y only if X is unset -- not if X is set but falsey.
# this allows the user to remove all perms by setting `keepFirefoxPermissions=`.
local keepPerms=${keepFirefoxPermissions:-.*}
# allow permissions to be specified as either regex OR literal
local jqKeepPermsRegexChoices=$(concatStringsSep '|' keepPerms)
local permsFromRegex=$(@jq@ '.permissions | map(select(test("^('"$jqKeepPermsRegexChoices"')$")))' "$m")
local jqKeepPermsArray='["'"$(concatStringsSep '", "' keepPerms)"'"]'
local permsFromLit=$(@jq@ ".permissions - (.permissions - $jqKeepPermsArray)" "$m")
local newPerms=$(@jq@ -n "$permsFromRegex + $permsFromLit | unique")
local newManifest=$(@jq@ '.permissions = '"$newPerms" "$m")
echo "$newManifest" > "$m"
echo " new perms: $(@jq@ '.permissions' "$m")"
}
fixupFirefoxAddon() {
local xpiPath=$1
local unpacked=$(mktemp -d)
# unpack the addon
echo "unpacking $xpiPath to $unpacked"
@unzip@ -q "$xpiPath" -d "$unpacked"
# patch the .xpi
# firefox requires addons to have an id field when sideloading:
# - <https://extensionworkshop.com/documentation/publish/distribute-sideloading/>
for m in manifest.json manifest_v2.json manifest_v3.json; do
if [ -e "$unpacked/$m" ]; then
patchManifest "$unpacked/$m"
fi
done
# repack the addon
echo "repacking $unpacked into $xpiPath XPI"
# change dir so as to pack into the toplevel
(cd "$unpacked"; @zip@ -r -q -FS "$xpiPath" .)
@strip_nondeterminism@ "$xpiPath"
}
fixupFirefoxAddons() {
if [ -f "$out" ]; then
# the output is a single firefox extension (legacy)
# TODO: remove this code path
fixupFirefoxAddon "$out"
else
# crawl the output for .xpi files to fixup
for xpi in $(@find@ "$out" -name '*.xpi'); do
fixupFirefoxAddon "$xpi"
done
fi
}
if [ -z "${dontFixupFirefoxAddons-}" ]; then
preFixupHooks+=('fixupFirefoxAddons')
fi