#!/usr/bin/env python3 from datetime import datetime, timezone from pathlib import Path from typing import Optional import argparse import logging import os import pwd logging.basicConfig( level=logging.INFO, format='%(levelname)s: %(message)s', ) logger = logging.getLogger(__name__) def _get_username(): return pwd.getpwuid(os.getuid()).pw_name # The recfile descriptor for package requests. # Used if Breadpack generates the file from scratch. REQUEST_DESCRIPTOR = """ %rec: PackageRequest %type: Date date %type: Processed bool %type: User line %type: Package line %mandatory: Package %allowed: Date User Processed Package Comment %sort: Processed Date %doc: Package requests made via breadpack """.strip() def request_subcommand( requestsfile: Path, package_name: str, comment: Optional[str] = None, **kwargs): if not requestsfile.exists(): logger.warning(f'Creating file {requestsfile}') with requestsfile.open('w') as f: f.write(REQUEST_DESCRIPTOR) data = { 'Date': datetime.now(timezone.utc).isoformat(), 'User': _get_username(), 'Processed': 'no', 'Package': package_name, } if comment: data['Comment'] = comment with requestsfile.open('a') as f: f.write('\n\n' + '\n'.join([f'{k}: {v}' for k, v in data.items()])) logger.info(f'Your request for {package_name!r} has been sent!') def list_subcommand(json: bool = False, upgradable: bool = False, **kwargs): raise NotImplementedError def lint_subcommand(file, **kwargs): raise NotImplementedError def check_subcommand(json: bool = False, save: bool = True, **kwargs): raise NotImplementedError def lock_subcommand(**kwargs): raise NotImplementedError def main(): parser = argparse.ArgumentParser( description='Breadpunk.club meta-package manager' ) parser.add_argument( '--file', type=Path, help='Path to the breadpack.json file.', default=Path('/bread/breadpack.json'), ) parser.add_argument( '--lockfile', type=Path, help='Path to the breadpack-lock.json file.', default=Path('/bread/breadpack-lock.json'), ) parser.add_argument( '--requestsfile', type=Path, help='Path to the breadpack-requests.rec file.', default=Path('/bread/breadpack-requests.rec'), ) def nocommand(**kwargs): parser.error('A subcommand is required.') parser.set_defaults(func=nocommand) subparsers = parser.add_subparsers( title='subcommands', ) request_parser = subparsers.add_parser( 'request', help='Request for a new package to be installed.', ) request_parser.add_argument( 'package_name', help='Name of the requested package.', ) request_parser.add_argument( '-c', '--comment', help='Optional comment to add notes ' 'for the admin processing your request.', default=None, ) request_parser.set_defaults(func=request_subcommand) list_parser = subparsers.add_parser( 'list', help='List installed packages.', ) list_parser.add_argument( '--json', help='Use JSON output.', action='store_true', default=False, ) list_parser.add_argument( '--upgradable', help='Only list packages with available updates.', action='store_true', default=False, ) list_parser.set_defaults(func=list_subcommand) lint_parser = subparsers.add_parser( 'lint', help='Check the syntax of the packages file and lockfile.', ) lint_parser.set_defaults(func=lint_subcommand) lock_parser = subparsers.add_parser( 'lock', help='Regenerate the lockfile from scratch.', ) lock_parser.set_defaults(func=lock_subcommand) check_parser = subparsers.add_parser( 'check', help='Check for available updates.', ) check_parser.add_argument( '--json', help='Use JSON output.', action='store_true', default=False, ) check_parser.add_argument( '--save', help='Update the lockfile. This is the default.', action='store_true', default=True, ) check_parser.add_argument( '--no-save', help='Do not update the lockfile.', action='store_false', dest='save', ) args = vars(parser.parse_args()) args.pop('func', nocommand)(**args) if __name__ == '__main__': main()