Created
June 21, 2025 22:03
-
-
Save travisbhartwell/2134385e45192eb695369597f93167dd to your computer and use it in GitHub Desktop.
Code coverage for MyProject
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#: # -*- mode: shell-script; sh-shell: bash; sh-basic-offset: 4; sh-indentation: 4; coding: utf-8 -*- | |
#: # shellcheck shell=bash | |
: | |
#: # MyCmd Project Command Group Library | |
#: # Library for Project Task Runner Functionality | |
: | |
4: set -o nounset -o errexit -o errtrace -o pipefail | |
: | |
4: (return 0 >/dev/null 2>&1) || { | |
-: echo >&2 "$0 is a library only meant to be sourced." | |
-: exit 1 | |
: } | |
: | |
4: mycmd:command_group.register_version "0.1" | |
4: mycmd:command_group.register_short_description "MyProject Command Group Library" | |
4: mycmd:command_group.register_help_text "The MyProject Project Task Runner" | |
: | |
4: [[ -n "${_MYCMD_SOURCING_FOR_HELP:-}" ]] && return | |
: | |
4: [[ -n "${_MYPROJECT_LIB:-}" ]] && return | |
8: readonly _MYPROJECT_LIB=1 | |
: | |
4: mycmd:command_group.load_support_lib "task" | |
4: mycmd:command_group.load_support_lib "user" | |
: | |
8: declare -Agx _MYPROJECT_MAPPINGS=() | |
: | |
8: readonly _PROJECT_ROOT_DIRECTORY=0 | |
8: readonly _PROJECT_TASK_DEFINITION_DIRECTORY=1 | |
: | |
8: _MYPROJECT_USER_CONFIG_FILE="$(mycmd:command_group.get_config_file "mapping-config")" | |
4: readonly _MYPROJECT_USER_CONFIG_FILE | |
: | |
#: # -------------------------------------------------------------------------------------------------- | |
#: # Project Locating Public Functions | |
F: function project.find_up() { | |
22: project._load_mapping_configuration | |
: | |
22: project._find_up "${@}" | |
: } | |
: | |
#: # -------------------------------------------------------------------------------------------------- | |
#: # Project Locating Private Helper Functions | |
F: function project._load_mapping_configuration() { | |
28: _MYPROJECT_MAPPINGS=() | |
: | |
28: local -r mapping_config_file="${MYPROJECT_MAPPING_CONFIG:-${_MYPROJECT_USER_CONFIG_FILE}}" | |
28: project._load_mapping_config_file "${mapping_config_file}" | |
: | |
#: # Parse MYPROJECT_MAPPING last so it can override values from the configuration file | |
28: if [[ -n "${MYPROJECT_MAPPING-}" ]]; then | |
7: local -r project_root_directory="${MYPROJECT_MAPPING%%:*}" | |
7: local -r task_definition_directory="${MYPROJECT_MAPPING##*:}" | |
7: mycmd.trace "Checking '${MYPROJECT_MAPPING}' with project root directory '${project_root_directory}' and task definition directory '${task_definition_directory}'." | |
: | |
7: project._add_task_definition_directory_mapping_if_valid "${project_root_directory}" "${task_definition_directory}" | |
: fi | |
: | |
28: project._log_myproject_mappings | |
: } | |
: | |
F: function project._load_mapping_config_file() { | |
28: local -r mapping_config_file="${1}" | |
: | |
28: if ! [[ -e "${mapping_config_file}" ]]; then | |
20: mycmd.debug "MyProject Mapping Configuration file '${mapping_config_file}' does not exist." | |
20: return | |
: fi | |
: | |
16: local -a config_lines=() | |
: | |
8: readarray -t config_lines <"${mapping_config_file}" | |
: | |
8: mycmd.trace "Processing ${#config_lines[@]} lines from mapping config file '${mapping_config_file}'." | |
: | |
8: local line | |
8: local project_root_directory | |
8: local task_definition_directory | |
: | |
36: for line in "${config_lines[@]}"; do | |
36: mycmd.trace "Processing config file line:" | |
36: mycmd.trace "${line}" | |
: | |
36: project_root_directory="${line%%=*}" | |
36: task_definition_directory="${line##*=}" | |
: | |
36: project._add_task_definition_directory_mapping_if_valid "${project_root_directory}" "${task_definition_directory}" | |
: done | |
: } | |
: | |
F: function project._add_task_definition_directory_mapping_if_valid() { | |
43: local project_root_directory="${1}" | |
43: local task_definition_directory="${2}" | |
: | |
43: mycmd.trace "Checking mapping with project root directory '${project_root_directory}' and task definition directory '${task_definition_directory}'." | |
: | |
86: if ! project_root_directory="$(project._resolve_directory "${project_root_directory}")"; then | |
8: mycmd.debug "Error canonicalizing project root directory, ignoring mapping." | |
8: return | |
: fi | |
35: readonly project_root_directory | |
: | |
70: if ! task_definition_directory="$(project._resolve_directory "${task_definition_directory}")"; then | |
8: mycmd.debug "Error canonicalizing task definition directory, ignoring mapping." | |
8: return | |
: fi | |
27: readonly task_definition_directory | |
: | |
27: if ! project._is_valid_task_definition_directory "${task_definition_directory}"; then | |
8: mycmd.debug "'${task_definition_directory}' is not a valid task definition directory, ignoring mapping." | |
8: return | |
: fi | |
: | |
19: mycmd.trace "Configuring mapping for '${project_root_directory}' as '${task_definition_directory}'." | |
19: _MYPROJECT_MAPPINGS["${project_root_directory}"]="${task_definition_directory}" | |
: } | |
: | |
F: function project._log_myproject_mappings() { | |
#: # TODO: Use mycmd.print_table when it can use mycmd.trace | |
28: mycmd.trace "MyProject Mapping is configured with ${#_MYPROJECT_MAPPINGS[@]} entries.\n" | |
: | |
28: local project_root_directory | |
28: local task_definition_directory | |
18: for project_root_directory in "${!_MYPROJECT_MAPPINGS[@]}"; do | |
18: task_definition_directory="${_MYPROJECT_MAPPINGS["${project_root_directory}"]}" | |
18: mycmd.trace "'${project_root_directory}' is mapped to use the task definition directory '${task_definition_directory}'." | |
: done | |
: } | |
: | |
F: function project._resolve_directory() { | |
78: local directory="${1}" | |
78: directory=${directory/#\~/${HOME}} | |
78: mycmd.canonicalize_path "${directory}" | |
: } | |
: | |
F: function project._is_valid_task_definition_directory() { | |
98: local -r task_definition_directory="${1}" | |
: | |
98: if ! [[ -d "${task_definition_directory}" ]]; then | |
60: mycmd.debug "'${task_definition_directory}' does not exist." | |
60: return 1 | |
: fi | |
: | |
76: if ! [[ "$(basename "${task_definition_directory}")" == "myproject" ]]; then | |
10: mycmd.debug "'${task_definition_directory}' is not named 'myproject'." | |
10: return 1 | |
: fi | |
: | |
28: if ! [[ -f "${task_definition_directory}/main" ]]; then | |
1: mycmd.debug "Required file 'main' missing from '${task_definition_directory}'." | |
1: return 1 | |
: fi | |
: | |
27: return 0 | |
: } | |
: | |
F: function project._find_up() { | |
77: local -n project_root_directories="${1}" | |
77: local starting_directory="${2}" | |
: | |
154: if ! starting_directory="$(mycmd.canonicalize_path "${starting_directory}")"; then | |
1: mycmd.debug "'${starting_directory}' not found." | |
1: return 1 | |
: fi | |
: | |
76: mycmd.trace "Looking in '${starting_directory}' for MyProject Task Definition directory." | |
: | |
76: if [[ -v _MYPROJECT_MAPPINGS[${starting_directory}] ]]; then | |
8: local -r task_definition_directory="${_MYPROJECT_MAPPINGS["${starting_directory}"]}" | |
: | |
8: mycmd.trace "Found MyProject Mapping for '${starting_directory}' as '${task_definition_directory}'." | |
: | |
8: project_root_directories["${_PROJECT_ROOT_DIRECTORY}"]="${starting_directory}" | |
#: # shellcheck disable=SC2034 | |
8: project_root_directories["${_PROJECT_TASK_DEFINITION_DIRECTORY}"]="${task_definition_directory}" | |
8: return 0 | |
: fi | |
: | |
68: local -r possible_path="${starting_directory}/myproject" | |
: | |
68: if project._is_valid_task_definition_directory "${possible_path}"; then | |
7: project_root_directories["${_PROJECT_ROOT_DIRECTORY}"]="${starting_directory}" | |
#: # shellcheck disable=SC2034 | |
7: project_root_directories["${_PROJECT_TASK_DEFINITION_DIRECTORY}"]="${possible_path}" | |
7: return 0 | |
: fi | |
: | |
61: if [[ "${starting_directory}" = "/" ]]; then | |
6: mycmd.debug "MyProject task definition directory not found." | |
6: return 1 | |
: fi | |
: | |
55: project._find_up "${!project_root_directories}" "${starting_directory}/.." | |
: } | |
: | |
#: # -------------------------------------------------------------------------------------------------- | |
#: # Project Task Definition File Loading | |
F: function project.load_closest_task_definition() { | |
2: local -a project_directories | |
2: if ! project.find_up project_directories "${PWD}"; then | |
-: mycmd.log "Not located in a MyProject Project." | |
-: return 1 | |
: fi | |
: | |
2: local -r project_root_directory="${project_directories["${_PROJECT_ROOT_DIRECTORY}"]}" | |
2: local -r task_definition_directory="${project_directories["${_PROJECT_TASK_DEFINITION_DIRECTORY}"]}" | |
: | |
2: mycmd.output "Using project root at '${project_root_directory}'.\n" | |
2: mycmd.output "Using task definition directory at '${task_definition_directory}'.\n" | |
: | |
2: if ! project:task_registry.load_task_definition_file "${project_root_directory}" "${task_definition_directory}" main; then | |
-: mycmd.log "Error loading project task definition files." | |
-: return 1 | |
: fi | |
: } | |
: | |
F: function project.load_all_task_definition_files() { | |
1: local -a project_directories | |
1: if ! project.find_up project_directories "${PWD}"; then | |
-: mycmd.log "Not located in a MyProject Project." | |
-: return 1 | |
: fi | |
: | |
1: local -r project_root_directory="${project_directories["${_PROJECT_ROOT_DIRECTORY}"]}" | |
1: local -r task_definition_directory="${project_directories["${_PROJECT_TASK_DEFINITION_DIRECTORY}"]}" | |
: | |
1: if ! project:task_registry.load_all_task_definition_files "${project_root_directory}" "${task_definition_directory}"; then | |
-: mycmd.log "Error loading project task definition files." | |
-: return 1 | |
: fi | |
: } | |
: | |
#: # -------------------------------------------------------------------------------------------------- | |
#: # Task Execution | |
F: function project.execute_task() { | |
4: local fully_qualified_task_name="${1}" | |
4: shift | |
: | |
4: local return_code=0 | |
: | |
#: # shellcheck disable=SC2034 | |
4: local -A task | |
4: if ! project:task_registry.get_task "${fully_qualified_task_name}" task; then | |
3: mycmd.debug "Task '${fully_qualified_task_name}' is not found" | |
3: return_code=1 | |
: fi | |
: | |
4: if (($# > 0 && "${return_code}" != 0)); then | |
2: fully_qualified_task_name="${fully_qualified_task_name}/${1}" | |
2: shift | |
: | |
2: if ! project:task_registry.get_task "${fully_qualified_task_name}" task; then | |
1: mycmd.log "Task '${fully_qualified_task_name}' is not found" | |
: else | |
1: return_code=0 | |
: fi | |
: fi | |
: | |
4: if (("${return_code}" != 0)); then | |
2: mycmd.log "Task not found." | |
2: return 1 | |
: fi | |
: | |
3: project:task.execute task "${@}" || return_code="${?}" | |
: | |
2: return "${return_code}" | |
: } | |
: | |
4: mycmd.trace "The MyProject command group library has been sourced." |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#: # -*- mode: shell-script; sh-shell: bash; sh-basic-offset: 4; sh-indentation: 4; coding: utf-8 -*- | |
#: # shellcheck shell=bash | |
: | |
#: # MyProject Task Command Group Support Library | |
#: # Support for Task Definition Data Structures and Related Functions for MyProject | |
: | |
4: set -o nounset -o errexit -o errtrace -o pipefail | |
: | |
4: (return 0 >/dev/null 2>&1) || { | |
-: echo >&2 "$0 is a library only meant to be sourced." | |
-: exit 1 | |
: } | |
: | |
4: [[ -n "${_MYPROJECT_TASK_LIB:-}" ]] && return | |
8: readonly _MYPROJECT_TASK_LIB=1 | |
: | |
4: declare -Agx _MYPROJECT_TASKS | |
4: declare -Agx _MYPROJECT_TASKS_TASK_DEFINITION_FILES | |
4: declare -Agx _MYPROJECT_TASKS_ARGUMENT_COUNTS | |
4: declare -Agx _MYPROJECT_TASKS_ARGUMENT_INDICES | |
4: declare -agx _MYPROJECT_TASKS_ARGUMENTS | |
4: declare -Agx _MYPROJECT_LOADED_TASK_DEFINITION_FILES | |
: | |
8: readonly _TASK_FULLY_QUALIFIED_NAME_FIELD='task-fully-qualified-name' | |
8: readonly _TASK_NAME_FIELD='task-name' | |
8: readonly _TASK_NAMESPACED_PARTS_STRING_FIELD='task-namespaced-parts-string' | |
8: readonly _TASK_TASK_DEFINITION_FILE_FIELD='task-definition-file' | |
8: readonly _TASK_FUNCTION_FIELD='task-function' | |
8: readonly _TASK_ARGUMENT_COUNT_FIELD='task-argument-count' | |
8: readonly _TASK_ARGUMENT_INDEX_FIELD='task-argument-index' | |
: | |
F: function project:task_registry._reset_registry() { | |
10: mycmd.trace "Resetting the MyProject Task Registry." | |
10: _MYPROJECT_TASKS=() | |
10: _MYPROJECT_TASKS_TASK_DEFINITION_FILES=() | |
10: _MYPROJECT_TASKS_ARGUMENT_COUNTS=() | |
10: _MYPROJECT_TASKS_ARGUMENT_INDICES=() | |
10: _MYPROJECT_TASKS_ARGUMENTS=() | |
10: _MYPROJECT_LOADED_TASK_DEFINITION_FILES=() | |
10: unset MYPROJECT_ROOT_DIRECTORY | |
10: unset MYPROJECT_TASK_DEFINITION_DIRECTORY | |
: } | |
: | |
4: project:task_registry._reset_registry | |
: | |
F: function project:task_registry.load_task_definition_file() { | |
5: local -r project_root_directory="${1}" | |
5: local -r task_definition_directory="${2}" | |
5: local -r task_definition_file_name="${3}" | |
: | |
5: declare -gx MYPROJECT_ROOT_DIRECTORY="${project_root_directory}" | |
5: declare -gx MYPROJECT_TASK_DEFINITION_DIRECTORY="${task_definition_directory}" | |
: | |
5: project:task_registry._load_task_definition_file "${task_definition_file_name}" | |
: } | |
: | |
F: function project:task_registry.load_all_task_definition_files() { | |
1: local -r project_root_directory="${1}" | |
1: local -r task_definition_directory="${2}" | |
: | |
#: # First load main | |
1: if ! project:task_registry.load_task_definition_file "${project_root_directory}" "${task_definition_directory}" main; then | |
-: mycmd.log "Error loading main task definition file." | |
-: return 1 | |
: fi | |
: | |
2: local -a task_definition_files=("${task_definition_directory}"/*) | |
: | |
1: local task_definition_file_path | |
1: local task_definition_file_name | |
: | |
2: for task_definition_file_path in "${task_definition_files[@]}"; do | |
4: task_definition_file_name="$(basename "${task_definition_file_path}")" | |
: | |
2: mycmd.trace "Attempting to load task definition file '${task_definition_file_name}'." | |
: | |
2: if ! project:task_registry.load_task_definition_file "${project_root_directory}" "${task_definition_directory}" "${task_definition_file_name}"; then | |
-: mycmd.log "Error loading task definition file name '${task_definition_file_name}'." | |
-: return 1 | |
: fi | |
: done | |
: } | |
: | |
F: function project:task_registry._load_task_definition_file() { | |
22: if [[ ! -v MYPROJECT_ROOT_DIRECTORY && ! -v MYPROJECT_TASK_DEFINITION_DIRECTORY ]]; then | |
-: mycmd.debug "Error: MYPROJECT_ROOT DIRECTORY and MYPROJECT_TASK_DEFINITION_DIRECTORY must be set." | |
-: return 1 | |
: fi | |
: | |
22: local -r task_definition_file_name="${1}" | |
: | |
22: if [[ -v _MYPROJECT_LOADED_TASK_DEFINITION_FILES[${task_definition_file_name}] ]]; then | |
18: mycmd.trace "'${task_definition_file_name}' already loaded." | |
18: return 0 | |
: fi | |
: | |
4: mycmd.trace "Loading '${task_definition_file_name}' from task definition directory '${MYPROJECT_TASK_DEFINITION_DIRECTORY}' for project '${MYPROJECT_ROOT_DIRECTORY}'." | |
: | |
4: declare -gx MYPROJECT_CURRENT_TASK_FILE="${task_definition_file_name}" | |
: | |
4: local -r task_definition_file="${MYPROJECT_TASK_DEFINITION_DIRECTORY}/${task_definition_file_name}" | |
: | |
4: local result=0 | |
4: if ! mycmd.source_lib_by_path_if_found "${task_definition_file}"; then | |
-: mycmd.log "Error loading task definition file '${task_definition_file}'." | |
-: result=1 | |
: fi | |
: | |
4: unset MYPROJECT_CURRENT_TASK_FILE | |
: | |
4: if ((result != 0)); then | |
-: return "${result}" | |
: fi | |
: | |
4: _MYPROJECT_LOADED_TASK_DEFINITION_FILES[${task_definition_file_name}]=1 | |
: | |
4: return 0 | |
: } | |
: | |
F: function project:task_registry.new_task() { | |
17: local -r task_definition_file_name="${1}" | |
17: local -r fully_qualified_task_name="${2}" | |
17: local -r task_function_name="${3}" | |
17: shift 3 | |
: | |
17: local -r argc="$#" | |
: | |
17: mycmd.trace "Attempting to register task '${fully_qualified_task_name}' with function '${task_function_name}'." | |
: | |
17: if [[ -v _MYPROJECT_TASKS["${fully_qualified_task_name}"] ]]; then | |
2: local existing_task_function_name="${_MYPROJECT_TASKS["${fully_qualified_task_name}"]}" | |
: | |
2: if [[ "${existing_task_function_name}" != "${task_function_name}" ]]; then | |
1: mycmd.debug "'${fully_qualified_task_name}' is already registered with function '${existing_task_function_name}', not '${task_function_name}'." | |
1: return 1 | |
: else | |
1: mycmd.debug "'${fully_qualified_task_name}' is already registered." | |
1: return 0 | |
: fi | |
: fi | |
: | |
15: _MYPROJECT_TASKS["${fully_qualified_task_name}"]="${task_function_name}" | |
15: _MYPROJECT_TASKS_TASK_DEFINITION_FILES["${fully_qualified_task_name}"]="${task_definition_file_name}" | |
15: _MYPROJECT_TASKS_ARGUMENT_COUNTS["${fully_qualified_task_name}"]="${argc}" | |
: | |
15: if ((argc > 0)); then | |
3: local index="${#_MYPROJECT_TASKS_ARGUMENTS[@]}" | |
3: _MYPROJECT_TASKS_ARGUMENTS+=("${@}") | |
3: _MYPROJECT_TASKS_ARGUMENT_INDICES["${fully_qualified_task_name}"]="${index}" | |
: | |
3: mycmd.trace "Created task arguments for task '${fully_qualified_task_name}' at index ${index} with argument count ${argc}." | |
: else | |
12: mycmd.trace "Registered 0 arguments for task '${fully_qualified_task_name}'." | |
: fi | |
: | |
15: mycmd.trace "Successfully registered task '${fully_qualified_task_name}' from task definition file '${task_definition_file_name}' with function '${task_function_name}' with ${argc} arguments." | |
15: return 0 | |
: } | |
: | |
F: function project:task_registry.get_task() { | |
17: local -r fully_qualified_task_name="${1}" | |
17: local -n task_struct_ref="${2}" | |
: | |
17: mycmd.trace "Attempting to load task with fully qualified name '${fully_qualified_task_name}'." | |
: | |
17: task_struct_ref=() | |
: | |
17: if ! project:task_registry._load_task_definition_file_for_task "${fully_qualified_task_name}"; then | |
-: mycmd.debug "Error loading task definition file for '${fully_qualified_task_name}'." | |
-: return 1 | |
: fi | |
: | |
17: if ! [[ -v _MYPROJECT_TASKS["${fully_qualified_task_name}"] ]]; then | |
5: mycmd.debug "Task '${fully_qualified_task_name}' is not found." | |
5: return 1 | |
: fi | |
: | |
12: mycmd.trace "Returning task definition:" | |
12: task_struct_ref["${_TASK_FULLY_QUALIFIED_NAME_FIELD}"]="${fully_qualified_task_name}" | |
12: mycmd.trace "- ${_TASK_FULLY_QUALIFIED_NAME_FIELD}: ${task_struct_ref["${_TASK_FULLY_QUALIFIED_NAME_FIELD}"]}" | |
: | |
12: local task_name | |
24: task_name="$(basename "${fully_qualified_task_name}")" | |
12: readonly task_name | |
12: task_struct_ref["${_TASK_NAME_FIELD}"]="${task_name}" | |
12: mycmd.trace "- ${_TASK_NAME_FIELD}: ${task_struct_ref["${_TASK_NAME_FIELD}"]}" | |
: | |
12: local -r namespaced_parts_string="${fully_qualified_task_name//\// }" | |
12: task_struct_ref["${_TASK_NAMESPACED_PARTS_STRING_FIELD}"]="${namespaced_parts_string}" | |
12: mycmd.trace "- ${_TASK_NAMESPACED_PARTS_STRING_FIELD}: ${task_struct_ref["${_TASK_NAMESPACED_PARTS_STRING_FIELD}"]}" | |
: | |
12: task_struct_ref["${_TASK_TASK_DEFINITION_FILE_FIELD}"]="${_MYPROJECT_TASKS_TASK_DEFINITION_FILES["${fully_qualified_task_name}"]}" | |
12: mycmd.trace "- ${_TASK_TASK_DEFINITION_FILE_FIELD}: ${task_struct_ref["${_TASK_TASK_DEFINITION_FILE_FIELD}"]}" | |
: | |
12: task_struct_ref["${_TASK_FUNCTION_FIELD}"]="${_MYPROJECT_TASKS["${fully_qualified_task_name}"]}" | |
12: mycmd.trace "- ${_TASK_FUNCTION_FIELD}: ${task_struct_ref["${_TASK_FUNCTION_FIELD}"]}" | |
: | |
12: local -r argc="${_MYPROJECT_TASKS_ARGUMENT_COUNTS["${fully_qualified_task_name}"]}" | |
12: task_struct_ref["${_TASK_ARGUMENT_COUNT_FIELD}"]="${argc}" | |
12: mycmd.trace "- ${_TASK_ARGUMENT_COUNT_FIELD}: ${task_struct_ref["${_TASK_ARGUMENT_COUNT_FIELD}"]}" | |
: | |
12: if (("${argc}" > 0)); then | |
2: if ! [[ -v _MYPROJECT_TASKS_ARGUMENT_INDICES["${fully_qualified_task_name}"] ]]; then | |
-: mycmd.debug "Required argument indices data missing for '${fully_qualified_task_name}'." | |
-: return 1 | |
: fi | |
: | |
2: task_struct_ref["${_TASK_ARGUMENT_INDEX_FIELD}"]="${_MYPROJECT_TASKS_ARGUMENT_INDICES["${fully_qualified_task_name}"]}" | |
2: mycmd.trace "- ${_TASK_ARGUMENT_INDEX_FIELD}: ${task_struct_ref["${_TASK_ARGUMENT_INDEX_FIELD}"]}" | |
: else | |
10: mycmd.trace "- ${_TASK_ARGUMENT_INDEX_FIELD}: No value set." | |
: fi | |
: } | |
: | |
F: function project:task_registry._load_task_definition_file_for_task() { | |
17: local -r fully_qualified_task_name="${1}" | |
: | |
17: mycmd.trace "Attempting to load task file, if necessary, for task with fully qualified name '${fully_qualified_task_name}'." | |
: | |
17: local task_definition_file_name | |
17: if [[ "${fully_qualified_task_name}" == *'/'* ]]; then | |
7: task_definition_file_name="${fully_qualified_task_name%%/*}" | |
: else | |
10: task_definition_file_name="main" | |
: fi | |
17: readonly task_definition_file_name | |
: | |
17: project:task_registry._load_task_definition_file "${task_definition_file_name}" | |
: } | |
: | |
F: function project:task_registry.list_tasks() { | |
-: if (("${#_MYPROJECT_TASKS[@]}" == 0)); then | |
-: mycmd.output "There are no registered tasks." | |
-: return 0 | |
: fi | |
: | |
-: mycmd.output "The following tasks are registered:" | |
: | |
-: local -a all_tasks_namespaced_parts | |
-: if ! project:task_registry._get_all_task_namespaced_parts all_tasks_namespaced_parts; then | |
-: mycmd.log "Error getting all task namespaced parts" | |
-: return 1 | |
: fi | |
: | |
-: local task | |
-: for task in "${all_tasks_namespaced_parts[@]}"; do | |
-: mycmd.output "${task}" | |
: done | |
: } | |
: | |
F: function project:task_registry._get_all_task_namespaced_parts() { | |
1: local -n all_tasks_namespaced_parts_ref="${1}" | |
: | |
2: local -a main_file_tasks=() | |
2: local -a other_tasks=() | |
: | |
1: local fully_qualified_task_name | |
1: local task_definition_file_name | |
1: local namespaced_parts_string | |
1: local -A task | |
: | |
6: for fully_qualified_task_name in "${!_MYPROJECT_TASKS[@]}"; do | |
6: task=() | |
: | |
6: if ! project:task_registry.get_task "${fully_qualified_task_name}" task; then | |
-: mycmd.debug "Unexpected error getting task '${fully_qualified_task_name}'." | |
-: return 1 | |
: fi | |
: | |
12: task_definition_file_name="$(project:task.get_task_definition_file_name task)" | |
12: namespaced_parts_string="$(project:task.get_namespaced_parts_as_string task)" | |
: | |
6: mycmd.trace "Found task with fully qualified name '${fully_qualified_task_name}', with namespaced parts string '${namespaced_parts_string}' with task file '${task_definition_file_name}'." | |
: | |
6: if [[ "${task_definition_file_name}" == "main" ]]; then | |
3: main_file_tasks+=("${namespaced_parts_string}") | |
: else | |
3: other_tasks+=("${namespaced_parts_string}") | |
: fi | |
: done | |
: | |
1: all_tasks_namespaced_parts_ref=() | |
: | |
1: if (("${#main_file_tasks[@]}" > 0)); then | |
#: # shellcheck disable=SC2034 | |
4: readarray -t all_tasks_namespaced_parts_ref < \ | |
-: <(printf '%s\n' "${main_file_tasks[@]}" | LC_ALL=en_US.UTF-8 sort || true) | |
: fi | |
: | |
1: if (("${#other_tasks[@]}" > 0)); then | |
1: local size_so_far="${#all_tasks_namespaced_parts_ref[@]}" | |
: | |
#: # shellcheck disable=SC2034 | |
4: readarray -t -O "${size_so_far}" all_tasks_namespaced_parts_ref < \ | |
-: <(printf '%s\n' "${other_tasks[@]}" | LC_ALL=en_US.UTF-8 sort || true) | |
: fi | |
: } | |
: | |
F: function project:task.get_fully_qualified_name() { | |
-: project:task._get_field_from_struct \ | |
10: "${_TASK_FULLY_QUALIFIED_NAME_FIELD}" \ | |
-: "${@}" | |
: } | |
: | |
F: function project:task.get_name() { | |
-: project:task._get_field_from_struct \ | |
4: "${_TASK_NAME_FIELD}" \ | |
-: "${@}" | |
: } | |
: | |
F: function project:task.get_namespaced_parts_as_string() { | |
-: project:task._get_field_from_struct \ | |
13: "${_TASK_NAMESPACED_PARTS_STRING_FIELD}" \ | |
-: "${@}" | |
: } | |
: | |
F: function project:task.get_task_definition_file_name() { | |
-: project:task._get_field_from_struct \ | |
13: "${_TASK_TASK_DEFINITION_FILE_FIELD}" \ | |
-: "${@}" | |
: } | |
: | |
F: function project:task.get_function_name() { | |
-: project:task._get_field_from_struct \ | |
11: "${_TASK_FUNCTION_FIELD}" \ | |
-: "${@}" | |
: } | |
: | |
F: function project:task.get_argument_count() { | |
-: project:task._get_field_from_struct \ | |
9: "${_TASK_ARGUMENT_COUNT_FIELD}" \ | |
-: "${@}" | |
: } | |
: | |
F: function project:task.get_argument_index() { | |
-: project:task._get_field_from_struct \ | |
6: "${_TASK_ARGUMENT_INDEX_FIELD}" \ | |
-: "${@}" | |
: } | |
: | |
F: function project:task._get_field_from_struct() { | |
66: local -r field_name="${1}" | |
#: # shellcheck disable=SC2178 | |
66: local -n task_struct_ref="${2}" | |
: | |
66: if [[ -v task_struct_ref["${field_name}"] ]]; then | |
63: echo "${task_struct_ref["${field_name}"]}" | |
63: return 0 | |
: else | |
3: mycmd.debug "Missing required task field '${field_name}'." | |
3: return 1 | |
: fi | |
: } | |
: | |
F: function project:task.get_arguments() { | |
#: # shellcheck disable=SC2178 | |
5: local -n task_struct_ref="${1}" | |
5: local -n arguments_ref="${2}" | |
: | |
5: local fully_qualified_task_name | |
10: fully_qualified_task_name="$(project:task.get_fully_qualified_name "${!task_struct_ref}")" | |
5: readonly fully_qualified_task_name | |
: | |
5: local -i argc | |
10: argc="$(project:task.get_argument_count "${!task_struct_ref}")" | |
5: readonly argc | |
: | |
5: if ((argc == 0)); then | |
3: mycmd.trace "No arguments defined for task '${fully_qualified_task_name}'." | |
#: # shellcheck disable=SC2034 | |
3: arguments_ref=() | |
3: return 0 | |
: fi | |
: | |
2: local -i index | |
4: if ! index="$(project:task.get_argument_index "${!task_struct_ref}")"; then | |
-: mycmd.log "Unexpected error: index should be set for task '${fully_qualified_task_name}'." | |
-: return 1 | |
: fi | |
2: readonly index | |
: | |
2: arguments_ref=("${_MYPROJECT_TASKS_ARGUMENTS[@]:index:argc}") | |
2: mycmd.trace "Set arguments for '${fully_qualified_task_name}' in ${!arguments_ref} as ${arguments_ref[*]}." | |
: } | |
: | |
F: function project:task.function_exists() { | |
#: # shellcheck disable=SC2178 | |
4: local -n task_struct_ref="${1}" | |
: | |
4: local task_function_name | |
8: task_function_name="$(project:task.get_function_name "${!task_struct_ref}")" | |
4: readonly task_function_name | |
: | |
4: if ! mycmd.function_exists "${task_function_name}"; then | |
1: local fully_qualified_task_name | |
2: fully_qualified_task_name="$(project:task.get_fully_qualified_name "${!task_struct_ref}")" | |
1: readonly fully_qualified_task_name | |
: | |
1: mycmd.log "Unknown function '${task_function_name}' for task '${fully_qualified_task_name}'." | |
1: return 1 | |
: fi | |
: | |
3: return 0 | |
: } | |
: | |
F: function project:task.execute() { | |
#: # shellcheck disable=SC2178 | |
4: local -n task_struct_ref="${1}" | |
4: shift | |
: | |
4: if ! project:task.function_exists "${!task_struct_ref}"; then | |
1: return 1 | |
: fi | |
: | |
3: local task_function_name | |
6: task_function_name="$(project:task.get_function_name "${!task_struct_ref}")" | |
3: readonly task_function_name | |
: | |
3: local namespaced_parts_string | |
6: namespaced_parts_string="$(project:task.get_namespaced_parts_as_string "${!task_struct_ref}")" | |
3: readonly namespaced_parts_string | |
: | |
3: local task_definition_file_name | |
6: task_definition_file_name="$(project:task.get_task_definition_file_name "${!task_struct_ref}")" | |
3: readonly task_definition_file_name | |
: | |
6: local -a task_arguments=() | |
3: project:task.get_arguments "${!task_struct_ref}" task_arguments | |
3: set -- "${task_arguments[@]}" "${@}" | |
: | |
3: declare -gx MYPROJECT_CURRENT_TASK_FILE="${task_definition_file_name}" | |
: | |
3: mycmd.output "➡️ Executing task '${namespaced_parts_string}'..." | |
: | |
3: local return_code=0 | |
4: "${task_function_name}" "${@}" || return_code="${?}" | |
: | |
3: if ((return_code == 0)); then | |
2: mycmd.output "✅ Task '${namespaced_parts_string}' succeeded." | |
: else | |
1: mycmd.output "❌ Task '${namespaced_parts_string}' failed." | |
: fi | |
: | |
3: return "${return_code}" | |
: } | |
: | |
4: mycmd.trace "The MyProject Task command group support library has been sourced." |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#: # -*- mode: shell-script; sh-shell: bash; sh-basic-offset: 4; sh-indentation: 4; coding: utf-8 -*- | |
#: # shellcheck shell=bash | |
: | |
#: # MyProject User Command Group Support Library | |
#: # All of the User-facing Functions for MyProject | |
: | |
4: set -o nounset -o errexit -o errtrace -o pipefail | |
: | |
4: (return 0 >/dev/null 2>&1) || { | |
-: echo >&2 "$0 is a library only meant to be sourced." | |
-: exit 1 | |
: } | |
: | |
4: [[ -n "${_MYPROJECT_USER_LIB:-}" ]] && return | |
8: readonly _MYPROJECT_USER_LIB=1 | |
: | |
4: mycmd:command_group.load_support_lib "task" | |
: | |
F: function myproject.register_task() { | |
12: local -r task_name="${1}" | |
12: local -r task_function_name="${2}" | |
12: shift 2 | |
: | |
12: if ! [[ -v MYPROJECT_CURRENT_TASK_FILE ]]; then | |
-: mycmd.debug "Required variable MYPROJECT_CURRENT_TASK_FILE is not set" | |
-: return 1 | |
: fi | |
: | |
12: local fully_qualified_task_name | |
12: if [[ "${MYPROJECT_CURRENT_TASK_FILE}" == "main" ]]; then | |
6: fully_qualified_task_name="${task_name}" | |
: else | |
6: fully_qualified_task_name="${MYPROJECT_CURRENT_TASK_FILE}/${task_name}" | |
: fi | |
12: readonly fully_qualified_task_name | |
: | |
-: project:task_registry.new_task \ | |
12: "${MYPROJECT_CURRENT_TASK_FILE}" \ | |
-: "${fully_qualified_task_name}" \ | |
-: "${task_function_name}" \ | |
-: "${@}" | |
: } | |
: | |
4: mycmd.trace "The MyProject User command group support library has been sourced." |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment