148 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			148 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
#!/usr/bin/python3
 | 
						|
"""Upload a gist containing packages installed on the system.
 | 
						|
 | 
						|
Dependencies
 | 
						|
============
 | 
						|
* pacman
 | 
						|
* gist
 | 
						|
"""
 | 
						|
 | 
						|
import argparse
 | 
						|
import configparser
 | 
						|
import re
 | 
						|
import subprocess
 | 
						|
import sys
 | 
						|
 | 
						|
# ========== Constants ==========
 | 
						|
# Paths
 | 
						|
CONFIG_FILE = "/etc/packaging-scripts.conf"
 | 
						|
DEFAULT_FILENAME = "pacman-packages.txt"
 | 
						|
 | 
						|
# Config file options
 | 
						|
CONFIG_SECTION = "pug2"
 | 
						|
CONFIG_OPTION_DESCRIPTION = "GIST_DESCRIPTION"
 | 
						|
CONFIG_OPTION_ENABLE = "RUN_ON_PACMAN_HOOK"
 | 
						|
CONFIG_OPTION_FILENAME = "GIST_FILENAME"
 | 
						|
CONFIG_OPTION_ID = "GIST_ID"
 | 
						|
 | 
						|
DESCRIPTION = "Send a list of explicitly installed pacman packages to a gist"
 | 
						|
 | 
						|
# ========== Functions ==========
 | 
						|
def extract_gist_id(url):
 | 
						|
    """Extract the gist id from a gist URL.
 | 
						|
 | 
						|
    Normalizes URLs from
 | 
						|
    * http(s)?://<subdomain>.<second level domain>.<top level domain>/<username>/<gist ID>
 | 
						|
    * <username>/<gist ID>
 | 
						|
    * <gist ID>
 | 
						|
 | 
						|
    to one of
 | 
						|
 | 
						|
    * <username>/<gist ID>
 | 
						|
    * <gist ID>
 | 
						|
 | 
						|
    both of which are valid for use with gist
 | 
						|
 | 
						|
    :param url: a valid URL containing the gist id
 | 
						|
    :type url: str
 | 
						|
    :returns: the valid gist ID
 | 
						|
    :rtype: str
 | 
						|
    """
 | 
						|
    return re.sub("^http(s)?://[\\w]*.[\\w]*.[\\w]/", "", url)
 | 
						|
 | 
						|
 | 
						|
def retrieve_gist_info(gist_id):
 | 
						|
    """Retrieve info from gist ID that is specified in the config file.
 | 
						|
 | 
						|
    :param gist_id: string id to read gist info
 | 
						|
    :returns: data read from gist
 | 
						|
    :rtype: bytes
 | 
						|
    """
 | 
						|
    return subprocess.run(
 | 
						|
        ["gist", "--read", gist_id], capture_output=True, text=True
 | 
						|
    ).stdout
 | 
						|
 | 
						|
 | 
						|
def package_lists_match(gist_id, package_list):
 | 
						|
    """Compare local package list to that of pacman and gist, return if they match.
 | 
						|
    :param gist_id: ID of the gist to read from
 | 
						|
    :param package_list: Newline separated list of packages installed on the system
 | 
						|
    :type gist_id: str
 | 
						|
    :type package_list: str
 | 
						|
    """
 | 
						|
    return retrieve_gist_info(gist_id) == package_list
 | 
						|
 | 
						|
 | 
						|
# ========== Main Script ==========
 | 
						|
if __name__ == "__main__":
 | 
						|
    parser = argparse.ArgumentParser(description=DESCRIPTION)
 | 
						|
    parser.add_argument(
 | 
						|
        "-c",
 | 
						|
        "--check-if-enabled",
 | 
						|
        dest="check_enabled",
 | 
						|
        help="check if enabled in config file",
 | 
						|
        action="store_true",
 | 
						|
    )
 | 
						|
    parser.add_argument(
 | 
						|
        "-f",
 | 
						|
        "--force-update",
 | 
						|
        help="force update regardless if package lists match",
 | 
						|
        action="store_true",
 | 
						|
    )
 | 
						|
    args = parser.parse_args()
 | 
						|
    config = configparser.ConfigParser()
 | 
						|
    config.read(CONFIG_FILE)
 | 
						|
 | 
						|
    # Check if script is enabled to run; if not, exit silently
 | 
						|
    if args.check_enabled and not config.getboolean(
 | 
						|
        CONFIG_SECTION, CONFIG_OPTION_ENABLE
 | 
						|
    ):
 | 
						|
        sys.exit(0)
 | 
						|
 | 
						|
    gist_description = config.get(CONFIG_SECTION, CONFIG_OPTION_DESCRIPTION)
 | 
						|
    gist_filename = config.get(CONFIG_SECTION, CONFIG_OPTION_FILENAME)
 | 
						|
    gist_id = config.get(CONFIG_SECTION, CONFIG_OPTION_ID)
 | 
						|
    gist_opts = ["--filename", gist_filename, "--description", gist_description]
 | 
						|
 | 
						|
    packages = subprocess.run(
 | 
						|
        ["pacman", "-Qqen"], capture_output=True, text=True
 | 
						|
    ).stdout
 | 
						|
 | 
						|
    if gist_id == "":
 | 
						|
        try:
 | 
						|
            print("No gist ID detected, creating new.")
 | 
						|
            gist_process = subprocess.run(
 | 
						|
                ["gist", *gist_opts],
 | 
						|
                input=packages,
 | 
						|
                capture_output=True,
 | 
						|
                text=True,
 | 
						|
                check=True,
 | 
						|
            )
 | 
						|
        except subprocess.CalledProcessError as e:
 | 
						|
            print(e.stdout.strip(), file=sys.stderr)
 | 
						|
            sys.exit(e.returncode)
 | 
						|
        else:
 | 
						|
            config[CONFIG_SECTION][CONFIG_OPTION_ID] = extract_gist_id(
 | 
						|
                gist_process.stdout
 | 
						|
            )
 | 
						|
            print("Gist creation complete.")
 | 
						|
 | 
						|
    elif package_lists_match(gist_id, packages) and not args.force_update:
 | 
						|
        print("Package lists match, no update necessary.")
 | 
						|
        sys.exit(0)
 | 
						|
    else:
 | 
						|
        try:
 | 
						|
            print("Updating package list.")
 | 
						|
            subprocess.run(
 | 
						|
                ["gist", "--update", gist_id, *gist_opts],
 | 
						|
                input=packages,
 | 
						|
                capture_output=True,
 | 
						|
                text=True,
 | 
						|
                check=True,
 | 
						|
            )
 | 
						|
        except subprocess.CalledProcessError as e:
 | 
						|
            print(e.stdout.strip(), file=sys.stderr)
 | 
						|
            sys.exit(e.returncode)
 | 
						|
        else:
 | 
						|
            print("Update complete.")
 |