Skip to content

Commit

Permalink
githubplugin: v1.0.0
Browse files Browse the repository at this point in the history
  • Loading branch information
Morg42 committed Nov 7, 2024
1 parent 4cf70f8 commit 6cde447
Show file tree
Hide file tree
Showing 5 changed files with 184 additions and 93 deletions.
71 changes: 66 additions & 5 deletions githubplugin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#########################################################################

import os
from shutil import rmtree
from pathlib import Path

from lib.model.smartplugin import SmartPlugin
Expand Down Expand Up @@ -269,9 +270,6 @@ class GithubPlugin(SmartPlugin):
shng plugins fork and branch, and then setting up a local repo containing
that fork. Additionally, the specified plugin will be soft-linked into the
"live" plugins repo worktree as a private plugin.
At the moment, this is a standalone demonstrator, which will be transformed
into a SmartPlugin later.
"""
PLUGIN_VERSION = '1.0.0'

Expand All @@ -297,7 +295,8 @@ def __init__(self, sh):
# 'link': os.path.join('plugins', f'priv_{plugin}'), # relativer Pfad-/Dateiname des Plugin-Symlinks unterhalb von shng
# 'rel_link_path': os.path.join(wt_path, plugin), # relativer Pfad des Pluginordners "unterhalb" von plugins/
# 'force': False, # vorhandene Dateien überschreiben
# 'repo': repo # git.Repo(path)
# 'repo': repo, # git.Repo(path)
# 'dirty': bool # repo is dirty?
# },
# '<id2>': {...}
# }
Expand Down Expand Up @@ -379,7 +378,8 @@ def read_repos_from_dir(self):
'link': os.path.join('plugins', f'priv_{plugin}'),
'rel_link_path': os.path.join(wt_path, plugin),
'force': False,
'repo': repo
'repo': repo,
'dirty': repo.is_dirty()
}

# add missing ids to repoitem
Expand Down Expand Up @@ -559,6 +559,67 @@ def create_repo(self, name) -> bool:

return True

def remove_plugin(self, name) -> bool:
""" remove plugin link, worktree and if not longer needed, local repo """
if name not in self.repos:
self.logger.warning(f'plugin entry {name} not found.')
return False

# get all data to remove
repo = self.repos[name]
link_path = repo['link']
wt_path = repo['full_wt_path']
repo_path = repo['full_repo_path']
owner = repo['owner']
# check if repo is used by other plugins
last = True
for r in self.repos:
if r == name:
continue
if self.repos[r]["owner"] == owner:
last = False
break

err = []
try:
self.logger.debug(f'removing link {link_path}')
os.remove(link_path)
except Exception as e:
err.append(e)
try:
self.logger.debug(f'removing worktree {wt_path}')
rmtree(wt_path)
except Exception as e:
err.append(e)
try:
self.logger.debug('pruning worktree')
repo['repo'].git.worktree('prune')
except Exception as e:
err.append(e)
if last:
try:
self.logger.debug(f'repo {repo_path} is no longer used, removing')
rmtree(repo_path)
except Exception as e:
err.append(e)

# remove repo entry from plugin dict
del self.repos[name]
del repo

# remove repo entry from repoitem
if self._repoitem is not None:
try:
self._repoitem.dict.delete(wt_path)
except Exception as e:
err.append(e)

if err:
self.logger.warning(f'error(s) occurred while removing plugin: {", ".join(err)}')
return False

return True

#
# github API methods
#
Expand Down
2 changes: 2 additions & 0 deletions githubplugin/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
GitPython
PyGithub
84 changes: 20 additions & 64 deletions githubplugin/user_doc.rst
Original file line number Diff line number Diff line change
@@ -1,91 +1,47 @@

.. index:: Plugins; Pluginname (in Kleinbuchstaben)
.. index:: Pluginname (in Kleinbuchstaben)
.. index:: Plugins; githubplugin
.. index:: githubplugin


===============================
Pluginname (in Kleinbuchstaben)
===============================
============
githubplugin
============


.. comment set image name and extension according to the image file you use for the plugin-logo
Wenn man das Plugin eines anderen Autors ausprobieren oder testen möchte, muss es aus einem fremden Repository von GitHub in die eigene Installation eingebunden werden.

.. image:: webif/static/img/plugin_logo.png
:alt: plugin logo
:width: 300px
:height: 300px
:scale: 50 %
:align: left
Dieses Plugin ermöglicht es komfortabel, fremde Plugins von GitHub zu installieren und wieder zu deinstallieren.

<Hier erfolgt die allgemeine Beschreibung des Zwecks des Plugins>
Auch wenn die Funktionen des Plugins grundsätzlich über Logiken genutzt werden können, erfolgt die Bedienung grundsätzlich über das pluginspezifische Webinterface, das über die Admin-UI von SmartHomeNG zugänglich ist.

Dort können Plugins angezeigt, installiert und entfernt werden. Das Löschen von installierten Plugins, deren git-Verzeichnisse nicht "sauber" sind (veränderte, gelöschte oder hinzugefügte Dateien im git-Index), können nicht über die Weboberfläche entfernt werden. Diese Änderungen müssen erst von Hand rückgängig gemacht oder per commit/push gesichert werden.

Vorsicht: Wenn Änderungen an Fremd-Plugins per `git commit` in den Index übernommen wurden, aber nicht per push oder Pull-Request an GitHub gesendet wurden, können diese beim Löschen des Plugins ggf. unwiderruflich verloren gehen.

Anforderungen
=============

...

Notwendige Software
-------------------

<Hier wird weitere benötigte Software beschrieben. Falls keine weitere Software benötigt wird, kann dieser
Abschnitt entfallen.>

Unterstützte Geräte
-------------------

<Hier werden unterstützte Geräte beschrieben. Falls keine keine speziell zu beschreibenden Geräte unterstützt
werden, kann dieser Abschnitt entfallen.>
Das Plugin benötigt die Python-Pakete GitPython und PyGithub.


Konfiguration
=============

.. comment Den Text **Pluginname (in Kleinbuchstaben)** durch :doc:`/plugins_doc/config/pluginname` ersetzen
Die Plugin Parameter, die Informationen zur Item-spezifischen Konfiguration des Plugins und zur Logik-spezifischen
Konfiguration sind unter **Pluginname (in Kleinbuchstaben)** beschrieben.

Dort findet sich auch die Dokumentation zu Funktionen, die das Plugin evtl. bereit stellt.


Funktionen
----------

<Hier können bei Bedarf ausführliche Beschreibungen zu den Funktionen dokumentiert werden.>

<Sonst diesen Abschnitt löschen>

|
Beispiele
=========

Hier können bei Bedarf Konfigurationsbeispiele dokumentiert werden.

|
Web Interface
=============

<Hier erfolgt die Beschreibung des Web Interfaces>

Tab 1: <Name des Tabs>
----------------------
Das Plugin ist ohne Konfiguration lauffähig.

<Hier wird der Inhalt und die Funktionalität des Tabs beschrieben.>
Optional kann ein GitHub-API-Key hinterlegt werden, um die Anzahl der möglichen GitHub-Zugriffe zu erhöhen.

.. image:: assets/webif_tab1.jpg
:class: screenshot
Die installiereten Fremd-Plugins werden über den Besitzer des GitHub-Repositories, den jeweiligen branch und den Pluginnamen identifiziert. Dazu wird ein Bezeichner nach dem Format `<Besitzer>/<Branch>-<Plugin>` erstellt. Alternativ kann vom Benutzer ein eigener Bezeichner frei gewählt werden. Um diese Bezeichner dauerhaft zu sichern, kann ein Item vom Typ `dict` bestimmt werden. Dieses benötigt nur die Item-Attribute

<Zu dem Tab ist ein Screenshot im Unterverzeichnis ``assets`` des Plugins abzulegen.
.. code:
repoitem: true
cache: true
|
Version History
===============

<In diesem Abschnitt kann die Versionshistorie dokumentiert werden, falls der Plugin Autor dieses möchte.
Diese Abschnitt ist optional.>
Die Plugin Parameter und die Informationen zur Item-spezifischen Konfiguration des Plugins sind unter :doc:`/plugins_doc/config/githubplugin` beschrieben.

Dort findet sich auch die Dokumentation zu Funktionen, die das Plugin evtl. bereit stellt.

20 changes: 19 additions & 1 deletion githubplugin/webif/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,18 @@ def __init__(self, webif_dir, plugin):
self.tplenv = self.init_template_environment()

@cherrypy.expose
def index(self):
def index(self, action=None):
"""
Build index.html for cherrypy
Render the template and return the html file to be delivered to the browser
:return: contents of the template after beeing rendered
"""
if action == 'rescan':
self.plugin.read_repos_from_dir()
raise cherrypy.HTTPRedirect(cherrypy.url())

# try to get the webif pagelength from the module.yaml configuration
pagelength = self.plugin.get_parameter_value('webif_pagelength')

Expand Down Expand Up @@ -119,6 +123,20 @@ def updatePlugins(self):
if plugins != {}:
return {"operation": "request", "result": "success", "data": plugins}

@cherrypy.expose
@cherrypy.tools.json_out()
@cherrypy.tools.json_in()
def removePlugin(self):
json = cherrypy.request.json
name = json.get("name")
if name is None or name == '' or name not in self.plugin.repos:
msg = f'Repo {name} nicht vorhanden.'
self.logger.error(msg)
return {"operation": "request", "result": "error", "data": msg}

if self.plugin.remove_plugin(name):
return {"operation": "request", "result": "success"}

@cherrypy.expose
@cherrypy.tools.json_out()
@cherrypy.tools.json_in()
Expand Down
Loading

0 comments on commit 6cde447

Please sign in to comment.