diff --git a/CHANGELOG.md b/CHANGELOG.md index 4c0ba04..b728640 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,6 @@ Changes yet to be released are documented here. -- Fix path expansion when running in systemd -- Fix bug in netrc writing -- Add nixos, home-manager, and darwin-nix modules - ## v0.1.0 Initial release. diff --git a/flake.nix b/flake.nix index 72df013..e1c0ef9 100644 --- a/flake.nix +++ b/flake.nix @@ -6,7 +6,6 @@ let systems = [ "x86_64-linux" - "aarch64-linux" "aarch64-darwin" ]; forAllSystems = fn: nixpkgs.lib.genAttrs systems (system: fn nixpkgs.legacyPackages.${system}); @@ -24,24 +23,18 @@ }; }); - checks = forAllSystems ( - pkgs: - { - tests = self.packages.${pkgs.stdenv.hostPlatform.system}.default.overrideAttrs (_: { - pname = "nix-cache-login-tests"; - checkPhase = '' - runHook preCheck - go test ./... - runHook postCheck - ''; - 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); - } - ); + checks = forAllSystems (pkgs: { + tests = self.packages.${pkgs.stdenv.hostPlatform.system}.default.overrideAttrs (_: { + pname = "nix-cache-login-tests"; + checkPhase = '' + runHook preCheck + go test ./... + runHook postCheck + ''; + doCheck = true; + }); + devShell = self.devShells.${pkgs.stdenv.hostPlatform.system}.default; + }); devShells = forAllSystems (pkgs: { default = pkgs.mkShell { @@ -51,54 +44,5 @@ ]; }; }); - - 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 = { - # Workstation: systemd user timer+service running `nix-cache-login refresh` - default = - { - config, - lib, - pkgs, - ... - }: - { - imports = [ ./nixos-module.nix ]; - services.nix-cache-login.package = - lib.mkDefault - self.packages.${pkgs.stdenv.hostPlatform.system}.default; - }; - - # Server: system-level timer+service running `nix-cache-login service-account` - server = - { - config, - lib, - pkgs, - ... - }: - { - imports = [ ./nixos-module-server.nix ]; - services.nix-cache-login-server.package = - lib.mkDefault - self.packages.${pkgs.stdenv.hostPlatform.system}.default; - }; - }; }; } diff --git a/go.mod b/go.mod index 423298f..78ab0a1 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module guardianproject.dev/ops/nix-cache-login -go 1.25.0 +go 1.25.7 require ( github.com/adrg/xdg v0.5.3 diff --git a/home-module.nix b/home-module.nix deleted file mode 100644 index 572366e..0000000 --- a/home-module.nix +++ /dev/null @@ -1,63 +0,0 @@ -{ - 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 { - nix.settings.netrc-file = "${config.xdg.configHome}/nix/netrc"; - 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"; - }; - }; - }; -} diff --git a/internal/config/config.go b/internal/config/config.go index cc37736..77faaa1 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -37,8 +37,8 @@ func Load(path string) (*Config, error) { return nil, fmt.Errorf("parsing config file: %w", err) } - cfg.NetrcPath = expandPath(cfg.NetrcPath) - cfg.ClientSecretFile = expandPath(cfg.ClientSecretFile) + cfg.NetrcPath = os.ExpandEnv(cfg.NetrcPath) + cfg.ClientSecretFile = os.ExpandEnv(cfg.ClientSecretFile) if cfg.ClientSecretFile != "" { secret, err := os.ReadFile(cfg.ClientSecretFile) @@ -71,29 +71,6 @@ func (c *Config) validate() error { 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. func RefreshTokenPath() string { return filepath.Join(xdg.ConfigHome, "nix-cache-login", "refresh-token") diff --git a/internal/netrc/netrc.go b/internal/netrc/netrc.go index 6eaae73..f020a4d 100644 --- a/internal/netrc/netrc.go +++ b/internal/netrc/netrc.go @@ -25,7 +25,7 @@ func Upsert(path, machine, password string) error { } } if !found { - entries = append(entries, entry{machine: machine, login: "dummy", password: password}) + entries = append(entries, entry{machine: machine, password: password}) } return write(path, entries) @@ -71,7 +71,6 @@ func GetPassword(path, machine string) (string, error) { type entry struct { machine string - login string password string } @@ -103,10 +102,6 @@ func parse(path string) ([]entry, error) { entries = append(entries, *current) } current = &entry{machine: fields[1]} - case "login": - if current != nil { - current.login = fields[1] - } case "password": if current != nil { current.password = fields[1] @@ -134,7 +129,7 @@ func write(path string, entries []entry) error { if i > 0 { b.WriteString("\n") } - fmt.Fprintf(&b, "machine %s\nlogin %s\npassword %s\n", e.machine, e.login, e.password) + fmt.Fprintf(&b, "machine %s\npassword %s\n", e.machine, e.password) } if err := os.WriteFile(path, []byte(b.String()), 0600); err != nil { diff --git a/internal/netrc/netrc_test.go b/internal/netrc/netrc_test.go index e8a0b51..611c8c4 100644 --- a/internal/netrc/netrc_test.go +++ b/internal/netrc/netrc_test.go @@ -27,7 +27,7 @@ func TestUpsertUpdateExisting(t *testing.T) { dir := t.TempDir() path := filepath.Join(dir, "netrc") - initial := "machine other.host\nlogin dummy\npassword otherpass\n\nmachine cache.example.com\nlogin dummy\npassword oldtoken\n" + initial := "machine other.host\npassword otherpass\n\nmachine cache.example.com\npassword oldtoken\n" if err := os.WriteFile(path, []byte(initial), 0600); err != nil { t.Fatal(err) } @@ -59,7 +59,7 @@ func TestUpsertAppend(t *testing.T) { dir := t.TempDir() path := filepath.Join(dir, "netrc") - initial := "machine existing.host\nlogin dummy\npassword existingpass\n" + initial := "machine existing.host\npassword existingpass\n" if err := os.WriteFile(path, []byte(initial), 0600); err != nil { t.Fatal(err) } @@ -89,7 +89,7 @@ func TestRemove(t *testing.T) { dir := t.TempDir() path := filepath.Join(dir, "netrc") - initial := "machine keep.host\nlogin dummy\npassword keeppass\n\nmachine remove.host\nlogin dummy\npassword removepass\n" + initial := "machine keep.host\npassword keeppass\n\nmachine remove.host\npassword removepass\n" if err := os.WriteFile(path, []byte(initial), 0600); err != nil { t.Fatal(err) } @@ -141,7 +141,7 @@ func TestGetPasswordNotFound(t *testing.T) { dir := t.TempDir() path := filepath.Join(dir, "netrc") - content := "machine other.host\nlogin dummy\npassword otherpass\n" + content := "machine other.host\npassword otherpass\n" if err := os.WriteFile(path, []byte(content), 0600); err != nil { t.Fatal(err) } @@ -179,7 +179,7 @@ func TestFilePermissionsCorrected(t *testing.T) { path := filepath.Join(dir, "netrc") // Create file with overly permissive mode - if err := os.WriteFile(path, []byte("machine old.host\nlogin dummy\npassword oldpass\n"), 0644); err != nil { + if err := os.WriteFile(path, []byte("machine old.host\npassword oldpass\n"), 0644); err != nil { t.Fatal(err) } diff --git a/nixos-module-server.nix b/nixos-module-server.nix deleted file mode 100644 index 85300b9..0000000 --- a/nixos-module-server.nix +++ /dev/null @@ -1,50 +0,0 @@ -{ config, lib, ... }: -let - cfg = config.services.nix-cache-login-server; -in -{ - options.services.nix-cache-login-server = { - enable = lib.mkEnableOption "nix-cache-login service-account token refresh"; - package = lib.mkOption { - type = lib.types.package; - description = "The nix-cache-login package to use."; - }; - configFile = lib.mkOption { - type = lib.types.path; - description = '' - Path to the nix-cache-login config.toml file. Must include - client_secret_file pointing to a readable credentials file. - ''; - example = "/etc/nix-cache-login/config.toml"; - }; - refreshInterval = lib.mkOption { - type = lib.types.str; - default = "15min"; - description = '' - Interval between token refresh attempts, as a systemd time span. - On failure the service logs an error and the timer retries on schedule. - ''; - example = "1h"; - }; - }; - - config = lib.mkIf cfg.enable { - environment.systemPackages = [ cfg.package ]; - systemd.services.nix-cache-login = { - description = "Nix cache login - service account token refresh"; - serviceConfig = { - Type = "oneshot"; - ExecStart = "${cfg.package}/bin/nix-cache-login --config ${cfg.configFile} service-account"; - }; - }; - - systemd.timers.nix-cache-login = { - description = "Nix cache login - periodic service account token refresh"; - timerConfig = { - OnBootSec = "2min"; - OnUnitActiveSec = cfg.refreshInterval; - }; - wantedBy = [ "timers.target" ]; - }; - }; -} diff --git a/nixos-module.nix b/nixos-module.nix deleted file mode 100644 index d2ad4e7..0000000 --- a/nixos-module.nix +++ /dev/null @@ -1,43 +0,0 @@ -{ 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.str; - default = "15min"; - description = '' - Interval between token refresh attempts, as a systemd time span. - If no valid session exists, the service logs an error and the timer - retries on the next interval. Run nix-cache-login to log in. - ''; - example = "1h"; - }; - }; - - config = lib.mkIf cfg.enable { - environment.systemPackages = [ cfg.package ]; - systemd.user.services.nix-cache-login = { - description = "Nix cache login - refresh access token"; - serviceConfig = { - Type = "oneshot"; - ExecStart = "${cfg.package}/bin/nix-cache-login refresh"; - }; - }; - - systemd.user.timers.nix-cache-login = { - description = "Nix cache login - periodic token refresh"; - timerConfig = { - OnBootSec = "2min"; - OnUnitActiveSec = cfg.refreshInterval; - }; - wantedBy = [ "timers.target" ]; - }; - }; -} diff --git a/nixos-test.nix b/nixos-test.nix deleted file mode 100644 index 9c5840f..0000000 --- a/nixos-test.nix +++ /dev/null @@ -1,29 +0,0 @@ -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}" - ) - ''; -} diff --git a/package.nix b/package.nix index e85a434..c5344f1 100644 --- a/package.nix +++ b/package.nix @@ -6,7 +6,7 @@ buildGoModule { pname = "nix-cache-login"; - version = "0.1.2"; + version = "0.1.0"; src = ./.; # src = fetchgit { # url = "https://guardianproject.dev/ops/nix-cache-login.git";