Skip to content
This repository has been archived by the owner on Jan 14, 2024. It is now read-only.

Commit

Permalink
#54: Move initialization class to boostrap package
Browse files Browse the repository at this point in the history
  • Loading branch information
blackandred committed Nov 24, 2020
1 parent b623f44 commit 0fdfb48
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 112 deletions.
4 changes: 2 additions & 2 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = rkd
author = RiotKit non-profit organization
author-email = riotkit@riseup.net
summary = Task executor - balance between Makefile and Gradle.
summary = Task executor, handy for DevOps and not only - balance between Makefile and Gradle.
description-file = README.rst
description-content-type = text/x-rst; charset=UTF-8
home-page = https://riotkit.org
Expand All @@ -27,4 +27,4 @@ packages =

[entry_points]
console_scripts =
rkd = rkd:main
rkd = rkd.bootstrap:main
108 changes: 1 addition & 107 deletions src/rkd/__init__.py
Original file line number Diff line number Diff line change
@@ -1,107 +1 @@
#!/usr/bin/env python3

import sys
import os
from dotenv import load_dotenv
from .argparsing import CommandlineParsingHelper
from .context import ContextFactory, ApplicationContext
from .resolver import TaskResolver
from .validator import TaskDeclarationValidator
from .execution.executor import OneByOneTaskExecutor
from .exception import TaskNotFoundException, ParsingException, YamlParsingException
from .api.inputoutput import SystemIO
from .api.inputoutput import UnbufferedStdout
from .aliasgroups import parse_alias_groups_from_env


class RiotKitDoApplication:
_ctx: ApplicationContext
_tasks_to_execute = []

@staticmethod
def load_environment():
paths = os.getenv('RKD_PATH', '').split(':')

for path in paths:
if os.path.isfile(path + '/.env'):
load_dotenv(path + '/.env')

load_dotenv(dotenv_path=os.getcwd() + '/.env')

@staticmethod
def make_stdout_unbuffered():
sys.stdout = UnbufferedStdout(sys.stdout)

@staticmethod
def prepend_development_paths():
"""Add ./src at the beginning of PYTHONPATH - very useful for development"""

sys.path = [os.getcwd() + '/src'] + sys.path

def main(self, argv: list):
if not CommandlineParsingHelper.has_any_task(argv) and not CommandlineParsingHelper.was_help_used(argv):
self.print_banner_and_exit()

# system wide IO instance with defaults, the :init task should override those settings
io = SystemIO()
io.silent = True
io.set_log_level(os.getenv('RKD_SYS_LOG_LEVEL', 'info'))

# preparse arguments that are before tasks
preparsed_args = CommandlineParsingHelper.preparse_args(argv)

# load context of components - all tasks, plugins etc.
try:
self._ctx = ContextFactory(io).create_unified_context(additional_imports=preparsed_args['imports'])

except ParsingException as e:
io.silent = False
io.error_msg('Cannot import tasks/module from RKD_IMPORTS environment variable or --imports switch. '
'Details: {}'.format(str(e)))
sys.exit(1)

except YamlParsingException as e:
io.silent = False
io.error_msg('Cannot import tasks/module from one of makefile.yaml files. Details: {}'.format(str(e)))
sys.exit(1)

task_resolver = TaskResolver(self._ctx, parse_alias_groups_from_env(os.getenv('RKD_ALIAS_GROUPS', '')))
executor = OneByOneTaskExecutor(self._ctx)

# iterate over each task, parse commandline arguments
requested_tasks = CommandlineParsingHelper.create_grouped_arguments([':init'] + argv[1:])

# validate all tasks
task_resolver.resolve(requested_tasks, TaskDeclarationValidator.assert_declaration_is_valid)

# execute all tasks
task_resolver.resolve(requested_tasks, executor.execute)

executor.get_observer().execution_finished()

sys.exit(1 if executor.get_observer().has_at_least_one_failed_task() else 0)

@staticmethod
def print_banner_and_exit():
with open(os.path.dirname(os.path.realpath(__file__)) + '/misc/banner.txt', 'rb') as banner_file:
print(banner_file.read().replace(b'\\x1B', b'\x1B').decode('utf-8'))

sys.exit(0)


def main():
app = RiotKitDoApplication()
app.make_stdout_unbuffered()
app.prepend_development_paths()
app.load_environment()

try:
app.main(argv=sys.argv)

except TaskNotFoundException as e:
print(e)
sys.exit(127)


if __name__ == '__main__':
main()
from .bootstrap import main, RiotKitDoApplication
2 changes: 1 addition & 1 deletion src/rkd/__main__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/usr/bin/env python3

from . import main
from .bootstrap import main

main()
4 changes: 3 additions & 1 deletion src/rkd/api/testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from io import StringIO
from copy import deepcopy
from contextlib import contextmanager
from rkd import RiotKitDoApplication, ApplicationContext
from rkd.bootstrap import RiotKitDoApplication
from rkd.execution.executor import OneByOneTaskExecutor
from rkd.api.contract import ExecutionContext
from rkd.api.contract import TaskInterface
Expand All @@ -23,6 +23,7 @@
from rkd.api.inputoutput import IO
from rkd.api.inputoutput import NullSystemIO
from rkd.api.inputoutput import BufferedSystemIO
from rkd.context import ApplicationContext


class OutputCapturingSafeTestCase(TestCase):
Expand Down Expand Up @@ -245,6 +246,7 @@ def execute_mocked_task_and_get_output(self, task: TaskInterface, args=None, env

with r_io.capture_descriptors(enable_standard_out=True, stream=str_io):
try:
# noinspection PyTypeChecker
result = task.execute(ExecutionContext(
TaskDeclaration(task),
args=args,
Expand Down
115 changes: 115 additions & 0 deletions src/rkd/bootstrap.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
#!/usr/bin/env python3

"""
Bootstrap
=========
Initializes the application, passes arguments and environment variables from operating system
Only this layer can use sys.exit() call to pass exit code to the operating system
"""

import sys
import os
from dotenv import load_dotenv
from .argparsing import CommandlineParsingHelper
from .context import ContextFactory, ApplicationContext
from .resolver import TaskResolver
from .validator import TaskDeclarationValidator
from .execution.executor import OneByOneTaskExecutor
from .exception import TaskNotFoundException, ParsingException, YamlParsingException
from .api.inputoutput import SystemIO
from .api.inputoutput import UnbufferedStdout
from .aliasgroups import parse_alias_groups_from_env


class RiotKitDoApplication(object):
_ctx: ApplicationContext
_tasks_to_execute = []

@staticmethod
def load_environment():
paths = os.getenv('RKD_PATH', '').split(':')

for path in paths:
if os.path.isfile(path + '/.env'):
load_dotenv(path + '/.env')

load_dotenv(dotenv_path=os.getcwd() + '/.env')

@staticmethod
def make_stdout_unbuffered():
sys.stdout = UnbufferedStdout(sys.stdout)

@staticmethod
def prepend_development_paths():
"""Add ./src at the beginning of PYTHONPATH - very useful for development"""

sys.path = [os.getcwd() + '/src'] + sys.path

def main(self, argv: list):
if not CommandlineParsingHelper.has_any_task(argv) and not CommandlineParsingHelper.was_help_used(argv):
self.print_banner_and_exit()

# system wide IO instance with defaults, the :init task should override those settings
io = SystemIO()
io.silent = True
io.set_log_level(os.getenv('RKD_SYS_LOG_LEVEL', 'info'))

# preparse arguments that are before tasks
preparsed_args = CommandlineParsingHelper.preparse_args(argv)

# load context of components - all tasks, plugins etc.
try:
self._ctx = ContextFactory(io).create_unified_context(additional_imports=preparsed_args['imports'])

except ParsingException as e:
io.silent = False
io.error_msg('Cannot import tasks/module from RKD_IMPORTS environment variable or --imports switch. '
'Details: {}'.format(str(e)))
sys.exit(1)

except YamlParsingException as e:
io.silent = False
io.error_msg('Cannot import tasks/module from one of makefile.yaml files. Details: {}'.format(str(e)))
sys.exit(1)

task_resolver = TaskResolver(self._ctx, parse_alias_groups_from_env(os.getenv('RKD_ALIAS_GROUPS', '')))
executor = OneByOneTaskExecutor(self._ctx)

# iterate over each task, parse commandline arguments
requested_tasks = CommandlineParsingHelper.create_grouped_arguments([':init'] + argv[1:])

# validate all tasks
task_resolver.resolve(requested_tasks, TaskDeclarationValidator.assert_declaration_is_valid)

# execute all tasks
task_resolver.resolve(requested_tasks, executor.execute)

executor.get_observer().execution_finished()

sys.exit(1 if executor.get_observer().has_at_least_one_failed_task() else 0)

@staticmethod
def print_banner_and_exit():
with open(os.path.dirname(os.path.realpath(__file__)) + '/misc/banner.txt', 'rb') as banner_file:
print(banner_file.read().replace(b'\\x1B', b'\x1B').decode('utf-8'))

sys.exit(0)


def main():
app = RiotKitDoApplication()
app.make_stdout_unbuffered()
app.prepend_development_paths()
app.load_environment()

try:
app.main(argv=sys.argv)

except TaskNotFoundException as e:
print(e)
sys.exit(127)


if __name__ == '__main__':
main()
2 changes: 1 addition & 1 deletion test/test_functional.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def test_logging_tasks_into_separate_files(self):

self.assertIn('RKD version', content) # RKD version globally as a tool
self.assertIn(':sh', content) # one of tasks
self.assertIn('rkd.standardlib.VersionTask', content)
self.assertIn('rkd.standardlib.core.VersionTask', content)

with open(second.name) as second_handle:
content = second_handle.read()
Expand Down

0 comments on commit 0fdfb48

Please sign in to comment.