From 287c1eacacefdffe5421772e6cf166e1ebf5cebf Mon Sep 17 00:00:00 2001 From: Wes Barnett Date: Sat, 13 Mar 2021 07:08:40 -0500 Subject: [PATCH 01/13] Add options for important=yes userdata Check if user-specified packages and user-specified commands are in important lists and if so add userdata important=yes Fixes #38. --- extra/snap-pac.ini | 8 ++++++++ scripts/snap_pac.py | 28 ++++++++++++++++++++++++---- tests/test_script.py | 33 ++++++++++++++++++++++++++++++--- 3 files changed, 62 insertions(+), 7 deletions(-) diff --git a/extra/snap-pac.ini b/extra/snap-pac.ini index 7e32a4b..92c9825 100644 --- a/extra/snap-pac.ini +++ b/extra/snap-pac.ini @@ -2,6 +2,7 @@ # Each section corresponds with a snapper configuration. Add additional sections to add # other configurations to be snapshotted. By default, only the root configuration is snapshotted. +# Create a setion named [DEFAULT] to have a setting apply for all snapper configurations [root] # How many characters to limit the description for snapper. @@ -19,6 +20,13 @@ cleanup_algorithm = number # Post snapshot description. Default is the list of packages involved in the pacman transaction #post_description = pacman post snapshot +# Uncomment to add "important=yes" to userdata for snapshots referring to these packages +#important_packages = ["linux"] + +# Uncomment to add "important=yes" to userdata for snapshots that were created with the +#following commands +#important_commands = ["pacman -Syu"] + # Example for another snapper configuration named "home" # [home] # snapshot = True diff --git a/scripts/snap_pac.py b/scripts/snap_pac.py index 6b86dbd..87bbd01 100755 --- a/scripts/snap_pac.py +++ b/scripts/snap_pac.py @@ -18,6 +18,7 @@ from argparse import ArgumentParser from configparser import ConfigParser +import json import logging from pathlib import Path import os @@ -30,7 +31,8 @@ logging.basicConfig(format="%(message)s", level=logging.INFO) class SnapperCmd: - def __init__(self, config, snapshot_type, cleanup_algorithm, description="", nodbus=False, pre_number=None): + def __init__(self, config, snapshot_type, cleanup_algorithm, description="", + nodbus=False, pre_number=None, important=False): self.cmd = ["snapper"] if nodbus: self.cmd.append("--no-dbus") @@ -40,6 +42,8 @@ class SnapperCmd: self.cmd.append("--print-number") if description: self.cmd.append(f"--description \"{description}\"") + if important: + self.cmd.append("--userdata \"important=yes\"") if snapshot_type == "post": if pre_number is not None: self.cmd.append(f"--pre-number {pre_number}") @@ -69,7 +73,7 @@ def setup_config_parser(ini_file, parent_cmd, packages): "snapshot": False, "cleanup_algorithm": "number", "pre_description": parent_cmd, - "post_description": packages, + "post_description": " ".join(packages), "desc_limit": 72 } config["root"] = { @@ -100,13 +104,25 @@ def get_pre_number(snapshot_type, prefile): return pre_number +def check_important_commands(config, snapper_config, parent_cmd): + important_commands = json.loads(config.get(snapper_config, "important_commands")) + return parent_cmd in important_commands + + +def check_important_packages(config, snapper_config, packages): + important_packages = json.loads(config.get(snapper_config, "important_packages")) + print(important_packages) + print(packages) + return any(x in important_packages for x in packages) + + def main(snap_pac_ini, snapper_conf_file, args): if os.getenv("SNAP_PAC_SKIP", "n").lower() in ["y", "yes", "true", "1"]: return False parent_cmd = os.popen(f"ps -p {os.getppid()} -o args=").read().strip() - packages = " ".join([line.rstrip("\n") for line in sys.stdin]) + packages = [line.rstrip("\n") for line in sys.stdin] config = setup_config_parser(snap_pac_ini, parent_cmd, packages) snapper_configs = get_snapper_configs(snapper_conf_file) chroot = os.stat("/") != os.stat("/proc/1/root/.") @@ -123,7 +139,11 @@ def main(snap_pac_ini, snapper_conf_file, args): description = get_description(args.type, config, snapper_config) pre_number = get_pre_number(args.type, prefile) - snapper_cmd = SnapperCmd(snapper_config, args.type, cleanup_algorithm, description, chroot, pre_number) + important = (check_important_commands(config, snapper_config, parent_cmd) or + check_important_packages(config, snapper_config, packages)) + + snapper_cmd = SnapperCmd(snapper_config, args.type, cleanup_algorithm, + description, chroot, pre_number, important) num = snapper_cmd() logging.info(f"==> {snapper_config}: {num}") diff --git a/tests/test_script.py b/tests/test_script.py index a629e0f..2cafe39 100644 --- a/tests/test_script.py +++ b/tests/test_script.py @@ -6,8 +6,8 @@ import os import pytest from scripts.snap_pac import ( - SnapperCmd, get_pre_number, get_snapper_configs, main, setup_config_parser, - get_description + SnapperCmd, check_important_commands, check_important_packages, get_pre_number, get_snapper_configs, + main, setup_config_parser, get_description ) @@ -54,6 +54,11 @@ def prefile(): SnapperCmd("root", "post", "number", "bar", True, 1234), "snapper --no-dbus --config root create --type post --cleanup-algorithm number --print-number" " --description \"bar\" --pre-number 1234" + ), + ( + SnapperCmd("root", "post", "number", "bar", False, 1234, True), + "snapper --config root create --type post --cleanup-algorithm number --print-number" + " --description \"bar\" --userdata \"important=yes\" --pre-number 1234" ) ]) def test_snapper_cmd(snapper_cmd, actual_cmd): @@ -84,7 +89,7 @@ def test_setup_config_parser(config): f.write("desc_limit = 3\n") f.write("post_description = a really long description\n") name = f.name - config2 = setup_config_parser(name, "foo", "bar") + config2 = setup_config_parser(name, "foo", ["bar"]) assert config == config2 @@ -104,3 +109,25 @@ def test_no_prefile(): @pytest.mark.parametrize("snapshot_type, description", [("pre", "foo"), ("post", "a r")]) def test_get_description(snapshot_type, description, config): assert get_description(snapshot_type, config, "home") == description + + +def test_important_commands(): + parent_cmd = "pacman -Syu" + with tempfile.NamedTemporaryFile("w", delete=False) as f: + f.write("[DEFAULT]\n") + f.write("important_commands = [\"pacman -Syu\"]\n") + name = f.name + config = setup_config_parser(name, parent_cmd, ["bar"]) + important = check_important_commands(config, "root", parent_cmd) + assert important + + +def test_important_packages(): + packages = ["bar", "linux", "vim"] + with tempfile.NamedTemporaryFile("w", delete=False) as f: + f.write("[DEFAULT]\n") + f.write("important_packages = [\"linux\"]\n") + name = f.name + config = setup_config_parser(name, "pacman -S", packages) + important = check_important_packages(config, "root", packages) + assert important From d81070c89836b744271c82a6895e3b56efb2e9c2 Mon Sep 17 00:00:00 2001 From: Wes Barnett Date: Sat, 13 Mar 2021 07:15:18 -0500 Subject: [PATCH 02/13] add defaults for important commands and packages --- scripts/snap_pac.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/snap_pac.py b/scripts/snap_pac.py index 87bbd01..4ecba43 100755 --- a/scripts/snap_pac.py +++ b/scripts/snap_pac.py @@ -75,6 +75,8 @@ def setup_config_parser(ini_file, parent_cmd, packages): "pre_description": parent_cmd, "post_description": " ".join(packages), "desc_limit": 72 + "important_packages": [], + "important_commands": [] } config["root"] = { "snapshot": True From fad08ebe292b1f136ea595b294f16ab0ea826b1e Mon Sep 17 00:00:00 2001 From: Wes Barnett Date: Sat, 13 Mar 2021 07:17:24 -0500 Subject: [PATCH 03/13] rm print --- scripts/snap_pac.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/scripts/snap_pac.py b/scripts/snap_pac.py index 4ecba43..154f3ae 100755 --- a/scripts/snap_pac.py +++ b/scripts/snap_pac.py @@ -113,8 +113,6 @@ def check_important_commands(config, snapper_config, parent_cmd): def check_important_packages(config, snapper_config, packages): important_packages = json.loads(config.get(snapper_config, "important_packages")) - print(important_packages) - print(packages) return any(x in important_packages for x in packages) From e5ea12d060b5b78b68180348cec398cd35cc04f0 Mon Sep 17 00:00:00 2001 From: Wes Barnett Date: Sat, 13 Mar 2021 07:18:45 -0500 Subject: [PATCH 04/13] fix syntax --- scripts/snap_pac.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/snap_pac.py b/scripts/snap_pac.py index 154f3ae..7838fa1 100755 --- a/scripts/snap_pac.py +++ b/scripts/snap_pac.py @@ -74,7 +74,7 @@ def setup_config_parser(ini_file, parent_cmd, packages): "cleanup_algorithm": "number", "pre_description": parent_cmd, "post_description": " ".join(packages), - "desc_limit": 72 + "desc_limit": 72, "important_packages": [], "important_commands": [] } From 8dfba55e07c89eaca63b7382ab46d3d440c55782 Mon Sep 17 00:00:00 2001 From: Wes Barnett Date: Sat, 13 Mar 2021 07:28:05 -0500 Subject: [PATCH 05/13] Fix missing data in pytest fixture --- tests/test_script.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_script.py b/tests/test_script.py index 2cafe39..291f17a 100644 --- a/tests/test_script.py +++ b/tests/test_script.py @@ -19,7 +19,9 @@ def config(): "cleanup_algorithm": "number", "pre_description": "foo", "post_description": "bar", - "desc_limit": 72 + "desc_limit": 72, + "important_packages": [], + "important_commands": [] } config["root"] = { "snapshot": True From 9ab0a7833d3e86bf90d5a12b0c37dc82a6d80e28 Mon Sep 17 00:00:00 2001 From: Wes Barnett Date: Sat, 13 Mar 2021 12:43:55 -0500 Subject: [PATCH 06/13] Add option to pass any userdata --- extra/snap-pac.ini | 8 +++++--- scripts/snap_pac.py | 20 +++++++++++++++----- tests/test_script.py | 32 +++++++++++++++++++++++++++++--- 3 files changed, 49 insertions(+), 11 deletions(-) diff --git a/extra/snap-pac.ini b/extra/snap-pac.ini index 92c9825..2ce6d94 100644 --- a/extra/snap-pac.ini +++ b/extra/snap-pac.ini @@ -21,12 +21,14 @@ cleanup_algorithm = number #post_description = pacman post snapshot # Uncomment to add "important=yes" to userdata for snapshots referring to these packages -#important_packages = ["linux"] +#important_packages = ["linux", "linux-lts] -# Uncomment to add "important=yes" to userdata for snapshots that were created with the -#following commands +# Uncomment to add "important=yes" to userdata for snapshots that were created with the following commands #important_commands = ["pacman -Syu"] +# Add custom userdata. Each key-value pair should be an item in the list +#userdata = ["key=value","foo=bar"] + # Example for another snapper configuration named "home" # [home] # snapshot = True diff --git a/scripts/snap_pac.py b/scripts/snap_pac.py index 7838fa1..b387483 100755 --- a/scripts/snap_pac.py +++ b/scripts/snap_pac.py @@ -32,7 +32,7 @@ logging.basicConfig(format="%(message)s", level=logging.INFO) class SnapperCmd: def __init__(self, config, snapshot_type, cleanup_algorithm, description="", - nodbus=False, pre_number=None, important=False): + nodbus=False, pre_number=None, userdata=""): self.cmd = ["snapper"] if nodbus: self.cmd.append("--no-dbus") @@ -42,8 +42,8 @@ class SnapperCmd: self.cmd.append("--print-number") if description: self.cmd.append(f"--description \"{description}\"") - if important: - self.cmd.append("--userdata \"important=yes\"") + if userdata: + self.cmd.append(f"--userdata \"{userdata}\"") if snapshot_type == "post": if pre_number is not None: self.cmd.append(f"--pre-number {pre_number}") @@ -76,7 +76,8 @@ def setup_config_parser(ini_file, parent_cmd, packages): "post_description": " ".join(packages), "desc_limit": 72, "important_packages": [], - "important_commands": [] + "important_commands": [], + "userdata": set() } config["root"] = { "snapshot": True @@ -116,6 +117,13 @@ def check_important_packages(config, snapper_config, packages): return any(x in important_packages for x in packages) +def get_userdata(config, snapper_config, important): + userdata = set(json.loads(config.get(snapper_config, "userdata"))) + if important: + userdata.add("important=yes") + return ",".join(sorted(list(userdata))) + + def main(snap_pac_ini, snapper_conf_file, args): if os.getenv("SNAP_PAC_SKIP", "n").lower() in ["y", "yes", "true", "1"]: @@ -142,8 +150,10 @@ def main(snap_pac_ini, snapper_conf_file, args): important = (check_important_commands(config, snapper_config, parent_cmd) or check_important_packages(config, snapper_config, packages)) + userdata = get_userdata(config, snapper_config, important) + snapper_cmd = SnapperCmd(snapper_config, args.type, cleanup_algorithm, - description, chroot, pre_number, important) + description, chroot, pre_number, userdata) num = snapper_cmd() logging.info(f"==> {snapper_config}: {num}") diff --git a/tests/test_script.py b/tests/test_script.py index 291f17a..e479062 100644 --- a/tests/test_script.py +++ b/tests/test_script.py @@ -7,7 +7,7 @@ import pytest from scripts.snap_pac import ( SnapperCmd, check_important_commands, check_important_packages, get_pre_number, get_snapper_configs, - main, setup_config_parser, get_description + get_userdata, main, setup_config_parser, get_description ) @@ -21,7 +21,8 @@ def config(): "post_description": "bar", "desc_limit": 72, "important_packages": [], - "important_commands": [] + "important_commands": [], + "userdata": set() } config["root"] = { "snapshot": True @@ -58,9 +59,14 @@ def prefile(): " --description \"bar\" --pre-number 1234" ), ( - SnapperCmd("root", "post", "number", "bar", False, 1234, True), + SnapperCmd("root", "post", "number", "bar", False, 1234, "important=yes"), "snapper --config root create --type post --cleanup-algorithm number --print-number" " --description \"bar\" --userdata \"important=yes\" --pre-number 1234" + ), + ( + SnapperCmd("root", "post", "number", "bar", False, 1234, "foo=bar,important=yes"), + "snapper --config root create --type post --cleanup-algorithm number --print-number" + " --description \"bar\" --userdata \"foo=bar,important=yes\" --pre-number 1234" ) ]) def test_snapper_cmd(snapper_cmd, actual_cmd): @@ -133,3 +139,23 @@ def test_important_packages(): config = setup_config_parser(name, "pacman -S", packages) important = check_important_packages(config, "root", packages) assert important + + +def test_load_userdata(): + with tempfile.NamedTemporaryFile("w", delete=False) as f: + f.write("[DEFAULT]\n") + f.write("userdata = [\"foo=bar\", \"requestid=42\"]\n") + name = f.name + config = setup_config_parser(name, "pacman -Syu", ["bar"]) + userdata = get_userdata(config, "root", False) + assert userdata == "foo=bar,requestid=42" + + +def test_load_userdata_and_important(): + with tempfile.NamedTemporaryFile("w", delete=False) as f: + f.write("[DEFAULT]\n") + f.write("userdata = [\"foo=bar\", \"requestid=42\"]\n") + name = f.name + config = setup_config_parser(name, "pacman -Syu", ["bar"]) + userdata = get_userdata(config, "root", True) + assert userdata == "foo=bar,important=yes,requestid=42" From 733f915ac0f33578e5e0c37048b3be7114c4af2c Mon Sep 17 00:00:00 2001 From: Wes Barnett Date: Sat, 13 Mar 2021 12:55:27 -0500 Subject: [PATCH 07/13] add list of options --- docs/source/configuration.rst | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index f177075..3a37ab7 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -21,7 +21,33 @@ self-explanatory. Each section corresponds with a snapper configuration. Add additional sections to add other snapper configurations to be snapshotted. By default, only the root configuration -is snapshotted. +is snapshotted. Additionally you can add a section named ``DEFAULT`` with options that +apply to all snapper configurations unless overridden in a later section. + +Each section can have the following entries: + +**desc_limit** - integer; maximum length of description string before being truncated. +Default: 72 + +**important_packages** - list of strings; names of packages that if involved in a pacman +transaction will add ``important=yes`` to the snapper userdata for the pair of +snapshots. Default: [] + +**important_commands** - list of strings; parent commands that will add +``important=yes`` to the snapper userdata for the pair of snapshots. Default: [] + +**pre_description** - string; description for the pre snapshot. Default: the parent +command that called the pacman hook. + +**post_description** - string; description for the post snapshot. Default: space +separated list of packages that were installed, upgraded, or removed. + +**snapshot** - boolean; whether or not to snapshot the configuration. Default: True for +``root`` configuration; False otherwise. + +**userdata** - list of strings; key-value pairs that will be added to the userdata for +the pair of snapshots. Default: [] + Environment Variables --------------------- From 7eb708101275579145773948b1428cd7db14c876 Mon Sep 17 00:00:00 2001 From: Wes Barnett Date: Sat, 13 Mar 2021 12:57:27 -0500 Subject: [PATCH 08/13] Link to new documentation --- README.md | 4 +- man8/snap-pac.8 | 202 ------------------------------------------------ 2 files changed, 2 insertions(+), 204 deletions(-) delete mode 100644 man8/snap-pac.8 diff --git a/README.md b/README.md index 931998b..0e063f6 100644 --- a/README.md +++ b/README.md @@ -40,11 +40,11 @@ self-explanatory. ## Documentation -Run `man 8 snap-pac` after installation. +See the [documentation here](https://wesbarnett.github.io/snap-pac/). ## Troubleshooting -After reviewing the man page, [check the issues page] and file a new issue if your +After reviewing the documentation, [check the issues page] and file a new issue if your problem is not covered. [download the latest release]: https://github.com/wesbarnett/snap-pac/releases diff --git a/man8/snap-pac.8 b/man8/snap-pac.8 deleted file mode 100644 index 985fce1..0000000 --- a/man8/snap-pac.8 +++ /dev/null @@ -1,202 +0,0 @@ -'\" t -.TH SNAP-PAC 8 2021-01-29 SNAP-PAC -.SH NAME -snap-pac \- Pacman hooks that use snapper to create pre/post btrfs snapshots -like openSUSE's YaST - -.SH DESCRIPTION - -This is a set of \fIpacman\fR hooks and script that causes \fIsnapper\fR to -automatically take a pre and post snapshot before and after pacman transactions, -similar to how YaST does with OpenSuse. This provides a simple way to undo -changes to a system after a pacman transaction. - -Because these are pacman hooks, it doesn't matter how you call pacman—whether -directly, through an AUR helper, or using an alias—snapper will create the -snapshots when pacman installs, upgrades, or removes a package. The pacman -command used is logged in the snapper description for the snapshots. -Additionally the snapshot numbers are output to the screen and to the pacman log -for each snapper configuration during the pacman transaction, so that the user can -easily find which changes he or she may want to revert. - -To undo changes from a pacman transaction, use \fIsnapper undochange\fR. See -\fBsnapper\fR(8) and \fBEXAMPLES\fR. - -If you have severe breakage—like snapper is gone for some reason and you can't -get it back—you'll have to resort to more extreme methods, such as taking a -snapshot of the pre snapshot and making it the default subvolume or mounting it -as \fI/\fR. Most likely you'll need to use a live USB to get into a chroot -environment to do any of these things. Snapper has a snapper rollback feature, -but your setup has to be properly configured to use it. The exact procedure -depends on your specific setup. Be careful. - -.SH CONFIGURATION -Configuration is done via Python ini configuration files. The defaults of the -should be suitable for most users, so you may not need to do any configuration at all. -By default only the "root" snapper configuration is snapshotted. - -A commented example configuration files is located at \fI/etc/snap-pac.ini.example\fR. - -.SH ENVIRONMENT VARIABLES - -To temporarily prevent snapshots from being performed for a single -pacman command, set the environment variable \fISNAP_PAC_SKIP\fR. For -example: - -.EX - - $ sudo SNAP_PAC_SKIP=y pacman -Syu - -.EE - - -.SH EXAMPLES - -Here is an example of how the snapshots are created and how to rollback and pacman -transaction. Here the \fBnano\fR package is installed: - -.EX - - # pacman -S nano - resolving dependencies... - looking for conflicting packages... - - Packages (1) nano-2.5.3-1 - - Total Installed Size: 2.14 MiB - - :: Proceed with installation? [Y/n] Y - (1/1) checking keys in keyring [######################################] 100% - (1/1) checking package integrity [######################################] 100% - (1/1) loading package files [######################################] 100% - (1/1) checking for file conflicts [######################################] 100% - (1/1) checking available disk space [######################################] 100% - :: Running pre-transaction hooks... - (1/1) Performing snapper pre snapshots for the following configurations... - => root: 1033 - :: Processing package changes... - (1/1) installing nano [######################################] 100% - :: Running post-transaction hooks... - (1/1) Performing snapper post snapshots for the following configurations... - => root: 1034 - -.EE - -The snapper snapshot number is given for each snapper configuration that is -used. This is also logged in pacman's log. - -Here are the snapshots created before and after the pacman transaction: - -.EX - - # snapper -c root list -t pre-post | tail -n 1 - 1033 | 1034 | Fri 22 Apr 2016 01:54:13 PM CDT | Fri 22 Apr 2016 01:54:14 PM CDT | pacman -S nano | - -.EE - -Here is what changed during the transaction: - -.EX - - # snapper -c root status 1033..1034 - +..... /etc/nanorc - c..... /etc/snapper/.snap-pac-pre - +..... /usr/bin/nano - +..... /usr/bin/rnano - +..... /usr/share/doc/nano - +..... /usr/share/doc/nano/faq.html - +..... /usr/share/doc/nano/fr - +..... /usr/share/doc/nano/fr/nano.1.html - +..... /usr/share/doc/nano/fr/nanorc.5.html - +..... /usr/share/doc/nano/fr/rnano.1.html - -.EE - -The above output is truncated, but it continues. See the \fBsnapper\fR(8) to -for what each symbol means. You can also do \fBsnapper diff\fR in the same way. - -Then, to undo the \fBpacman\fR transaction: - -.EX - - # snapper -c root undochange 1033..1034 - create:0 modify:3 delete:100 - -.EE - -Now nano is no longer installed, along with all the files it changed: - -.EX - - $ pacman -Qi nano - error: package 'nano' was not found - -.EE - -.SH TROUBLESHOOTING - -.SS snap-pac is only taking snapshots of the root configuration. -That's the default behavior. See \fBCONFIGURATION\fR. - -.SS No snapshots are being taken when I run pacman. -No snapper configurations are set up for snap-pac's pacman hooks. By default -snap-pac will take snapshots for the root configuration and any other -configuration which has SNAPSHOT set to yes in its configuration file. -See \fBCONFIGURATION\fR. - -.SS After restoring snapshot from snap-pac, the pacman database is locked. -The pre/post snaphots are taken while pacman is running, so this is expected. -Follow the instructions pacman gives you (e.g., removing the lock file). You can add the -database lock file to a snapper filter so that snapper won't consider it when performing -\fBsnapper diff\fR, \fBsnapper status\fR, \fBsnapper undochange\fR, etc. See the -\fBFilters\fR section in \fBsnapper\fR(8) for more information. - - -.SH FAQ -.SS Does snap-pac backup non-btrfs /boot partitions? -No, but you can add a hook that does it for you. It would be -something like the following: - -.EX - - [Trigger] - Operation = Upgrade - Operation = Install - Operation = Remove - Type = Package - Target = linux - - [Action] - Description = Backing up /boot... - When = PreTransaction - Exec = /usr/bin/rsync -avzq --delete /boot /.bootbackup - -.EE - -.SS How do I link old kernel modules automatically when the kernel is upgraded? -This behavior is no longer a part of this package. Use a pacman hook like the following: - - [Trigger] - Operation = Upgrade - Operation = Install - Operation = Remove - Type = Package - Target = linux - - [Action] - Description = Symlinking old kernel modules... - When = PostTransaction - Exec = /usr/bin/bash -c "find /usr/lib/modules -xtype l -delete; ln -sv /.snapshots/$(snapper -c root list | awk 'END{print $1}')/snapshot/usr/lib/modules/$(uname -r) /usr/lib/modules/" - -.SH HOMEPAGE -https://github.com/wesbarnett/snap-pac - -.SH AUTHORS -Wes Barnett - -.SH SEE ALSO -.BR alpm-hooks (5), -.BR snapper (8), -.BR snapper-configs (5), -.BR pacman (8) - From 8ca92a0bb4c81a2fdf17697517adc7dda35c4036 Mon Sep 17 00:00:00 2001 From: Wes Barnett Date: Sat, 13 Mar 2021 13:33:32 -0500 Subject: [PATCH 09/13] Update documentation --- docs/source/conf.py | 12 ++++++++++++ docs/source/configuration.rst | 37 ++++++++++++++--------------------- docs/source/index.rst | 4 ++-- 3 files changed, 29 insertions(+), 24 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 64b1b2e..6127d14 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -50,3 +50,15 @@ html_theme = 'alabaster' # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] + +html_theme_options = { + 'badge_branch': 'main', + 'fixed_sidebar': True, + 'github_banner': True, + 'github_user': 'wesbarnett', + 'github_repo': 'snap-pac', + 'github_type': 'star', + 'show_related': True +} + +man_pages = [("index", "snap-pac", "Pacman hooks that use snapper to create pre/post btrfs snapshots like openSUSE's YaST", "Wes Barnett", "8")] diff --git a/docs/source/configuration.rst b/docs/source/configuration.rst index 3a37ab7..fc250a3 100644 --- a/docs/source/configuration.rst +++ b/docs/source/configuration.rst @@ -26,28 +26,21 @@ apply to all snapper configurations unless overridden in a later section. Each section can have the following entries: -**desc_limit** - integer; maximum length of description string before being truncated. -Default: 72 - -**important_packages** - list of strings; names of packages that if involved in a pacman -transaction will add ``important=yes`` to the snapper userdata for the pair of -snapshots. Default: [] - -**important_commands** - list of strings; parent commands that will add -``important=yes`` to the snapper userdata for the pair of snapshots. Default: [] - -**pre_description** - string; description for the pre snapshot. Default: the parent -command that called the pacman hook. - -**post_description** - string; description for the post snapshot. Default: space -separated list of packages that were installed, upgraded, or removed. - -**snapshot** - boolean; whether or not to snapshot the configuration. Default: True for -``root`` configuration; False otherwise. - -**userdata** - list of strings; key-value pairs that will be added to the userdata for -the pair of snapshots. Default: [] - +* ``desc_limit`` - integer; maximum length of description string before being truncated. + Default: 72 +* ``important_packages`` - list of strings; names of packages that if involved in a pacman + transaction will add ``important=yes`` to the snapper userdata for the pair of + snapshots. Default: [] +* ``important_commands`` - list of strings; parent commands that will add + ``important=yes`` to the snapper userdata for the pair of snapshots. Default: [] +* ``pre_description`` - string; description for the pre snapshot. Default: the parent + command that called the pacman hook. +* ``post_description`` - string; description for the post snapshot. Default: space + separated list of packages that were installed, upgraded, or removed. +* ``snapshot`` - boolean; whether or not to snapshot the configuration. Default: True for + ``root`` configuration; False otherwise. +* ``userdata`` - list of strings; key-value pairs that will be added to the userdata for + the pair of snapshots. Default: [] Environment Variables --------------------- diff --git a/docs/source/index.rst b/docs/source/index.rst index 018ddb5..94663bf 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -3,8 +3,8 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. -Welcome to snap-pac's documentation! -==================================== +snap-pac +======== .. toctree:: :maxdepth: 2 From bf541e667d6bcd1ac0ea71559b0bdab7eb765bbf Mon Sep 17 00:00:00 2001 From: Wes Barnett Date: Sat, 13 Mar 2021 13:45:15 -0500 Subject: [PATCH 10/13] update docs --- docs/source/conf.py | 1 + docs/source/examples.rst | 5 ++--- docs/source/index.rst | 7 +++---- docs/source/troubleshooting.rst | 2 +- 4 files changed, 7 insertions(+), 8 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 6127d14..3de3400 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -62,3 +62,4 @@ html_theme_options = { } man_pages = [("index", "snap-pac", "Pacman hooks that use snapper to create pre/post btrfs snapshots like openSUSE's YaST", "Wes Barnett", "8")] +manpages_url = 'https://man.archlinux.org/man/{page}.{section}' diff --git a/docs/source/examples.rst b/docs/source/examples.rst index 5877ed1..eb93adb 100644 --- a/docs/source/examples.rst +++ b/docs/source/examples.rst @@ -67,9 +67,8 @@ Here is what changed during the transaction: +..... /usr/share/doc/nano/fr/nanorc.5.html +..... /usr/share/doc/nano/fr/rnano.1.html -The above output is truncated, but it continues. See the `snapper(8) -`_ to for what each symbol means. You can also -do ``snapper diff`` in the same way. +The above output is truncated, but it continues. See :manpage:`snapper(8)` to for what each +symbol means. You can also do ``snapper diff`` in the same way. Then, to undo the pacman transaction: diff --git a/docs/source/index.rst b/docs/source/index.rst index 94663bf..feb8dc3 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -7,7 +7,7 @@ snap-pac ======== .. toctree:: - :maxdepth: 2 + :maxdepth: 1 installation configuration @@ -29,9 +29,8 @@ are output to the screen and to the pacman log for each snapper configuration du pacman transaction, so that the user can easily find which changes he or she may want to revert. -To undo changes from a pacman transaction, use ``snapper undochange``. See the `snapper -documentation `_ for more details as well as -examples. +To undo changes from a pacman transaction, use ``snapper undochange``. See the :manpage:`snapper(8)` +for more details as well as examples. If you have severe breakage—like snapper is gone for some reason and you can't get it back—you'll have to resort to more extreme methods, such as taking a snapshot of the pre diff --git a/docs/source/troubleshooting.rst b/docs/source/troubleshooting.rst index be961c6..e7c9028 100644 --- a/docs/source/troubleshooting.rst +++ b/docs/source/troubleshooting.rst @@ -20,4 +20,4 @@ The pre/post snaphots are taken while pacman is running, so this is expected. F the instructions pacman gives you (*e.g.*, removing the lock file). You can add the database lock file to a snapper filter so that snapper won't consider it when performing snapper diff, snapper status, snapper undochange, etc. See the Filters -section in `snapper(8) `_ for more information. +section in :manpage:`snapper(8)` for more information. From a78e7b6d713f74dca3b79c16f72107b11f284f33 Mon Sep 17 00:00:00 2001 From: Wes Barnett Date: Sat, 13 Mar 2021 13:58:02 -0500 Subject: [PATCH 11/13] update docs --- docs/source/index.rst | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/source/index.rst b/docs/source/index.rst index feb8dc3..d723988 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -6,15 +6,6 @@ snap-pac ======== -.. toctree:: - :maxdepth: 1 - - installation - configuration - examples - troubleshooting - faq - This is a set of `pacman `_ hooks and script that causes `snapper `_ to automatically take a pre and post snapshot before and after pacman transactions, similar to how `YaST `_ does with @@ -38,3 +29,12 @@ snapshot and making it the default subvolume or mounting it as /. Most likely yo need to use a live USB to get into a chroot environment to do any of these things. Snapper has a ``snapper rollback`` feature, but your setup has to be properly configured to use it. The exact procedure depends on your specific setup. Be careful. + +.. toctree:: + :maxdepth: 1 + + installation + configuration + examples + troubleshooting + faq From 292f1713adbb770ecce776a78b6c46b0e46291d1 Mon Sep 17 00:00:00 2001 From: Wes Barnett Date: Sat, 13 Mar 2021 13:59:36 -0500 Subject: [PATCH 12/13] update docs --- .github/workflows/docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 31c52ac..99af7a1 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -19,7 +19,7 @@ jobs: - name: Install dependencies run: python -m pip install --upgrade pip sphinx - name: Build docs - run: cd docs && sphinx-build source build + run: sphinx-build -a docs/source docs/build - name: Deploy uses: peaceiris/actions-gh-pages@v3 with: From 1ef50ddd428bea363b4bdf39d520526b970e3fb3 Mon Sep 17 00:00:00 2001 From: Wes Barnett Date: Sat, 13 Mar 2021 14:01:23 -0500 Subject: [PATCH 13/13] add back manpage --- README.md | 3 +- man8/snap-pac.8 | 392 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 394 insertions(+), 1 deletion(-) create mode 100644 man8/snap-pac.8 diff --git a/README.md b/README.md index 0e063f6..bcd88d3 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,8 @@ self-explanatory. ## Documentation -See the [documentation here](https://wesbarnett.github.io/snap-pac/). +See the [documentation here](https://wesbarnett.github.io/snap-pac/) or `man 8 snap-pac` +after installation. ## Troubleshooting diff --git a/man8/snap-pac.8 b/man8/snap-pac.8 new file mode 100644 index 0000000..479f5bc --- /dev/null +++ b/man8/snap-pac.8 @@ -0,0 +1,392 @@ +.\" Man page generated from reStructuredText. +. +.TH "SNAP-PAC" "8" "Mar 13, 2021" "" "snap-pac" +.SH NAME +snap-pac \- Pacman hooks that use snapper to create pre/post btrfs snapshots like openSUSE's YaST +. +.nr rst2man-indent-level 0 +. +.de1 rstReportMargin +\\$1 \\n[an-margin] +level \\n[rst2man-indent-level] +level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] +- +\\n[rst2man-indent0] +\\n[rst2man-indent1] +\\n[rst2man-indent2] +.. +.de1 INDENT +.\" .rstReportMargin pre: +. RS \\$1 +. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] +. nr rst2man-indent-level +1 +.\" .rstReportMargin post: +.. +.de UNINDENT +. RE +.\" indent \\n[an-margin] +.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] +.nr rst2man-indent-level -1 +.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] +.in \\n[rst2man-indent\\n[rst2man-indent-level]]u +.. + +.SH DESCRIPTION +.sp +This is a set of \fI\%pacman\fP hooks and script that causes +\fI\%snapper\fP to automatically take a pre and post snapshot before and +after pacman transactions, similar to how \fI\%YaST\fP does with +OpenSuse. This provides a simple way to undo changes to a system after a pacman +transaction. +.sp +Because these are pacman hooks, it doesn\(aqt matter how you call pacman—whether +directly, through an AUR helper, or using an alias—snapper will create the snapshots +when pacman installs, upgrades, or removes a package. The pacman command used is +logged in the snapper description for the snapshots. Additionally the snapshot numbers +are output to the screen and to the pacman log for each snapper configuration during the +pacman transaction, so that the user can easily find which changes he or she may want to +revert. +.sp +To undo changes from a pacman transaction, use \fBsnapper undochange\fP\&. See the \fBsnapper(8)\fP +for more details as well as examples. +.sp +If you have severe breakage—like snapper is gone for some reason and you can\(aqt get it +back—you\(aqll have to resort to more extreme methods, such as taking a snapshot of the pre +snapshot and making it the default subvolume or mounting it as /. Most likely you\(aqll +need to use a live USB to get into a chroot environment to do any of these things. +Snapper has a \fBsnapper rollback\fP feature, but your setup has to be properly configured to +use it. The exact procedure depends on your specific setup. Be careful. +.SH INSTALLATION +.sp +Install the \fBsnap\-pac\fP package using pacman: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +pacman \-S snap\-pac +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +Alternatively download the \fI\%latest release and signature\fP\&. Then, verify the download: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +gpg \-\-verify snap\-pac\-.tar.gz.sig +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +where \fB\fP is the version number you downloaded. +.sp +Finally, run: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +make install +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +I have signed the release tarball and commits with my PGP key. Starting with release +2.2, the tarballs are signed with my key with fingerprint +\fBF7B28C61944FE30DABEEB0B01070BCC98C18BD66\fP\&. +.sp +For previous releases, the key\(aqs fingerprint was +\fB8535CEF3F3C38EE69555BF67E4B5E45AA3B8C5C3\fP\&. +.SH CONFIGURATION +.sp +Configuration is done via Python ini configuration files. The defaults +should be suitable for most users, so you may not need to do any configuration at all. +By default only the \fBroot\fP snapper configuration is snapshotted. +.sp +A commented example configuration files is located at \fB/etc/snap\-pac.ini.example\fP\&. +.sp +To configure, copy the example configuration file: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +cp /etc/snap\-pac.ini{.example,} +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +Then edit with your favorite editor. The file is commented and should be +self\-explanatory. +.sp +Each section corresponds with a snapper configuration. Add additional sections to add +other snapper configurations to be snapshotted. By default, only the root configuration +is snapshotted. Additionally you can add a section named \fBDEFAULT\fP with options that +apply to all snapper configurations unless overridden in a later section. +.sp +Each section can have the following entries: +.INDENT 0.0 +.IP \(bu 2 +\fBdesc_limit\fP \- integer; maximum length of description string before being truncated. +Default: 72 +.IP \(bu 2 +\fBimportant_packages\fP \- list of strings; names of packages that if involved in a pacman +transaction will add \fBimportant=yes\fP to the snapper userdata for the pair of +snapshots. Default: [] +.IP \(bu 2 +\fBimportant_commands\fP \- list of strings; parent commands that will add +\fBimportant=yes\fP to the snapper userdata for the pair of snapshots. Default: [] +.IP \(bu 2 +\fBpre_description\fP \- string; description for the pre snapshot. Default: the parent +command that called the pacman hook. +.IP \(bu 2 +\fBpost_description\fP \- string; description for the post snapshot. Default: space +separated list of packages that were installed, upgraded, or removed. +.IP \(bu 2 +\fBsnapshot\fP \- boolean; whether or not to snapshot the configuration. Default: True for +\fBroot\fP configuration; False otherwise. +.IP \(bu 2 +\fBuserdata\fP \- list of strings; key\-value pairs that will be added to the userdata for +the pair of snapshots. Default: [] +.UNINDENT +.SS Environment Variables +.sp +To temporarily prevent snapshots from being performed for a single pacman +command, set the environment variable \fBSNAP_PAC_SKIP\fP\&. For example: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +sudo SNAP_PAC_SKIP=y pacman \-Syu +.ft P +.fi +.UNINDENT +.UNINDENT +.SH EXAMPLE +.sp +Here is an example of how the snapshots are created and how to rollback and pacman +transaction. Here the nano package is installed: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +pacman \-S nano +.ft P +.fi +.UNINDENT +.UNINDENT +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +resolving dependencies... +looking for conflicting packages... + +Packages (1) nano\-2.5.3\-1 + +Total Installed Size: 2.14 MiB + +:: Proceed with installation? [Y/n] Y +(1/1) checking keys in keyring [######################################] 100% +(1/1) checking package integrity [######################################] 100% +(1/1) loading package files [######################################] 100% +(1/1) checking for file conflicts [######################################] 100% +(1/1) checking available disk space [######################################] 100% +:: Running pre\-transaction hooks... +(1/1) Performing snapper pre snapshots for the following configurations... +=> root: 1033 +:: Processing package changes... +(1/1) installing nano [######################################] 100% +:: Running post\-transaction hooks... +(1/1) Performing snapper post snapshots for the following configurations... +=> root: 1034 +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +The snapper snapshot number is given for each snapper configuration that is used. This +is also logged in pacman\(aqs log. +.sp +Here are the snapshots created before and after the pacman transaction: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +snapper \-c root list \-t pre\-post | tail \-n 1 +.ft P +.fi +.UNINDENT +.UNINDENT +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +1033 | 1034 | Fri 22 Apr 2016 01:54:13 PM CDT | Fri 22 Apr 2016 01:54:14 PM CDT | pacman \-S nano | +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +Here is what changed during the transaction: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +snapper \-c root status 1033..1034 +.ft P +.fi +.UNINDENT +.UNINDENT +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C ++..... /etc/nanorc +c..... /etc/snapper/.snap\-pac\-pre ++..... /usr/bin/nano ++..... /usr/bin/rnano ++..... /usr/share/doc/nano ++..... /usr/share/doc/nano/faq.html ++..... /usr/share/doc/nano/fr ++..... /usr/share/doc/nano/fr/nano.1.html ++..... /usr/share/doc/nano/fr/nanorc.5.html ++..... /usr/share/doc/nano/fr/rnano.1.html +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +The above output is truncated, but it continues. See \fBsnapper(8)\fP to for what each +symbol means. You can also do \fBsnapper diff\fP in the same way. +.sp +Then, to undo the pacman transaction: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +snapper \-c root undochange 1033..1034 +.ft P +.fi +.UNINDENT +.UNINDENT +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +create:0 modify:3 delete:100 +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +Now nano is no longer installed, along with all the files it changed: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +pacman \-Qi nano +.ft P +.fi +.UNINDENT +.UNINDENT +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +error: package \(aqnano\(aq was not found +.ft P +.fi +.UNINDENT +.UNINDENT +.SH TROUBLESHOOTING +.sp +\fBsnap\-pac is only taking snapshots of the root configuration.\fP +.sp +That\(aqs the default behavior. See configuration\&. +.sp +\fBNo snapshots are being taken when I run pacman.\fP +.sp +No snapper configurations are set up for snap\-pac\(aqs pacman hooks. By default snap\-pac +will take snapshots for the root configuration and any other configuration which has +SNAPSHOT set to yes in its configuration file. See configuration\&. +.sp +\fBAfter restoring snapshot from snap\-pac, the pacman database is locked.\fP +.sp +The pre/post snaphots are taken while pacman is running, so this is expected. Follow +the instructions pacman gives you (\fIe.g.\fP, removing the lock file). You can add the +database lock file to a snapper filter so that snapper won\(aqt consider it when +performing snapper diff, snapper status, snapper undochange, etc. See the Filters +section in \fBsnapper(8)\fP for more information. +.SH FAQ +.sp +\fBDoes snap\-pac backup non\-btrfs /boot partitions?\fP +.sp +No, but you can add a hook that does it for you. It would be something like the following: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +[Trigger] +Operation = Upgrade +Operation = Install +Operation = Remove +Type = Package +Target = linux + +[Action] +Description = Backing up /boot... +When = PreTransaction +Exec = /usr/bin/rsync \-avzq \-\-delete /boot /.bootbackup +.ft P +.fi +.UNINDENT +.UNINDENT +.sp +\fBHow do I link old kernel modules automatically when the kernel is upgraded?\fP +.sp +This behavior is no longer a part of this package. Use a pacman hook like the following: +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +[Trigger] +Operation = Upgrade +Operation = Install +Operation = Remove +Type = Package +Target = linux + +[Action] +Description = Symlinking old kernel modules... +When = PostTransaction +Exec = /usr/bin/bash \-c "find /usr/lib/modules \-xtype l \-delete; ln \-sv /.snapshots/$(snapper \-c root list | awk \(aqEND{print $1}\(aq)/snapshot/usr/lib/modules/$(uname \-r) /usr/lib/modules/" +.ft P +.fi +.UNINDENT +.UNINDENT +.SH AUTHOR +Wes Barnett +.SH COPYRIGHT +2021, Wes Barnett, PhD +.\" Generated by docutils manpage writer. +.