diff options
author | Devan Franchini <twitch153@gentoo.org> | 2014-06-18 16:39:52 -0400 |
---|---|---|
committer | Devan Franchini <twitch153@gentoo.org> | 2014-08-10 21:15:55 -0400 |
commit | 763eaef6d29bed6f9a7c78ade8de0c61f868ab94 (patch) | |
tree | e5bdb82f7b5661bde5b966cff8e306262df0f689 /layman/maker.py | |
parent | config.py: Adds output attribute for OptionConfig (diff) | |
download | layman-763eaef6d29bed6f9a7c78ade8de0c61f868ab94.tar.gz layman-763eaef6d29bed6f9a7c78ade8de0c61f868ab94.tar.bz2 layman-763eaef6d29bed6f9a7c78ade8de0c61f868ab94.zip |
Adds layman-overlay-maker tool
This tool has been made to assist users in creating their own
layman overlay definitions.
constants.py: Adds overlay component constants.
Diffstat (limited to 'layman/maker.py')
-rw-r--r-- | layman/maker.py | 361 |
1 files changed, 361 insertions, 0 deletions
diff --git a/layman/maker.py b/layman/maker.py new file mode 100644 index 0000000..583dcd4 --- /dev/null +++ b/layman/maker.py @@ -0,0 +1,361 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# File: maker.py +# +# Creates overlay definitions and writes them to an XML file +# +# Copyright: +# (c) 2014 Devan Franchini +# Distributed under the terms of the GNU General Public License v2 +# +# Author(s): +# Devan Franchini <twitch153@gentoo.org> +# + +#=============================================================================== +# +# Dependencies +# +#------------------------------------------------------------------------------- +from __future__ import unicode_literals + +import layman.overlays.overlay as Overlay +import xml.etree.ElementTree as ET + +import copy +import os +import sys + +from layman.api import LaymanAPI +from layman.compatibility import fileopen +from layman.constants import COMPONENT_DEFAULTS, POSSIBLE_COMPONENTS +from layman.config import OptionConfig +from layman.utils import indent, reload_config + +#py3 +if sys.hexversion >= 0x30200f0: + _UNICODE = 'unicode' +else: + _UNICODE = 'UTF-8' + +class Interactive(object): + + def __init__(self): + self.config = OptionConfig() + reload_config(self.config) + self.layman_inst = LaymanAPI(config=self.config) + self.overlay = {} + self.overlays = [] + self.overlays_available = self.layman_inst.get_available() + self.supported_types = self.layman_inst.supported_types().keys() + + def __call__(self, overlay_package=None, path=None): + + if not overlay_package: + for x in range(1, int(self.get_input("How many overlays would you like to create?: "))+1): + print('') + print('Overlay #%(x)s: ' % ({'x': str(x)})) + print('~~~~~~~~~~~~~') + + self.update_required() + print('') + self.get_overlay_components() + ovl = Overlay.Overlay(config=self.config, ovl_dict=self.overlay, ignore=1) + self.overlays.append((self.overlay['name'], ovl)) + else: + ovl_name, ovl = overlay_package + self.overlays.append((ovl_name, ovl)) + + result = self.write(path) + return result + + + def get_input(self, msg): + ''' + py2 py3 compatibility function + to obtain user input. + + @params msg: message prompt for user + @rtype str: input from user + ''' + try: + value = raw_input(msg) + except NameError: + value = input(msg) + + return value + + + def get_ans(self, msg): + ''' + Handles yes/no input + + @params msg: message prompt for user + @rtype boolean: reflects whether the user answered yes or no. + ''' + ans = self.get_input(msg).lower() + + while ans not in ('y', 'yes', 'n', 'no'): + ans = self.get_input('Please respond with [y/n]: ').lower() + + return ans in ('y', 'yes') + + + def check_overlay_type(self, ovl_type): + ''' + Validates overlay type. + + @params ovl_type: str of overlay type + @rtype None or str (if overlay type is valid). + ''' + if ovl_type.lower() in self.supported_types: + return ovl_type.lower() + print('Specified type "%(type)s" not valid.' % ({'type': ovl_type})) + print('Supported types include: %(types)s.'\ + % ({'types': ', '.join(self.supported_types)})) + return None + + + def guess_overlay_type(self, source_uri): + ''' + Guesses the overlay type based on the source given. + + @params source-uri: str of source. + @rtype None or str (if overlay type was guessed correctly). + ''' + + type_checks = copy.deepcopy(self.supported_types) + + #Modify the type checks for special overlay types. + if 'tar' in type_checks: + type_checks.remove(type_checks[type_checks.index('tar')]) + type_checks.insert(len(type_checks), '.tar') + + if 'bzr' in type_checks: + type_checks.remove(self.supported_types[type_checks.index('bzr')]) + type_checks.insert(len(type_checks), 'bazaar') + + for guess in type_checks: + if guess in source_uri: + return guess + + if 'bitbucket.org' in source_uri: + return 'mercurial' + + return None + + + def update_required(self): + ''' + Prompts user for optional components and updates + the required components accordingly. + ''' + # Don't assume they want the same + # info for the next overlay. + self.required = copy.deepcopy(COMPONENT_DEFAULTS) + + for possible in POSSIBLE_COMPONENTS: + if possible not in self.required: + available = self.get_ans("Include %(comp)s for this overlay? [y/n]: " \ + % ({'comp': possible})) + if available: + self.required.append(possible) + + + def get_feeds(self): + ''' + Prompts user for any overlay RSS feeds + and updates overlay dict with values. + ''' + feed_amount = int(self.get_input('How many RSS feeds exist for this overlay?: ')) + feeds = [] + + for i in range(1, feed_amount + 1): + if feed_amount > 1: + feeds.append(self.get_input('Define overlay feed[%(i)s]: '\ + % ({'i': str(i)}))) + else: + feeds.append(self.get_input('Define overlay feed: ')) + + self.overlay['feeds'] = feeds + print('') + + + def get_name(self): + ''' + Prompts user for the overlay name + and updates the overlay dict. + ''' + name = self.get_input('Define overlay name: ') + + while name in self.overlays_available: + print('!!! Overlay name already defined in list of installed overlays.') + name = self.get_input('Please specify a different overlay name: ') + + self.overlay['name'] = name + + + def get_sources(self): + ''' + Prompts user for possible overlay source + information such as type, url, and branch + and then appends the values to the overlay + being created. + ''' + ovl_type = None + source_amount = int(self.get_input('How many different sources,'\ + ' protocols, or mirrors exist for this overlay?: ')) + + self.overlay['sources'] = [] + + for i in range(1, source_amount + 1): + sources = [] + if source_amount > 1: + sources.append(self.get_input('Define source[%(i)s] URL: '\ + % ({'i': str(i)}))) + + ovl_type = self.guess_overlay_type(sources[0]) + correct = self.get_ans('Is %(type)s the correct overlay'\ + ' type?: ' % ({'type': ovl_type})) + while not ovl_type or not correct: + ovl_type = self.check_overlay_type(\ + self.get_input('Please provide overlay'\ + ' type: ')) + correct = True + + sources.append(ovl_type) + if 'branch' in self.required: + sources.append(self.get_input('Define source[%(i)s]\'s '\ + 'branch (if applicable): ' % ({'i': str(i)}))) + else: + sources.append('') + else: + sources.append(self.get_input('Define source URL: ')) + + ovl_type = self.guess_overlay_type(sources[0]) + correct = self.get_ans('Is %(type)s the correct overlay'\ + ' type?: ' % ({'type': ovl_type})) + while not ovl_type or not correct: + ovl_type = self.check_overlay_type(\ + self.get_input('Please provide overlay'\ + ' type: ')) + correct = True + + sources.append(ovl_type) + if 'branch' in self.required: + sources.append(self.get_input('Define source branch (if applicable): ')) + else: + sources.append('') + + self.overlay['sources'].append(sources) + print('') + + + def get_owner(self): + ''' + Prompts user for overlay owner info and + then appends the values to the overlay + being created. + ''' + print('') + self.overlay['owner_name'] = self.get_input('Define owner name: ') + self.overlay['owner_email'] = self.get_input('Define owner email: ') + print('') + + + def get_component(self, component, msg): + ''' + Sets overlay component value. + + @params component: (str) component to be set + @params msg: (str) prompt message for component + ''' + if component not in ('branch', 'type'): + if component in ('feeds', 'name', 'owner', 'sources'): + getattr(self, 'get_%(comp)s' % ({'comp': component}))() + else: + self.overlay[component] = getattr(self, 'get_input')(msg) + + + def get_overlay_components(self): + ''' + Acquires overlay components via user interface. + ''' + self.overlay = {} + + for component in self.required: + self.get_component(component, 'Define %(comp)s: '\ + % ({'comp': component})) + + + def read(self, path): + ''' + Reads overlay.xml files and appends the contents + to be sorted when writing to the file. + + @params path: path to file to be read + ''' + try: + document = ET.parse(path) + except xml.etree.ElementTree.ParseError as error: + msg = 'Interactive.read(); encountered error: %(error)s'\ + % ({'error': error}) + print(msg) + + overlays = document.findall('overlay') + document.findall('repo') + + for overlay in overlays: + ovl_name = overlay.find('name') + ovl = Overlay.Overlay(config=self.config, xml=overlay, ignore=1) + self.overlays.append((ovl_name.text, ovl)) + + + def _sort_to_tree(self): + ''' + Sorts all Overlay objects by overlay name + and converts the sorted overlay objects to + XML that is then appended to the tree. + ''' + self.overlays = sorted(self.overlays) + for overlay in self.overlays: + self.tree.append(overlay[1].to_xml()) + + + def write(self, destination): + ''' + Writes overlay file to desired location. + + @params destination: path & file to write xml to. + @rtype bool: reflects success or failure to write xml. + ''' + if not destination: + filename = self.get_input('Desired overlay file name: ') + filepath = self.config.get_option('overlay_defs') + + if not filename.endswith('.xml'): + filename += ".xml" + + if not filepath.endswith(os.path.sep): + filepath += os.path.sep + + destination = filepath + filename + + self.tree = ET.Element('repositories', version='1.1', encoding=_UNICODE) + + if os.path.isfile(destination): + self.read(destination) + + self._sort_to_tree() + indent(self.tree) + self.tree = ET.ElementTree(self.tree) + + try: + with fileopen(destination, 'w') as xml: + self.tree.write(xml, encoding=_UNICODE) + print('Successfully wrote to: %(path)s' % ({'path': destination})) + return True + + except IOError as e: + print("Writing XML failed: %(error)s" % ({'error': e})) + + return False |