Skip to content

Actions

This section documents each action type that Simple-CDD-YAML is able to use in YAML recipes.

Usage

Actions are listed under the root level actions keyword, which usually appears after the variables and profile name definition:

{% set profile=profile or "base" -%} 

profile: {{profile}} 

actions:
  action: recipe
    description: ...

Implementation

All actions inherit from the Action class.

Action

Abstract action base class

Source code in simple_cdd_yaml/actions.py
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