class Action:
""" Abstract action base class """
action_out = None
def __init__(self, args_dict):
self.profile = args_dict['profile']
self.input_dir = pl.Path(args_dict['input'])
self.output_dir = pl.Path(args_dict['output'])
self.debos = args_dict['debos']
self.debos_output_dir = pl.Path(args_dict['debos_output']) / self.profile
self.result = {
'architecture': '',
'chroot_default': False,
'actions': [],
'pre-actions': [],
'post-actions': [],
}
@staticmethod
def _print(text, header=None, width=68):
""" Print text wrapped """
initial_indent = ' '
if header:
print(f' {header}')
initial_indent = ' '
wrapped_text = textwrap.wrap(
text,
width=width,
initial_indent=initial_indent,
subsequent_indent=' ',
break_on_hyphens=False,
)
print('\n'.join(wrapped_text))
def _action_inform(self, props):
""" Inform on action start action status """
if action_type := props.get('action'):
print(f' {action_type} action '.upper().center(70, '='))
if description := props.get('description'):
self._print(f'Description: {description}')
for src_file in ('recipe', 'preconf', 'source', 'command', 'script'):
if src := props.get(src_file):
self._print(f'{src_file.capitalize()}: {src}')
def _read_substitute(self, filename, substitutions):
""" Read string from file and perform jinja2 substitutions """
input_file = self.input_dir / filename
with open(input_file, mode='r', encoding='utf-8') as file:
template = jinja2.Template(file.read())
return template.render(substitutions)
def _write_action(self, string, extension, directory='profiles',
no_duplicate=False):
""" Append string to profile file """
filename = self.profile + '.' + extension
output_file = self.output_dir / directory / filename
if no_duplicate and output_file.is_file():
with open(output_file, mode='r', encoding='utf-8') as file:
if string in file.read():
return
with open(output_file, mode='a', encoding='utf-8') as file:
file.write(string)
def perform_action(self, props):
""" Perform the action specific tasks and return result """
raise NotImplementedError('Action is an abstract base class!')
def perform_debos_action(self, props):
""" Process debos action specific tasks and return result """
raise NotImplementedError('Action is an abstract base class!')
def execute(self, props):
""" Execute an action """
self._action_inform(props)
if self.debos:
result = self.perform_debos_action(props)
else:
result = self.perform_action(props)
if result:
if not self.action_out:
self.action_out = props['action']
self._write_action(result, self.action_out)
def append_result(self, new_result: dict, key='actions'):
""" Append a new result to the result list """
added_result = copy.deepcopy(new_result)
self.result[key].append(added_result)
def extend_result(self, new_result: dict, key='actions'):
""" Append a new result to the result list """
added_result = copy.deepcopy(new_result)
self.result[key].extend(added_result)
def prepend_result(self, new_result: dict, key='actions'):
""" Prepend a new result to the result list """
added_result = copy.deepcopy(new_result)
self.result[key].insert(0, added_result)
def combine_results(self, result):
""" Combine two result sets """
for key in ('actions', 'pre-actions', 'post-actions'):
self.result[key].extend(result[key])
for option in ('architecture', 'chroot_default'):
if result.get(option):
self.result[option] = result[option]
def unique_filename(self, base='script', ext='sh', description=None):
""" Create a name from description or using uuid """
name = base + '_'
if description:
name += description.lower()
else:
name += str(uuid.uuid4().hex)
return "".join([x if x.isalnum() else "_" for x in name]) + '.' + ext