···76 peer-to-peer communication,
77- can be additionally configured with environment variables,
78- automatically determines whether `netbird-ui-<name>` should be available,
000007980[autoStart](#opt-services.netbird.clients._name_.autoStart) allows you to start the client (an actual systemd service)
81on demand, for example to connect to work-related or otherwise conflicting network only when required.
···76 peer-to-peer communication,
77- can be additionally configured with environment variables,
78- automatically determines whether `netbird-ui-<name>` should be available,
79+- does not enable [routing features](#opt-services.netbird.useRoutingFeatures) by default
80+ If you plan to use routing features, you must explicitly enable them. By enabling them, the service will
81+ configure the firewall and enable IP forwarding on the system.
82+ When set to `client` or `both`, reverse path filtering will be set to loose instead of strict.
83+ When set to `server` or `both`, IP forwarding will be enabled.
8485[autoStart](#opt-services.netbird.clients._name_.autoStart) allows you to start the client (an actual systemd service)
86on demand, for example to connect to work-related or otherwise conflicting network only when required.
+55-26
nixos/modules/services/networking/netbird.nix
···24 mkMerge
25 mkOption
26 mkOptionDefault
027 mkPackageOption
28 nameValuePair
29 optional
···83 type = bool;
84 default = false;
85 description = ''
86- Enables backwards compatible Netbird client service.
8788 This is strictly equivalent to:
89···112 };
113 ui.package = mkPackageOption pkgs "netbird-ui" { };
11400000000000000000115 clients = mkOption {
116 type = attrsOf (
117 submodule (
···125 type = port;
126 example = literalExpression "51820";
127 description = ''
128- Port the Netbird client listens on.
129 '';
130 };
131···146 default = null;
147 example = "127.0.0.123";
148 description = ''
149- An explicit address that Netbird will serve `*.netbird.cloud.` (usually) entries on.
150151- Netbird serves DNS on it's own (dynamic) client address by default.
152 '';
153 };
154···200 description = ''
201 Start the service with the system.
202203- As of 2024-02-13 it is not possible to start a Netbird client daemon without immediately
204 connecting to the network, but it is [planned for a near future](https://github.com/netbirdio/netbird/projects/2#card-91718018).
205 '';
206 };
···209 type = bool;
210 default = true;
211 description = ''
212- Opens up firewall `port` for communication between Netbird peers directly over LAN or public IP,
213 without using (internet-hosted) TURN servers as intermediaries.
214 '';
215 };
···247 "trace"
248 ];
249 default = "info";
250- description = "Log level of the Netbird daemon.";
251 };
252253 ui.enable = mkOption {
···255 default = nixosConfig.services.netbird.ui.enable;
256 defaultText = literalExpression ''client.ui.enable'';
257 description = ''
258- Controls presence of `netbird-ui` wrapper for this Netbird client.
259 '';
260 };
261···292 mkdir -p "$out/share/applications"
293 substitute ${cfg.ui.package}/share/applications/netbird.desktop \
294 "$out/share/applications/${mkBin "netbird"}.desktop" \
295- --replace-fail 'Name=Netbird' "Name=Netbird @ ${client.service.name}" \
296 --replace-fail '${lib.getExe cfg.ui.package}' "$out/bin/${mkBin "netbird-ui"}"
297 '')
298 ];
···348 type = path;
349 default = "/var/lib/${client.dir.baseName}";
350 description = ''
351- A state directory used by Netbird client to store `config.json`, `state.json` & `resolv.conf`.
352 '';
353 };
354 dir.runtime = mkOption {
355 type = path;
356 default = "/var/run/${client.dir.baseName}";
357 description = ''
358- A runtime directory used by Netbird client.
359 '';
360 };
361 service.name = mkOption {
···415 );
416 default = { };
417 description = ''
418- Attribute set of Netbird client daemons, by default each one will:
419420 1. be manageable using dedicated tooling:
421 - `netbird-<name>` script,
422- - `Netbird - netbird-<name>` graphical interface when appropriate (see `ui.enable`),
423 2. run as a `netbird-<name>.service`,
424 3. listen for incoming remote connections on the port `51820` (`openFirewall` by default),
425 4. manage the `netbird-<name>` wireguard interface,
···467 networking.dhcpcd.denyInterfaces = toClientList (client: client.interface);
468 networking.networkmanager.unmanaged = toClientList (client: "interface-name:${client.interface}");
469470- networking.firewall.allowedUDPPorts = concatLists (
471- toClientList (client: optional client.openFirewall client.port)
472- );
00473474- # Ports opened on a specific
475- networking.firewall.interfaces = listToAttrs (
476- toClientList (client: {
477- name = client.interface;
478- value.allowedUDPPorts = optionals client.openFirewall [
479- 5353 # required for the DNS forwarding/routing to work
480- ];
481- })
482- );
000000000483484 systemd.network.networks = mkIf config.networking.useNetworkd (
485 toClientAttrs (
···601 # see https://github.com/systemd/systemd/blob/17f3e91e8107b2b29fe25755651b230bbc81a514/src/resolve/org.freedesktop.resolve1.policy#L43-L43
602 # see all actions used at https://github.com/netbirdio/netbird/blob/13e7198046a0d73a9cd91bf8e063fafb3d41885c/client/internal/dns/systemd_linux.go#L29-L32
603 security.polkit.extraConfig = mkIf config.services.resolved.enable ''
604- // systemd-resolved access for Netbird clients
605 polkit.addRule(function(action, subject) {
606 var actions = [
607 "org.freedesktop.resolve1.revert",
···24 mkMerge
25 mkOption
26 mkOptionDefault
27+ mkOverride
28 mkPackageOption
29 nameValuePair
30 optional
···84 type = bool;
85 default = false;
86 description = ''
87+ Enables backward-compatible NetBird client service.
8889 This is strictly equivalent to:
90···113 };
114 ui.package = mkPackageOption pkgs "netbird-ui" { };
115116+ useRoutingFeatures = mkOption {
117+ type = enum [
118+ "none"
119+ "client"
120+ "server"
121+ "both"
122+ ];
123+ default = "none";
124+ example = "server";
125+ description = ''
126+ Enables settings required for NetBird's routing features: Network Resources, Network Routes & Exit Nodes.
127+128+ When set to `client` or `both`, reverse path filtering will be set to loose instead of strict.
129+ When set to `server` or `both`, IP forwarding will be enabled.
130+ '';
131+ };
132+133 clients = mkOption {
134 type = attrsOf (
135 submodule (
···143 type = port;
144 example = literalExpression "51820";
145 description = ''
146+ Port the NetBird client listens on.
147 '';
148 };
149···164 default = null;
165 example = "127.0.0.123";
166 description = ''
167+ An explicit address that NetBird will serve `*.netbird.cloud.` (usually) entries on.
168169+ NetBird serves DNS on it's own (dynamic) client address by default.
170 '';
171 };
172···218 description = ''
219 Start the service with the system.
220221+ As of 2024-02-13 it is not possible to start a NetBird client daemon without immediately
222 connecting to the network, but it is [planned for a near future](https://github.com/netbirdio/netbird/projects/2#card-91718018).
223 '';
224 };
···227 type = bool;
228 default = true;
229 description = ''
230+ Opens up firewall `port` for communication between NetBird peers directly over LAN or public IP,
231 without using (internet-hosted) TURN servers as intermediaries.
232 '';
233 };
···265 "trace"
266 ];
267 default = "info";
268+ description = "Log level of the NetBird daemon.";
269 };
270271 ui.enable = mkOption {
···273 default = nixosConfig.services.netbird.ui.enable;
274 defaultText = literalExpression ''client.ui.enable'';
275 description = ''
276+ Controls presence of `netbird-ui` wrapper for this NetBird client.
277 '';
278 };
279···310 mkdir -p "$out/share/applications"
311 substitute ${cfg.ui.package}/share/applications/netbird.desktop \
312 "$out/share/applications/${mkBin "netbird"}.desktop" \
313+ --replace-fail 'Name=NetBird' "Name=NetBird @ ${client.service.name}" \
314 --replace-fail '${lib.getExe cfg.ui.package}' "$out/bin/${mkBin "netbird-ui"}"
315 '')
316 ];
···366 type = path;
367 default = "/var/lib/${client.dir.baseName}";
368 description = ''
369+ A state directory used by NetBird client to store `config.json`, `state.json` & `resolv.conf`.
370 '';
371 };
372 dir.runtime = mkOption {
373 type = path;
374 default = "/var/run/${client.dir.baseName}";
375 description = ''
376+ A runtime directory used by NetBird client.
377 '';
378 };
379 service.name = mkOption {
···433 );
434 default = { };
435 description = ''
436+ Attribute set of NetBird client daemons, by default each one will:
437438 1. be manageable using dedicated tooling:
439 - `netbird-<name>` script,
440+ - `NetBird - netbird-<name>` graphical interface when appropriate (see `ui.enable`),
441 2. run as a `netbird-<name>.service`,
442 3. listen for incoming remote connections on the port `51820` (`openFirewall` by default),
443 4. manage the `netbird-<name>` wireguard interface,
···485 networking.dhcpcd.denyInterfaces = toClientList (client: client.interface);
486 networking.networkmanager.unmanaged = toClientList (client: "interface-name:${client.interface}");
487488+ # Required for the routing ("Exit node") feature(s) to work
489+ boot.kernel.sysctl = mkIf (cfg.useRoutingFeatures == "server" || cfg.useRoutingFeatures == "both") {
490+ "net.ipv4.conf.all.forwarding" = mkOverride 97 true;
491+ "net.ipv6.conf.all.forwarding" = mkOverride 97 true;
492+ };
493494+ networking.firewall = {
495+ allowedUDPPorts = concatLists (toClientList (client: optional client.openFirewall client.port));
496+497+ # Required for the routing ("Exit node") feature(s) to work
498+ checkReversePath = mkIf (
499+ cfg.useRoutingFeatures == "client" || cfg.useRoutingFeatures == "both"
500+ ) "loose";
501+502+ # Ports opened on a specific
503+ interfaces = listToAttrs (
504+ toClientList (client: {
505+ name = client.interface;
506+ value.allowedUDPPorts = optionals client.openFirewall [
507+ 5353 # required for the DNS forwarding/routing to work
508+ ];
509+ })
510+ );
511+ };
512513 systemd.network.networks = mkIf config.networking.useNetworkd (
514 toClientAttrs (
···630 # see https://github.com/systemd/systemd/blob/17f3e91e8107b2b29fe25755651b230bbc81a514/src/resolve/org.freedesktop.resolve1.policy#L43-L43
631 # see all actions used at https://github.com/netbirdio/netbird/blob/13e7198046a0d73a9cd91bf8e063fafb3d41885c/client/internal/dns/systemd_linux.go#L29-L32
632 security.polkit.extraConfig = mkIf config.services.resolved.enable ''
633+ // systemd-resolved access for NetBird clients
634 polkit.addRule(function(action, subject) {
635 var actions = [
636 "org.freedesktop.resolve1.revert",
+22-50
nixos/tests/lomiri.nix
···64 ];
65 }
66 ''
67- magick -size 640x480 canvas:white -pointsize 30 -fill black -annotate +100+100 '${wallpaperText}' $out
68- '';
69- # gsettings tool with access to wallpaper schema
70- lomiri-gsettings =
71- pkgs:
72- pkgs.stdenv.mkDerivation {
73- name = "lomiri-gsettings";
74- dontUnpack = true;
75- nativeBuildInputs = with pkgs; [
76- glib
77- wrapGAppsHook3
78- ];
79- buildInputs = with pkgs; [
80- # Not using the Lomiri-namespaced setting yet
81- # lomiri.lomiri-schemas
82- gsettings-desktop-schemas
83- ];
84- installPhase = ''
85- runHook preInstall
86- mkdir -p $out/bin
87- ln -s ${pkgs.lib.getExe' pkgs.glib "gsettings"} $out/bin/lomiri-gsettings
88- runHook postInstall
89 '';
90- };
91- setLomiriWallpaperService =
92- pkgs:
93- let
94- lomiriServices = [
95- "lomiri.service"
96- "lomiri-full-greeter.service"
97- "lomiri-full-shell.service"
98- "lomiri-greeter.service"
99- "lomiri-shell.service"
100- ];
101- in
102- rec {
103- description = "Set Lomiri wallpaper to something OCR-able";
104- wantedBy = lomiriServices;
105- before = lomiriServices;
106- serviceConfig = {
107- Type = "oneshot";
108- # Not using the Lomiri-namespaed settings yet
109- # ExecStart = "${lomiri-gsettings pkgs}/bin/lomiri-gsettings set com.lomiri.Shell background-picture-uri file://${wallpaperFile pkgs}";
110- ExecStart = "${lomiri-gsettings pkgs}/bin/lomiri-gsettings set org.gnome.desktop.background picture-uri file://${wallpaperFile pkgs}";
111 };
112 };
0113114 sharedTestFunctions = ''
115 def wait_for_text(text):
···412 })
413 ];
414 };
0000415416 # Help with OCR
417 systemd.tmpfiles.settings = {
418 "10-lomiri-test-setup" = terminalOcrTmpfilesSetup { inherit pkgs lib config; };
419 };
420-421- systemd.user.services.set-lomiri-wallpaper = setLomiriWallpaperService pkgs;
422 };
423424 enableOCR = true;
···562 ];
563 };
5640000565 # Help with OCR
566 systemd.tmpfiles.settings = {
567 "10-lomiri-test-setup" = terminalOcrTmpfilesSetup { inherit pkgs lib config; };
568 };
569-570- systemd.user.services.set-lomiri-wallpaper = setLomiriWallpaperService pkgs;
571 };
572573 enableOCR = true;
···710711 environment.etc."${wallpaperName}".source = wallpaperFile pkgs;
712713- systemd.user.services.set-lomiri-wallpaper = setLomiriWallpaperService pkgs;
00714715 # Help with OCR
716 systemd.tmpfiles.settings = {
···733 machine.wait_until_succeeds("pgrep -u lightdm -f 'lomiri --mode=greeter'")
734735 # Start page shows current time
736- wait_for_text(r"(AM|PM)")
0737 machine.screenshot("lomiri_greeter_launched")
738739 # Advance to login part
···747748 # Output rendering from Lomiri has started when it starts printing performance diagnostics
749 machine.wait_for_console_text("Last frame took")
0750 # Look for datetime's clock, one of the last elements to load
751 wait_for_text(r"(AM|PM)")
752 machine.screenshot("lomiri_launched")
···849 ocr = [ "Log Out" ];
850 extraCheck = ''
851 # We should be able to log out and return to the greeter
852- mouse_click(600, 280) # "Log Out"
853 mouse_click(340, 220) # confirm logout
854 machine.wait_until_fails("pgrep -u ${user} -f 'lomiri --mode=full-shell'")
855 '';
···64 ];
65 }
66 ''
67+ magick -size 640x480 canvas:black -pointsize 30 -fill white -annotate +100+100 '${wallpaperText}' $out
00000000000000000000068 '';
69+70+ lomiriWallpaperDconfSettings = pkgs: {
71+ settings = {
72+ "org/gnome/desktop/background" = {
73+ picture-uri = "file://${wallpaperFile pkgs}";
000000000000000074 };
75 };
76+ };
7778 sharedTestFunctions = ''
79 def wait_for_text(text):
···376 })
377 ];
378 };
379+380+ programs.dconf.profiles.user.databases = [
381+ (lomiriWallpaperDconfSettings pkgs)
382+ ];
383384 # Help with OCR
385 systemd.tmpfiles.settings = {
386 "10-lomiri-test-setup" = terminalOcrTmpfilesSetup { inherit pkgs lib config; };
387 };
00388 };
389390 enableOCR = true;
···528 ];
529 };
530531+ programs.dconf.profiles.user.databases = [
532+ (lomiriWallpaperDconfSettings pkgs)
533+ ];
534+535 # Help with OCR
536 systemd.tmpfiles.settings = {
537 "10-lomiri-test-setup" = terminalOcrTmpfilesSetup { inherit pkgs lib config; };
538 };
00539 };
540541 enableOCR = true;
···678679 environment.etc."${wallpaperName}".source = wallpaperFile pkgs;
680681+ programs.dconf.profiles.user.databases = [
682+ (lomiriWallpaperDconfSettings pkgs)
683+ ];
684685 # Help with OCR
686 systemd.tmpfiles.settings = {
···703 machine.wait_until_succeeds("pgrep -u lightdm -f 'lomiri --mode=greeter'")
704705 # Start page shows current time
706+ # And the greeter *actually* renders our wallpaper!
707+ wait_for_text(r"(AM|PM|Lorem|ipsum)")
708 machine.screenshot("lomiri_greeter_launched")
709710 # Advance to login part
···718719 # Output rendering from Lomiri has started when it starts printing performance diagnostics
720 machine.wait_for_console_text("Last frame took")
721+ # And the desktop doesn't render the wallpaper anymore. Grumble grumble...
722 # Look for datetime's clock, one of the last elements to load
723 wait_for_text(r"(AM|PM)")
724 machine.screenshot("lomiri_launched")
···821 ocr = [ "Log Out" ];
822 extraCheck = ''
823 # We should be able to log out and return to the greeter
824+ mouse_click(600, 250) # "Log Out"
825 mouse_click(340, 220) # confirm logout
826 machine.wait_until_fails("pgrep -u ${user} -f 'lomiri --mode=full-shell'")
827 '';
···33and the name of the extension you want to package as `extension`:
3435```sh
36-./query-extension-index.sh --cli-version=2.61.0 --extension=azure-devops --download
00037```
3839The output should look something like this:
···41```json
42{
43 "pname": "azure-devops",
44- "description": "Tools for managing Azure DevOps.",
45- "version": "1.0.1",
46- "url": "https://github.com/Azure/azure-devops-cli-extension/releases/download/20240514.1/azure_devops-1.0.1-py2.py3-none-any.whl",
47- "sha256": "f300d0288f017148514ebe6f5912aef10c7a6f29bdc0c916b922edf1d75bc7db",
48 "license": "MIT",
49- "requires": [
50- "distro (==1.3.0)",
51- "distro==1.3.0"
52 ]
53}
54```
···59{
60 azure-devops = mkAzExtension {
61 pname = "azure-devops";
62- version = "1.0.0";
63- url = "https://github.com/Azure/azure-devops-cli-extension/releases/download/20240206.1/azure_devops-${version}-py2.py3-none-any.whl";
64- sha256 = "658a2854d8c80f874f9382d421fa45abf6a38d00334737dda006f8dec64cf70a";
65 description = "Tools for managing Azure DevOps";
66 propagatedBuildInputs = with python3Packages; [ distro ];
67 meta.maintainers = with lib.maintainers; [ katexochen ];
···7172* The attribute name should be the same as `pname`.
73* Replace the version in `url` with `${version}`.
74-* The json output `requires` must be transformed into `propagetedBuildInputs`.
75* If `license` is `"MIT"`, it can be left out in the nix expression, as the builder defaults to that license.
76* Add yourself as maintainer in `meta.maintainers`.
77
···33and the name of the extension you want to package as `extension`:
3435```sh
36+nix run .#azure-cli.extension-tool -- \
37+ --cli-version=2.61.0 \
38+ --extension=azure-devops \
39+ --init
40```
4142The output should look something like this:
···44```json
45{
46 "pname": "azure-devops",
47+ "version": "1.0.2",
48+ "url": "https://github.com/Azure/azure-devops-cli-extension/releases/download/20250624.2/azure_devops-1.0.2-py2.py3-none-any.whl",
49+ "hash": "sha256-4rDeAqOnRRKMP26MJxG4u9vBuos6/SQIoVgfNbBpulk=",
50+ "description": "Tools for managing Azure DevOps",
51 "license": "MIT",
52+ "requirements": [
53+ "distro (>=1.6.0)"
054 ]
55}
56```
···61{
62 azure-devops = mkAzExtension {
63 pname = "azure-devops";
64+ version = "1.0.2";
65+ url = "https://github.com/Azure/azure-devops-cli-extension/releases/download/20250624.2/azure_devops-${version}-py2.py3-none-any.whl";
66+ hash = "sha256-4rDeAqOnRRKMP26MJxG4u9vBuos6/SQIoVgfNbBpulk=";
67 description = "Tools for managing Azure DevOps";
68 propagatedBuildInputs = with python3Packages; [ distro ];
69 meta.maintainers = with lib.maintainers; [ katexochen ];
···7374* The attribute name should be the same as `pname`.
75* Replace the version in `url` with `${version}`.
76+* The json output `requirements` must be transformed into package `requirements`.
77* If `license` is `"MIT"`, it can be left out in the nix expression, as the builder defaults to that license.
78* Add yourself as maintainer in `meta.maintainers`.
79
+55-4
pkgs/by-name/az/azure-cli/extensions-tool.py
···174 return max(versions, key=lambda e: parse(e["metadata"]["version"]), default=None)
175176177-def processExtension(
178 extVersions: dict,
179 cli_version: Version,
180 ext_name: Optional[str] = None,
181 requirements: bool = False,
182-) -> Optional[Ext]:
183 versions = filter(_filter_invalid, extVersions)
184 versions = filter(lambda v: _filter_compatible(v, cli_version), versions)
185 latest = _get_latest_version(versions)
···188 if ext_name and latest["metadata"]["name"] != ext_name:
189 return None
190 if not requirements and "run_requires" in latest["metadata"]:
000000000000191 return None
192193 return _transform_dict_to_obj(latest)
···335 action=argparse.BooleanOptionalAction,
336 help="whether to commit changes to git",
337 )
00000338 args = parser.parse_args()
339 cli_version = parse(args.cli_version)
340···348 assert index["formatVersion"] == "1" # only support formatVersion 1
349 extensions_remote = index["extensions"]
35000000000000000000000000000000000351 if args.extension:
352 logger.info(f"updating extension: {args.extension}")
353354 ext = Optional[Ext]
355 for _ext_name, extension in extensions_remote.items():
356- extension = processExtension(
357 extension, cli_version, args.extension, requirements=True
358 )
359 if extension:
···402403 extensions_remote_filtered = set()
404 for _ext_name, extension in extensions_remote.items():
405- extension = processExtension(extension, cli_version, args.extension)
00406 if extension:
407 extensions_remote_filtered.add(extension)
408
···174 return max(versions, key=lambda e: parse(e["metadata"]["version"]), default=None)
175176177+def find_extension_version(
178 extVersions: dict,
179 cli_version: Version,
180 ext_name: Optional[str] = None,
181 requirements: bool = False,
182+) -> Optional[Dict[str, Any]]:
183 versions = filter(_filter_invalid, extVersions)
184 versions = filter(lambda v: _filter_compatible(v, cli_version), versions)
185 latest = _get_latest_version(versions)
···188 if ext_name and latest["metadata"]["name"] != ext_name:
189 return None
190 if not requirements and "run_requires" in latest["metadata"]:
191+ return None
192+ return latest
193+194+195+def find_and_transform_extension_version(
196+ extVersions: dict,
197+ cli_version: Version,
198+ ext_name: Optional[str] = None,
199+ requirements: bool = False,
200+) -> Optional[Ext]:
201+ latest = find_extension_version(extVersions, cli_version, ext_name, requirements)
202+ if not latest:
203 return None
204205 return _transform_dict_to_obj(latest)
···347 action=argparse.BooleanOptionalAction,
348 help="whether to commit changes to git",
349 )
350+ parser.add_argument(
351+ "--init",
352+ action=argparse.BooleanOptionalAction,
353+ help="whether you want to init a new extension",
354+ )
355 args = parser.parse_args()
356 cli_version = parse(args.cli_version)
357···365 assert index["formatVersion"] == "1" # only support formatVersion 1
366 extensions_remote = index["extensions"]
367368+ # init just prints the json of the extension version that matches the cli version.
369+ if args.init:
370+ if not args.extension:
371+ logger.error("extension name is required for --init")
372+ exit(1)
373+374+ for ext_name, ext_versions in extensions_remote.items():
375+ if ext_name != args.extension:
376+ continue
377+ ext = find_extension_version(
378+ ext_versions,
379+ cli_version,
380+ args.extension,
381+ requirements=True,
382+ )
383+ break
384+ if not ext:
385+ logger.error(f"Extension {args.extension} not found in index")
386+ exit(1)
387+388+ ext_translated = {
389+ "pname": ext["metadata"]["name"],
390+ "version": ext["metadata"]["version"],
391+ "url": ext["downloadUrl"],
392+ "hash": _convert_hash_digest_from_hex_to_b64_sri(ext["sha256Digest"]),
393+ "description": ext["metadata"]["summary"].rstrip("."),
394+ "license": ext["metadata"]["license"],
395+ "requirements": ext["metadata"]["run_requires"][0]["requires"],
396+ }
397+ print(json.dumps(ext_translated, indent=2))
398+ return
399+400 if args.extension:
401 logger.info(f"updating extension: {args.extension}")
402403 ext = Optional[Ext]
404 for _ext_name, extension in extensions_remote.items():
405+ extension = find_and_transform_extension_version(
406 extension, cli_version, args.extension, requirements=True
407 )
408 if extension:
···451452 extensions_remote_filtered = set()
453 for _ext_name, extension in extensions_remote.items():
454+ extension = find_and_transform_extension_version(
455+ extension, cli_version, args.extension
456+ )
457 if extension:
458 extensions_remote_filtered.add(extension)
459
···2 lib,
3 stdenv,
4 fetchurl,
5- fetchpatch,
6 bison,
7 cmake,
8 pkg-config,
···3132stdenv.mkDerivation (finalAttrs: {
33 pname = "mysql";
34- version = "8.0.42";
3536 src = fetchurl {
37 url = "https://dev.mysql.com/get/Downloads/MySQL-${lib.versions.majorMinor finalAttrs.version}/mysql-${finalAttrs.version}.tar.gz";
38- hash = "sha256-XrIsIMILdlxYlMkBBIW9B9iptuv7YovP0wYHAXFVJv4=";
39 };
4041 nativeBuildInputs = [
···4849 patches = [
50 ./no-force-outline-atomics.patch # Do not force compilers to turn on -moutline-atomics switch
51- # Fix compilation with LLVM 19, adapted from https://github.com/mysql/mysql-server/commit/3a51d7fca76e02257f5c42b6a4fc0c5426bf0421
52- # in https://github.com/NixOS/nixpkgs/pull/374591#issuecomment-2615855076
53- ./libcpp-fixes.patch
54- (fetchpatch {
55- url = "https://github.com/mysql/mysql-server/commit/4a5c00d26f95faa986ffed7a15ee15e868e9dcf2.patch";
56- hash = "sha256-MEl1lQlDYtFjHk0+S02RQFnxMr+YeFxAyNjpDtVHyeE=";
57- })
58 ];
5960 ## NOTE: MySQL upstream frequently twiddles the invocations of libtool. When updating, you might proactively grep for libtool references.
···2 lib,
3 stdenv,
4 fetchurl,
05 bison,
6 cmake,
7 pkg-config,
···3031stdenv.mkDerivation (finalAttrs: {
32 pname = "mysql";
33+ version = "8.0.43";
3435 src = fetchurl {
36 url = "https://dev.mysql.com/get/Downloads/MySQL-${lib.versions.majorMinor finalAttrs.version}/mysql-${finalAttrs.version}.tar.gz";
37+ hash = "sha256-diUKgQFch49iUhz68w3/DqmyUJeNKx3/AHQIo5jV25M=";
38 };
3940 nativeBuildInputs = [
···4748 patches = [
49 ./no-force-outline-atomics.patch # Do not force compilers to turn on -moutline-atomics switch
000000050 ];
5152 ## NOTE: MySQL upstream frequently twiddles the invocations of libtool. When updating, you might proactively grep for libtool references.
-183
pkgs/servers/sql/mysql/libcpp-fixes.patch
···1-diff --git a/include/my_char_traits.h b/include/my_char_traits.h
2-new file mode 100644
3-index 00000000..6336bc03
4---- /dev/null
5-+++ b/include/my_char_traits.h
6-@@ -0,0 +1,65 @@
7-+/* Copyright (c) 2024, Oracle and/or its affiliates.
8-+
9-+ This program is free software; you can redistribute it and/or modify
10-+ it under the terms of the GNU General Public License, version 2.0,
11-+ as published by the Free Software Foundation.
12-+
13-+ This program is designed to work with certain software (including
14-+ but not limited to OpenSSL) that is licensed under separate terms,
15-+ as designated in a particular file or component or in included license
16-+ documentation. The authors of MySQL hereby grant you an additional
17-+ permission to link the program and your derivative works with the
18-+ separately licensed software that they have either included with
19-+ the program or referenced in the documentation.
20-+
21-+ This program is distributed in the hope that it will be useful,
22-+ but WITHOUT ANY WARRANTY; without even the implied warranty of
23-+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24-+ GNU General Public License, version 2.0, for more details.
25-+
26-+ You should have received a copy of the GNU General Public License
27-+ along with this program; if not, write to the Free Software
28-+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */
29-+
30-+#ifndef MY_CHAR_TRAITS_INCLUDED
31-+#define MY_CHAR_TRAITS_INCLUDED
32-+
33-+#include <cstring>
34-+
35-+template <class CharT>
36-+struct my_char_traits;
37-+
38-+/*
39-+ This is a standards-compliant, drop-in replacement for
40-+ std::char_traits<unsigned char>
41-+ We need this because clang libc++ is removing support for it in clang 19.
42-+ It is not a complete implementation. Rather we implement just enough to
43-+ compile any usage of char_traits<uchar> we have in our codebase.
44-+ */
45-+template <>
46-+struct my_char_traits<unsigned char> {
47-+ using char_type = unsigned char;
48-+ using int_type = unsigned int;
49-+
50-+ static void assign(char_type &c1, const char_type &c2) { c1 = c2; }
51-+
52-+ static char_type *assign(char_type *s, std::size_t n, char_type a) {
53-+ return static_cast<char_type *>(memset(s, a, n));
54-+ }
55-+
56-+ static int compare(const char_type *s1, const char_type *s2, std::size_t n) {
57-+ return memcmp(s1, s2, n);
58-+ }
59-+
60-+ static char_type *move(char_type *s1, const char_type *s2, std::size_t n) {
61-+ if (n == 0) return s1;
62-+ return static_cast<char_type *>(memmove(s1, s2, n));
63-+ }
64-+
65-+ static char_type *copy(char_type *s1, const char_type *s2, std::size_t n) {
66-+ if (n == 0) return s1;
67-+ return static_cast<char_type *>(memcpy(s1, s2, n));
68-+ }
69-+};
70-+
71-+#endif // MY_CHAR_TRAITS_INCLUDED
72-diff --git a/sql/mdl_context_backup.h b/sql/mdl_context_backup.h
73-index 89e7e23d..cf9c307e 100644
74---- a/sql/mdl_context_backup.h
75-+++ b/sql/mdl_context_backup.h
76-@@ -28,6 +28,7 @@
77- #include <map>
78- #include <memory>
79-80-+#include "my_char_traits.h"
81- #include "sql/malloc_allocator.h"
82- #include "sql/mdl.h"
83-84-@@ -47,7 +48,8 @@ class MDL_context_backup_manager {
85- /**
86- Key for uniquely identifying MDL_context in the MDL_context_backup map.
87- */
88-- typedef std::basic_string<uchar> MDL_context_backup_key;
89-+ using MDL_context_backup_key =
90-+ std::basic_string<uchar, my_char_traits<uchar>>;
91-92- class MDL_context_backup;
93-94-diff --git a/sql/stream_cipher.h b/sql/stream_cipher.h
95-index 606d4064..358fbb41 100644
96---- a/sql/stream_cipher.h
97-+++ b/sql/stream_cipher.h
98-@@ -28,6 +28,8 @@
99- #include <memory>
100- #include <string>
101-102-+#include "my_char_traits.h"
103-+
104- /**
105- @file stream_cipher.h
106-107-@@ -35,7 +37,8 @@
108- binary log files.
109- */
110-111--typedef std::basic_string<unsigned char> Key_string;
112-+using Key_string =
113-+ std::basic_string<unsigned char, my_char_traits<unsigned char>>;
114-115- /**
116- @class Stream_cipher
117-diff --git a/unittest/gunit/binlogevents/transaction_compression-t.cc b/unittest/gunit/binlogevents/transaction_compression-t.cc
118-index ba13f979..01af0e3a 100644
119---- a/unittest/gunit/binlogevents/transaction_compression-t.cc
120-+++ b/unittest/gunit/binlogevents/transaction_compression-t.cc
121-@@ -23,6 +23,7 @@
122- */
123-124- #include <array>
125-+#include <string>
126-127- #include <gtest/gtest.h>
128- #include "libbinlogevents/include/binary_log.h"
129-@@ -51,14 +52,13 @@ class TransactionPayloadCompressionTest : public ::testing::Test {
130- using Managed_buffer_t = Decompressor_t::Managed_buffer_t;
131- using Size_t = Decompressor_t::Size_t;
132- using Char_t = Decompressor_t::Char_t;
133-- using String_t = std::basic_string<Char_t>;
134- using Decompress_status_t =
135- binary_log::transaction::compression::Decompress_status;
136- using Compress_status_t =
137- binary_log::transaction::compression::Compress_status;
138-139-- static String_t constant_data(Size_t size) {
140-- return String_t(size, (Char_t)'a');
141-+ static std::string constant_data(Size_t size) {
142-+ return std::string(size, (Char_t)'a');
143- }
144-145- protected:
146-@@ -69,7 +69,7 @@ class TransactionPayloadCompressionTest : public ::testing::Test {
147- void TearDown() override {}
148-149- static void compression_idempotency_test(Compressor_t &c, Decompressor_t &d,
150-- String_t data) {
151-+ const std::string &data) {
152- auto debug_string = concat(
153- binary_log::transaction::compression::type_to_string(c.get_type_code()),
154- " ", data.size());
155-@@ -104,8 +104,8 @@ class TransactionPayloadCompressionTest : public ::testing::Test {
156-157- // Check decompressed data
158- ASSERT_EQ(managed_buffer.read_part().size(), data.size()) << debug_string;
159-- ASSERT_EQ(data, String_t(managed_buffer.read_part().begin(),
160-- managed_buffer.read_part().end()))
161-+ ASSERT_EQ(data, std::string(managed_buffer.read_part().begin(),
162-+ managed_buffer.read_part().end()))
163- << debug_string;
164-165- // Check that we reached EOF
166-@@ -118,7 +118,7 @@ TEST_F(TransactionPayloadCompressionTest, CompressDecompressZstdTest) {
167- for (auto size : buffer_sizes) {
168- binary_log::transaction::compression::Zstd_dec d;
169- binary_log::transaction::compression::Zstd_comp c;
170-- String_t data{TransactionPayloadCompressionTest::constant_data(size)};
171-+ std::string data{TransactionPayloadCompressionTest::constant_data(size)};
172- TransactionPayloadCompressionTest::compression_idempotency_test(c, d, data);
173- c.set_compression_level(22);
174- TransactionPayloadCompressionTest::compression_idempotency_test(c, d, data);
175-@@ -129,7 +129,7 @@ TEST_F(TransactionPayloadCompressionTest, CompressDecompressNoneTest) {
176- for (auto size : buffer_sizes) {
177- binary_log::transaction::compression::None_dec d;
178- binary_log::transaction::compression::None_comp c;
179-- String_t data{TransactionPayloadCompressionTest::constant_data(size)};
180-+ std::string data{TransactionPayloadCompressionTest::constant_data(size)};
181- TransactionPayloadCompressionTest::compression_idempotency_test(c, d, data);
182- }
183- }