Git-autocommit.nix, is this useful for anyone?

I made this little thing to monitor a git repo and autocommit (I use this for my obsidian vault, among other things), not sure if it’s useful, or if it belongs somewhere, figured I’d post here just in case.

{ pkgs, lib, config, ... }:
with lib;
let
  gitAutoCommit = pkgs.writeShellScriptBin "gitAutoCommit" ''
    if ! [[ $(git status) =~ "working tree clean" ]]; then
      ${pkgs.gitFull}/bin/git add .
      ${pkgs.gitFull}/bin/git commit -m "auto commit"

      ${pkgs.gitFull}/bin/git pull --rebase

      ${pkgs.gitFull}/bin/git push
    fi
  '';

  mkGitAutoCommitService = name: cfg:
    let
      gitCommand = lib.optionalString (cfg.sshKey != "") "GIT_SSH_COMMAND='ssh -i ${cfg.sshKey} -o IdentitiesOnly=yes' " + "git";
    in
    nameValuePair "git-autocommit-job-${name}" {
      description = "gitAutoCommit job ${name}";
      after = [ "network-online.target" ];
      requires = [ "network-online.target" ];
      path = with pkgs; [
        gitFull
        openssh
      ];
      script = ''
        cd ${cfg.path}
        if ! [[ $(git status) =~ "working tree clean" ]]; then
          git add .
          git commit -m "auto commit"

          ${gitCommand} pull --rebase

          ${gitCommand} push
        fi
      '';
      serviceConfig = {
        Type = "oneshot";
        User = cfg.user;
        Group = cfg.group;
        RemainAfterExit = "no";
      };
    };

  mkGitAutoCommitTimers = name: cfg:
    nameValuePair "git-autocommit-job-${name}" {
      description = "Git Autocommit job ${name} timer";
      wantedBy = [ "timers.target" ];
      timerConfig = {
        OnBootSec = "10s";
        OnUnitActiveSec = cfg.interval;
      };
    };

in
{
  options = {
    services.gitAutoCommit.repos = mkOption {
      default = { };
      type = types.attrsOf (types.submodule (
        name: {
          options = {
            path = mkOption {
              type = types.str;
              description = "path to top-level git repo dir";
            };
            interval = mkOption {
              type = with types; either str (listOf str);
              description = "systemd.time; OnUnitActiveSec setting";
            };
            user = mkOption {
              type = types.str;
              description = "user to run as";
            };
            group = mkOption {
              type = types.str;
              description = "group to run as";
            };
            sshKey = mkOption {
              type = types.str;
              description = "ssh key to use";
              default = "";
            };
          };
        }
      ));
    };
  };

  config = mkIf (config.services.gitAutoCommit.repos != { })
    (with config.services.gitAutoCommit; {

      systemd.services = mapAttrs' mkGitAutoCommitService repos;

      systemd.timers = mapAttrs' mkGitAutoCommitTimers repos;

      environment.systemPackages = [ gitAutoCommit ];
    });
}

2 Likes

Nice!

I occasionally use git “autocommit”, especially when doing changes to the code where I am not sure what the correct solution would be, and have to iterate many times, often doing undo/redo. The autocommit keeps track of all things I tried along the way, and I can go back a few steps without worry.

The way I do it is by simply running a watchexec, as I need this only briefly

watchexec -r -e rs,toml -- "git commit -a -m 'autocommit on change'"
1 Like

That is nice and clean!