From 93d291887a3a92c38512bc1d3055dda979bd8ebe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jorge=20Sen=C3=ADn?= Date: Sun, 17 Nov 2019 03:38:33 +0100 Subject: [PATCH 01/10] Extend cli commands with plugins Added a search for plugins modules that contains a `def get_commands()` that returns a clic.Command object. This object is merged with others commands to populate the cli. An example file that should be placed at `~/.piku/plugins/postgres/__init__.py` ``` import click @click.group() def postgres(): """Postgres command plugin""" pass @postgres.command("postgres:create") @click.argument('name') @click.argument('user') @click.argument('password') def postgres_create(): """Postgres create a database""" pass @postgres.command("postgres:drop") @click.argument('name') def postgres_drop(): """Postgres drops a database""" pass @postgres.command("postgres:import") @click.argument('name') def postgres_drop(): """Postgres import a database""" pass @postgres.command("postgres:dump") @click.argument('name') def postgres_drop(): """Postgres dumps a database SQL""" pass def cli_commands(): return postgres ``` --- piku.py | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/piku.py b/piku.py index e90d1bf..af009dc 100755 --- a/piku.py +++ b/piku.py @@ -8,7 +8,8 @@ try: except AssertionError: exit("Piku requires Python 3.5 or above") -from click import argument, command, group, get_current_context, option, secho as echo, pass_context +from importlib import import_module +from click import argument, command, group, get_current_context, option, secho as echo, pass_context, CommandCollection from collections import defaultdict, deque from datetime import datetime from fcntl import fcntl, F_SETFL, F_GETFL @@ -17,11 +18,12 @@ from hashlib import md5 from json import loads from multiprocessing import cpu_count from os import chmod, getgid, getuid, symlink, unlink, remove, stat, listdir, environ, makedirs, O_NONBLOCK -from os.path import abspath, basename, dirname, exists, getmtime, join, realpath, splitext +from os.path import abspath, basename, dirname, exists, getmtime, join, realpath, splitext, isdir from re import sub from shutil import copyfile, rmtree, which from socket import socket, AF_INET, SOCK_STREAM from sys import argv, stdin, stdout, stderr, version_info, exit +from sys import path as sys_path from stat import S_IRUSR, S_IWUSR, S_IXUSR from subprocess import call, check_output, Popen, STDOUT, PIPE from tempfile import NamedTemporaryFile @@ -41,6 +43,7 @@ if 'sbin' not in environ['PATH']: PIKU_ROOT = environ.get('PIKU_ROOT', join(environ['HOME'],'.piku')) PIKU_BIN = join(environ['HOME'],'bin') PIKU_SCRIPT = realpath(__file__) +PLUGIN_ROOT = abspath(join(PIKU_ROOT, "plugins")) APP_ROOT = abspath(join(PIKU_ROOT, "apps")) ENV_ROOT = abspath(join(PIKU_ROOT, "envs")) GIT_ROOT = abspath(join(PIKU_ROOT, "repos")) @@ -1357,6 +1360,19 @@ def cmd_git_receive_pack(app): # Handle the actual receive. We'll be called with 'git-hook' after it happens call('git-shell -c "{}" '.format(argv[1] + " '{}'".format(app)), cwd=GIT_ROOT, shell=True) +def _get_plugin_commands(path): + sys_path.append(abspath(path)) + + cli_commands = [] + for item in listdir(path): + module_path = join(path, item) + if not isdir(module_path): + continue + module = import_module(item) + if hasattr(module, 'cli_commands'): + cli_commands.append(module.cli_commands()) + + return cli_commands @piku.command("help") @pass_context @@ -1366,4 +1382,7 @@ def cmd_help(ctx): if __name__ == '__main__': - piku() + cli_commands = _get_plugin_commands(path=PLUGIN_ROOT) + cli_commands.append(piku) + cli = CommandCollection(sources=cli_commands) + cli() From 480d2858826a53d180abaf3a792aa5e35d792c1f Mon Sep 17 00:00:00 2001 From: Chris McCormick Date: Tue, 26 Nov 2019 12:20:25 +0800 Subject: [PATCH 02/10] Guard against non-existent plugins dir. --- piku.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/piku.py b/piku.py index c93a96c..f33f924 100755 --- a/piku.py +++ b/piku.py @@ -1386,13 +1386,14 @@ def _get_plugin_commands(path): sys_path.append(abspath(path)) cli_commands = [] - for item in listdir(path): - module_path = join(path, item) - if not isdir(module_path): - continue - module = import_module(item) - if hasattr(module, 'cli_commands'): - cli_commands.append(module.cli_commands()) + if isdir(path): + for item in listdir(path): + module_path = join(path, item) + if not isdir(module_path): + continue + module = import_module(item) + if hasattr(module, 'cli_commands'): + cli_commands.append(module.cli_commands()) return cli_commands From 9e5d56a4ca978e98c56a4f00221f4ecfe5d7e966 Mon Sep 17 00:00:00 2001 From: Chris McCormick Date: Tue, 26 Nov 2019 12:23:35 +0800 Subject: [PATCH 03/10] Guard against plugin modules containing errors. --- piku.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/piku.py b/piku.py index f33f924..d312277 100755 --- a/piku.py +++ b/piku.py @@ -1389,11 +1389,13 @@ def _get_plugin_commands(path): if isdir(path): for item in listdir(path): module_path = join(path, item) - if not isdir(module_path): - continue - module = import_module(item) - if hasattr(module, 'cli_commands'): - cli_commands.append(module.cli_commands()) + if isdir(module_path): + try: + module = import_module(item) + except: + module = None + if hasattr(module, 'cli_commands'): + cli_commands.append(module.cli_commands()) return cli_commands From 8882bb5f61914b16bfab8928663c1c8efc2e1ead Mon Sep 17 00:00:00 2001 From: Chris McCormick Date: Tue, 26 Nov 2019 12:26:31 +0800 Subject: [PATCH 04/10] Fix flake8 issues. --- piku.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/piku.py b/piku.py index d312277..ccb13ea 100755 --- a/piku.py +++ b/piku.py @@ -9,7 +9,6 @@ except AssertionError: exit("Piku requires Python 3.5 or above") from importlib import import_module -from click import argument, command, group, get_current_context, option, secho as echo, pass_context, CommandCollection from collections import defaultdict, deque from fcntl import fcntl, F_SETFL, F_GETFL from glob import glob @@ -20,10 +19,8 @@ from os import chmod, getgid, getuid, symlink, unlink, remove, stat, listdir, en from os.path import abspath, basename, dirname, exists, getmtime, join, realpath, splitext, isdir from re import sub from shutil import copyfile, rmtree, which -from socket import socket, AF_INET, SOCK_STREAM from sys import argv, stdin, stdout, stderr, version_info, exit, path as sys_path from pwd import getpwuid -from shutil import copyfile, rmtree, which from socket import socket, AF_INET, SOCK_STREAM from stat import S_IRUSR, S_IWUSR, S_IXUSR from subprocess import call, check_output, Popen, STDOUT @@ -32,7 +29,7 @@ from time import sleep from traceback import format_exc from urllib.request import urlopen -from click import argument, group, secho as echo, pass_context +from click import argument, command, group, get_current_context, option, secho as echo, pass_context, CommandCollection # === Make sure we can access all system binaries === @@ -258,8 +255,8 @@ def parse_procfile(filename): if line.startswith("#") or not line: continue try: - kind, command = map(lambda x: x.strip(), line.split(":", 1)) - workers[kind] = command + kind, command_line = map(lambda x: x.strip(), line.split(":", 1)) + workers[kind] = command_line except Exception: echo("Warning: unrecognized Procfile entry '{}' at line {}".format(line, line_number), fg='yellow') if len(workers) == 0: From ede54d9563a76f59aa2a1104435e285bd9c2c289 Mon Sep 17 00:00:00 2001 From: Chris McCormick Date: Tue, 26 Nov 2019 12:35:11 +0800 Subject: [PATCH 05/10] Fixed further linting issues. --- piku.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/piku.py b/piku.py index ccb13ea..d6e7ab4 100755 --- a/piku.py +++ b/piku.py @@ -29,7 +29,7 @@ from time import sleep from traceback import format_exc from urllib.request import urlopen -from click import argument, command, group, get_current_context, option, secho as echo, pass_context, CommandCollection +from click import argument, group, secho as echo, pass_context, CommandCollection # === Make sure we can access all system binaries === @@ -255,8 +255,8 @@ def parse_procfile(filename): if line.startswith("#") or not line: continue try: - kind, command_line = map(lambda x: x.strip(), line.split(":", 1)) - workers[kind] = command_line + kind, command = map(lambda x: x.strip(), line.split(":", 1)) + workers[kind] = command except Exception: echo("Warning: unrecognized Procfile entry '{}' at line {}".format(line, line_number), fg='yellow') if len(workers) == 0: @@ -1379,6 +1379,7 @@ def cmd_git_upload_pack(app): # Handle the actual receive. We'll be called with 'git-hook' after it happens call('git-shell -c "{}" '.format(argv[1] + " '{}'".format(app)), cwd=GIT_ROOT, shell=True) + def _get_plugin_commands(path): sys_path.append(abspath(path)) @@ -1396,6 +1397,7 @@ def _get_plugin_commands(path): return cli_commands + @piku.command("help") @pass_context def cmd_help(ctx): From 2dbc5d0233919913289d988598cb452f1c4c427d Mon Sep 17 00:00:00 2001 From: Chris McCormick Date: Tue, 26 Nov 2019 12:39:01 +0800 Subject: [PATCH 06/10] Remove bare exception (linting fix). --- piku.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/piku.py b/piku.py index d6e7ab4..f394290 100755 --- a/piku.py +++ b/piku.py @@ -1390,7 +1390,7 @@ def _get_plugin_commands(path): if isdir(module_path): try: module = import_module(item) - except: + except Exception: module = None if hasattr(module, 'cli_commands'): cli_commands.append(module.cli_commands()) From 3fcb8792a51a767de29743b0ef05fd249ecaa3ec Mon Sep 17 00:00:00 2001 From: Chris McCormick Date: Wed, 27 Nov 2019 21:34:12 +0800 Subject: [PATCH 07/10] Fix shifting import lines around. --- piku.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/piku.py b/piku.py index f394290..d01e097 100755 --- a/piku.py +++ b/piku.py @@ -13,17 +13,17 @@ from collections import defaultdict, deque from fcntl import fcntl, F_SETFL, F_GETFL from glob import glob from grp import getgrgid +from pwd import getpwuid from json import loads from multiprocessing import cpu_count -from os import chmod, getgid, getuid, symlink, unlink, remove, stat, listdir, environ, makedirs, O_NONBLOCK from os.path import abspath, basename, dirname, exists, getmtime, join, realpath, splitext, isdir +from os import chmod, getgid, getuid, symlink, unlink, remove, stat, listdir, environ, makedirs, O_NONBLOCK from re import sub from shutil import copyfile, rmtree, which -from sys import argv, stdin, stdout, stderr, version_info, exit, path as sys_path -from pwd import getpwuid from socket import socket, AF_INET, SOCK_STREAM from stat import S_IRUSR, S_IWUSR, S_IXUSR from subprocess import call, check_output, Popen, STDOUT +from sys import argv, stdin, stdout, stderr, version_info, exit, path as sys_path from tempfile import NamedTemporaryFile from time import sleep from traceback import format_exc From 57b60ca73542f7a6776e177506e74d1bd426f584 Mon Sep 17 00:00:00 2001 From: Chris McCormick Date: Wed, 27 Nov 2019 21:51:10 +0800 Subject: [PATCH 08/10] Reordering imports for cleaner merge. --- piku.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/piku.py b/piku.py index af1d0cd..ac25f1f 100755 --- a/piku.py +++ b/piku.py @@ -12,10 +12,10 @@ from importlib import import_module from collections import defaultdict, deque from fcntl import fcntl, F_SETFL, F_GETFL from glob import glob +from os import chmod, getgid, getuid, symlink, unlink, remove, stat, listdir, environ, makedirs, O_NONBLOCK +from os.path import abspath, basename, dirname, exists, getmtime, join, realpath, splitext from grp import getgrgid from pwd import getpwuid -from os.path import abspath, basename, dirname, exists, getmtime, join, realpath, splitext -from os import chmod, getgid, getuid, symlink, unlink, remove, stat, listdir, environ, makedirs, O_NONBLOCK from re import sub from shutil import copyfile, rmtree, which from socket import socket, AF_INET, SOCK_STREAM From f82360b52c5ebb0bd015d3aa821bfe94cd6294b0 Mon Sep 17 00:00:00 2001 From: Chris McCormick Date: Thu, 28 Nov 2019 07:17:40 +0800 Subject: [PATCH 09/10] Fixed missing imports. --- piku.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/piku.py b/piku.py index ac25f1f..d1269ed 100755 --- a/piku.py +++ b/piku.py @@ -12,10 +12,13 @@ from importlib import import_module from collections import defaultdict, deque from fcntl import fcntl, F_SETFL, F_GETFL from glob import glob -from os import chmod, getgid, getuid, symlink, unlink, remove, stat, listdir, environ, makedirs, O_NONBLOCK -from os.path import abspath, basename, dirname, exists, getmtime, join, realpath, splitext from grp import getgrgid from pwd import getpwuid +from json import loads +from multiprocessing import cpu_count +from os import chmod, getgid, getuid, symlink, unlink, remove, stat, listdir, environ, makedirs, O_NONBLOCK +from os.path import abspath, basename, dirname, exists, getmtime, join, realpath, splitext, isdir +from pwd import getpwuid from re import sub from shutil import copyfile, rmtree, which from socket import socket, AF_INET, SOCK_STREAM From 9b3c0eb963eb3790d8996a688510b5882b383e90 Mon Sep 17 00:00:00 2001 From: Chris McCormick Date: Thu, 28 Nov 2019 08:19:41 +0800 Subject: [PATCH 10/10] Fixed double declaration. --- piku.py | 1 - 1 file changed, 1 deletion(-) diff --git a/piku.py b/piku.py index d1269ed..19b3bf7 100755 --- a/piku.py +++ b/piku.py @@ -18,7 +18,6 @@ from json import loads from multiprocessing import cpu_count from os import chmod, getgid, getuid, symlink, unlink, remove, stat, listdir, environ, makedirs, O_NONBLOCK from os.path import abspath, basename, dirname, exists, getmtime, join, realpath, splitext, isdir -from pwd import getpwuid from re import sub from shutil import copyfile, rmtree, which from socket import socket, AF_INET, SOCK_STREAM