From 3d8829a3216dc99ffbdcc2eedc0402e4df4c0dd8 Mon Sep 17 00:00:00 2001 From: ibizaman Date: Sun, 21 Dec 2025 20:43:16 +0100 Subject: [PATCH 1/2] nixos/turborepo-remote-cache: init --- .../manual/release-notes/rl-2605.section.md | 2 + nixos/modules/module-list.nix | 1 + .../development/turborepo-remote-cache.nix | 131 ++++++++++++++++++ 3 files changed, 134 insertions(+) create mode 100644 nixos/modules/services/development/turborepo-remote-cache.nix diff --git a/nixos/doc/manual/release-notes/rl-2605.section.md b/nixos/doc/manual/release-notes/rl-2605.section.md index 8e4856ec0cd1..fdfe4e6ab97a 100644 --- a/nixos/doc/manual/release-notes/rl-2605.section.md +++ b/nixos/doc/manual/release-notes/rl-2605.section.md @@ -60,6 +60,8 @@ - [udp-over-tcp](https://github.com/mullvad/udp-over-tcp), a tunnel for proxying UDP traffic over a TCP stream. Available as `services.udp-over-tcp`. +- [turborepo-remote-cache](https://ducktors.github.io/turborepo-remote-cache/), an open-source implementation of the [Turborepo custom remote cache server](https://turbo.build/repo/docs/core-concepts/remote-caching#self-hosting). Available as [services.turborepo-remote-cache](options.html#opt-services.turborepo-remote-cache). + - [Komodo Periphery](https://github.com/moghtech/komodo), a multi-server Docker and Git deployment agent by Komodo. Available as [services.komodo-periphery](#opt-services.komodo-periphery.enable). - [Shoko](https://shokoanime.com), an anime management system. Available as [services.shoko](#opt-services.shoko.enable). diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix index fde4180a04a6..a4545314c585 100644 --- a/nixos/modules/module-list.nix +++ b/nixos/modules/module-list.nix @@ -609,6 +609,7 @@ ./services/development/lorri.nix ./services/development/nixseparatedebuginfod2.nix ./services/development/rstudio-server/default.nix + ./services/development/turborepo-remote-cache.nix ./services/development/vsmartcard-vpcd.nix ./services/development/zammad.nix ./services/display-managers/cosmic-greeter.nix diff --git a/nixos/modules/services/development/turborepo-remote-cache.nix b/nixos/modules/services/development/turborepo-remote-cache.nix new file mode 100644 index 000000000000..4b2ca2fdb13c --- /dev/null +++ b/nixos/modules/services/development/turborepo-remote-cache.nix @@ -0,0 +1,131 @@ +{ + config, + lib, + pkgs, + ... +}: +let + cfg = config.services.turborepo-remote-cache; + + inherit (lib) + boolToString + isBool + literalExpression + mapAttrs + mkEnableOption + mkIf + mkOption + mkPackageOption + types + ; +in +{ + options.services.turborepo-remote-cache = { + enable = mkEnableOption "Turborepo Remote Cache" // { + description = '' + Enables the daemon for `turborepo-remote-cache`, + an open source implementation of the Turborepo custom remote cache server. + ''; + }; + + package = mkPackageOption pkgs "turborepo-remote-cache" { }; + + environment = mkOption { + type = types.attrsOf ( + types.nullOr ( + types.oneOf [ + types.bool + types.int + types.str + ] + ) + ); + default = { }; + description = '' + Environment variables to set. + + Turborepo-remote-cache is configured through the use of environment variables. + The available configuration options can be found in the [documentation][envvars]. + + [envvars]: https://ducktors.github.io/turborepo-remote-cache/environment-variables.html + + Note that all environment variables set through this configuration + parameter will be readable by anyone with access to the host + machine. Therefore, sensitive information like {env}`TURBO_TOKEN` + should never be set using this configuration option, but should instead use + [](#opt-services.turborepo-remote-cache.environmentFile). + See the documentation for that option for more information. + + Any environment variables specified in the + [](#opt-services.turborepo-remote-cache.environmentFile) + will supersede environment variables specified in this option. + ''; + + example = literalExpression '' + { + NODE_ENV = "production"; + PORT = 8080; + } + ''; + }; + + environmentFile = mkOption { + type = types.nullOr types.path; + default = null; + description = '' + Additional environment file as defined in {manpage}`systemd.exec(5)`. + + Secrets like {env}`TURBO_TOKEN` may be passed to the + service without making them readable to everyone with access to + systemctl by using this configuration parameter. + + Note that this file needs to be available on the host on which + `turborepo-remote-cache` is running. + + See the [documentation][envvars] + and the [](#opt-services.turborepo-remote-cache.environment) configuration parameter + for further options. + + [envvars]: https://ducktors.github.io/turborepo-remote-cache/environment-variables.html + ''; + example = "/run/secrets/turborepo-remote-cache.env"; + }; + + openFirewall = mkOption { + type = types.bool; + default = false; + description = '' + Open ports in the firewall for turborepo-remote-cache daemon. + ''; + }; + }; + + config = mkIf cfg.enable { + systemd.services.turborepo-remote-cache = { + serviceConfig = { + Restart = "always"; + EnvironmentFile = cfg.environmentFile; + ExecStart = "${cfg.package}/bin/turborepo-remote-cache"; + + DynamicUser = true; + ProtectSystem = "strict"; + ProtectHome = true; + NoNewPrivileges = true; + PrivateDevices = true; + PrivateTmp = true; + }; + environment = mapAttrs ( + name: value: if isBool value then boolToString value else toString value + ) cfg.environment; + wantedBy = [ "default.target" ]; + }; + + networking.firewall.allowedTCPPorts = + let + # 3000 is the default port as specified in + # https://ducktors.github.io/turborepo-remote-cache/environment-variables.html + port = cfg.environment.PORT or 3000; + in + mkIf cfg.openFirewall [ port ]; + }; +} From bd3fc6d18645a6ce0be0d5acd60272ed1f83e5b9 Mon Sep 17 00:00:00 2001 From: ibizaman Date: Sun, 10 Aug 2025 22:49:51 +0200 Subject: [PATCH 2/2] nixosTests.turborepo-remote-cache: init --- nixos/tests/all-tests.nix | 1 + nixos/tests/turborepo-remote-cache.nix | 65 +++++++++++++++++++ .../tu/turborepo-remote-cache/package.nix | 6 +- 3 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 nixos/tests/turborepo-remote-cache.nix diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix index 109a14676734..8e21a71df7b4 100644 --- a/nixos/tests/all-tests.nix +++ b/nixos/tests/all-tests.nix @@ -1663,6 +1663,7 @@ in tuliprox = runTest ./tuliprox.nix; tuned = runTest ./tuned.nix; tuptime = runTest ./tuptime.nix; + turborepo-remote-cache = runTest ./turborepo-remote-cache.nix; turbovnc-headless-server = runTest ./turbovnc-headless-server.nix; turn-rs = runTest ./turn-rs.nix; tusd = runTest ./tusd/default.nix; diff --git a/nixos/tests/turborepo-remote-cache.nix b/nixos/tests/turborepo-remote-cache.nix new file mode 100644 index 000000000000..86cccd52c275 --- /dev/null +++ b/nixos/tests/turborepo-remote-cache.nix @@ -0,0 +1,65 @@ +{ lib, ... }: + +let + ports = { + turborepo-remote-cache = 8080; + }; + + token = "myrandomtoken"; + team = "nixos-team"; + # require('crypto').randomBytes(20).toString('hex'); + artifactid = "82286e93a6f84e18a8fe4770c5a88b24653e7f59"; +in +{ + name = "turborepo-remote-cache"; + + nodes.machine = { + services.turborepo-remote-cache = { + enable = true; + + environment = { + PORT = ports.turborepo-remote-cache; + TURBO_TEAM = team; + TURBO_TOKEN = token; + }; + }; + }; + + testScript = '' + import json + + machine.wait_for_unit("turborepo-remote-cache.service") + machine.wait_for_open_port(${toString ports.turborepo-remote-cache}) + + def read_json(input): + try: + return json.loads(input) + except Exception as e: + print(input) + raise e + + out = read_json(machine.succeed("""curl 127.0.0.1:${toString ports.turborepo-remote-cache}/v8/artifacts/status? \ + -H 'Content-Type: application/json' \ + -H "Authorization: Bearer ${token}" + """)) + + if out["status"] != "enabled": + raise Exception(f"status is not enabled in response: {out}") + + out = read_json(machine.succeed("""curl -X PUT "127.0.0.1:${toString ports.turborepo-remote-cache}/v8/artifacts/${artifactid}?teamId=${team}" \ + -H "Authorization: Bearer ${token}" \ + -H "Content-Type: application/octet-stream" \ + -d 'myartifact'""")) + if "urls" not in out: + raise Exception(f"'urls' not in response: {out}") + + out = machine.succeed("""curl 127.0.0.1:${toString ports.turborepo-remote-cache}/v8/artifacts/${artifactid}?teamId=${team} \ + -H 'Content-Type: application/json' \ + -H "Authorization: Bearer ${token}" + """) + if out != "myartifact": + raise Exception(f"reponse in not 'myartifact': {out}") + ''; + + meta.maintainers = [ lib.maintainers.ibizaman ]; +} diff --git a/pkgs/by-name/tu/turborepo-remote-cache/package.nix b/pkgs/by-name/tu/turborepo-remote-cache/package.nix index 9479a3b82fe6..0a151a358720 100644 --- a/pkgs/by-name/tu/turborepo-remote-cache/package.nix +++ b/pkgs/by-name/tu/turborepo-remote-cache/package.nix @@ -9,6 +9,7 @@ fetchPnpmDeps, makeWrapper, nix-update-script, + nixosTests, }: stdenvNoCC.mkDerivation (finalAttrs: { pname = "turborepo-remote-cache"; @@ -73,7 +74,10 @@ stdenvNoCC.mkDerivation (finalAttrs: { runHook postInstall ''; - passthru.updateScript = nix-update-script { }; + passthru = { + tests = { inherit (nixosTests) turborepo-remote-cache; }; + updateScript = nix-update-script { }; + }; meta = { homepage = "https://github.com/ducktors/turborepo-remote-cache";