I’m interested in getting finalAttrs pushed more across nixpkgs, especially in cases like this where it fixes potential AppImage double extraction (thanks @uku3lig !):
master ← guylamar2006:appimagetools-finalAttrs
opened 04:56PM - 16 Aug 26 UTC
This pull request refactors multiple Nix package definitions for AppImage-based … applications to use a more modern and consistent pattern with `appimageTools.wrapType2`. The main focus is on improving maintainability and reducing code duplication by eliminating manual extraction of AppImage contents and standardizing the use of `finalAttrs`. No functional changes to the packaged applications are intended.
Key changes by theme:
**Migration to `appimageTools.wrapType2` and `finalAttrs`:**
* Refactored all affected packages to use `appimageTools.wrapType2 (finalAttrs: { ... })` instead of the previous `let ... in appimageTools.wrapType2 { ... }` pattern, removing manual extraction (`appimageTools.extract`) and direct attribute passing (`inherit pname version src`). This brings consistency and leverages the more robust attribute set provided by `finalAttrs`. [[1]](diffhunk://#diff-2282dd71e981c4d8ae0b455d9bdbd3816522bf7efa32c79860d4bace0856d8deL8-R26) [[2]](diffhunk://#diff-d6d1bd9ca7e87b8babe91ad60c37cd353d380d2e21ebc2d85ed5608f2bf76cffL8-R22) [[3]](diffhunk://#diff-8b382cd33cd86e337c55a8ab0ecf102a14e0cf99dbf1be0ff7aab91bbb7aa3d4L6-R7) [[4]](diffhunk://#diff-df2afd4dcbc46f64747fee8685d575c98d88e6719baf9ea950495201f17b12cfL8-L36) [[5]](diffhunk://#diff-e625e8a8a3064459943959084e0c378e86360ba4188858bd11363722075c95fbL7-R19) [[6]](diffhunk://#diff-edf871fe4d22fd41fdbaed06517b94be110bb65ad84625ceebdd9846ee6e4dd7L3-R19) [[7]](diffhunk://#diff-4cf5a7e5eca240bfe78f85d47d40877b757501c91432963492613f77d4c974a6L7-R23) [[8]](diffhunk://#diff-b9c1ac2e1e9d721c230f6fa954afee6944a85739f8639cf869110867f4160bbaL7-R21) [[9]](diffhunk://#diff-c14b1b5fe0f4cf325df763f6000397571d6346b89ff9f96e07d6a46844613356L7-R26) [[10]](diffhunk://#diff-905beab46f6d65cf5754451b2d520dab9599596470e344e74a6d6540aa4641baL7-R22) [[11]](diffhunk://#diff-8f904ff150fac950788fc8bcc2c75c00d6e40729c22ab38a2ad6006f1cdd8af8R29-R48)
**Standardization of variable usage:**
* Updated all references to `version`, `pname`, and extracted contents to use `finalAttrs.version`, `finalAttrs.contents`, etc., ensuring that all dynamic attributes are consistently sourced from the final attribute set. [[1]](diffhunk://#diff-2282dd71e981c4d8ae0b455d9bdbd3816522bf7efa32c79860d4bace0856d8deL8-R26) [[2]](diffhunk://#diff-d6d1bd9ca7e87b8babe91ad60c37cd353d380d2e21ebc2d85ed5608f2bf76cffL8-R22) [[3]](diffhunk://#diff-8b382cd33cd86e337c55a8ab0ecf102a14e0cf99dbf1be0ff7aab91bbb7aa3d4L19-R25) [[4]](diffhunk://#diff-df2afd4dcbc46f64747fee8685d575c98d88e6719baf9ea950495201f17b12cfL53-R50) [[5]](diffhunk://#diff-e625e8a8a3064459943959084e0c378e86360ba4188858bd11363722075c95fbL7-R19) [[6]](diffhunk://#diff-edf871fe4d22fd41fdbaed06517b94be110bb65ad84625ceebdd9846ee6e4dd7L3-R19) [[7]](diffhunk://#diff-4cf5a7e5eca240bfe78f85d47d40877b757501c91432963492613f77d4c974a6L7-R23) [[8]](diffhunk://#diff-b9c1ac2e1e9d721c230f6fa954afee6944a85739f8639cf869110867f4160bbaL7-R21) [[9]](diffhunk://#diff-c14b1b5fe0f4cf325df763f6000397571d6346b89ff9f96e07d6a46844613356L7-R26) [[10]](diffhunk://#diff-905beab46f6d65cf5754451b2d520dab9599596470e344e74a6d6540aa4641baL7-R22) [[11]](diffhunk://#diff-8f904ff150fac950788fc8bcc2c75c00d6e40729c22ab38a2ad6006f1cdd8af8R29-R48)
**Simplification and modernization of desktop integration:**
* Replaced manual desktop item creation and icon installation to use the new pattern, updating paths and commands to use `finalAttrs.contents` and ensuring all substitutions and file operations are robust (e.g., using `--replace-fail`). [[1]](diffhunk://#diff-2282dd71e981c4d8ae0b455d9bdbd3816522bf7efa32c79860d4bace0856d8deL8-R26) [[2]](diffhunk://#diff-d6d1bd9ca7e87b8babe91ad60c37cd353d380d2e21ebc2d85ed5608f2bf76cffL8-R22) [[3]](diffhunk://#diff-8b382cd33cd86e337c55a8ab0ecf102a14e0cf99dbf1be0ff7aab91bbb7aa3d4L19-R25) [[4]](diffhunk://#diff-df2afd4dcbc46f64747fee8685d575c98d88e6719baf9ea950495201f17b12cfL53-R50) [[5]](diffhunk://#diff-e625e8a8a3064459943959084e0c378e86360ba4188858bd11363722075c95fbL7-R19) [[6]](diffhunk://#diff-edf871fe4d22fd41fdbaed06517b94be110bb65ad84625ceebdd9846ee6e4dd7L3-R19) [[7]](diffhunk://#diff-4cf5a7e5eca240bfe78f85d47d40877b757501c91432963492613f77d4c974a6L7-R23) [[8]](diffhunk://#diff-b9c1ac2e1e9d721c230f6fa954afee6944a85739f8639cf869110867f4160bbaL7-R21) [[9]](diffhunk://#diff-c14b1b5fe0f4cf325df763f6000397571d6346b89ff9f96e07d6a46844613356L7-R26) [[10]](diffhunk://#diff-905beab46f6d65cf5754451b2d520dab9599596470e344e74a6d6540aa4641baL7-R22) [[11]](diffhunk://#diff-8f904ff150fac950788fc8bcc2c75c00d6e40729c22ab38a2ad6006f1cdd8af8R29-R48)
**Meta attribute improvements:**
* Updated `meta.changelog` and similar attributes to use `finalAttrs.version` for dynamic versioning in URLs, improving maintainability. [[1]](diffhunk://#diff-d6d1bd9ca7e87b8babe91ad60c37cd353d380d2e21ebc2d85ed5608f2bf76cffL39-R37) [[2]](diffhunk://#diff-e625e8a8a3064459943959084e0c378e86360ba4188858bd11363722075c95fbL35-R37) [[3]](diffhunk://#diff-b9c1ac2e1e9d721c230f6fa954afee6944a85739f8639cf869110867f4160bbaL33-R38)
**Removal of redundant code and improved readability:**
* Eliminated unnecessary `let` bindings, intermediate variables, and redundant code blocks, leading to cleaner and more maintainable package definitions. [[1]](diffhunk://#diff-2282dd71e981c4d8ae0b455d9bdbd3816522bf7efa32c79860d4bace0856d8deL42-R37) [[2]](diffhunk://#diff-d6d1bd9ca7e87b8babe91ad60c37cd353d380d2e21ebc2d85ed5608f2bf76cffL39-R37) [[3]](diffhunk://#diff-8b382cd33cd86e337c55a8ab0ecf102a14e0cf99dbf1be0ff7aab91bbb7aa3d4L44-R39) [[4]](diffhunk://#diff-8b382cd33cd86e337c55a8ab0ecf102a14e0cf99dbf1be0ff7aab91bbb7aa3d4L59-R54) [[5]](diffhunk://#diff-df2afd4dcbc46f64747fee8685d575c98d88e6719baf9ea950495201f17b12cfL68-R61) [[6]](diffhunk://#diff-e625e8a8a3064459943959084e0c378e86360ba4188858bd11363722075c95fbL35-R37) [[7]](diffhunk://#diff-edf871fe4d22fd41fdbaed06517b94be110bb65ad84625ceebdd9846ee6e4dd7L36-R31) [[8]](diffhunk://#diff-4cf5a7e5eca240bfe78f85d47d40877b757501c91432963492613f77d4c974a6L39-R37) [[9]](diffhunk://#diff-b9c1ac2e1e9d721c230f6fa954afee6944a85739f8639cf869110867f4160bbaL33-R38) [[10]](diffhunk://#diff-c14b1b5fe0f4cf325df763f6000397571d6346b89ff9f96e07d6a46844613356L47-R40) [[11]](diffhunk://#diff-905beab46f6d65cf5754451b2d520dab9599596470e344e74a6d6540aa4641baL36-R33) [[12]](diffhunk://#diff-8f904ff150fac950788fc8bcc2c75c00d6e40729c22ab38a2ad6006f1cdd8af8R29-R48)
## Things done
- Built on platform:
- [x] x86_64-linux
- [ ] aarch64-linux
- [ ] aarch64-darwin
- Tested, as applicable:
- [ ] [NixOS tests] in [nixos/tests].
- [ ] [Package tests] at `passthru.tests`.
- [ ] Tests in [lib/tests] or [pkgs/test] for functions and "core" functionality.
- [x] Ran `nixpkgs-review` on this PR. See [nixpkgs-review usage].
- [x] Tested basic functionality of all binary files, usually in `./result/bin/`.
- Nixpkgs Release Notes
- [ ] Package update: when the change is major or breaking.
- NixOS Release Notes
- [ ] Module addition: when adding a new NixOS module.
- [ ] Module update: when the change is significant.
- [x] Fits [CONTRIBUTING.md], [pkgs/README.md], [maintainers/README.md] and other READMEs.
- [x] Follows the [automation/AI policy].
[NixOS tests]: https://nixos.org/manual/nixos/unstable/index.html#sec-nixos-tests
[Package tests]: https://github.com/NixOS/nixpkgs/blob/master/pkgs/README.md#package-tests
[nixpkgs-review usage]: https://github.com/Mic92/nixpkgs-review#usage
[CONTRIBUTING.md]: https://github.com/NixOS/nixpkgs/blob/master/CONTRIBUTING.md
[automation/AI policy]: https://github.com/NixOS/nixpkgs/blob/master/CONTRIBUTING.md#automationai-policy
[lib/tests]: https://github.com/NixOS/nixpkgs/blob/master/lib/tests
[maintainers/README.md]: https://github.com/NixOS/nixpkgs/blob/master/maintainers/README.md
[nixos/tests]: https://github.com/NixOS/nixpkgs/blob/master/nixos/tests
[pkgs/README.md]: https://github.com/NixOS/nixpkgs/blob/master/pkgs/README.md
[pkgs/test]: https://github.com/NixOS/nixpkgs/blob/master/pkgs/test
Unfortunately, it seems like bulk PRs like this aren’t a good way to go, and creating 1 LLM-asissted PR for each package is just more spammy.
It seems like a sprintable tracking issue is the way to go for this specific case, but I have a few questions about making one:
Is there any way to notify all maintainers of each package listed in a tracking issue?
Should we even notify all the maintainers of listed packages in tracking issues?
What about if you created an independently verifiable check that the new LLM-assisted finalAttrs output is the byte-for-byte identical (at least excluding embedded store paths) to the old appimageTools.extract pattern? Would a bulk PR/independent PR for each package be more likely to be accepted I was to do that?
Do we have any existing tools to help with this? I know that Lila recommends diffoscope for reviewing diffs, but do we have any existing tools to compare the outputs of 2 different derivations w/o self output path noise?
Maybe I should make small script like this that subs out the self store paths and then runs diffoscope.
1 Like
tree-sapii:
finalAttrs shound not change the closure at all iirc
Ah, there were some whitespace changes in the scripts for some of them (additional blank lines), which is why my agent-assisted diffoscope run of the PR I linked had some output path changes. I thought it might have some other changes, but apparently manual extract (usually, at least in the cases I checked) gets called with the same arguments that wrapType2 does, so this is mostly just an overriding/cleanliness change.
I made this a few months ago: jackr/finalAttrs-migrator: little project for funzies - Codeberg.org and landed this PR with it. Feel free to use it if it’s helpful
Be aware that it’s quite hard to land these PRs when they get large, since review is costly.
A more sophisticated approach would build an AST, as opposed to text replacement. I’ll be trying my hand at that in a few months probably.
1 Like
My rule of thumb with large scale migration is generally to avoid treewide when rebuild get involved or that the diff is not trivially predictible (with finalAttrs, I would have to look the entire package in cases there is a missing case). Thus I would personally split this one or keep up varous pr small. In any case, theses type of migration can be planned and spread on a long period to avoid spamming prs too much (this is also what was done with pyproject). We had good result with pinging maintainers on meta.changelog, but I think that it is preferable to not ask for modernization.
A good part of the finalAttrs is also happening within other type of cleanup or packages update. For instance with python “format” migration. I do like the approach of individual investigation, to take the time to do things properly, remove unwanted packages or catch problems (in the pyproject, we plenty of weird cases, even some unnoticed broken software…)
1 Like
Neat, although a bit different then the specific finalAttrs use-case I’m targeting with AppImage support.
I’ll go ahead and make a simple Tracking issue for all appimageTools-based packages not using finalAttrs, and clean them up and verify as I find the time. Just wanted to make sure I’m doing everything right here!
1 Like
Made a tracking issue here:
opened 10:05PM - 23 Oct 26 UTC
3.skill: sprintable
5.scope: tracking
After https://github.com/NixOS/nixpkgs/pull/498806, we can now use `finalAttrs.c… ontents` instead of manually invoking `appimageTools.extract`. This brings us the usual `finalAttrs` benefits, as well as reducing boilerplate when the extracted AppImage is needed.
## Packages
- [ ] `altair`
- [ ] `altus`
- [ ] `ankama-launcher`
- [ ] `apidog`
- [ ] `arduino-ide`
- [ ] `artisan`
- [ ] `artix-games-launcher`
- [ ] `betterdiscord-installer`
- [ ] `beyond-all-reason`
- [ ] `bloomrpc`
- [ ] `bootstrap-studio`
- [ ] `buckets`
- [ ] `buttercup-desktop`
- [ ] `cables`
- [ ] `caido-desktop`
- [ ] `capacities`
- [ ] `caprine-bin`
- [ ] `chatzone-desktop`
- [ ] `chrysalis`
- [ ] `cisco-packet-tracer_9`
- [ ] `clash-nyanpasu`
- [ ] `clickup`
- [ ] `clockify`
- [ ] `codux`
- [ ] `cozy-drive`
- [ ] `cubelify`
- [ ] `cura-appimage`
- [ ] `dbgate`
- [ ] `deskreen`
- [ ] `electron-mail`
- [ ] `everdo`
- [ ] `fiddler-everywhere`
- [ ] `firefly-desktop`
- [ ] `fluent-reader`
- [ ] `freelens-bin`
- [ ] `ftb-app`
- [ ] `gdevelop`
- [ ] `golden-cheetah-bin`
- [ ] `gsender`
- [ ] `hamrs`
- [ ] `hamrs-pro`
- [ ] `hamsket`
- [ ] `handheld-daemon-ui`
- [ ] `heynote`
- [ ] `hifile`
- [ ] `hoppscotch`
- [ ] `httpie-desktop`
- [ ] `hydralauncher`
- [ ] `insomnia`
- [ ] `irccloud`
- [ ] `jan`
- [ ] `jbrowse`
- [ ] `jet-pilot`
- [ ] `kchat`
- [ ] `keet`
- [ ] `kirimoto`
- [ ] `kmeet`
- [ ] `krunker`
- [ ] `labymod-launcher`
- [ ] `ledger-live-desktop`
- [ ] `lens`
- [ ] `listen1`
- [ ] `lmath`
- [ ] `lmstudio`
- [ ] `lmstudio-bionic`
- [ ] `losslesscut-bin`
- [ ] `lunar-client`
- [ ] `lunatask`
- [ ] `mathpix-snipping-tool`
- [ ] `mobilecoin-wallet`
- [ ] `mochi`
- [ ] `mockoon`
- [ ] `molotov`
- [ ] `motrix`
- [ ] `mqttx`
- [ ] `muffon`
- [ ] `mycrypto`
- [ ] `nosql-booster`
- [ ] `nosql-workbench`
- [ ] `notable`
- [ ] `notesnook`
- [ ] `notion-app-enhanced`
- [ ] `nrfconnect`
- [ ] `nrfconnect-bluetooth-low-energy`
- [ ] `nuclear`
- [ ] `p3x-onenote`
- [ ] `parsify`
- [ ] `pinokio`
- [ ] `plexamp`
- [ ] `protonup-qt`
- [ ] `pureref`
- [ ] `qidi-slicer-bin`
- [ ] `qidi-studio`
- [ ] `quba`
- [ ] `rambox`
- [ ] `raven-reader`
- [ ] `redact`
- [ ] `requestly`
- [ ] `saleae-logic-2`
- [ ] `simplenote`
- [ ] `simplex-chat-desktop`
- [ ] `snipaste`
- [ ] `sonixd`
- [ ] `sozi`
- [ ] `spinshare`
- [ ] `ssb-patchwork`
- [ ] `steam-art-manager`
- [ ] `steam-rom-manager`
- [ ] `sunsama`
- [ ] `timeular`
- [ ] `trezor-suite`
- [ ] `trucky`
- [ ] `tutanota-desktop`
- [ ] `upscayl`
- [ ] `via`
- [ ] `vial`
- [ ] `vita3k`
- [ ] `volanta`
- [ ] `wootility`
- [ ] `wowup-cf`
- [ ] `xlights`
- [ ] `zecwallet-lite`
- [ ] `zettlr`
- [ ] `zoho-mail-desktop`
pbsds
October 24, 2026, 4:10pm
8
I rarely see a benefit of using finalAttrs over rec or let ... in: it doesn’t make overriding src any easier.
The main pro i see is if you need to be able to react to overrides (e.g. in passthru.tests) or if you need access to finalAttrs.finalPackage. The con on the other hand is more expensive eval.
As such i don’t see the benefit or blindly migrating to it.
I’m just interested in this because appimageContents = appimageTools.extract { inherit pname version src; }; is a common pattern and IMO finalAttrs.contents is cleaner.
finalAttrs.contents also responds to src overrides, as opposed to appimageTools.extract in a let block.
1 Like
pbsds
October 24, 2026, 4:35pm
10
In this case yes, finalAttrs is strictly beneficial
1 Like
Update: after a few very annoying hours with my coding agent, I was able to get an ast-grep-based treewide for some trivial cases (Still needs me to do a manual test to make sure that I didn’t break any packages with this EDIT: 0 rebuilds! Although it did break nixpkgs-review ).
master ← bitbloxhub:push-ryoqwwkstvpv
opened 07:06PM - 24 Oct 26 UTC
Ran this AI-generated `ast-grep` rule with `ast-grep` 0.51.0:
```yaml
id: appi… mage-wraptype2-finalattrs
language: Nix
rule:
pattern:
context: |
let
pname = $PNAME;
version = $VERSION;
src = $SRC;
appimageContents = appimageTools.extract { inherit pname version src; };
in
appimageTools.wrapType2 $ATTRSET
selector: let_expression
# Exact AST shape rejects extra let bindings; comments remain match-significant.
strictness: ast
constraints:
ATTRSET:
# Keep only the common package inherit order.
all:
- kind: attrset_expression
- pattern: '{ inherit pname version src; $$$REST }'
transform:
# Resolve `src` in finalAttrs scope; rewrite references in package attrs.
FINAL_SRC:
rewrite:
source: $SRC
rewriters:
- finalattrs-pname
- finalattrs-version
- finalattrs-src
- finalattrs-contents
FINAL_ATTRSET:
rewrite:
source: $ATTRSET
rewriters:
- remove-package-inherit
- finalattrs-pname
- finalattrs-version
- finalattrs-src
- finalattrs-contents
FINAL_ATTRS_BODY:
substring:
source: $FINAL_ATTRSET
startChar: 1
endChar: -2
rewriters:
# Remove only the package identity inherit; add those attrs in the root fix.
- id: remove-package-inherit
rule:
pattern:
context: '{ inherit pname version src; }'
selector: inherit
fix: ''
# Rewrite variable references, not binding names or shell text.
# These anchored regexes constrain captured AST variable expressions, not attrset structure.
- id: finalattrs-pname
rule:
pattern: $REF
kind: variable_expression
constraints:
REF:
regex: '^pname$'
fix: finalAttrs.pname
- id: finalattrs-version
rule:
pattern: $REF
kind: variable_expression
constraints:
REF:
regex: '^version$'
fix: finalAttrs.version
- id: finalattrs-src
rule:
pattern: $REF
kind: variable_expression
constraints:
REF:
regex: '^src$'
fix: finalAttrs.src
- id: finalattrs-contents
rule:
pattern: $REF
kind: variable_expression
constraints:
REF:
regex: '^appimageContents$'
fix: finalAttrs.contents
fix: |-
appimageTools.wrapType2 (finalAttrs: {
pname = $PNAME;
version = $VERSION;
src = $FINAL_SRC;
$FINAL_ATTRS_BODY
})
```
(It took a REALLY LONG TIME to get this rule)
Then I ran `nix fmt`.
Part of https://github.com/NixOS/nixpkgs/issues/578068.
## Things done
- [x] Fits [CONTRIBUTING.md], [pkgs/README.md], [maintainers/README.md] and other READMEs.
- [x] Follows the [automation/AI policy].
- Built on platform:
- [ ] x86_64-linux
- [ ] aarch64-linux
- [ ] aarch64-darwin
- Tested, as applicable:
- [ ] Basic functionality of all binary files, usually in `./result/bin/`.
- [ ] Ran `nixpkgs-review` on this PR. See [nixpkgs-review usage].
- [ ] [Package tests] at `passthru.tests`.
- [ ] [NixOS tests] in [nixos/tests].
- [ ] Tests in [lib/tests] or [pkgs/test] for functions and "core" functionality.
- Nixpkgs Release Notes
- [ ] Package update: when the change is major or breaking.
- NixOS Release Notes
- [ ] Module addition: when adding a new NixOS module.
- [ ] Module update: when the change is significant.
[Package tests]: https://github.com/NixOS/nixpkgs/blob/master/pkgs/README.md#package-tests
[NixOS tests]: https://nixos.org/manual/nixos/unstable/index.html#sec-nixos-tests
[nixpkgs-review usage]: https://github.com/Mic92/nixpkgs-review#usage
[CONTRIBUTING.md]: https://github.com/NixOS/nixpkgs/blob/master/CONTRIBUTING.md
[automation/AI policy]: https://github.com/NixOS/nixpkgs/blob/master/CONTRIBUTING.md#automationai-policy
[lib/tests]: https://github.com/NixOS/nixpkgs/blob/master/lib/tests
[maintainers/README.md]: https://github.com/NixOS/nixpkgs/blob/master/maintainers/README.md
[nixos/tests]: https://github.com/NixOS/nixpkgs/blob/master/nixos/tests
[pkgs/README.md]: https://github.com/NixOS/nixpkgs/blob/master/pkgs/README.md
[pkgs/test]: https://github.com/NixOS/nixpkgs/blob/master/pkgs/test
For the rest, I think that I’ll just get my coding agent to fix them in batch with more autonomous self-verification, then do a manual review of their changes and submit them to nixpkgs as I have the time to do review.
1 Like
pbsds
October 24, 2026, 7:23pm
12
This prior art with treesitter might be of interest in the future GitHub - Aleksanaa/by-name-migrate · GitHub (it also has a neat open PR waiting to get merged)
1 Like
Neat, although not all cases are as clear-cut as these. I just thought I’d save tokens (given the amount of tokens it took to make that rule though, I’m not sure it succeeded) and time (definitely failed that one) by doing a simple AST migration for trivial cases, as most other cases are non-trivial (trivial by LLMs, but not by pure deterministic automation, at least not without building in 20 edge cases used by 1 package each).