Ejemplo n.º 1
0
    def commit_push(self,
                    tree,
                    message,
                    remote_name='origin',
                    username=None,
                    password=None):
        """Commits and pushes staged changes in the tree
        TODO: Ensure that the config keys are correct
        4/13/20 21 - seems to be working

        Args:
            tree (Oid): Oid id created from repositiory index (ex: repo.index.write_tree()) containing the tracked file changes (proably)
            message (string): commit message
            remote_name (string, optional): name of the remote to push to
            username (string, optional): username of user account used for pushing
            password (string, optional): password of user account used for pushing
        """
        if username is None:
            username = Config.get('user/username')
        if password is None:
            password = Config.get('user/password')
        # Create commit on current branch, parent is current commit, author and commiter is the default signature
        self.create_commit(self.head.name, self.default_signature,
                           self.default_signature, message, tree,
                           [self.head.target])
        self.push(remote_name, username, password)
Ejemplo n.º 2
0
 def install(name, force=False):
     """Install plugin by name"""
     # Log installing plugin
     Logger.debug(f'MEG Plugins: Installing plugin <{name}>')
     # Get the available plugin by name
     available_plugin = PluginManager.get_available(name)
     if available_plugin is None:
         # Log failed to install plugin
         Logger.warning(f'MEG Plugins: Failed to install plugin <{name}>')
         return False
     # Log updating plugin cache information
     Logger.debug(
         f'MEG Plugins: Found plugin cache information <{available_plugin.path()}>'
     )
     PluginManager._log_plugin(available_plugin)
     # Get installed plugin by name, if present
     plugin = PluginManager.get(name)
     if plugin is not None:
         # Check plugin is up to date or forcing installation
         if available_plugin.compare_versions(plugin) <= 0 and not force:
             return True
     # Get the plugins cache path
     plugins_path = Config.get('path/plugins')
     # Get the plugin installation path
     plugin_basename = os.path.basename(available_plugin.path())
     plugin_path = os.path.join(plugins_path, plugin_basename)
     try:
         # Remove the previous plugin path, if necessary
         if not Config.remove_path(plugin_path):
             return False
         # Open the local plugin cache repository
         cache_path = os.path.join(Config.get('path/plugin_cache'),
                                   'remote',
                                   PluginManager.DEFAULT_BARE_REPO_PATH)
         cache = GitManager.open(cache_path, bare=True)
         if cache is None:
             raise GitException(
                 f'Could not open local plugin cache <{cache_path}>')
         # Log installing plugin
         Logger.debug(f'MEG Plugins: Installing plugin <{plugin_path}>')
         # Install plugin by checking out
         cache.checkout_head(directory=plugins_path,
                             paths=[plugin_basename + '/*'])
         # Load (or update) plugin information
         plugin = PluginManager._update(plugin_path, force)
         if plugin is not None:
             # Log plugin information
             PluginManager._log_plugin(plugin)
             # Setup plugin dependencies
             plugin.setup().check_returncode()
             # Add the plugin
             PluginManager.__instance['plugins'][plugin.name()] = plugin
     except Exception as e:
         # Log that loading the plugin cache information failed
         Logger.warning(f'MEG Plugins: {e}')
         Logger.warning(
             f'MEG Plugins: Could not load information for plugin <{plugin_path}>'
         )
         return False
     return True
Ejemplo n.º 3
0
 def __init__(self, **kwargs):
     """UI manager constructor."""
     # Load window resource if needed
     if UIManager.__widgets is None:
         # Load the resource setup from the package
         UIManager.__widgets = uic.loadUiType(pkg_resources.resource_filename(__name__, UIManager.UI_FILE))
     # Initialize the super class
     super().__init__(**kwargs)
     # Setup window resource
     UIManager.__widgets[0]().setupUi(self)
     # Set the window panel stack
     self._panels = []
     self._current_panel = None
     self._current_popup = None
     # Set handler for closing a panel
     self._panel = self.findChild(QtWidgets.QTabWidget, 'panelwidget')
     self._panel.tabCloseRequested.connect(self.remove_view_by_index)
     self._panel.currentChanged.connect(self._show_view_by_index)
     # Get status widget
     self._statusbar = self.findChild(QtWidgets.QStatusBar, 'statusbar')
     # Set handlers for main buttons
     # TODO: Add more handlers for these
     self._action_clone = self.findChild(QtWidgets.QAction, 'action_Clone')
     self._action_clone.triggered.connect(App.open_clone_panel)
     self._action_open = self.findChild(QtWidgets.QAction, 'action_Open')
     self._action_open.triggered.connect(App.open_repo_panel)
     self._action_quit = self.findChild(QtWidgets.QAction, 'action_Quit')
     self._action_quit.triggered.connect(App.quit)
     self._action_about = self.findChild(QtWidgets.QAction, 'action_About')
     self._action_about.triggered.connect(App.open_about)
     self._action_preferences = self.findChild(QtWidgets.QAction, 'action_Preferences')
     self._action_preferences.triggered.connect(App.open_prefs_panel)
     self._action_manage_plugins = self.findChild(QtWidgets.QAction, 'action_Manage_Plugins')
     self._action_manage_plugins.triggered.connect(App.open_plugins_panel)
     # Set the default title
     self.set_title()
     # Set the icon
     icon_path = App.get_icon()
     if icon_path is not None:
         self.setWindowIcon(QtGui.QIcon(icon_path))
     # Restore the state from the configuration if needed
     window_state = Config.get('window/state', 'none')
     state = self.windowState()
     if window_state == 'maximized':
         state &= ~(QtCore.Qt.WindowMinimized | QtCore.Qt.WindowFullScreen)
         state |= QtCore.Qt.WindowMaximized
     elif window_state == 'minimized':
         state &= ~(QtCore.Qt.WindowMaximized | QtCore.Qt.WindowFullScreen)
         state |= QtCore.Qt.WindowMinimized
     elif window_state == 'fullscreen':
         state &= ~(QtCore.Qt.WindowMinimized | QtCore.Qt.WindowMaximized)
         state |= QtCore.Qt.WindowFullScreen
     self.setWindowState(state)
     # Restore the window geometry from the configuration if needed
     geometry = Config.get('window/geometry', None)
     if isinstance(geometry, list) and len(geometry) == 4:
         self.setGeometry(geometry[0], geometry[1], geometry[2], geometry[3])
Ejemplo n.º 4
0
 def clone(repo_url,
           repo_path=None,
           checkout_branch=None,
           bare=False,
           *args,
           **kwargs):
     """Clone a remote git repository to a local repository"""
     # Check there is git repository manager instance
     if GitManager.__instance is None:
         GitManager()
     if GitManager.__instance is not None:
         # Log cloning repo
         Logger.debug(
             f'MEG Git: Cloning git repository <{repo_url}> to <{repo_path}>'
         )
         try:
             # Get the repository path if not provided
             if repo_path is None:
                 # Get the root path in the following order:
                 #  1. The configured repositories directory path
                 #  2. The configured user directory path
                 #  3. The current working directory path
                 repo_prefix = Config.get(
                     'path/repos', Config.get('path/user', os.curdir))
                 # Append the name of the repository to the path
                 if isinstance(repo_url, str):
                     repo_path = os.path.join(repo_prefix,
                                              pathlib.Path(repo_url).stem)
                 elif isinstance(repo_url, pathlib.Path):
                     repo_path = os.path.join(repo_prefix, repo_url.stem)
                 else:
                     raise GitException(
                         f'No local repository path was provided and the path could not be determined from the remote <{repo_url}>'
                     )
             # Clone the repository by creating a repository instance
             return GitRepository(repo_path,
                                  repo_url,
                                  checkout_branch=checkout_branch,
                                  bare=bare,
                                  *args,
                                  **kwargs)
         except Exception as e:
             # Log that cloning the repo failed
             Logger.warning(f'MEG Git: {e}')
             Logger.warning(
                 f'MEG Git: Could not clone git repository <{repo_url}> to <{repo_path}>'
             )
     return None
Ejemplo n.º 5
0
 def update_cache():
     """Update cached plugin information"""
     # Check there is plugin manager instance
     update = False
     if PluginManager.__instance is None:
         PluginManager(False)
         update = True
     if PluginManager.__instance is None:
         return False
     # Log updating plugin information
     Logger.debug(f'MEG Plugins: Updating plugin cache')
     # Clear previous plugin cache information
     if 'plugin_cache' not in PluginManager.__instance or not isinstance(
             PluginManager.__instance['plugin_cache'], dict):
         PluginManager.__instance['plugin_cache'] = {}
     # Get the plugin cache path and create if needed
     cache_path = os.path.join(Config.get('path/plugin_cache'), 'remote')
     os.makedirs(cache_path, exist_ok=True)
     # Open or clone the plugins repository with the plugin cache path
     cache = GitManager.open_or_clone(
         os.path.join(cache_path, PluginManager.DEFAULT_BARE_REPO_PATH),
         Config.get('plugins/url', PluginManager.DEFAULT_CACHE_URL),
         bare=True)
     if cache is None:
         # Log that loading the plugin cache information failed
         Logger.warning(
             f'MEG Plugins: Could not update plugin cache information')
         return False
     try:
         # Log updating plugin cache information
         Logger.debug(f'MEG Plugins: Fetching plugin cache information')
         # Fetch and update the plugin cache
         cache.fetch_all()
         fetch_head = cache.lookup_reference('FETCH_HEAD')
         if fetch_head is not None:
             cache.head.set_target(fetch_head.target)
         # Checkout the plugin information files
         cache.checkout_head(
             directory=cache_path,
             paths=['*/' + PluginInformation.DEFAULT_PLUGIN_INFO_PATH])
     except Exception as e:
         # Log that loading the plugin cache information failed
         Logger.warning(f'MEG Plugins: {e}')
         Logger.warning(
             f'MEG Plugins: Could not update plugin cache information')
         return False
     # Log updating plugin cache information
     return PluginManager._update_cache(cache_path, update)
Ejemplo n.º 6
0
 def _install_archive_list(archive, archive_list, force):
     """Install plugin from archive list"""
     # Get the plugin cache path and create if needed
     plugins_path = os.path.join(Config.get('path/plugin_cache'), 'local')
     os.makedirs(plugins_path, exist_ok=True)
     # Extract found plugins from archive to install
     retval = True
     for plugin_name, plugin_list in archive_list:
         # Get the plugin path
         plugin_path = os.path.join(plugins_path, plugin_name)
         try:
             # Remove the previous plugin path, if necessary
             if Config.remove_path(plugin_path):
                 # Log caching plugin
                 Logger.debug(f'MEG Plugins: Locally caching plugin <{plugin_path}>')
                 # Extract each plugin from archive to install
                 archive.extractall(plugin_path, plugin_list)
                 # Install cached plugin
                 retval = PluginManager.install_path(plugin_path, force) and retval
             else:
                 retval = False
         except Exception as e:
             # Log that caching plugin locally failed
             Logger.warning(f'MEG Plugins: {e}')
             Logger.warning(f'MEG Plugins: Failed to locally cache plugin <{plugin_path}>')
             retval = False
     return retval
Ejemplo n.º 7
0
 def __init__(self, update=True, **kwargs):
     """Plugin manager constructor"""
     # Check if there is already a plugin manager instance
     if PluginManager.__instance is not None:
         # Except if another instance is created
         raise PluginException(self.__class__.__name__ + " is a singleton!")
     else:
         # Initialize super class constructor
         super().__init__(**kwargs)
         # Set this as the current plugin manager instance
         PluginManager.__instance = self
         # Set plugin and cache paths in python path for import
         sys.path.append(Config.get('path/plugins'))
         sys.path.append(Config.get('path/cache'))
         # Load information about plugins
         if update:
             PluginManager.update()
Ejemplo n.º 8
0
 def enable(self):
     """Enable the plugin"""
     # Get the enabled plugins
     enabled_plugins = Config.get('plugins', [])
     # Check if this plugin is already enabled
     if isinstance(enabled_plugins, list) and not self.name() in enabled_plugins:
         # Enable this plugin
         enabled_plugins.append(self.name())
         Config.set('plugins', enabled_plugins)
Ejemplo n.º 9
0
    def sync(self, remote_name='origin', username=None, password=None):
        """Pulls and then pushes, merge conflicts resolved by pull

        Args:
            username (string, optional): username of user account used for pushing
            password (string, optional): password of user account used for pushing
        """
        if username is None:
            username = Config.get('user/username')
        if password is None:
            password = Config.get('user/password')
        self.__permissions.save()
        self.__locking.save()
        self.pull(remote_name, username=username, password=password)
        if self.isChanged(username):
            self.stageChanges(username)
            self.create_commit('HEAD', self.default_signature,
                               self.default_signature, "MEG SYNC",
                               self.index.write_tree(), [self.head.target])
            self.push(remote_name, username=username, password=password)
Ejemplo n.º 10
0
 def disable(self):
     """Disable the plugin"""
     # Get the enabled plugins
     enabled_plugins = Config.get('plugins', [])
     # Check if this plugin is already enabled
     if isinstance(enabled_plugins, list) and self.name() in enabled_plugins:
         # Disable this plugin
         enabled_plugins.remove(self.name())
         Config.set('plugins', enabled_plugins)
         # Unload plugin
         self.unload()
Ejemplo n.º 11
0
 def on_show(self):
     """Showing the panel."""
     # Load the repos
     repos = Config.get('repos')
     repos = [
         QtWidgets.QTreeWidgetItem(
             [os.path.basename(repo['path']), repo['path'], repo['url']])
         for repo in repos if repo['path']
     ]
     self._tree_widget.clear()
     self._tree_widget.addTopLevelItems(repos)
Ejemplo n.º 12
0
 def save_repo_entry(repo_path, repo_url=None):
     repos = Config.get('repos', defaultValue=[])
     duplicate_found = False
     for index, repo in enumerate(repos):
         if repo['path'] == repo_path:
             repos[index]['url'] = repo_url
             duplicate_found = True
             break
     if not duplicate_found:
         repos.append({'path': repo_path, 'url': repo_url})
     Config.set('repos', repos)
     Config.save()
Ejemplo n.º 13
0
    def push(self, remote_name='origin', username=None, password=None):
        """Pushes current commits
        4/13/20 21 - seems to be working

        Args:
            remote_name (string, optional): name of the remote to push to
            username (string, optional): username of user account used for pushing
            password (string, optional): password of user account used for pushing
        """
        if username is None:
            username = Config.get('user/username')
        if password is None:
            password = Config.get('user/password')
        creds = pygit2.UserPass(username, password)
        remote = self.remotes[remote_name]
        remote.credentials = creds
        try:
            remote.push([self.head.name],
                        callbacks=pygit2.RemoteCallbacks(credentials=creds))
        except GitError as e:
            Logger.warning(e)
            Logger.warning("MEG Git Repository: Failed to push commit")
Ejemplo n.º 14
0
 def install_path(path, force=False):
     """Install plugin from local path"""
     # Check there is plugin manager instance
     if PluginManager.__instance is None:
         PluginManager()
     if PluginManager.__instance is not None:
         # Check if path does not exist
         if os.path.exists(path) and os.path.isdir(path):
             # Log trying to load plugin information from path
             Logger.debug(
                 f'MEG Plugins: Installing plugin from path <{path}>')
             try:
                 # Get the plugin information for the path if no other version is installed
                 plugin = PluginManager._update(path, force)
                 if plugin is not None:
                     # Log plugin information
                     PluginManager._log_plugin(plugin)
                     # Log trying to load plugin information from path
                     Logger.debug(
                         f'MEG Plugins: Installing plugin <{plugin.name()}>'
                     )
                 elif not force:
                     # The same version exists and no force so this is installed
                     return True
                 # Get the installed plugin path
                 plugin_path = os.path.join(Config.get('path/plugins'),
                                            os.path.basename(path))
                 # Remove the previous plugin path, if necessary
                 if not Config.remove_path(plugin_path):
                     return False
                 # Copy the path to the plugins directory
                 shutil.copytree(path, plugin_path)
                 # Load (or update) plugin information
                 plugin = Plugin(plugin_path)
                 if plugin is not None:
                     # Log plugin information
                     PluginManager._log_plugin(plugin)
                     # Setup plugin dependencies
                     plugin.setup().check_returncode()
                     # Add the plugin
                     PluginManager.__instance['plugins'][
                         plugin.name()] = plugin
                 return True
             except Exception as e:
                 # Log that installing the plugin from the path failed
                 Logger.warning(f'MEG Plugins: {e}')
                 Logger.warning(
                     f'MEG Plugins: Failed to install plugin from path <{path}>'
                 )
     return False
Ejemplo n.º 15
0
 def setup(self):
     """Install the plugin dependencies"""
     # Setup plugin dependencies
     depends = self.dependencies()
     if isinstance(depends, list) and len(depends) > 0:
         # Execute PIP to install the dependencies
         if os.name == 'nt':
             cmd = ['py', '-3']
         else:
             cmd = ['python3']
         cmd.extend(['-m', 'pip', 'install', '--target', Config.get('path/cache'), '--upgrade'])
         cmd.extend(depends)
         return subprocess.run(cmd, encoding='utf-8', stdin=None, stdout=subprocess.PIPE, stderr=None, startupinfo=subprocess.STARTUPINFO(wShowWindow=False))
     # No dependencies so return success status
     return subprocess.CompletedProcess([], 0)
Ejemplo n.º 16
0
 def load_enabled():
     """Load enabled plugins"""
     # Check there is plugin manager instance
     if PluginManager.__instance is None:
         PluginManager()
     if PluginManager.__instance is None:
         return False
     # Get the enabled plugin names
     enabled_plugins = Config.get('plugins', [])
     if not isinstance(enabled_plugins, list):
         return False
     retval = True
     # For each enabled plugin, load the plugin
     for name in enabled_plugins:
         retval = PluginManager.load(name) and retval
     return retval
Ejemplo n.º 17
0
 def isChanged(self, username):
     """Are there local changes from the last commit
     Only counts changes alowed by locking and permission module commitable files
     """
     mask = pygit2.GIT_STATUS_WT_DELETED | pygit2.GIT_STATUS_WT_RENAMED | pygit2.GIT_STATUS_WT_MODIFIED | pygit2.GIT_STATUS_WT_NEW
     for path, status in self.status().items():
         if status & mask == 0:
             continue
         lockEntry = self.__locking.findLock(path)
         if (((lockEntry is None
               or lockEntry["user"] == Config.get('user/username'))
              and self.__permissions.can_write(username, path))
                 or path == Locking.LOCKFILE_PATH
                 or path == Permissions.PERMISSION_PATH):
             return True
     return False
Ejemplo n.º 18
0
 def update():
     """Update local plugin information"""
     # Check there is plugin manager instance
     if PluginManager.__instance is None:
         PluginManager(False)
     if PluginManager.__instance is None:
         return False
     # Log updating plugin information
     Logger.debug(
         f'MEG Plugins: Updating plugin information {Config.get("path/plugins")}'
     )
     # Unload all plugins
     PluginManager.unload_all()
     # Clear previous plugin information
     if 'plugins' not in PluginManager.__instance or not isinstance(
             PluginManager.__instance['plugins'], dict):
         PluginManager.__instance['plugins'] = {}
     # Obtain the available plugins from the plugins path
     retval = True
     for (path, dirs, files) in os.walk(Config.get('path/plugins')):
         # For each plugin load the information
         for d in dirs:
             # Log debug information about the plugin
             plugin_path = os.path.join(path, d)
             Logger.debug(
                 f'MEG Plugins: Found plugin information <{plugin_path}>')
             try:
                 # Get the plugin path and load plugin information
                 plugin = PluginManager._update(plugin_path)
                 if plugin is not None:
                     # Log plugin information
                     PluginManager._log_plugin(plugin)
                     # Add the plugin
                     PluginManager.__instance['plugins'][
                         plugin.name()] = plugin
             except Exception as e:
                 # Log that loading the plugin information failed
                 Logger.warning(f'MEG Plugins: {e}')
                 Logger.warning(
                     f'MEG Plugins: Could not load information for plugin <{plugin_path}>'
                 )
                 # Do not say there was an error if the file does not exists
                 if not isinstance(e, OSError) or e.errno != errno.ENOENT:
                     retval = False
         # Do not actually walk the directory tree, only get directories directly under plugins path
         break
     return retval
Ejemplo n.º 19
0
 def clean_plugin_cache():
     """Clean plugin cache"""
     # Remove the plugin cache directory or the block file
     return Config.remove_path(Config.get('path/plugin_cache'))
Ejemplo n.º 20
0
 def clean_plugins():
     """Clean plugins"""
     # Remove the plugins directory or the block file
     return Config.remove_path(Config.get('path/plugins'))
Ejemplo n.º 21
0
 def clean_cache():
     """Clean dependency cache"""
     # Remove the cache directory or the block file
     return Config.remove_path(Config.get('path/cache'))
Ejemplo n.º 22
0
 def enabled(self):
     """Get if plugin is enabled"""
     # Check if plugin is enabled
     enabled_plugins = Config.get('plugins', [])
     return isinstance(enabled_plugins,
                       list) and self.name() in enabled_plugins
Ejemplo n.º 23
0
    def pull(self,
             remote_name='origin',
             fail_on_conflict=False,
             username=None,
             password=None):
        """Pull and merge
        Merge is done fully automaticly, currently uses 'ours' on conflicts

        Args:
            remote_ref_name (string): name of reference to the remote being pulled from
        """
        if username is None:
            username = Config.get('user/username')
        if password is None:
            password = Config.get('user/password')
        self.__permissions.save()
        self.__locking.save()
        # Find state of both references have
        self.fetch_all()
        remoteId = self.lookup_reference("FETCH_HEAD").resolve().target
        localId = self.lookup_reference("HEAD").resolve().target
        ahead, behind = self.ahead_behind(localId, remoteId)
        # Pull only required if we are behind
        if behind > 0:
            # If changes, commit and prepare for merge
            if self.isChanged(username):
                self.stageChanges(username)
                self.create_commit('HEAD', self.default_signature,
                                   self.default_signature, "MEG PULL OWN",
                                   self.index.write_tree(), [self.head.target])
            # Find the kind of required merge
            mergeState, _ = self.merge_analysis(remoteId)
            # Preform merge
            if mergeState & pygit2.GIT_MERGE_ANALYSIS_FASTFORWARD:
                # Fastforward and checkout remote
                self.checkout_tree(self.get(remoteId))
                self.head.set_target(remoteId)
                self.checkout_head()
            elif mergeState & pygit2.GIT_MERGE_ANALYSIS_NORMAL:
                # Preform painful merge
                if fail_on_conflict:
                    self.state_cleanup()
                    return False
                # Find files not to staged (because lock, permissions,...) and discard changes so merging can occur
                badPaths = [
                    entry.path for entry in self.stageChanges(username)
                ]
                self.checkout_head(strategy=pygit2.GIT_CHECKOUT_FORCE,
                                   paths=badPaths)
                # Merge will stage changes automaticly and find conflicts
                self.merge(remoteId)
                if self.index.conflicts is not None:
                    self.resolveLockingPermissionsMerge()
                    self.resolveGeneralMerge(username)
                else:
                    self.__permissions.load()
                    self.__locking.load()
                # Check there are no merge conflicts before committing
                if self.index.conflicts is None or len(
                        self.index.conflicts) == 0:
                    # Commit the merge
                    self.index.add_all()
                    self.create_commit('HEAD', self.default_signature,
                                       self.default_signature, "MEG MERGE",
                                       self.index.write_tree(),
                                       [self.head.target, remoteId])
                self.push(remote_name, username, password)
            self.state_cleanup()
        return True