diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 27635498f54d..1ed8ae285d89 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -30,7 +30,7 @@ For new packages please briefly describe the package or provide a link to its ho - [NixOS 25.11 Release Notes](https://github.com/NixOS/nixpkgs/blob/master/nixos/doc/manual/release-notes/rl-2511.section.md) (or backporting [24.11](https://github.com/NixOS/nixpkgs/blob/master/nixos/doc/manual/release-notes/rl-2411.section.md) and [25.05](https://github.com/NixOS/nixpkgs/blob/master/nixos/doc/manual/release-notes/rl-2505.section.md) NixOS Release notes) - [ ] (Module updates) Added a release notes entry if the change is significant - [ ] (Module addition) Added a release notes entry if adding a new NixOS module -- [ ] Fits [CONTRIBUTING.md](https://github.com/NixOS/nixpkgs/blob/master/CONTRIBUTING.md). +- [ ] Fits [CONTRIBUTING.md](https://github.com/NixOS/nixpkgs/blob/master/CONTRIBUTING.md), [pkgs/README.md](https://github.com/NixOS/nixpkgs/blob/master/pkgs/README.md), [maintainers/README.md](https://github.com/NixOS/nixpkgs/blob/master/maintainers/README.md) and other contributing documentation in corresponding paths. diff --git a/maintainers/maintainer-list.nix b/maintainers/maintainer-list.nix index 58cf591b197f..6792993f7015 100644 --- a/maintainers/maintainer-list.nix +++ b/maintainers/maintainer-list.nix @@ -12543,6 +12543,13 @@ githubId = 39434424; name = "Felix Springer"; }; + junestepp = { + email = "git@junestepp.me"; + github = "junestepp"; + githubId = 26205306; + name = "June Stepp"; + keys = [ { fingerprint = "2561 0243 2233 CFE6 E13E 3C33 348C 6EB3 39AE C582"; } ]; + }; junjihashimoto = { email = "junji.hashimoto@gmail.com"; github = "junjihashimoto"; diff --git a/maintainers/team-list.nix b/maintainers/team-list.nix index 35aa999a58be..28f2f1007e40 100644 --- a/maintainers/team-list.nix +++ b/maintainers/team-list.nix @@ -915,6 +915,7 @@ with lib.maintainers; eljamm ethancedwards8 fricklerhandwerk + prince213 wegank ]; scope = "Maintain NGI-supported software."; diff --git a/nixos/doc/manual/release-notes/rl-2305.section.md b/nixos/doc/manual/release-notes/rl-2305.section.md index cae573c3452a..7ffe079977af 100644 --- a/nixos/doc/manual/release-notes/rl-2305.section.md +++ b/nixos/doc/manual/release-notes/rl-2305.section.md @@ -105,7 +105,7 @@ In addition to numerous new and updated packages, this release has the following - [ivpn](https://www.ivpn.net/), a secure, private VPN with fast WireGuard connections. Available as [services.ivpn](#opt-services.ivpn.enable). -- [vmalert](https://victoriametrics.com/), an alerting engine for VictoriaMetrics. Available as [services.vmalert](#opt-services.vmalert.enable). +- [vmalert](https://victoriametrics.com/), an alerting engine for VictoriaMetrics. Available as [services.vmalert.instances](#opt-services.vmalert.instances._name_.enable). - [jellyseerr](https://github.com/Fallenbagel/jellyseerr), a web-based requests manager for Jellyfin, forked from Overseerr. Available as [services.jellyseerr](#opt-services.jellyseerr.enable). diff --git a/nixos/modules/misc/man-db.nix b/nixos/modules/misc/man-db.nix index 56b935c97597..dc6af4fe9692 100644 --- a/nixos/modules/misc/man-db.nix +++ b/nixos/modules/misc/man-db.nix @@ -69,10 +69,19 @@ in environment.systemPackages = [ cfg.package ]; environment.etc."man_db.conf".text = let + # We unfortunately can’t use the customized `cfg.package` when + # cross‐compiling. Instead we detect that situation and work + # around it by using the vanilla one, like the OpenSSH module. + buildPackage = + if pkgs.stdenv.buildPlatform.canExecute pkgs.stdenv.hostPlatform then + cfg.package + else + pkgs.buildPackages.man-db; + manualCache = pkgs.runCommand "man-cache" { - nativeBuildInputs = [ cfg.package ]; + nativeBuildInputs = [ buildPackage ]; } '' echo "MANDB_MAP ${cfg.manualPages}/share/man $out" > man.conf diff --git a/nixos/modules/services/desktops/pipewire/pipewire.nix b/nixos/modules/services/desktops/pipewire/pipewire.nix index 5f1de7e63f3f..6e90855114e0 100644 --- a/nixos/modules/services/desktops/pipewire/pipewire.nix +++ b/nixos/modules/services/desktops/pipewire/pipewire.nix @@ -359,8 +359,7 @@ in systemd.services.pipewire.bindsTo = [ "dbus.service" ]; systemd.user.services.pipewire.bindsTo = [ "dbus.service" ]; - # Enable either system or user units. Note that for pipewire-pulse there - # are only user units, which work in both cases. + # Enable either system or user units. systemd.sockets.pipewire.enable = cfg.systemWide; systemd.services.pipewire.enable = cfg.systemWide; systemd.user.sockets.pipewire.enable = !cfg.systemWide; diff --git a/nixos/modules/services/monitoring/vmalert.nix b/nixos/modules/services/monitoring/vmalert.nix index 438d067643fb..0baaa039e395 100644 --- a/nixos/modules/services/monitoring/vmalert.nix +++ b/nixos/modules/services/monitoring/vmalert.nix @@ -10,9 +10,9 @@ let format = pkgs.formats.yaml { }; - confOpts = concatStringsSep " \\\n" ( - mapAttrsToList mkLine (filterAttrs (_: v: v != false) cfg.settings) - ); + mkConfOpts = + settings: + concatStringsSep " \\\n" (mapAttrsToList mkLine (filterAttrs (_: v: v != false) settings)); confType = with types; let @@ -33,124 +33,171 @@ let concatMapStringsSep " " (v: "-${key}=${escapeShellArg (toString v)}") value else "-${key}=${escapeShellArg (toString value)}"; + + vmalertName = name: "vmalert" + lib.optionalString (name != "") ("-" + name); + enabledInstances = lib.filterAttrs (name: conf: conf.enable) config.services.vmalert.instances; in { + imports = [ + (lib.mkRenamedOptionModule + [ "services" "vmalert" "enable" ] + [ "services" "vmalert" "instances" "" "enable" ] + ) + (lib.mkRenamedOptionModule + [ "services" "vmalert" "rules" ] + [ "services" "vmalert" "instances" "" "rules" ] + ) + (lib.mkRenamedOptionModule + [ "services" "vmalert" "settings" ] + [ "services" "vmalert" "instances" "" "settings" ] + ) + ]; + # interface - options.services.vmalert = { - enable = lib.mkOption { - type = lib.types.bool; - default = false; - description = '' - Wether to enable VictoriaMetrics's `vmalert`. + options.services.vmalert.package = mkPackageOption pkgs "victoriametrics" { }; - `vmalert` evaluates alerting and recording rules against a data source, sends notifications via Alertmanager. - ''; - }; + options.services.vmalert.instances = mkOption { + default = { }; - package = mkPackageOption pkgs "victoriametrics" { }; + description = '' + Define multiple instances of vmalert. + ''; - settings = mkOption { - type = types.submodule { - freeformType = confType; - options = { + type = types.attrsOf ( + types.submodule ( + { name, config, ... }: + { + options = { + enable = lib.mkOption { + type = lib.types.bool; + default = false; + description = '' + Wether to enable VictoriaMetrics's `vmalert`. - "datasource.url" = mkOption { - type = types.nonEmptyStr; - example = "http://localhost:8428"; - description = '' - Datasource compatible with Prometheus HTTP API. - ''; + `vmalert` evaluates alerting and recording rules against a data source, sends notifications via Alertmanager. + ''; + }; + + settings = mkOption { + type = types.submodule { + freeformType = confType; + options = { + + "datasource.url" = mkOption { + type = types.nonEmptyStr; + example = "http://localhost:8428"; + description = '' + Datasource compatible with Prometheus HTTP API. + ''; + }; + + "notifier.url" = mkOption { + type = with types; listOf nonEmptyStr; + default = [ ]; + example = [ "http://127.0.0.1:9093" ]; + description = '' + Prometheus Alertmanager URL. List all Alertmanager URLs if it runs in the cluster mode to ensure high availability. + ''; + }; + + "rule" = mkOption { + type = with types; listOf path; + description = '' + Path to the files with alerting and/or recording rules. + + ::: {.note} + Consider using the {option}`services.vmalert.instances..rules` option as a convenient alternative for declaring rules + directly in the `nix` language. + ::: + ''; + }; + + }; + }; + default = { }; + example = { + "datasource.url" = "http://localhost:8428"; + "datasource.disableKeepAlive" = true; + "datasource.showURL" = false; + "rule" = [ + "http:///path/to/rules" + "dir/*.yaml" + ]; + }; + description = '' + `vmalert` configuration, passed via command line flags. Refer to + + for details on supported values. + ''; + }; + + rules = mkOption { + type = format.type; + default = { }; + example = { + group = [ + { + name = "TestGroup"; + rules = [ + { + alert = "ExampleAlertAlwaysFiring"; + expr = '' + sum by(job) + (up == 1) + ''; + } + ]; + } + ]; + }; + description = '' + A list of the given alerting or recording rules against configured `"datasource.url"` compatible with + Prometheus HTTP API for `vmalert` to execute. Refer to + + for details on supported values. + ''; + }; }; - "notifier.url" = mkOption { - type = with types; listOf nonEmptyStr; - default = [ ]; - example = [ "http://127.0.0.1:9093" ]; - description = '' - Prometheus Alertmanager URL. List all Alertmanager URLs if it runs in the cluster mode to ensure high availability. - ''; - }; - - "rule" = mkOption { - type = with types; listOf path; - description = '' - Path to the files with alerting and/or recording rules. - - ::: {.note} - Consider using the {option}`services.vmalert.rules` option as a convenient alternative for declaring rules - directly in the `nix` language. - ::: - ''; - }; - - }; - }; - default = { }; - example = { - "datasource.url" = "http://localhost:8428"; - "datasource.disableKeepAlive" = true; - "datasource.showURL" = false; - "rule" = [ - "http:///path/to/rules" - "dir/*.yaml" - ]; - }; - description = '' - `vmalert` configuration, passed via command line flags. Refer to - - for details on supported values. - ''; - }; - - rules = mkOption { - type = format.type; - default = { }; - example = { - group = [ - { - name = "TestGroup"; - rules = [ - { - alert = "ExampleAlertAlwaysFiring"; - expr = '' - sum by(job) - (up == 1) - ''; - } + config = { + settings.rule = [ + "/etc/${vmalertName name}/rules.yml" ]; - } - ]; - }; - description = '' - A list of the given alerting or recording rules against configured `"datasource.url"` compatible with - Prometheus HTTP API for `vmalert` to execute. Refer to - - for details on supported values. - ''; - }; + }; + } + ) + ); }; # implementation - config = mkIf cfg.enable { + config = mkIf (enabledInstances != { }) { + environment.etc = lib.mapAttrs' ( + name: + { rules, ... }: + lib.nameValuePair "${vmalertName name}/rules.yml" { + source = format.generate "rules.yml" rules; + } + ) enabledInstances; - environment.etc."vmalert/rules.yml".source = format.generate "rules.yml" cfg.rules; + systemd.services = lib.mapAttrs' ( + name: + { settings, ... }: + let + name' = vmalertName name; + in + lib.nameValuePair name' { + description = "vmalert service"; + wantedBy = [ "multi-user.target" ]; + after = [ "network.target" ]; + reloadTriggers = [ config.environment.etc."${name'}/rules.yml".source ]; - services.vmalert.settings.rule = [ - "/etc/vmalert/rules.yml" - ]; - - systemd.services.vmalert = { - description = "vmalert service"; - wantedBy = [ "multi-user.target" ]; - after = [ "network.target" ]; - reloadTriggers = [ config.environment.etc."vmalert/rules.yml".source ]; - - serviceConfig = { - DynamicUser = true; - Restart = "on-failure"; - ExecStart = "${cfg.package}/bin/vmalert ${confOpts}"; - ExecReload = ''${pkgs.coreutils}/bin/kill -SIGHUP "$MAINPID"''; - }; - }; + serviceConfig = { + DynamicUser = true; + Restart = "on-failure"; + ExecStart = "${cfg.package}/bin/vmalert ${mkConfOpts settings}"; + ExecReload = ''${pkgs.coreutils}/bin/kill -SIGHUP "$MAINPID"''; + }; + } + ) enabledInstances; }; } diff --git a/nixos/tests/victoriametrics/vmalert.nix b/nixos/tests/victoriametrics/vmalert.nix index f92c35a82010..aa5026cbd8f6 100644 --- a/nixos/tests/victoriametrics/vmalert.nix +++ b/nixos/tests/victoriametrics/vmalert.nix @@ -55,7 +55,7 @@ import ../make-test-python.nix ( }; }; - services.vmalert = { + services.vmalert.instances."" = { enable = true; settings = { "datasource.url" = "http://localhost:8428"; # victoriametrics' api diff --git a/pkgs/applications/editors/vim/plugins/generated.nix b/pkgs/applications/editors/vim/plugins/generated.nix index a0a7b4bbca53..470cb6f93dfa 100644 --- a/pkgs/applications/editors/vim/plugins/generated.nix +++ b/pkgs/applications/editors/vim/plugins/generated.nix @@ -15133,6 +15133,19 @@ final: prev: { meta.hydraPlatforms = [ ]; }; + ts-autotag-nvim = buildVimPlugin { + pname = "ts-autotag.nvim"; + version = "2025-01-15"; + src = fetchFromGitHub { + owner = "tronikelis"; + repo = "ts-autotag.nvim"; + rev = "0f1dc38fddd99b468ef58938d7cd99ce1d6bcb0e"; + sha256 = "1m0gng9v5dn6mm026nxw0n24bvysr156xhwvf83pv9xqp28xfwar"; + }; + meta.homepage = "https://github.com/tronikelis/ts-autotag.nvim/"; + meta.hydraPlatforms = [ ]; + }; + ts-comments-nvim = buildVimPlugin { pname = "ts-comments.nvim"; version = "2025-02-27"; diff --git a/pkgs/applications/editors/vim/plugins/vim-plugin-names b/pkgs/applications/editors/vim/plugins/vim-plugin-names index 46b6468a5c87..4a01c9bf25e7 100644 --- a/pkgs/applications/editors/vim/plugins/vim-plugin-names +++ b/pkgs/applications/editors/vim/plugins/vim-plugin-names @@ -1161,6 +1161,7 @@ https://github.com/simonmclean/triptych.nvim/,HEAD, https://github.com/folke/trouble.nvim/,, https://github.com/Pocco81/true-zen.nvim/,, https://github.com/tesaguri/trust.vim/,HEAD, +https://github.com/tronikelis/ts-autotag.nvim/,HEAD, https://github.com/folke/ts-comments.nvim/,HEAD, https://github.com/dmmulroy/tsc.nvim/,HEAD, https://github.com/jgdavey/tslime.vim/,, diff --git a/pkgs/applications/video/mpv/scripts/easycrop.nix b/pkgs/applications/video/mpv/scripts/easycrop.nix new file mode 100644 index 000000000000..1ed3d648cb2d --- /dev/null +++ b/pkgs/applications/video/mpv/scripts/easycrop.nix @@ -0,0 +1,44 @@ +{ + lib, + buildLua, + fetchFromGitHub, + unstableGitUpdater, +}: + +buildLua { + pname = "easycrop"; + version = "0-unstable-2018-01-24"; + + src = fetchFromGitHub { + owner = "aidanholm"; + repo = "mpv-easycrop"; + rev = "b8a67bb9039e19dec54d92ea57076c0c98e981aa"; + hash = "sha256-VRQP8j/Z/OvVqrEpvWcLmJFotxbTRynHoqvfIQIQmqY="; + }; + + scriptPath = "easycrop.lua"; + + passthru.updateScript = unstableGitUpdater { }; + + meta = { + description = "Manually crop videos during playback in mpv"; + longDescription = '' + A simple mpv script for manually cropping videos with ease. + + - Works during video playback + - No need to re-encode or modify video files + + Press "c" to begin cropping. Click at one corner of the desired + cropping rectangle, and click a second time at the opposite + corner; the video will be cropped immediately. Pressing "c" again + will undo the current crop. + + If you wish to use a key other than "c" to crop, the keybind + `easy_crop` can be changed. + ''; + homepage = "https://github.com/aidanholm/mpv-easycrop"; + license = lib.licenses.gpl3; + platforms = lib.platforms.all; + maintainers = with lib.maintainers; [ RossSmyth ]; + }; +} diff --git a/pkgs/by-name/am/ameba-ls/package.nix b/pkgs/by-name/am/ameba-ls/package.nix new file mode 100644 index 000000000000..3c6fbcbce3a1 --- /dev/null +++ b/pkgs/by-name/am/ameba-ls/package.nix @@ -0,0 +1,60 @@ +{ + lib, + fetchFromGitHub, + crystal_1_15, + versionCheckHook, +}: + +let + # Use the same Crystal minor version as specified in upstream + crystal = crystal_1_15; +in +crystal.buildCrystalPackage rec { + pname = "ameba-ls"; + version = "0.1.0"; + + src = fetchFromGitHub { + owner = "crystal-lang-tools"; + repo = "ameba-ls"; + tag = "v${version}"; + hash = "sha256-TEHjR+34wrq24XJNLhWZCEzcDEMDlmUHv0iiF4Z6JlI="; + }; + + shardsFile = ./shards.nix; + + crystalBinaries.ameba-ls.src = "src/ameba-ls.cr"; + + buildTargets = [ + "ameba-ls" + ]; + + # There are no actual tests + doCheck = false; + + installPhase = '' + runHook preInstall + + install -Dm555 bin/ameba-ls -t "$out/bin/" + + runHook postInstall + ''; + + nativeInstallCheckInputs = [ + versionCheckHook + ]; + doInstallCheck = true; + versionCheckProgram = "${placeholder "out"}/bin/ameba-ls"; + versionCheckProgramArg = "--version"; + + meta = { + description = "Crystal language server powered by Ameba linter"; + homepage = "https://github.com/crystal-lang-tools/ameba-ls"; + changelog = "https://github.com/crystal-lang-tools/ameba-ls/blob/v${version}/CHANGELOG.md"; + license = lib.licenses.mit; + platforms = lib.platforms.unix; + maintainers = with lib.maintainers; [ + kachick + ]; + mainProgram = "ameba-ls"; + }; +} diff --git a/pkgs/by-name/am/ameba-ls/shards.nix b/pkgs/by-name/am/ameba-ls/shards.nix new file mode 100644 index 000000000000..33ece94ac460 --- /dev/null +++ b/pkgs/by-name/am/ameba-ls/shards.nix @@ -0,0 +1,27 @@ +{ + ameba = { + url = "https://github.com/crystal-ameba/ameba.git"; + rev = "a21dea0b44642f4fc87429283f8b0dd9f1e47a9f"; + sha256 = "1kzr4ynd4r5w87y2czzrlir1dvqmv43ijm07804kgsy1g20k00fs"; + }; + larimar = { + url = "https://github.com/nobodywasishere/larimar.git"; + rev = "97d37e665f7189a7ec35f54fb65003a8438d6cf0"; + sha256 = "0s5hnfdybwbfk8sbjzrly2p6xppc5niww14h9cx00xkm8m1rlyj2"; + }; + lsprotocol = { + url = "https://github.com/nobodywasishere/lsprotocol-crystal.git"; + rev = "28986890c7657af4aefea8355ca3f3c7fc2bc9dd"; + sha256 = "0pccgq5g87mnvrhpgw3j22p3wgch8kp1svxcrbz2dha7zvgn65kj"; + }; + rwlock = { + url = "https://github.com/spider-gazelle/readers-writer.git"; + rev = "v1.0.7"; + sha256 = "1cs4ang50cza7sb5zh94rl1ppwcn9z1l8jjcsshhy4w72wkbqyny"; + }; + tree_sitter = { + url = "https://github.com/crystal-lang-tools/crystal-tree-sitter.git"; + rev = "1d46ca231a641b30b8e7fbbae7eba050f7717a9f"; + sha256 = "16g0ii3b3pmpnwmx2iz9dr1865pgfka7a724dfj62csjavqm5i1k"; + }; +} diff --git a/pkgs/by-name/ar/artichoke/package.nix b/pkgs/by-name/ar/artichoke/package.nix new file mode 100644 index 000000000000..f3d93d31a96a --- /dev/null +++ b/pkgs/by-name/ar/artichoke/package.nix @@ -0,0 +1,58 @@ +{ + lib, + rustPlatform, + fetchFromGitHub, + llvmPackages, + _experimental-update-script-combinators, + unstableGitUpdater, + nix-update-script, +}: + +rustPlatform.buildRustPackage { + pname = "artichoke"; + version = "0-unstable-2025-06-01"; + + src = fetchFromGitHub { + owner = "artichoke"; + repo = "artichoke"; + rev = "7c35392d8c7622cd8ab8eccaee73d57633b2b901"; + hash = "sha256-7YPExT+5F+5MMk/yLfG4Rk8ZDwsYfVKlkvIroFB22No="; + }; + + cargoHash = "sha256-cN70yYYKhktUoswow63ZXHvfFbXDo1rUrTWm22LluCM="; + + nativeBuildInputs = [ + rustPlatform.bindgenHook + ]; + + doInstallCheck = true; + installCheckPhase = '' + runHook preInstallCheck + + stdout="$("$out/bin/artichoke" -e 'puts "Hello World!"')" + [[ "$stdout" == 'Hello World!' ]] + + runHook postInstallCheck + ''; + + passthru = { + updateScript = _experimental-update-script-combinators.sequence [ + (unstableGitUpdater { }) + (nix-update-script { + # Updating `cargoHash` + extraArgs = [ "--version=skip" ]; + }) + ]; + }; + + meta = { + description = "Ruby implementation written in Rust and Ruby"; + homepage = "https://www.artichokeruby.org/"; + license = lib.licenses.mit; + maintainers = with lib.maintainers; [ + kachick + ]; + mainProgram = "artichoke"; + platforms = with lib.platforms; unix ++ windows; + }; +} diff --git a/pkgs/by-name/bi/biz-ud-gothic/package.nix b/pkgs/by-name/bi/biz-ud-gothic/package.nix new file mode 100644 index 000000000000..17db66a647e5 --- /dev/null +++ b/pkgs/by-name/bi/biz-ud-gothic/package.nix @@ -0,0 +1,41 @@ +{ + lib, + stdenvNoCC, + fetchzip, + nix-update-script, +}: + +stdenvNoCC.mkDerivation (finalAttrs: { + pname = "biz-ud-gothic"; + version = "1.051"; + + src = fetchzip { + # Sticking with this assets file due to ongoing discussions. + # We may switch to a different asset once the issue is resolved or clarifications are provided. + # ref: https://github.com/googlefonts/morisawa-biz-ud-gothic/issues/47 + url = "https://github.com/googlefonts/morisawa-biz-ud-gothic/releases/download/v${finalAttrs.version}/morisawa-biz-ud-gothic-fonts.zip"; + hash = "sha256-7PlIrQX1fnFHXm7mjfoOCVp3GSnLT2GlVZdSoZbh/s4="; + }; + + installPhase = '' + runHook preInstall + + install -Dm444 fonts/ttf/*.ttf -t "$out/share/fonts/truetype/" + + runHook postInstall + ''; + + passthru = { + updateScript = nix-update-script { }; + }; + + meta = { + description = "Universal Design Japanese font"; + homepage = "https://github.com/googlefonts/morisawa-biz-ud-gothic"; + license = lib.licenses.ofl; + maintainers = with lib.maintainers; [ + kachick + ]; + platforms = lib.platforms.all; + }; +}) diff --git a/pkgs/by-name/bo/boring/package.nix b/pkgs/by-name/bo/boring/package.nix new file mode 100644 index 000000000000..4ba545f08c5f --- /dev/null +++ b/pkgs/by-name/bo/boring/package.nix @@ -0,0 +1,56 @@ +{ + boring, + buildGoModule, + fetchFromGitHub, + installShellFiles, + lib, + stdenv, + testers, +}: + +buildGoModule (finalAttrs: { + pname = "boring"; + version = "0.11.4"; + + src = fetchFromGitHub { + owner = "alebeck"; + repo = "boring"; + tag = finalAttrs.version; + hash = "sha256-N0GVXtw6Gp6iHKBD2Lk6FX8XaUnkPgZduPaczYdApAs="; + }; + + nativeBuildInputs = [ + installShellFiles + ]; + + vendorHash = "sha256-j8A0F+o3EnzJdge+T/gHAwRGwzC86oD6ddZejUs/C7o="; + + ldflags = [ + "-s" + "-w" + "-X main.version=${finalAttrs.version}" + ]; + + postInstall = lib.optionalString (stdenv.buildPlatform.canExecute stdenv.hostPlatform) '' + installShellCompletion --cmd boring \ + --bash <($out/bin/boring --shell bash) \ + --fish <($out/bin/boring --shell fish) \ + --zsh <($out/bin/boring --shell zsh) + ''; + + passthru.tests.version = testers.testVersion { + package = boring; + command = "boring version"; + version = "boring ${finalAttrs.version}"; + }; + + meta = { + description = "SSH tunnel manager"; + homepage = "https://github.com/alebeck/boring"; + license = lib.licenses.mit; + maintainers = with lib.maintainers; [ + jacobkoziej + ]; + mainProgram = "boring"; + }; +}) diff --git a/pkgs/by-name/br/brave/make-brave.nix b/pkgs/by-name/br/brave/make-brave.nix index 9b098c5213dc..aecc20af7dc2 100644 --- a/pkgs/by-name/br/brave/make-brave.nix +++ b/pkgs/by-name/br/brave/make-brave.nix @@ -205,7 +205,8 @@ stdenv.mkDerivation { # Fix path to bash in $BINARYWRAPPER substituteInPlace $BINARYWRAPPER \ - --replace /bin/bash ${stdenv.shell} + --replace /bin/bash ${stdenv.shell} \ + --replace-fail 'CHROME_WRAPPER' 'WRAPPER' ln -sf $BINARYWRAPPER $out/bin/brave @@ -263,6 +264,7 @@ stdenv.mkDerivation { coreutils ] } + --set CHROME_WRAPPER ${pname} ${optionalString (enableFeatures != [ ]) '' --add-flags "--enable-features=${strings.concatStringsSep "," enableFeatures}\''${NIXOS_OZONE_WL:+\''${WAYLAND_DISPLAY:+,WaylandWindowDecorations --enable-wayland-ime=true}}" ''} diff --git a/pkgs/by-name/ci/cie-middleware-linux/deps.json b/pkgs/by-name/ci/cie-middleware-linux/deps.json index a1554a15e26f..593cd2e1b767 100644 --- a/pkgs/by-name/ci/cie-middleware-linux/deps.json +++ b/pkgs/by-name/ci/cie-middleware-linux/deps.json @@ -14,28 +14,28 @@ "jar": "sha256-CV/R3HeIjAc/C+OaAYFW7lJnInmLCd6eKF7yE14W6sQ=", "pom": "sha256-NQkZQkMk4nUKPdwvobzmqQrIziklaYpgqbTR1uSSL/4=" }, - "com/diffplug/durian#durian-swt.os/4.2.2": { - "jar": "sha256-a1Mca0vlgaizLq2GHdwVwsk7IMZl+00z4DgUg8JERfQ=", - "module": "sha256-rVlQLGknZu48M0vkliigDctNka4aSPJjLitxUStDXPk=", - "pom": "sha256-GzxJFP1eLM4pZq1wdWY5ZBFFwdNCB3CTV4Py3yY2kIU=" + "com/diffplug/durian#durian-swt.os/4.3.0": { + "jar": "sha256-geK2Oafkvm3JtyRXE88G9cq1HynbLha5tXZFyW/eKIQ=", + "module": "sha256-IFNqlfL+sr9DBRKMaq7Lb9idxFeYqchfJgK4qAnXUNs=", + "pom": "sha256-Q1z/VXiZht7arXF/aPuo1UgklHhWLc2EsirU1lZvRAs=" }, - "com/diffplug/spotless#com.diffplug.spotless.gradle.plugin/6.25.0": { - "pom": "sha256-9FyCsS+qzYWs1HTrppkyL6XeqIQIskfQ5L3pQSkIIjo=" + "com/diffplug/spotless#com.diffplug.spotless.gradle.plugin/7.0.2": { + "pom": "sha256-7R3td6KWpv4hpQJ5ySbAe+FK98CMJDfTaFxw/Pa7oC0=" }, - "com/diffplug/spotless#spotless-lib-extra/2.45.0": { - "jar": "sha256-YCy7zTgo7pz7LjCn+bMDNcaScTB3FBTUzdKU0h/ly2c=", - "module": "sha256-9pnkNfTlzgPbYJpHaO6wNj1uB8ZfvPrx/GKcTnbuf7A=", - "pom": "sha256-5x2LkRDdSNLn9KVLi/uozlWpbmteu9T0OpJGZJz1b7A=" + "com/diffplug/spotless#spotless-lib-extra/3.0.2": { + "jar": "sha256-sOd3RtYz1EXnhImsPQitLqGzU3xNBk5KvkbMQtYjA+s=", + "module": "sha256-vSVeQkQbWRehU8U9z5fP08IEevN2zF3Yu1Z/aEAWtFk=", + "pom": "sha256-IVesGayscKzQRQH8WbvJZNsZD1tx5O1e/s6o5c9o7Os=" }, - "com/diffplug/spotless#spotless-lib/2.45.0": { - "jar": "sha256-sllply4dmAKAyirlKRl+2bMWCq5ItQbPGTXwG9Exhmc=", - "module": "sha256-+x+8+TUAczrHWcp99E8P9mVTEze0LaAS4on/CINNiQ8=", - "pom": "sha256-WKd8IsQLIc8m29tCEwFu9HrM9bBwchfHkyqQ9D+PMNw=" + "com/diffplug/spotless#spotless-lib/3.0.2": { + "jar": "sha256-P5p/38WwOsIIlINBcJEMFcTyuE7UzjZ3iYowetWJg3w=", + "module": "sha256-E1WLrsCR6gDxYmXNNSOBePT+ejv61zXel214XUF/ss0=", + "pom": "sha256-jxtFo4m6Jeel8DvZ8KS9BKp+dHXgku6C1VUJYrLPdV8=" }, - "com/diffplug/spotless#spotless-plugin-gradle/6.25.0": { - "jar": "sha256-9euQikxdpGKZ51Q/qtoEAtLEt31Yx7Qy1Lblk0mygKM=", - "module": "sha256-RoHRe/PJIF2DeOynBcAAywzJjcx40DATy2iJjGvSx0Q=", - "pom": "sha256-q1ZuPYS2w/rHqPySXy279TzZdZywOvPAfQ3EN9OXqNo=" + "com/diffplug/spotless#spotless-plugin-gradle/7.0.2": { + "jar": "sha256-WaNMT4SkjUyNkp4viZBjaeZUduwEmaQ96Hw+QSeXfNU=", + "module": "sha256-rxC8mydsNqlNcRh+kVhwJ1yyRVZTntzqGYpYL30Tsws=", + "pom": "sha256-JyVoPfbvTNSIr+sgANqJIpQcqQ513D49uFIupxWKaMQ=" }, "com/googlecode/concurrent-trees#concurrent-trees/2.6.1": { "jar": "sha256-BONySYTipcv1VgbPo3KlvT08XSohUzpwBOPN5Tl2H6U=", @@ -59,31 +59,31 @@ "module": "sha256-akesUDZOZZhFlAH7hvm2z832N7mzowRbHMM8v0xAghg=", "pom": "sha256-rrO3CiTBA+0MVFQfNfXFEdJ85gyuN2pZbX1lNpf4zJU=" }, - "commons-codec#commons-codec/1.16.0": { - "jar": "sha256-VllfsgsLhbyR0NUD2tULt/G5r8Du1d/6bLslkpAASE0=", - "pom": "sha256-bLWVeBnfOTlW/TEaOgw/XuwevEm6Wy0J8/ROYWf6PnQ=" + "commons-codec#commons-codec/1.17.0": { + "jar": "sha256-9wDegKwnDQNE/ep0aCAdi5yAXlxkgzHDYZ8u4GfM/Fk=", + "pom": "sha256-wBxM2l5Aj0HtHYPkoKFwz1OAG2M4q6SfD5BHhrwSFPw=" }, - "dev/equo/ide#solstice/1.7.5": { - "jar": "sha256-BuFLxDrMMx2ra16iAfxnNk7RI/mCyF+lEx8IF+1lrk8=", - "module": "sha256-eYp7cGdyE27iijLt2GOx6fgWE6NJhAXXS+ilyb6/9U8=", - "pom": "sha256-20U7urXn2opDE5sNzTuuZykzIfKcTZH1p5XZ/2xS3d8=" + "dev/equo/ide#solstice/1.8.1": { + "jar": "sha256-bluizOgTvh1xzNwuzz5JJxsU5pG/u7GhFM86MOdzsQ0=", + "module": "sha256-pnYDnqavCPJXtG4Hwr8VcaRqTUtbnMuGw/yY0H+v6hs=", + "pom": "sha256-arSo7K4qu9NrkZ0Lm5+yTBdxSPE+U2TJegxu4Ro/xCY=" }, - "org/apache#apache/29": { - "pom": "sha256-PkkDcXSCC70N9jQgqXclWIY5iVTCoGKR+mH3J6w1s3c=" + "org/apache#apache/31": { + "pom": "sha256-VV0MnqppwEKv+SSSe5OB6PgXQTbTVe6tRFIkRS5ikcw=" }, - "org/apache/commons#commons-parent/58": { - "pom": "sha256-LUsS4YiZBjq9fHUni1+pejcp2Ah4zuy2pA2UbpwNVZA=" + "org/apache/commons#commons-parent/69": { + "pom": "sha256-1Q2pw5vcqCPWGNG0oDtz8ZZJf8uGFv0NpyfIYjWSqbs=" }, - "org/eclipse/jgit#org.eclipse.jgit-parent/6.7.0.202309050840-r": { - "pom": "sha256-u56FQW2Y0HMfx2f41w6EaAQWAdZnKuItsqx5n3qjkR8=" + "org/eclipse/jgit#org.eclipse.jgit-parent/6.10.0.202406032230-r": { + "pom": "sha256-8tNTmgp5Iv15RwgsGQHSCQ2uB0mGsi2r2XO0OYzR6i4=" }, - "org/eclipse/jgit#org.eclipse.jgit/6.7.0.202309050840-r": { - "jar": "sha256-tWRHfQkiQaqrUMhKxd0aw3XAGCBE1+VlnTpgqQ4ugBo=", - "pom": "sha256-BNB83b8ZjfpuRIuan7lA94HAEq2T2eqCBv4KTTplwZI=" + "org/eclipse/jgit#org.eclipse.jgit/6.10.0.202406032230-r": { + "jar": "sha256-Q/kvOttoGl8wBrl56NNBwSqM/YAp8ofEK88KgDd1Za4=", + "pom": "sha256-BVlUQr62ogYQi2c6qcZpLIPkHfGDF33GcROxzD9Sgd0=" }, - "org/eclipse/platform#org.eclipse.osgi/3.18.300": { - "jar": "sha256-urlD5Y7dFzCSOGctunpFrsni2svd24GKjPF3I+oT+iI=", - "pom": "sha256-4nl2N1mZxUJ/y8//PzvCD77a+tiqRRArN59cL5fI/rQ=" + "org/eclipse/platform#org.eclipse.osgi/3.18.500": { + "jar": "sha256-gLJ11YN5cjspHqZQJJzDgJyPELNPeKr5iBMs1tQ0q04=", + "pom": "sha256-4o9b4Azk7Sx+SAnsrQW5UwfzWhflhWAHhri97juk2Wg=" }, "org/jetbrains#annotations/13.0": { "jar": "sha256-rOKhDcji1f00kl7KwD5JiLLA+FFlDJS4zvSbob0RFHg=", @@ -114,9 +114,9 @@ "jar": "sha256-VemJxRK4CQd5n4VDCfO8d4LFs9E5MkQtA3nVxHJxFQQ=", "pom": "sha256-fin79z/fceBnnT3ufmgP1XNGT6AWRKT1irgZ0sCI09I=" }, - "org/junit#junit-bom/5.9.3": { - "module": "sha256-tAH9JZAeWCpSSqU0PEs54ovFbiSWHBBpvytLv87ka5M=", - "pom": "sha256-TQMpzZ5y8kIOXKFXJMv+b/puX9KIg2FRYnEZD9w0Ltc=" + "org/junit#junit-bom/5.10.2": { + "module": "sha256-3iOxFLPkEZqP5usXvtWjhSgWaYus5nBxV51tkn67CAo=", + "pom": "sha256-Fp3ZBKSw9lIM/+ZYzGIpK/6fPBSpifqSEgckzeQ6mWg=" }, "org/slf4j#slf4j-api/1.7.36": { "jar": "sha256-0+9XXj5JeWeNwBvx3M5RAhSTtNEft/G+itmCh3wWocA=", @@ -137,31 +137,31 @@ } }, "https://repo.maven.apache.org/maven2": { - "com/google/code/gson#gson-parent/2.11.0": { - "pom": "sha256-issfO3Km8CaRasBzW62aqwKT1Sftt7NlMn3vE6k2e3o=" + "com/google/code/gson#gson-parent/2.12.1": { + "pom": "sha256-yeewt+Mb574iaEl5wGgAHGUssRPE5u2JTjm2Q97gf8E=" }, - "com/google/code/gson#gson/2.11.0": { - "jar": "sha256-V5KNblpu3rKr03cKj5W6RNzkXzsjt6ncKzCcWBVSp4s=", - "pom": "sha256-wOVHvqmYiI5uJcWIapDnYicryItSdTQ90sBd7Wyi42A=" + "com/google/code/gson#gson/2.12.1": { + "jar": "sha256-6+4T1ft0d81/HMAQ4MNW34yoBwlxUkjal/eeNcy0++w=", + "pom": "sha256-C1c17IX0UoLx4sdpd5gAQnsVCoFj9AUJOpKAtxyrGXg=" }, - "com/google/errorprone#error_prone_annotations/2.27.0": { - "jar": "sha256-JMkjNyxY410LnxagKJKbua7cd1IYZ8J08r0HNd9bofU=", - "pom": "sha256-TKWjXWEjXhZUmsNG0eNFUc3w/ifoSqV+A8vrJV6k5do=" + "com/google/errorprone#error_prone_annotations/2.36.0": { + "jar": "sha256-d0QOJwsLyaJJkDxaB2w2pyLEiGyk9CZ18pA6HFPtYaU=", + "pom": "sha256-15z9N8hfdta3VMdQHuHchEe3smQsI4LXeCUhZr0zHpw=" }, - "com/google/errorprone#error_prone_parent/2.27.0": { - "pom": "sha256-+oGCnQSVWd9pJ/nJpv1rvQn4tQ5tRzaucsgwC2w9dlQ=" + "com/google/errorprone#error_prone_parent/2.36.0": { + "pom": "sha256-Okz8imvtYetI6Wl5b8MeoNJwtj5nBZmUamGIOttwlNw=" }, "commons-io#commons-io/2.18.0": { "jar": "sha256-88oPjWPEDiOlbVQQHGDV7e4Ta0LYS/uFvHljCTEJz4s=", "pom": "sha256-Y9lpQetE35yQ0q2yrYw/aZwuBl5wcEXF2vcT/KUrz8o=" }, - "commons-logging#commons-logging/1.3.3": { - "jar": "sha256-WCj5bAnYhvmxoJk8eASyfPT87IU0UXFk9RN6yLZ+qbk=", - "pom": "sha256-El1hQurD93REC6cCwF8o+eCYxS0QcSrhFJast3EGs7o=" + "commons-logging#commons-logging/1.3.4": { + "jar": "sha256-vC3+MvHvBlCeagZRRMGt97Qg6r8RqH8wvRJ/j6ozIBY=", + "pom": "sha256-1L2jSJKqzL9PrTP8MjqKqQfvnXmYRlnaJJRMCoa/CGU=" }, - "net/java/dev/jna#jna/5.15.0": { - "jar": "sha256-pWQVjSirUSf8apWAKO1UJ5/gmZZixGQltqOwmipSCU0=", - "pom": "sha256-J2YC/zZ6TDkVXa7MHoy1T0eJ5dgN+Qo6i2YD8d61ngU=" + "net/java/dev/jna#jna/5.17.0": { + "jar": "sha256-s6lAjnxR4I7w47/MCPRD9uwPYZG6jNfBjVPSsi5b28A=", + "pom": "sha256-UBoP8F2EpK0Q9t4lvpT0k5i3CjG+jzoO2fTGtE++/uQ=" }, "org/apache#apache/32": { "pom": "sha256-z9hywOwn9Trmj0PbwP7N7YrddzB5pTr705DkB7Qs5y8=" @@ -169,26 +169,26 @@ "org/apache#apache/33": { "pom": "sha256-14vYUkxfg4ChkKZSVoZimpXf5RLfIRETg6bYwJI6RBU=" }, - "org/apache/commons#commons-parent/71": { - "pom": "sha256-lbe+cPMWrkyiL2+90I3iGC6HzYdKZQ3nw9M4anR6gqM=" + "org/apache/commons#commons-parent/72": { + "pom": "sha256-Q0Xev8dnsa6saKvdcvxn0YtSHUs5A3KhG2P/DFhrIyA=" }, "org/apache/commons#commons-parent/78": { "pom": "sha256-Ai0gLmVe3QTyoQ7L5FPZKXeSTTg4Ckyow1nxgXqAMg4=" }, - "org/apache/pdfbox#fontbox/3.0.3": { - "jar": "sha256-ZWkMPzmwShTRLBf0mYwVGGzod9Pi7CIscIV348wCgDA=", - "pom": "sha256-sik+UDqncEMGEVD4hGg9f2FMz1Fjvi0PBSyinzx2d+I=" + "org/apache/pdfbox#fontbox/3.0.4": { + "jar": "sha256-Le7GIy9dbTsxJ2WS0xaArpcir1fSTLD3bacOK6DpnhI=", + "pom": "sha256-8MzDMOmyDYlXigqHArR6OZvNYojCOG10/6uPjjAoROg=" }, - "org/apache/pdfbox#pdfbox-io/3.0.3": { - "jar": "sha256-Ej6jGHtJfFTmYdUMPIZ0eb93Zo/0UOUHEMZY8rtGh7o=", - "pom": "sha256-EXAbuMWnNaSzFA9zroM6KBzqCeO8P583kbmpenRekNQ=" + "org/apache/pdfbox#pdfbox-io/3.0.4": { + "jar": "sha256-ep1HRvLhOh4i9O/kf7r5mXY8rqQXSFNa+ToB7txQ9VQ=", + "pom": "sha256-YLURdX737TBIv4LH2ClvcdC8RwAgEYQzqzs+OEC/jqU=" }, - "org/apache/pdfbox#pdfbox-parent/3.0.3": { - "pom": "sha256-yGXhzv8Jq1Kwh+cmDE8V025bW4vk/+IERvqkCiygvcw=" + "org/apache/pdfbox#pdfbox-parent/3.0.4": { + "pom": "sha256-w5j++zUez/mTEYCNftU0bnHzVrETS9c6JQTLgXB9tFE=" }, - "org/apache/pdfbox#pdfbox/3.0.3": { - "jar": "sha256-W+ONLsgWkbBdU163IN5NxWbF0H5aBHMfoAZo0VOotKY=", - "pom": "sha256-vgiV9rLCDzEdYjXJam/SqsECsxkE0/TDnqUll3WwcAg=" + "org/apache/pdfbox#pdfbox/3.0.4": { + "jar": "sha256-CaD/J9b4Sh3EAGDLCgHezyrU75HDa8kbmDbCVL6KrkU=", + "pom": "sha256-098DLfK90jT+NwMO+sNXa6Yj/Jf1ERNUxMUq13dfkOQ=" }, "org/junit#junit-bom/5.11.0-M2": { "module": "sha256-hkd6vPSQ1soFmqmXPLEI0ipQb0nRpVabsyzGy/Q8LM4=", diff --git a/pkgs/by-name/ci/cie-middleware-linux/package.nix b/pkgs/by-name/ci/cie-middleware-linux/package.nix index 7647f20c69a7..0495b82c0693 100644 --- a/pkgs/by-name/ci/cie-middleware-linux/package.nix +++ b/pkgs/by-name/ci/cie-middleware-linux/package.nix @@ -21,13 +21,13 @@ let pname = "cie-middleware-linux"; - version = "1.5.6"; + version = "1.5.9"; src = fetchFromGitHub { owner = "M0rf30"; repo = "cie-middleware-linux"; rev = version; - sha256 = "sha256-2P/1hQTmeQ6qE7RgAeLOZTszcLcIpa2XX1S2ahXRHcc="; + hash = "sha256-2UMKxanF35oBNBtIqfU46QUYJwXiTU1xCrCMqzqetgI="; }; gradle = gradle_8; @@ -61,8 +61,6 @@ stdenv.mkDerivation { libxml2 ]; - patches = [ ./use-system-podofo.patch ]; - postPatch = '' # substitute the cieid command with this $out/bin/cieid substituteInPlace libs/pkcs11/src/CSP/AbilitaCIE.cpp \ @@ -114,6 +112,7 @@ stdenv.mkDerivation { popd # Install the Java application + ls cie-java/build/libs/CIEID-standalone.jar install -Dm755 cie-java/build/libs/CIEID-standalone.jar \ "$out/share/cieid/cieid.jar" @@ -123,11 +122,11 @@ stdenv.mkDerivation { --add-flags "-Djna.library.path='$out/lib:${libraries}'" \ --add-flags '-Dawt.useSystemAAFontSettings=on' \ --add-flags "-cp $out/share/cieid/cieid.jar" \ - --add-flags "it.ipzs.cieid.MainApplication" + --add-flags "app.m0rf30.cieid.MainApplication" # Install other files - install -Dm644 data/cieid.desktop "$out/share/applications/cieid.desktop" - install -Dm755 data/logo.png "$out/share/pixmaps/cieid.png" + install -Dm644 data/app.m0rf30.cieid.desktop -t "$out/share/applications" + install -Dm755 data/app.m0rf30.cieid.svg -t "$out/share/pixmaps" install -Dm644 LICENSE "$out/share/licenses/cieid/LICENSE" ''; @@ -146,8 +145,6 @@ stdenv.mkDerivation { ''; license = licenses.bsd3; platforms = platforms.unix; - # Note: fails due to a lot of broken type conversions - badPlatforms = platforms.darwin; maintainers = with maintainers; [ rnhmjoj ]; }; } diff --git a/pkgs/by-name/ci/cie-middleware-linux/use-system-podofo.patch b/pkgs/by-name/ci/cie-middleware-linux/use-system-podofo.patch deleted file mode 100644 index 52806d4f96e8..000000000000 --- a/pkgs/by-name/ci/cie-middleware-linux/use-system-podofo.patch +++ /dev/null @@ -1,343 +0,0 @@ -commit c9ac4243a6def08790bbf5552bb31894169596ca -Author: rnhmjoj -Date: Wed Apr 3 12:54:58 2024 +0200 - - use system podofo - -diff --git a/libs/meson.build b/libs/meson.build -index 3ee31c1..5022ba8 100644 ---- a/libs/meson.build -+++ b/libs/meson.build -@@ -16,21 +16,15 @@ curl_dep = dependency('libcurl') - fontconfig_dep = dependency('fontconfig') - freetype_dep = dependency('freetype2') - png_dep = dependency('libpng') --podofo_dep = cpp.find_library('libpodofo', dirs: libdir) -+podofo_dep = dependency('libpodofo') - libxml2_dep = dependency('libxml-2.0', required: false) - xml2_dep = dependency('xml2', required: false) - zlib_dep = dependency('zlib') - - inc_so = include_directories('pkcs11/src/.', 'shared/src/') - --inc_a = include_directories( -- 'sign-sdk/include', -- 'sign-sdk/include/podofo', -- 'sign-sdk/include/podofo/include', -- 'sign-sdk/include/podofo/include/podofo', -- 'sign-sdk/src', -- 'shared/src/', --) -+inc_a = include_directories('sign-sdk/include', 'sign-sdk/src', 'shared/src/') -+ - cie_pkcs11_sources = [ - 'shared/src/Util/log.cpp', - 'shared/src/Util/funccallinfo.cpp', -diff --git a/libs/sign-sdk/include/PdfSignatureGenerator.h b/libs/sign-sdk/include/PdfSignatureGenerator.h -index 93ab445..65d438f 100644 ---- a/libs/sign-sdk/include/PdfSignatureGenerator.h -+++ b/libs/sign-sdk/include/PdfSignatureGenerator.h -@@ -10,9 +10,7 @@ - #ifndef _PDFSIGNATUREGENERATOR_H_ - #define _PDFSIGNATUREGENERATOR_H_ - #include "Util/UUCByteArray.h" --#include "podofo/doc/PdfSignOutputDevice.h" --#include "podofo/doc/PdfSignatureField.h" --#include "podofo/podofo.h" -+#include - - using namespace PoDoFo; - using namespace std; -@@ -60,7 +58,11 @@ class PdfSignatureGenerator { - const double getHeight(int pageIndex); - - private: -- PdfMemDocument* m_pPdfDocument; -+ PdfDocument* m_pPdfDocument; -+ -+ PdfMemDocument* m_pPdfMemDocument; -+ -+ PdfWriter* m_pPdfWriter; - - PdfSignatureField* m_pSignatureField; - -diff --git a/libs/sign-sdk/src/PdfSignatureGenerator.cpp b/libs/sign-sdk/src/PdfSignatureGenerator.cpp -index 44ef54a..e8b8c8e 100644 ---- a/libs/sign-sdk/src/PdfSignatureGenerator.cpp -+++ b/libs/sign-sdk/src/PdfSignatureGenerator.cpp -@@ -27,7 +27,7 @@ int GetNumberOfSignatures(PdfMemDocument* pPdfDocument); - USE_LOG; - - PdfSignatureGenerator::PdfSignatureGenerator() -- : m_pPdfDocument(NULL), -+ : m_pPdfMemDocument(NULL), - m_pSignatureField(NULL), - m_pSignOutputDevice(NULL), - m_pFinalOutDevice(NULL), -@@ -37,7 +37,7 @@ PdfSignatureGenerator::PdfSignatureGenerator() - } - - PdfSignatureGenerator::~PdfSignatureGenerator() { -- if (m_pPdfDocument) delete m_pPdfDocument; -+ if (m_pPdfMemDocument) delete m_pPdfMemDocument; - - if (m_pSignatureField) delete m_pSignatureField; - -@@ -51,21 +51,21 @@ PdfSignatureGenerator::~PdfSignatureGenerator() { - } - - int PdfSignatureGenerator::Load(const char* pdf, int len) { -- if (m_pPdfDocument) delete m_pPdfDocument; -+ if (m_pPdfMemDocument) delete m_pPdfMemDocument; - - try { - printf("PDF LENGTH"); - printf("%i", len); - printf("STOP"); - -- m_pPdfDocument = new PdfMemDocument(); -- m_pPdfDocument->Load(pdf, len); -- printf("OK m_pPdfDocument"); -- int nSigns = PDFVerifier::GetNumberOfSignatures(m_pPdfDocument); -+ m_pPdfMemDocument = new PdfMemDocument(); -+ m_pPdfMemDocument->Load(pdf); -+ printf("OK m_pPdfMemDocument"); -+ int nSigns = PDFVerifier::GetNumberOfSignatures(m_pPdfMemDocument); - printf("OK nSigns: %d", nSigns); - - if (nSigns > 0) { -- m_pPdfDocument->SetIncrementalUpdates(true); -+ m_pPdfWriter->PdfWriter::SetIncrementalUpdate(true); - } - m_actualLen = len; - -@@ -82,14 +82,8 @@ void PdfSignatureGenerator::AddFont(const char* szFontName, - // printf(szFontName); - // printf(szFontPath); - -- m_pPdfDocument->CreateFont( -- szFontName, false, false, -- PdfEncodingFactory::GlobalWinAnsiEncodingInstance(), -- PdfFontCache::eFontCreationFlags_AutoSelectBase14, true, szFontPath); -- m_pPdfDocument->CreateFont( -- szFontName, true, false, -- PdfEncodingFactory::GlobalWinAnsiEncodingInstance(), -- PdfFontCache::eFontCreationFlags_AutoSelectBase14, true, szFontPath); -+ m_pPdfDocument->PoDoFo::PdfDocument::CreateFont( szFontName, false, PdfEncodingFactory::GlobalWinAnsiEncodingInstance(), PdfFontCache::eFontCreationFlags_AutoSelectBase14, true); -+ m_pPdfDocument->PoDoFo::PdfDocument::CreateFont( szFontName, true, PdfEncodingFactory::GlobalWinAnsiEncodingInstance(), PdfFontCache::eFontCreationFlags_AutoSelectBase14, true); - } - - void PdfSignatureGenerator::InitSignature( -@@ -130,7 +124,7 @@ void PdfSignatureGenerator::InitSignature( - - if (m_pSignatureField) delete m_pSignatureField; - -- PdfPage* pPage = m_pPdfDocument->GetPage(pageIndex); -+ PdfPage* pPage = m_pPdfMemDocument->GetPage(pageIndex); - PdfRect cropBox = pPage->GetCropBox(); - - float left0 = left * cropBox.GetWidth(); -@@ -145,15 +139,14 @@ void PdfSignatureGenerator::InitSignature( - - LOG_DBG((0, "InitSignature", "PdfSignatureField")); - -- m_pSignatureField = new PdfSignatureField( -- pPage, rect, m_pPdfDocument, PdfString(szFieldName), szSubFilter); -+ m_pSignatureField = new PdfSignatureField(pPage, rect, m_pPdfMemDocument); - - LOG_DBG((0, "InitSignature", "PdfSignatureField OK")); - - if (szReason && szReason[0]) { - PdfString reason(szReason); - PdfString reasonLabel(szReasonLabel); -- m_pSignatureField->SetSignatureReason(reasonLabel, reason); -+ m_pSignatureField->SetSignatureReason(reason); - } - - LOG_DBG((0, "InitSignature", "szReason OK")); -@@ -161,7 +154,7 @@ void PdfSignatureGenerator::InitSignature( - if (szLocation && szLocation[0]) { - PdfString location(szLocation); - PdfString locationLabel(szLocationLabel); -- m_pSignatureField->SetSignatureLocation(locationLabel, location); -+ m_pSignatureField->SetSignatureLocation(location); - } - - LOG_DBG((0, "InitSignature", "szLocation OK")); -@@ -171,54 +164,42 @@ void PdfSignatureGenerator::InitSignature( - - LOG_DBG((0, "InitSignature", "Date OK")); - -- if (szName && szName[0]) { -- PdfString name(szName); -- PdfString nameLabel(szNameLabel); -- m_pSignatureField->SetSignatureName(nameLabel, name); -- } -- -- LOG_DBG((0, "InitSignature", "szName OK")); -- -- m_pSignatureField->SetSignatureSize(SIGNATURE_SIZE); -+ m_pSignOutputDevice->PdfSignOutputDevice::SetSignatureSize(SIGNATURE_SIZE); - - LOG_DBG((0, "InitSignature", "SIGNATURE_SIZE OK")); - -- // if((szImagePath && szImagePath[0]) || (szDescription && szDescription[0])) -- if (width * height > 0) { -- try { -- // m_pSignatureField->SetFontSize(5); -- m_pSignatureField->SetAppearance(szImagePath, szDescription); -- LOG_DBG((0, "InitSignature", "SetAppearance OK")); -- } catch (PdfError& error) { -- LOG_ERR((0, "InitSignature", "SetAppearance error: %s, %s", -- PdfError::ErrorMessage(error.GetError()), error.what())); -- } catch (PdfError* perror) { -- LOG_ERR((0, "InitSignature", "SetAppearance error2: %s, %s", -- PdfError::ErrorMessage(perror->GetError()), perror->what())); -- } catch (std::exception& ex) { -- LOG_ERR( -- (0, "InitSignature", "SetAppearance std exception, %s", ex.what())); -- } catch (std::exception* pex) { -- LOG_ERR((0, "InitSignature", "SetAppearance std exception2, %s", -- pex->what())); -- } catch (...) { -- LOG_ERR((0, "InitSignature", "SetAppearance unknown error")); -- } -- } -+ // if (width * height > 0) { -+ // try { -+ // m_pSignatureField->SetAppearance(szImagePath, szDescription); -+ // LOG_DBG((0, "InitSignature", "SetAppearance OK")); -+ // } catch (PdfError& error) { -+ // LOG_ERR((0, "InitSignature", "SetAppearance error: %s, %s", -+ // PdfError::ErrorMessage(error.GetError()), error.what())); -+ // } catch (PdfError* perror) { -+ // LOG_ERR((0, "InitSignature", "SetAppearance error2: %s, %s", -+ // PdfError::ErrorMessage(perror->GetError()), perror->what())); -+ // } catch (std::exception& ex) { -+ // LOG_ERR( -+ // (0, "InitSignature", "SetAppearance std exception, %s", -+ // ex.what())); -+ // } catch (std::exception* pex) { -+ // LOG_ERR((0, "InitSignature", "SetAppearance std exception2, %s", -+ // pex->what())); -+ // } catch (...) { -+ // LOG_ERR((0, "InitSignature", "SetAppearance unknown error")); -+ // } -+ // } - -- if (szGraphometricData && szGraphometricData[0]) -- m_pSignatureField->SetGraphometricData( -- PdfString("Aruba_Sign_Biometric_Data"), PdfString(szGraphometricData), -- PdfString(szVersion)); -+ // if (szGraphometricData && szGraphometricData[0]) -+ // m_pSignatureField->SetGraphometricData( -+ // PdfString("Aruba_Sign_Biometric_Data"), -+ // PdfString(szGraphometricData), PdfString(szVersion)); - -- LOG_DBG((0, "InitSignature", "szGraphometricData OK")); -+ // LOG_DBG((0, "InitSignature", "szGraphometricData OK")); - - LOG_DBG((0, "InitSignature", "m_actualLen %d", m_actualLen)); - // crea il nuovo doc con il campo di firma -- int fulllen = m_actualLen * 2 + SIGNATURE_SIZE * 2 + -- (szGraphometricData -- ? (strlen(szGraphometricData) + strlen(szVersion) + 100) -- : 0); -+ int fulllen = m_actualLen * 2 + SIGNATURE_SIZE * 2; - - int mainDoclen = 0; - m_pMainDocbuffer = NULL; -@@ -227,7 +208,7 @@ void PdfSignatureGenerator::InitSignature( - LOG_DBG((0, "InitSignature", "fulllen %d", fulllen)); - m_pMainDocbuffer = new char[fulllen]; - PdfOutputDevice pdfOutDevice(m_pMainDocbuffer, fulllen); -- m_pPdfDocument->Write(&pdfOutDevice); -+ m_pPdfMemDocument->Write(&pdfOutDevice); - mainDoclen = pdfOutDevice.GetLength(); - } catch (::PoDoFo::PdfError err) { - if (m_pMainDocbuffer) { -@@ -301,32 +282,32 @@ void PdfSignatureGenerator::GetSignedPdf(UUCByteArray& signedPdf) { - } - - const double PdfSignatureGenerator::getWidth(int pageIndex) { -- if (m_pPdfDocument) { -- PdfPage* pPage = m_pPdfDocument->GetPage(pageIndex); -+ if (m_pPdfMemDocument) { -+ PdfPage* pPage = m_pPdfMemDocument->GetPage(pageIndex); - return pPage->GetPageSize().GetWidth(); - } - return 0; - } - - const double PdfSignatureGenerator::getHeight(int pageIndex) { -- if (m_pPdfDocument) { -- PdfPage* pPage = m_pPdfDocument->GetPage(pageIndex); -+ if (m_pPdfMemDocument) { -+ PdfPage* pPage = m_pPdfMemDocument->GetPage(pageIndex); - return pPage->GetPageSize().GetHeight(); - } - return 0; - } - - const double PdfSignatureGenerator::lastSignatureY(int left, int bottom) { -- if (!m_pPdfDocument) return -1; -+ if (!m_pPdfMemDocument) return -1; - /// Find the document catalog dictionary -- const PdfObject* const trailer = m_pPdfDocument->GetTrailer(); -+ const PdfObject* const trailer = m_pPdfMemDocument->GetTrailer(); - if (!trailer->IsDictionary()) return -1; - const PdfObject* const catalogRef = - trailer->GetDictionary().GetKey(PdfName("Root")); - if (catalogRef == 0 || !catalogRef->IsReference()) - return -2; // throw std::invalid_argument("Invalid /Root entry"); - const PdfObject* const catalog = -- m_pPdfDocument->GetObjects().GetObject(catalogRef->GetReference()); -+ m_pPdfMemDocument->GetObjects().GetObject(catalogRef->GetReference()); - if (catalog == 0 || !catalog->IsDictionary()) - return -3; // throw std::invalid_argument("Invalid or non-dictionary - // referenced by /Root entry"); -@@ -336,8 +317,8 @@ const double PdfSignatureGenerator::lastSignatureY(int left, int bottom) { - catalog->GetDictionary().GetKey(PdfName("AcroForm")); - if (acroFormValue == 0) return bottom; - if (acroFormValue->IsReference()) -- acroFormValue = -- m_pPdfDocument->GetObjects().GetObject(acroFormValue->GetReference()); -+ acroFormValue = m_pPdfMemDocument->GetObjects().GetObject( -+ acroFormValue->GetReference()); - - if (!acroFormValue->IsDictionary()) return bottom; - -@@ -346,8 +327,8 @@ const double PdfSignatureGenerator::lastSignatureY(int left, int bottom) { - if (fieldsValue == 0) return bottom; - - if (fieldsValue->IsReference()) -- fieldsValue = -- m_pPdfDocument->GetObjects().GetObject(acroFormValue->GetReference()); -+ fieldsValue = m_pPdfMemDocument->GetObjects().GetObject( -+ acroFormValue->GetReference()); - - if (!fieldsValue->IsArray()) return bottom; - -@@ -360,8 +341,8 @@ const double PdfSignatureGenerator::lastSignatureY(int left, int bottom) { - - for (unsigned int i = 0; i < array.size(); i++) { - const PdfObject* pObj = -- m_pPdfDocument->GetObjects().GetObject(array[i].GetReference()); -- if (IsSignatureField(m_pPdfDocument, pObj)) { -+ m_pPdfMemDocument->GetObjects().GetObject(array[i].GetReference()); -+ if (IsSignatureField(m_pPdfMemDocument, pObj)) { - const PdfObject* const keyRect = - pObj->GetDictionary().GetKey(PdfName("Rect")); - if (keyRect == 0) { -diff --git a/libs/sign-sdk/src/disigonsdk.cpp b/libs/sign-sdk/src/disigonsdk.cpp -index 250c93f..84e1b0b 100644 ---- a/libs/sign-sdk/src/disigonsdk.cpp -+++ b/libs/sign-sdk/src/disigonsdk.cpp -@@ -5,6 +5,7 @@ - - #include - #include -+#include - #include - #include - diff --git a/pkgs/by-name/de/dependabot-cli/package.nix b/pkgs/by-name/de/dependabot-cli/package.nix index ccce75d5721c..fef777d03e45 100644 --- a/pkgs/by-name/de/dependabot-cli/package.nix +++ b/pkgs/by-name/de/dependabot-cli/package.nix @@ -12,20 +12,20 @@ }: let pname = "dependabot-cli"; - version = "1.65.0"; + version = "1.66.0"; # `tag` is what `dependabot` uses to find the relevant docker images. tag = "nixpkgs-dependabot-cli-${version}"; # Get these hashes from # nix run nixpkgs#nix-prefetch-docker -- --image-name ghcr.io/github/dependabot-update-job-proxy/dependabot-update-job-proxy --image-tag latest --final-image-name dependabot-update-job-proxy --final-image-tag ${tag} - updateJobProxy.imageDigest = "sha256:ef245bd38aaa3cf89cafcffe0630d3ad3cff840488a2051a48517454e7f42368"; - updateJobProxy.hash = "sha256-yndoGLpyV2MiIs0QXbF/W0xJ6jtmnw/ezL54VM80/CI="; + updateJobProxy.imageDigest = "sha256:0b0d8c67cad11fa0885fcc3fe0add06638c29c19f05a83f80077d5dbb70c2037"; + updateJobProxy.hash = "sha256-7O/1NYdhtmO+MAwfu8BSaJQ1RVkXrFPBpfRy0N7p1lQ="; # Get these hashes from # nix run nixpkgs#nix-prefetch-docker -- --image-name ghcr.io/dependabot/dependabot-updater-github-actions --image-tag latest --final-image-name dependabot-updater-github-actions --final-image-tag ${tag} - updaterGitHubActions.imageDigest = "sha256:adeaa00b4ae49e942adccec76d4487a393eebd0dec27cd75a3cdf6cc46d801d7"; - updaterGitHubActions.hash = "sha256-ni9rSEpeo0gIdYy2CIIpnIWg0kttoTnvRwbZ71QwmIg="; + updaterGitHubActions.imageDigest = "sha256:11de6594db1c23e7ed4a6b621e8584b4a3b34484d51f2f8aa850c21fbce9094f"; + updaterGitHubActions.hash = "sha256-cImOCW7tggBWEPlmE55b4OFMxf/+VGLoqx0tRualowo="; in buildGoModule { inherit pname version; @@ -34,10 +34,10 @@ buildGoModule { owner = "dependabot"; repo = "cli"; rev = "v${version}"; - hash = "sha256-A7CPn0YDeyr+d1OUde2TGfSt3eCfrK4k3S7NWsvCGx0="; + hash = "sha256-9VgcQgiNv1v6+jnaWK10yccC1ILSxiIj9ZCIhHY57jk="; }; - vendorHash = "sha256-pnB1SkuEGm0KfkDfjnoff5fZRsAgD5w2H4UwsD3Jlbo="; + vendorHash = "sha256-gENlo1EPzsML+HkDBg4a2VGTUhyKY8AhlpHVszYWBno="; ldflags = [ "-s" diff --git a/pkgs/by-name/do/dooit/package.nix b/pkgs/by-name/do/dooit/package.nix index 361729775ae1..df0cfae3fc9d 100644 --- a/pkgs/by-name/do/dooit/package.nix +++ b/pkgs/by-name/do/dooit/package.nix @@ -9,14 +9,14 @@ }: python3.pkgs.buildPythonApplication rec { pname = "dooit"; - version = "3.2.2"; + version = "3.2.3"; pyproject = true; src = fetchFromGitHub { owner = "dooit-org"; repo = "dooit"; tag = "v${version}"; - hash = "sha256-2W3iO4AOuDdDKJDMMY8YKXlI+dQKRI3PQtkdi9J3wZo="; + hash = "sha256-bI9X+2tTLnQwxfsnBmy2vBI3lJ4UX418zOy3oniVKWc="; }; build-system = with python3.pkgs; [ poetry-core ]; diff --git a/pkgs/by-name/el/element-desktop/element-desktop-pin.nix b/pkgs/by-name/el/element-desktop/element-desktop-pin.nix index 1a72d1488ab6..178e8c7b5ce6 100644 --- a/pkgs/by-name/el/element-desktop/element-desktop-pin.nix +++ b/pkgs/by-name/el/element-desktop/element-desktop-pin.nix @@ -1,7 +1,7 @@ { - "version" = "1.11.100"; + "version" = "1.11.102"; "hashes" = { - "desktopSrcHash" = "sha256-qlKZkBPWcD1eyEetCrIKsSXmodg6DYCmENfY+UT7Khc="; - "desktopYarnHash" = "sha256-wuRAeb4IpA2Ihr3ohaMPvFsaMod4Bg8o9lm8yzStwmk="; + "desktopSrcHash" = "sha256-wefoN8Nk31lwJFYbBRoKfy+0n69yVg6jskqP6aTHApE="; + "desktopYarnHash" = "sha256-/Gy/sYk8EBWU07zXwPl0zsDW5ADRq1j5PH4lPFe8dxk="; }; } diff --git a/pkgs/by-name/el/element-desktop/package.nix b/pkgs/by-name/el/element-desktop/package.nix index 3e8ae7ee091f..a3683e80d6b7 100644 --- a/pkgs/by-name/el/element-desktop/package.nix +++ b/pkgs/by-name/el/element-desktop/package.nix @@ -4,15 +4,15 @@ fetchFromGitHub, makeWrapper, makeDesktopItem, - yarnConfigHook, + yarn, nodejs, - fetchYarnDeps, jq, - electron_35, + electron_36, element-web, sqlcipher, callPackage, desktopToDarwinBundle, + typescript, useKeytar ? true, # command line arguments which are always set commandLineArgs ? "", @@ -22,7 +22,7 @@ let pinData = import ./element-desktop-pin.nix; inherit (pinData.hashes) desktopSrcHash desktopYarnHash; executableName = "element-desktop"; - electron = electron_35; + electron = electron_36; keytar = callPackage ./keytar { inherit electron; }; @@ -41,16 +41,22 @@ stdenv.mkDerivation ( hash = desktopSrcHash; }; - offlineCache = fetchYarnDeps { - yarnLock = finalAttrs.src + "/yarn.lock"; - sha256 = desktopYarnHash; + # TODO: fetchYarnDeps currently does not deal properly with a dependency + # declared as a pin to a commit in a specific git repository. + # While it does download everything correctly, `yarn install --offline` + # always wants to `git ls-remote` to the repository, ignoring the local + # cached tarball. + offlineCache = callPackage ./yarn.nix { + inherit (finalAttrs) version src; + hash = desktopYarnHash; }; nativeBuildInputs = [ - yarnConfigHook nodejs makeWrapper jq + yarn + typescript ] ++ lib.optionals stdenv.hostPlatform.isDarwin [ desktopToDarwinBundle ]; inherit seshat; @@ -60,13 +66,21 @@ stdenv.mkDerivation ( # this shouldn't be in the closure just for unused scripts. dontPatchShebangs = true; + configurePhase = '' + mkdir -p node_modules/ + cp -r $offlineCache/node_modules/* node_modules/ + substituteInPlace package.json --replace-fail "tsx " "node node_modules/tsx/dist/cli.mjs " + ''; + buildPhase = '' runHook preBuild yarn --offline run build:ts - yarn --offline run i18n + node node_modules/matrix-web-i18n/scripts/gen-i18n.js + yarn --offline run i18n:sort yarn --offline run build:res + chmod -R a+w node_modules/keytar-forked rm -rf node_modules/matrix-seshat node_modules/keytar-forked ${lib.optionalString useKeytar "ln -s ${keytar} node_modules/keytar-forked"} ln -s $seshat node_modules/matrix-seshat @@ -82,6 +96,7 @@ stdenv.mkDerivation ( ln -s '${element-web}' "$out/share/element/webapp" cp -r '.' "$out/share/element/electron" cp -r './res/img' "$out/share/element" + chmod -R "a+w" "$out/share/element/electron/node_modules" rm -rf "$out/share/element/electron/node_modules" cp -r './node_modules' "$out/share/element/electron" cp $out/share/element/electron/lib/i18n/strings/en_EN.json $out/share/element/electron/lib/i18n/strings/en-us.json diff --git a/pkgs/by-name/el/element-desktop/yarn.nix b/pkgs/by-name/el/element-desktop/yarn.nix new file mode 100644 index 000000000000..5a1274349537 --- /dev/null +++ b/pkgs/by-name/el/element-desktop/yarn.nix @@ -0,0 +1,38 @@ +{ + stdenvNoCC, + yarn, + cacert, + git, + version, + src, + hash, +}: +stdenvNoCC.mkDerivation { + pname = "element-desktop-yarn-deps"; + inherit version src; + + nativeBuildInputs = [ + cacert + yarn + git + ]; + + dontInstall = true; + + NODE_EXTRA_CA_CERTS = "${cacert}/etc/ssl/certs/ca-bundle.crt"; + + buildPhase = '' + export HOME=$(mktemp -d) + export YARN_ENABLE_TELEMETRY=0 + + yarn install --frozen-lockfile --ignore-platform --skip-integrity-check --ignore-scripts --no-progress --non-interactive + + mkdir -p $out/node_modules + cp -r node_modules/* $out/node_modules/ + ''; + + dontPatchShebangs = true; + + outputHash = hash; + outputHashMode = "recursive"; +} diff --git a/pkgs/by-name/el/element-web-unwrapped/element-web-pin.nix b/pkgs/by-name/el/element-web-unwrapped/element-web-pin.nix index 2fb0517243dc..bf3c641d42d4 100644 --- a/pkgs/by-name/el/element-web-unwrapped/element-web-pin.nix +++ b/pkgs/by-name/el/element-web-unwrapped/element-web-pin.nix @@ -1,7 +1,7 @@ { - "version" = "1.11.100"; + "version" = "1.11.102"; "hashes" = { - "webSrcHash" = "sha256-FiYjWOJ50Vhbs9vgEqK64HTVtwSuy4/BZAkPK4c6DXQ="; - "webYarnHash" = "sha256-C1yVJHU9ClTJHQfMLkdZEeRWVVu68eJp2kxnIlLinY8="; + "webSrcHash" = "sha256-B21fBRcksX8MoyWiqf1sa9yowJ/Z/wcM3vEqslunv7A="; + "webYarnHash" = "sha256-NXoGMEJM4uxFMCb5YX0ue9hgxe22hzG0MTeAOaBrJO4="; }; } diff --git a/pkgs/by-name/fa/fakeroot/package.nix b/pkgs/by-name/fa/fakeroot/package.nix index 414880911221..a7fbca8e3305 100644 --- a/pkgs/by-name/fa/fakeroot/package.nix +++ b/pkgs/by-name/fa/fakeroot/package.nix @@ -41,7 +41,7 @@ stdenv.mkDerivation (finalAttrs: { autoreconfHook po4a ]; - buildInputs = lib.optional (!stdenv.hostPlatform.isDarwin) libcap; + buildInputs = lib.optional stdenv.hostPlatform.isLinux libcap; postUnpack = '' sed -i \ diff --git a/pkgs/by-name/fr/frankenphp/package.nix b/pkgs/by-name/fr/frankenphp/package.nix index 71dd976cbff0..075fd4bfa35e 100644 --- a/pkgs/by-name/fr/frankenphp/package.nix +++ b/pkgs/by-name/fr/frankenphp/package.nix @@ -31,13 +31,13 @@ let in buildGoModule rec { pname = "frankenphp"; - version = "1.6.2"; + version = "1.7.0"; src = fetchFromGitHub { owner = "dunglas"; repo = "frankenphp"; tag = "v${version}"; - hash = "sha256-YYEipvr0qqudKuLrINt5htl2oCp6wXWLpyvCPuwR9e8="; + hash = "sha256-FukxXuZwF7P5tUao0nTT7bELGIYivtoOBQQkHA7ZE3s="; }; sourceRoot = "${src.name}/caddy"; @@ -45,7 +45,7 @@ buildGoModule rec { # frankenphp requires C code that would be removed with `go mod tidy` # https://github.com/golang/go/issues/26366 proxyVendor = true; - vendorHash = "sha256-M1z9Yv+WomKA+UACOUT8xcWdw+SD1MY2jfaDO4zYUQg="; + vendorHash = "sha256-likIETO/eq1kATNbbYHxXxvIPH7q5tp3WHjz+zvNOws="; buildInputs = [ phpUnwrapped diff --git a/pkgs/by-name/ge/geminicommit/package.nix b/pkgs/by-name/ge/geminicommit/package.nix index ff58bebd43bd..2c83ac9c285a 100644 --- a/pkgs/by-name/ge/geminicommit/package.nix +++ b/pkgs/by-name/ge/geminicommit/package.nix @@ -4,20 +4,21 @@ fetchFromGitHub, installShellFiles, stdenv, + versionCheckHook, }: buildGoModule (finalAttrs: { pname = "geminicommit"; - version = "0.4.0"; + version = "0.4.1"; src = fetchFromGitHub { owner = "tfkhdyt"; repo = "geminicommit"; tag = "v${finalAttrs.version}"; - hash = "sha256-hJevJkniyICUUr1UyS0A5SKuuYRU0dGHMWzF99Yr2Eo="; + hash = "sha256-QUI5JI1udOo3IOXegoes3pwwgSfxXIjxXIPsA5SuAJo="; }; - vendorHash = "sha256-IfqlPg+HPcOfjlwwuLi2/R21UD83xQzWyUmzM7JSDEs="; + vendorHash = "sha256-AFm+1RQ6sMSe+kY/cw1Ly/8WEj2/yk0nJQiEJzV6jKg="; nativeBuildInputs = [ installShellFiles @@ -26,9 +27,18 @@ buildGoModule (finalAttrs: { postInstall = let cmd = finalAttrs.meta.mainProgram; + goDefaultCmd = finalAttrs.pname; in - lib.optionalString (with stdenv; buildPlatform.canExecute hostPlatform) '' - # `geminicommit` requires write permissions to $HOME for its `config.toml` + # The official github released binary is renamed since v0.4.1, + # see: https://github.com/tfkhdyt/geminicommit/releases/tag/v0.4.1 + # Here we link the old name (which is also the `go build` default name) + # for backward compatibility: + '' + mv $out/bin/${goDefaultCmd} $out/bin/${cmd} + ln -s $out/bin/${cmd} $out/bin/${goDefaultCmd} + '' + + lib.optionalString (with stdenv; buildPlatform.canExecute hostPlatform) '' + # `gmc` requires write permissions to $HOME for its `config.toml` # ... which is automatically initiated on startup export HOME=$(mktemp -d) @@ -39,11 +49,17 @@ buildGoModule (finalAttrs: { done ''; + nativeInstallCheckInputs = [ + versionCheckHook + ]; + + doInstallCheck = true; + meta = { description = "CLI that generates git commit messages with Google Gemini AI"; homepage = "https://github.com/tfkhdyt/geminicommit"; license = lib.licenses.gpl3Only; maintainers = with lib.maintainers; [ bryango ]; - mainProgram = "geminicommit"; + mainProgram = "gmc"; }; }) diff --git a/pkgs/applications/version-management/git-machete/default.nix b/pkgs/by-name/gi/git-machete/package.nix similarity index 88% rename from pkgs/applications/version-management/git-machete/default.nix rename to pkgs/by-name/gi/git-machete/package.nix index 5b9db51a215f..6dc30fe03830 100644 --- a/pkgs/applications/version-management/git-machete/default.nix +++ b/pkgs/by-name/gi/git-machete/package.nix @@ -1,15 +1,13 @@ { lib, - buildPythonApplication, - pytest-mock, - pytestCheckHook, + python3, fetchFromGitHub, installShellFiles, git, nix-update-script, }: -buildPythonApplication rec { +python3.pkgs.buildPythonApplication rec { pname = "git-machete"; version = "3.34.1"; @@ -22,11 +20,14 @@ buildPythonApplication rec { nativeBuildInputs = [ installShellFiles ]; - nativeCheckInputs = [ - git - pytest-mock - pytestCheckHook - ]; + nativeCheckInputs = + [ + git + ] + ++ (with python3.pkgs; [ + pytest-mock + pytestCheckHook + ]); disabledTests = [ # Requires fully functioning shells including zsh modules and bash diff --git a/pkgs/by-name/gi/git-town/package.nix b/pkgs/by-name/gi/git-town/package.nix index 471f2c3d0ff1..5eca702dc9d6 100644 --- a/pkgs/by-name/gi/git-town/package.nix +++ b/pkgs/by-name/gi/git-town/package.nix @@ -13,13 +13,13 @@ buildGoModule rec { pname = "git-town"; - version = "20.2.0"; + version = "21.0.0"; src = fetchFromGitHub { owner = "git-town"; repo = "git-town"; tag = "v${version}"; - hash = "sha256-q1wiqE2pd1qLw0nTw3CEpJybGia+KwRacgejofk4kJ8="; + hash = "sha256-Wnp3qkF7e358uGftBy5vi3GandyZsAyxESvgUFB7mmM="; }; vendorHash = null; diff --git a/pkgs/by-name/ha/harper/package.nix b/pkgs/by-name/ha/harper/package.nix index ea6eca7fe723..1a7c92eda526 100644 --- a/pkgs/by-name/ha/harper/package.nix +++ b/pkgs/by-name/ha/harper/package.nix @@ -7,18 +7,18 @@ rustPlatform.buildRustPackage rec { pname = "harper"; - version = "0.40.0"; + version = "0.41.0"; src = fetchFromGitHub { owner = "Automattic"; repo = "harper"; rev = "v${version}"; - hash = "sha256-QrIRfAehrKgJwAB3pWfSpIUnIwRLjuuRbTEgUpCe2LM="; + hash = "sha256-o5F9gDeHFq2U9q/kRQVn4otbbQVV4tg6n5Ap7Dwm7oI="; }; buildAndTestSubdir = "harper-ls"; useFetchCargoVendor = true; - cargoHash = "sha256-hkr7gNRwY+L45Efk6mWPrykj60n3ChryBKRFCC4W1Q0="; + cargoHash = "sha256-KgcsLzFrN+ZDgV6cZmkUv4tUt5ko4+giHq19NjfuF74="; passthru.updateScript = nix-update-script { }; diff --git a/pkgs/by-name/io/ior/package.nix b/pkgs/by-name/io/ior/package.nix index d0c018ebdcaf..78a197edf480 100644 --- a/pkgs/by-name/io/ior/package.nix +++ b/pkgs/by-name/io/ior/package.nix @@ -5,20 +5,25 @@ mpi, perl, autoreconfHook, + pkg-config, }: -stdenv.mkDerivation rec { +stdenv.mkDerivation (finalAttrs: { pname = "ior"; - version = "3.3.0"; + version = "4.0.0"; src = fetchFromGitHub { owner = "hpc"; repo = "ior"; - rev = version; - sha256 = "sha256-pSjptDfiPlaToXe1yHyk9MQMC9PqcVSjqAmWLD11iOM="; + tag = finalAttrs.version; + hash = "sha256-WsfJWHHfkiHZ+rPk6ck6mDErTXwt6Dhgm+yqOtw4Fvo="; }; - nativeBuildInputs = [ autoreconfHook ]; + nativeBuildInputs = [ + autoreconfHook + pkg-config + ]; + buildInputs = [ mpi perl @@ -26,11 +31,11 @@ stdenv.mkDerivation rec { enableParallelBuilding = true; - meta = with lib; { + meta = { homepage = "https://ior.readthedocs.io/en/latest/"; description = "Parallel file system I/O performance test"; - license = licenses.gpl2Only; - platforms = platforms.linux; - maintainers = with maintainers; [ bzizou ]; + license = lib.licenses.gpl2Only; + platforms = lib.platforms.linux; + maintainers = with lib.maintainers; [ bzizou ]; }; -} +}) diff --git a/pkgs/by-name/iw/iwmenu/package.nix b/pkgs/by-name/iw/iwmenu/package.nix new file mode 100644 index 000000000000..e42d3c12c42a --- /dev/null +++ b/pkgs/by-name/iw/iwmenu/package.nix @@ -0,0 +1,40 @@ +{ + dbus, + fetchFromGitHub, + lib, + iwd, + rustPlatform, +}: + +rustPlatform.buildRustPackage rec { + pname = "iwmenu"; + version = "0.2.0"; + + src = fetchFromGitHub { + owner = "e-tho"; + repo = "iwmenu"; + tag = "v${version}"; + hash = "sha256-F1w2Lp0/iXUHh0PXAZ/wD78C2uVtAcWlEKqBI5I/hnE="; + }; + + cargoHash = "sha256-NjA8n11pOytXsotEQurYxDHPhwXG5vpdlyscmVUIzfA="; + + buildInputs = [ + dbus + iwd + ]; + + meta = { + homepage = "https://github.com/e-tho/iwmenu"; + description = "Launcher-driven Wi-Fi manager for Linux"; + longDescription = '' + Use `iwmenu --launcher ` + Supported launchers are: `dmenu`, `fuzzel`, `rofi`, `walker` and `custom` with `stdin` + for details refer to https://github.com/e-tho/iwmenu/blob/main/README.md#usage + ''; + mainProgram = "iwmenu"; + platforms = lib.platforms.linux; + license = lib.licenses.gpl3Plus; + maintainers = with lib.maintainers; [ vuimuich ]; + }; +} diff --git a/pkgs/by-name/jq/jq/disable-end-of-epoch-conversion-test.patch b/pkgs/by-name/jq/jq/disable-end-of-epoch-conversion-test.patch new file mode 100644 index 000000000000..570d443db12f --- /dev/null +++ b/pkgs/by-name/jq/jq/disable-end-of-epoch-conversion-test.patch @@ -0,0 +1,16 @@ +diff --git a/tests/optional.test b/tests/optional.test +index 2623731..85bc9e9 100644 +--- a/tests/optional.test ++++ b/tests/optional.test +@@ -12,11 +12,6 @@ last(range(365 * 67)|("1970-03-01T01:02:03Z"|strptime("%Y-%m-%dT%H:%M:%SZ")|mkti + null + [2037,1,11,1,2,3,3,41] + +-# Regression test for #3276 +-fromdate +-"2038-01-19T03:14:08Z" +-2147483648 +- + # %e is not available on mingw/WIN32 + strftime("%A, %B %e, %Y") + 1435677542.822351 diff --git a/pkgs/by-name/jq/jq/package.nix b/pkgs/by-name/jq/jq/package.nix index 074e24725fc6..5d3a7c3aa975 100644 --- a/pkgs/by-name/jq/jq/package.nix +++ b/pkgs/by-name/jq/jq/package.nix @@ -30,6 +30,15 @@ stdenv.mkDerivation (finalAttrs: { "out" ]; + # tortured syntax to avoid rebuilds + # needed because epoch conversion test here is right at the end of 32 bit integer space + # See also: https://github.com/jqlang/jq/blob/859a8073ee8a21f2133154eea7c2bd5e0d60837f/tests/optional.test#L15-L18 + # "-D_TIME_BITS=64 -D_FILE_OFFSET_BITS=64" would be preferrable, but breaks with dynamic linking, + # unless done globally in stdenv for all of 32 bit. + ${if stdenv.hostPlatform.is32bit then "patches" else null} = [ + ./disable-end-of-epoch-conversion-test.patch + ]; + # https://github.com/jqlang/jq/issues/2871 postPatch = lib.optionalString stdenv.hostPlatform.isFreeBSD '' substituteInPlace Makefile.am --replace-fail "tests/mantest" "" --replace-fail "tests/optionaltest" "" diff --git a/pkgs/by-name/ko/kopia-ui/fix-paths.patch b/pkgs/by-name/ko/kopia-ui/fix-paths.patch index 22744100099e..8086285f0df4 100644 --- a/pkgs/by-name/ko/kopia-ui/fix-paths.patch +++ b/pkgs/by-name/ko/kopia-ui/fix-paths.patch @@ -1,42 +1,63 @@ diff --git a/public/utils.js b/public/utils.js -index 3cd38b63..54152694 100644 +index c5963e41..30f72965 100644 --- a/public/utils.js +++ b/public/utils.js -@@ -17,7 +17,7 @@ const osShortName = function () { +@@ -17,7 +17,7 @@ const osShortName = (function () { export function iconsPath() { - if (!app.isPackaged) { -- return path.join(__dirname, "..", "resources", osShortName, "icons"); -+ return path.join(__dirname, "..", "..", "icons"); - } + if (!app.isPackaged) { +- return path.join(__dirname, "..", "resources", osShortName, "icons"); ++ return path.join(__dirname, "..", "..", "icons"); + } - return path.join(process.resourcesPath, "icons"); -@@ -25,26 +25,14 @@ export function iconsPath() { + return path.join(process.resourcesPath, "icons"); +@@ -25,47 +25,14 @@ export function iconsPath() { export function publicPath() { - if (!app.isPackaged) { -- return path.join(__dirname, "..", "public"); -+ return path.join(__dirname, "..", "..", "public"); - } + if (!app.isPackaged) { +- return path.join(__dirname, "..", "public"); ++ return path.join(__dirname, "..", "..", "public"); + } - return process.resourcesPath; + return process.resourcesPath; } export function defaultServerBinary() { -- if (!app.isPackaged) { -- return { -- "mac": path.join(__dirname, "..", "..", "dist", "kopia_darwin_amd64", "kopia"), -- "win": path.join(__dirname, "..", "..", "dist", "kopia_windows_amd64", "kopia.exe"), -- "linux": path.join(__dirname, "..", "..", "dist", "kopia_linux_amd64", "kopia"), -- }[osShortName] -- } -- +- if (!app.isPackaged) { - return { -- "mac": path.join(process.resourcesPath, "server", "kopia"), -- "win": path.join(process.resourcesPath, "server", "kopia.exe"), -- "linux": path.join(process.resourcesPath, "server", "kopia"), -- }[osShortName] -+ return "KOPIA" +- mac: path.join( +- __dirname, +- "..", +- "..", +- "dist", +- "kopia_darwin_amd64", +- "kopia", +- ), +- win: path.join( +- __dirname, +- "..", +- "..", +- "dist", +- "kopia_windows_amd64", +- "kopia.exe", +- ), +- linux: path.join( +- __dirname, +- "..", +- "..", +- "dist", +- "kopia_linux_amd64", +- "kopia", +- ), +- }[osShortName]; +- } +- +- return { +- mac: path.join(process.resourcesPath, "server", "kopia"), +- win: path.join(process.resourcesPath, "server", "kopia.exe"), +- linux: path.join(process.resourcesPath, "server", "kopia"), +- }[osShortName]; ++ return "KOPIA"; } export function selectByOS(x) { - return x[osShortName] + return x[osShortName]; diff --git a/pkgs/by-name/ko/kopia-ui/package.nix b/pkgs/by-name/ko/kopia-ui/package.nix index 3becaeac4ecd..989146e16853 100644 --- a/pkgs/by-name/ko/kopia-ui/package.nix +++ b/pkgs/by-name/ko/kopia-ui/package.nix @@ -10,12 +10,12 @@ kopia, }: let - version = "0.20.0"; + version = "0.20.1"; src = fetchFromGitHub { owner = "kopia"; repo = "kopia"; tag = "v${version}"; - hash = "sha256-AM9Mpy+9ZCvEtFtzYC87vqCNpLxO+yWVd4th9DDQ2QI="; + hash = "sha256-hKtrHv7MQjA/AQ/frjP2tPT6zqVPPGnBxYuhWtUgIl0="; }; in buildNpmPackage { @@ -24,7 +24,7 @@ buildNpmPackage { sourceRoot = "${src.name}/app"; - npmDepsHash = "sha256-FXPfZnqpuI6NgHk81HEJ7Hj8xCpXD0BKJgFeQ/Oce04="; + npmDepsHash = "sha256-5gXM5aLsUsJhxbt6IKY4Sg4SBI9ATe248K1TyZThg/0="; makeCacheWritable = true; nativeBuildInputs = [ diff --git a/pkgs/by-name/la/ladybird/001-revert-fake-skia-update.patch b/pkgs/by-name/la/ladybird/001-revert-fake-skia-update.patch deleted file mode 100644 index 257fa212fc55..000000000000 --- a/pkgs/by-name/la/ladybird/001-revert-fake-skia-update.patch +++ /dev/null @@ -1,13 +0,0 @@ -diff --git a/vcpkg.json b/vcpkg.json -index 5a824884b2..e4193aa45f 100644 ---- a/vcpkg.json -+++ b/vcpkg.json -@@ -199,7 +199,7 @@ - }, - { - "name": "skia", -- "version": "134#2" -+ "version": "129#0" - }, - { - "name": "sqlite3", diff --git a/pkgs/by-name/la/ladybird/package.nix b/pkgs/by-name/la/ladybird/package.nix index 45cc7bd1b20d..7c054e74e004 100644 --- a/pkgs/by-name/la/ladybird/package.nix +++ b/pkgs/by-name/la/ladybird/package.nix @@ -38,22 +38,15 @@ let in stdenv.mkDerivation (finalAttrs: { pname = "ladybird"; - version = "0-unstable-2025-05-24"; + version = "0-unstable-2025-06-03"; src = fetchFromGitHub { owner = "LadybirdWebBrowser"; repo = "ladybird"; - rev = "fbd1f771613fc6f13fcc20dcad04c7065633a2c2"; - hash = "sha256-Gtfnq46JrzfpcapMr6Ez+5BNQ59H/Djsgp7n6QvMSUM="; + rev = "4c54a28c45be4e8185158d40a37e083e038a6465"; + hash = "sha256-YHWkG2RJk6NaouRvis2L+njtYWKB7T569y1Tq+mYdz0="; }; - patches = [ - # Revert https://github.com/LadybirdBrowser/ladybird/commit/51d189198d3fc61141fc367dc315c7f50492a57e - # This commit doesn't update the skia used by ladybird vcpkg, but it does update the skia that - # that cmake wants. - ./001-revert-fake-skia-update.patch - ]; - postPatch = '' sed -i '/iconutil/d' UI/CMakeLists.txt @@ -146,7 +139,7 @@ stdenv.mkDerivation (finalAttrs: { # ld: [...]/OESVertexArrayObject.cpp.o: undefined reference to symbol 'glIsVertexArrayOES' # ld: [...]/libGL.so.1: error adding symbols: DSO missing from command line # https://github.com/LadybirdBrowser/ladybird/issues/371#issuecomment-2616415434 - env.NIX_LDFLAGS = "-lGL"; + env.NIX_LDFLAGS = "-lGL -lfontconfig"; postInstall = lib.optionalString stdenv.hostPlatform.isDarwin '' mkdir -p $out/Applications $out/bin diff --git a/pkgs/by-name/li/libast/package.nix b/pkgs/by-name/li/libast/package.nix deleted file mode 100644 index 3ac848203286..000000000000 --- a/pkgs/by-name/li/libast/package.nix +++ /dev/null @@ -1,33 +0,0 @@ -{ - lib, - stdenv, - fetchFromGitHub, - autoreconfHook, - pkg-config, -}: - -stdenv.mkDerivation rec { - pname = "libast"; - version = "0.8"; - - src = fetchFromGitHub { - owner = "mej"; - repo = "libast"; - rev = version; - hash = "sha256-rnqToFi+d6D6O+JDHQxkVjTc/0RBag6Jqv4uDcE4PNc="; - }; - - nativeBuildInputs = [ - autoreconfHook - pkg-config - ]; - - meta = with lib; { - inherit (src.meta) homepage; - description = "Library of Assorted Spiffy Things"; - mainProgram = "libast-config"; - license = licenses.bsd2; - maintainers = [ ]; - platforms = platforms.unix; - }; -} diff --git a/pkgs/by-name/li/lillydap/package.nix b/pkgs/by-name/li/lillydap/package.nix index d095f3a4a8ca..115673095020 100644 --- a/pkgs/by-name/li/lillydap/package.nix +++ b/pkgs/by-name/li/lillydap/package.nix @@ -6,7 +6,7 @@ gperf, arpa2cm, quickder, - nix-update-script, + gitUpdater, }: stdenv.mkDerivation (finalAttrs: { @@ -32,7 +32,7 @@ stdenv.mkDerivation (finalAttrs: { quickder ]; - passthru.updateScript = nix-update-script { }; + passthru.updateScript = gitUpdater { rev-prefix = "v"; }; meta = { description = "Little LDAP: Event-driven, lock-free kernel for dynamic data servers, clients, filters"; diff --git a/pkgs/by-name/lx/lxdvdrip/package.nix b/pkgs/by-name/lx/lxdvdrip/package.nix deleted file mode 100644 index 600913966764..000000000000 --- a/pkgs/by-name/lx/lxdvdrip/package.nix +++ /dev/null @@ -1,39 +0,0 @@ -{ - lib, - stdenv, - fetchurl, - libdvdread, -}: - -stdenv.mkDerivation rec { - pname = "lxdvdrip"; - version = "1.77"; - - src = fetchurl { - url = "mirror://sourceforge/lxdvdrip/lxdvdrip-${version}.tgz"; - hash = "sha256-OzHrscftsCmJvSw7bb/Z2WDP322VCuQDY58dW2OqxB8="; - }; - - postPatch = '' - sed -i -e s,/usr/local,$out, -e s,/etc,$out/etc,g Makefile - sed -i -e s,/usr/local,$out, mbuffer/Makefile - ''; - - makeFlags = [ - "PREFIX=${placeholder "out"}" - ]; - - preInstall = '' - mkdir -p $out/man/man1 $out/bin $out/share $out/etc - ''; - - buildInputs = [ libdvdread ]; - - meta = { - broken = (stdenv.hostPlatform.isLinux && stdenv.hostPlatform.isAarch64); - description = "Command line tool to make a copy from a video DVD for private use"; - homepage = "https://sourceforge.net/projects/lxdvdrip"; - license = lib.licenses.gpl2; - platforms = lib.platforms.linux; - }; -} diff --git a/pkgs/by-name/mp/mpls/package.nix b/pkgs/by-name/mp/mpls/package.nix index 0e9abac8f657..baca6ca2a8d9 100644 --- a/pkgs/by-name/mp/mpls/package.nix +++ b/pkgs/by-name/mp/mpls/package.nix @@ -7,13 +7,13 @@ }: buildGoModule rec { pname = "mpls"; - version = "0.15.2"; + version = "0.15.3"; src = fetchFromGitHub { owner = "mhersson"; repo = "mpls"; tag = "v${version}"; - hash = "sha256-UQIGg31OJ8vTqlj5JLYxOxg9oS0+PXPcdocAJbUgpzY="; + hash = "sha256-MGrvJOnjNNXU8Z9rqDIacb5awKxf50xYeNkY06U4cUk="; }; vendorHash = "sha256-n3DG3sR7HOQPQJW1t1qC94EKkDBgXpdmjUWtLzAE7kY="; diff --git a/pkgs/by-name/mu/multimarkdown/package.nix b/pkgs/by-name/mu/multimarkdown/package.nix index 20b53464d92c..b69eb9fc27d1 100644 --- a/pkgs/by-name/mu/multimarkdown/package.nix +++ b/pkgs/by-name/mu/multimarkdown/package.nix @@ -7,35 +7,28 @@ pkg-config, }: -stdenv.mkDerivation rec { +stdenv.mkDerivation (finalAttrs: { pname = "multimarkdown"; - version = "6.6.0"; + version = "6.7.0"; src = fetchFromGitHub { owner = "fletcher"; repo = "MultiMarkdown-6"; - rev = version; - hash = "sha256-emJbY0wucoc/GdjlILoeqjwuwuPpTjXTqZN0gUKOyLg="; + tag = finalAttrs.version; + hash = "sha256-b6yCn0NFpONI7WwfjDOc0d2nCKMIiUXi+rsnytiNc0Q="; }; postPatch = '' patchShebangs tools/enumsToPerl.pl ''; - postInstall = '' - # Move files from $out/ to sub directories to prevent conflicts - # with other packages: - mkdir -p $out/share/doc/multimarkdown/ - mv $out/LICENSE.txt $out/README.txt $out/share/doc/multimarkdown/ - ''; - nativeBuildInputs = [ cmake perl pkg-config ]; - meta = with lib; { + meta = { homepage = "https://fletcher.github.io/MultiMarkdown-6/introduction.html"; description = "Derivative of Markdown that adds new syntax features"; longDescription = '' @@ -59,8 +52,8 @@ stdenv.mkDerivation rec { - glossary entries (LaTeX only) - document metadata (e.g. title, author, date, etc.) ''; - license = with licenses; [ mit ]; - platforms = platforms.all; - maintainers = with maintainers; [ ]; + license = with lib.licenses; [ mit ]; + platforms = lib.platforms.all; + maintainers = with lib.maintainers; [ ]; }; -} +}) diff --git a/pkgs/by-name/ne/neuron/package.nix b/pkgs/by-name/ne/neuron/package.nix index d6b7098654bc..5456114455d3 100644 --- a/pkgs/by-name/ne/neuron/package.nix +++ b/pkgs/by-name/ne/neuron/package.nix @@ -24,7 +24,7 @@ let in stdenv.mkDerivation (finalAttrs: { pname = "neuron"; - version = "8.2.6"; + version = "8.2.7"; # format is for pythonModule conversion format = "other"; @@ -64,7 +64,7 @@ stdenv.mkDerivation (finalAttrs: { python3.pkgs.mpi4py ] ++ optionals useRx3d [ - python3.pkgs.cython_0 # NOTE: cython<3 is required as of 8.2.6 + python3.pkgs.cython_0 # NOTE: cython<3 is required as of 8.2.7 python3.pkgs.numpy ]; @@ -99,7 +99,7 @@ stdenv.mkDerivation (finalAttrs: { repo = "nrn"; rev = finalAttrs.version; fetchSubmodules = true; - hash = "sha256-xASBpsF8rIzrb5G+4Qi6rvWC2wqL7nAGlSeMsBAI6WM="; + hash = "sha256-dmpx0Wud0IhdFvvTJuW/w1Uq6vFYaNal9n27LAqV1Qc="; }; meta = with lib; { diff --git a/pkgs/by-name/ni/niri/package.nix b/pkgs/by-name/ni/niri/package.nix index 338564c76338..3c473ac59cd2 100644 --- a/pkgs/by-name/ni/niri/package.nix +++ b/pkgs/by-name/ni/niri/package.nix @@ -3,6 +3,7 @@ dbus, eudev, fetchFromGitHub, + installShellFiles, libdisplay-info, libglvnd, libinput, @@ -15,6 +16,7 @@ pkg-config, rustPlatform, seatd, + stdenv, systemd, wayland, withDbus ? true, @@ -46,6 +48,7 @@ rustPlatform.buildRustPackage (finalAttrs: { strictDeps = true; nativeBuildInputs = [ + installShellFiles pkg-config rustPlatform.bindgenHook ]; @@ -88,6 +91,12 @@ rustPlatform.buildRustPackage (finalAttrs: { '' + lib.optionalString withDinit '' install -Dm0644 resources/dinit/niri{-shutdown,} -t $out/lib/dinit.d/user + '' + + lib.optionalString (stdenv.buildPlatform.canExecute stdenv.hostPlatform) '' + installShellCompletion --cmd $pname \ + --bash <($out/bin/niri completions bash) \ + --fish <($out/bin/niri completions fish) \ + --zsh <($out/bin/niri completions zsh) ''; env = { diff --git a/pkgs/by-name/ra/radicle-node/61865b5b5ad715e2b812087947281f0add9aa05e.patch b/pkgs/by-name/ra/radicle-node/61865b5b5ad715e2b812087947281f0add9aa05e.patch deleted file mode 100644 index 742ac55a7f54..000000000000 --- a/pkgs/by-name/ra/radicle-node/61865b5b5ad715e2b812087947281f0add9aa05e.patch +++ /dev/null @@ -1,22 +0,0 @@ -commit 61865b5b5ad715e2b812087947281f0add9aa05e -Author: Fintan Halpenny -Date: 2024-11-19 14:38:38 +0100 - - cob: fix documentation - - `no_run` will compile but not run the code. Use `ignore` instead, and also added - `rust` for syntax highlighting. - -diff --git a/radicle-cob/src/backend/git/stable.rs b/radicle-cob/src/backend/git/stable.rs -index 0d8ea27b..ef18ae55 100644 ---- a/radicle-cob/src/backend/git/stable.rs -+++ b/radicle-cob/src/backend/git/stable.rs -@@ -53,7 +53,7 @@ pub fn read_timestamp() -> i64 { - /// - /// # Usage - /// --/// ```no_run -+/// ```rust, ignore - /// let oid1 = with_advanced_timestamp(|| cob.update("New revision OID")); - /// let oid2 = with_advanced_timestamp(|| cob.update("Another revision OID")); - /// ``` diff --git a/pkgs/by-name/ra/radicle-node/package.nix b/pkgs/by-name/ra/radicle-node/package.nix index 22dab16a679e..9024e11bddde 100644 --- a/pkgs/by-name/ra/radicle-node/package.nix +++ b/pkgs/by-name/ra/radicle-node/package.nix @@ -19,20 +19,22 @@ }: rustPlatform.buildRustPackage rec { pname = "radicle-node"; - version = "1.1.0"; + version = "1.2.0"; env.RADICLE_VERSION = version; src = fetchgit { url = "https://seed.radicle.xyz/z3gqcJUoA1n9HaHKufZs5FCSGazv5.git"; - rev = "refs/namespaces/z6MksFqXN3Yhqk8pTJdUGLwATkRfQvwZXPqR2qMEhbS9wzpT/refs/tags/v${version}"; - hash = "sha256-M4oz9tWjI/eqV4Gz1b512MEmvsZ5u3R9y6P9VeeH9CA="; + rev = "refs/namespaces/z6MkireRatUThvd3qzfKht1S44wpm4FEWSSa4PRMTSQZ3voM/refs/tags/v${version}"; + hash = "sha256-AWgLhL6GslE3r2FcZu2imV5ZtEKlUD+a4C5waRGO2lM="; + leaveDotGit = true; + postFetch = '' + git -C $out rev-parse HEAD > $out/.git_head + git -C $out log -1 --pretty=%ct HEAD > $out/.git_time + rm -rf $out/.git + ''; }; useFetchCargoVendor = true; - cargoHash = "sha256-SzwBQxTqQafHDtH8+OWkAMDnKh3AH0PeSMBWpHprQWM="; - - patches = [ - ./61865b5b5ad715e2b812087947281f0add9aa05e.patch - ]; + cargoHash = "sha256-/6VlRwWtJfHf6tXD2HJUTbThwTYeZFTJqtaxclrm3+c="; nativeBuildInputs = [ asciidoctor @@ -41,6 +43,11 @@ rustPlatform.buildRustPackage rec { ]; nativeCheckInputs = [ git ]; + preBuild = '' + export GIT_HEAD=$(<$src/.git_head) + export SOURCE_DATE_EPOCH=$(<$src/.git_time) + ''; + # tests regularly time out on aarch64 doCheck = stdenv.hostPlatform.isLinux && stdenv.hostPlatform.isx86; diff --git a/pkgs/by-name/ro/roon-server/package.nix b/pkgs/by-name/ro/roon-server/package.nix index 63b751a2a2e6..058b724b5fa8 100644 --- a/pkgs/by-name/ro/roon-server/package.nix +++ b/pkgs/by-name/ro/roon-server/package.nix @@ -16,7 +16,7 @@ stdenv, }: let - version = "2.51.1534"; + version = "2.52.1538"; urlVersion = builtins.replaceStrings [ "." ] [ "0" ] version; in stdenv.mkDerivation { @@ -25,7 +25,7 @@ stdenv.mkDerivation { src = fetchurl { url = "https://download.roonlabs.com/updates/production/RoonServer_linuxx64_${urlVersion}.tar.bz2"; - hash = "sha256-x9zbWJ4lrqfC1CPquGsdgzhO3WBzd46dlZy6APqJbcg="; + hash = "sha256-pWg1Cp8aNdR/hoVZDF3kUznJtYsjJYX9J4g1xbmn/lg="; }; dontConfigure = true; @@ -104,6 +104,7 @@ stdenv.mkDerivation { runHook postInstall ''; + passthru.updateScript = ./update.py; meta = with lib; { description = "Music player for music lovers"; changelog = "https://community.roonlabs.com/c/roon/software-release-notes/18"; @@ -113,6 +114,7 @@ stdenv.mkDerivation { maintainers = with maintainers; [ lovesegfault steell + ramblurr ]; platforms = [ "x86_64-linux" ]; mainProgram = "RoonServer"; diff --git a/pkgs/by-name/ro/roon-server/update.py b/pkgs/by-name/ro/roon-server/update.py new file mode 100755 index 000000000000..7be5e4a00bb3 --- /dev/null +++ b/pkgs/by-name/ro/roon-server/update.py @@ -0,0 +1,131 @@ +#!/usr/bin/env nix-shell +#!nix-shell -I nixpkgs=./. -i python3 -p python3 curl common-updater-scripts nix coreutils + +""" +Updater script for the roon-server package. +""" + +import subprocess +import urllib.request +import re +import sys +import os + + +def get_current_version(): + """Get the current version of roon-server from the package.nix file.""" + result = subprocess.run( + [ + "nix-instantiate", + "--eval", + "-E", + "with import ./. {}; roon-server.version or (lib.getVersion roon-server)", + ], + capture_output=True, + text=True, + ) + result.check_returncode() + return result.stdout.strip().strip('"') + + +def get_latest_version_info(): + """Get the latest version information from the Roon Labs API.""" + url = "https://updates.roonlabs.net/update/?v=2&platform=linux&version=&product=RoonServer&branding=roon&branch=production&curbranch=production" + with urllib.request.urlopen(url) as response: + content = response.read().decode("utf-8") + + # Parse the response + info = {} + for line in content.splitlines(): + if "=" in line: + key, value = line.split("=", 1) + info[key] = value + + return info + + +def parse_version(display_version): + """Parse the display version string to get the version in the format used in the package.nix file.""" + # Example: "2.47 (build 1510) production" -> "2.47.1510" + match = re.search(r"(\d+\.\d+)\s+\(build\s+(\d+)\)", display_version) + if match: + return f"{match.group(1)}.{match.group(2)}" + return None + + +def get_hash(url): + """Calculate the hash of the package.""" + result = subprocess.run( + ["nix-prefetch-url", "--type", "sha256", url], capture_output=True, text=True + ) + result.check_returncode() + pkg_hash = result.stdout.strip() + + result = subprocess.run( + ["nix", "hash", "to-sri", f"sha256:{pkg_hash}"], capture_output=True, text=True + ) + result.check_returncode() + return result.stdout.strip() + + +def update_package(new_version, hash_value): + """Update the package.nix file with the new version and hash.""" + subprocess.run( + [ + "update-source-version", + "roon-server", + new_version, + hash_value, + "--ignore-same-version", + ], + check=True, + ) + + +def main(): + current_version = get_current_version() + print(f"Current roon-server version: {current_version}") + + try: + latest_info = get_latest_version_info() + + display_version = latest_info.get("displayversion", "") + download_url = latest_info.get("updateurl", "") + + if not display_version or not download_url: + print("Error: Failed to get version information from Roon Labs API") + sys.exit(1) + + print(f"Latest version from API: {display_version}") + print(f"Download URL: {download_url}") + + new_version = parse_version(display_version) + if not new_version: + print( + f"Error: Failed to parse version from display version: {display_version}" + ) + sys.exit(1) + + print(f"Parsed version: {new_version}") + + if new_version == current_version: + print("roon-server is already up to date!") + return + + print(f"Calculating hash for new version {new_version}...") + hash_value = get_hash(download_url) + + print( + f"Updating package.nix with new version {new_version} and hash {hash_value}" + ) + update_package(new_version, hash_value) + + print(f"Successfully updated roon-server to version {new_version}") + + except Exception as e: + print(f"Error: {e}") + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/pkgs/by-name/se/sesh/package.nix b/pkgs/by-name/se/sesh/package.nix index 2eaa1c09b4d7..98409eb9b19a 100644 --- a/pkgs/by-name/se/sesh/package.nix +++ b/pkgs/by-name/se/sesh/package.nix @@ -5,13 +5,13 @@ }: buildGoModule rec { pname = "sesh"; - version = "2.13.0"; + version = "2.14.0"; src = fetchFromGitHub { owner = "joshmedeski"; repo = "sesh"; rev = "v${version}"; - hash = "sha256-YFvUYacuvyzNXwY+y9kI4tPlrlojDuZpR7VaTGdVqb8="; + hash = "sha256-Dla43xI6y7J9M18IloSm1uDeHAhfslU56Z0Q3nVzjIk="; }; vendorHash = "sha256-3wNp1meUoUFPa2CEgKjuWcu4I6sxta3FPFvCb9QMQhQ="; diff --git a/pkgs/by-name/sh/shaperglot-cli/package.nix b/pkgs/by-name/sh/shaperglot-cli/package.nix new file mode 100644 index 000000000000..57e63811bc50 --- /dev/null +++ b/pkgs/by-name/sh/shaperglot-cli/package.nix @@ -0,0 +1,64 @@ +{ + lib, + fetchFromGitHub, + rustPlatform, + _experimental-update-script-combinators, + unstableGitUpdater, + nix-update-script, +}: + +rustPlatform.buildRustPackage (finalAttrs: { + pname = "shaperglot-cli"; + version = "0-unstable-2025-05-27"; + + src = fetchFromGitHub { + owner = "googlefonts"; + repo = "shaperglot"; + rev = "0d934110dfdf315761255e34040f207f7d7868b5"; + hash = "sha256-5Bgvx4Yv74nQLd037L5uBj6oySqqp947LI/6yGwYSKY="; + }; + + cargoHash = "sha256-UMPoPNpyM/+1rq4U6xQ1DF4W+51p5YjQXr/8zLiPvEI="; + + cargoBuildFlags = [ + "--package=shaperglot-cli" + ]; + + cargoTestFlags = [ + "--package=shaperglot-cli" + ]; + + doInstallCheck = true; + installCheckPhase = '' + runHook preInstallCheck + + describe_output="$("$out/bin/shaperglot" describe English)" + [[ "$describe_output" == *'support'* ]] + + runHook postInstallCheck + ''; + + passthru = { + updateScript = _experimental-update-script-combinators.sequence [ + (unstableGitUpdater { + branch = "main"; + # Git tag differs from CLI version: https://github.com/googlefonts/shaperglot/issues/138 + hardcodeZeroVersion = true; + }) + (nix-update-script { + # Updating `cargoHash` + extraArgs = [ "--version=skip" ]; + }) + ]; + }; + + meta = { + description = "Test font files for language support"; + homepage = "https://github.com/googlefonts/shaperglot"; + license = lib.licenses.asl20; + maintainers = with lib.maintainers; [ + kachick + ]; + mainProgram = "shaperglot"; + }; +}) diff --git a/pkgs/by-name/sh/shottr/package.nix b/pkgs/by-name/sh/shottr/package.nix new file mode 100644 index 000000000000..4de63b3f11a3 --- /dev/null +++ b/pkgs/by-name/sh/shottr/package.nix @@ -0,0 +1,65 @@ +{ + lib, + stdenvNoCC, + fetchurl, + writeShellApplication, + cacert, + curl, + common-updater-scripts, + pup, + undmg, +}: + +stdenvNoCC.mkDerivation (finalAttrs: { + pname = "shottr"; + version = "1.8.1"; + + src = fetchurl { + url = "https://shottr.cc/dl/Shottr-${finalAttrs.version}.dmg"; + hash = "sha256-I3LNLuhIRdjKDn79HWRK2B/tVsV+1aGt/aY442y3r2I="; + }; + + nativeBuildInputs = [ undmg ]; + + sourceRoot = "."; + + installPhase = '' + runHook preInstall + + mkdir -p "$out/Applications" + cp -R Shottr.app "$out/Applications" + + mkdir -p "$out/bin" + ln -s "$out/Applications/Shottr.app/Contents/MacOS/Shottr" "$out/bin/shottr" + + runHook postInstall + ''; + + passthru.updateScript = lib.getExe (writeShellApplication { + name = "shottr-update-script"; + runtimeInputs = [ + cacert + common-updater-scripts + curl + pup + ]; + text = '' + version="$(curl -s https://shottr.cc/newversion.html \ + | pup 'a[href*="Shottr-"] attr{href}' \ + | sed -E 's|/dl/Shottr-||' \ + | sed -E 's|\.dmg||')" + update-source-version shottr "$version" + ''; + }); + + meta = { + changelog = "https://shottr.cc/newversion.html"; + description = "MacOS screenshot app with scrolling screenshots, OCR, annotation and measurement instruments"; + homepage = "https://shottr.cc/"; + license = lib.licenses.unfree; + mainProgram = "shottr"; + maintainers = with lib.maintainers; [ donteatoreo ]; + platforms = lib.platforms.darwin; + sourceProvenance = with lib.sourceTypes; [ binaryNativeCode ]; + }; +}) diff --git a/pkgs/by-name/st/stockfish/package.nix b/pkgs/by-name/st/stockfish/package.nix index 50216db2b95e..02cfd355f012 100644 --- a/pkgs/by-name/st/stockfish/package.nix +++ b/pkgs/by-name/st/stockfish/package.nix @@ -3,7 +3,12 @@ stdenv, fetchurl, fetchFromGitHub, - apple-sdk, + versionCheckHook, + _experimental-update-script-combinators, + nix-update-script, + writeShellApplication, + nix, + gnugrep, }: let @@ -24,29 +29,31 @@ let "unknown"; # These files can be found in src/evaluate.h - nnueBigFile = "nn-1111cefa1111.nnue"; + nnueBigFile = "nn-1c0000000000.nnue"; + nnueBigHash = "sha256-HAAAAAAApn1imZnZMtDDc/dFDOQ80S0FYoaPTq+a4q0="; nnueBig = fetchurl { name = nnueBigFile; url = "https://tests.stockfishchess.org/api/nn/${nnueBigFile}"; - sha256 = "sha256-ERHO+hERa3cWG9SxTatMUPJuWSDHVvSGFZK+Pc1t4XQ="; + hash = nnueBigHash; }; nnueSmallFile = "nn-37f18f62d772.nnue"; + nnueSmallHash = "sha256-N/GPYtdy8xB+HWqso4mMEww8hvKrY+ZVX7vKIGNaiZ0="; nnueSmall = fetchurl { name = nnueSmallFile; url = "https://tests.stockfishchess.org/api/nn/${nnueSmallFile}"; - sha256 = "sha256-N/GPYtdy8xB+HWqso4mMEww8hvKrY+ZVX7vKIGNaiZ0="; + hash = nnueSmallHash; }; in stdenv.mkDerivation rec { pname = "stockfish"; - version = "17"; + version = "17.1"; src = fetchFromGitHub { owner = "official-stockfish"; repo = "Stockfish"; - rev = "sf_${version}"; - sha256 = "sha256-oXvLaC5TEUPlHjhm7tOxpNPY88QxYHFw+Cev3Q8NEeQ="; + tag = "sf_${version}"; + hash = "sha256-c8o1d7/yPnF3Eo7M/MSzYuYQr2qt2tIwyu7WfuKMAzg="; }; postUnpack = '' @@ -64,6 +71,37 @@ stdenv.mkDerivation rec { enableParallelBuilding = true; + nativeInstallCheckInputs = [ + versionCheckHook + ]; + doInstallCheck = true; + versionCheckProgram = "${placeholder "out"}/bin/stockfish"; + versionCheckProgramArg = "--help"; + + passthru = { + updateScript = _experimental-update-script-combinators.sequence [ + (nix-update-script { + extraArgs = [ "--version-regex=^sf_([\\d.]+)$" ]; + }) + (lib.getExe (writeShellApplication { + name = "${pname}-nnue-updater"; + runtimeInputs = [ + nix + gnugrep + ]; + runtimeEnv = { + PNAME = pname; + PKG_FILE = builtins.toString ./package.nix; + NNUE_BIG_FILE = nnueBigFile; + NNUE_BIG_HASH = nnueBigHash; + NNUE_SMALL_FILE = nnueSmallFile; + NNUE_SMALL_HASH = nnueSmallHash; + }; + text = builtins.readFile ./update.bash; + })) + ]; + }; + meta = with lib; { homepage = "https://stockfishchess.org/"; description = "Strong open source chess engine"; diff --git a/pkgs/by-name/st/stockfish/update.bash b/pkgs/by-name/st/stockfish/update.bash new file mode 100644 index 000000000000..aaf294fd027d --- /dev/null +++ b/pkgs/by-name/st/stockfish/update.bash @@ -0,0 +1,20 @@ +new_src="$(nix-build --attr "pkgs.$PNAME.src" --no-out-link)" +new_nnue_big_file="$(grep --perl-regexp --only-matching 'EvalFileDefaultNameBig "\Knn-(\w+).nnue' "$new_src/src/evaluate.h")" +new_nnue_small_file="$(grep --perl-regexp --only-matching 'EvalFileDefaultNameSmall "\Knn-(\w+).nnue' "$new_src/src/evaluate.h")" +new_nnue_big_hash="$( + nix hash convert --hash-algo sha256 "$( + nix-prefetch-url --type sha256 "https://tests.stockfishchess.org/api/nn/$new_nnue_big_file" + )" +)" +new_nnue_small_hash="$( + nix hash convert --hash-algo sha256 "$( + nix-prefetch-url --type sha256 "https://tests.stockfishchess.org/api/nn/$new_nnue_small_file" + )" +)" + +pkg_body="$(<"$PKG_FILE")" +pkg_body="${pkg_body//"$NNUE_BIG_FILE"/"$new_nnue_big_file"}" +pkg_body="${pkg_body//"$NNUE_BIG_HASH"/"$new_nnue_big_hash"}" +pkg_body="${pkg_body//"$NNUE_SMALL_FILE"/"$new_nnue_small_file"}" +pkg_body="${pkg_body//"$NNUE_SMALL_HASH"/"$new_nnue_small_hash"}" +echo "$pkg_body" >"$PKG_FILE" diff --git a/pkgs/by-name/st/storj-uplink/package.nix b/pkgs/by-name/st/storj-uplink/package.nix index 30c770ea82a9..b1bcb44831eb 100644 --- a/pkgs/by-name/st/storj-uplink/package.nix +++ b/pkgs/by-name/st/storj-uplink/package.nix @@ -6,18 +6,18 @@ buildGoModule rec { pname = "storj-uplink"; - version = "1.128.4"; + version = "1.130.6"; src = fetchFromGitHub { owner = "storj"; repo = "storj"; rev = "v${version}"; - hash = "sha256-7CH//aZ7DOXIP6A1gAZpiFO55LrLtBhvtZl/tVhYl8g="; + hash = "sha256-5IFMIiDP+HmR/yc7OQjs23lO5Cw12lZBsD8oIo2CaLE="; }; subPackages = [ "cmd/uplink" ]; - vendorHash = "sha256-CTcFTEKj5s43OlrIC7lOh3Lh/6k8/Igckv0zwrdGKbE="; + vendorHash = "sha256-PnRnnl3uFs889eYQbD+oNvDtJgNepNIJ90KTjnGthI8="; ldflags = [ "-s" diff --git a/pkgs/by-name/st/strobealign/package.nix b/pkgs/by-name/st/strobealign/package.nix new file mode 100644 index 000000000000..0dd0a976f512 --- /dev/null +++ b/pkgs/by-name/st/strobealign/package.nix @@ -0,0 +1,38 @@ +{ + stdenv, + lib, + fetchFromGitHub, + cmake, + pkg-config, + isa-l, + zlib, +}: +stdenv.mkDerivation (self: { + pname = "strobealign"; + version = "0.16.1"; + + src = fetchFromGitHub { + owner = "ksahlin"; + repo = "strobealign"; + tag = "v${self.version}"; + hash = "sha256-RZxIT6iwanRuPk2sWv/QRkUaPMdterOKCo30FPZHC8o="; + }; + + nativeBuildInputs = [ + cmake + pkg-config + ]; + buildInputs = [ + zlib + isa-l + ]; + + meta = { + description = "Read aligner for short reads"; + mainProgram = "strobealign"; + license = lib.licenses.mit; + homepage = "https://github.com/ksahlin/strobealign"; + maintainers = [ lib.maintainers.jbedo ]; + platforms = lib.platforms.unix; + }; +}) diff --git a/pkgs/by-name/st/style50/package.nix b/pkgs/by-name/st/style50/package.nix new file mode 100644 index 000000000000..034f7428aa4e --- /dev/null +++ b/pkgs/by-name/st/style50/package.nix @@ -0,0 +1,68 @@ +{ + lib, + python3Packages, + fetchFromGitHub, + libclang, + versionCheckHook, +}: + +python3Packages.buildPythonApplication rec { + pname = "style50"; + version = "2.10.4"; + pyproject = true; + + src = fetchFromGitHub { + owner = "cs50"; + repo = "style50"; + tag = "v${version}"; + hash = "sha256-59V3QZMYH5edBXv1GNdoaQxerDfKmLKUZ7VL3cvDvuE="; + }; + + build-system = [ + python3Packages.setuptools + ]; + + dependencies = with python3Packages; [ + autopep8 + icdiff + jinja2 + jsbeautifier + pycodestyle + python-magic + termcolor + ]; + + preFixup = '' + makeWrapperArgs+=(--prefix PATH : ${ + lib.makeBinPath [ + libclang # clang-format + ] + }) + ''; + + pythonRelaxDeps = [ + "pycodestyle" + ]; + + pythonRemoveDeps = [ + "clang-format" + ]; + + pythonImportsCheck = [ "style50" ]; + + nativeCheckInputs = [ versionCheckHook ]; + versionCheckProgramArg = "--version"; + + # no python tests + + meta = { + description = "Tool for checking code against the CS50 style guide"; + homepage = "https://cs50.readthedocs.io/style50/"; + downloadPage = "https://github.com/cs50/style50"; + changelog = "https://github.com/cs50/style50/releases/tag/v${version}"; + license = lib.licenses.gpl3Only; + platforms = lib.platforms.unix; + maintainers = with lib.maintainers; [ ethancedwards8 ]; + mainProgram = "style50"; + }; +} diff --git a/pkgs/by-name/ve/vencord/package.nix b/pkgs/by-name/ve/vencord/package.nix index 5d5ecefbee0d..3b919d9e583e 100644 --- a/pkgs/by-name/ve/vencord/package.nix +++ b/pkgs/by-name/ve/vencord/package.nix @@ -1,6 +1,5 @@ { curl, - esbuild, fetchFromGitHub, git, jq, @@ -15,13 +14,13 @@ stdenv.mkDerivation (finalAttrs: { pname = "vencord"; - version = "1.12.2"; + version = "1.12.3"; src = fetchFromGitHub { owner = "Vendicated"; repo = "Vencord"; rev = "v${finalAttrs.version}"; - hash = "sha256-a4lbeuXEHDMDko8wte7jUdJ0yUcjfq3UPQAuSiz1UQU="; + hash = "sha256-fOZXgyA61G+D7otNNO8d89ghR/GiYPJ7vSZtj9TeGuU="; }; pnpmDeps = pnpm_10.fetchDeps { @@ -36,20 +35,6 @@ stdenv.mkDerivation (finalAttrs: { ]; env = { - ESBUILD_BINARY_PATH = lib.getExe ( - esbuild.overrideAttrs ( - final: _: { - version = "0.25.1"; - src = fetchFromGitHub { - owner = "evanw"; - repo = "esbuild"; - rev = "v${final.version}"; - hash = "sha256-vrhtdrvrcC3dQoJM6hWq6wrGJLSiVww/CNPlL1N5kQ8="; - }; - vendorHash = "sha256-+BfxCyg0KkDQpHt/wycy/8CTG6YBA/VJvJFhhzUnSiQ="; - } - ) - ); VENCORD_REMOTE = "${finalAttrs.src.owner}/${finalAttrs.src.repo}"; VENCORD_HASH = "${finalAttrs.version}"; }; diff --git a/pkgs/by-name/wi/wiremix/package.nix b/pkgs/by-name/wi/wiremix/package.nix new file mode 100644 index 000000000000..92f040b05e59 --- /dev/null +++ b/pkgs/by-name/wi/wiremix/package.nix @@ -0,0 +1,38 @@ +{ + lib, + fetchCrate, + rustPlatform, + pkg-config, + pipewire, +}: + +rustPlatform.buildRustPackage rec { + pname = "wiremix"; + version = "0.4.0"; + + src = fetchCrate { + inherit pname version; + hash = "sha256-LtwKG3phUuNgwXlAJMhZkOenYHGyXGRhNcr6+WKxVz0="; + }; + + useFetchCargoVendor = true; + cargoHash = "sha256-Qc+VubikiYox1zqy2HO3InRI8aFT8AorrFZBQhNGFOQ="; + + nativeBuildInputs = [ + pkg-config + rustPlatform.bindgenHook + ]; + buildInputs = [ pipewire ]; + + meta = { + description = "Simple TUI mixer for PipeWire"; + mainProgram = "wiremix"; + homepage = "https://github.com/tsowell/wiremix"; + license = with lib.licenses; [ + asl20 + mit + ]; + maintainers = with lib.maintainers; [ tsowell ]; + platforms = lib.platforms.linux; + }; +} diff --git a/pkgs/by-name/zs/zsh-forgit/package.nix b/pkgs/by-name/zs/zsh-forgit/package.nix index df964328188a..11c8fe9609d0 100644 --- a/pkgs/by-name/zs/zsh-forgit/package.nix +++ b/pkgs/by-name/zs/zsh-forgit/package.nix @@ -15,13 +15,13 @@ stdenvNoCC.mkDerivation (finalAttrs: { pname = "zsh-forgit"; - version = "25.05.0"; + version = "25.06.0"; src = fetchFromGitHub { owner = "wfxr"; repo = "forgit"; tag = finalAttrs.version; - hash = "sha256-U+MtgunPEmo/kv/lQI2BBi2WUBgt3wFkaUdfRzJWoGQ="; + hash = "sha256-D1we3pOPXNsK8KgEaRBAmD5eH1i2ud4zX1GwYbOyZvY="; }; strictDeps = true; diff --git a/pkgs/desktops/xfce/core/libxfce4windowing/default.nix b/pkgs/desktops/xfce/core/libxfce4windowing/default.nix index a79d48d52927..0112316d4c83 100644 --- a/pkgs/desktops/xfce/core/libxfce4windowing/default.nix +++ b/pkgs/desktops/xfce/core/libxfce4windowing/default.nix @@ -2,6 +2,7 @@ stdenv, lib, mkXfceDerivation, + python3, wayland-scanner, glib, gtk3, @@ -22,12 +23,13 @@ mkXfceDerivation { category = "xfce"; pname = "libxfce4windowing"; - version = "4.20.2"; + version = "4.20.3"; - sha256 = "sha256-Xw1hs854K5dZCAYoBMoqJzdSxPRFUYqEpWxg4DLSK5Q="; + sha256 = "sha256-l58cTz28UPSVfoIpjBCoSwcqdUJfG9e4UlhVYPyEeAs="; nativeBuildInputs = [ + python3 wayland-scanner ] ++ lib.optionals withIntrospection [ @@ -46,6 +48,10 @@ mkXfceDerivation { wlr-protocols ]; + postPatch = '' + patchShebangs xdt-gen-visibility + ''; + meta = { description = "Windowing concept abstraction library for X11 and Wayland"; license = lib.licenses.lgpl21Plus; diff --git a/pkgs/development/compilers/flutter/versions/3_32/data.json b/pkgs/development/compilers/flutter/versions/3_32/data.json index a4e1d1db18c4..ace17df24812 100644 --- a/pkgs/development/compilers/flutter/versions/3_32/data.json +++ b/pkgs/development/compilers/flutter/versions/3_32/data.json @@ -1,73 +1,73 @@ { - "version": "3.32.0", - "engineVersion": "18818009497c581ede5d8a3b8b833b81d00cebb7", + "version": "3.32.2", + "engineVersion": "109150893958777c8f2215f6cfd3e89e984e8dea", "engineSwiftShaderHash": "sha256-ATVcuxqPHqHOWYyO7DoX9LdgUiO3INUi7m9Mc6ccc1M=", "engineSwiftShaderRev": "d040a5bab638bf7c226235c95787ba6288bb6416", "channel": "stable", "engineHashes": { "aarch64-linux": { - "aarch64-linux": "sha256-zGlgnVbNPZ9OX5VFxaajc8xqPKftzZMkyem2NLk8EH8=", - "x86_64-linux": "sha256-zGlgnVbNPZ9OX5VFxaajc8xqPKftzZMkyem2NLk8EH8=" + "aarch64-linux": "sha256-l/7p9fPVPIx/CqT4xTUPQLlBU18aZSlimamELMgkAjY=", + "x86_64-linux": "sha256-l/7p9fPVPIx/CqT4xTUPQLlBU18aZSlimamELMgkAjY=" }, "x86_64-linux": { - "aarch64-linux": "sha256-TWwsPtAnkQzsvNLUd5Q3G80ALeh7nM/XV4M6rfBvUGY=", - "x86_64-linux": "sha256-TWwsPtAnkQzsvNLUd5Q3G80ALeh7nM/XV4M6rfBvUGY=" + "aarch64-linux": "sha256-6shoelu1oOcTbzXlFcAOsVMF3b/3RZ2vHuMEXvKyzbo=", + "x86_64-linux": "sha256-6shoelu1oOcTbzXlFcAOsVMF3b/3RZ2vHuMEXvKyzbo=" } }, - "dartVersion": "3.8.0", + "dartVersion": "3.8.1", "dartHash": { - "x86_64-linux": "sha256-/b6dNwqeMymHKI4cz2ggfVtcHWtdsrlVyEZ1Z2G0RKw=", - "aarch64-linux": "sha256-tXIeGM7n1lx9GOwOMEFapiW0sHBlhupx5NCAtFd73Pk=", - "x86_64-darwin": "sha256-wD9Z3J29N4ecWKmCetj5GnbTQ8LiJ1tZFEbPuG6ny5w=", - "aarch64-darwin": "sha256-IIFn6OTpTH8LzwZzF7V8yUrhHkjavMUWX7n62YEMABk=" + "x86_64-linux": "sha256-3eE40VMwrPFD502lIaz+CkD7mBnSI/WqJ3C4DVQ01Z4=", + "aarch64-linux": "sha256-0GXCO00ar5532h+cXBEIe8BhGVKOuGuoPzr1M00muh4=", + "x86_64-darwin": "sha256-S3iGDVLollApke2SnXAcV919qsDTVmz5Gf9fTletr00=", + "aarch64-darwin": "sha256-haHQks9N1mBIqRsYg9sOLw7ra7gC708gsTWfKxvIK1c=" }, - "flutterHash": "sha256-Wk84RfnPvPVVnnbwGcnHIcRnu3Kizo2AkRQbVSEw+1M=", + "flutterHash": "sha256-bL+WRW9tOLEXeAwZKgwN4y8Vj6s6FOCUm3JJilLCkY8=", "artifactHashes": { "android": { - "aarch64-darwin": "sha256-+YUzIG/qUFb4X2ftR6Gf0h1mt/5Dy59B7QD09X1XVYY=", - "aarch64-linux": "sha256-lZIo8AGzrXant29kfiOKlfQ0SJAPXpsdZCaKfqxn0zA=", - "x86_64-darwin": "sha256-+YUzIG/qUFb4X2ftR6Gf0h1mt/5Dy59B7QD09X1XVYY=", - "x86_64-linux": "sha256-lZIo8AGzrXant29kfiOKlfQ0SJAPXpsdZCaKfqxn0zA=" + "aarch64-darwin": "sha256-VW1JzE6NfpGk2WFyPnvgkhlc+/dgkVtfPJ3HLF2SxjM=", + "aarch64-linux": "sha256-aNZyIBedC35v2/lePY1UcJYP3Wh0JccPrsABHlhdoOI=", + "x86_64-darwin": "sha256-VW1JzE6NfpGk2WFyPnvgkhlc+/dgkVtfPJ3HLF2SxjM=", + "x86_64-linux": "sha256-aNZyIBedC35v2/lePY1UcJYP3Wh0JccPrsABHlhdoOI=" }, "fuchsia": { - "aarch64-darwin": "sha256-F4uNyPIt6FSVCSOjR6mtgHHMMcJd3pAdT4uEiBJSASk=", - "aarch64-linux": "sha256-F4uNyPIt6FSVCSOjR6mtgHHMMcJd3pAdT4uEiBJSASk=", - "x86_64-darwin": "sha256-F4uNyPIt6FSVCSOjR6mtgHHMMcJd3pAdT4uEiBJSASk=", - "x86_64-linux": "sha256-F4uNyPIt6FSVCSOjR6mtgHHMMcJd3pAdT4uEiBJSASk=" + "aarch64-darwin": "sha256-8Jo1dP2uugp75BjQvvgvxnCJI0oyLlClJ2YaaiSkJzs=", + "aarch64-linux": "sha256-8Jo1dP2uugp75BjQvvgvxnCJI0oyLlClJ2YaaiSkJzs=", + "x86_64-darwin": "sha256-8Jo1dP2uugp75BjQvvgvxnCJI0oyLlClJ2YaaiSkJzs=", + "x86_64-linux": "sha256-8Jo1dP2uugp75BjQvvgvxnCJI0oyLlClJ2YaaiSkJzs=" }, "ios": { - "aarch64-darwin": "sha256-HlGXG7hyi4oCdN4bGP6GYa++rTruZC7v+laR4733xS4=", - "aarch64-linux": "sha256-HlGXG7hyi4oCdN4bGP6GYa++rTruZC7v+laR4733xS4=", - "x86_64-darwin": "sha256-HlGXG7hyi4oCdN4bGP6GYa++rTruZC7v+laR4733xS4=", - "x86_64-linux": "sha256-HlGXG7hyi4oCdN4bGP6GYa++rTruZC7v+laR4733xS4=" + "aarch64-darwin": "sha256-75DZ9m1yxybk/xP2UMDr/thBtP2q3iOHPakxxHRbIWs=", + "aarch64-linux": "sha256-75DZ9m1yxybk/xP2UMDr/thBtP2q3iOHPakxxHRbIWs=", + "x86_64-darwin": "sha256-75DZ9m1yxybk/xP2UMDr/thBtP2q3iOHPakxxHRbIWs=", + "x86_64-linux": "sha256-75DZ9m1yxybk/xP2UMDr/thBtP2q3iOHPakxxHRbIWs=" }, "linux": { - "aarch64-darwin": "sha256-y/xxUQK/9GeXjMTUPZSGhfP83rJzCm1Pd2PMjnAHdnk=", - "aarch64-linux": "sha256-y/xxUQK/9GeXjMTUPZSGhfP83rJzCm1Pd2PMjnAHdnk=", - "x86_64-darwin": "sha256-P5UQiY4QHLJQMmTpTfTFfh6QrqelhWyaGpSXNtM8vJE=", - "x86_64-linux": "sha256-P5UQiY4QHLJQMmTpTfTFfh6QrqelhWyaGpSXNtM8vJE=" + "aarch64-darwin": "sha256-7gGCUYERyMdga0bgxWo2ZCn9yUwIKKBOuzXvNfWS6Bw=", + "aarch64-linux": "sha256-7gGCUYERyMdga0bgxWo2ZCn9yUwIKKBOuzXvNfWS6Bw=", + "x86_64-darwin": "sha256-VlkWtHFJc8AbzvQbEL8IzXNpkrz4hIR7yK+gRHH1Qtc=", + "x86_64-linux": "sha256-VlkWtHFJc8AbzvQbEL8IzXNpkrz4hIR7yK+gRHH1Qtc=" }, "macos": { - "aarch64-darwin": "sha256-FkvjRiKWa/FUITdwK1ldLCcsfjs8nrH7L0mXGHnVIko=", - "aarch64-linux": "sha256-FkvjRiKWa/FUITdwK1ldLCcsfjs8nrH7L0mXGHnVIko=", - "x86_64-darwin": "sha256-FkvjRiKWa/FUITdwK1ldLCcsfjs8nrH7L0mXGHnVIko=", - "x86_64-linux": "sha256-FkvjRiKWa/FUITdwK1ldLCcsfjs8nrH7L0mXGHnVIko=" + "aarch64-darwin": "sha256-fonn1C8JJ/Dpid6eyzqgTw5jm4dgdt1KF+sd0d5wRNc=", + "aarch64-linux": "sha256-fonn1C8JJ/Dpid6eyzqgTw5jm4dgdt1KF+sd0d5wRNc=", + "x86_64-darwin": "sha256-fonn1C8JJ/Dpid6eyzqgTw5jm4dgdt1KF+sd0d5wRNc=", + "x86_64-linux": "sha256-fonn1C8JJ/Dpid6eyzqgTw5jm4dgdt1KF+sd0d5wRNc=" }, "universal": { - "aarch64-darwin": "sha256-bt/UiY3t1wZUnpMo1uVQPdlG/7X+OrAOZRXIaXKH3Ls=", - "aarch64-linux": "sha256-NShVNyK6Cx5bddhv6hg3rM+QPczrm2MmHCgb14h78SA=", - "x86_64-darwin": "sha256-zXSufTmlyaHM0fJinirUfB3XjD3dzKFD6iyhUADsyQY=", - "x86_64-linux": "sha256-MQw71FYYuLKd60cwMddAjVe9vgwx8MR4xVe0Z9J7uAo=" + "aarch64-darwin": "sha256-XXktfE+Rx2kj+4PYlWZmfHCRheWDF1K+kCo1McnjZao=", + "aarch64-linux": "sha256-jRJ5OLuoV0b/Rd7SpYmyjCL7ED8iWjawvS8ZMAqVowE=", + "x86_64-darwin": "sha256-lTudK0BzZznIOAc2bTildCLg0ZeLJX19suhG4ZgH1Eo=", + "x86_64-linux": "sha256-M41bQrf1amzW1QGkq37gNgN5zh3v4/+OuBxp4CWUSqI=" }, "web": { - "aarch64-darwin": "sha256-dpMJQsUd98X+tmUnqRZQ1o5g334i7dyxBNvg2znJeaE=", - "aarch64-linux": "sha256-dpMJQsUd98X+tmUnqRZQ1o5g334i7dyxBNvg2znJeaE=", - "x86_64-darwin": "sha256-dpMJQsUd98X+tmUnqRZQ1o5g334i7dyxBNvg2znJeaE=", - "x86_64-linux": "sha256-dpMJQsUd98X+tmUnqRZQ1o5g334i7dyxBNvg2znJeaE=" + "aarch64-darwin": "sha256-l6/ZekcmlueBP7DdAuQpjGuYc6MFlMSWW4tnU2PixNU=", + "aarch64-linux": "sha256-l6/ZekcmlueBP7DdAuQpjGuYc6MFlMSWW4tnU2PixNU=", + "x86_64-darwin": "sha256-l6/ZekcmlueBP7DdAuQpjGuYc6MFlMSWW4tnU2PixNU=", + "x86_64-linux": "sha256-l6/ZekcmlueBP7DdAuQpjGuYc6MFlMSWW4tnU2PixNU=" }, "windows": { - "x86_64-darwin": "sha256-3ZfYqlyjBaBaZjxp9k2DCsD4QZL/+fbXfCFxX3i4Q/Q=", - "x86_64-linux": "sha256-3ZfYqlyjBaBaZjxp9k2DCsD4QZL/+fbXfCFxX3i4Q/Q=" + "x86_64-darwin": "sha256-zxVV76R80bRqzJaWCBj9RlLkpB4HmX1iTYcdLAbiqT4=", + "x86_64-linux": "sha256-zxVV76R80bRqzJaWCBj9RlLkpB4HmX1iTYcdLAbiqT4=" } }, "pubspecLock": { @@ -266,11 +266,11 @@ "dependency": "direct main", "description": { "name": "dds", - "sha256": "4e206d308bd94595af42e4a561948ef8dba211c9db47f4360c48a5a1f8b44cb5", + "sha256": "76fc5140ce4e8922711bbe6dfd3713283ecc7f386c52fc7f594cd5f3e6b80633", "url": "https://pub.dev" }, "source": "hosted", - "version": "5.0.0" + "version": "5.0.2" }, "dds_service_extensions": { "dependency": "direct main", @@ -286,11 +286,11 @@ "dependency": "direct main", "description": { "name": "devtools_shared", - "sha256": "fa71f07006dfdf3f226ec76db95a4bad156820c081452cc99d18a4f291001bee", + "sha256": "659e2d65aa5ef5c3551163811c5c6fa1b973b3df80d8cac6f618035edcdc1096", "url": "https://pub.dev" }, "source": "hosted", - "version": "11.2.0" + "version": "11.2.1" }, "dtd": { "dependency": "direct main", diff --git a/pkgs/development/compilers/llvm/common/clang/default.nix b/pkgs/development/compilers/llvm/common/clang/default.nix index d121bfcd7869..03e8aaec9b54 100644 --- a/pkgs/development/compilers/llvm/common/clang/default.nix +++ b/pkgs/development/compilers/llvm/common/clang/default.nix @@ -201,7 +201,7 @@ stdenv.mkDerivation ( "python" ]; - separateDebugInfo = true; + separateDebugInfo = stdenv.buildPlatform.is64bit; # OOMs on 32 bit postInstall = '' diff --git a/pkgs/development/interpreters/python/cpython/3.14/CVE-2025-4517.patch b/pkgs/development/interpreters/python/cpython/3.14/CVE-2025-4517.patch new file mode 100644 index 000000000000..5ad45fdde9fa --- /dev/null +++ b/pkgs/development/interpreters/python/cpython/3.14/CVE-2025-4517.patch @@ -0,0 +1,2055 @@ +From 9e0ac76d96cf80b49055f6d6b9a6763fb9215c2a Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=C5=81ukasz=20Langa?= +Date: Tue, 3 Jun 2025 14:05:00 +0200 +Subject: [PATCH] [3.14] gh-135034: Normalize link targets in tarfile, add + `os.path.realpath(strict='allow_missing')` (gh-135037) (gh-135065) +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Addresses CVEs 2024-12718, 2025-4138, 2025-4330, and 2025-4517. + +(cherry picked from commit 3612d8f51741b11f36f8fb0494d79086bac9390a) + +Signed-off-by: Łukasz Langa +Co-authored-by: Petr Viktorin +Co-authored-by: Seth Michael Larson +Co-authored-by: Adam Turner <9087854+AA-Turner@users.noreply.github.com> +Co-authored-by: Serhiy Storchaka +--- + Doc/library/os.path.rst | 32 +- + Doc/library/tarfile.rst | 20 ++ + Doc/whatsnew/3.14.rst | 32 ++ + Lib/genericpath.py | 11 +- + Lib/ntpath.py | 38 ++- + Lib/posixpath.py | 57 ++-- + Lib/tarfile.py | 163 +++++++-- + Lib/test/test_ntpath.py | 216 ++++++++++-- + Lib/test/test_posixpath.py | 252 +++++++++++--- + Lib/test/test_tarfile.py | 310 +++++++++++++++++- + ...-06-02-11-32-23.gh-issue-135034.RLGjbp.rst | 6 + + 11 files changed, 967 insertions(+), 170 deletions(-) + create mode 100644 Misc/NEWS.d/next/Security/2025-06-02-11-32-23.gh-issue-135034.RLGjbp.rst + +diff --git a/Doc/library/os.path.rst b/Doc/library/os.path.rst +index ecbbc1d7605f9f..f72aee19d8f332 100644 +--- a/Doc/library/os.path.rst ++++ b/Doc/library/os.path.rst +@@ -408,9 +408,26 @@ the :mod:`glob` module.) + system). On Windows, this function will also resolve MS-DOS (also called 8.3) + style names such as ``C:\\PROGRA~1`` to ``C:\\Program Files``. + +- If a path doesn't exist or a symlink loop is encountered, and *strict* is +- ``True``, :exc:`OSError` is raised. If *strict* is ``False`` these errors +- are ignored, and so the result might be missing or otherwise inaccessible. ++ By default, the path is evaluated up to the first component that does not ++ exist, is a symlink loop, or whose evaluation raises :exc:`OSError`. ++ All such components are appended unchanged to the existing part of the path. ++ ++ Some errors that are handled this way include "access denied", "not a ++ directory", or "bad argument to internal function". Thus, the ++ resulting path may be missing or inaccessible, may still contain ++ links or loops, and may traverse non-directories. ++ ++ This behavior can be modified by keyword arguments: ++ ++ If *strict* is ``True``, the first error encountered when evaluating the path is ++ re-raised. ++ In particular, :exc:`FileNotFoundError` is raised if *path* does not exist, ++ or another :exc:`OSError` if it is otherwise inaccessible. ++ ++ If *strict* is :py:data:`os.path.ALLOW_MISSING`, errors other than ++ :exc:`FileNotFoundError` are re-raised (as with ``strict=True``). ++ Thus, the returned path will not contain any symbolic links, but the named ++ file and some of its parent directories may be missing. + + .. note:: + This function emulates the operating system's procedure for making a path +@@ -429,6 +446,15 @@ the :mod:`glob` module.) + .. versionchanged:: 3.10 + The *strict* parameter was added. + ++ .. versionchanged:: next ++ The :py:data:`~os.path.ALLOW_MISSING` value for the *strict* parameter ++ was added. ++ ++.. data:: ALLOW_MISSING ++ ++ Special value used for the *strict* argument in :func:`realpath`. ++ ++ .. versionadded:: next + + .. function:: relpath(path, start=os.curdir) + +diff --git a/Doc/library/tarfile.rst b/Doc/library/tarfile.rst +index f9cb5495e60cd2..7cec108a5bd41d 100644 +--- a/Doc/library/tarfile.rst ++++ b/Doc/library/tarfile.rst +@@ -255,6 +255,15 @@ The :mod:`tarfile` module defines the following exceptions: + Raised to refuse extracting a symbolic link pointing outside the destination + directory. + ++.. exception:: LinkFallbackError ++ ++ Raised to refuse emulating a link (hard or symbolic) by extracting another ++ archive member, when that member would be rejected by the filter location. ++ The exception that was raised to reject the replacement member is available ++ as :attr:`!BaseException.__context__`. ++ ++ .. versionadded:: next ++ + + The following constants are available at the module level: + +@@ -1068,6 +1077,12 @@ reused in custom filters: + Implements the ``'data'`` filter. + In addition to what ``tar_filter`` does: + ++ - Normalize link targets (:attr:`TarInfo.linkname`) using ++ :func:`os.path.normpath`. ++ Note that this removes internal ``..`` components, which may change the ++ meaning of the link if the path in :attr:`!TarInfo.linkname` traverses ++ symbolic links. ++ + - :ref:`Refuse ` to extract links (hard or soft) + that link to absolute paths, or ones that link outside the destination. + +@@ -1099,6 +1114,10 @@ reused in custom filters: + Note that this filter does not block *all* dangerous archive features. + See :ref:`tarfile-further-verification` for details. + ++ .. versionchanged:: next ++ ++ Link targets are now normalized. ++ + + .. _tarfile-extraction-refuse: + +@@ -1127,6 +1146,7 @@ Here is an incomplete list of things to consider: + * Extract to a :func:`new temporary directory ` + to prevent e.g. exploiting pre-existing links, and to make it easier to + clean up after a failed extraction. ++* Disallow symbolic links if you do not need the functionality. + * When working with untrusted data, use external (e.g. OS-level) limits on + disk, memory and CPU usage. + * Check filenames against an allow-list of characters +diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst +index 561d1a8914b50c..1e6a3615963ff4 100644 +--- a/Doc/whatsnew/3.14.rst ++++ b/Doc/whatsnew/3.14.rst +@@ -1608,6 +1608,16 @@ os + (Contributed by Cody Maloney in :gh:`129205`.) + + ++os.path ++------- ++ ++* The *strict* parameter to :func:`os.path.realpath` accepts a new value, ++ :data:`os.path.ALLOW_MISSING`. ++ If used, errors other than :exc:`FileNotFoundError` will be re-raised; ++ the resulting path can be missing but it will be free of symlinks. ++ (Contributed by Petr Viktorin for :cve:`2025-4517`.) ++ ++ + pathlib + ------- + +@@ -1796,6 +1806,28 @@ sysconfig + (Contributed by Xuehai Pan in :gh:`131799`.) + + ++tarfile ++------- ++ ++* :func:`~tarfile.data_filter` now normalizes symbolic link targets in order to ++ avoid path traversal attacks. ++ (Contributed by Petr Viktorin in :gh:`127987` and :cve:`2025-4138`.) ++* :func:`~tarfile.TarFile.extractall` now skips fixing up directory attributes ++ when a directory was removed or replaced by another kind of file. ++ (Contributed by Petr Viktorin in :gh:`127987` and :cve:`2024-12718`.) ++* :func:`~tarfile.TarFile.extract` and :func:`~tarfile.TarFile.extractall` ++ now (re-)apply the extraction filter when substituting a link (hard or ++ symbolic) with a copy of another archive member, and when fixing up ++ directory attributes. ++ The former raises a new exception, :exc:`~tarfile.LinkFallbackError`. ++ (Contributed by Petr Viktorin for :cve:`2025-4330` and :cve:`2024-12718`.) ++* :func:`~tarfile.TarFile.extract` and :func:`~tarfile.TarFile.extractall` ++ no longer extract rejected members when ++ :func:`~tarfile.TarFile.errorlevel` is zero. ++ (Contributed by Matt Prodani and Petr Viktorin in :gh:`112887` ++ and :cve:`2025-4435`.) ++ ++ + threading + --------- + +diff --git a/Lib/genericpath.py b/Lib/genericpath.py +index ba7b0a13c7f81d..9363f564aab7a6 100644 +--- a/Lib/genericpath.py ++++ b/Lib/genericpath.py +@@ -8,7 +8,7 @@ + + __all__ = ['commonprefix', 'exists', 'getatime', 'getctime', 'getmtime', + 'getsize', 'isdevdrive', 'isdir', 'isfile', 'isjunction', 'islink', +- 'lexists', 'samefile', 'sameopenfile', 'samestat'] ++ 'lexists', 'samefile', 'sameopenfile', 'samestat', 'ALLOW_MISSING'] + + + # Does a path exist? +@@ -189,3 +189,12 @@ def _check_arg_types(funcname, *args): + f'os.PathLike object, not {s.__class__.__name__!r}') from None + if hasstr and hasbytes: + raise TypeError("Can't mix strings and bytes in path components") from None ++ ++# A singleton with a true boolean value. ++@object.__new__ ++class ALLOW_MISSING: ++ """Special value for use in realpath().""" ++ def __repr__(self): ++ return 'os.path.ALLOW_MISSING' ++ def __reduce__(self): ++ return self.__class__.__name__ +diff --git a/Lib/ntpath.py b/Lib/ntpath.py +index 52ff2af743af6c..9cdc16480f9afe 100644 +--- a/Lib/ntpath.py ++++ b/Lib/ntpath.py +@@ -29,7 +29,7 @@ + "abspath","curdir","pardir","sep","pathsep","defpath","altsep", + "extsep","devnull","realpath","supports_unicode_filenames","relpath", + "samefile", "sameopenfile", "samestat", "commonpath", "isjunction", +- "isdevdrive"] ++ "isdevdrive", "ALLOW_MISSING"] + + def _get_bothseps(path): + if isinstance(path, bytes): +@@ -601,9 +601,10 @@ def abspath(path): + from nt import _findfirstfile, _getfinalpathname, readlink as _nt_readlink + except ImportError: + # realpath is a no-op on systems without _getfinalpathname support. +- realpath = abspath ++ def realpath(path, *, strict=False): ++ return abspath(path) + else: +- def _readlink_deep(path): ++ def _readlink_deep(path, ignored_error=OSError): + # These error codes indicate that we should stop reading links and + # return the path we currently have. + # 1: ERROR_INVALID_FUNCTION +@@ -636,7 +637,7 @@ def _readlink_deep(path): + path = old_path + break + path = normpath(join(dirname(old_path), path)) +- except OSError as ex: ++ except ignored_error as ex: + if ex.winerror in allowed_winerror: + break + raise +@@ -645,7 +646,7 @@ def _readlink_deep(path): + break + return path + +- def _getfinalpathname_nonstrict(path): ++ def _getfinalpathname_nonstrict(path, ignored_error=OSError): + # These error codes indicate that we should stop resolving the path + # and return the value we currently have. + # 1: ERROR_INVALID_FUNCTION +@@ -673,17 +674,18 @@ def _getfinalpathname_nonstrict(path): + try: + path = _getfinalpathname(path) + return join(path, tail) if tail else path +- except OSError as ex: ++ except ignored_error as ex: + if ex.winerror not in allowed_winerror: + raise + try: + # The OS could not resolve this path fully, so we attempt + # to follow the link ourselves. If we succeed, join the tail + # and return. +- new_path = _readlink_deep(path) ++ new_path = _readlink_deep(path, ++ ignored_error=ignored_error) + if new_path != path: + return join(new_path, tail) if tail else new_path +- except OSError: ++ except ignored_error: + # If we fail to readlink(), let's keep traversing + pass + # If we get these errors, try to get the real name of the file without accessing it. +@@ -691,7 +693,7 @@ def _getfinalpathname_nonstrict(path): + try: + name = _findfirstfile(path) + path, _ = split(path) +- except OSError: ++ except ignored_error: + path, name = split(path) + else: + path, name = split(path) +@@ -721,6 +723,15 @@ def realpath(path, *, strict=False): + if normcase(path) == devnull: + return '\\\\.\\NUL' + had_prefix = path.startswith(prefix) ++ ++ if strict is ALLOW_MISSING: ++ ignored_error = FileNotFoundError ++ strict = True ++ elif strict: ++ ignored_error = () ++ else: ++ ignored_error = OSError ++ + if not had_prefix and not isabs(path): + path = join(cwd, path) + try: +@@ -728,17 +739,16 @@ def realpath(path, *, strict=False): + initial_winerror = 0 + except ValueError as ex: + # gh-106242: Raised for embedded null characters +- # In strict mode, we convert into an OSError. ++ # In strict modes, we convert into an OSError. + # Non-strict mode returns the path as-is, since we've already + # made it absolute. + if strict: + raise OSError(str(ex)) from None + path = normpath(path) +- except OSError as ex: +- if strict: +- raise ++ except ignored_error as ex: + initial_winerror = ex.winerror +- path = _getfinalpathname_nonstrict(path) ++ path = _getfinalpathname_nonstrict(path, ++ ignored_error=ignored_error) + # The path returned by _getfinalpathname will always start with \\?\ - + # strip off that prefix unless it was already provided on the original + # path. +diff --git a/Lib/posixpath.py b/Lib/posixpath.py +index db72ded8826056..d38f3bd5872bcd 100644 +--- a/Lib/posixpath.py ++++ b/Lib/posixpath.py +@@ -36,7 +36,7 @@ + "samefile","sameopenfile","samestat", + "curdir","pardir","sep","pathsep","defpath","altsep","extsep", + "devnull","realpath","supports_unicode_filenames","relpath", +- "commonpath", "isjunction","isdevdrive"] ++ "commonpath", "isjunction","isdevdrive","ALLOW_MISSING"] + + + def _get_sep(path): +@@ -402,10 +402,18 @@ def realpath(filename, *, strict=False): + curdir = '.' + pardir = '..' + getcwd = os.getcwd +- return _realpath(filename, strict, sep, curdir, pardir, getcwd) ++ if strict is ALLOW_MISSING: ++ ignored_error = FileNotFoundError ++ strict = True ++ elif strict: ++ ignored_error = () ++ else: ++ ignored_error = OSError ++ ++ lstat = os.lstat ++ readlink = os.readlink ++ maxlinks = None + +-def _realpath(filename, strict=False, sep=sep, curdir=curdir, pardir=pardir, +- getcwd=os.getcwd, lstat=os.lstat, readlink=os.readlink, maxlinks=None): + # The stack of unresolved path parts. When popped, a special value of None + # indicates that a symlink target has been resolved, and that the original + # symlink path can be retrieved by popping again. The [::-1] slice is a +@@ -477,27 +485,28 @@ def _realpath(filename, strict=False, sep=sep, curdir=curdir, pardir=pardir, + path = newpath + continue + target = readlink(newpath) +- except OSError: +- if strict: +- raise +- path = newpath ++ except ignored_error: ++ pass ++ else: ++ # Resolve the symbolic link ++ if target.startswith(sep): ++ # Symlink target is absolute; reset resolved path. ++ path = sep ++ if maxlinks is None: ++ # Mark this symlink as seen but not fully resolved. ++ seen[newpath] = None ++ # Push the symlink path onto the stack, and signal its specialness ++ # by also pushing None. When these entries are popped, we'll ++ # record the fully-resolved symlink target in the 'seen' mapping. ++ rest.append(newpath) ++ rest.append(None) ++ # Push the unresolved symlink target parts onto the stack. ++ target_parts = target.split(sep)[::-1] ++ rest.extend(target_parts) ++ part_count += len(target_parts) + continue +- # Resolve the symbolic link +- if target.startswith(sep): +- # Symlink target is absolute; reset resolved path. +- path = sep +- if maxlinks is None: +- # Mark this symlink as seen but not fully resolved. +- seen[newpath] = None +- # Push the symlink path onto the stack, and signal its specialness +- # by also pushing None. When these entries are popped, we'll +- # record the fully-resolved symlink target in the 'seen' mapping. +- rest.append(newpath) +- rest.append(None) +- # Push the unresolved symlink target parts onto the stack. +- target_parts = target.split(sep)[::-1] +- rest.extend(target_parts) +- part_count += len(target_parts) ++ # An error occurred and was ignored. ++ path = newpath + + return path + +diff --git a/Lib/tarfile.py b/Lib/tarfile.py +index 212b71f6509740..068aa13ed70356 100644 +--- a/Lib/tarfile.py ++++ b/Lib/tarfile.py +@@ -67,7 +67,7 @@ + "DEFAULT_FORMAT", "open","fully_trusted_filter", "data_filter", + "tar_filter", "FilterError", "AbsoluteLinkError", + "OutsideDestinationError", "SpecialFileError", "AbsolutePathError", +- "LinkOutsideDestinationError"] ++ "LinkOutsideDestinationError", "LinkFallbackError"] + + + #--------------------------------------------------------- +@@ -766,10 +766,22 @@ def __init__(self, tarinfo, path): + super().__init__(f'{tarinfo.name!r} would link to {path!r}, ' + + 'which is outside the destination') + ++class LinkFallbackError(FilterError): ++ def __init__(self, tarinfo, path): ++ self.tarinfo = tarinfo ++ self._path = path ++ super().__init__(f'link {tarinfo.name!r} would be extracted as a ' ++ + f'copy of {path!r}, which was rejected') ++ ++# Errors caused by filters -- both "fatal" and "non-fatal" -- that ++# we consider to be issues with the argument, rather than a bug in the ++# filter function ++_FILTER_ERRORS = (FilterError, OSError, ExtractError) ++ + def _get_filtered_attrs(member, dest_path, for_data=True): + new_attrs = {} + name = member.name +- dest_path = os.path.realpath(dest_path) ++ dest_path = os.path.realpath(dest_path, strict=os.path.ALLOW_MISSING) + # Strip leading / (tar's directory separator) from filenames. + # Include os.sep (target OS directory separator) as well. + if name.startswith(('/', os.sep)): +@@ -779,7 +791,8 @@ def _get_filtered_attrs(member, dest_path, for_data=True): + # For example, 'C:/foo' on Windows. + raise AbsolutePathError(member) + # Ensure we stay in the destination +- target_path = os.path.realpath(os.path.join(dest_path, name)) ++ target_path = os.path.realpath(os.path.join(dest_path, name), ++ strict=os.path.ALLOW_MISSING) + if os.path.commonpath([target_path, dest_path]) != dest_path: + raise OutsideDestinationError(member, target_path) + # Limit permissions (no high bits, and go-w) +@@ -817,6 +830,9 @@ def _get_filtered_attrs(member, dest_path, for_data=True): + if member.islnk() or member.issym(): + if os.path.isabs(member.linkname): + raise AbsoluteLinkError(member) ++ normalized = os.path.normpath(member.linkname) ++ if normalized != member.linkname: ++ new_attrs['linkname'] = normalized + if member.issym(): + target_path = os.path.join(dest_path, + os.path.dirname(name), +@@ -824,7 +840,8 @@ def _get_filtered_attrs(member, dest_path, for_data=True): + else: + target_path = os.path.join(dest_path, + member.linkname) +- target_path = os.path.realpath(target_path) ++ target_path = os.path.realpath(target_path, ++ strict=os.path.ALLOW_MISSING) + if os.path.commonpath([target_path, dest_path]) != dest_path: + raise LinkOutsideDestinationError(member, target_path) + return new_attrs +@@ -2386,30 +2403,58 @@ def extractall(self, path=".", members=None, *, numeric_owner=False, + members = self + + for member in members: +- tarinfo = self._get_extract_tarinfo(member, filter_function, path) ++ tarinfo, unfiltered = self._get_extract_tarinfo( ++ member, filter_function, path) + if tarinfo is None: + continue + if tarinfo.isdir(): + # For directories, delay setting attributes until later, + # since permissions can interfere with extraction and + # extracting contents can reset mtime. +- directories.append(tarinfo) ++ directories.append(unfiltered) + self._extract_one(tarinfo, path, set_attrs=not tarinfo.isdir(), +- numeric_owner=numeric_owner) ++ numeric_owner=numeric_owner, ++ filter_function=filter_function) + + # Reverse sort directories. + directories.sort(key=lambda a: a.name, reverse=True) + ++ + # Set correct owner, mtime and filemode on directories. +- for tarinfo in directories: +- dirpath = os.path.join(path, tarinfo.name) ++ for unfiltered in directories: + try: ++ # Need to re-apply any filter, to take the *current* filesystem ++ # state into account. ++ try: ++ tarinfo = filter_function(unfiltered, path) ++ except _FILTER_ERRORS as exc: ++ self._log_no_directory_fixup(unfiltered, repr(exc)) ++ continue ++ if tarinfo is None: ++ self._log_no_directory_fixup(unfiltered, ++ 'excluded by filter') ++ continue ++ dirpath = os.path.join(path, tarinfo.name) ++ try: ++ lstat = os.lstat(dirpath) ++ except FileNotFoundError: ++ self._log_no_directory_fixup(tarinfo, 'missing') ++ continue ++ if not stat.S_ISDIR(lstat.st_mode): ++ # This is no longer a directory; presumably a later ++ # member overwrote the entry. ++ self._log_no_directory_fixup(tarinfo, 'not a directory') ++ continue + self.chown(tarinfo, dirpath, numeric_owner=numeric_owner) + self.utime(tarinfo, dirpath) + self.chmod(tarinfo, dirpath) + except ExtractError as e: + self._handle_nonfatal_error(e) + ++ def _log_no_directory_fixup(self, member, reason): ++ self._dbg(2, "tarfile: Not fixing up directory %r (%s)" % ++ (member.name, reason)) ++ + def extract(self, member, path="", set_attrs=True, *, numeric_owner=False, + filter=None): + """Extract a member from the archive to the current working directory, +@@ -2425,41 +2470,56 @@ def extract(self, member, path="", set_attrs=True, *, numeric_owner=False, + String names of common filters are accepted. + """ + filter_function = self._get_filter_function(filter) +- tarinfo = self._get_extract_tarinfo(member, filter_function, path) ++ tarinfo, unfiltered = self._get_extract_tarinfo( ++ member, filter_function, path) + if tarinfo is not None: + self._extract_one(tarinfo, path, set_attrs, numeric_owner) + + def _get_extract_tarinfo(self, member, filter_function, path): +- """Get filtered TarInfo (or None) from member, which might be a str""" ++ """Get (filtered, unfiltered) TarInfos from *member* ++ ++ *member* might be a string. ++ ++ Return (None, None) if not found. ++ """ ++ + if isinstance(member, str): +- tarinfo = self.getmember(member) ++ unfiltered = self.getmember(member) + else: +- tarinfo = member ++ unfiltered = member + +- unfiltered = tarinfo ++ filtered = None + try: +- tarinfo = filter_function(tarinfo, path) ++ filtered = filter_function(unfiltered, path) + except (OSError, UnicodeEncodeError, FilterError) as e: + self._handle_fatal_error(e) + except ExtractError as e: + self._handle_nonfatal_error(e) +- if tarinfo is None: ++ if filtered is None: + self._dbg(2, "tarfile: Excluded %r" % unfiltered.name) +- return None ++ return None, None ++ + # Prepare the link target for makelink(). +- if tarinfo.islnk(): +- tarinfo = copy.copy(tarinfo) +- tarinfo._link_target = os.path.join(path, tarinfo.linkname) +- return tarinfo ++ if filtered.islnk(): ++ filtered = copy.copy(filtered) ++ filtered._link_target = os.path.join(path, filtered.linkname) ++ return filtered, unfiltered ++ ++ def _extract_one(self, tarinfo, path, set_attrs, numeric_owner, ++ filter_function=None): ++ """Extract from filtered tarinfo to disk. + +- def _extract_one(self, tarinfo, path, set_attrs, numeric_owner): +- """Extract from filtered tarinfo to disk""" ++ filter_function is only used when extracting a *different* ++ member (e.g. as fallback to creating a symlink) ++ """ + self._check("r") + + try: + self._extract_member(tarinfo, os.path.join(path, tarinfo.name), + set_attrs=set_attrs, +- numeric_owner=numeric_owner) ++ numeric_owner=numeric_owner, ++ filter_function=filter_function, ++ extraction_root=path) + except (OSError, UnicodeEncodeError) as e: + self._handle_fatal_error(e) + except ExtractError as e: +@@ -2517,9 +2577,13 @@ def extractfile(self, member): + return None + + def _extract_member(self, tarinfo, targetpath, set_attrs=True, +- numeric_owner=False): +- """Extract the TarInfo object tarinfo to a physical ++ numeric_owner=False, *, filter_function=None, ++ extraction_root=None): ++ """Extract the filtered TarInfo object tarinfo to a physical + file called targetpath. ++ ++ filter_function is only used when extracting a *different* ++ member (e.g. as fallback to creating a symlink) + """ + # Fetch the TarInfo object for the given name + # and build the destination pathname, replacing +@@ -2548,7 +2612,10 @@ def _extract_member(self, tarinfo, targetpath, set_attrs=True, + elif tarinfo.ischr() or tarinfo.isblk(): + self.makedev(tarinfo, targetpath) + elif tarinfo.islnk() or tarinfo.issym(): +- self.makelink(tarinfo, targetpath) ++ self.makelink_with_filter( ++ tarinfo, targetpath, ++ filter_function=filter_function, ++ extraction_root=extraction_root) + elif tarinfo.type not in SUPPORTED_TYPES: + self.makeunknown(tarinfo, targetpath) + else: +@@ -2631,10 +2698,18 @@ def makedev(self, tarinfo, targetpath): + os.makedev(tarinfo.devmajor, tarinfo.devminor)) + + def makelink(self, tarinfo, targetpath): ++ return self.makelink_with_filter(tarinfo, targetpath, None, None) ++ ++ def makelink_with_filter(self, tarinfo, targetpath, ++ filter_function, extraction_root): + """Make a (symbolic) link called targetpath. If it cannot be created + (platform limitation), we try to make a copy of the referenced file + instead of a link. ++ ++ filter_function is only used when extracting a *different* ++ member (e.g. as fallback to creating a link). + """ ++ keyerror_to_extracterror = False + try: + # For systems that support symbolic and hard links. + if tarinfo.issym(): +@@ -2642,18 +2717,38 @@ def makelink(self, tarinfo, targetpath): + # Avoid FileExistsError on following os.symlink. + os.unlink(targetpath) + os.symlink(tarinfo.linkname, targetpath) ++ return + else: + if os.path.exists(tarinfo._link_target): + os.link(tarinfo._link_target, targetpath) +- else: +- self._extract_member(self._find_link_target(tarinfo), +- targetpath) ++ return + except symlink_exception: ++ keyerror_to_extracterror = True ++ ++ try: ++ unfiltered = self._find_link_target(tarinfo) ++ except KeyError: ++ if keyerror_to_extracterror: ++ raise ExtractError( ++ "unable to resolve link inside archive") from None ++ else: ++ raise ++ ++ if filter_function is None: ++ filtered = unfiltered ++ else: ++ if extraction_root is None: ++ raise ExtractError( ++ "makelink_with_filter: if filter_function is not None, " ++ + "extraction_root must also not be None") + try: +- self._extract_member(self._find_link_target(tarinfo), +- targetpath) +- except KeyError: +- raise ExtractError("unable to resolve link inside archive") from None ++ filtered = filter_function(unfiltered, extraction_root) ++ except _FILTER_ERRORS as cause: ++ raise LinkFallbackError(tarinfo, unfiltered.name) from cause ++ if filtered is not None: ++ self._extract_member(filtered, targetpath, ++ filter_function=filter_function, ++ extraction_root=extraction_root) + + def chown(self, tarinfo, targetpath, numeric_owner): + """Set owner of targetpath according to tarinfo. If numeric_owner +diff --git a/Lib/test/test_ntpath.py b/Lib/test/test_ntpath.py +index f83ef225a6e48e..927a0befcc8c13 100644 +--- a/Lib/test/test_ntpath.py ++++ b/Lib/test/test_ntpath.py +@@ -7,7 +7,8 @@ + import unittest + import warnings + from test.support import cpython_only, os_helper +-from test.support import TestFailed, is_emscripten ++from test.support import TestFailed ++from ntpath import ALLOW_MISSING + from test.support.os_helper import FakePath + from test import test_genericpath + from tempfile import TemporaryFile +@@ -77,6 +78,27 @@ def tester(fn, wantResult): + %(str(fn), str(wantResult), repr(gotResult))) + + ++def _parameterize(*parameters): ++ """Simplistic decorator to parametrize a test ++ ++ Runs the decorated test multiple times in subTest, with a value from ++ 'parameters' passed as an extra positional argument. ++ Calls doCleanups() after each run. ++ ++ Not for general use. Intended to avoid indenting for easier backports. ++ ++ See https://discuss.python.org/t/91827 for discussing generalizations. ++ """ ++ def _parametrize_decorator(func): ++ def _parameterized(self, *args, **kwargs): ++ for parameter in parameters: ++ with self.subTest(parameter): ++ func(self, *args, parameter, **kwargs) ++ self.doCleanups() ++ return _parameterized ++ return _parametrize_decorator ++ ++ + class NtpathTestCase(unittest.TestCase): + def assertPathEqual(self, path1, path2): + if path1 == path2 or _norm(path1) == _norm(path2): +@@ -475,6 +497,27 @@ def test_realpath_curdir(self): + tester("ntpath.realpath('.\\.')", expected) + tester("ntpath.realpath('\\'.join(['.'] * 100))", expected) + ++ def test_realpath_curdir_strict(self): ++ expected = ntpath.normpath(os.getcwd()) ++ tester("ntpath.realpath('.', strict=True)", expected) ++ tester("ntpath.realpath('./.', strict=True)", expected) ++ tester("ntpath.realpath('/'.join(['.'] * 100), strict=True)", expected) ++ tester("ntpath.realpath('.\\.', strict=True)", expected) ++ tester("ntpath.realpath('\\'.join(['.'] * 100), strict=True)", expected) ++ ++ def test_realpath_curdir_missing_ok(self): ++ expected = ntpath.normpath(os.getcwd()) ++ tester("ntpath.realpath('.', strict=ALLOW_MISSING)", ++ expected) ++ tester("ntpath.realpath('./.', strict=ALLOW_MISSING)", ++ expected) ++ tester("ntpath.realpath('/'.join(['.'] * 100), strict=ALLOW_MISSING)", ++ expected) ++ tester("ntpath.realpath('.\\.', strict=ALLOW_MISSING)", ++ expected) ++ tester("ntpath.realpath('\\'.join(['.'] * 100), strict=ALLOW_MISSING)", ++ expected) ++ + def test_realpath_pardir(self): + expected = ntpath.normpath(os.getcwd()) + tester("ntpath.realpath('..')", ntpath.dirname(expected)) +@@ -487,24 +530,59 @@ def test_realpath_pardir(self): + tester("ntpath.realpath('\\'.join(['..'] * 50))", + ntpath.splitdrive(expected)[0] + '\\') + ++ def test_realpath_pardir_strict(self): ++ expected = ntpath.normpath(os.getcwd()) ++ tester("ntpath.realpath('..', strict=True)", ntpath.dirname(expected)) ++ tester("ntpath.realpath('../..', strict=True)", ++ ntpath.dirname(ntpath.dirname(expected))) ++ tester("ntpath.realpath('/'.join(['..'] * 50), strict=True)", ++ ntpath.splitdrive(expected)[0] + '\\') ++ tester("ntpath.realpath('..\\..', strict=True)", ++ ntpath.dirname(ntpath.dirname(expected))) ++ tester("ntpath.realpath('\\'.join(['..'] * 50), strict=True)", ++ ntpath.splitdrive(expected)[0] + '\\') ++ ++ def test_realpath_pardir_missing_ok(self): ++ expected = ntpath.normpath(os.getcwd()) ++ tester("ntpath.realpath('..', strict=ALLOW_MISSING)", ++ ntpath.dirname(expected)) ++ tester("ntpath.realpath('../..', strict=ALLOW_MISSING)", ++ ntpath.dirname(ntpath.dirname(expected))) ++ tester("ntpath.realpath('/'.join(['..'] * 50), strict=ALLOW_MISSING)", ++ ntpath.splitdrive(expected)[0] + '\\') ++ tester("ntpath.realpath('..\\..', strict=ALLOW_MISSING)", ++ ntpath.dirname(ntpath.dirname(expected))) ++ tester("ntpath.realpath('\\'.join(['..'] * 50), strict=ALLOW_MISSING)", ++ ntpath.splitdrive(expected)[0] + '\\') ++ + @os_helper.skip_unless_symlink + @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname') +- def test_realpath_basic(self): ++ @_parameterize({}, {'strict': True}, {'strict': ALLOW_MISSING}) ++ def test_realpath_basic(self, kwargs): + ABSTFN = ntpath.abspath(os_helper.TESTFN) + open(ABSTFN, "wb").close() + self.addCleanup(os_helper.unlink, ABSTFN) + self.addCleanup(os_helper.unlink, ABSTFN + "1") + + os.symlink(ABSTFN, ABSTFN + "1") +- self.assertPathEqual(ntpath.realpath(ABSTFN + "1"), ABSTFN) +- self.assertPathEqual(ntpath.realpath(os.fsencode(ABSTFN + "1")), ++ self.assertPathEqual(ntpath.realpath(ABSTFN + "1", **kwargs), ABSTFN) ++ self.assertPathEqual(ntpath.realpath(os.fsencode(ABSTFN + "1"), **kwargs), + os.fsencode(ABSTFN)) + + # gh-88013: call ntpath.realpath with binary drive name may raise a + # TypeError. The drive should not exist to reproduce the bug. + drives = {f"{c}:\\" for c in string.ascii_uppercase} - set(os.listdrives()) + d = drives.pop().encode() +- self.assertEqual(ntpath.realpath(d), d) ++ self.assertEqual(ntpath.realpath(d, strict=False), d) ++ ++ # gh-106242: Embedded nulls and non-strict fallback to abspath ++ if kwargs: ++ with self.assertRaises(OSError): ++ ntpath.realpath(os_helper.TESTFN + "\0spam", ++ **kwargs) ++ else: ++ self.assertEqual(ABSTFN + "\0spam", ++ ntpath.realpath(os_helper.TESTFN + "\0spam", **kwargs)) + + @os_helper.skip_unless_symlink + @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname') +@@ -527,51 +605,66 @@ def test_realpath_invalid_paths(self): + self.assertEqual(realpath(path, strict=False), path) + # gh-106242: Embedded nulls should raise OSError (not ValueError) + self.assertRaises(OSError, realpath, path, strict=True) ++ self.assertRaises(OSError, realpath, path, strict=ALLOW_MISSING) + path = ABSTFNb + b'\x00' + self.assertEqual(realpath(path, strict=False), path) + self.assertRaises(OSError, realpath, path, strict=True) ++ self.assertRaises(OSError, realpath, path, strict=ALLOW_MISSING) + path = ABSTFN + '\\nonexistent\\x\x00' + self.assertEqual(realpath(path, strict=False), path) + self.assertRaises(OSError, realpath, path, strict=True) ++ self.assertRaises(OSError, realpath, path, strict=ALLOW_MISSING) + path = ABSTFNb + b'\\nonexistent\\x\x00' + self.assertEqual(realpath(path, strict=False), path) + self.assertRaises(OSError, realpath, path, strict=True) ++ self.assertRaises(OSError, realpath, path, strict=ALLOW_MISSING) + path = ABSTFN + '\x00\\..' + self.assertEqual(realpath(path, strict=False), os.getcwd()) + self.assertEqual(realpath(path, strict=True), os.getcwd()) ++ self.assertEqual(realpath(path, strict=ALLOW_MISSING), os.getcwd()) + path = ABSTFNb + b'\x00\\..' + self.assertEqual(realpath(path, strict=False), os.getcwdb()) + self.assertEqual(realpath(path, strict=True), os.getcwdb()) ++ self.assertEqual(realpath(path, strict=ALLOW_MISSING), os.getcwdb()) + path = ABSTFN + '\\nonexistent\\x\x00\\..' + self.assertEqual(realpath(path, strict=False), ABSTFN + '\\nonexistent') + self.assertRaises(OSError, realpath, path, strict=True) ++ self.assertEqual(realpath(path, strict=ALLOW_MISSING), ABSTFN + '\\nonexistent') + path = ABSTFNb + b'\\nonexistent\\x\x00\\..' + self.assertEqual(realpath(path, strict=False), ABSTFNb + b'\\nonexistent') + self.assertRaises(OSError, realpath, path, strict=True) ++ self.assertEqual(realpath(path, strict=ALLOW_MISSING), ABSTFNb + b'\\nonexistent') + ++ @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname') ++ @_parameterize({}, {'strict': True}, {'strict': ALLOW_MISSING}) ++ def test_realpath_invalid_unicode_paths(self, kwargs): ++ realpath = ntpath.realpath ++ ABSTFN = ntpath.abspath(os_helper.TESTFN) ++ ABSTFNb = os.fsencode(ABSTFN) + path = ABSTFNb + b'\xff' +- self.assertRaises(UnicodeDecodeError, realpath, path, strict=False) +- self.assertRaises(UnicodeDecodeError, realpath, path, strict=True) ++ self.assertRaises(UnicodeDecodeError, realpath, path, **kwargs) ++ self.assertRaises(UnicodeDecodeError, realpath, path, **kwargs) + path = ABSTFNb + b'\\nonexistent\\\xff' +- self.assertRaises(UnicodeDecodeError, realpath, path, strict=False) +- self.assertRaises(UnicodeDecodeError, realpath, path, strict=True) ++ self.assertRaises(UnicodeDecodeError, realpath, path, **kwargs) ++ self.assertRaises(UnicodeDecodeError, realpath, path, **kwargs) + path = ABSTFNb + b'\xff\\..' +- self.assertRaises(UnicodeDecodeError, realpath, path, strict=False) +- self.assertRaises(UnicodeDecodeError, realpath, path, strict=True) ++ self.assertRaises(UnicodeDecodeError, realpath, path, **kwargs) ++ self.assertRaises(UnicodeDecodeError, realpath, path, **kwargs) + path = ABSTFNb + b'\\nonexistent\\\xff\\..' +- self.assertRaises(UnicodeDecodeError, realpath, path, strict=False) +- self.assertRaises(UnicodeDecodeError, realpath, path, strict=True) ++ self.assertRaises(UnicodeDecodeError, realpath, path, **kwargs) ++ self.assertRaises(UnicodeDecodeError, realpath, path, **kwargs) + + @os_helper.skip_unless_symlink + @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname') +- def test_realpath_relative(self): ++ @_parameterize({}, {'strict': True}, {'strict': ALLOW_MISSING}) ++ def test_realpath_relative(self, kwargs): + ABSTFN = ntpath.abspath(os_helper.TESTFN) + open(ABSTFN, "wb").close() + self.addCleanup(os_helper.unlink, ABSTFN) + self.addCleanup(os_helper.unlink, ABSTFN + "1") + + os.symlink(ABSTFN, ntpath.relpath(ABSTFN + "1")) +- self.assertPathEqual(ntpath.realpath(ABSTFN + "1"), ABSTFN) ++ self.assertPathEqual(ntpath.realpath(ABSTFN + "1", **kwargs), ABSTFN) + + @os_helper.skip_unless_symlink + @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname') +@@ -723,7 +816,62 @@ def test_realpath_symlink_loops_strict(self): + + @os_helper.skip_unless_symlink + @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname') +- def test_realpath_symlink_prefix(self): ++ def test_realpath_symlink_loops_raise(self): ++ # Symlink loops raise OSError in ALLOW_MISSING mode ++ ABSTFN = ntpath.abspath(os_helper.TESTFN) ++ self.addCleanup(os_helper.unlink, ABSTFN) ++ self.addCleanup(os_helper.unlink, ABSTFN + "1") ++ self.addCleanup(os_helper.unlink, ABSTFN + "2") ++ self.addCleanup(os_helper.unlink, ABSTFN + "y") ++ self.addCleanup(os_helper.unlink, ABSTFN + "c") ++ self.addCleanup(os_helper.unlink, ABSTFN + "a") ++ self.addCleanup(os_helper.unlink, ABSTFN + "x") ++ ++ os.symlink(ABSTFN, ABSTFN) ++ self.assertRaises(OSError, ntpath.realpath, ABSTFN, strict=ALLOW_MISSING) ++ ++ os.symlink(ABSTFN + "1", ABSTFN + "2") ++ os.symlink(ABSTFN + "2", ABSTFN + "1") ++ self.assertRaises(OSError, ntpath.realpath, ABSTFN + "1", ++ strict=ALLOW_MISSING) ++ self.assertRaises(OSError, ntpath.realpath, ABSTFN + "2", ++ strict=ALLOW_MISSING) ++ self.assertRaises(OSError, ntpath.realpath, ABSTFN + "1\\x", ++ strict=ALLOW_MISSING) ++ ++ # Windows eliminates '..' components before resolving links; ++ # realpath is not expected to raise if this removes the loop. ++ self.assertPathEqual(ntpath.realpath(ABSTFN + "1\\.."), ++ ntpath.dirname(ABSTFN)) ++ self.assertPathEqual(ntpath.realpath(ABSTFN + "1\\..\\x"), ++ ntpath.dirname(ABSTFN) + "\\x") ++ ++ os.symlink(ABSTFN + "x", ABSTFN + "y") ++ self.assertPathEqual(ntpath.realpath(ABSTFN + "1\\..\\" ++ + ntpath.basename(ABSTFN) + "y"), ++ ABSTFN + "x") ++ self.assertRaises( ++ OSError, ntpath.realpath, ++ ABSTFN + "1\\..\\" + ntpath.basename(ABSTFN) + "1", ++ strict=ALLOW_MISSING) ++ ++ os.symlink(ntpath.basename(ABSTFN) + "a\\b", ABSTFN + "a") ++ self.assertRaises(OSError, ntpath.realpath, ABSTFN + "a", ++ strict=ALLOW_MISSING) ++ ++ os.symlink("..\\" + ntpath.basename(ntpath.dirname(ABSTFN)) ++ + "\\" + ntpath.basename(ABSTFN) + "c", ABSTFN + "c") ++ self.assertRaises(OSError, ntpath.realpath, ABSTFN + "c", ++ strict=ALLOW_MISSING) ++ ++ # Test using relative path as well. ++ self.assertRaises(OSError, ntpath.realpath, ntpath.basename(ABSTFN), ++ strict=ALLOW_MISSING) ++ ++ @os_helper.skip_unless_symlink ++ @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname') ++ @_parameterize({}, {'strict': True}, {'strict': ALLOW_MISSING}) ++ def test_realpath_symlink_prefix(self, kwargs): + ABSTFN = ntpath.abspath(os_helper.TESTFN) + self.addCleanup(os_helper.unlink, ABSTFN + "3") + self.addCleanup(os_helper.unlink, "\\\\?\\" + ABSTFN + "3.") +@@ -738,9 +886,9 @@ def test_realpath_symlink_prefix(self): + f.write(b'1') + os.symlink("\\\\?\\" + ABSTFN + "3.", ABSTFN + "3.link") + +- self.assertPathEqual(ntpath.realpath(ABSTFN + "3link"), ++ self.assertPathEqual(ntpath.realpath(ABSTFN + "3link", **kwargs), + ABSTFN + "3") +- self.assertPathEqual(ntpath.realpath(ABSTFN + "3.link"), ++ self.assertPathEqual(ntpath.realpath(ABSTFN + "3.link", **kwargs), + "\\\\?\\" + ABSTFN + "3.") + + # Resolved paths should be usable to open target files +@@ -750,14 +898,17 @@ def test_realpath_symlink_prefix(self): + self.assertEqual(f.read(), b'1') + + # When the prefix is included, it is not stripped +- self.assertPathEqual(ntpath.realpath("\\\\?\\" + ABSTFN + "3link"), ++ self.assertPathEqual(ntpath.realpath("\\\\?\\" + ABSTFN + "3link", **kwargs), + "\\\\?\\" + ABSTFN + "3") +- self.assertPathEqual(ntpath.realpath("\\\\?\\" + ABSTFN + "3.link"), ++ self.assertPathEqual(ntpath.realpath("\\\\?\\" + ABSTFN + "3.link", **kwargs), + "\\\\?\\" + ABSTFN + "3.") + + @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname') + def test_realpath_nul(self): + tester("ntpath.realpath('NUL')", r'\\.\NUL') ++ tester("ntpath.realpath('NUL', strict=False)", r'\\.\NUL') ++ tester("ntpath.realpath('NUL', strict=True)", r'\\.\NUL') ++ tester("ntpath.realpath('NUL', strict=ALLOW_MISSING)", r'\\.\NUL') + + @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname') + @unittest.skipUnless(HAVE_GETSHORTPATHNAME, 'need _getshortpathname') +@@ -781,12 +932,20 @@ def test_realpath_cwd(self): + + self.assertPathEqual(test_file_long, ntpath.realpath(test_file_short)) + +- with os_helper.change_cwd(test_dir_long): +- self.assertPathEqual(test_file_long, ntpath.realpath("file.txt")) +- with os_helper.change_cwd(test_dir_long.lower()): +- self.assertPathEqual(test_file_long, ntpath.realpath("file.txt")) +- with os_helper.change_cwd(test_dir_short): +- self.assertPathEqual(test_file_long, ntpath.realpath("file.txt")) ++ for kwargs in {}, {'strict': True}, {'strict': ALLOW_MISSING}: ++ with self.subTest(**kwargs): ++ with os_helper.change_cwd(test_dir_long): ++ self.assertPathEqual( ++ test_file_long, ++ ntpath.realpath("file.txt", **kwargs)) ++ with os_helper.change_cwd(test_dir_long.lower()): ++ self.assertPathEqual( ++ test_file_long, ++ ntpath.realpath("file.txt", **kwargs)) ++ with os_helper.change_cwd(test_dir_short): ++ self.assertPathEqual( ++ test_file_long, ++ ntpath.realpath("file.txt", **kwargs)) + + @unittest.skipUnless(HAVE_GETFINALPATHNAME, 'need _getfinalpathname') + def test_realpath_permission(self): +@@ -807,12 +966,15 @@ def test_realpath_permission(self): + # Automatic generation of short names may be disabled on + # NTFS volumes for the sake of performance. + # They're not supported at all on ReFS and exFAT. +- subprocess.run( ++ p = subprocess.run( + # Try to set the short name manually. + ['fsutil.exe', 'file', 'setShortName', test_file, 'LONGFI~1.TXT'], + creationflags=subprocess.DETACHED_PROCESS + ) + ++ if p.returncode: ++ raise unittest.SkipTest('failed to set short name') ++ + try: + self.assertPathEqual(test_file, ntpath.realpath(test_file_short)) + except AssertionError: +diff --git a/Lib/test/test_posixpath.py b/Lib/test/test_posixpath.py +index f3f9895f529470..c45ce6d3ef7820 100644 +--- a/Lib/test/test_posixpath.py ++++ b/Lib/test/test_posixpath.py +@@ -4,7 +4,8 @@ + import random + import sys + import unittest +-from posixpath import realpath, abspath, dirname, basename ++from functools import partial ++from posixpath import realpath, abspath, dirname, basename, ALLOW_MISSING + from test import support + from test import test_genericpath + from test.support import import_helper +@@ -33,6 +34,27 @@ def skip_if_ABSTFN_contains_backslash(test): + msg = "ABSTFN is not a posix path - tests fail" + return [test, unittest.skip(msg)(test)][found_backslash] + ++ ++def _parameterize(*parameters): ++ """Simplistic decorator to parametrize a test ++ ++ Runs the decorated test multiple times in subTest, with a value from ++ 'parameters' passed as an extra positional argument. ++ Does *not* call doCleanups() after each run. ++ ++ Not for general use. Intended to avoid indenting for easier backports. ++ ++ See https://discuss.python.org/t/91827 for discussing generalizations. ++ """ ++ def _parametrize_decorator(func): ++ def _parameterized(self, *args, **kwargs): ++ for parameter in parameters: ++ with self.subTest(parameter): ++ func(self, *args, parameter, **kwargs) ++ return _parameterized ++ return _parametrize_decorator ++ ++ + class PosixPathTest(unittest.TestCase): + + def setUp(self): +@@ -442,32 +464,35 @@ def test_normpath(self): + self.assertEqual(result, expected) + + @skip_if_ABSTFN_contains_backslash +- def test_realpath_curdir(self): +- self.assertEqual(realpath('.'), os.getcwd()) +- self.assertEqual(realpath('./.'), os.getcwd()) +- self.assertEqual(realpath('/'.join(['.'] * 100)), os.getcwd()) ++ @_parameterize({}, {'strict': True}, {'strict': ALLOW_MISSING}) ++ def test_realpath_curdir(self, kwargs): ++ self.assertEqual(realpath('.', **kwargs), os.getcwd()) ++ self.assertEqual(realpath('./.', **kwargs), os.getcwd()) ++ self.assertEqual(realpath('/'.join(['.'] * 100), **kwargs), os.getcwd()) + +- self.assertEqual(realpath(b'.'), os.getcwdb()) +- self.assertEqual(realpath(b'./.'), os.getcwdb()) +- self.assertEqual(realpath(b'/'.join([b'.'] * 100)), os.getcwdb()) ++ self.assertEqual(realpath(b'.', **kwargs), os.getcwdb()) ++ self.assertEqual(realpath(b'./.', **kwargs), os.getcwdb()) ++ self.assertEqual(realpath(b'/'.join([b'.'] * 100), **kwargs), os.getcwdb()) + + @skip_if_ABSTFN_contains_backslash +- def test_realpath_pardir(self): +- self.assertEqual(realpath('..'), dirname(os.getcwd())) +- self.assertEqual(realpath('../..'), dirname(dirname(os.getcwd()))) +- self.assertEqual(realpath('/'.join(['..'] * 100)), '/') ++ @_parameterize({}, {'strict': True}, {'strict': ALLOW_MISSING}) ++ def test_realpath_pardir(self, kwargs): ++ self.assertEqual(realpath('..', **kwargs), dirname(os.getcwd())) ++ self.assertEqual(realpath('../..', **kwargs), dirname(dirname(os.getcwd()))) ++ self.assertEqual(realpath('/'.join(['..'] * 100), **kwargs), '/') + +- self.assertEqual(realpath(b'..'), dirname(os.getcwdb())) +- self.assertEqual(realpath(b'../..'), dirname(dirname(os.getcwdb()))) +- self.assertEqual(realpath(b'/'.join([b'..'] * 100)), b'/') ++ self.assertEqual(realpath(b'..', **kwargs), dirname(os.getcwdb())) ++ self.assertEqual(realpath(b'../..', **kwargs), dirname(dirname(os.getcwdb()))) ++ self.assertEqual(realpath(b'/'.join([b'..'] * 100), **kwargs), b'/') + + @os_helper.skip_unless_symlink + @skip_if_ABSTFN_contains_backslash +- def test_realpath_basic(self): ++ @_parameterize({}, {'strict': ALLOW_MISSING}) ++ def test_realpath_basic(self, kwargs): + # Basic operation. + try: + os.symlink(ABSTFN+"1", ABSTFN) +- self.assertEqual(realpath(ABSTFN), ABSTFN+"1") ++ self.assertEqual(realpath(ABSTFN, **kwargs), ABSTFN+"1") + finally: + os_helper.unlink(ABSTFN) + +@@ -487,90 +512,115 @@ def test_realpath_invalid_paths(self): + path = '/\x00' + self.assertRaises(ValueError, realpath, path, strict=False) + self.assertRaises(ValueError, realpath, path, strict=True) ++ self.assertRaises(ValueError, realpath, path, strict=ALLOW_MISSING) + path = b'/\x00' + self.assertRaises(ValueError, realpath, path, strict=False) + self.assertRaises(ValueError, realpath, path, strict=True) ++ self.assertRaises(ValueError, realpath, path, strict=ALLOW_MISSING) + path = '/nonexistent/x\x00' + self.assertRaises(ValueError, realpath, path, strict=False) + self.assertRaises(FileNotFoundError, realpath, path, strict=True) ++ self.assertRaises(ValueError, realpath, path, strict=ALLOW_MISSING) + path = b'/nonexistent/x\x00' + self.assertRaises(ValueError, realpath, path, strict=False) + self.assertRaises(FileNotFoundError, realpath, path, strict=True) ++ self.assertRaises(ValueError, realpath, path, strict=ALLOW_MISSING) + path = '/\x00/..' + self.assertRaises(ValueError, realpath, path, strict=False) + self.assertRaises(ValueError, realpath, path, strict=True) ++ self.assertRaises(ValueError, realpath, path, strict=ALLOW_MISSING) + path = b'/\x00/..' + self.assertRaises(ValueError, realpath, path, strict=False) + self.assertRaises(ValueError, realpath, path, strict=True) ++ self.assertRaises(ValueError, realpath, path, strict=ALLOW_MISSING) ++ + path = '/nonexistent/x\x00/..' + self.assertRaises(ValueError, realpath, path, strict=False) + self.assertRaises(FileNotFoundError, realpath, path, strict=True) ++ self.assertRaises(ValueError, realpath, path, strict=ALLOW_MISSING) + path = b'/nonexistent/x\x00/..' + self.assertRaises(ValueError, realpath, path, strict=False) + self.assertRaises(FileNotFoundError, realpath, path, strict=True) ++ self.assertRaises(ValueError, realpath, path, strict=ALLOW_MISSING) + + path = '/\udfff' + if sys.platform == 'win32': + self.assertEqual(realpath(path, strict=False), path) + self.assertRaises(FileNotFoundError, realpath, path, strict=True) ++ self.assertEqual(realpath(path, strict=ALLOW_MISSING), path) + else: + self.assertRaises(UnicodeEncodeError, realpath, path, strict=False) + self.assertRaises(UnicodeEncodeError, realpath, path, strict=True) ++ self.assertRaises(UnicodeEncodeError, realpath, path, strict=ALLOW_MISSING) + path = '/nonexistent/\udfff' + if sys.platform == 'win32': + self.assertEqual(realpath(path, strict=False), path) ++ self.assertEqual(realpath(path, strict=ALLOW_MISSING), path) + else: + self.assertRaises(UnicodeEncodeError, realpath, path, strict=False) ++ self.assertRaises(UnicodeEncodeError, realpath, path, strict=ALLOW_MISSING) + self.assertRaises(FileNotFoundError, realpath, path, strict=True) + path = '/\udfff/..' + if sys.platform == 'win32': + self.assertEqual(realpath(path, strict=False), '/') + self.assertRaises(FileNotFoundError, realpath, path, strict=True) ++ self.assertEqual(realpath(path, strict=ALLOW_MISSING), '/') + else: + self.assertRaises(UnicodeEncodeError, realpath, path, strict=False) + self.assertRaises(UnicodeEncodeError, realpath, path, strict=True) ++ self.assertRaises(UnicodeEncodeError, realpath, path, strict=ALLOW_MISSING) + path = '/nonexistent/\udfff/..' + if sys.platform == 'win32': + self.assertEqual(realpath(path, strict=False), '/nonexistent') ++ self.assertEqual(realpath(path, strict=ALLOW_MISSING), '/nonexistent') + else: + self.assertRaises(UnicodeEncodeError, realpath, path, strict=False) ++ self.assertRaises(UnicodeEncodeError, realpath, path, strict=ALLOW_MISSING) + self.assertRaises(FileNotFoundError, realpath, path, strict=True) + + path = b'/\xff' + if sys.platform == 'win32': + self.assertRaises(UnicodeDecodeError, realpath, path, strict=False) + self.assertRaises(UnicodeDecodeError, realpath, path, strict=True) ++ self.assertRaises(UnicodeDecodeError, realpath, path, strict=ALLOW_MISSING) + else: + self.assertEqual(realpath(path, strict=False), path) + if support.is_wasi: + self.assertRaises(OSError, realpath, path, strict=True) ++ self.assertRaises(OSError, realpath, path, strict=ALLOW_MISSING) + else: + self.assertRaises(FileNotFoundError, realpath, path, strict=True) ++ self.assertEqual(realpath(path, strict=ALLOW_MISSING), path) + path = b'/nonexistent/\xff' + if sys.platform == 'win32': + self.assertRaises(UnicodeDecodeError, realpath, path, strict=False) ++ self.assertRaises(UnicodeDecodeError, realpath, path, strict=ALLOW_MISSING) + else: + self.assertEqual(realpath(path, strict=False), path) + if support.is_wasi: + self.assertRaises(OSError, realpath, path, strict=True) ++ self.assertRaises(OSError, realpath, path, strict=ALLOW_MISSING) + else: + self.assertRaises(FileNotFoundError, realpath, path, strict=True) + + @os_helper.skip_unless_symlink + @skip_if_ABSTFN_contains_backslash +- def test_realpath_relative(self): ++ @_parameterize({}, {'strict': ALLOW_MISSING}) ++ def test_realpath_relative(self, kwargs): + try: + os.symlink(posixpath.relpath(ABSTFN+"1"), ABSTFN) +- self.assertEqual(realpath(ABSTFN), ABSTFN+"1") ++ self.assertEqual(realpath(ABSTFN, **kwargs), ABSTFN+"1") + finally: + os_helper.unlink(ABSTFN) + + @os_helper.skip_unless_symlink + @skip_if_ABSTFN_contains_backslash +- def test_realpath_missing_pardir(self): ++ @_parameterize({}, {'strict': ALLOW_MISSING}) ++ def test_realpath_missing_pardir(self, kwargs): + try: + os.symlink(TESTFN + "1", TESTFN) +- self.assertEqual(realpath("nonexistent/../" + TESTFN), ABSTFN + "1") ++ self.assertEqual( ++ realpath("nonexistent/../" + TESTFN, **kwargs), ABSTFN + "1") + finally: + os_helper.unlink(TESTFN) + +@@ -617,37 +667,38 @@ def test_realpath_symlink_loops(self): + + @os_helper.skip_unless_symlink + @skip_if_ABSTFN_contains_backslash +- def test_realpath_symlink_loops_strict(self): ++ @_parameterize({'strict': True}, {'strict': ALLOW_MISSING}) ++ def test_realpath_symlink_loops_strict(self, kwargs): + # Bug #43757, raise OSError if we get into an infinite symlink loop in +- # strict mode. ++ # the strict modes. + try: + os.symlink(ABSTFN, ABSTFN) +- self.assertRaises(OSError, realpath, ABSTFN, strict=True) ++ self.assertRaises(OSError, realpath, ABSTFN, **kwargs) + + os.symlink(ABSTFN+"1", ABSTFN+"2") + os.symlink(ABSTFN+"2", ABSTFN+"1") +- self.assertRaises(OSError, realpath, ABSTFN+"1", strict=True) +- self.assertRaises(OSError, realpath, ABSTFN+"2", strict=True) ++ self.assertRaises(OSError, realpath, ABSTFN+"1", **kwargs) ++ self.assertRaises(OSError, realpath, ABSTFN+"2", **kwargs) + +- self.assertRaises(OSError, realpath, ABSTFN+"1/x", strict=True) +- self.assertRaises(OSError, realpath, ABSTFN+"1/..", strict=True) +- self.assertRaises(OSError, realpath, ABSTFN+"1/../x", strict=True) ++ self.assertRaises(OSError, realpath, ABSTFN+"1/x", **kwargs) ++ self.assertRaises(OSError, realpath, ABSTFN+"1/..", **kwargs) ++ self.assertRaises(OSError, realpath, ABSTFN+"1/../x", **kwargs) + os.symlink(ABSTFN+"x", ABSTFN+"y") + self.assertRaises(OSError, realpath, +- ABSTFN+"1/../" + basename(ABSTFN) + "y", strict=True) ++ ABSTFN+"1/../" + basename(ABSTFN) + "y", **kwargs) + self.assertRaises(OSError, realpath, +- ABSTFN+"1/../" + basename(ABSTFN) + "1", strict=True) ++ ABSTFN+"1/../" + basename(ABSTFN) + "1", **kwargs) + + os.symlink(basename(ABSTFN) + "a/b", ABSTFN+"a") +- self.assertRaises(OSError, realpath, ABSTFN+"a", strict=True) ++ self.assertRaises(OSError, realpath, ABSTFN+"a", **kwargs) + + os.symlink("../" + basename(dirname(ABSTFN)) + "/" + + basename(ABSTFN) + "c", ABSTFN+"c") +- self.assertRaises(OSError, realpath, ABSTFN+"c", strict=True) ++ self.assertRaises(OSError, realpath, ABSTFN+"c", **kwargs) + + # Test using relative path as well. + with os_helper.change_cwd(dirname(ABSTFN)): +- self.assertRaises(OSError, realpath, basename(ABSTFN), strict=True) ++ self.assertRaises(OSError, realpath, basename(ABSTFN), **kwargs) + finally: + os_helper.unlink(ABSTFN) + os_helper.unlink(ABSTFN+"1") +@@ -658,13 +709,14 @@ def test_realpath_symlink_loops_strict(self): + + @os_helper.skip_unless_symlink + @skip_if_ABSTFN_contains_backslash +- def test_realpath_repeated_indirect_symlinks(self): ++ @_parameterize({}, {'strict': True}, {'strict': ALLOW_MISSING}) ++ def test_realpath_repeated_indirect_symlinks(self, kwargs): + # Issue #6975. + try: + os.mkdir(ABSTFN) + os.symlink('../' + basename(ABSTFN), ABSTFN + '/self') + os.symlink('self/self/self', ABSTFN + '/link') +- self.assertEqual(realpath(ABSTFN + '/link'), ABSTFN) ++ self.assertEqual(realpath(ABSTFN + '/link', **kwargs), ABSTFN) + finally: + os_helper.unlink(ABSTFN + '/self') + os_helper.unlink(ABSTFN + '/link') +@@ -672,14 +724,15 @@ def test_realpath_repeated_indirect_symlinks(self): + + @os_helper.skip_unless_symlink + @skip_if_ABSTFN_contains_backslash +- def test_realpath_deep_recursion(self): ++ @_parameterize({}, {'strict': True}, {'strict': ALLOW_MISSING}) ++ def test_realpath_deep_recursion(self, kwargs): + depth = 10 + try: + os.mkdir(ABSTFN) + for i in range(depth): + os.symlink('/'.join(['%d' % i] * 10), ABSTFN + '/%d' % (i + 1)) + os.symlink('.', ABSTFN + '/0') +- self.assertEqual(realpath(ABSTFN + '/%d' % depth), ABSTFN) ++ self.assertEqual(realpath(ABSTFN + '/%d' % depth, **kwargs), ABSTFN) + + # Test using relative path as well. + with os_helper.change_cwd(ABSTFN): +@@ -691,7 +744,8 @@ def test_realpath_deep_recursion(self): + + @os_helper.skip_unless_symlink + @skip_if_ABSTFN_contains_backslash +- def test_realpath_resolve_parents(self): ++ @_parameterize({}, {'strict': ALLOW_MISSING}) ++ def test_realpath_resolve_parents(self, kwargs): + # We also need to resolve any symlinks in the parents of a relative + # path passed to realpath. E.g.: current working directory is + # /usr/doc with 'doc' being a symlink to /usr/share/doc. We call +@@ -702,7 +756,8 @@ def test_realpath_resolve_parents(self): + os.symlink(ABSTFN + "/y", ABSTFN + "/k") + + with os_helper.change_cwd(ABSTFN + "/k"): +- self.assertEqual(realpath("a"), ABSTFN + "/y/a") ++ self.assertEqual(realpath("a", **kwargs), ++ ABSTFN + "/y/a") + finally: + os_helper.unlink(ABSTFN + "/k") + os_helper.rmdir(ABSTFN + "/y") +@@ -710,7 +765,8 @@ def test_realpath_resolve_parents(self): + + @os_helper.skip_unless_symlink + @skip_if_ABSTFN_contains_backslash +- def test_realpath_resolve_before_normalizing(self): ++ @_parameterize({}, {'strict': True}, {'strict': ALLOW_MISSING}) ++ def test_realpath_resolve_before_normalizing(self, kwargs): + # Bug #990669: Symbolic links should be resolved before we + # normalize the path. E.g.: if we have directories 'a', 'k' and 'y' + # in the following hierarchy: +@@ -725,10 +781,10 @@ def test_realpath_resolve_before_normalizing(self): + os.symlink(ABSTFN + "/k/y", ABSTFN + "/link-y") + + # Absolute path. +- self.assertEqual(realpath(ABSTFN + "/link-y/.."), ABSTFN + "/k") ++ self.assertEqual(realpath(ABSTFN + "/link-y/..", **kwargs), ABSTFN + "/k") + # Relative path. + with os_helper.change_cwd(dirname(ABSTFN)): +- self.assertEqual(realpath(basename(ABSTFN) + "/link-y/.."), ++ self.assertEqual(realpath(basename(ABSTFN) + "/link-y/..", **kwargs), + ABSTFN + "/k") + finally: + os_helper.unlink(ABSTFN + "/link-y") +@@ -738,7 +794,8 @@ def test_realpath_resolve_before_normalizing(self): + + @os_helper.skip_unless_symlink + @skip_if_ABSTFN_contains_backslash +- def test_realpath_resolve_first(self): ++ @_parameterize({}, {'strict': True}, {'strict': ALLOW_MISSING}) ++ def test_realpath_resolve_first(self, kwargs): + # Bug #1213894: The first component of the path, if not absolute, + # must be resolved too. + +@@ -748,8 +805,8 @@ def test_realpath_resolve_first(self): + os.symlink(ABSTFN, ABSTFN + "link") + with os_helper.change_cwd(dirname(ABSTFN)): + base = basename(ABSTFN) +- self.assertEqual(realpath(base + "link"), ABSTFN) +- self.assertEqual(realpath(base + "link/k"), ABSTFN + "/k") ++ self.assertEqual(realpath(base + "link", **kwargs), ABSTFN) ++ self.assertEqual(realpath(base + "link/k", **kwargs), ABSTFN + "/k") + finally: + os_helper.unlink(ABSTFN + "link") + os_helper.rmdir(ABSTFN + "/k") +@@ -767,12 +824,67 @@ def test_realpath_unreadable_symlink(self): + self.assertEqual(realpath(ABSTFN + '/foo'), ABSTFN + '/foo') + self.assertEqual(realpath(ABSTFN + '/../foo'), dirname(ABSTFN) + '/foo') + self.assertEqual(realpath(ABSTFN + '/foo/..'), ABSTFN) +- with self.assertRaises(PermissionError): +- realpath(ABSTFN, strict=True) + finally: + os.chmod(ABSTFN, 0o755, follow_symlinks=False) + os_helper.unlink(ABSTFN) + ++ @os_helper.skip_unless_symlink ++ @skip_if_ABSTFN_contains_backslash ++ @unittest.skipIf(os.chmod not in os.supports_follow_symlinks, "Can't set symlink permissions") ++ @unittest.skipIf(sys.platform != "darwin", "only macOS requires read permission to readlink()") ++ @_parameterize({'strict': True}, {'strict': ALLOW_MISSING}) ++ def test_realpath_unreadable_symlink_strict(self, kwargs): ++ try: ++ os.symlink(ABSTFN+"1", ABSTFN) ++ os.chmod(ABSTFN, 0o000, follow_symlinks=False) ++ with self.assertRaises(PermissionError): ++ realpath(ABSTFN, **kwargs) ++ with self.assertRaises(PermissionError): ++ realpath(ABSTFN + '/foo', **kwargs), ++ with self.assertRaises(PermissionError): ++ realpath(ABSTFN + '/../foo', **kwargs) ++ with self.assertRaises(PermissionError): ++ realpath(ABSTFN + '/foo/..', **kwargs) ++ finally: ++ os.chmod(ABSTFN, 0o755, follow_symlinks=False) ++ os.unlink(ABSTFN) ++ ++ @skip_if_ABSTFN_contains_backslash ++ @os_helper.skip_unless_symlink ++ def test_realpath_unreadable_directory(self): ++ try: ++ os.mkdir(ABSTFN) ++ os.mkdir(ABSTFN + '/k') ++ os.chmod(ABSTFN, 0o000) ++ self.assertEqual(realpath(ABSTFN, strict=False), ABSTFN) ++ self.assertEqual(realpath(ABSTFN, strict=True), ABSTFN) ++ self.assertEqual(realpath(ABSTFN, strict=ALLOW_MISSING), ABSTFN) ++ ++ try: ++ os.stat(ABSTFN) ++ except PermissionError: ++ pass ++ else: ++ self.skipTest('Cannot block permissions') ++ ++ self.assertEqual(realpath(ABSTFN + '/k', strict=False), ++ ABSTFN + '/k') ++ self.assertRaises(PermissionError, realpath, ABSTFN + '/k', ++ strict=True) ++ self.assertRaises(PermissionError, realpath, ABSTFN + '/k', ++ strict=ALLOW_MISSING) ++ ++ self.assertEqual(realpath(ABSTFN + '/missing', strict=False), ++ ABSTFN + '/missing') ++ self.assertRaises(PermissionError, realpath, ABSTFN + '/missing', ++ strict=True) ++ self.assertRaises(PermissionError, realpath, ABSTFN + '/missing', ++ strict=ALLOW_MISSING) ++ finally: ++ os.chmod(ABSTFN, 0o755) ++ os_helper.rmdir(ABSTFN + '/k') ++ os_helper.rmdir(ABSTFN) ++ + @skip_if_ABSTFN_contains_backslash + def test_realpath_nonterminal_file(self): + try: +@@ -780,14 +892,27 @@ def test_realpath_nonterminal_file(self): + f.write('test_posixpath wuz ere') + self.assertEqual(realpath(ABSTFN, strict=False), ABSTFN) + self.assertEqual(realpath(ABSTFN, strict=True), ABSTFN) ++ self.assertEqual(realpath(ABSTFN, strict=ALLOW_MISSING), ABSTFN) ++ + self.assertEqual(realpath(ABSTFN + "/", strict=False), ABSTFN) + self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/", strict=True) ++ self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/", ++ strict=ALLOW_MISSING) ++ + self.assertEqual(realpath(ABSTFN + "/.", strict=False), ABSTFN) + self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/.", strict=True) ++ self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/.", ++ strict=ALLOW_MISSING) ++ + self.assertEqual(realpath(ABSTFN + "/..", strict=False), dirname(ABSTFN)) + self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/..", strict=True) ++ self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/..", ++ strict=ALLOW_MISSING) ++ + self.assertEqual(realpath(ABSTFN + "/subdir", strict=False), ABSTFN + "/subdir") + self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/subdir", strict=True) ++ self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/subdir", ++ strict=ALLOW_MISSING) + finally: + os_helper.unlink(ABSTFN) + +@@ -800,14 +925,27 @@ def test_realpath_nonterminal_symlink_to_file(self): + os.symlink(ABSTFN + "1", ABSTFN) + self.assertEqual(realpath(ABSTFN, strict=False), ABSTFN + "1") + self.assertEqual(realpath(ABSTFN, strict=True), ABSTFN + "1") ++ self.assertEqual(realpath(ABSTFN, strict=ALLOW_MISSING), ABSTFN + "1") ++ + self.assertEqual(realpath(ABSTFN + "/", strict=False), ABSTFN + "1") + self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/", strict=True) ++ self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/", ++ strict=ALLOW_MISSING) ++ + self.assertEqual(realpath(ABSTFN + "/.", strict=False), ABSTFN + "1") + self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/.", strict=True) ++ self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/.", ++ strict=ALLOW_MISSING) ++ + self.assertEqual(realpath(ABSTFN + "/..", strict=False), dirname(ABSTFN)) + self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/..", strict=True) ++ self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/..", ++ strict=ALLOW_MISSING) ++ + self.assertEqual(realpath(ABSTFN + "/subdir", strict=False), ABSTFN + "1/subdir") + self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/subdir", strict=True) ++ self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/subdir", ++ strict=ALLOW_MISSING) + finally: + os_helper.unlink(ABSTFN) + os_helper.unlink(ABSTFN + "1") +@@ -822,14 +960,27 @@ def test_realpath_nonterminal_symlink_to_symlinks_to_file(self): + os.symlink(ABSTFN + "1", ABSTFN) + self.assertEqual(realpath(ABSTFN, strict=False), ABSTFN + "2") + self.assertEqual(realpath(ABSTFN, strict=True), ABSTFN + "2") ++ self.assertEqual(realpath(ABSTFN, strict=True), ABSTFN + "2") ++ + self.assertEqual(realpath(ABSTFN + "/", strict=False), ABSTFN + "2") + self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/", strict=True) ++ self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/", ++ strict=ALLOW_MISSING) ++ + self.assertEqual(realpath(ABSTFN + "/.", strict=False), ABSTFN + "2") + self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/.", strict=True) ++ self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/.", ++ strict=ALLOW_MISSING) ++ + self.assertEqual(realpath(ABSTFN + "/..", strict=False), dirname(ABSTFN)) + self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/..", strict=True) ++ self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/..", ++ strict=ALLOW_MISSING) ++ + self.assertEqual(realpath(ABSTFN + "/subdir", strict=False), ABSTFN + "2/subdir") + self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/subdir", strict=True) ++ self.assertRaises(NotADirectoryError, realpath, ABSTFN + "/subdir", ++ strict=ALLOW_MISSING) + finally: + os_helper.unlink(ABSTFN) + os_helper.unlink(ABSTFN + "1") +@@ -1017,9 +1168,12 @@ def test_path_normpath(self): + def test_path_abspath(self): + self.assertPathEqual(self.path.abspath) + +- def test_path_realpath(self): ++ @_parameterize({}, {'strict': True}, {'strict': ALLOW_MISSING}) ++ def test_path_realpath(self, kwargs): + self.assertPathEqual(self.path.realpath) + ++ self.assertPathEqual(partial(self.path.realpath, **kwargs)) ++ + def test_path_relpath(self): + self.assertPathEqual(self.path.relpath) + +diff --git a/Lib/test/test_tarfile.py b/Lib/test/test_tarfile.py +index cf218a2bf14369..7055e1ed147a9e 100644 +--- a/Lib/test/test_tarfile.py ++++ b/Lib/test/test_tarfile.py +@@ -2715,6 +2715,31 @@ def test_useful_error_message_when_modules_missing(self): + str(excinfo.exception), + ) + ++ @unittest.skipUnless(os_helper.can_symlink(), 'requires symlink support') ++ @unittest.skipUnless(hasattr(os, 'chmod'), "missing os.chmod") ++ @unittest.mock.patch('os.chmod') ++ def test_deferred_directory_attributes_update(self, mock_chmod): ++ # Regression test for gh-127987: setting attributes on arbitrary files ++ tempdir = os.path.join(TEMPDIR, 'test127987') ++ def mock_chmod_side_effect(path, mode, **kwargs): ++ target_path = os.path.realpath(path) ++ if os.path.commonpath([target_path, tempdir]) != tempdir: ++ raise Exception("should not try to chmod anything outside the destination", target_path) ++ mock_chmod.side_effect = mock_chmod_side_effect ++ ++ outside_tree_dir = os.path.join(TEMPDIR, 'outside_tree_dir') ++ with ArchiveMaker() as arc: ++ arc.add('x', symlink_to='.') ++ arc.add('x', type=tarfile.DIRTYPE, mode='?rwsrwsrwt') ++ arc.add('x', symlink_to=outside_tree_dir) ++ ++ os.makedirs(outside_tree_dir) ++ try: ++ arc.open().extractall(path=tempdir, filter='tar') ++ finally: ++ os_helper.rmtree(outside_tree_dir) ++ os_helper.rmtree(tempdir) ++ + + class CommandLineTest(unittest.TestCase): + +@@ -3275,6 +3300,10 @@ def check_files_present(self, directory): + got_paths = set( + p.relative_to(directory) + for p in pathlib.Path(directory).glob('**/*')) ++ if self.extraction_filter in (None, 'data'): ++ # The 'data' filter is expected to reject special files ++ for path in 'ustar/fifotype', 'ustar/blktype', 'ustar/chrtype': ++ got_paths.discard(pathlib.Path(path)) + self.assertEqual(self.control_paths, got_paths) + + @contextmanager +@@ -3504,12 +3533,28 @@ def __exit__(self, *exc): + self.bio = None + + def add(self, name, *, type=None, symlink_to=None, hardlink_to=None, +- mode=None, size=None, **kwargs): +- """Add a member to the test archive. Call within `with`.""" ++ mode=None, size=None, content=None, **kwargs): ++ """Add a member to the test archive. Call within `with`. ++ ++ Provides many shortcuts: ++ - default `type` is based on symlink_to, hardlink_to, and trailing `/` ++ in name (which is stripped) ++ - size & content defaults are based on each other ++ - content can be str or bytes ++ - mode should be textual ('-rwxrwxrwx') ++ ++ (add more! this is unstable internal test-only API) ++ """ + name = str(name) + tarinfo = tarfile.TarInfo(name).replace(**kwargs) ++ if content is not None: ++ if isinstance(content, str): ++ content = content.encode() ++ size = len(content) + if size is not None: + tarinfo.size = size ++ if content is None: ++ content = bytes(tarinfo.size) + if mode: + tarinfo.mode = _filemode_to_int(mode) + if symlink_to is not None: +@@ -3523,7 +3568,7 @@ def add(self, name, *, type=None, symlink_to=None, hardlink_to=None, + if type is not None: + tarinfo.type = type + if tarinfo.isreg(): +- fileobj = io.BytesIO(bytes(tarinfo.size)) ++ fileobj = io.BytesIO(content) + else: + fileobj = None + self.tar_w.addfile(tarinfo, fileobj) +@@ -3557,7 +3602,7 @@ class TestExtractionFilters(unittest.TestCase): + destdir = outerdir / 'dest' + + @contextmanager +- def check_context(self, tar, filter): ++ def check_context(self, tar, filter, *, check_flag=True): + """Extracts `tar` to `self.destdir` and allows checking the result + + If an error occurs, it must be checked using `expect_exception` +@@ -3566,27 +3611,40 @@ def check_context(self, tar, filter): + except the destination directory itself and parent directories of + other files. + When checking directories, do so before their contents. ++ ++ A file called 'flag' is made in outerdir (i.e. outside destdir) ++ before extraction; it should not be altered nor should its contents ++ be read/copied. + """ + with os_helper.temp_dir(self.outerdir): ++ flag_path = self.outerdir / 'flag' ++ flag_path.write_text('capture me') + try: + tar.extractall(self.destdir, filter=filter) + except Exception as exc: + self.raised_exception = exc ++ self.reraise_exception = True + self.expected_paths = set() + else: + self.raised_exception = None ++ self.reraise_exception = False + self.expected_paths = set(self.outerdir.glob('**/*')) + self.expected_paths.discard(self.destdir) ++ self.expected_paths.discard(flag_path) + try: +- yield ++ yield self + finally: + tar.close() +- if self.raised_exception: ++ if self.reraise_exception: + raise self.raised_exception + self.assertEqual(self.expected_paths, set()) ++ if check_flag: ++ self.assertEqual(flag_path.read_text(), 'capture me') ++ else: ++ assert filter == 'fully_trusted' + + def expect_file(self, name, type=None, symlink_to=None, mode=None, +- size=None): ++ size=None, content=None): + """Check a single file. See check_context.""" + if self.raised_exception: + raise self.raised_exception +@@ -3605,26 +3663,45 @@ def expect_file(self, name, type=None, symlink_to=None, mode=None, + # The symlink might be the same (textually) as what we expect, + # but some systems change the link to an equivalent path, so + # we fall back to samefile(). +- if expected != got: +- self.assertTrue(got.samefile(expected)) ++ try: ++ if expected != got: ++ self.assertTrue(got.samefile(expected)) ++ except Exception as e: ++ # attach a note, so it's shown even if `samefile` fails ++ e.add_note(f'{expected=}, {got=}') ++ raise + elif type == tarfile.REGTYPE or type is None: + self.assertTrue(path.is_file()) + elif type == tarfile.DIRTYPE: + self.assertTrue(path.is_dir()) + elif type == tarfile.FIFOTYPE: + self.assertTrue(path.is_fifo()) ++ elif type == tarfile.SYMTYPE: ++ self.assertTrue(path.is_symlink()) + else: + raise NotImplementedError(type) + if size is not None: + self.assertEqual(path.stat().st_size, size) ++ if content is not None: ++ self.assertEqual(path.read_text(), content) + for parent in path.parents: + self.expected_paths.discard(parent) + ++ def expect_any_tree(self, name): ++ """Check a directory; forget about its contents.""" ++ tree_path = (self.destdir / name).resolve() ++ self.expect_file(tree_path, type=tarfile.DIRTYPE) ++ self.expected_paths = { ++ p for p in self.expected_paths ++ if tree_path not in p.parents ++ } ++ + def expect_exception(self, exc_type, message_re='.'): + with self.assertRaisesRegex(exc_type, message_re): + if self.raised_exception is not None: + raise self.raised_exception +- self.raised_exception = None ++ self.reraise_exception = False ++ return self.raised_exception + + def test_benign_file(self): + with ArchiveMaker() as arc: +@@ -3709,6 +3786,80 @@ def test_parent_symlink(self): + with self.check_context(arc.open(), 'data'): + self.expect_file('parent/evil') + ++ @symlink_test ++ @os_helper.skip_unless_symlink ++ def test_realpath_limit_attack(self): ++ # (CVE-2025-4517) ++ ++ with ArchiveMaker() as arc: ++ # populate the symlinks and dirs that expand in os.path.realpath() ++ # The component length is chosen so that in common cases, the unexpanded ++ # path fits in PATH_MAX, but it overflows when the final symlink ++ # is expanded ++ steps = "abcdefghijklmnop" ++ if sys.platform == 'win32': ++ component = 'd' * 25 ++ elif 'PC_PATH_MAX' in os.pathconf_names: ++ max_path_len = os.pathconf(self.outerdir.parent, "PC_PATH_MAX") ++ path_sep_len = 1 ++ dest_len = len(str(self.destdir)) + path_sep_len ++ component_len = (max_path_len - dest_len) // (len(steps) + path_sep_len) ++ component = 'd' * component_len ++ else: ++ raise NotImplementedError("Need to guess component length for {sys.platform}") ++ path = "" ++ step_path = "" ++ for i in steps: ++ arc.add(os.path.join(path, component), type=tarfile.DIRTYPE, ++ mode='drwxrwxrwx') ++ arc.add(os.path.join(path, i), symlink_to=component) ++ path = os.path.join(path, component) ++ step_path = os.path.join(step_path, i) ++ # create the final symlink that exceeds PATH_MAX and simply points ++ # to the top dir. ++ # this link will never be expanded by ++ # os.path.realpath(strict=False), nor anything after it. ++ linkpath = os.path.join(*steps, "l"*254) ++ parent_segments = [".."] * len(steps) ++ arc.add(linkpath, symlink_to=os.path.join(*parent_segments)) ++ # make a symlink outside to keep the tar command happy ++ arc.add("escape", symlink_to=os.path.join(linkpath, "..")) ++ # use the symlinks above, that are not checked, to create a hardlink ++ # to a file outside of the destination path ++ arc.add("flaglink", hardlink_to=os.path.join("escape", "flag")) ++ # now that we have the hardlink we can overwrite the file ++ arc.add("flaglink", content='overwrite') ++ # we can also create new files as well! ++ arc.add("escape/newfile", content='new') ++ ++ with (self.subTest('fully_trusted'), ++ self.check_context(arc.open(), filter='fully_trusted', ++ check_flag=False)): ++ if sys.platform == 'win32': ++ self.expect_exception((FileNotFoundError, FileExistsError)) ++ elif self.raised_exception: ++ # Cannot symlink/hardlink: tarfile falls back to getmember() ++ self.expect_exception(KeyError) ++ # Otherwise, this block should never enter. ++ else: ++ self.expect_any_tree(component) ++ self.expect_file('flaglink', content='overwrite') ++ self.expect_file('../newfile', content='new') ++ self.expect_file('escape', type=tarfile.SYMTYPE) ++ self.expect_file('a', symlink_to=component) ++ ++ for filter in 'tar', 'data': ++ with self.subTest(filter), self.check_context(arc.open(), filter=filter): ++ exc = self.expect_exception((OSError, KeyError)) ++ if isinstance(exc, OSError): ++ if sys.platform == 'win32': ++ # 3: ERROR_PATH_NOT_FOUND ++ # 5: ERROR_ACCESS_DENIED ++ # 206: ERROR_FILENAME_EXCED_RANGE ++ self.assertIn(exc.winerror, (3, 5, 206)) ++ else: ++ self.assertEqual(exc.errno, errno.ENAMETOOLONG) ++ + @symlink_test + def test_parent_symlink2(self): + # Test interplaying symlinks +@@ -3931,8 +4082,8 @@ def test_chains(self): + arc.add('symlink2', symlink_to=os.path.join( + 'linkdir', 'hardlink2')) + arc.add('targetdir/target', size=3) +- arc.add('linkdir/hardlink', hardlink_to='targetdir/target') +- arc.add('linkdir/hardlink2', hardlink_to='linkdir/symlink') ++ arc.add('linkdir/hardlink', hardlink_to=os.path.join('targetdir', 'target')) ++ arc.add('linkdir/hardlink2', hardlink_to=os.path.join('linkdir', 'symlink')) + + for filter in 'tar', 'data', 'fully_trusted': + with self.check_context(arc.open(), filter): +@@ -3948,6 +4099,129 @@ def test_chains(self): + self.expect_file('linkdir/symlink', size=3) + self.expect_file('symlink2', size=3) + ++ @symlink_test ++ def test_sneaky_hardlink_fallback(self): ++ # (CVE-2025-4330) ++ # Test that when hardlink extraction falls back to extracting members ++ # from the archive, the extracted member is (re-)filtered. ++ with ArchiveMaker() as arc: ++ # Create a directory structure so the c/escape symlink stays ++ # inside the path ++ arc.add("a/t/dummy") ++ # Create b/ directory ++ arc.add("b/") ++ # Point "c" to the bottom of the tree in "a" ++ arc.add("c", symlink_to=os.path.join("a", "t")) ++ # link to non-existant location under "a" ++ arc.add("c/escape", symlink_to=os.path.join("..", "..", ++ "link_here")) ++ # Move "c" to point to "b" ("c/escape" no longer exists) ++ arc.add("c", symlink_to="b") ++ # Attempt to create a hard link to "c/escape". Since it doesn't ++ # exist it will attempt to extract "cescape" but at "boom". ++ arc.add("boom", hardlink_to=os.path.join("c", "escape")) ++ ++ with self.check_context(arc.open(), 'data'): ++ if not os_helper.can_symlink(): ++ # When 'c/escape' is extracted, 'c' is a regular ++ # directory, and 'c/escape' *would* point outside ++ # the destination if symlinks were allowed. ++ self.expect_exception( ++ tarfile.LinkOutsideDestinationError) ++ elif sys.platform == "win32": ++ # On Windows, 'c/escape' points outside the destination ++ self.expect_exception(tarfile.LinkOutsideDestinationError) ++ else: ++ e = self.expect_exception( ++ tarfile.LinkFallbackError, ++ "link 'boom' would be extracted as a copy of " ++ + "'c/escape', which was rejected") ++ self.assertIsInstance(e.__cause__, ++ tarfile.LinkOutsideDestinationError) ++ for filter in 'tar', 'fully_trusted': ++ with self.subTest(filter), self.check_context(arc.open(), filter): ++ if not os_helper.can_symlink(): ++ self.expect_file("a/t/dummy") ++ self.expect_file("b/") ++ self.expect_file("c/") ++ else: ++ self.expect_file("a/t/dummy") ++ self.expect_file("b/") ++ self.expect_file("a/t/escape", symlink_to='../../link_here') ++ self.expect_file("boom", symlink_to='../../link_here') ++ self.expect_file("c", symlink_to='b') ++ ++ @symlink_test ++ def test_exfiltration_via_symlink(self): ++ # (CVE-2025-4138) ++ # Test changing symlinks that result in a symlink pointing outside ++ # the extraction directory, unless prevented by 'data' filter's ++ # normalization. ++ with ArchiveMaker() as arc: ++ arc.add("escape", symlink_to=os.path.join('link', 'link', '..', '..', 'link-here')) ++ arc.add("link", symlink_to='./') ++ ++ for filter in 'tar', 'data', 'fully_trusted': ++ with self.check_context(arc.open(), filter): ++ if os_helper.can_symlink(): ++ self.expect_file("link", symlink_to='./') ++ if filter == 'data': ++ self.expect_file("escape", symlink_to='link-here') ++ else: ++ self.expect_file("escape", ++ symlink_to='link/link/../../link-here') ++ else: ++ # Nothing is extracted. ++ pass ++ ++ @symlink_test ++ def test_chmod_outside_dir(self): ++ # (CVE-2024-12718) ++ # Test that members used for delayed updates of directory metadata ++ # are (re-)filtered. ++ with ArchiveMaker() as arc: ++ # "pwn" is a veeeery innocent symlink: ++ arc.add("a/pwn", symlink_to='.') ++ # But now "pwn" is also a directory, so it's scheduled to have its ++ # metadata updated later: ++ arc.add("a/pwn/", mode='drwxrwxrwx') ++ # Oops, "pwn" is not so innocent any more: ++ arc.add("a/pwn", symlink_to='x/../') ++ # Newly created symlink points to the dest dir, ++ # so it's OK for the "data" filter. ++ arc.add('a/x', symlink_to=('../')) ++ # But now "pwn" points outside the dest dir ++ ++ for filter in 'tar', 'data', 'fully_trusted': ++ with self.check_context(arc.open(), filter) as cc: ++ if not os_helper.can_symlink(): ++ self.expect_file("a/pwn/") ++ elif filter == 'data': ++ self.expect_file("a/x", symlink_to='../') ++ self.expect_file("a/pwn", symlink_to='.') ++ else: ++ self.expect_file("a/x", symlink_to='../') ++ self.expect_file("a/pwn", symlink_to='x/../') ++ if sys.platform != "win32": ++ st_mode = cc.outerdir.stat().st_mode ++ self.assertNotEqual(st_mode & 0o777, 0o777) ++ ++ def test_link_fallback_normalizes(self): ++ # Make sure hardlink fallbacks work for non-normalized paths for all ++ # filters ++ with ArchiveMaker() as arc: ++ arc.add("dir/") ++ arc.add("dir/../afile") ++ arc.add("link1", hardlink_to='dir/../afile') ++ arc.add("link2", hardlink_to='dir/../dir/../afile') ++ ++ for filter in 'tar', 'data', 'fully_trusted': ++ with self.check_context(arc.open(), filter) as cc: ++ self.expect_file("dir/") ++ self.expect_file("afile") ++ self.expect_file("link1") ++ self.expect_file("link2") ++ + def test_modes(self): + # Test how file modes are extracted + # (Note that the modes are ignored on platforms without working chmod) +@@ -4072,7 +4346,7 @@ def test_tar_filter(self): + # The 'tar' filter returns TarInfo objects with the same name/type. + # (It can also fail for particularly "evil" input, but we don't have + # that in the test archive.) +- with tarfile.TarFile.open(tarname) as tar: ++ with tarfile.TarFile.open(tarname, encoding="iso8859-1") as tar: + for tarinfo in tar.getmembers(): + try: + filtered = tarfile.tar_filter(tarinfo, '') +@@ -4084,7 +4358,7 @@ def test_tar_filter(self): + def test_data_filter(self): + # The 'data' filter either raises, or returns TarInfo with the same + # name/type. +- with tarfile.TarFile.open(tarname) as tar: ++ with tarfile.TarFile.open(tarname, encoding="iso8859-1") as tar: + for tarinfo in tar.getmembers(): + try: + filtered = tarfile.data_filter(tarinfo, '') +@@ -4242,13 +4516,13 @@ def valueerror_filter(tarinfo, path): + # If errorlevel is 0, errors affected by errorlevel are ignored + + with self.check_context(arc.open(errorlevel=0), extracterror_filter): +- self.expect_file('file') ++ pass + + with self.check_context(arc.open(errorlevel=0), filtererror_filter): +- self.expect_file('file') ++ pass + + with self.check_context(arc.open(errorlevel=0), oserror_filter): +- self.expect_file('file') ++ pass + + with self.check_context(arc.open(errorlevel=0), tarerror_filter): + self.expect_exception(tarfile.TarError) +@@ -4259,7 +4533,7 @@ def valueerror_filter(tarinfo, path): + # If 1, all fatal errors are raised + + with self.check_context(arc.open(errorlevel=1), extracterror_filter): +- self.expect_file('file') ++ pass + + with self.check_context(arc.open(errorlevel=1), filtererror_filter): + self.expect_exception(tarfile.FilterError) +diff --git a/Misc/NEWS.d/next/Security/2025-06-02-11-32-23.gh-issue-135034.RLGjbp.rst b/Misc/NEWS.d/next/Security/2025-06-02-11-32-23.gh-issue-135034.RLGjbp.rst +new file mode 100644 +index 00000000000000..08a0087e203671 +--- /dev/null ++++ b/Misc/NEWS.d/next/Security/2025-06-02-11-32-23.gh-issue-135034.RLGjbp.rst +@@ -0,0 +1,6 @@ ++Fixes multiple issues that allowed ``tarfile`` extraction filters ++(``filter="data"`` and ``filter="tar"``) to be bypassed using crafted ++symlinks and hard links. ++ ++Addresses :cve:`2024-12718`, :cve:`2025-4138`, :cve:`2025-4330`, and :cve:`2025-4517`. ++ diff --git a/pkgs/development/interpreters/python/cpython/default.nix b/pkgs/development/interpreters/python/cpython/default.nix index 03d0383402fd..4cf35d59ad74 100644 --- a/pkgs/development/interpreters/python/cpython/default.nix +++ b/pkgs/development/interpreters/python/cpython/default.nix @@ -349,6 +349,9 @@ stdenv.mkDerivation (finalAttrs: { ++ optionals (pythonAtLeast "3.13") [ ./3.13/virtualenv-permissions.patch ] + ++ optionals (pythonAtLeast "3.14") [ + ./3.14/CVE-2025-4517.patch + ] ++ optionals mimetypesSupport [ # Make the mimetypes module refer to the right file ./mimetypes.patch diff --git a/pkgs/development/python-modules/beautifulsoup4/default.nix b/pkgs/development/python-modules/beautifulsoup4/default.nix index 4ca473b72b5d..72dbe6e438da 100644 --- a/pkgs/development/python-modules/beautifulsoup4/default.nix +++ b/pkgs/development/python-modules/beautifulsoup4/default.nix @@ -52,7 +52,7 @@ buildPythonPackage rec { # backport test fix for behavior changes in libxml 2.14.3 (fetchpatch { url = "https://git.launchpad.net/beautifulsoup/patch/?id=53d328406ec8c37c0edbd00ace3782be63e2e7e5"; - excludes = ["CHANGELOG"]; + excludes = [ "CHANGELOG" ]; hash = "sha256-RtavbpnfT6x0A8L3tAvCXwKUpty1ASPGJKdks7evBr8="; }) ]; diff --git a/pkgs/development/python-modules/black/default.nix b/pkgs/development/python-modules/black/default.nix index 27f18cca5279..37c8e876fa62 100644 --- a/pkgs/development/python-modules/black/default.nix +++ b/pkgs/development/python-modules/black/default.nix @@ -95,6 +95,9 @@ buildPythonPackage rec { [ # requires network access "test_gen_check_output" + # broken on Python 3.13.4 + # FIXME: remove this when fixed upstream + "test_simple_format[pep_701]" ] ++ lib.optionals stdenv.hostPlatform.isDarwin [ # fails on darwin diff --git a/pkgs/development/python-modules/networkx/default.nix b/pkgs/development/python-modules/networkx/default.nix index eba7e30c2307..91ff6b59b531 100644 --- a/pkgs/development/python-modules/networkx/default.nix +++ b/pkgs/development/python-modules/networkx/default.nix @@ -2,6 +2,7 @@ lib, buildPythonPackage, fetchPypi, + fetchpatch, pythonOlder, # build-system @@ -38,6 +39,16 @@ buildPythonPackage rec { hash = "sha256-MHw2aUKMU2KqsnyKEmCqj0fE6R04kfSL4BQXONjQU+E="; }; + # backport patch to fix tests with Python 3.13.4 + # FIXME: remove in next update + patches = [ + (fetchpatch { + url = "https://github.com/networkx/networkx/commit/d85b04a8b9619580d8901f35400414f612c83113.patch"; + includes = [ "networkx/generators/lattice.py" ]; + hash = "sha256-6y/aJBDgNkUzmQ6o52CGVVzqoQgkCEXA4iAXhv1cS0c="; + }) + ]; + nativeBuildInputs = [ setuptools ]; optional-dependencies = { diff --git a/pkgs/development/python-modules/pyngrok/default.nix b/pkgs/development/python-modules/pyngrok/default.nix index 619d98e60636..5a6f3d32b490 100644 --- a/pkgs/development/python-modules/pyngrok/default.nix +++ b/pkgs/development/python-modules/pyngrok/default.nix @@ -9,14 +9,14 @@ buildPythonPackage rec { pname = "pyngrok"; - version = "7.2.9"; + version = "7.2.11"; pyproject = true; disabled = pythonOlder "3.8"; src = fetchPypi { inherit pname version; - hash = "sha256-Ixt/1Kel6CZajWFQ07o8gtnf/UCe2u23LE5isy3kT5E="; + hash = "sha256-n4iQOJvZWrwA7KE4Rsj0haDbZmfcBZh+uY/eNDrfslo="; }; build-system = [ diff --git a/pkgs/development/tools/minizinc/ide.nix b/pkgs/development/tools/minizinc/ide.nix index 13e26ff9d44b..53d8bcd9ce2e 100644 --- a/pkgs/development/tools/minizinc/ide.nix +++ b/pkgs/development/tools/minizinc/ide.nix @@ -7,6 +7,8 @@ qtwebsockets, minizinc, makeWrapper, + copyDesktopItems, + makeDesktopItem, }: let @@ -31,12 +33,30 @@ stdenv.mkDerivation rec { nativeBuildInputs = [ qmake makeWrapper + copyDesktopItems ]; buildInputs = [ qtbase qtwebsockets ]; + desktopItems = [ + (makeDesktopItem { + name = "minizinc"; + desktopName = "MiniZincIDE"; + icon = "minizinc"; + comment = meta.description; + exec = "MiniZincIDE"; + type = "Application"; + terminal = false; + categories = [ + "Science" + "Development" + "Education" + ]; + }) + ]; + sourceRoot = "${src.name}/MiniZincIDE"; dontWrapQtApps = true; diff --git a/pkgs/games/anki/addons/adjust-sound-volume/default.nix b/pkgs/games/anki/addons/adjust-sound-volume/default.nix new file mode 100644 index 000000000000..cdeb7d3d727a --- /dev/null +++ b/pkgs/games/anki/addons/adjust-sound-volume/default.nix @@ -0,0 +1,23 @@ +{ + lib, + anki-utils, + fetchFromGitHub, + nix-update-script, +}: +anki-utils.buildAnkiAddon (finalAttrs: { + pname = "adjust-sound-volume"; + version = "0.0.6"; + src = fetchFromGitHub { + owner = "mnogu"; + repo = "adjust-sound-volume"; + rev = "v${finalAttrs.version}"; + hash = "sha256-6reIUz+tHKd4KQpuofLa/tIL5lCloj3yODZ8Cz29jFU="; + }; + passthru.updateScript = nix-update-script { }; + meta = { + description = "Add a new menu item for adjusting the sound volume"; + homepage = "https://github.com/mnogu/adjust-sound-volume"; + license = lib.licenses.agpl3Plus; + maintainers = with lib.maintainers; [ junestepp ]; + }; +}) diff --git a/pkgs/games/anki/addons/anki-connect/default.nix b/pkgs/games/anki/addons/anki-connect/default.nix new file mode 100644 index 000000000000..27a0411e52f7 --- /dev/null +++ b/pkgs/games/anki/addons/anki-connect/default.nix @@ -0,0 +1,27 @@ +{ + lib, + anki-utils, + fetchFromSourcehut, + nix-update-script, +}: +anki-utils.buildAnkiAddon (finalAttrs: { + pname = "anki-connect"; + version = "24.7.25.0"; + src = fetchFromSourcehut { + owner = "~foosoft"; + repo = "anki-connect"; + rev = finalAttrs.version; + hash = "sha256-N98EoCE/Bx+9QUQVeU64FXHXSek7ASBVv1b9ltJ4G1U="; + }; + sourceRoot = "${finalAttrs.src.name}/plugin"; + passthru.updateScript = nix-update-script { }; + meta = { + description = '' + Enable external applications such as Yomichan to communicate + with Anki over a simple HTTP API + ''; + homepage = "https://foosoft.net/projects/anki-connect/"; + license = lib.licenses.gpl3Plus; + maintainers = with lib.maintainers; [ junestepp ]; + }; +}) diff --git a/pkgs/games/anki/addons/anki-utils.nix b/pkgs/games/anki/addons/anki-utils.nix new file mode 100644 index 000000000000..e2247ebac874 --- /dev/null +++ b/pkgs/games/anki/addons/anki-utils.nix @@ -0,0 +1,126 @@ +{ + lib, + stdenv, + symlinkJoin, + lndir, + formats, + runCommand, +}: +{ + buildAnkiAddon = lib.extendMkDerivation { + constructDrv = stdenv.mkDerivation; + extendDrvArgs = + finalAttrs: + { + pname, + version, + src, + sourceRoot ? "", + configurePhase ? '' + runHook preConfigure + runHook postConfigure + '', + buildPhase ? '' + runHook preBuild + runHook postBuild + '', + dontPatchELF ? true, + dontStrip ? true, + nativeBuildInputs ? [ ], + passthru ? { }, + meta ? { }, + # Script run after "user_files" folder is populated. + # Used when an add-on needs to process and change "user_files" based + # on what the user added to it. + processUserFiles ? "", + ... + }: + { + inherit + version + src + sourceRoot + configurePhase + buildPhase + dontPatchELF + dontStrip + nativeBuildInputs + ; + + pname = "anki-addon-${pname}"; + + installPrefix = "share/anki/addons/${pname}"; + + installPhase = '' + runHook preInstall + + mkdir -p "$out/$installPrefix" + find . -mindepth 1 -maxdepth 1 | xargs -d'\n' mv -t "$out/$installPrefix/" + + runHook postInstall + ''; + + passthru = { + withConfig = + { + # JSON add-on config. The available options for an add-on are in its + # config.json file. + # See https://addon-docs.ankiweb.net/addon-config.html#config-json + config ? { }, + # Path to a folder to be merged with the add-on "user_files" folder. + # See https://addon-docs.ankiweb.net/addon-config.html#user-files. + userFiles ? null, + }: + let + metaConfigFormat = formats.json { }; + addonMetaConfig = metaConfigFormat.generate "meta.json" { inherit config; }; + in + symlinkJoin { + pname = "${finalAttrs.pname}-with-config"; + inherit (finalAttrs) version meta; + + paths = [ + finalAttrs.finalPackage + ]; + + postBuild = '' + cd $out/${finalAttrs.installPrefix} + + rm -f meta.json + ln -s ${addonMetaConfig} meta.json + + mkdir -p user_files + ${ + if (userFiles != null) then + '' + ${lndir}/bin/lndir -silent "${userFiles}" user_files + '' + else + "" + } + + ${processUserFiles} + ''; + }; + } // passthru; + + meta = { + platforms = lib.platforms.all; + } // meta; + }; + }; + + buildAnkiAddonsDir = + addonPackages: + let + addonDirs = map (pkg: "${pkg}/share/anki/addons") addonPackages; + addons = lib.concatMapStringsSep " " (p: "${p}/*") addonDirs; + in + runCommand "anki-addons" { } '' + mkdir $out + [[ '${addons}' ]] || exit 0 + for addon in ${addons}; do + ln -s "$addon" $out/ + done + ''; +} diff --git a/pkgs/games/anki/addons/default.nix b/pkgs/games/anki/addons/default.nix new file mode 100644 index 000000000000..d602b1ea877e --- /dev/null +++ b/pkgs/games/anki/addons/default.nix @@ -0,0 +1,18 @@ +{ + callPackage, +}: +{ + adjust-sound-volume = callPackage ./adjust-sound-volume { }; + + anki-connect = callPackage ./anki-connect { }; + + local-audio-yomichan = callPackage ./local-audio-yomichan { }; + + passfail2 = callPackage ./passfail2 { }; + + recolor = callPackage ./recolor { }; + + reviewer-refocus-card = callPackage ./reviewer-refocus-card { }; + + yomichan-forvo-server = callPackage ./yomichan-forvo-server { }; +} diff --git a/pkgs/games/anki/addons/local-audio-yomichan/default.nix b/pkgs/games/anki/addons/local-audio-yomichan/default.nix new file mode 100644 index 000000000000..7ba361c8590c --- /dev/null +++ b/pkgs/games/anki/addons/local-audio-yomichan/default.nix @@ -0,0 +1,73 @@ +{ + lib, + anki-utils, + fetchFromGitHub, + python3, + nix-update-script, +}: +anki-utils.buildAnkiAddon (finalAttrs: { + pname = "local-audio-yomichan"; + version = "0-unstable-2025-04-26"; + src = fetchFromGitHub { + owner = "yomidevs"; + repo = "local-audio-yomichan"; + rev = "34750f1d8ca1cb473128fea7976a4d981e5e78a4"; + sparseCheckout = [ "plugin" ]; + hash = "sha256-2gyggcvxParay+1B7Sg2COKyocoxaRO1WTz+ymdRp4w="; + }; + sourceRoot = "${finalAttrs.src.name}/plugin"; + processUserFiles = '' + # Addon will try to load extra stuff unless Python package name is "plugin". + temp=$(mktemp -d) + ln -s $PWD $temp/plugin + # Addoon expects `user_files` dir at `$XDG_DATA_HOME/local-audio-yomichan` + ln -s $PWD/user_files $temp/local-audio-yomichan + + PYTHONPATH=$temp \ + WO_ANKI=1 \ + XDG_DATA_HOME=$temp \ + ${lib.getExe python3} -c \ + "from plugin import db_utils; \ + db_utils.init_db()" + ''; + passthru.updateScript = nix-update-script { + extraArgs = [ "--version=branch" ]; + }; + meta = { + description = "Run a local audio server for Yomitan"; + longDescription = '' + This add-on must be configured with an audio collection. + + Example: + + ```nix + pkgs.ankiAddons.local-audio-yomichan.withConfig { + userFiles = + let + audio-collection = + pkgs.runCommand "local-yomichan-audio-collection" + { + outputHashMode = "recursive"; + outputHash = "sha256-NxbcXh2SDPfCd+ZHAWT5JdxRecNbT4Xpo0pxX5/DOfo="; + + src = pkgs.requireFile { + name = "local-yomichan-audio-collection-2023-06-11-opus.tar.xz"; + url = "https://github.com/yomidevs/local-audio-yomichan?tab=readme-ov-file#steps"; + sha256 = "1xsxp8iggklv77rj972mqaa1i8f9hvr3ir0r2mwfqcdz4q120hr1"; + }; + } + ''' + mkdir -p $out + cd $out + tar -xf "$src" + '''; + in + "''${audio-collection}/user_files"; + } + ``` + ''; + homepage = "https://github.com/yomidevs/local-audio-yomichan"; + license = lib.licenses.mit; + maintainers = with lib.maintainers; [ junestepp ]; + }; +}) diff --git a/pkgs/games/anki/addons/passfail2/default.nix b/pkgs/games/anki/addons/passfail2/default.nix new file mode 100644 index 000000000000..73746c1a568a --- /dev/null +++ b/pkgs/games/anki/addons/passfail2/default.nix @@ -0,0 +1,34 @@ +{ + lib, + anki-utils, + fetchFromGitHub, + nix-update-script, +}: +anki-utils.buildAnkiAddon (finalAttrs: { + pname = "passfail2"; + version = "0.3.0-unstable-2024-10-17"; + src = fetchFromGitHub { + owner = "lambdadog"; + repo = "passfail2"; + rev = "d5313e4f1217e968b36edbc0a4fe92386209ffe6"; + hash = "sha256-HMe6/fHpYj/MN0dUFj3W71vK7qqcp9l1xm8SAiKkJLs="; + }; + buildPhase = '' + runHook preBuild + + substitute build_info.py.in build_info.py \ + --replace-fail '$version' '"${finalAttrs.version}"' + + runHook postBuild + ''; + passthru.updateScript = nix-update-script { }; + meta = { + description = '' + Replaces the default Anki review buttons with only two options: + “Fail” and “Pass” + ''; + homepage = "https://github.com/lambdadog/passfail2"; + license = lib.licenses.gpl3Plus; + maintainers = with lib.maintainers; [ junestepp ]; + }; +}) diff --git a/pkgs/games/anki/addons/recolor/default.nix b/pkgs/games/anki/addons/recolor/default.nix new file mode 100644 index 000000000000..3e60b0193904 --- /dev/null +++ b/pkgs/games/anki/addons/recolor/default.nix @@ -0,0 +1,27 @@ +{ + lib, + anki-utils, + fetchFromGitHub, + nix-update-script, +}: +anki-utils.buildAnkiAddon (finalAttrs: { + pname = "recolor"; + version = "3.1"; + src = fetchFromGitHub { + owner = "AnKing-VIP"; + repo = "AnkiRecolor"; + rev = finalAttrs.version; + sparseCheckout = [ "src/addon" ]; + hash = "sha256-28DJq2l9DP8O6OsbNQCZ0pm4S6CQ3Yz0Vfvlj+iQw8Y="; + }; + sourceRoot = "${finalAttrs.src.name}/src/addon"; + passthru.updateScript = nix-update-script { }; + meta = { + description = "ReColor your Anki desktop to whatever aesthetic you like"; + homepage = "https://github.com/AnKing-VIP/AnkiRecolor"; + # No license file, but it can be assumed to be AGPL3 based on + # https://ankiweb.net/account/terms. + license = lib.licenses.agpl3Only; + maintainers = with lib.maintainers; [ junestepp ]; + }; +}) diff --git a/pkgs/games/anki/addons/reviewer-refocus-card/default.nix b/pkgs/games/anki/addons/reviewer-refocus-card/default.nix new file mode 100644 index 000000000000..6fde24afd22e --- /dev/null +++ b/pkgs/games/anki/addons/reviewer-refocus-card/default.nix @@ -0,0 +1,30 @@ +{ + lib, + anki-utils, + fetchFromGitHub, + nix-update-script, +}: +anki-utils.buildAnkiAddon (finalAttrs: { + pname = "reviewer-refocus-card"; + version = "0-unstable-2022-12-24"; + src = fetchFromGitHub { + owner = "glutanimate"; + repo = "anki-addons-misc"; + rev = "7b981836e0a6637a1853f3e8d73d022ab95fed31"; + sparseCheckout = [ "src/reviewer_refocus_card" ]; + hash = "sha256-181hyc4ED+0lBzn1FnrBvNIYIUQF8xEDB3uHK6SkpHw="; + }; + sourceRoot = "${finalAttrs.src.name}/src/reviewer_refocus_card"; + passthru.updateScript = nix-update-script { + extraArgs = [ "--version=branch" ]; + }; + meta = { + description = '' + Set focus to the card area, allowing you to scroll through your cards using + Page Up / Page Down, etc + ''; + homepage = "https://github.com/glutanimate/anki-addons-misc"; + license = lib.licenses.agpl3Only; + maintainers = with lib.maintainers; [ junestepp ]; + }; +}) diff --git a/pkgs/games/anki/addons/yomichan-forvo-server/default.nix b/pkgs/games/anki/addons/yomichan-forvo-server/default.nix new file mode 100644 index 000000000000..751798f6e3a6 --- /dev/null +++ b/pkgs/games/anki/addons/yomichan-forvo-server/default.nix @@ -0,0 +1,25 @@ +{ + lib, + anki-utils, + fetchFromGitHub, + nix-update-script, +}: +anki-utils.buildAnkiAddon { + pname = "yomichan-forvo-server"; + version = "0-unstable-2024-10-21"; + src = fetchFromGitHub { + owner = "jamesnicolas"; + repo = "yomichan-forvo-server"; + rev = "364fc6d5d10969f516e0fa283460dfaf08c98e15"; + hash = "sha256-Jpee9hkXCiBmSW7hzJ1rAg45XVIiLC8WENc09+ySFVI="; + }; + passthru.updateScript = nix-update-script { + extraArgs = [ "--version=branch" ]; + }; + meta = { + description = "Audio server for yomichan that scrapes forvo for audio files"; + homepage = "https://github.com/jamesnicolas/yomichan-forvo-server"; + license = lib.licenses.unlicense; + maintainers = with lib.maintainers; [ junestepp ]; + }; +} diff --git a/pkgs/games/anki/default.nix b/pkgs/games/anki/default.nix index ab520dea5da0..15b88f67a7bc 100644 --- a/pkgs/games/anki/default.nix +++ b/pkgs/games/anki/default.nix @@ -10,6 +10,7 @@ lame, mpv-unwrapped, ninja, + callPackage, nixosTests, nodejs, jq, @@ -91,6 +92,8 @@ python3.pkgs.buildPythonApplication rec { ./patches/disable-auto-update.patch ./patches/remove-the-gl-library-workaround.patch ./patches/skip-formatting-python-code.patch + # Used in with-addons.nix + ./patches/allow-setting-addons-folder.patch ]; inherit cargoDeps; @@ -269,6 +272,7 @@ python3.pkgs.buildPythonApplication rec { ''; passthru = { + withAddons = ankiAddons: callPackage ./with-addons.nix { inherit ankiAddons; }; tests.anki-sync-server = nixosTests.anki-sync-server; }; @@ -292,6 +296,7 @@ python3.pkgs.buildPythonApplication rec { inherit (mesa.meta) platforms; maintainers = with maintainers; [ euank + junestepp oxij ]; # Reported to crash at launch on darwin (as of 2.1.65) diff --git a/pkgs/games/anki/patches/allow-setting-addons-folder.patch b/pkgs/games/anki/patches/allow-setting-addons-folder.patch new file mode 100644 index 000000000000..12f7846267d5 --- /dev/null +++ b/pkgs/games/anki/patches/allow-setting-addons-folder.patch @@ -0,0 +1,15 @@ +diff --git a/qt/aqt/profiles.py b/qt/aqt/profiles.py +index 469908c1b2..34612d6e08 100644 +--- a/qt/aqt/profiles.py ++++ b/qt/aqt/profiles.py +@@ -310,7 +310,9 @@ def profileFolder(self, create: bool = True) -> str: + return path + + def addonFolder(self) -> str: +- return self._ensureExists(os.path.join(self.base, "addons21")) ++ path = Path(os.environ.get("ANKI_ADDONS") or Path(self.base) / "addons21") ++ path.mkdir(parents=True, exist_ok=True) ++ return str(path.resolve()) + + def backupFolder(self) -> str: + return self._ensureExists(os.path.join(self.profileFolder(), "backups")) diff --git a/pkgs/games/anki/with-addons.nix b/pkgs/games/anki/with-addons.nix new file mode 100644 index 000000000000..d47c2c16f316 --- /dev/null +++ b/pkgs/games/anki/with-addons.nix @@ -0,0 +1,107 @@ +{ + lib, + symlinkJoin, + makeWrapper, + anki, + anki-utils, + writeTextDir, + ankiAddons ? [ ], +}: +/* + `ankiAddons` + : A set of Anki add-ons to be installed. Here's a an example: + + ~~~ + pkgs.anki.withAddons [ + # When the add-on is already available in nixpkgs + pkgs.ankiAddons.anki-connect + + # When the add-on is not available in nixpkgs + (pkgs.anki-utils.buildAnkiAddon (finalAttrs: { + pname = "recolor"; + version = "3.1"; + src = pkgs.fetchFromGitHub { + owner = "AnKing-VIP"; + repo = "AnkiRecolor"; + rev = finalAttrs.version; + sparseCheckout = [ "src/addon" ]; + hash = "sha256-28DJq2l9DP8O6OsbNQCZ0pm4S6CQ3Yz0Vfvlj+iQw8Y="; + }; + sourceRoot = "source/src/addon"; + })) + + # When the add-on needs to be configured + pkgs.ankiAddons.passfail2.withConfig { + config = { + again_button_name = "not quite"; + good_button_name = "excellent"; + }; + + user_files = ./dir-to-be-merged-into-addon-user-files-dir; + }; + ] + ~~~ + + The original `anki` executable will be wrapped so that it uses the addons from + `ankiAddons`. + + This only works with Anki versions patched to support the `ANKI_ADDONS` environment + variable. `pkgs.anki` has this, but `pkgs.anki-bin` does not. +*/ +let + defaultAddons = [ + (anki-utils.buildAnkiAddon { + pname = "nixos"; + version = "1.0"; + src = writeTextDir "__init__.py" '' + import aqt + from aqt.qt import QMessageBox + import json + + def addons_dialog_will_show(dialog: aqt.addons.AddonsDialog) -> None: + dialog.setEnabled(False) + QMessageBox.information( + dialog, + "NixOS Info", + ("These add-ons are managed by NixOS.
" + "See " + "github.com/NixOS/nixpkgs/tree/master/pkgs/games/anki/with-addons.nix") + ) + + def addon_tried_to_write_config(module: str, conf: dict) -> None: + message_box = QMessageBox( + QMessageBox.Icon.Warning, + "NixOS Info", + (f"The add-on module: \"{module}\" tried to update its config.
" + "See " + "github.com/NixOS/nixpkgs/tree/master/pkgs/games/anki/with-addons.nix" + " for how to configure add-ons managed by NixOS.") + ) + message_box.setDetailedText(json.dumps(conf)) + message_box.exec() + + aqt.gui_hooks.addons_dialog_will_show.append(addons_dialog_will_show) + aqt.mw.addonManager.writeConfig = addon_tried_to_write_config + ''; + meta.maintainers = with lib.maintainers; [ junestepp ]; + }) + ]; +in +symlinkJoin { + inherit (anki) version; + pname = "${anki.pname}-with-addons"; + + paths = [ anki ]; + + nativeBuildInputs = [ makeWrapper ]; + postBuild = '' + wrapProgram $out/bin/anki \ + --set ANKI_ADDONS "${anki-utils.buildAnkiAddonsDir (ankiAddons ++ defaultAddons)}" + ''; + + meta = builtins.removeAttrs anki.meta [ + "name" + "outputsToInstall" + "position" + ]; +} diff --git a/pkgs/os-specific/bsd/freebsd/pkgs/filterSource.nix b/pkgs/os-specific/bsd/freebsd/pkgs/filterSource.nix index 09674056d9cd..80dad06be8f8 100644 --- a/pkgs/os-specific/bsd/freebsd/pkgs/filterSource.nix +++ b/pkgs/os-specific/bsd/freebsd/pkgs/filterSource.nix @@ -21,12 +21,17 @@ in runCommand "${pname}-filtered-src" { nativeBuildInputs = [ - (pkgsBuildBuild.rsync.override { - enableZstd = false; - enableXXHash = false; - enableOpenSSL = false; - enableLZ4 = false; - }) + ( + (pkgsBuildBuild.rsync.override { + enableZstd = false; + enableXXHash = false; + enableOpenSSL = false; + enableLZ4 = false; + }).overrideAttrs + { + doCheck = false; + } + ) ]; } '' diff --git a/pkgs/os-specific/linux/prl-tools/default.nix b/pkgs/os-specific/linux/prl-tools/default.nix index 4e7b391f840a..ba2fe72e1277 100644 --- a/pkgs/os-specific/linux/prl-tools/default.nix +++ b/pkgs/os-specific/linux/prl-tools/default.nix @@ -43,13 +43,13 @@ let in stdenv.mkDerivation (finalAttrs: { pname = "prl-tools"; - version = "20.3.1-55959"; + version = "20.3.2-55975"; # We download the full distribution to extract prl-tools-lin.iso from # => ${dmg}/Parallels\ Desktop.app/Contents/Resources/Tools/prl-tools-lin.iso src = fetchurl { url = "https://download.parallels.com/desktop/v${lib.versions.major finalAttrs.version}/${finalAttrs.version}/ParallelsDesktop-${finalAttrs.version}.dmg"; - hash = "sha256-/J6NouI2htptG06Undeg1rUfbWUu6q/nV2P9D+sS7OA="; + hash = "sha256-eazDR+eSUcp81XdRfYRHIt7E4FNCEjsh0M0wYQQYmMQ="; }; hardeningDisable = [ diff --git a/pkgs/servers/sql/postgresql/ext/omnigres.nix b/pkgs/servers/sql/postgresql/ext/omnigres.nix index c7e967882413..c422de9becd9 100644 --- a/pkgs/servers/sql/postgresql/ext/omnigres.nix +++ b/pkgs/servers/sql/postgresql/ext/omnigres.nix @@ -12,6 +12,7 @@ postgresqlBuildExtension, postgresqlTestExtension, python3, + unstableGitUpdater, }: let @@ -19,13 +20,13 @@ let in postgresqlBuildExtension (finalAttrs: { pname = "omnigres"; - version = "0-unstable-2025-05-16"; + version = "0-unstable-2025-06-03"; src = fetchFromGitHub { owner = "omnigres"; repo = "omnigres"; - rev = "84f14792d80fb6fd60b680b7825245a8e7c5583e"; - hash = "sha256-jOlHXl7ANhMwOPizd5KH+wYZmBNNkkIa9jbXZR8Xu28="; + rev = "d347be5ae1d79645ac277d19080eacba7b229cf8"; + hash = "sha256-LKsH+aeLg7v2RfK80D3mgXdPB8jMIv5uFdf+3c5Z0vA="; }; strictDeps = true; @@ -75,6 +76,10 @@ postgresqlBuildExtension (finalAttrs: { ''; }; + passthru.updateScript = unstableGitUpdater { + hardcodeZeroVersion = true; + }; + meta = { description = "Postgres as a Business Operating System"; homepage = "https://docs.omnigres.org"; diff --git a/pkgs/stdenv/freebsd/default.nix b/pkgs/stdenv/freebsd/default.nix index b52c8ea37244..7485cf1ce6eb 100644 --- a/pkgs/stdenv/freebsd/default.nix +++ b/pkgs/stdenv/freebsd/default.nix @@ -230,6 +230,7 @@ let ]; }; iconv = linkBootstrap { paths = [ "bin/iconv" ]; }; + libiconv = linkBootstrap { paths = [ "include/iconv.h" ]; }; patch = linkBootstrap { paths = [ "bin/patch" ]; }; gnutar = linkBootstrap { paths = [ "bin/tar" ]; }; gawk = linkBootstrap { @@ -486,6 +487,7 @@ in diffutils findutils iconv + libiconv patch gnutar gawk @@ -501,6 +503,7 @@ in inherit lib; inherit (self) stdenvNoCC; inherit (prevStage) curl; + inherit (config) rewriteURL; }; gettext = super.gettext.overrideAttrs { NIX_CFLAGS_COMPILE = "-DHAVE_ICONV=1"; # we clearly have iconv. what do you want? diff --git a/pkgs/tools/graphics/mangohud/default.nix b/pkgs/tools/graphics/mangohud/default.nix index cba3c24f70d8..b565ce52b92c 100644 --- a/pkgs/tools/graphics/mangohud/default.nix +++ b/pkgs/tools/graphics/mangohud/default.nix @@ -10,6 +10,8 @@ gnused, xdg-utils, dbus, + libGL, + libX11, hwdata, mangohud32, addDriverRunpath, @@ -20,7 +22,6 @@ ninja, pkg-config, unzip, - libX11, wayland, libXNVCtrl, nlohmann_json, @@ -138,6 +139,8 @@ stdenv.mkDerivation (finalAttrs: { ]; libdbus = dbus.lib; + libGL = libGL; + libX11 = libX11; inherit hwdata; }) ]; diff --git a/pkgs/tools/graphics/mangohud/hardcode-dependencies.patch b/pkgs/tools/graphics/mangohud/hardcode-dependencies.patch index c19b2081c97f..14589826208d 100644 --- a/pkgs/tools/graphics/mangohud/hardcode-dependencies.patch +++ b/pkgs/tools/graphics/mangohud/hardcode-dependencies.patch @@ -11,8 +11,33 @@ index 7379af1..4eef3fe 100644 SPDLOG_ERROR("Could not load libdbus-1.so.3"); return false; } +diff --git a/src/loaders/loader_glx.cpp b/src/loaders/loader_glx.cpp +index aa453b8..0f6479d 100644 +--- a/src/loaders/loader_glx.cpp ++++ b/src/loaders/loader_glx.cpp +@@ -23,7 +23,7 @@ bool glx_loader::Load() { + handle = real_dlopen("glxtrace.so", RTLD_LAZY); + #endif + if (!handle) +- handle = real_dlopen("libGL.so.1", RTLD_LAZY); ++ handle = real_dlopen("@libGL@/lib/libGL.so.1", RTLD_LAZY); + if (!handle) { + SPDLOG_ERROR("Failed to open " MANGOHUD_ARCH " libGL.so.1: {}", dlerror()); + return false; +diff --git a/src/loaders/loader_x11.cpp b/src/loaders/loader_x11.cpp +index 214fd50..2f08857 100644 +--- a/src/loaders/loader_x11.cpp ++++ b/src/loaders/loader_x11.cpp +@@ -110,6 +110,6 @@ static std::shared_ptr loader; + std::shared_ptr get_libx11() + { + if (!loader) +- loader = std::make_shared("libX11.so.6"); ++ loader = std::make_shared("@libX11@/lib/libX11.so.6"); + return loader; + } diff --git a/src/logging.cpp b/src/logging.cpp -index ca986d4..c4d99ea 100644 +index 4ef7023..e24f6da 100644 --- a/src/logging.cpp +++ b/src/logging.cpp @@ -28,8 +28,12 @@ string exec(string command) { diff --git a/pkgs/top-level/aliases.nix b/pkgs/top-level/aliases.nix index 1d9d50ade96b..ffde927df90e 100644 --- a/pkgs/top-level/aliases.nix +++ b/pkgs/top-level/aliases.nix @@ -992,6 +992,7 @@ mapAliases { ledger_agent = ledger-agent; # Added 2024-01-07 lfs = dysk; # Added 2023-07-03 libAfterImage = throw "'libAfterImage' has been removed from nixpkgs, as it's no longer in development for a long time"; # Added 2024-06-01 + libast = throw "'libast' has been removed due to lack of maintenance upstream."; # Added 2025-06-09 libav = throw "libav has been removed as it was insecure and abandoned upstream for over half a decade; please use FFmpeg"; # Added 2024-08-25 libav_0_8 = libav; # Added 2024-08-25 libav_11 = libav; # Added 2024-08-25 @@ -1166,6 +1167,7 @@ mapAliases { lv_img_conv = throw "'lv_img_conv' has been removed from nixpkgs as it is broken"; # Added 2024-06-18 lxd = lib.warnOnInstantiate "lxd has been renamed to lxd-lts" lxd-lts; # Added 2024-04-01 lxd-unwrapped = lib.warnOnInstantiate "lxd-unwrapped has been renamed to lxd-unwrapped-lts" lxd-unwrapped-lts; # Added 2024-04-01 + lxdvdrip = throw "'lxdvdrip' has been removed due to lack of upstream maintenance."; # Added 2025-06-09 lzma = throw "'lzma' has been renamed to/replaced by 'xz'"; # Converted to throw 2024-10-17 lzwolf = throw "'lzwolf' has been removed because it's no longer maintained upstream. Consider using 'ecwolf'"; # Added 2025-03-02 diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index f5f0f1604603..f447973a3c8a 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -1310,8 +1310,6 @@ with pkgs; git-imerge = python3Packages.callPackage ../applications/version-management/git-imerge { }; - git-machete = python3Packages.callPackage ../applications/version-management/git-machete { }; - git-publish = python3Packages.callPackage ../applications/version-management/git-publish { }; git-recent = callPackage ../applications/version-management/git-recent { @@ -14837,6 +14835,8 @@ with pkgs; amoeba-data = callPackage ../games/amoeba/data.nix { }; anki = callPackage ../games/anki { }; + anki-utils = callPackage ../games/anki/addons/anki-utils.nix { }; + ankiAddons = recurseIntoAttrs (callPackage ../games/anki/addons { }); anki-bin = callPackage ../games/anki/bin.nix { }; anki-sync-server = callPackage ../games/anki/sync-server.nix { }; diff --git a/pkgs/top-level/python-aliases.nix b/pkgs/top-level/python-aliases.nix index e64605e3ee42..8f2c4b1453ea 100644 --- a/pkgs/top-level/python-aliases.nix +++ b/pkgs/top-level/python-aliases.nix @@ -166,7 +166,7 @@ mapAliases ({ cryptacular = throw "cryptacular was removed, because it was disabled on all python version since 3.6 and last updated in 2021"; # Added 2024-05-13 cryptography_vectors = "cryptography_vectors is no longer exposed in python*Packages because it is used for testing cryptography only."; # Added 2022-03-23 cufflinks = throw "cufflinks has removed, since it is abandoned and broken"; # added 2025-02-16 - curve25519-donna = throw "unused leaf package with dead upstream repository and no release in 10 years"; # added 2025-05-21 + curve25519-donna = throw "curve25519-donna was removed, since it is abandoned and unmaintained since 2015"; # added 2025-05-21 cx_Freeze = cx-freeze; # added 2023-08-02 cx_oracle = cx-oracle; # added 2024-01-03 d2to1 = throw "d2to1 is archived and no longer works with setuptools v68"; # added 2023-07-30