Skip to content

Commit

Permalink
feat: Add support for pre and post hooks (#130)
Browse files Browse the repository at this point in the history
  • Loading branch information
xmnlab authored Oct 16, 2024
1 parent 96612b3 commit 74bdada
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 61 deletions.
98 changes: 54 additions & 44 deletions .makim.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ groups:

preview:
help: Preview the documentation
dependencies:
- task: docs.build
hooks:
pre-run:
- task: docs.build
run: mkdocs serve --watch docs --config-file mkdocs.yaml

package:
Expand Down Expand Up @@ -61,8 +62,9 @@ groups:

smoke-1:
help: Run smoke tests for group 1
dependencies:
- task: docker.killall
hooks:
pre-run:
- task: docker.killall
vars:
SUGAR_FLAGS: --verbose --group group1
run: |
Expand All @@ -82,34 +84,32 @@ groups:
smoke-2:
help: Run smoke tests for group 2
dependencies:
- task: docker.killall
hooks:
pre-run:
- task: docker.killall
vars:
SUGAR_FLAGS: --verbose --group group2
run: |
sugar ${{ vars.SUGAR_FLAGS }} compose build --all
sugar ${{ vars.SUGAR_FLAGS }} compose build
sugar ${{ vars.SUGAR_FLAGS }} compose build --services service2-1
sugar ${{ vars.SUGAR_FLAGS }} compose pull --all
sugar ${{ vars.SUGAR_FLAGS }} compose pull
sugar ${{ vars.SUGAR_FLAGS }} compose pull --services service2-1
sugar ${{ vars.SUGAR_FLAGS }} compose-ext start --all --options -d
sugar ${{ vars.SUGAR_FLAGS }} compose-ext restart --all --options -d
sugar ${{ vars.SUGAR_FLAGS }} compose exec --service service2-1 --options -T --cmd env
sugar ${{ vars.SUGAR_FLAGS }} compose stop --all
sugar ${{ vars.SUGAR_FLAGS }} compose run --service service2-1 --options -T --cmd env
sugar ${{ vars.SUGAR_FLAGS }} compose down
smoke-services:
help:
dependencies:
- task: docker.killall
hooks:
pre-run:
- task: docker.killall
run: |
sugar --verbose --config-file tests/containers/.services.sugar.yaml compose build
sugar --verbose --file tests/containers/.services.sugar.yaml compose build
smoke-mix:
help: Run smoke tests for group mix
dependencies:
- task: docker.killall
hooks:
pre-run:
- task: docker.killall
vars:
SUGAR_FLAGS: --verbose --group group-mix
run: |
Expand All @@ -128,8 +128,9 @@ groups:
smoke-main:
help: Run smoke tests for group main
dependencies:
- task: docker.killall
hooks:
pre-run:
- task: docker.killall
vars:
SUGAR_FLAGS: --verbose --group group1
run: |
Expand All @@ -150,29 +151,22 @@ groups:
sugar ${{ vars.SUGAR_FLAGS }} compose run --service service1-1 --options -T --cmd env
sugar ${{ vars.SUGAR_FLAGS }} compose top
sugar ${{ vars.SUGAR_FLAGS }} compose up --options -d
sugar ${{ vars.SUGAR_FLAGS }} compose version --verbose
sugar ${{ vars.SUGAR_FLAGS }} compose version
# port is not complete supported
# sugar ${{ vars.SUGAR_FLAGS }} compose events --service service1-1 --options --json --dry-run
smoke-defaults:
help: Run smoke tests for group defaults
dependencies:
- task: docker.killall
vars:
SUGAR_FLAGS: --verbose --group group-defaults
run: |
export SUGAR_PROJECT_NAME="test-`python -c 'from uuid import uuid4; print(uuid4().hex[:7])'`"
echo $SUGAR_PROJECT_NAME
sugar ${{ vars.SUGAR_FLAGS }} compose build
sugar ${{ vars.SUGAR_FLAGS }} compose-ext start --options -d
sugar ${{ vars.SUGAR_FLAGS }} compose-ext restart --options -d
docker ps|grep $SUGAR_PROJECT_NAME
# TODO: it seems that the SUGAR_PROJECT_NAME is not used properly from this block
# docker ps|grep $SUGAR_PROJECT_NAME
sugar ${{ vars.SUGAR_FLAGS }} compose-ext stop
smoke-final:
help: Run final smoke tests
dependencies:
- task: docker.killall
hooks:
pre-run:
- task: docker.killall
run: |
sugar --verbose --group group-defaults compose-ext restart --options -d
sugar --verbose --group group1 compose pause
Expand All @@ -184,28 +178,44 @@ groups:
smoke-experimental:
help: Run simple text for experimental commands
dependencies:
- task: docker.killall
hooks:
pre-run:
- task: docker.killall
run: |
sugar --verbose compose attach--help
sugar --verbose compose attach --help
sugar --verbose compose cp --help
sugar --verbose compose ls --help
sugar --verbose compose scale --help
sugar --verbose compose wait --help
sugar --verbose compose watch --help
smoke-hooks:
help: Run simple text for experimental commands
hooks:
pre-run:
- task: docker.killall
vars:
SUGAR_FLAGS: --file .hooks.sugar.yaml
dir: tests/containers
run: |
sugar ${{ vars.SUGAR_FLAGS }} compose config | grep "RUNNING PRE-RUN FOR CONFIG"
sugar ${{ vars.SUGAR_FLAGS }} compose config | grep "CONFIG EXECUTED WITH SUCCESS"
sugar ${{ vars.SUGAR_FLAGS }} compose-ext config | grep "RUNNING PRE-RUN FOR CONFIG"
sugar ${{ vars.SUGAR_FLAGS }} compose-ext config | grep "CONFIG EXECUTED WITH SUCCESS"
smoke:
help: Run final smoke tests
dependencies:
- task: docker.killall
- task: tests.smoke-1
- task: tests.smoke-2
- task: tests.smoke-mix
- task: tests.smoke-main
- task: tests.smoke-defaults
- task: tests.smoke-final
- task: tests.smoke-services
- task: tests.smoke-experimental
hooks:
pre-run:
- task: docker.killall
- task: tests.smoke-1
- task: tests.smoke-2
- task: tests.smoke-mix
- task: tests.smoke-main
- task: tests.smoke-final
- task: tests.smoke-services
- task: tests.smoke-experimental
- task: tests.smoke-hooks
run: |
sugar --verbose compose --help
sugar --verbose compose version
Expand Down
2 changes: 1 addition & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ rich = { version = ">=10.11.0", optional = true }
textual = { version = ">=0.48", optional = true }
plotille = { version = ">=5", optional = true }
compose-go = ">=1.27.0"
xonsh = ">=0.15.0"

[tool.poetry.extras]
tui = [
Expand Down
9 changes: 6 additions & 3 deletions src/sugar/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ def version_callback() -> None:
@app.callback(invoke_without_command=True)
def main(
ctx: typer.Context,
file: str = Option(
'',
'--file',
help='Set the sugar config file.',
),
group: str = Option(
'',
'--group',
Expand Down Expand Up @@ -126,7 +131,6 @@ def main(
flags_dry_run['dry_run'] = True

if ctx.invoked_subcommand is None:
# typer.echo('Welcome to sugar. For usage, try --help.')
raise typer.Exit()


Expand Down Expand Up @@ -376,12 +380,11 @@ def extract_options_and_cmd_args() -> tuple[list[str], list[str]]:
for sugar_arg in [
'--verbose',
'--version',
'--service-group',
'--group',
'--services',
'--service',
'--all',
'--config-file',
'--file',
]:
if sugar_arg not in args:
continue
Expand Down
73 changes: 60 additions & 13 deletions src/sugar/extensions/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import os
import shlex
import sys
import tempfile

from copy import deepcopy
from pathlib import Path
Expand All @@ -19,6 +20,7 @@

from sugar import __version__
from sugar.logs import SugarErrorType, SugarLogs
from sugar.utils import camel_to_snake

TEMPLATE = Environment(
autoescape=False,
Expand Down Expand Up @@ -47,6 +49,7 @@ class SugarBase:
service_names: list[str] = []
group_selected: str = ''
verbose: bool = False
hooks: dict[str, list[dict[str, Any]]] = {}

def __init_subclass__(cls, **kwargs: Any) -> None:
"""Initialize the actions list for all the created commands."""
Expand Down Expand Up @@ -106,6 +109,12 @@ def _call_backend_app(
_out: Union[io.TextIOWrapper, io.StringIO, Any] = sys.stdout,
_err: Union[io.TextIOWrapper, io.StringIO, Any] = sys.stderr,
) -> None:
# Execute pre-run hooks
extension = camel_to_snake(
self.__class__.__name__.replace('Sugar', '')
)
self._execute_hooks('pre-run', extension, action)

sh_extras = {
'_in': sys.stdin,
'_out': _out,
Expand Down Expand Up @@ -151,6 +160,7 @@ def _call_backend_app(
SugarLogs.raise_error(
f'Process {pid} killed.', SugarErrorType.SH_KEYBOARD_INTERRUPT
)
self._execute_hooks('post-run', extension, action)

def _check_config_file(self) -> bool:
return Path(self.file).exists()
Expand All @@ -159,6 +169,40 @@ def _check_config_file(self) -> bool:
def _check_services_item(self) -> bool:
return hasattr(self.config, 'services')

def _execute_hooks(
self, hook_type: str, extension: str, action: str
) -> None:
"""Execute hooks specific type, extension, and action."""
hooks = self.hooks.get(hook_type, [])

sh_extras = {
'_in': sys.stdin,
'_out': sys.stdout,
'_err': sys.stderr,
'_no_err': True,
'_env': os.environ,
}

fd, filepath = tempfile.mkstemp(suffix='sugar', text=True)

for hook in hooks:
targets = hook.get('targets', {})
target_ext = targets.get(extension, [])
hook_name = hook.get('name', '')

if not target_ext or action not in target_ext:
continue

SugarLogs.print_info(f'Running {hook_type} hook: {hook_name} ...')
cmd = hook.get('run', '').strip()

with open(filepath, 'w') as f:
f.write(cmd)

sh.xonsh(filepath, **sh_extras)

os.remove(filepath)

# set default group main
def _load_root_services(self) -> None:
"""Load services attribute in the root of the configuration."""
Expand Down Expand Up @@ -213,7 +257,7 @@ def _filter_service_group(self) -> None:
default_services = [
service['name']
for service in group_data.get('services', {}).get(
'available'
'available', []
)
]
group_data['services']['default'] = ','.join(
Expand All @@ -223,7 +267,7 @@ def _filter_service_group(self) -> None:
return

SugarLogs.raise_error(
f'The given group service "{group_name}" was not found '
f'The given group service "{selected_group_name}" was not found '
'in the configuration file.',
SugarErrorType.SUGAR_MISSING_PARAMETER,
)
Expand All @@ -248,6 +292,9 @@ def _load_config(self) -> None:
SugarErrorType.SUGAR_INVALID_CONFIGURATION,
)

# Load hooks
self.hooks = self.config.get('hooks', {})

def _load_backend_app(self) -> None:
backend_cmd = self.config.get('backend', '')
supported_backends = ['compose']
Expand Down Expand Up @@ -341,29 +388,29 @@ def _get_services_names(self, **kwargs: Any) -> list[str]:
return []

_arg_services = kwargs.get('services', '')
_arg_all = kwargs.get('all', '')
_arg_all = kwargs.get('all', False)

services_config = self.service_group['services']
service_names: list[str] = []
services_default = services_config.get('default', '')

if _arg_all:
service_names = [
v['name']
for v in self.service_group.get('services', {}).get(
'available'
service['name']
for service in self.service_group.get('services', {}).get(
'available', []
)
]
elif _arg_services == '' and not services_default:
elif _arg_services:
service_names = _arg_services.split(',')
elif services_default:
service_names = services_default.split(',')
else:
SugarLogs.raise_error(
'If you want to execute the operation for all services, '
'use --all parameter.',
SugarErrorType.SUGAR_INVALID_PARAMETER,
)
elif _arg_services:
service_names = _arg_services.split(',')
elif services_config.get('default'):
service_names = services_default.split(',')

return service_names

Expand All @@ -374,7 +421,7 @@ def _verify_config(self) -> None:
SugarErrorType.SUGAR_INVALID_CONFIGURATION,
)

if not len(self.config['groups']):
if not len(self.config.get('groups', {})):
SugarLogs.raise_error(
'No service groups found.',
SugarErrorType.SUGAR_INVALID_CONFIGURATION,
Expand All @@ -390,7 +437,7 @@ def load(
dry_run: bool = False,
verbose: bool = False,
) -> None:
"""Load makim configuration."""
"""Load sugar configuration."""
self.file = file
self.group_selected = group
self.dry_run = dry_run
Expand Down
Loading

0 comments on commit 74bdada

Please sign in to comment.