hashctl 1.1a0
A commandline (CLI) utility written in Python that (i) records the changes done in a config file; (ii) automatically backups the config change config file; (iii) monitors the changes made in the config file.
Requires Python: >=3.7

# hashctl

`hashctl` is a Linux commandline (CLI) utility that assists its user in management of config files, specifically by (i) recording all the changes done in the config files; (ii) automatically backing up the config file after every changes done, as well as updating its recorded cryptographic hash; (iii) monitoring the config files in case there are changes done by comparing its cryptographic hash in `hashctl` database and the new cryptographic hash; (iv) intercepting commands for automatic backup.

# How does it works?

`hashctl` sums all the config file (`DEFAULT=["/etc","$HOME/.config"]`) in user's system using SHA256 and save all the hashes in `$HOME/.hashctl/hashes`. In every changes done in the said location of the config files (e.g. `edit /etc/fstab`), `hashctl` automatically hash it and saves the new has to `$HOME/.hashctl/hashes` and automatically backups the changed config file to `$HOME/.hashctl/backups`. Furthermore, `hashctl` also intercept and evaluates the commands of the user before execution (see [interceptor.md](https://github.com/iaacornus/hashctl/blob/main/interceptor.md))

# Installation

You can install using python-pip, you can install python-pip using your distribution's documentation.

    pip3 install hashctl

You can also build it yourself from the source:

    pip3 install setuptools wheel
    git clone https://github.com/iaacornus/hashctl
    cd hashctl/src
    python setup.py bdist_wheel
    cd dist && pip3 install hashctl-1.0-py3-none-any.whl

# How to use `hashctl`

Since the first release, new features had been implemented such as command interception, whereas `hashctl` intercepts and evaluate the commands based on the given rules (see [interception.md](https://github.com/iaacornus/hashctl/blob/main/interceptor.md)).

```
usage: hashctl [OPTIONS]

hashctl is a commandline (CLI) utility written in Python that backups and
records the changes done in config files.

optional arguments:
  -h, --help            show this help message and exit
  -u, --update          Update the backups and calculated cryptographic hash
                        of config files.
  -a ACTION [ACTION ...], --action ACTION [ACTION ...]
                        Record this change to the file.
  -d, --differences     Show every file changed.
  -c, --cleanup         Remove the unused files from database.
  -s, --setup           Setup hashctl and its services.
```

At first use, it is recommended to setup first `hashctl` and make your config file database by using:

    hashctl -s

It is recommended/necessary to restart the terminal emulator after setting up `hashctl` to ensure that the modified bash function is sourced. 

`hashctl` setup consists of 3 phases. First phase is the setup of `hashctl` database and backups, second phase is the setup of interceptor and the third is the setup of `systemd service` in which the interceptor depends on.

`hashctl` automatically set ups and activates its service upon the calling of `hashctl -s`, you can disable or mask it with `sudo systemctl mask/disable hashctl.service`.

By defaults, `hashctl` only backups and sums the config files in `/etc` and `$HOME/.config` and only monitors small list of commands namely, `nvim`, `vim`, `nano` and `vi`. But you can add more directories by editing `rules.py`:

    $HOME/.local/lib/python<replace with your python version>/site-packages/rules.py

    # rules.py
    ...

    class Rules():
        editRules = ["nano", "vim", "vi", "nvim"] # add all commands that should be monitored here
        confLOC = ["/etc", f"{homedir}/.config"] # add the directory here

**Examples**

(1.) `hashctl -a sudo nano /etc/fstab`. `hashctl` will save the changes done to `/etc/fstab` in its `changelog` located in `$HOME/.hashctl/changelog` which you can view with `hashctl -d`:

Before editing `/etc/fstab`. The calculated sum by `hashctl` saved is :

    cat $HOME/.hashctl/hashes | grep fstab
    >> 4437cb86e92adcb08f5689aa8479d83357cdb9b4e2a0ed027f02ec80ea9999b6  /etc/fstab

After edit : `hashctl -a sudo nano /etc/fstab`:

    cat $HOME/.hashctl/hashes | grep fstab
    >> c73cc0f0fb2c6103a83bd545f98c07deee7a2ad334aabfbf13aadf56921ec150  /etc/fstab

`hashctl -d` will then output the changes made by the user:

    hashctl -d
      [+] INFO 08/10/21 16:40:17 : sudo nano /etc/fstab
      --- /home/test/.hashctl/backups/fstab
      +++ nano
      @@ -12,3 +12,4 @@
       UUID=c9ddde68-26bc-45a9-975a-0bccbd7847c5 /                       btrfs   subvol=root,compress=zstd:1 0 0

       UUID=35f31376-4b53-488e-ba05-30e645993182 /boot                   ext4    defaults        1 2

       UUID=c9ddde68-26bc-45a9-975a-0bccbd7847c5 /home                   btrfs   subvol=home,compress=zstd:1 0 0

      +UUID="THIS IS SAMPLE"	/home/test/hashctl	defaults	ext4	0 0

The `changelog` by `hashctl` will record every changes done regardless of their frequency and amount. It serves as history that can only be altered by user or something such as system failure, if ever.

(2.) `hashctl -c`, this cleanup will remove all the unedit file and will only retain the recorded edited file. You can see the file edit records in `$HOME/.hashctl/.mostu`.

# Tricks

The interception feature of `hashctl` is flexible and can be edited by user with little code experience as other rules is yet to be implemented. By modifying `$HOME/.hashctl/service/check.py` you can add more rules in its interceptor.

    # ../check.py
    rules = ["-U", "update", "upgrade", "-S", "install", "-Syy", "groupinstall", "remove",
            "-R", "-Rns", "-Rs", "-Rsu", "-Rdd", "-Rn","-Rd", "-Rnd", "autoremove"]
    package_manager = ["pacman", "dnf", "apt", "dpkg", "yum", "zypper"] # add more package manager

    # it is also possible to add text editors
    text_editors = ["nano", "vim", "vi", ...]

    for x in sys.argv[1:]:
        if x in package_manager and rules and text_editors:
            command = f"hashctl -u"
            os.system(command)
        else:
            pass
    ...

Then reload the `systemd service` by `sudo systemctl restart hashctl.service` and relaunch the current terminal.

# Special Thanks

The interceptor feature is in debt to [rcaloras/bash-preexec](https://github.com/rcaloras/bash-preexec). Thank you for its owner and be sure to give it a star. It is very handy and useful utility.