Compare commits

...

2 commits

Author SHA1 Message Date
29fe7f76c1 expand the path to support working in systemd service
All checks were successful
buildbot/nix-eval Build done.
buildbot/nix-build Build done.
buildbot/nix-effects Build done.
2026-02-27 09:28:29 +01:00
ba34ac0d67 add nixos and darwin-nix modules 2026-02-27 09:27:34 +01:00
6 changed files with 156 additions and 15 deletions

View file

@ -4,6 +4,9 @@
Changes yet to be released are documented here. Changes yet to be released are documented here.
- Fix path expansion when running in systemd
- Add nixos, home-manager, and darwin-nix modules
## v0.1.0 ## v0.1.0
Initial release. Initial release.

View file

@ -6,6 +6,7 @@
let let
systems = [ systems = [
"x86_64-linux" "x86_64-linux"
"aarch64-linux"
"aarch64-darwin" "aarch64-darwin"
]; ];
forAllSystems = fn: nixpkgs.lib.genAttrs systems (system: fn nixpkgs.legacyPackages.${system}); forAllSystems = fn: nixpkgs.lib.genAttrs systems (system: fn nixpkgs.legacyPackages.${system});
@ -23,18 +24,24 @@
}; };
}); });
checks = forAllSystems (pkgs: { checks = forAllSystems (
tests = self.packages.${pkgs.stdenv.hostPlatform.system}.default.overrideAttrs (_: { pkgs:
pname = "nix-cache-login-tests"; {
checkPhase = '' tests = self.packages.${pkgs.stdenv.hostPlatform.system}.default.overrideAttrs (_: {
runHook preCheck pname = "nix-cache-login-tests";
go test ./... checkPhase = ''
runHook postCheck runHook preCheck
''; go test ./...
doCheck = true; runHook postCheck
}); '';
devShell = self.devShells.${pkgs.stdenv.hostPlatform.system}.default; doCheck = true;
}); });
devShell = self.devShells.${pkgs.stdenv.hostPlatform.system}.default;
}
// pkgs.lib.optionalAttrs pkgs.stdenv.isLinux {
nixos-module = pkgs.testers.runNixOSTest (import ./nixos-test.nix self);
}
);
devShells = forAllSystems (pkgs: { devShells = forAllSystems (pkgs: {
default = pkgs.mkShell { default = pkgs.mkShell {
@ -45,6 +52,23 @@
}; };
}); });
homeModules = {
# Workstation (Linux + macOS): home-manager module running `nix-cache-login refresh`
default =
{
config,
lib,
pkgs,
...
}:
{
imports = [ ./home-module.nix ];
services.nix-cache-login.package =
lib.mkDefault
self.packages.${pkgs.stdenv.hostPlatform.system}.default;
};
};
nixosModules = { nixosModules = {
# Workstation: systemd user timer+service running `nix-cache-login refresh` # Workstation: systemd user timer+service running `nix-cache-login refresh`
default = default =

62
home-module.nix Normal file
View file

@ -0,0 +1,62 @@
{
config,
lib,
...
}:
let
cfg = config.services.nix-cache-login;
in
{
options.services.nix-cache-login = {
enable = lib.mkEnableOption "nix-cache-login automatic token refresh";
package = lib.mkOption {
type = lib.types.package;
description = "The nix-cache-login package to use.";
};
refreshInterval = lib.mkOption {
type = lib.types.ints.positive;
default = 900;
description = ''
How often to attempt token refresh, in seconds.
If no valid session exists, the service logs an error and retries on
the next interval. Run {command}`nix-cache-login` to log in.
'';
example = 1800;
};
};
config = lib.mkIf cfg.enable {
home.packages = [ cfg.package ];
systemd.user.services.nix-cache-login = {
Unit.Description = "Nix cache login - refresh access token";
Service = {
Type = "oneshot";
ExecStart = "${cfg.package}/bin/nix-cache-login refresh";
};
};
systemd.user.timers.nix-cache-login = {
Unit.Description = "Nix cache login - periodic token refresh";
Timer = {
OnBootSec = "2min";
OnUnitActiveSec = "${toString cfg.refreshInterval}s";
};
Install.WantedBy = [ "timers.target" ];
};
launchd.agents.nix-cache-login = {
enable = true;
config = {
ProgramArguments = [
"${cfg.package}/bin/nix-cache-login"
"refresh"
];
StartInterval = cfg.refreshInterval;
RunAtLoad = true;
ProcessType = "Background";
StandardOutPath = "${config.home.homeDirectory}/Library/Logs/nix-cache-login.log";
StandardErrorPath = "${config.home.homeDirectory}/Library/Logs/nix-cache-login.log";
};
};
};
}

View file

@ -37,8 +37,8 @@ func Load(path string) (*Config, error) {
return nil, fmt.Errorf("parsing config file: %w", err) return nil, fmt.Errorf("parsing config file: %w", err)
} }
cfg.NetrcPath = os.ExpandEnv(cfg.NetrcPath) cfg.NetrcPath = expandPath(cfg.NetrcPath)
cfg.ClientSecretFile = os.ExpandEnv(cfg.ClientSecretFile) cfg.ClientSecretFile = expandPath(cfg.ClientSecretFile)
if cfg.ClientSecretFile != "" { if cfg.ClientSecretFile != "" {
secret, err := os.ReadFile(cfg.ClientSecretFile) secret, err := os.ReadFile(cfg.ClientSecretFile)
@ -71,6 +71,29 @@ func (c *Config) validate() error {
return nil return nil
} }
// expandPath expands environment variables in a path. XDG base directory
// variables are resolved using the xdg library, which applies the XDG spec
// fallbacks (e.g. $HOME/.config when $XDG_CONFIG_HOME is unset). This ensures
// correct behaviour in systemd user services, which do not set XDG variables.
func expandPath(s string) string {
return os.Expand(s, func(key string) string {
switch key {
case "XDG_CONFIG_HOME":
return xdg.ConfigHome
case "XDG_DATA_HOME":
return xdg.DataHome
case "XDG_CACHE_HOME":
return xdg.CacheHome
case "XDG_STATE_HOME":
return xdg.StateHome
case "XDG_RUNTIME_DIR":
return xdg.RuntimeDir
default:
return os.Getenv(key)
}
})
}
// RefreshTokenPath returns the path to the stored refresh token. // RefreshTokenPath returns the path to the stored refresh token.
func RefreshTokenPath() string { func RefreshTokenPath() string {
return filepath.Join(xdg.ConfigHome, "nix-cache-login", "refresh-token") return filepath.Join(xdg.ConfigHome, "nix-cache-login", "refresh-token")

29
nixos-test.nix Normal file
View file

@ -0,0 +1,29 @@
self: {
name = "nix-cache-login-nixos-module";
nodes.machine =
{ ... }:
{
imports = [ self.nixosModules.default ];
services.nix-cache-login.enable = true;
};
testScript = ''
machine.wait_for_unit("multi-user.target")
# The module should install timer and service unit files for all users
machine.succeed("test -f /etc/systemd/user/nix-cache-login.timer")
machine.succeed("test -f /etc/systemd/user/nix-cache-login.service")
# wantedBy = ["timers.target"] should create this symlink
machine.succeed(
"test -L /etc/systemd/user/timers.target.wants/nix-cache-login.timer"
)
# Service unit should reference the correct subcommand
unit = machine.succeed("cat /etc/systemd/user/nix-cache-login.service")
assert "nix-cache-login refresh" in unit, (
f"ExecStart not found in service unit:\n{unit}"
)
'';
}

View file

@ -6,7 +6,7 @@
buildGoModule { buildGoModule {
pname = "nix-cache-login"; pname = "nix-cache-login";
version = "0.1.1"; version = "0.1.2";
src = ./.; src = ./.;
# src = fetchgit { # src = fetchgit {
# url = "https://guardianproject.dev/ops/nix-cache-login.git"; # url = "https://guardianproject.dev/ops/nix-cache-login.git";