From b6f3c99bd8a54d6f364ddd70e6454973697544da Mon Sep 17 00:00:00 2001 From: JGBSouza Date: Tue, 15 Apr 2025 23:33:50 -0300 Subject: [PATCH 01/33] src: patch_track: Add initial files for kw patch-track To support the new `kw patch-track` feature, we are adding initial files and creating database tables to store the necessary information. This includes SQL commands to create the table `patch`, the file src/patch with the initial versions of `parse_patch_track_options` and the `patch_track_main` and the initial documentation `documentation/man/features/kw-patch-track` file. Signed-off-by: JGBSouza --- database/kwdb.sql | 12 +++ documentation/conf.py | 1 + documentation/man/features/kw-patch-track.rst | 15 ++++ kw | 7 ++ src/patch_track.sh | 79 +++++++++++++++++++ 5 files changed, 114 insertions(+) create mode 100644 documentation/man/features/kw-patch-track.rst create mode 100644 src/patch_track.sh diff --git a/database/kwdb.sql b/database/kwdb.sql index 68b7189f7..45d47ba80 100644 --- a/database/kwdb.sql +++ b/database/kwdb.sql @@ -46,6 +46,18 @@ CREATE TABLE IF NOT EXISTS "event" ( PRIMARY KEY("id") ); +-- This is the table that holds the events triggered by kw, currently pertains +-- to the executed commands that are saved and the pomodoro sessions created +CREATE TABLE IF NOT EXISTS "patch" ( + "id" INTEGER NOT NULL UNIQUE, + "date" TEXT DEFAULT (date('now', 'localtime')), + "time" TEXT DEFAULT (time('now', 'localtime')), + "status" VARCHAR(50) DEFAULT ('SENT') NOT NULL , + "title" TEXT NOT NULL, + CHECK ("status" IN ('SENT', 'APPROVED', 'MERGED', 'REVIEWED', 'REJECTED')) + PRIMARY KEY("id") +); + -- This is the relationship between an "event" that executes a given -- "command_label" CREATE TABLE IF NOT EXISTS "executed" ( diff --git a/documentation/conf.py b/documentation/conf.py index 83a930351..95ba4d066 100644 --- a/documentation/conf.py +++ b/documentation/conf.py @@ -181,6 +181,7 @@ ('man/features/kw-self-update', 'kw-self-update', 'kw self-update mechanism', ['David Tadokoro, Everaldo Junior'], 1), ('man/features/kw-patch-hub', 'kw-patch-hub', 'Terminal UI to interact with patches from lore.kernel.org', ['David Tadokoro, Rodrigo Siqueira'], 1), ('man/features/kw-bd', 'kw-bd', 'build and then deploy the kernel', ['Briza Mel de Sousa, Lorenzo Salvador'], 1), + ('man/features/kw-patch-track', 'kw-patch-track', 'track and display patches sent with kw-send-patch', ['João Guilherme Barbosa de Souza'], 1), ] diff --git a/documentation/man/features/kw-patch-track.rst b/documentation/man/features/kw-patch-track.rst new file mode 100644 index 000000000..96facace9 --- /dev/null +++ b/documentation/man/features/kw-patch-track.rst @@ -0,0 +1,15 @@ +================================================================== +kw-patch-track - Track and display patches sent with kw-send-patch +================================================================== + +.. _patch-track-doc: + +SYNOPSIS +======== +| *kw patch-track* + +DESCRIPTION +=========== +The `kw patch-track` feature deal with tracking the patches submissions using the +feature `kw send-patch`, keeping notes of it's shipping date, status, title, +making it possible to view shipments, and update their status as needed. \ No newline at end of file diff --git a/kw b/kw index a9089a226..cda51574e 100755 --- a/kw +++ b/kw @@ -268,6 +268,13 @@ function kw() patch_hub_main "$@" ) ;; + patch-track) + ( + include "${KW_LIB_DIR}/patch_track.sh" + + patch_track_main "$@" + ) + ;; clear-cache) include "${KW_LIB_DIR}/deploy.sh" diff --git a/src/patch_track.sh b/src/patch_track.sh new file mode 100644 index 000000000..009c4bd38 --- /dev/null +++ b/src/patch_track.sh @@ -0,0 +1,79 @@ +include "${KW_LIB_DIR}/lib/kwlib.sh" +include "${KW_LIB_DIR}/lib/kw_string.sh" + +declare -gA options_values + +function patch_track_main() +{ + local flag + + flag=${flag:-'SILENT'} + + if [[ -z "$*" ]]; then + complain 'Please, provide an argument' + patch_track_help "$@" + exit 22 # EINVAL + fi + + parse_patch_track "$@" + if [[ "$?" -gt 0 ]]; then + complain "${options_values['ERROR']}" + patch_track_help "$@" + exit 22 # EINVAL + fi + + return 0 +} + +# Parses the command-line arguments for the patch track operation. +# It populates the options_values associative array with parsed options. +# +# Return: +# Returns 22 if there are invalid arguments. +function parse_patch_track() +{ + local long_options='help' + local short_options='h' + local options + + options="$(kw_parse "$short_options" "$long_options" "$@")" + + if [[ "$?" != 0 ]]; then + options_values['ERROR']="$(kw_parse_get_errors 'kw patch_track' "$short_options" \ + "$long_options" "$@")" + return 22 # EINVAL + fi + + eval "set -- ${options}" + + while [[ "$#" -gt 0 ]]; do + case "$1" in + --help | -h) + patch_track_help '--help' + exit + ;; + --) + shift + ;; + *) + options_values['ERROR']="$1" + return 22 # EINVAL + ;; + esac + done +} + +# Displays help information for the patch track command. +# It prints usage instructions and available options. +# Return: +# Returns nothing +function patch_track_help() +{ + if [[ "$1" == --help ]]; then + include "$KW_LIB_DIR/help.sh" + kworkflow_man 'patch-track' + return + fi + + printf '%s\n' 'kw patch-track:' +} From e4714df171ae040ba009ce83cc229e4726bdf13b Mon Sep 17 00:00:00 2001 From: JGBSouza Date: Sun, 20 Apr 2025 16:49:23 -0300 Subject: [PATCH 02/33] src: patch_track: Adiciona nova funcao register_patch_track To support the new feature `kw patch-track`, it should be able to register the patches that are sent with kw send_patches. In this sense, add a new function register-patch-track that is called after the send_patch mail_send function, registering infos from the patch as an id, the status, date, time and title. Signed-off-by: JGBSouza --- src/lib/kwlib.sh | 19 ++++++++ src/patch_track.sh | 37 +++++++++++++++ src/send_patch.sh | 12 ++++- tests/unit/patch_track_test.sh | 86 ++++++++++++++++++++++++++++++++++ tests/unit/send_patch_test.sh | 25 +++++----- 5 files changed, 165 insertions(+), 14 deletions(-) create mode 100755 tests/unit/patch_track_test.sh diff --git a/src/lib/kwlib.sh b/src/lib/kwlib.sh index f67ccc6d4..7c55151cd 100644 --- a/src/lib/kwlib.sh +++ b/src/lib/kwlib.sh @@ -752,3 +752,22 @@ function get_git_repository_branches() _branches["$branch"]="$branch_metadata" done <<< "$output" } + +# This function receives a patch file and extracts it's subject +# @FILE_PATH: Path to the patch file +# +# Return: +# The title of the patch. +function get_patch_subject() +{ + local -r FILE_PATH="$1" + local patch_title + + if [[ ! -f "$FILE_PATH" ]]; then + return 1 # EPERM + fi + + patch_title=$(grep -m 1 "^Subject: " "$FILE_PATH" | sed 's/^Subject: \[PATCH[^]]*\] //') + + printf '%s\n' "$patch_title" +} diff --git a/src/patch_track.sh b/src/patch_track.sh index 009c4bd38..d48f739a6 100644 --- a/src/patch_track.sh +++ b/src/patch_track.sh @@ -1,6 +1,8 @@ include "${KW_LIB_DIR}/lib/kwlib.sh" include "${KW_LIB_DIR}/lib/kw_string.sh" +declare -gr DATABASE_PATCH_TABLE='patch' + declare -gA options_values function patch_track_main() @@ -25,6 +27,41 @@ function patch_track_main() return 0 } +# This function inserts each patch subject into the database and handles +# errors related to empty subjects or database insertion failures. +# +# @patches_subjects: Array of patch subjects to be registered. +# +# Return: +# Returns 0 if successful; 22 if there is an invalid argument or +# an error during insertion. +function register_patch_track() +{ + local -n _patches_subjects="$1" + local sql_operation_result + local ret + + for patch_subject in "${_patches_subjects[@]}"; do + if [[ -z "$patch_subject" ]]; then + complain 'Patch subject is empty' + return 61 # ENODATA + fi + + sql_operation_result=$(insert_into "$DATABASE_PATCH_TABLE" '("title")' "('${patch_subject}')" '' 'VERBOSE') + ret="$?" + + if [[ "$ret" -eq 2 || "$ret" -eq 61 ]]; then + complain "$sql_operation_result" + return 22 # EINVAL + elif [[ "$ret" -ne 0 ]]; then + complain "($LINENO):" $'Error while trying to insert patch into the database with the command:\n'"${sql_operation_result}" + return 22 # EINVAL + fi + done + + success "Patch registered successfully." +} + # Parses the command-line arguments for the patch track operation. # It populates the options_values associative array with parsed options. # diff --git a/src/send_patch.sh b/src/send_patch.sh index c6c9db98d..67d939edf 100644 --- a/src/send_patch.sh +++ b/src/send_patch.sh @@ -6,6 +6,7 @@ include "${KW_LIB_DIR}/lib/kw_config_loader.sh" include "${KW_LIB_DIR}/lib/kwlib.sh" include "${KW_LIB_DIR}/lib/kw_string.sh" +include "${KW_LIB_DIR}/patch_track.sh" # Hash containing user options declare -gA options_values @@ -24,6 +25,7 @@ declare -gr email_regex='[A-Za-z0-9_\.-]+@[A-Za-z0-9_-]+(\.[A-Za-z0-9]+)+' function send_patch_main() { local flag + declare -a patches_subjects=() flag=${flag:-'SILENT'} @@ -42,7 +44,9 @@ function send_patch_main() [[ -n "${options_values['VERBOSE']}" ]] && flag='VERBOSE' if [[ -n "${options_values['SEND']}" ]]; then - mail_send "$flag" + mail_send "$flag" patches_subjects + register_patch_track patches_subjects + return 0 fi @@ -87,6 +91,7 @@ function send_patch_main() function mail_send() { local flag="$1" + local -n patches_titles="$2" local opts="${send_patch_config[send_opts]}" local to_recipients="${options_values['TO']}" local cc_recipients="${options_values['CC']}" @@ -119,7 +124,7 @@ function mail_send() fi # Don't generate a cover letter when sending only one patch - patch_count="$(pre_generate_patches "$commit_range" "$version")" + patch_count="$(pre_generate_patches "$commit_range" "$version" 'patches_titles')" if [[ "$patch_count" -eq 1 ]]; then opts="$(sed 's/--cover-letter//g' <<< "$opts")" cover_letter='' @@ -182,6 +187,7 @@ function pre_generate_patches() { local commit_range="$1" local version="$2" + local -n _patches_titles="$3" local patch_cache="${KW_CACHE_DIR}/patches" local count=0 @@ -195,6 +201,8 @@ function pre_generate_patches() for patch_path in "${patch_cache}/"*; do if is_a_patch "$patch_path"; then ((count++)) + title=$(get_patch_subject "$patch_path") + _patches_titles+=("$title") fi done diff --git a/tests/unit/patch_track_test.sh b/tests/unit/patch_track_test.sh new file mode 100755 index 000000000..78ea04a8a --- /dev/null +++ b/tests/unit/patch_track_test.sh @@ -0,0 +1,86 @@ +#!/usr/bin/env bash + +include './src/patch_track.sh' +include './tests/unit/utils.sh' + +function oneTimeSetUp() +{ + declare -g DB_FILES + + export KW_ETC_DIR="${SHUNIT_TMPDIR}/etc/" + export KW_CACHE_DIR="${SHUNIT_TMPDIR}/cache/" + export KW_DATA_DIR="${SHUNIT_TMPDIR}" + + DB_FILES="$(realpath './tests/unit/samples/db_files')" + KW_DB_DIR="$(realpath './database')" +} + +function setUp() +{ + declare -gA options_values + declare -gA set_confs + + setupDatabase +} + +function tearDown() +{ + unset options_values + unset set_confs + + tearDownDatabase +} + +function setupDatabase() +{ + declare -g TEST_GROUP_NAME='TEST_GROUP' + declare -g TEST_CONTACT_INFOS=('name' 'email') + declare -g TEST_GROUP_ID + + execute_sql_script "${KW_DB_DIR}/kwdb.sql" > /dev/null 2>&1 + #sqlite3 "${KW_DATA_DIR}/kw.db" -batch "INSERT INTO \"${DATABASE_TABLE_GROUP}\" (name) VALUES (\"${TEST_GROUP_NAME}\");" + #TEST_GROUP_ID="$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT id FROM \"${DATABASE_TABLE_GROUP}\" WHERE name='${TEST_GROUP_NAME}';")" + #sqlite3 "${KW_DATA_DIR}/kw.db" -batch "INSERT INTO \"${DATABASE_TABLE_CONTACT}\" (name, email) VALUES (\"${TEST_CONTACT_INFOS[0]}\",\"${TEST_CONTACT_INFOS[1]}\");" + #sqlite3 "${KW_DATA_DIR}/kw.db" -batch "INSERT INTO \"${DATABASE_TABLE_CONTACT_GROUP}\" (contact_id, group_id) VALUES (1,1);" +} + +function tearDownDatabase() +{ + is_safe_path_to_remove "${KW_DATA_DIR}/kw.db" + if [[ "$?" == 0 ]]; then + rm "${KW_DATA_DIR}/kw.db" + fi +} + +function test_register_patch_track() +{ + local expected + local output + local ret + local _patches_titles + + # invalid values + _patches_titles=('') + output=$(register_patch_track _patches_titles) + ret="$?" + expected='Patch subject is empty' + assert_equals_helper 'Empty group should not be valid' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected an error' "$LINENO" "$ret" 61 + + _patches_titles=("invalid_name'") + output=$(register_patch_track _patches_titles 2> /dev/null) + ret="$?" + expected=$'Error while trying to insert patch into the database with the command:\n' + expected+='sqlite3 -init /home/joao-souza/Mac0499---TCC/database/pre_cmd.sql "/tmp/shunit.tH1Kq2/tmp/kw.db"' + expected+="-batch INSERT INTO patch (\"title\") VALUES ('invalid_name'');'" + assertContains "$output" "$expected" + assert_equals_helper 'Expected an error' "$LINENO" "$ret" 22 + + # valid values + _patches_titles=('valid_name') + output=$(register_patch_track _patches_titles) + ret="$?" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 0 +} + +invoke_shunit diff --git a/tests/unit/send_patch_test.sh b/tests/unit/send_patch_test.sh index 1adbde4aa..d009357cf 100755 --- a/tests/unit/send_patch_test.sh +++ b/tests/unit/send_patch_test.sh @@ -427,6 +427,7 @@ function test_mail_send() local expected local output local ret + declare -a patches_subjects=() cd "$FAKE_GIT" || { ret="$?" @@ -436,75 +437,75 @@ function test_mail_send() parse_mail_options - output=$(mail_send 'TEST_MODE') + output=$(mail_send 'TEST_MODE' patches_subjects) expected='git send-email @^' assert_equals_helper 'Testing send without options' "$LINENO" "$expected" "$output" parse_mail_options '--to=mail@test.com' - output=$(mail_send 'TEST_MODE') + output=$(mail_send 'TEST_MODE' patches_subjects) expected='git send-email --to="mail@test.com" @^' assert_equals_helper 'Testing send with to option' "$LINENO" "$expected" "$output" parse_mail_options '--to=name1@lala.com,name2@lala.xpto,name3 second ,test123@serious.gov' - output=$(mail_send 'TEST_MODE') + output=$(mail_send 'TEST_MODE' patches_subjects) expected='git send-email --to="name1@lala.com,name2@lala.xpto,name3 second ,test123@serious.gov" @^' assert_equals_helper 'Testing send with to option' "$LINENO" "$expected" "$output" parse_mail_options '--cc=mail@test.com' - output=$(mail_send 'TEST_MODE') + output=$(mail_send 'TEST_MODE' patches_subjects) expected='git send-email --cc="mail@test.com" @^' assert_equals_helper 'Testing send with c option' "$LINENO" "$expected" "$output" parse_mail_options '--cc=name1@lala.com,name2@lala.xpto,name3 second ,test123@serious.gov' - output=$(mail_send 'TEST_MODE') + output=$(mail_send 'TEST_MODE' patches_subjects) expected='git send-email --cc="name1@lala.com,name2@lala.xpto,name3 second ,test123@serious.gov" @^' assert_equals_helper 'Testing send with cc option' "$LINENO" "$expected" "$output" parse_mail_options '--simulate' - output=$(mail_send 'TEST_MODE') + output=$(mail_send 'TEST_MODE' patches_subjects) expected='git send-email --dry-run @^' assert_equals_helper 'Testing send with simulate option' "$LINENO" "$expected" "$output" parse_mail_options '--private' - output=$(mail_send 'TEST_MODE') + output=$(mail_send 'TEST_MODE' patches_subjects) expected="git send-email --suppress-cc=all @^" assert_equals_helper 'Testing send with to option' "$LINENO" "$expected" "$output" parse_mail_options '--rfc' - output=$(mail_send 'TEST_MODE') + output=$(mail_send 'TEST_MODE' patches_subjects) expected="git send-email --rfc @^" assert_equals_helper 'Testing send with rfc option' "$LINENO" "$expected" "$output" parse_mail_options '--to=mail@test.com' 'HEAD~' - output=$(mail_send 'TEST_MODE') + output=$(mail_send 'TEST_MODE' patches_subjects) expected='git send-email --to="mail@test.com" HEAD~' assert_equals_helper 'Testing send with patch option' "$LINENO" "$expected" "$output" parse_mail_options '--to=mail@test.com' -13 -v2 extra_args -- --other_arg - output=$(mail_send 'TEST_MODE') + output=$(mail_send 'TEST_MODE' patches_subjects) expected='git send-email --to="mail@test.com" extra_args --other_arg -13 -v2' assert_equals_helper 'Testing no options option' "$LINENO" "$expected" "$output" parse_mail_options '--to=mail@test.com' parse_configuration "$KW_MAIL_CONFIG_SAMPLE" send_patch_config - output=$(mail_send 'TEST_MODE') + output=$(mail_send 'TEST_MODE' patches_subjects) expected='git send-email --to="mail@test.com" --annotate --no-chain-reply-to --thread @^' assert_equals_helper 'Testing default option' "$LINENO" "$expected" "$output" parse_mail_options '--to=mail@test.com' '@^^' parse_configuration "$KW_CONFIG_SAMPLE" - output=$(mail_send 'TEST_MODE') + output=$(mail_send 'TEST_MODE' patches_subjects) expected='git send-email --to="mail@test.com" --annotate --cover-letter --no-chain-reply-to --thread @^^' assert_equals_helper 'Testing default option' "$LINENO" "$expected" "$output" From a7e259a75e09f15061ddf95e2d81c569cde4675d Mon Sep 17 00:00:00 2001 From: JGBSouza Date: Sun, 20 Apr 2025 17:16:17 -0300 Subject: [PATCH 03/33] src: patch_track: Add kw patch-track dashboard To support the new `kw patch-track` feature, it should be able to show the panel with the patches registered in the database. In this sense, add a new functionality `dashboard` that displays in the terminal the infos from all off the patches sent with kw-patch-track Signed-off-by: JGBSouza --- documentation/man/features/kw-patch-track.rst | 35 +++++- src/patch_track.sh | 110 +++++++++++++++++- tests/unit/patch_track_test.sh | 35 ++++-- 3 files changed, 169 insertions(+), 11 deletions(-) diff --git a/documentation/man/features/kw-patch-track.rst b/documentation/man/features/kw-patch-track.rst index 96facace9..06d004b10 100644 --- a/documentation/man/features/kw-patch-track.rst +++ b/documentation/man/features/kw-patch-track.rst @@ -12,4 +12,37 @@ DESCRIPTION =========== The `kw patch-track` feature deal with tracking the patches submissions using the feature `kw send-patch`, keeping notes of it's shipping date, status, title, -making it possible to view shipments, and update their status as needed. \ No newline at end of file +making it possible to view shipments, and update their status as needed. + +OPTIONS +======= +-d, \--dashboard: + Displays the patches dashboard, showing the patches id, created date-time, + status and title. + +-a , \--after : + Specify a date to look up for the patches after this date. + +-b ', \--before : + Specify a date to look up for the patches before this date. + +-f , \--from : + Specify a date to look up for the patches from this date. + +EXAMPLES +======== +To check your patches dashboard use: + + kw patch-track --dashboard + +For checking your patches within an specific date use: + + kw patch-track --dashboard --from + +To check your patches after an specific date: + + kw patch-track --dashboard --after + +And to check the patches before an specific date, use: + + kw patch-track --dashboard --before diff --git a/src/patch_track.sh b/src/patch_track.sh index d48f739a6..dd4545382 100644 --- a/src/patch_track.sh +++ b/src/patch_track.sh @@ -4,6 +4,7 @@ include "${KW_LIB_DIR}/lib/kw_string.sh" declare -gr DATABASE_PATCH_TABLE='patch' declare -gA options_values +declare -gA condition_array function patch_track_main() { @@ -24,6 +25,11 @@ function patch_track_main() exit 22 # EINVAL fi + if [[ -n "${options_values['DASHBOARD']}" ]]; then + show_patches_dashboard "${options_values['FROM']}" "${options_values['BEFORE']}" "${options_values['AFTER']}" "$flag" + return 0 + fi + return 0 } @@ -62,6 +68,81 @@ function register_patch_track() success "Patch registered successfully." } +# This function displays the patches dashboard based on provided filters. +# It fetches patches from the database according to the conditions +# and prints them in a formatted table. +# +# @flag: Display mode flag (e.g., SILENT). +# +# Return: +# No specific return value. +function show_patches_dashboard() +{ + local flag="$1" + local columns="$2" + local from="${options_values['FROM']}" + local before="${options_values['BEFORE']}" + local after="${options_values['AFTER']}" + local patches_info + declare -a patches_array + + if [[ -n "$from" ]]; then + condition_array=(['date,=']="${from}") + else + if [[ -n "$before" ]]; then + condition_array=(['date,<=']="${before}") + fi + if [[ -n "$after" ]]; then + condition_array=(['date,>=']="${after}") + fi + fi + + patches_info=$(select_from "$DATABASE_PATCH_TABLE" '' '' 'condition_array') + readarray -t patches_array <<< "$patches_info" + + print_patches_dashboard 'patches_array' "$columns" +} + +# Displays the patches dashboard based on provided filters. It +# fetches patches from the database according to the conditions +# and prints them in a formatted table. +# +# @_patches_array: an array formatted as: [index]=[id|date|time|status] +# for each of the patches that will be displayed. +# Return: +# No specific return value. +function print_patches_dashboard() +{ + local -n _patches_array="$1" + local columns="$2" + local id + local date + local time + local status + local title + local id_width=6 + local date_width=12 + local time_width=10 + local status_width=10 + local title_width=$(("$columns" - id_width - date_width - time_width - status_width - 6)) + + if [[ -z $columns ]]; then + columns="$(tput cols)" + fi + + printf "%-${id_width}s|%-${date_width}s|%-${time_width}s|%-${status_width}s|%s\n" "ID" "Date" "Time" "Status" "Title" + printf "%-${columns}s\n" | tr ' ' '-' + + # Print rows + for patch in "${!_patches_array[@]}"; do + IFS='|' read -r id date time status title <<< "${_patches_array[$patch]}" + printf "%-${id_width}s|%-${date_width}s|%-${time_width}s|%-${status_width}s|%s\n" "$id" "$date" "$time" "$status" "$title" + done + + tput cnorm > /dev/tty + printf "%-${columns}s\n" | tr ' ' '-' +} + # Parses the command-line arguments for the patch track operation. # It populates the options_values associative array with parsed options. # @@ -69,8 +150,8 @@ function register_patch_track() # Returns 22 if there are invalid arguments. function parse_patch_track() { - local long_options='help' - local short_options='h' + local long_options='help,dashboard,from:,before:,after:' + local short_options='h,d,f:,b:,a:' local options options="$(kw_parse "$short_options" "$long_options" "$@")" @@ -83,8 +164,30 @@ function parse_patch_track() eval "set -- ${options}" + # Default values + options_values['DASHBOARD']='' + options_values['FROM']='' + options_values['BEFORE']='' + options_values['AFTER']='' + while [[ "$#" -gt 0 ]]; do case "$1" in + --dashboard | -d) + options_values['DASHBOARD']=1 + shift + ;; + --from | -f) + options_values['FROM']="$2" + shift 2 + ;; + --before | -b) + options_values['BEFORE']="$2" + shift 2 + ;; + --after | -a) + options_values['AFTER']="$2" + shift 2 + ;; --help | -h) patch_track_help '--help' exit @@ -112,5 +215,6 @@ function patch_track_help() return fi - printf '%s\n' 'kw patch-track:' + printf '%s\n' 'kw patch-track:' \ + ' patch-track (-d|--dashboard) [[--from ] | [--after ] [--before ]] - Show patches dashboard in chronological order ' } diff --git a/tests/unit/patch_track_test.sh b/tests/unit/patch_track_test.sh index 78ea04a8a..2f2d68a77 100755 --- a/tests/unit/patch_track_test.sh +++ b/tests/unit/patch_track_test.sh @@ -33,15 +33,12 @@ function tearDown() function setupDatabase() { - declare -g TEST_GROUP_NAME='TEST_GROUP' - declare -g TEST_CONTACT_INFOS=('name' 'email') - declare -g TEST_GROUP_ID + declare -g TEST_PATCH_TITLE='TEST_PATCH' + declare -g TEST_PATCH_ID execute_sql_script "${KW_DB_DIR}/kwdb.sql" > /dev/null 2>&1 - #sqlite3 "${KW_DATA_DIR}/kw.db" -batch "INSERT INTO \"${DATABASE_TABLE_GROUP}\" (name) VALUES (\"${TEST_GROUP_NAME}\");" - #TEST_GROUP_ID="$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT id FROM \"${DATABASE_TABLE_GROUP}\" WHERE name='${TEST_GROUP_NAME}';")" - #sqlite3 "${KW_DATA_DIR}/kw.db" -batch "INSERT INTO \"${DATABASE_TABLE_CONTACT}\" (name, email) VALUES (\"${TEST_CONTACT_INFOS[0]}\",\"${TEST_CONTACT_INFOS[1]}\");" - #sqlite3 "${KW_DATA_DIR}/kw.db" -batch "INSERT INTO \"${DATABASE_TABLE_CONTACT_GROUP}\" (contact_id, group_id) VALUES (1,1);" + sqlite3 "${KW_DATA_DIR}/kw.db" -batch "INSERT INTO \"${DATABASE_PATCH_TABLE}\" (title) VALUES (\"${TEST_PATCH_TITLE}\");" + TEST_PATCH_ID="$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT id FROM \"${DATABASE_PATCH_TABLE}\" WHERE title='${TEST_PATCH_TITLE}';")" } function tearDownDatabase() @@ -83,4 +80,28 @@ function test_register_patch_track() assert_equals_helper 'Expected no error' "$LINENO" "$ret" 0 } +function test_show_patches_dashboard() +{ + local expected + local output + local ret + local patch_info + local IFS + + # valid values + patch_info="$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT * FROM \"${DATABASE_PATCH_TABLE}\" WHERE title = (\"${TEST_PATCH_TITLE}\");")" + IFS='|' read -r id date time status title <<< "$patch_info" + + output=$(show_patches_dashboard '' 150) + ret="$?" + + expected+=$'ID |Date |Time |Status |Title\n' + expected+=$'------------------------------------------------------------------------------------------------------------------------------------------------------\n' + expected+="${id} |${date} |${time} |${status} |${title}" + expected+=$'\n' + expected+=$'------------------------------------------------------------------------------------------------------------------------------------------------------' + assert_equals_helper 'Empty group should not be valid' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 0 +} + invoke_shunit From 7bfc8109a69b37310e28059a47c8acdf5fe25b8a Mon Sep 17 00:00:00 2001 From: JGBSouza Date: Sun, 20 Apr 2025 21:13:47 -0300 Subject: [PATCH 04/33] src: patch_track: Add new feature set-status To support the new `kw patch-track` feature, we need a method to set the patches status when they are updated. In this sense, add the new functionality `set-status` that receives a valid status and a flag `id` that indicates wich path status will be changed. Signed-off-by: JGBSouza --- documentation/man/features/kw-patch-track.rst | 11 + src/patch_track.sh | 147 +++++++++++++- tests/unit/patch_track_test.sh | 192 ++++++++++++++++++ 3 files changed, 341 insertions(+), 9 deletions(-) diff --git a/documentation/man/features/kw-patch-track.rst b/documentation/man/features/kw-patch-track.rst index 06d004b10..e4fc99c1a 100644 --- a/documentation/man/features/kw-patch-track.rst +++ b/documentation/man/features/kw-patch-track.rst @@ -29,6 +29,13 @@ OPTIONS -f , \--from : Specify a date to look up for the patches from this date. +\--id '' + Specify a patch id + +--s='', \--set-status='' + Set a new status for an specific patch as SENT, APPROVED, + REJECTED, MERGED or REVIEWED. + EXAMPLES ======== To check your patches dashboard use: @@ -46,3 +53,7 @@ To check your patches after an specific date: And to check the patches before an specific date, use: kw patch-track --dashboard --before + +To change the patch status for an specific patch + + kw patch-track --id --set-status \ No newline at end of file diff --git a/src/patch_track.sh b/src/patch_track.sh index dd4545382..9604a1a14 100644 --- a/src/patch_track.sh +++ b/src/patch_track.sh @@ -5,6 +5,7 @@ declare -gr DATABASE_PATCH_TABLE='patch' declare -gA options_values declare -gA condition_array +declare -gA updates_array function patch_track_main() { @@ -30,6 +31,19 @@ function patch_track_main() return 0 fi + if [[ -n "${options_values['SET_STATUS']}" ]]; then + if [[ -z "${options_values['PATCH_ID']}" ]]; then + complain 'Patch id not specified with `--id `' + return 22 # EINVAL + fi + + set_patch_status "${options_values['PATCH_ID']}" "${options_values['STATUS']}" "$flag" + if [[ "$?" -eq 0 ]]; then + echo "Patch status updated successfully." + fi + return 0 + fi + return 0 } @@ -143,6 +157,110 @@ function print_patches_dashboard() printf "%-${columns}s\n" | tr ' ' '-' } +# This sets the status of a specified patch. It updates the status +# of a patch identified by its ID and handles errors related to +# empty IDs or statuses, as well as database update failures. +# +# @patch_id: ID of the patch to be updated. +# @patch_new_status: New status to be set for the patch. +# +# Return: +# Returns 0 if successful; 22 if there is an invalid argument or +# an error during the update. +function set_patch_status() +{ + local patch_id="$1" + local patch_new_status="$2" + local formatted_status + local sql_operation_result + local ret + + if [[ -z "$patch_id" ]]; then + complain 'Patch ID is empty' + return 61 # ENODATA + fi + + if [[ -z "$patch_new_status" ]]; then + complain 'New status is empty' + return 61 # ENODATA + fi + + formatted_status=$(check_valid_status "$patch_new_status") + + if [[ "$?" -ne 0 ]]; then + formatted_status=$(get_patch_status) + fi + + condition_array=(['id']="${patch_id}") + updates_array=(['status']="${formatted_status}") + + sql_operation_result=$(update_into "$DATABASE_PATCH_TABLE" 'updates_array' '' 'condition_array' 'VERBOSE') + ret="$?" + + if [[ "$ret" -eq 2 || "$ret" -eq 61 ]]; then + complain "$sql_operation_result" + return 22 # EINVAL + elif [[ "$ret" -ne 0 ]]; then + complain "($LINENO):" $'Error while trying to update patch status in the database with the command:\n'"${sql_operation_result}" + return 22 # EINVAL + fi + + return 0 +} + +# This function prompts the user for the current patch status and validates +# it. It returns the validated status or prompts again if the status +# is invalid. +# +# Return: +# Returns the formatted status. +function get_patch_status() +{ + local status + local formatted_status + local message=$'Enter your current patch status' + local default_status=$'[Sent - S], [Reviewed - RW], [Approved - A], [Rejected - R], [Merged - M]' + + status=$(ask_with_default "$message" "$default_status") + formatted_status=$(check_valid_status "$status") + + if [[ "$?" -ne 0 ]]; then + formatted_status=$(get_patch_status) + fi + + printf '%s' "$formatted_status" +} + +# This function checks if the provided status is valid and returns +# the corresponding formatted status. +# +# @status: The status to be validated. +# +# Return: +# Returns 0 if valid; non-zero otherwise. +function check_valid_status() +{ + local status="$1" + + if [[ -z "$status" ]]; then + return 61 # ENODATA + elif [[ "$status" =~ ^([sS][eE][nN][tT]|[sS])+$ ]]; then + printf '%s' 'SENT' + elif [[ "$status" =~ ^([aA][pP][pP][rR][oO][vV][eE][dD]|[aA])+$ ]]; then + printf '%s' 'APPROVED' + elif [[ "$status" =~ ^([rR][eE][jJ][eE][cC][tT][eE][dD]|[rR])+$ ]]; then + printf '%s' 'REJECTED' + elif [[ "$status" =~ ^([mM][eE][rR][gG][eE][dD]|[mM])+$ ]]; then + printf '%s' 'MERGED' + elif [[ "$status" =~ ^([rR][eE][vV][iI][eE][wW][eE][dD]|[rR][wW])+$ ]]; then + printf '%s' 'REVIEWED' + else + return 22 # EINVAL + fi + + return 0 +} + # Parses the command-line arguments for the patch track operation. # It populates the options_values associative array with parsed options. # @@ -150,9 +268,10 @@ function print_patches_dashboard() # Returns 22 if there are invalid arguments. function parse_patch_track() { - local long_options='help,dashboard,from:,before:,after:' - local short_options='h,d,f:,b:,a:' + local long_options='help,dashboard,from:,before:,after:,id:,set-status:' + local short_options='d,f:,b:,a:,s:' local options + local option options="$(kw_parse "$short_options" "$long_options" "$@")" @@ -166,6 +285,9 @@ function parse_patch_track() # Default values options_values['DASHBOARD']='' + options_values['PATCH_ID']='' + options_values['SET_STATUS']= + options_values['STATUS']='' options_values['FROM']='' options_values['BEFORE']='' options_values['AFTER']='' @@ -176,6 +298,16 @@ function parse_patch_track() options_values['DASHBOARD']=1 shift ;; + --id) + options_values['PATCH_ID']="$2" + shift 2 + ;; + --set-status | -s) + option="$(str_strip "${2}")" + options_values['SET_STATUS']=1 + options_values['STATUS']="$option" + shift 2 + ;; --from | -f) options_values['FROM']="$2" shift 2 @@ -189,15 +321,11 @@ function parse_patch_track() shift 2 ;; --help | -h) - patch_track_help '--help' + patch_track_help "$1" exit ;; - --) - shift - ;; *) - options_values['ERROR']="$1" - return 22 # EINVAL + shift ;; esac done @@ -216,5 +344,6 @@ function patch_track_help() fi printf '%s\n' 'kw patch-track:' \ - ' patch-track (-d|--dashboard) [[--from ] | [--after ] [--before ]] - Show patches dashboard in chronological order ' + ' patch-track (-d|--dashboard) [[--from ] | [--after ] [--before ]] - Show patches dashboard in chronological order ' \ + ' patch-track (--id ) [-s[=]| --set-status[=]] - Set the patch status ' } diff --git a/tests/unit/patch_track_test.sh b/tests/unit/patch_track_test.sh index 2f2d68a77..cff4779ad 100755 --- a/tests/unit/patch_track_test.sh +++ b/tests/unit/patch_track_test.sh @@ -104,4 +104,196 @@ function test_show_patches_dashboard() assert_equals_helper 'Expected no error' "$LINENO" "$ret" 0 } +function test_set_patch_status() +{ + local expected + local output + local ret + + # valid values + set_patch_status "$TEST_PATCH_ID" 'MERGED' + ret="$?" + expected='MERGED' + output=$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT status FROM \"${DATABASE_PATCH_TABLE}\" WHERE ID=\"${TEST_PATCH_ID}\";") + assert_equals_helper 'Empty group should not be valid' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 0 + + set_patch_status "$TEST_PATCH_ID" 'SENT' + ret="$?" + expected='SENT' + output=$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT status FROM \"${DATABASE_PATCH_TABLE}\" WHERE ID=\"${TEST_PATCH_ID}\";") + assert_equals_helper 'Empty group should not be valid' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 0 + + printf 'REVIEWED\n' | set_patch_status "$TEST_PATCH_ID" 'INVALID' + ret="$?" + expected='REVIEWED' + output=$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT status FROM \"${DATABASE_PATCH_TABLE}\" WHERE ID=\"${TEST_PATCH_ID}\";") + assert_equals_helper 'Empty group should not be valid' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 0 + + # invalid values + output=$(set_patch_status '' 'SENT') + ret="$?" + expected='Patch ID is empty' + assert_equals_helper 'Empty group should not be valid' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 61 + + output=$(set_patch_status "$TEST_PATCH_ID" '') + ret="$?" + expected='New status is empty' + assert_equals_helper 'Empty group should not be valid' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error' "$LINENO" "$ret" 61 +} + +function test_get_patch_status() +{ + local expected + local output + local ret + + output=$(printf 's\n' | get_patch_status) + ret="$?" + expected='SENT' + assert_equals_helper 'Status should have been received' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error for "s"' "$LINENO" "$ret" 0 +} +function test_check_valid_status() +{ + local expected + local output + local ret + + # SENT + output=$(check_valid_status 's') + ret="$?" + expected='SENT' + assert_equals_helper 'Status "s" should be valid' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error for "s"' "$LINENO" "$ret" 0 + + output=$(check_valid_status 'sent') + ret="$?" + expected='SENT' + assert_equals_helper 'Status "sent" should be valid' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error for "sent"' "$LINENO" "$ret" 0 + + output=$(check_valid_status 'SeNt') + ret="$?" + expected='SENT' + assert_equals_helper 'Status "SeNt" should be valid' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error for "SeNt"' "$LINENO" "$ret" 0 + + output=$(check_valid_status 'SENT') + ret="$?" + expected='SENT' + assert_equals_helper 'Status "SENT" should be valid' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error for "SENT"' "$LINENO" "$ret" 0 + + # APPROVED + output=$(check_valid_status 'a') + ret="$?" + expected='APPROVED' + assert_equals_helper 'Status "a" should be valid' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error for "a"' "$LINENO" "$ret" 0 + + output=$(check_valid_status 'approved') + ret="$?" + expected='APPROVED' + assert_equals_helper 'Status "approved" should be valid' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error for "approved"' "$LINENO" "$ret" 0 + + output=$(check_valid_status 'ApPrOvEd') + ret="$?" + expected='APPROVED' + assert_equals_helper 'Status "ApPrOvEd" should be valid' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error for "ApPrOvEd"' "$LINENO" "$ret" 0 + + output=$(check_valid_status 'APPROVED') + ret="$?" + expected='APPROVED' + assert_equals_helper 'Status "APPROVED" should be valid' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error for "APPROVED"' "$LINENO" "$ret" 0 + + # REJECTED + output=$(check_valid_status 'r') + ret="$?" + expected='REJECTED' + assert_equals_helper 'Status "r" should be valid' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error for "r"' "$LINENO" "$ret" 0 + + output=$(check_valid_status 'rejected') + ret="$?" + expected='REJECTED' + assert_equals_helper 'Status "rejected" should be valid' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error for "rejected"' "$LINENO" "$ret" 0 + + output=$(check_valid_status 'ReJeCtEd') + ret="$?" + expected='REJECTED' + assert_equals_helper 'Status "ReJeCtEd" should be valid' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error for "ReJeCtEd"' "$LINENO" "$ret" 0 + + output=$(check_valid_status 'REJECTED') + ret="$?" + expected='REJECTED' + assert_equals_helper 'Status "REJECTED" should be valid' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error for "REJECTED"' "$LINENO" "$ret" 0 + + # MERGED + output=$(check_valid_status 'm') + ret="$?" + expected='MERGED' + assert_equals_helper 'Status "m" should be valid' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error for "m"' "$LINENO" "$ret" 0 + + output=$(check_valid_status 'merged') + ret="$?" + expected='MERGED' + assert_equals_helper 'Status "merged" should be valid' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error for "merged"' "$LINENO" "$ret" 0 + + output=$(check_valid_status 'MeRgEd') + ret="$?" + expected='MERGED' + assert_equals_helper 'Status "MeRgEd" should be valid' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error for "MeRgEd"' "$LINENO" "$ret" 0 + + output=$(check_valid_status 'MERGED') + ret="$?" + expected='MERGED' + assert_equals_helper 'Status "MERGED" should be valid' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error for "MERGED"' "$LINENO" "$ret" 0 + + # REVIEWED + output=$(check_valid_status 'rw') + ret="$?" + expected='REVIEWED' + assert_equals_helper 'Status "rw" should be valid' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error for "rw"' "$LINENO" "$ret" 0 + + output=$(check_valid_status 'Rw') + ret="$?" + expected='REVIEWED' + assert_equals_helper 'Status "Rw" should be valid' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error for "Rw"' "$LINENO" "$ret" 0 + + output=$(check_valid_status 'REVIEWED') + ret="$?" + expected='REVIEWED' + assert_equals_helper 'Status "REVIEWED" should be valid' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error for "REVIEWED"' "$LINENO" "$ret" 0 + + output=$(check_valid_status 'ReViEwEd') + ret="$?" + expected='REVIEWED' + assert_equals_helper 'Status "ReViEwEd" should be valid' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected no error for "ReViEwEd"' "$LINENO" "$ret" 0 + + #invalid values + check_valid_status 'InVaLID' + ret="$?" + assert_equals_helper 'Expected no error for "ReViEwEd"' "$LINENO" "$ret" 22 + +} + invoke_shunit From 50d49c244088452691ce0005ecc765101b9d6a16 Mon Sep 17 00:00:00 2001 From: --local <--local> Date: Sun, 21 Sep 2025 14:42:30 -0300 Subject: [PATCH 05/33] add: adiciona novos modelos banco de dados Signed-off-by: --local <--local> --- database/kwdb.sql | 76 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 71 insertions(+), 5 deletions(-) diff --git a/database/kwdb.sql b/database/kwdb.sql index 45d47ba80..53d2c1c5c 100644 --- a/database/kwdb.sql +++ b/database/kwdb.sql @@ -49,15 +49,81 @@ CREATE TABLE IF NOT EXISTS "event" ( -- This is the table that holds the events triggered by kw, currently pertains -- to the executed commands that are saved and the pomodoro sessions created CREATE TABLE IF NOT EXISTS "patch" ( - "id" INTEGER NOT NULL UNIQUE, - "date" TEXT DEFAULT (date('now', 'localtime')), - "time" TEXT DEFAULT (time('now', 'localtime')), - "status" VARCHAR(50) DEFAULT ('SENT') NOT NULL , + "id" INTEGER, + "created_at" TEXT DEFAULT (datetime('now','localtime')), + "status" VARCHAR(50) DEFAULT ('SENT') NOT NULL, "title" TEXT NOT NULL, - CHECK ("status" IN ('SENT', 'APPROVED', 'MERGED', 'REVIEWED', 'REJECTED')) + "last_patch_id" INTEGER, + "outdated" INTEGER NOT NULL CHECK ("outdated" IN (0, 1)) DEFAULT 0, + "version" INTEGER NOT NULL, + "patch_serie_id" INTEGER NOT NULL, + CHECK ("status" IN ('SENT', 'APPROVED', 'MERGED', 'REVIEWED', 'REJECTED', 'OUTDATED')), + PRIMARY KEY("id"), + FOREIGN KEY ("last_patch_id") REFERENCES "patch"("id") ON DELETE CASCADE, + FOREIGN KEY ("patch_serie_id") REFERENCES "patch_serie"("id") ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS "tag" ( + "id" INTEGER, + "name" VARCHAR(50) NOT NULL UNIQUE, + PRIMARY KEY("id") +); + +CREATE TABLE IF NOT EXISTS "patch_tag_relation" ( + "id" INTEGER, + "tag_id" INTEGER NOT NULL, + "patch_id" INTEGER NOT NULL, + PRIMARY KEY("id"), + FOREIGN KEY ("tag_id") REFERENCES "tag"("id") ON DELETE CASCADE, + FOREIGN KEY ("patch_id") REFERENCES "patch"("id") ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS "submission" ( + "id" INTEGER, + "created_at" TEXT DEFAULT (datetime('now','localtime')), + "patch_serie_id" INTEGER NOT NULL, + PRIMARY KEY("id"), + FOREIGN KEY ("patch_serie_id") REFERENCES "patch_serie"("id") ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS "patch_submission_relation" ( + "patch_id" INTEGER NOT NULL, + "submission_id" INTEGER NOT NULL, + PRIMARY KEY ("patch_id", "submission_id"), + FOREIGN KEY ("submission_id") REFERENCES "submission"("id") ON DELETE CASCADE, + FOREIGN KEY ("patch_id") REFERENCES "patch"("id") ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS "patch_serie" ( + "id" INTEGER, + "created_at" TEXT DEFAULT (datetime('now','localtime')), + "status" VARCHAR(50) DEFAULT ('SENT') NOT NULL, + "title" TEXT NOT NULL, + "last_interaction_at" TEXT DEFAULT (datetime('now','localtime')), + "active" INTEGER NOT NULL CHECK ("active" IN (0, 1)) DEFAULT 1, + "author_email" TEXT NOT NULL, + "repository_id" INTEGER, + PRIMARY KEY("id"), + FOREIGN KEY ("repository_id") REFERENCES "repository"("id") ON DELETE CASCADE, + FOREIGN KEY ("next_patch") REFERENCES "patch"("id") ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS "repository" ( + "id" INTEGER, + "created_at" TEXT DEFAULT (datetime('now','localtime')), + "name" TEXT NOT NULL UNIQUE, + "origin_url" TEXT NOT NULL UNIQUE, PRIMARY KEY("id") ); +CREATE TABLE IF NOT EXISTS "repository_maintainer" ( + "id" INTEGER, + "repository_id" INTEGER NOT NULL, + "contact_id" INTEGER NOT NULL, + PRIMARY KEY ("repository_id", "contact_id"), + FOREIGN KEY ("repository_id") REFERENCES "repository"("id") ON DELETE CASCADE +); + -- This is the relationship between an "event" that executes a given -- "command_label" CREATE TABLE IF NOT EXISTS "executed" ( From f14a1cb90a734870c1b53652bfbcead85c47c2a7 Mon Sep 17 00:00:00 2001 From: --local <--local> Date: Sun, 5 Oct 2025 15:20:21 -0300 Subject: [PATCH 06/33] save: evitando perder Signed-off-by: --local <--local> --- database/kwdb.sql | 35 +- output.log | 0 src/lib/kw_db.sh | 65 +- src/lib/kwlib.sh | 14 +- .../patch_track_utils/contribution_utils.sh | 155 +++++ .../patch_track_utils/patch_track_utils.sh | 625 ++++++++++++++++++ src/lib/patch_track_utils/patch_utils.sh | 282 ++++++++ src/lib/patch_track_utils/submission_utils.sh | 193 ++++++ src/patch_track.sh | 67 +- src/send_patch.sh | 4 +- tests/unit/patch_track_test.sh | 487 ++++++++------ 11 files changed, 1681 insertions(+), 246 deletions(-) create mode 100644 output.log create mode 100644 src/lib/patch_track_utils/contribution_utils.sh create mode 100644 src/lib/patch_track_utils/patch_track_utils.sh create mode 100644 src/lib/patch_track_utils/patch_utils.sh create mode 100644 src/lib/patch_track_utils/submission_utils.sh diff --git a/database/kwdb.sql b/database/kwdb.sql index 53d2c1c5c..a8135036b 100644 --- a/database/kwdb.sql +++ b/database/kwdb.sql @@ -56,14 +56,16 @@ CREATE TABLE IF NOT EXISTS "patch" ( "last_patch_id" INTEGER, "outdated" INTEGER NOT NULL CHECK ("outdated" IN (0, 1)) DEFAULT 0, "version" INTEGER NOT NULL, - "patch_serie_id" INTEGER NOT NULL, - CHECK ("status" IN ('SENT', 'APPROVED', 'MERGED', 'REVIEWED', 'REJECTED', 'OUTDATED')), + "author" TEXT, + "contribution_id" INTEGER NOT NULL, + "commit_hash" TEXT, + CHECK ("status" IN ('SENT', 'APPROVED', 'MERGED', 'REVIEWED', 'REJECTED')), PRIMARY KEY("id"), FOREIGN KEY ("last_patch_id") REFERENCES "patch"("id") ON DELETE CASCADE, - FOREIGN KEY ("patch_serie_id") REFERENCES "patch_serie"("id") ON DELETE CASCADE + FOREIGN KEY ("contribution_id") REFERENCES "contribution"("id") ON DELETE CASCADE ); -CREATE TABLE IF NOT EXISTS "tag" ( +CREATE TABLE IF NOT EXISTS "patch_tag" ( "id" INTEGER, "name" VARCHAR(50) NOT NULL UNIQUE, PRIMARY KEY("id") @@ -74,46 +76,49 @@ CREATE TABLE IF NOT EXISTS "patch_tag_relation" ( "tag_id" INTEGER NOT NULL, "patch_id" INTEGER NOT NULL, PRIMARY KEY("id"), - FOREIGN KEY ("tag_id") REFERENCES "tag"("id") ON DELETE CASCADE, + FOREIGN KEY ("tag_id") REFERENCES "patch_tag"("id") ON DELETE CASCADE, FOREIGN KEY ("patch_id") REFERENCES "patch"("id") ON DELETE CASCADE ); CREATE TABLE IF NOT EXISTS "submission" ( "id" INTEGER, "created_at" TEXT DEFAULT (datetime('now','localtime')), - "patch_serie_id" INTEGER NOT NULL, + "contribution_id" INTEGER NOT NULL, + "send_by" TEXT NOT NULL, PRIMARY KEY("id"), - FOREIGN KEY ("patch_serie_id") REFERENCES "patch_serie"("id") ON DELETE CASCADE + FOREIGN KEY ("contribution_id") REFERENCES "contribution"("id") ON DELETE CASCADE ); -CREATE TABLE IF NOT EXISTS "patch_submission_relation" ( +CREATE TABLE IF NOT EXISTS "patch_submission" ( "patch_id" INTEGER NOT NULL, "submission_id" INTEGER NOT NULL, - PRIMARY KEY ("patch_id", "submission_id"), + "message_id" TEXT NOT NULL, + PRIMARY KEY ("patch_id", "submission_id", "message_id"), FOREIGN KEY ("submission_id") REFERENCES "submission"("id") ON DELETE CASCADE, FOREIGN KEY ("patch_id") REFERENCES "patch"("id") ON DELETE CASCADE ); -CREATE TABLE IF NOT EXISTS "patch_serie" ( +CREATE TABLE IF NOT EXISTS "contribution" ( "id" INTEGER, "created_at" TEXT DEFAULT (datetime('now','localtime')), "status" VARCHAR(50) DEFAULT ('SENT') NOT NULL, - "title" TEXT NOT NULL, + "title" TEXT NOT NULL UNIQUE, "last_interaction_at" TEXT DEFAULT (datetime('now','localtime')), "active" INTEGER NOT NULL CHECK ("active" IN (0, 1)) DEFAULT 1, "author_email" TEXT NOT NULL, "repository_id" INTEGER, PRIMARY KEY("id"), - FOREIGN KEY ("repository_id") REFERENCES "repository"("id") ON DELETE CASCADE, - FOREIGN KEY ("next_patch") REFERENCES "patch"("id") ON DELETE CASCADE + FOREIGN KEY ("repository_id") REFERENCES "repository"("id") ON DELETE CASCADE ); CREATE TABLE IF NOT EXISTS "repository" ( "id" INTEGER, "created_at" TEXT DEFAULT (datetime('now','localtime')), - "name" TEXT NOT NULL UNIQUE, - "origin_url" TEXT NOT NULL UNIQUE, + "name" TEXT NOT NULL, + "origin_url" TEXT NOT NULL, + "branch_name" TEXT NOT NULL, PRIMARY KEY("id") + UNIQUE("origin_url", "branch_name") ); CREATE TABLE IF NOT EXISTS "repository_maintainer" ( diff --git a/output.log b/output.log new file mode 100644 index 000000000..e69de29bb diff --git a/src/lib/kw_db.sh b/src/lib/kw_db.sh index e81d1715c..4aaa8f034 100644 --- a/src/lib/kw_db.sh +++ b/src/lib/kw_db.sh @@ -203,10 +203,11 @@ function select_from() local columns="${2:-"*"}" local pre_cmd="$3" local _condition_array="$4" - local order_by=${5:-''} - local flag=${6:-'SILENT'} - local db="${7:-"$DB_NAME"}" - local db_folder="${8:-"$KW_DATA_DIR"}" + local order_by=${5:-} + local limit=${6-} + local flag=${7:-'SILENT'} + local db="${8:-"$DB_NAME"}" + local db_folder="${9:-"$KW_DATA_DIR"}" local where_clause local db_path local query @@ -233,6 +234,10 @@ function select_from() query="${query::-2} ORDER BY ${order_by} ;" fi + if [[ -n "${limit}" ]]; then + query="${query::-2} LIMIT ${limit} ;" + fi + cmd="sqlite3 -init "${KW_DB_DIR}/pre_cmd.sql" -cmd \"${pre_cmd}\" \"${db_path}\" -batch \"${query}\"" cmd_manager "$flag" "$cmd" } @@ -389,3 +394,55 @@ function format_values_db() printf '%s\n' "${values%?}" # removes last comma } + +function get_database_table_info() +{ + local _database_table_name="$1" + local _entity_infos="$2" + local -n _table_info_condition_array="$3" + local _order_by="${4:-}" + local _limit="${5:-}" + + sql_operation_result="$(select_from "$_database_table_name" "$_entity_infos" '' '_table_info_condition_array' "$_order_by" "$_limit")" + ret="$?" + + if [[ "$ret" -eq 2 || "$ret" -eq 61 ]]; then + complain "$sql_operation_result" + return "$ret" # EINVAL + elif [[ "$ret" -ne 0 ]]; then + complain "($LINENO): Error while trying to get ${_database_table_name} info from the database with the command:"$'\n'"${sql_operation_result}" + return "$ret" # EINVAL + fi + + printf '%s\n' "$sql_operation_result" + return 0 +} + +function check_existence() +{ + local _database_table_name="$1" + local -n _check_existence_condition_array="$2" + local sql_operation_result + local ret + + sql_operation_result="$(get_database_table_info "$_database_table_name" 'COUNT(*)' '_check_existence_condition_array')" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$sql_operation_result" + return "$ret" # EINVAL + fi + + if [[ -z "$sql_operation_result" ]]; then + complain "Error while trying to check entity existence in ${_database_table_name}" + return "$ret" # EINVAL + fi + + if [[ "$sql_operation_result" -eq 0 ]]; then + printf '%s\n' 0 + else + printf '%s\n' 1 + fi + + return 0 +} \ No newline at end of file diff --git a/src/lib/kwlib.sh b/src/lib/kwlib.sh index 7c55151cd..be1b31b55 100644 --- a/src/lib/kwlib.sh +++ b/src/lib/kwlib.sh @@ -753,21 +753,21 @@ function get_git_repository_branches() done <<< "$output" } -# This function receives a patch file and extracts it's subject +# Extract patch metadata into an associative array and serialize to base64 # @FILE_PATH: Path to the patch file # -# Return: -# The title of the patch. -function get_patch_subject() +# Return (stdout): +# Base64 string with serialized key=value pairs +function get_patch_commit_hash() { local -r FILE_PATH="$1" - local patch_title + local patch_commit if [[ ! -f "$FILE_PATH" ]]; then return 1 # EPERM fi - patch_title=$(grep -m 1 "^Subject: " "$FILE_PATH" | sed 's/^Subject: \[PATCH[^]]*\] //') + patch_commit=$(grep -m 1 "^From " "$FILE_PATH" | awk '{print $2}') - printf '%s\n' "$patch_title" + printf '%s\n' "${patch_commit}aaaaaaaa" } diff --git a/src/lib/patch_track_utils/contribution_utils.sh b/src/lib/patch_track_utils/contribution_utils.sh new file mode 100644 index 000000000..c9dc37c3f --- /dev/null +++ b/src/lib/patch_track_utils/contribution_utils.sh @@ -0,0 +1,155 @@ +include "${KW_LIB_DIR}/lib/kw_db.sh" + +declare -gr DATABASE_CONTRIBUTION_TABLE='contribution' +declare -Ag condition_array + +function insert_contribution() +{ + local _contribution_title="$1" + local _author_email="$2" + local _repository_id="$3" + + # Checagem de valores nulos + if [[ -z "$_contribution_title" || -z "$_author_email" || -z "$_repository_id" ]]; then + complain "($LINENO): missing mandatory field(s) for insert_contribution" + return 22 # EINVAL + fi + + sql_operation_result=$(insert_into "$DATABASE_CONTRIBUTION_TABLE" \ + '("title", "author_email", "repository_id")' \ + "('${_contribution_title}', '${_author_email}', '${_repository_id}')") + + ret="$?" + + if [[ "$ret" -eq 2 || "$ret" -eq 61 ]]; then + complain "$sql_operation_result" + return 22 # EINVAL + elif [[ "$ret" -ne 0 ]]; then + complain "($LINENO):" $'Error while trying to insert new contribution:\n'"$sql_operation_result" + return 22 # EINVAL + fi + + return 0 +} + +function new_contribution() +{ + local _contribution_title="$1" + local _author_email="$2" + local _repository_id="$3" + local sql_operation_result + local insert_contribution_result + local get_contribution_id_result + + # Checagem de valores nulos + if [[ -z "$_contribution_title" || -z "$_author_email" || -z "$_repository_id" ]]; then + complain "($LINENO): missing mandatory field(s) for new_contribution" + return 22 # EINVAL + fi + + #patch_existent_result="$(check_contribution_existence_by_title "$_contribution_title")" + #ret="$?" + + #if [[ "$ret" -ne 0 ]]; then + # complain "$patch_existent_result" + # return "$ret" + #fi + + #if [[ "$patch_existent_result" -ne 0 ]]; then + # complain "($LINENO): error while trying to create new contribution ${_contribution_title}, contribution already exists" + # return 22 # EINVAL + #fi + + insert_contribution_result=$(insert_contribution "$_contribution_title" "$_author_email" "$_repository_id") + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$insert_contribution_result" + return "$ret" + fi + + get_contribution_id_result=$(get_contribution_info_by_title 'id' "$_contribution_title") + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$get_contribution_id_result" + return "$ret" + fi + + printf '%s\n' "$get_contribution_id_result" + return 0 +} + +function check_contribution_existence_by_title() +{ + local _contribution_title="$1" + local sql_operation_result + local ret + + # Checagem de valor nulo + if [[ -z "$_contribution_title" ]]; then + complain "($LINENO): empty contribution title for check_contribution_existence_by_title" + return 22 # EINVAL + fi + + sql_operation_result="$(check_existence "$DATABASE_CONTRIBUTION_TABLE" "$_contribution_title")" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$sql_operation_result" + return "$ret" + fi + + if [[ -z "$sql_operation_result" ]]; then + complain "($LINENO): check existence returned null" + return 61 # ENODATA + fi + + printf '%s\n' "$sql_operation_result" + return 0 +} + +function get_contribution_info_by_title() +{ + local _contribution_infos="$1" + local _contribution_title="$2" + local sql_operation_result + local ret + + if [[ -z "$_contribution_title" ]]; then + complain "($LINENO): empty contribution title for get_contribution_info_by_title" + return 22 # EINVAL + fi + + condition_array=(['title']="${_contribution_title}") + + sql_operation_result="$(get_contribution_info "$_contribution_infos" 'condition_array')" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$sql_operation_result" + return "$ret" + fi + + printf '%s\n' "$sql_operation_result" + return 0 +} + +function get_contribution_info() +{ + local _contribution_infos="$1" + local -n _contrib_info_condition_array="$2" + local sql_operation_result + local ret + + sql_operation_result="$(get_database_table_info "$DATABASE_CONTRIBUTION_TABLE" "$_contribution_infos" '_contrib_info_condition_array')" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$sql_operation_result" + return 22 # EINVAL + fi + + printf '%s\n' "$sql_operation_result" + return 0 +} diff --git a/src/lib/patch_track_utils/patch_track_utils.sh b/src/lib/patch_track_utils/patch_track_utils.sh new file mode 100644 index 000000000..721779b58 --- /dev/null +++ b/src/lib/patch_track_utils/patch_track_utils.sh @@ -0,0 +1,625 @@ +include "${KW_LIB_DIR}/lib/kwdb.sh" + +declare -gr DATABASE_PATCH_TABLE='patch' +declare -Ag condition_array + +function insert_patch() +{ + local _patch_title="$1" + local _last_patch_id="$2" + local _patch_author="$3" + local _contribution_id="$4" + local _submission_id="$5" + local _commit_hash="$6" + local _patch_version="$7" + local get_last_patch_version_result + local sql_operation_result + local ret + + sql_operation_result=$(insert_into "$DATABASE_PATCH_TABLE" \ + '("title", "last_patch_id", "author", "contribution_id", "_submission_id", "version", "_commit_hash")' \ + "('${patch_title}', '${_last_patch_id}', '${_patch_author}', '${_contribution_id}', '${_submission_id}',\ + '${_patch_version}', '${_commit_hash}')"\ + '' 'VERBOSE') + ret="$?" + + if [[ "$ret" -eq 2 || "$ret" -eq 61 ]]; then + complain "$sql_operation_result" + return 22 # EINVAL + elif [[ "$ret" -ne 0 ]]; then + complain "($LINENO):" $'Error while trying to insert new patch:\n'"${sql_operation_result}" + return 22 # EINVAL + fi + + return 0 +} + +function insert_contribution() +{ + local _contribution_title="$1" + local _author_email="$2" + local _repository_id="$3" + + sql_operation_result=$(insert_into "$DATABASE_PATCH_TABLE" \ + '("title", "author_email", "repository_id")' \ + "('${_contribution_title}', '${_author_email}', '${_repository_id}')" '' 'VERBOSE') + + ret="$?" + + if [[ "$ret" -eq 2 || "$ret" -eq 61 ]]; then + complain "$sql_operation_result" + return 22 # EINVAL + elif [[ "$ret" -ne 0 ]]; then + complain "($LINENO):" $'Error while tr lhklying to insert new patch:\n'"${sql_operation_result}" + return 22 # EINVAL + fi + + return 0 +} + +function insert_submission() +{ + local _contribution_id="$1" + local _send_by="$2" + local sql_operation_result + local ret + + sql_operation_result=$(insert_into "$DATABASE_SUBMISSION_TABLE" '("contribution_id", "send_by")' "('${patch_title}', '${_send_by}')" '' 'VERBOSE') + ret="$?" + + if [[ "$ret" -eq 2 || "$ret" -eq 61 ]]; then + complain "$sql_operation_result" + return 22 # EINVAL + elif [[ "$ret" -ne 0 ]]; then + complain "($LINENO):" $'Error while trying to insert new patch:\n'"${sql_operation_result}" + return 22 # EINVAL + fi + + return 0 +} + +function replace_patch() +{ + local _patch_title="$1" + local _last_patch_id="$2" + local _patch_author="$3" + local _contribution_id="$4" + local _submission_id="$5" + local _commit_hash="$6" + + get_patch_version_result="$(decide_patch_version "$_last_patch_id")" + ret="$?" + + if [[ "$ret" -new 0 ]]; then + complain "$get_patch_version_result" + return 22 # EINVAL + fi + + new_patch_result="$(new_patch "$_patch_title" "$_last_patch_id" "$_patch_author" "$_contribution_id"\ + "$_submission_id" "$_commit_hash" "$get_patch_version_result")" + ret="$?" + + if [[ "$ret" -new 0 ]]; then + complain "$new_patch_result" + return 22 # EINVAL + fi + + printf "$new_patch_result" + return 0 +} + +function new_patch() +{ + # TODO NEW PATCH + # TODO CREATE_NEW_PATCH_OR_GET_EXISTENT + # CRIAR NEW_SUBMISSION + # CRIAR SUBMETER PATCH + # ALTERAR A ENTIDADE QUE RELACIONA PATCH E SUBMISSION PARA TER CAMPOS COMO MESSAGE-ID + # OUTDATED DO PATCH APENAS QUANDO NOVO PATCH SOBRESCREVER ELE OU ELE FOR "DESLIGADO DA SÉRIE" + # CHECAR ESTADO DO PATCH SEMPRE OLHA PARA ÚLTIMA SUBMISSÃO DELE + + local _patch_title="$1" + local _last_patch_id="$2" + local _patch_author="$3" + local _contribution_id="$4" + local _submission_id="$5" + local _commit_hash="$6" + local _patch_version="$7" + local ret + local patch_existent_result + local insert_contribution_result + local get_contribution_id_result + + if [[ -z "$_patch_version" ]]; then + _patch_version=1 + fi + + patch_existent_result="$(check_patch_existence_by_unique_attributes "$contribution_name")" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$patch_existent_result" + return "$ret" # EINVAL + fi + + if [[ "$patch_existent_result" -ne 0 ]]; then + complain "($LINENO): error while trying to create new patch serie ${contribution_name}, patch serie already exists" + return 22 # EINVAL + fi + + insert_contribution_result=$(insert_patch "$_contribution_title" "$_author_email" "$_repository_id") + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$insert_contribution_result" + return "$ret" # EINVAL + fi + + get_contribution_id_result=$(get_patch_info_by_commit_hash 'id' "$_commit_hash") + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$get_contribution_id_result" + return "$ret" # EINVAL + fi + + printf '%s\n' "$get_contribution_id_result" + return 0 +} + +function new_contribution() +{ + local _contribution_title="$1" + local _author_email="$2" + local _repository_id="$3" + local sql_operation_result + local insert_contribution_result + local get_contribution_id_result + + patch_existent_result="$(check_contribution_existence_by_title "$contribution_name")" + + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$patch_existent_result" + return "$ret" # EINVAL + fi + + if [[ "$patch_existent_result" -ne 0 ]]; then + complain "($LINENO): error while trying to create new patch serie ${contribution_name}, patch serie already exists" + return 22 # EINVAL + fi + + insert_contribution_result=$(insert_contribution "$_contribution_title" "$_author_email" "$_repository_id") + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$insert_contribution_result" + return "$ret" # EINVAL + fi + + get_contribution_id_result=$(get_contribution_info_by_title "$_contribution_title") + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$get_contribution_id_result" + return "$ret" # EINVAL + fi + + printf '%s\n' "$get_contribution_id_result" + return 0 +} + +function new_submission() +{ + local _submission_author="$3" + local _contribution_id="$4" + local ret + local patch_existent_result + local insert_submission_result + local get_submission_id_result + + insert_contribution_result=$(insert_submission "$_contribution_id" "$_submission_author") + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$insert_contribution_result" + return "$ret" # EINVAL + fi + + get_submission_id_result=$(get_last_submission_by_contribution_id 'id' "$_commit_hash") + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$get_contribution_id_result" + return "$ret" # EINVAL + fi + + printf '%s\n' "$get_contribution_id_result" + return 0 +} + +function get_patch_info_by_id() +{ + local _patch_infos="$1" + local _patch_id="$2" + local sql_operation_result + local ret + + condition_array=(['id']="${_patch_id}") + sql_operation_result="$(get_patch_info "$_patch_infos" 'condition_array')" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$sql_operation_result" + return "$ret" # EINVAL + fi + + printf '%s\n' "$sql_operation_result" + return 0 +} + +function get_last_submission_infos_by_contribution_id() +{ + local _submission_infos="$1" + local _contribution_id="$2" + local sql_operation_result + local ret + + condition_array=(['contribution_id']="${_contribution_id}") + sql_operation_result="$(get_submission_info "$_submission_infos" 'condition_array' 'ID DESC' '1')" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$sql_operation_result" + return "$ret" # EINVAL + fi + + printf '%s\n' "$sql_operation_result" + return 0 +} + +function get_submission_info() +{ + local _submission_infos="$1" + local -n _condition_array="$2" + local _order_by="$3" + local _limit="$4" + + sql_operation_result="$(get_database_table_info "$DATABASE_SUBMISSION_TABLE" "$_patch_infos" '_condition_array' "$_order_by" "$_limit")" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$sql_operation_result" + return "$ret" # EINVAL + fi + + printf '%s\n' "$sql_operation_result" + return 0 +} + +function new_patch_submission() +{ + local _patch_id="$1" + local _submission_id="$2" + local _message_id="$3" + + if [[ -z "$_patch_version" ]]; then + _patch_version=1 + fi + + patch_existent_result="$(check_patch_submission_existence_by_unique_attributes "$_patch_id" "$_submission_id" "$_message_id")" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$patch_existent_result" + return "$ret" # EINVAL + fi + + if [[ "$patch_existent_result" -ne 0 ]]; then + complain "($LINENO): error while trying to create new patch serie ${contribution_name}, patch serie already exists" + return 22 # EINVAL + fi + + insert_contribution_result=$(insert_patch "$_contribution_title" "$_author_email" "$_repository_id") + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$insert_contribution_result" + return "$ret" # EINVAL + fi + + get_contribution_id_result=$(get_patch_info_by_commit_hash 'id' "$_commit_hash") + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$get_contribution_id_result" + return "$ret" # EINVAL + fi + + printf '%s\n' "$get_contribution_id_result" + return 0 +} + +function check_patch_submission_existence_by_unique_attributes() +{ + local _patch_id="$1" + local _submission_id="$2" + local _message_id="$3" + + # CHECK campos não null, ex, se commit_hash é null não é seguro considerar que é o mesmo patch + condition_array=(['patch_id']="${_patch_id}") + condition_array=(['_submission_id']="${_submission_id}") + condition_array=(['_message_id']="${_message_id}") + + sql_operation_result="$(check_patch_submission_existence '_contribution_title')" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "${sql_operation_result}" + return "$ret" # EINVAL + fi + + printf '%s\n' "${sql_operation_result}" + + return 0 +} + +function check_patch_submission_existence() +{ + local _condition_array="$1" + local sql_operation_result + local ret + + sql_operation_result="$(get_patch_submission_info 'id' '_condition_array')" + ret="$?" + + if [[ "$ret" -ne 0 && "$ret" -ne 61 ]]; then + complain "${sql_operation_result}" + return "$ret" # EINVAL + fi + + if [[ "$ret" -eq 61 ]]; then + printf '%s\n' 0 + else + printf '%s\n' 1 + fi + + return 0 +} + +function get_patch_submission_info() +{ + local _patch_submission_infos="$1" + local -n _condition_array="$2" + + sql_operation_result="$(get_database_table_info "$DATABASE_PATCH_SUBMISSION_TABLE" "$_patch_submission_infos" '_condition_array')" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$sql_operation_result" + return "$ret" # EINVAL + fi + + printf '%s\n' "$sql_operation_result" + return 0 +} + +function insert_patch_submission() +{ + local _patch_id="$1" + local _submission_id="$2" + local _message_id="$3" + local ret + + sql_operation_result=$(insert_into "$DATABASE_PATCH_SUBMISSION_TABLE" '("patch_id", "submission_id", "message_id")' "('${_patch_id}', '${_submission_id}', '${_message_id}')" '' 'VERBOSE') + ret="$?" + + if [[ "$ret" -eq 2 || "$ret" -eq 61 ]]; then + complain "$sql_operation_result" + return 22 # EINVAL + elif [[ "$ret" -ne 0 ]]; then + complain "($LINENO):" $'Error while trying to insert new patch:\n'"${sql_operation_result}" + return 22 # EINVAL + fi + + return 0 +} + +function get_patch_info_by_commit_hash() +{ + local _patch_infos="$1" + local _commit_hash="$2" + local sql_operation_result + local ret + + condition_array=(['commit_hash']="${_commit_hash}") + sql_operation_result="$(get_patch_info "$_patch_infos" 'condition_array')" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$sql_operation_result" + return "$ret" # EINVAL + fi + + printf '%s\n' "$sql_operation_result" + return 0 +} + +function get_patch_info() +{ + local _patch_infos="$1" + local -n _condition_array="$2" + + sql_operation_result="$(get_database_table_info "$DATABASE_PATCH_TABLE" "$_patch_infos" '' '_condition_array' '' 'VERBOSE')" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$sql_operation_result" + return "$ret" # EINVAL + fi + + printf '%s\n' "$sql_operation_result" + return 0 +} + +function check_patch_existence_by_unique_attributes() +{ + local _patch_title="$1" + local _patch_author="$2" + local _contribution_id="$3" + local _commit_hash="$4" + + # CHECK campos não null, ex, se commit_hash é null não é seguro considerar que é o mesmo patch + condition_array=(['title']="${_patch_title}") + condition_array=(['author']="${_patch_author}") + condition_array=(['contribution_id']="${_contribution_id}") + condition_array=(['commit_hash']="${_commit_hash}") + + sql_operation_result="$(check_patch_existence '_contribution_title')" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "${sql_operation_result}" + return "$ret" # EINVAL + fi + + printf '%s\n' "${sql_operation_result}" + + return 0 +} + +function check_patch_existence() +{ + local _condition_array="$1" + local sql_operation_result + local ret + + sql_operation_result="$(get_patch_info '_condition_array')" + ret="$?" + + if [[ "$ret" -ne 0 && "$ret" -ne 61 ]]; then + complain "${sql_operation_result}" + return "$ret" # EINVAL + fi + + if [[ "$ret" -eq 61 ]]; then + printf '%s\n' 0 + else + printf '%s\n' 1 + fi + + return 0 +} + +function check_contribution_existence_by_title() +{ + local _contribution_title="$1" + local sql_operation_result + local ret + + sql_operation_result="$(get_contribution_info_by_title "$_contribution_title")" + ret="$?" + + if [[ "$ret" -ne 0 && "$ret" -ne 61 ]]; then + complain "${sql_operation_result}" + return "$ret" # EINVAL + fi + + if [[ "$ret" -eq 61 ]]; then + printf '%s\n' 0 + else + printf '%s\n' 1 + fi + + return 0 +} + +function get_contribution_info_by_title() +{ + local _contribution_title="$1" + local sql_operation_result + local ret + + condition_array=(['title']="${_contribution_title}") + + sql_operation_result="$(get_contribution_info 'condition_array')" + ret="$?" + + if [[ "$ret" -ne 0 && "$ret" -ne 61 ]]; then + complain "${sql_operation_result}" + return "$ret" # EINVAL + fi + + if [[ "$ret" -eq 61 ]]; then + printf '%s\n' 0 + else + printf '%s\n' 1 + fi + + return 0 +} + +function get_contribution_info() +{ + local _condition_array="$1" + local "$_contribution_infos" + local sql_operation_result + local ret + + sql_operation_result="$(get_database_table_info "$DATABASE_contribution_TABLE" "$_contribution_infos" '' 'condition_array' '' 'VERBOSE')" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "${get_last_patch_version_result}" + return 22 # EINVAL + fi + + printf '%s\n' "$sql_operation_result" + return 0 +} + +function get_database_table_info() +{ + local _database_table_name="$1" + local _contribution_infos="$2" + local -n _condition_array="$3" + local _order_by="${4:-''}" + local _limit="${5:-''}" + + sql_operation_result="$(select_from "$DATABASE_CONTRIBUTION_TABLE" "$_contribution_infos" '' '_condition_array' "$_order_by" "$_limit" 'VERBOSE')" + ret="$?" + + if [[ "$ret" -eq 2 || "$ret" -eq 61 ]]; then + complain "$sql_operation_result" + return 22 # EINVAL + elif [[ "$ret" -ne 0 ]]; then + complain "($LINENO): Error while trying to get ${_database_table_name} info from the database with the command:"$'\n'"${sql_operation_result}" + return 22 # EINVAL + elif [[ -z "$sql_operation_resul" ]]; then + complain "($LINENO): Error while trying to get ${_database_table_name} info from the database: no patch found for id: ${_patch_id}" + return 61 # ENODATA + fi +} + +function decide_patch_version() +{ + local _last_patch_id="$1" + local get_last_patch_version_result + local ret + + if [[ -z "$_last_patch_id" ]]; then + return 22 # EINVAL + fi + + get_last_patch_version_result="$(get_patch_info_by_id 'version' "$_last_patch_id")" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "${get_last_patch_version_result}" + return 22 # EINVAL + fi + + printf '%d\n' $((get_last_patch_version_result + 1)) + return 0 +} \ No newline at end of file diff --git a/src/lib/patch_track_utils/patch_utils.sh b/src/lib/patch_track_utils/patch_utils.sh new file mode 100644 index 000000000..68a62b0a6 --- /dev/null +++ b/src/lib/patch_track_utils/patch_utils.sh @@ -0,0 +1,282 @@ +include "${KW_LIB_DIR}/lib/kw_db.sh" + +declare -gr DATABASE_PATCH_TABLE='patch' +declare -Ag condition_array + +function insert_patch() +{ + local _patch_title="$1" + local _last_patch_id="$2" + local _patch_author="$3" + local _contribution_id="$4" + local _commit_hash="$5" + local _patch_version="$6" + local get_last_patch_version_result + local sql_operation_result + local ret + + if [[ -z "$_patch_title" || -z "$_patch_author" || -z "$_contribution_id" || -z "$_commit_hash" ]]; then + complain "($LINENO): missing mandatory field for a new patch" + return 22 # EINVAL + fi + + columns='"title", "author", "contribution_id", "version", "commit_hash"' + values="'${_patch_title}', '${_patch_author}', '${_contribution_id}', '${_patch_version}', '${_commit_hash}'" + + if [[ -n "${_last_patch_id}" ]]; then + columns="$columns, \"last_patch_id\"" + values="$values, '${_last_patch_id}'" + fi + + sql_operation_result=$(insert_into "$DATABASE_PATCH_TABLE" \ + "($columns)" \ + "($values)") + ret="$?" + + if [[ "$ret" -eq 2 || "$ret" -eq 61 ]]; then + complain "$sql_operation_result" + return 22 # EINVAL + elif [[ "$ret" -ne 0 ]]; then + complain "($LINENO):" $'Error while trying to insert new patch:\n'"$sql_operation_result" + return 22 # EINVAL + fi + + return 0 +} + +function replace_patch() +{ + local _patch_title="$1" + local _last_patch_id="$2" + local _patch_author="$3" + local _contribution_id="$4" + local _submission_id="$5" + local _commit_hash="$6" + + if [[ -z "$_last_patch_id" || -z "$_patch_title" || -z "$_commit_hash" ]]; then + complain "($LINENO): missing mandatory field for a new patch" + return 22 # EINVAL + fi + + + get_patch_version_result="$(decide_patch_version "$_last_patch_id")" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$get_patch_version_result" + return 22 # EINVAL + fi + + new_patch_result="$(new_patch "$_patch_title" "$_last_patch_id" "$_patch_author" "$_contribution_id"\ + "$_submission_id" "$_commit_hash" "$get_patch_version_result")" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$new_patch_result" + return 22 # EINVAL + fi + + printf "$new_patch_result" + return 0 +} + +function new_patch() +{ + local _patch_title="$1" + local _last_patch_id="$2" + local _patch_author="$3" + local _contribution_id="$4" + local _commit_hash="$5" + local _patch_version="$6" + local ret + local patch_existent_result + local insert_contribution_result + local get_contribution_id_result + + if [[ -z "$_patch_title" || -z "$_patch_author" || -z "$_contribution_id" || -z "$_commit_hash" ]]; then + complain "($LINENO): missing mandatory field for new_patch" + return 22 # EINVAL + fi + + if [[ -z "$_patch_version" ]]; then + _patch_version=1 + fi + + patch_existent_result="$(check_patch_existence_by_unique_attributes "$_patch_title" "$_patch_author" "$_contribution_id" "$_commit_hash")" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$patch_existent_result" + return "$ret" # EINVAL + fi + + if [[ -z "$patch_existent_result" ]]; then + complain "($LINENO): unexpected empty result from check_patch_existence_by_unique_attributes" + return 22 # EINVAL + fi + + if [[ "$patch_existent_result" -ne 0 ]]; then + complain "($LINENO): error while trying to create new patch serie ${contribution_name}, patch serie already exists" + return 22 # EINVAL + fi + + insert_contribution_result=$(insert_patch "$_patch_title" "$_last_patch_id" "$_patch_author" "$_contribution_id"\ + "$_commit_hash" "$_patch_version") + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$insert_contribution_result" + return "$ret" # EINVAL + fi + + get_contribution_id_result=$(get_patch_info_by_commit_hash 'id' "$_commit_hash") + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$get_contribution_id_result" + return "$ret" # EINVAL + fi + + printf '%s\n' "$get_contribution_id_result" + return 0 +} + +function get_patch_info_by_id() +{ + local _patch_infos="$1" + local _patch_id="$2" + local sql_operation_result + local ret + + if [[ -z "$_patch_id" ]]; then + complain "($LINENO): empty patch id" + return 22 # EINVAL + fi + + + condition_array=(['id']="${_patch_id}") + sql_operation_result="$(get_patch_info "$_patch_infos" 'condition_array')" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$sql_operation_result" + return "$ret" # EINVAL + fi + + if [[ -z "$sql_operation_result" ]]; then + complain "($LINENO): no result found for patch id $_patch_id" + return 61 # ENODATA + fi + + printf '%s\n' "$sql_operation_result" + return 0 +} + +function get_patch_info_by_commit_hash() +{ + local _patch_infos="$1" + local _commit_hash="$2" + local sql_operation_result + local ret + + if [[ -z "$_commit_hash" ]]; then + complain "($LINENO): empty commit hash" + return 22 # EINVAL + fi + + condition_array=(['commit_hash']="${_commit_hash}") + sql_operation_result="$(get_patch_info "$_patch_infos" 'condition_array')" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$sql_operation_result" + return "$ret" # EINVAL + fi + + if [[ -z "$sql_operation_result" ]]; then + complain "($LINENO): no result found for commit hash $_commit_hash" + return 61 # ENODATA + fi + + printf '%s\n' "$sql_operation_result" + return 0 +} + +function get_patch_info() +{ + local _patch_infos="$1" + local -n _patch_condition_array="$2" + + sql_operation_result="$(get_database_table_info "$DATABASE_PATCH_TABLE" "$_patch_infos" '_patch_condition_array')" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$sql_operation_result" + return "$ret" # EINVAL + fi + + printf '%s\n' "$sql_operation_result" + return 0 +} + +function check_patch_existence_by_unique_attributes() +{ + local _patch_title="$1" + local _patch_author="$2" + local _contribution_id="$3" + local _commit_hash="$4" + + if [[ -z "$_patch_title" && -z "$_patch_author" && -z "$_contribution_id" && -z "$_commit_hash" ]]; then + complain "($LINENO): no attributes provided for check_patch_existence_by_unique_attributes" + return 22 # EINVAL + fi + + condition_array=(['title']="${_patch_title}" + ['author']="${_patch_author}" + ['contribution_id']="${_contribution_id}" + ['commit_hash']="${_commit_hash}") + + + sql_operation_result="$(check_existence "$DATABASE_PATCH_TABLE" 'condition_array')" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$sql_operation_result" + return "$ret" # EINVAL + fi + + if [[ -z "$sql_operation_result" ]]; then + complain "($LINENO): check existence returned null" + return 61 # ENODATA + fi + + printf '%s\n' "$sql_operation_result" + return 0 +} + +function decide_patch_version() +{ + local _last_patch_id="$1" + local get_last_patch_version_result + local ret + + if [[ -z "$_last_patch_id" ]]; then + return 22 # EINVAL + fi + + get_last_patch_version_result="$(get_patch_info_by_id 'version' "$_last_patch_id")" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$get_last_patch_version_result" + return 22 # EINVAL + fi + + if [[ -z "$get_last_patch_version_result" ]]; then + complain "($LINENO): empty version returned for patch id $_last_patch_id" + return 61 # ENODATA + fi + + printf '%d\n' $((get_last_patch_version_result + 1)) + return 0 +} \ No newline at end of file diff --git a/src/lib/patch_track_utils/submission_utils.sh b/src/lib/patch_track_utils/submission_utils.sh new file mode 100644 index 000000000..bfcf4620d --- /dev/null +++ b/src/lib/patch_track_utils/submission_utils.sh @@ -0,0 +1,193 @@ +include "${KW_LIB_DIR}/lib/kw_db.sh" + +declare -gr DATABASE_SUBMISSION_TABLE='submission' +declare -gr DATABASE_PATCH_SUBMISSION_TABLE='patch_submission' +declare -Ag patch_track_condition_array + +function insert_submission() +{ + local _contribution_id="$1" + local _send_by="$2" + local sql_operation_result + local ret + + sql_operation_result="$(insert_into "$DATABASE_SUBMISSION_TABLE" '("contribution_id", "send_by")' "('${_contribution_id}', '${_send_by}')")" + ret="$?" + + if [[ "$ret" -eq 2 || "$ret" -eq 61 ]]; then + complain "$sql_operation_result" + return "$ret" # EINVAL + elif [[ "$ret" -ne 0 ]]; then + complain "($LINENO):" $'Error while trying to insert new submission:\n'"$sql_operation_result" + return "$ret" # EINVAL + fi + + return 0 +} + +function new_submission() +{ + local _contribution_id="$1" + local _submission_author="$2" + local ret + local patch_existent_result + local insert_submission_result + local get_submission_id_result + + insert_submission_result="$(insert_submission "$_contribution_id" "$_submission_author")" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$insert_submission_result" + return "$ret" # EINVAL + fi + + get_submission_id_result="$(get_last_submission_infos_by_contribution_id 'id' "$_contribution_id")" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$get_contribution_id_result" + return "$ret" # EINVAL + fi + + printf '%s\n' "$get_contribution_id_result" + return 0 +} + +function get_last_submission_infos_by_contribution_id() +{ + local _submission_infos="$1" + local _contribution_id="$2" + local sql_operation_result + local ret + + patch_track_condition_array=(['contribution_id']="${_contribution_id}") + + sql_operation_result="$(get_submission_info "$_submission_infos" 'patch_track_condition_array' 'ID DESC' '1')" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$sql_operation_result" + return "$ret" # EINVAL + fi + + printf '%s\n' "$sql_operation_result" + return 0 +} + +function get_submission_info() +{ + local _submission_infos="$1" + local -n _submission_condition_array="$2" + local _order_by="${3:-}" + local _limit="${4:-}" + + sql_operation_result="$(get_database_table_info "$DATABASE_SUBMISSION_TABLE" "$_submission_infos" '_submission_condition_array' "$_order_by" "$_limit")" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$sql_operation_result" + return "$ret" # EINVAL + fi + + printf '%s\n' "$sql_operation_result" + return 0 +} + +function new_patch_submission() +{ + local _patch_id="$1" + local _submission_id="$2" + local _message_id="$3" + + patch_existent_result="$(check_patch_submission_existence_by_unique_attributes "$_patch_id" "$_submission_id" "$_message_id")" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$patch_existent_result" + return "$ret" # EINVAL + fi + + if [[ "$patch_existent_result" -ne 0 ]]; then + complain "($LINENO): error while trying to create new patch submission for patch-id {$_patch_id} into submission {$_submission_id}" + return 22 # EINVAL + fi + + insert_patch_submission_result="$(insert_patch_submission "$_patch_id" "$_submission_id" "$_message_id")" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$insert_patch_submission_result" + return "$ret" # EINVAL + fi + + return 0 +} + +function check_patch_submission_existence_by_unique_attributes() +{ + local _patch_id="$1" + local _submission_id="$2" + local _message_id="$3" + + if [[ -z "$_patch_id" || -z "$_submission_id" || -z "$_message_id" ]]; then + complain "($LINENO): missing mandatory field(s) for new_patch_submission" + return 22 # EINVAL + fi + + patch_track_condition_array=(['patch_id']="${_patch_id}" + ['submission_id']="${_submission_id}" + ['message_id']="${_message_id}") + + sql_operation_result="$(check_existence "$DATABASE_PATCH_SUBMISSION_TABLE" 'patch_track_condition_array')" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$sql_operation_result" + return "$ret" # EINVAL + fi + + printf '%s\n' "${sql_operation_result}" + return 0 +} + +function get_patch_submission_info() +{ + local _patch_submission_infos="$1" + local -n _patch_submission_condition_array="$2" + local _order_by="${3:-}" + local _limit="${4:-}" + + sql_operation_result="$(get_database_table_info "$DATABASE_PATCH_SUBMISSION_TABLE" "$_patch_submission_infos" '_patch_submission_condition_array' "$_order_by" "$_limit")" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$sql_operation_result" + return "$ret" # EINVAL + fi + + printf '%s\n' "$sql_operation_result" + return 0 +} + +function insert_patch_submission() +{ + local _patch_id="$1" + local _submission_id="$2" + local _message_id="$3" + local ret + + sql_operation_result="$(insert_into "$DATABASE_PATCH_SUBMISSION_TABLE" '("patch_id", "submission_id", "message_id")' \ + "('${_patch_id}', '${_submission_id}', '${_message_id}')" '' 'VERBOSE')" + ret="$?" + + if [[ "$ret" -eq 2 || "$ret" -eq 61 ]]; then + complain "$sql_operation_result" + return 22 # EINVAL + elif [[ "$ret" -ne 0 ]]; then + complain "($LINENO):" $'Error while trying to insert new patch submission:\n'"${sql_operation_result}" + return 22 # EINVAL + fi + + return 0 +} diff --git a/src/patch_track.sh b/src/patch_track.sh index 9604a1a14..e97098df9 100644 --- a/src/patch_track.sh +++ b/src/patch_track.sh @@ -1,8 +1,6 @@ include "${KW_LIB_DIR}/lib/kwlib.sh" include "${KW_LIB_DIR}/lib/kw_string.sh" -declare -gr DATABASE_PATCH_TABLE='patch' - declare -gA options_values declare -gA condition_array declare -gA updates_array @@ -57,31 +55,62 @@ function patch_track_main() # an error during insertion. function register_patch_track() { - local -n _patches_subjects="$1" + local -n _patches_commit_hash="$1" local sql_operation_result local ret - for patch_subject in "${_patches_subjects[@]}"; do - if [[ -z "$patch_subject" ]]; then - complain 'Patch subject is empty' - return 61 # ENODATA - fi - - sql_operation_result=$(insert_into "$DATABASE_PATCH_TABLE" '("title")' "('${patch_subject}')" '' 'VERBOSE') - ret="$?" - - if [[ "$ret" -eq 2 || "$ret" -eq 61 ]]; then - complain "$sql_operation_result" - return 22 # EINVAL - elif [[ "$ret" -ne 0 ]]; then - complain "($LINENO):" $'Error while trying to insert patch into the database with the command:\n'"${sql_operation_result}" - return 22 # EINVAL - fi + for commit_hash in "${_patches_commit_hash[@]}"; do + #if [[ -z "$commit_hash" ]]; then + # complain 'Patch hash is empty' + # return 61 # ENODATA + #fi + echo "$commit_hash" + echo "1:" + #insert_patch_result=$(new_patch "$patch_metadata") + #if [[ "$ret" -ne 0 ]]; then + # return $ret # EINVAL + #fi done success "Patch registered successfully." } +function extract_commit_hash_from_patch_files() { + local patches_dir="$1" + local -n _commits_array="$2" + + for patch_path in "${patches_dir}/"*; do + if is_a_patch "$patch_path"; then + local sha + sha=$(head -n1 "$patch_path" | awk '{print $2}') + _commits_array+=("AAA$sha") + fi + done +} + +function extract_patches_from_file() +{ + _total_patches_extracted="$1" + _send_email_log_file="$2" + + for n in $(seq 1 "$_total_patches_extracted"); do + header_block=$(grep -Poz '^From:[^\n]*\nTo:[^\n]*\nSubject:[^\n]*\nDate:[^\n]*\nMessage-ID:[^\n]*\nX-Commit-SHA:[^\n]*' "$_send_email_log_file" | head -n "$n" | tail -n 1) + + if [[ -z "$header_block" ]]; then + #echo "Erro: patch número $n não encontrado" >&2 + continue + fi + + # extrai cada campo do bloco + from=$(grep '^From:' <<< "$header_block" | sed 's/^From:[[:space:]]*//') + to=$(grep '^To:' <<< "$header_block" | sed 's/^To:[[:space:]]*//') + subject=$(grep '^Subject:' <<< "$header_block" | sed 's/^Subject:[[:space:]]*//') + date=$(grep '^Date:' <<< "$header_block" | sed 's/^Date:[[:space:]]*//') + message_id=$(grep '^Message-ID:' <<< "$header_block" | sed 's/^Message-ID:[[:space:]]*//') + xcommit_sha=$(grep '^X-Commit-SHA:' <<< "$header_block" | sed 's/^X-Commit-SHA:[[:space:]]*//') + done +} + # This function displays the patches dashboard based on provided filters. # It fetches patches from the database according to the conditions # and prints them in a formatted table. diff --git a/src/send_patch.sh b/src/send_patch.sh index 67d939edf..97040008e 100644 --- a/src/send_patch.sh +++ b/src/send_patch.sh @@ -143,7 +143,7 @@ function mail_send() [[ -n "$rfc" ]] && cmd+=" $rfc" [[ -n "$extra_opts" ]] && cmd+=" $extra_opts" - cmd_manager "$flag" "$cmd" + cmd_manager "$flag" "$cmd" KW_REDIRECT_MODE "$output_file" } # Validates the recipient list given by the user to the options `--to` and @@ -201,7 +201,7 @@ function pre_generate_patches() for patch_path in "${patch_cache}/"*; do if is_a_patch "$patch_path"; then ((count++)) - title=$(get_patch_subject "$patch_path") + title=("aaaa") _patches_titles+=("$title") fi done diff --git a/tests/unit/patch_track_test.sh b/tests/unit/patch_track_test.sh index cff4779ad..88f47d89e 100755 --- a/tests/unit/patch_track_test.sh +++ b/tests/unit/patch_track_test.sh @@ -1,6 +1,10 @@ #!/usr/bin/env bash include './src/patch_track.sh' +include './src/lib/patch_track_utils/patch_utils.sh' +include './src/lib/patch_track_utils/contribution_utils.sh' +include './src/lib/patch_track_utils/submission_utils.sh' +include './src/lib/kw_db.sh' include './tests/unit/utils.sh' function oneTimeSetUp() @@ -37,8 +41,27 @@ function setupDatabase() declare -g TEST_PATCH_ID execute_sql_script "${KW_DB_DIR}/kwdb.sql" > /dev/null 2>&1 - sqlite3 "${KW_DATA_DIR}/kw.db" -batch "INSERT INTO \"${DATABASE_PATCH_TABLE}\" (title) VALUES (\"${TEST_PATCH_TITLE}\");" - TEST_PATCH_ID="$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT id FROM \"${DATABASE_PATCH_TABLE}\" WHERE title='${TEST_PATCH_TITLE}';")" + #sqlite3 "${KW_DATA_DIR}/kw.db" -batch "INSERT INTO \"${DATABASE_PATCH_TABLE}\" (title) VALUES (\"${TEST_PATCH_TITLE}\");" + #TEST_PATCH_ID="$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT id FROM \"${DATABASE_PATCH_TABLE}\" WHERE title='${TEST_PATCH_TITLE}';")" + + # --- Criação de uma contribution inicial --- + TEST_CONTRIBUTION_TITLE='TEST_CONTRIBUTION' + sqlite3 "${KW_DATA_DIR}/kw.db" -batch "INSERT INTO contribution (title, author_email, repository_id) VALUES ('${TEST_CONTRIBUTION_TITLE}', 'test@example.com', 1);" + TEST_CONTRIBUTION_ID="$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT id FROM contribution WHERE title='${TEST_CONTRIBUTION_TITLE}';")" + + # --- Criação de uma submission inicial --- + TEST_SUBMISSION_AUTHOR='test@example.com' + sqlite3 "${KW_DATA_DIR}/kw.db" -batch "INSERT INTO submission (contribution_id, send_by) VALUES ('${TEST_CONTRIBUTION_ID}', '${TEST_SUBMISSION_AUTHOR}');" + TEST_SUBMISSION_ID="$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT id FROM submission WHERE contribution_id='${TEST_CONTRIBUTION_ID}' ORDER BY id DESC LIMIT 1;")" + + # --- Criação de um patch inicial --- + TEST_PATCH_TITLE='TEST_PATCH' + sqlite3 "${KW_DATA_DIR}/kw.db" -batch "INSERT INTO patch (title, author, contribution_id, commit_hash, version) VALUES ('${TEST_PATCH_TITLE}', '${TEST_SUBMISSION_AUTHOR}', '${TEST_CONTRIBUTION_ID}', 'abc123', 1);" + TEST_PATCH_ID="$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT id FROM patch WHERE title='${TEST_PATCH_TITLE}';")" + + TEST_REPOSITORY_TITLE='TEST_REPOSITORY' + sqlite3 "${KW_DATA_DIR}/kw.db" -batch "INSERT INTO repository (name, origin_url, branch_name) VALUES ('${TEST_REPOSITORY_TITLE}', '${TEST_SUBMISSION_AUTHOR}', 'branch');" + TEST_PATCH_ID="$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT id FROM repository WHERE name='${TEST_REPOSITORY_TITLE}';")" } function tearDownDatabase() @@ -49,251 +72,317 @@ function tearDownDatabase() fi } -function test_register_patch_track() +function test_check_patch_existence() { - local expected local output local ret - local _patches_titles - - # invalid values - _patches_titles=('') - output=$(register_patch_track _patches_titles) + local expected + local patch_title='TEST_PATCH' + local patch_author='test_author' + local contribution_id=1 + local commit_hash='abc123' + + + sqlite3 "${KW_DATA_DIR}/kw.db" -batch \ + "INSERT INTO \"${DATABASE_PATCH_TABLE}\" (title, author, contribution_id, commit_hash, version) \ + VALUES ('TEST_PATCH', 'test_author', 1, 'abc123', 1);" + + output=$(check_patch_existence_by_unique_attributes "$patch_title" "$patch_author" "$contribution_id" "$commit_hash") + expected=1 ret="$?" - expected='Patch subject is empty' - assert_equals_helper 'Empty group should not be valid' "$LINENO" "$expected" "$output" - assert_equals_helper 'Expected an error' "$LINENO" "$ret" 61 - _patches_titles=("invalid_name'") - output=$(register_patch_track _patches_titles 2> /dev/null) - ret="$?" - expected=$'Error while trying to insert patch into the database with the command:\n' - expected+='sqlite3 -init /home/joao-souza/Mac0499---TCC/database/pre_cmd.sql "/tmp/shunit.tH1Kq2/tmp/kw.db"' - expected+="-batch INSERT INTO patch (\"title\") VALUES ('invalid_name'');'" - assertContains "$output" "$expected" - assert_equals_helper 'Expected an error' "$LINENO" "$ret" 22 - - # valid values - _patches_titles=('valid_name') - output=$(register_patch_track _patches_titles) + assert_equals_helper 'The patch should exist' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected success' "$LINENO" 0 "$ret" + + output=$(check_patch_existence_by_unique_attributes "patch_title" "patch_author" "contribution_id" "commit_hash") + expected=0 ret="$?" - assert_equals_helper 'Expected no error' "$LINENO" "$ret" 0 + + assert_equals_helper 'The patch should not not exist' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected success' "$LINENO" 0 "$ret" } -function test_show_patches_dashboard() +function test_decide_patch_version() { - local expected local output local ret - local patch_info - local IFS - # valid values - patch_info="$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT * FROM \"${DATABASE_PATCH_TABLE}\" WHERE title = (\"${TEST_PATCH_TITLE}\");")" - IFS='|' read -r id date time status title <<< "$patch_info" + # id de patch válido de teste + local last_patch_id=1 - output=$(show_patches_dashboard '' 150) + output=$(decide_patch_version "$last_patch_id") ret="$?" + expected=2 + # verifica se retornou sucesso + assert_equals_helper 'The group should have been created' "$LINENO" "$expected" "$output" + assert_equals_helper 'Expected success' "$LINENO" 0 "$ret" +} + +function test_get_database_table_info() { + local patch_id + local result + + # Insere um patch para teste + sqlite3 "${KW_DATA_DIR}/kw.db" "INSERT INTO patch (title, author, contribution_id, commit_hash, version) VALUES ('TEST_PATCH', 'test_author', 1, 'cde123', 1);" + patch_id="$(sqlite3 "${KW_DATA_DIR}/kw.db" "SELECT id FROM patch WHERE commit_hash='cde123';")" - expected+=$'ID |Date |Time |Status |Title\n' - expected+=$'------------------------------------------------------------------------------------------------------------------------------------------------------\n' - expected+="${id} |${date} |${time} |${status} |${title}" - expected+=$'\n' - expected+=$'------------------------------------------------------------------------------------------------------------------------------------------------------' - assert_equals_helper 'Empty group should not be valid' "$LINENO" "$expected" "$output" - assert_equals_helper 'Expected no error' "$LINENO" "$ret" 0 + declare -A cond_array=(['id']="$patch_id") + result="$(get_database_table_info 'patch' 'id' 'cond_array')" + ret="$?" + + assert_equals_helper 'Expected get_database_table_info to succeed' "$LINENO" 0 "$ret" + assert_equals_helper 'Expected get_database_table_info to succeed' "$LINENO" "$patch_id" "$result" } -function test_set_patch_status() -{ - local expected - local output - local ret +function test_check_existence() { + local patch_id + local result - # valid values - set_patch_status "$TEST_PATCH_ID" 'MERGED' - ret="$?" - expected='MERGED' - output=$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT status FROM \"${DATABASE_PATCH_TABLE}\" WHERE ID=\"${TEST_PATCH_ID}\";") - assert_equals_helper 'Empty group should not be valid' "$LINENO" "$expected" "$output" - assert_equals_helper 'Expected no error' "$LINENO" "$ret" 0 + # Insere um patch para teste + sqlite3 "${KW_DATA_DIR}/kw.db" "INSERT INTO patch (title, author, contribution_id, commit_hash, version) VALUES ('EXISTING_PATCH', 'author_test', 2, 'def456', 1);" + patch_id="$(sqlite3 "${KW_DATA_DIR}/kw.db" "SELECT id FROM patch WHERE title='EXISTING_PATCH';")" - set_patch_status "$TEST_PATCH_ID" 'SENT' - ret="$?" - expected='SENT' - output=$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT status FROM \"${DATABASE_PATCH_TABLE}\" WHERE ID=\"${TEST_PATCH_ID}\";") - assert_equals_helper 'Empty group should not be valid' "$LINENO" "$expected" "$output" - assert_equals_helper 'Expected no error' "$LINENO" "$ret" 0 + declare -A cond_array=(['id']="$patch_id") + result="$(check_existence 'patch' 'cond_array')" + ret="$?" - printf 'REVIEWED\n' | set_patch_status "$TEST_PATCH_ID" 'INVALID' - ret="$?" - expected='REVIEWED' - output=$(sqlite3 "${KW_DATA_DIR}/kw.db" -batch "SELECT status FROM \"${DATABASE_PATCH_TABLE}\" WHERE ID=\"${TEST_PATCH_ID}\";") - assert_equals_helper 'Empty group should not be valid' "$LINENO" "$expected" "$output" - assert_equals_helper 'Expected no error' "$LINENO" "$ret" 0 + assert_equals_helper 'Expected check_existence to succeed' "$LINENO" 0 "$ret" + assert_equals_helper 'Expected existence to return 1' "$LINENO" 1 "$result" - # invalid values - output=$(set_patch_status '' 'SENT') - ret="$?" - expected='Patch ID is empty' - assert_equals_helper 'Empty group should not be valid' "$LINENO" "$expected" "$output" - assert_equals_helper 'Expected no error' "$LINENO" "$ret" 61 + declare -A cond_array=(['id']="150") + result="$(check_existence 'patch' 'cond_array')" + ret="$?" - output=$(set_patch_status "$TEST_PATCH_ID" '') - ret="$?" - expected='New status is empty' - assert_equals_helper 'Empty group should not be valid' "$LINENO" "$expected" "$output" - assert_equals_helper 'Expected no error' "$LINENO" "$ret" 61 + assert_equals_helper 'Expected check_existence to succeed' "$LINENO" 0 "$ret" + assert_equals_helper 'Expected existence to return 0' "$LINENO" 0 "$result" } -function test_get_patch_status() -{ - local expected - local output - local ret +function test_get_patch_info() { + local patch_id + local result - output=$(printf 's\n' | get_patch_status) - ret="$?" - expected='SENT' - assert_equals_helper 'Status should have been received' "$LINENO" "$expected" "$output" - assert_equals_helper 'Expected no error for "s"' "$LINENO" "$ret" 0 + # Insere patch válido + sqlite3 "${KW_DATA_DIR}/kw.db" "INSERT INTO patch (title, author, contribution_id, commit_hash, version) VALUES ('PATCH_INFO', 'authorX', 3, 'hash123', 1);" + patch_id="$(sqlite3 "${KW_DATA_DIR}/kw.db" "SELECT id FROM patch WHERE commit_hash='hash123';")" + + declare -A cond_array=(['id']="$patch_id") + result="$(get_patch_info 'id,title,commit_hash' cond_array)" + ret="$?" + + assert_equals_helper 'Expected get_patch_info to succeed' "$LINENO" 0 "$ret" + assertContains "$result" "hash123" + assertContains "$result" "PATCH_INFO" } -function test_check_valid_status() -{ - local expected - local output - local ret - # SENT - output=$(check_valid_status 's') - ret="$?" - expected='SENT' - assert_equals_helper 'Status "s" should be valid' "$LINENO" "$expected" "$output" - assert_equals_helper 'Expected no error for "s"' "$LINENO" "$ret" 0 +function test_get_patch_info_by_commit_hash() { + local result - output=$(check_valid_status 'sent') - ret="$?" - expected='SENT' - assert_equals_helper 'Status "sent" should be valid' "$LINENO" "$expected" "$output" - assert_equals_helper 'Expected no error for "sent"' "$LINENO" "$ret" 0 + # Insere patch válido + sqlite3 "${KW_DATA_DIR}/kw.db" "INSERT INTO patch (title, author, contribution_id, commit_hash, version) VALUES ('PATCH_BY_HASH', 'authorY', 4, 'hash999', 2);" - output=$(check_valid_status 'SeNt') - ret="$?" - expected='SENT' - assert_equals_helper 'Status "SeNt" should be valid' "$LINENO" "$expected" "$output" - assert_equals_helper 'Expected no error for "SeNt"' "$LINENO" "$ret" 0 + result="$(get_patch_info_by_commit_hash 'id,title,author,commit_hash' 'hash999')" + ret="$?" - output=$(check_valid_status 'SENT') - ret="$?" - expected='SENT' - assert_equals_helper 'Status "SENT" should be valid' "$LINENO" "$expected" "$output" - assert_equals_helper 'Expected no error for "SENT"' "$LINENO" "$ret" 0 + assert_equals_helper 'Expected get_patch_info_by_commit_hash to succeed' "$LINENO" 0 "$ret" + assertContains "$result" "hash999" + assertContains "$result" "PATCH_BY_HASH" +} - # APPROVED - output=$(check_valid_status 'a') - ret="$?" - expected='APPROVED' - assert_equals_helper 'Status "a" should be valid' "$LINENO" "$expected" "$output" - assert_equals_helper 'Expected no error for "a"' "$LINENO" "$ret" 0 +function test_new_patch() { + local result + local ret + local patch_id - output=$(check_valid_status 'approved') - ret="$?" - expected='APPROVED' - assert_equals_helper 'Status "approved" should be valid' "$LINENO" "$expected" "$output" - assert_equals_helper 'Expected no error for "approved"' "$LINENO" "$ret" 0 + # Criar novo patch + result="$(new_patch 'PATCH_OK' '' 'authorx@email' 1 'hash777' 1)" + ret="$?" - output=$(check_valid_status 'ApPrOvEd') - ret="$?" - expected='APPROVED' - assert_equals_helper 'Status "ApPrOvEd" should be valid' "$LINENO" "$expected" "$output" - assert_equals_helper 'Expected no error for "ApPrOvEd"' "$LINENO" "$ret" 0 + assert_equals_helper 'Expected new_patch to succeed' "$LINENO" 0 "$ret" - output=$(check_valid_status 'APPROVED') - ret="$?" - expected='APPROVED' - assert_equals_helper 'Status "APPROVED" should be valid' "$LINENO" "$expected" "$output" - assert_equals_helper 'Expected no error for "APPROVED"' "$LINENO" "$ret" 0 + # Conferir que id retornado corresponde a um patch no banco + patch_id="$(sqlite3 "${KW_DATA_DIR}/kw.db" "SELECT id FROM patch WHERE commit_hash='hash777';")" + assertEquals "Patch id returned must match db record" "$patch_id" "$result" +} - # REJECTED - output=$(check_valid_status 'r') - ret="$?" - expected='REJECTED' - assert_equals_helper 'Status "r" should be valid' "$LINENO" "$expected" "$output" - assert_equals_helper 'Expected no error for "r"' "$LINENO" "$ret" 0 +function test_get_patch_info_by_id() { + local result + local ret + local patch_id - output=$(check_valid_status 'rejected') - ret="$?" - expected='REJECTED' - assert_equals_helper 'Status "rejected" should be valid' "$LINENO" "$expected" "$output" - assert_equals_helper 'Expected no error for "rejected"' "$LINENO" "$ret" 0 + # Inserir patch diretamente + sqlite3 "${KW_DATA_DIR}/kw.db" \ + "INSERT INTO patch (id, title, author, contribution_id, commit_hash, version) \ + VALUES (120, 'PATCH_INFO_ID', 'authorY', 1, 'hash888', 2);" + patch_id="$(sqlite3 "${KW_DATA_DIR}/kw.db" "SELECT id FROM patch WHERE commit_hash='hash888';")" - output=$(check_valid_status 'ReJeCtEd') - ret="$?" - expected='REJECTED' - assert_equals_helper 'Status "ReJeCtEd" should be valid' "$LINENO" "$expected" "$output" - assert_equals_helper 'Expected no error for "ReJeCtEd"' "$LINENO" "$ret" 0 + # Buscar patch pelo id + result="$(get_patch_info_by_id 'id,title,commit_hash' "$patch_id")" + ret="$?" - output=$(check_valid_status 'REJECTED') - ret="$?" - expected='REJECTED' - assert_equals_helper 'Status "REJECTED" should be valid' "$LINENO" "$expected" "$output" - assert_equals_helper 'Expected no error for "REJECTED"' "$LINENO" "$ret" 0 + assert_equals_helper 'Expected get_patch_info_by_id to succeed' "$LINENO" 0 "$ret" + assertContains "$result" "hash888" + assertContains "$result" "PATCH_INFO_ID" +} - # MERGED - output=$(check_valid_status 'm') - ret="$?" - expected='MERGED' - assert_equals_helper 'Status "m" should be valid' "$LINENO" "$expected" "$output" - assert_equals_helper 'Expected no error for "m"' "$LINENO" "$ret" 0 +function test_insert_submission() { + output="$(insert_submission '1' "test_insert_submission@example.com")" + ret="$?" + assert_equals_helper 'insert_submission should succeed' "$LINENO" 0 "$ret" - output=$(check_valid_status 'merged') - ret="$?" - expected='MERGED' - assert_equals_helper 'Status "merged" should be valid' "$LINENO" "$expected" "$output" - assert_equals_helper 'Expected no error for "merged"' "$LINENO" "$ret" 0 + count="$(sqlite3 "${KW_DATA_DIR}/kw.db" "SELECT COUNT(*) FROM submission WHERE contribution_id=1 AND send_by='test_insert_submission@example.com';")" + assert_equals_helper 'submission row must exist' "$LINENO" 1 "$count" +} - output=$(check_valid_status 'MeRgEd') - ret="$?" - expected='MERGED' - assert_equals_helper 'Status "MeRgEd" should be valid' "$LINENO" "$expected" "$output" - assert_equals_helper 'Expected no error for "MeRgEd"' "$LINENO" "$ret" 0 +function test_get_last_submission_infos_by_contribution_id() { + # prepara contribution e duas submissions + sqlite3 "${KW_DATA_DIR}/kw.db" "INSERT INTO contribution (title, author_email, repository_id) VALUES ('C_TEST3', 'c@t', 3);" + contrib_id="$(sqlite3 "${KW_DATA_DIR}/kw.db" "SELECT id FROM contribution WHERE title='C_TEST3' LIMIT 1;")" + sqlite3 "${KW_DATA_DIR}/kw.db" "INSERT INTO submission (contribution_id, send_by) VALUES (${contrib_id}, 's1');" + sqlite3 "${KW_DATA_DIR}/kw.db" "INSERT INTO submission (contribution_id, send_by) VALUES (${contrib_id}, 's2');" + last_id="$(sqlite3 "${KW_DATA_DIR}/kw.db" "SELECT id FROM submission WHERE contribution_id=${contrib_id} ORDER BY id DESC LIMIT 1;")" + + output="$(get_last_submission_infos_by_contribution_id 'id' "$contrib_id")" + ret="$?" + assert_equals_helper 'get_last_submission_infos_by_contribution_id should succeed' "$LINENO" 0 "$ret" + assertContains "$output" "$last_id" +} - output=$(check_valid_status 'MERGED') - ret="$?" - expected='MERGED' - assert_equals_helper 'Status "MERGED" should be valid' "$LINENO" "$expected" "$output" - assert_equals_helper 'Expected no error for "MERGED"' "$LINENO" "$ret" 0 +function test_get_submission_info() { + # prepara contribution + submission + sqlite3 "${KW_DATA_DIR}/kw.db" "INSERT INTO contribution (title, author_email, repository_id) VALUES ('C_TEST4', 'd@t', 4);" + contrib_id="$(sqlite3 "${KW_DATA_DIR}/kw.db" "SELECT id FROM contribution WHERE title='C_TEST4' LIMIT 1;")" + sqlite3 "${KW_DATA_DIR}/kw.db" "INSERT INTO submission (contribution_id, send_by) VALUES (${contrib_id}, 'senderX');" + sub_id="$(sqlite3 "${KW_DATA_DIR}/kw.db" "SELECT id FROM submission WHERE contribution_id=${contrib_id} ORDER BY id DESC LIMIT 1;")" + + # definir array com o nome esperado internamente + declare -gA _submission_test_condition_array + _submission_test_condition_array=( ['contribution_id']="${contrib_id}" ) + + output="$(get_submission_info 'id,send_by' '_submission_test_condition_array' 'id DESC' '1')" + ret="$?" + assert_equals_helper 'get_submission_info should succeed' "$LINENO" 0 "$ret" + assertContains "$output" "${sub_id}" + assertContains "$output" "senderX" +} - # REVIEWED - output=$(check_valid_status 'rw') - ret="$?" - expected='REVIEWED' - assert_equals_helper 'Status "rw" should be valid' "$LINENO" "$expected" "$output" - assert_equals_helper 'Expected no error for "rw"' "$LINENO" "$ret" 0 +function test_new_submission() { + # prepara contribution + output="$(new_submission '1' "test_new_submission@email.com")" + ret="$?" + assert_equals_helper 'new_submission should return success' "$LINENO" 0 "$ret" - output=$(check_valid_status 'Rw') - ret="$?" - expected='REVIEWED' - assert_equals_helper 'Status "Rw" should be valid' "$LINENO" "$expected" "$output" - assert_equals_helper 'Expected no error for "Rw"' "$LINENO" "$ret" 0 + db_last_id="$(sqlite3 "${KW_DATA_DIR}/kw.db" "SELECT id FROM submission WHERE contribution_id=${contrib_id} ORDER BY id DESC LIMIT 1;")" + assert_equals_helper 'returned id must match last insertion' "$LINENO" "$db_last_id" "$output" +} - output=$(check_valid_status 'REVIEWED') - ret="$?" - expected='REVIEWED' - assert_equals_helper 'Status "REVIEWED" should be valid' "$LINENO" "$expected" "$output" - assert_equals_helper 'Expected no error for "REVIEWED"' "$LINENO" "$ret" 0 +function test_insert_patch_submission() { + # prepara contribution, submission, patch + sqlite3 "${KW_DATA_DIR}/kw.db" "INSERT INTO contribution (title, author_email, repository_id) VALUES ('C_TEST5', 'e@t', 5);" + contrib_id="$(sqlite3 "${KW_DATA_DIR}/kw.db" "SELECT id FROM contribution WHERE title='C_TEST5' LIMIT 1;")" + sqlite3 "${KW_DATA_DIR}/kw.db" "INSERT INTO submission (contribution_id, send_by) VALUES (${contrib_id}, 'snd');" + submission_id="$(sqlite3 "${KW_DATA_DIR}/kw.db" "SELECT id FROM submission WHERE contribution_id=${contrib_id} ORDER BY id DESC LIMIT 1;")" + sqlite3 "${KW_DATA_DIR}/kw.db" "INSERT INTO patch (title, author, contribution_id, commit_hash, version) VALUES ('PATCH_A', 'authorA', ${contrib_id}, 'hA', 1);" + patch_id="$(sqlite3 "${KW_DATA_DIR}/kw.db" "SELECT id FROM patch WHERE commit_hash='hA' LIMIT 1;")" + + output="$(insert_patch_submission "$patch_id" "$submission_id" "msg-1")" + ret="$?" + assert_equals_helper 'insert_patch_submission should succeed' "$LINENO" 0 "$ret" + + count="$(sqlite3 "${KW_DATA_DIR}/kw.db" "SELECT COUNT(*) FROM patch_submission WHERE patch_id=${patch_id} AND submission_id=${submission_id} AND message_id='msg-1';")" + assert_equals_helper 'patch_submission row must exist' "$LINENO" 1 "$count" +} - output=$(check_valid_status 'ReViEwEd') - ret="$?" - expected='REVIEWED' - assert_equals_helper 'Status "ReViEwEd" should be valid' "$LINENO" "$expected" "$output" - assert_equals_helper 'Expected no error for "ReViEwEd"' "$LINENO" "$ret" 0 +function test_new_patch_submission() { + # prepara contribution, submission, patch + sqlite3 "${KW_DATA_DIR}/kw.db" "INSERT INTO contribution (title, author_email, repository_id) VALUES ('C_TEST6', 'f@t', 6);" + contrib_id="$(sqlite3 "${KW_DATA_DIR}/kw.db" "SELECT id FROM contribution WHERE title='C_TEST6' LIMIT 1;")" + sqlite3 "${KW_DATA_DIR}/kw.db" "INSERT INTO submission (contribution_id, send_by) VALUES (${contrib_id}, 'snd2');" + submission_id="$(sqlite3 "${KW_DATA_DIR}/kw.db" "SELECT id FROM submission WHERE contribution_id=${contrib_id} ORDER BY id DESC LIMIT 1;")" + sqlite3 "${KW_DATA_DIR}/kw.db" "INSERT INTO patch (title, author, contribution_id, commit_hash, version) VALUES ('PATCH_B', 'authorB', ${contrib_id}, 'hB', 1);" + patch_id="$(sqlite3 "${KW_DATA_DIR}/kw.db" "SELECT id FROM patch WHERE commit_hash='hB' LIMIT 1;")" + + output="$(new_patch_submission "$patch_id" "$submission_id" "msg-2")" + ret="$?" + assert_equals_helper 'new_patch_submission should succeed' "$LINENO" 0 "$ret" + + count="$(sqlite3 "${KW_DATA_DIR}/kw.db" "SELECT COUNT(*) FROM patch_submission WHERE patch_id=${patch_id} AND submission_id=${submission_id} AND message_id='msg-2';")" + assert_equals_helper 'patch_submission row must exist after new_patch_submission' "$LINENO" 1 "$count" +} - #invalid values - check_valid_status 'InVaLID' - ret="$?" - assert_equals_helper 'Expected no error for "ReViEwEd"' "$LINENO" "$ret" 22 +function test_check_patch_submission_existence_by_unique_attributes() { + # prepara contribution, submission, patch + relation + sqlite3 "${KW_DATA_DIR}/kw.db" "INSERT INTO contribution (title, author_email, repository_id) VALUES ('C_TEST7', 'g@t', 7);" + contrib_id="$(sqlite3 "${KW_DATA_DIR}/kw.db" "SELECT id FROM contribution WHERE title='C_TEST7' LIMIT 1;")" + sqlite3 "${KW_DATA_DIR}/kw.db" "INSERT INTO submission (contribution_id, send_by) VALUES (${contrib_id}, 'snd3');" + submission_id="$(sqlite3 "${KW_DATA_DIR}/kw.db" "SELECT id FROM submission WHERE contribution_id=${contrib_id} ORDER BY id DESC LIMIT 1;")" + sqlite3 "${KW_DATA_DIR}/kw.db" "INSERT INTO patch (title, author, contribution_id, commit_hash, version) VALUES ('PATCH_C', 'authorC', ${contrib_id}, 'hC', 1);" + patch_id="$(sqlite3 "${KW_DATA_DIR}/kw.db" "SELECT id FROM patch WHERE commit_hash='hC' LIMIT 1;")" + sqlite3 "${KW_DATA_DIR}/kw.db" "INSERT INTO patch_submission (patch_id, submission_id, message_id) VALUES (${patch_id}, ${submission_id}, 'msg-exist');" + + output="$(check_patch_submission_existence_by_unique_attributes "$patch_id" "$submission_id" 'msg-exist')" + ret="$?" + assert_equals_helper 'check_patch_submission_existence_by_unique_attributes should succeed' "$LINENO" 0 "$ret" + assert_equals_helper 'existence must be reported as 1' "$LINENO" 1 "$output" +} + +function test_get_patch_submission_info() { + # prepara relation + sqlite3 "${KW_DATA_DIR}/kw.db" "INSERT INTO contribution (title, author_email, repository_id) VALUES ('C_TEST8', 'h@t', 8);" + contrib_id="$(sqlite3 "${KW_DATA_DIR}/kw.db" "SELECT id FROM contribution WHERE title='C_TEST8' LIMIT 1;")" + sqlite3 "${KW_DATA_DIR}/kw.db" "INSERT INTO submission (contribution_id, send_by) VALUES (${contrib_id}, 'snd4');" + submission_id="$(sqlite3 "${KW_DATA_DIR}/kw.db" "SELECT id FROM submission WHERE contribution_id=${contrib_id} ORDER BY id DESC LIMIT 1;")" + sqlite3 "${KW_DATA_DIR}/kw.db" "INSERT INTO patch (title, author, contribution_id, commit_hash, version) VALUES ('PATCH_D', 'authorD', ${contrib_id}, 'hD', 1);" + patch_id="$(sqlite3 "${KW_DATA_DIR}/kw.db" "SELECT id FROM patch WHERE commit_hash='hD' LIMIT 1;")" + sqlite3 "${KW_DATA_DIR}/kw.db" "INSERT INTO patch_submission (patch_id, submission_id, message_id) VALUES (${patch_id}, ${submission_id}, 'msg-info');" + + declare -gA _patch_submission_test_condition_array + _patch_submission_test_condition_array=( ['patch_id']="${patch_id}" ) + + output="$(get_patch_submission_info 'submission_id,message_id' _patch_submission_test_condition_array)" + ret="$?" + assert_equals_helper 'get_patch_submission_info should succeed' "$LINENO" 0 "$ret" + assertContains "$output" "msg-info" + assertContains "$output" "${submission_id}" +} + +function test_insert_contribution_success() { + insert_contribution "Contrib Test 1" "user1@example.com" 1 + ret="$?" + assertEquals "insert_contribution should succeed" 0 "$ret" + + db_count="$(sqlite3 "${KW_DATA_DIR}/kw.db" "SELECT COUNT(*) FROM contribution WHERE title='Contrib Test 1' AND author_email='user1@example.com' AND repository_id=1;")" + assertEquals "Contribution should exist in DB" 1 "$db_count" +} + +function test_new_contribution_success() { + output="$(new_contribution "Contrib Test 2" "user2@example.com" 1)" + ret="$?" + + db_id="$(sqlite3 "${KW_DATA_DIR}/kw.db" "SELECT id FROM contribution WHERE title='Contrib Test 2';")" + assert_equals_helper "Returned ID should match DB" "$LINENO" "$db_id" "$output" + assert_equals_helper "new_contribution_success" "$LINENO" "$ret" 0 +} + +function test_check_contribution_existence_by_title_success() { + output="$(check_contribution_existence_by_title "TEST_CONTRIBUTION")" + ret="$?" + assert_equals_helper "check_contribution_existence_by_title should succeed" "$LINENO" 0 "$ret" + assert_equals_helper "Existence check should return 1" "$LINENO" 1 "$output" +} + +function test_get_contribution_info_by_title_success() { + output="$(get_contribution_info_by_title 'id' 'TEST_CONTRIBUTION')" + ret="$?" + assert_equals_helper "get_contribution_info_by_title should succeed" "$LINENO" 0 "$ret" + assert_equals_helper "Output ID should not be null" "$LINENO" 1 "$output" +} +function test_get_contribution_info_success() { + condition_array=(['title']="TEST_CONTRIBUTION") + output="$(get_contribution_info 'id' 'condition_array')" + ret="$?" + assert_equals_helper "get_contribution_info should succeed" "$LINENO" 0 "$ret" + assert_equals_helper "Output ID should not be null" "$LINENO" 1 "$output" } invoke_shunit From 1beb3da5928b233f77577c2c0b4372b254a1bdf8 Mon Sep 17 00:00:00 2001 From: JGBSouza Date: Wed, 8 Oct 2025 00:37:32 -0300 Subject: [PATCH 07/33] pre registro patches Signed-off-by: JGBSouza --- database/kwdb.sql | 18 +- message1.eml | 0 src/lib/kw_db.sh | 2 + src/lib/kwlib.sh | 22 +- .../patch_track_utils/contribution_utils.sh | 77 ++++-- src/lib/patch_track_utils/patch_utils.sh | 30 +- src/lib/patch_track_utils/submission_utils.sh | 8 +- src/patch_track.sh | 257 +++++++++++++----- src/send_patch.sh | 34 +-- test.sh | 51 ++++ tests/unit/patch_track_test.sh | 24 +- 11 files changed, 361 insertions(+), 162 deletions(-) create mode 100644 message1.eml create mode 100755 test.sh diff --git a/database/kwdb.sql b/database/kwdb.sql index a8135036b..52cf07024 100644 --- a/database/kwdb.sql +++ b/database/kwdb.sql @@ -53,10 +53,7 @@ CREATE TABLE IF NOT EXISTS "patch" ( "created_at" TEXT DEFAULT (datetime('now','localtime')), "status" VARCHAR(50) DEFAULT ('SENT') NOT NULL, "title" TEXT NOT NULL, - "last_patch_id" INTEGER, - "outdated" INTEGER NOT NULL CHECK ("outdated" IN (0, 1)) DEFAULT 0, - "version" INTEGER NOT NULL, - "author" TEXT, + "author_email" TEXT, "contribution_id" INTEGER NOT NULL, "commit_hash" TEXT, CHECK ("status" IN ('SENT', 'APPROVED', 'MERGED', 'REVIEWED', 'REJECTED')), @@ -65,24 +62,22 @@ CREATE TABLE IF NOT EXISTS "patch" ( FOREIGN KEY ("contribution_id") REFERENCES "contribution"("id") ON DELETE CASCADE ); -CREATE TABLE IF NOT EXISTS "patch_tag" ( +CREATE TABLE IF NOT EXISTS "contribution_tag" ( "id" INTEGER, "name" VARCHAR(50) NOT NULL UNIQUE, PRIMARY KEY("id") ); -CREATE TABLE IF NOT EXISTS "patch_tag_relation" ( - "id" INTEGER, +CREATE TABLE IF NOT EXISTS "contribution_tag_relation" ( "tag_id" INTEGER NOT NULL, - "patch_id" INTEGER NOT NULL, - PRIMARY KEY("id"), + "contribution_id" INTEGER NOT NULL, FOREIGN KEY ("tag_id") REFERENCES "patch_tag"("id") ON DELETE CASCADE, - FOREIGN KEY ("patch_id") REFERENCES "patch"("id") ON DELETE CASCADE + FOREIGN KEY ("contribution_id") REFERENCES "contribution"("id") ON DELETE CASCADE, + UNIQUE("tag_id", "contribution_id") ); CREATE TABLE IF NOT EXISTS "submission" ( "id" INTEGER, - "created_at" TEXT DEFAULT (datetime('now','localtime')), "contribution_id" INTEGER NOT NULL, "send_by" TEXT NOT NULL, PRIMARY KEY("id"), @@ -92,6 +87,7 @@ CREATE TABLE IF NOT EXISTS "submission" ( CREATE TABLE IF NOT EXISTS "patch_submission" ( "patch_id" INTEGER NOT NULL, "submission_id" INTEGER NOT NULL, + "created_at" TEXT DEFAULT (datetime('now','localtime')), "message_id" TEXT NOT NULL, PRIMARY KEY ("patch_id", "submission_id", "message_id"), FOREIGN KEY ("submission_id") REFERENCES "submission"("id") ON DELETE CASCADE, diff --git a/message1.eml b/message1.eml new file mode 100644 index 000000000..e69de29bb diff --git a/src/lib/kw_db.sh b/src/lib/kw_db.sh index 4aaa8f034..0941e25ef 100644 --- a/src/lib/kw_db.sh +++ b/src/lib/kw_db.sh @@ -95,6 +95,7 @@ function insert_into() [[ -n "$entries" && ! "$entries" =~ ^\(.*\)$ ]] && entries="($entries)" cmd="sqlite3 -init "${KW_DB_DIR}/pre_cmd.sql" \"${db_path}\" -batch \"INSERT INTO ${table} ${entries} VALUES ${values};\"" + echo "$cmd" > /dev/tty cmd_manager "$flag" "$cmd" } @@ -239,6 +240,7 @@ function select_from() fi cmd="sqlite3 -init "${KW_DB_DIR}/pre_cmd.sql" -cmd \"${pre_cmd}\" \"${db_path}\" -batch \"${query}\"" + echo "$cmd" > /dev/tty cmd_manager "$flag" "$cmd" } diff --git a/src/lib/kwlib.sh b/src/lib/kwlib.sh index be1b31b55..18bc93ea6 100644 --- a/src/lib/kwlib.sh +++ b/src/lib/kwlib.sh @@ -768,6 +768,24 @@ function get_patch_commit_hash() fi patch_commit=$(grep -m 1 "^From " "$FILE_PATH" | awk '{print $2}') - - printf '%s\n' "${patch_commit}aaaaaaaa" + printf '%s\n' "${patch_commit}" } + +# This function receives a patch file and extracts it's subject +# @FILE_PATH: Path to the patch file +# +# Return: +# The title of the patch. +function get_patch_subject() +{ + local -r FILE_PATH="$1" + local patch_title + + if [[ ! -f "$FILE_PATH" ]]; then + return 1 # EPERM + fi + + patch_title=$(grep -m 1 "^Subject: " "$FILE_PATH" | sed 's/^Subject: \[PATCH[^]]*\] //') + + printf '%s\n' "$patch_title" +} \ No newline at end of file diff --git a/src/lib/patch_track_utils/contribution_utils.sh b/src/lib/patch_track_utils/contribution_utils.sh index c9dc37c3f..9e281b687 100644 --- a/src/lib/patch_track_utils/contribution_utils.sh +++ b/src/lib/patch_track_utils/contribution_utils.sh @@ -9,16 +9,25 @@ function insert_contribution() local _author_email="$2" local _repository_id="$3" + echo "começou insert" > /dev/tty + # Checagem de valores nulos - if [[ -z "$_contribution_title" || -z "$_author_email" || -z "$_repository_id" ]]; then + if [[ -z "$_contribution_title" || -z "$_author_email" ]]; then complain "($LINENO): missing mandatory field(s) for insert_contribution" return 22 # EINVAL fi - sql_operation_result=$(insert_into "$DATABASE_CONTRIBUTION_TABLE" \ - '("title", "author_email", "repository_id")' \ - "('${_contribution_title}', '${_author_email}', '${_repository_id}')") + columns='"title", "author_email"' + values="'${_contribution_title}', '${_author_email}'" + + if [[ -n "${_repository_id}" ]]; then + columns="$columns, \"repository_id\"" + values="$values, '${_repository_id}'" + fi + sql_operation_result=$(insert_into "$DATABASE_CONTRIBUTION_TABLE" \ + "($columns)" \ + "($values)") ret="$?" if [[ "$ret" -eq 2 || "$ret" -eq 61 ]]; then @@ -29,46 +38,50 @@ function insert_contribution() return 22 # EINVAL fi + echo "terminou insert" > /dev/tty + return 0 } -function new_contribution() +function get_or_create_contribution() { local _contribution_title="$1" local _author_email="$2" local _repository_id="$3" local sql_operation_result local insert_contribution_result - local get_contribution_id_result + local get_contribution_id_resultget_or_create_contribution # Checagem de valores nulos - if [[ -z "$_contribution_title" || -z "$_author_email" || -z "$_repository_id" ]]; then + if [[ -z "$_contribution_title" || -z "$_author_email" ]]; then complain "($LINENO): missing mandatory field(s) for new_contribution" return 22 # EINVAL fi - #patch_existent_result="$(check_contribution_existence_by_title "$_contribution_title")" - #ret="$?" - - #if [[ "$ret" -ne 0 ]]; then - # complain "$patch_existent_result" - # return "$ret" - #fi - - #if [[ "$patch_existent_result" -ne 0 ]]; then - # complain "($LINENO): error while trying to create new contribution ${_contribution_title}, contribution already exists" - # return 22 # EINVAL - #fi + echo "check existence ${_contribution_title} ee ${_author_email}" > /dev/tty - insert_contribution_result=$(insert_contribution "$_contribution_title" "$_author_email" "$_repository_id") + patch_existent_result="$(check_contribution_existence_by_unique_attributes "$_contribution_title" "$_author_email")" ret="$?" if [[ "$ret" -ne 0 ]]; then - complain "$insert_contribution_result" + complain "$patch_existent_result" return "$ret" fi - get_contribution_id_result=$(get_contribution_info_by_title 'id' "$_contribution_title") + if [[ "$patch_existent_result" -eq 0 ]]; then + echo "insert" > /dev/tty + + insert_contribution_result=$(insert_contribution "$_contribution_title" "$_author_email" "$_repository_id") + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$insert_contribution_result" + return "$ret" + fi + fi + + echo "get id" > /dev/tty + get_contribution_id_result=$(get_contribution_info_by_unique_attributes 'id' "$_contribution_title" "$_author_email") ret="$?" if [[ "$ret" -ne 0 ]]; then @@ -80,19 +93,23 @@ function new_contribution() return 0 } -function check_contribution_existence_by_title() +function check_contribution_existence_by_unique_attributes() { local _contribution_title="$1" + local _author_email="$2" local sql_operation_result local ret # Checagem de valor nulo - if [[ -z "$_contribution_title" ]]; then - complain "($LINENO): empty contribution title for check_contribution_existence_by_title" + if [[ -z "$_contribution_title" || -z "$_author_email" ]]; then + complain "($LINENO): empty contribution infos for check_contribution_existence_by_unique_attributes" return 22 # EINVAL fi - sql_operation_result="$(check_existence "$DATABASE_CONTRIBUTION_TABLE" "$_contribution_title")" + condition_array=(['title']="${_contribution_title}") + condition_array=(['author_email']="${_author_email}") + + sql_operation_result="$(check_existence "$DATABASE_CONTRIBUTION_TABLE" 'condition_array')" ret="$?" if [[ "$ret" -ne 0 ]]; then @@ -109,20 +126,22 @@ function check_contribution_existence_by_title() return 0 } -function get_contribution_info_by_title() +function get_contribution_info_by_unique_attributes() { local _contribution_infos="$1" local _contribution_title="$2" + local _author_email="$3" local sql_operation_result local ret - if [[ -z "$_contribution_title" ]]; then + if [[ -z "$_contribution_title" || -z "$_author_email" ]]; then complain "($LINENO): empty contribution title for get_contribution_info_by_title" return 22 # EINVAL fi condition_array=(['title']="${_contribution_title}") - + condition_array=(['author_email']="${_author_email}") + sql_operation_result="$(get_contribution_info "$_contribution_infos" 'condition_array')" ret="$?" diff --git a/src/lib/patch_track_utils/patch_utils.sh b/src/lib/patch_track_utils/patch_utils.sh index 68a62b0a6..c24fcc446 100644 --- a/src/lib/patch_track_utils/patch_utils.sh +++ b/src/lib/patch_track_utils/patch_utils.sh @@ -80,7 +80,7 @@ function replace_patch() return 0 } -function new_patch() +function get_or_create_patch() { local _patch_title="$1" local _last_patch_id="$2" @@ -93,7 +93,7 @@ function new_patch() local insert_contribution_result local get_contribution_id_result - if [[ -z "$_patch_title" || -z "$_patch_author" || -z "$_contribution_id" || -z "$_commit_hash" ]]; then + if [[ -z "$_patch_title" || -z "$_patch_author" || -z "$_contribution_id" ]]; then complain "($LINENO): missing mandatory field for new_patch" return 22 # EINVAL fi @@ -115,18 +115,15 @@ function new_patch() return 22 # EINVAL fi - if [[ "$patch_existent_result" -ne 0 ]]; then - complain "($LINENO): error while trying to create new patch serie ${contribution_name}, patch serie already exists" - return 22 # EINVAL - fi - - insert_contribution_result=$(insert_patch "$_patch_title" "$_last_patch_id" "$_patch_author" "$_contribution_id"\ - "$_commit_hash" "$_patch_version") - ret="$?" + if [[ "$patch_existent_result" -eq 0 ]]; then + insert_contribution_result=$(insert_patch "$_patch_title" "$_last_patch_id" "$_patch_author" "$_contribution_id"\ + "$_commit_hash" "$_patch_version") + ret="$?" - if [[ "$ret" -ne 0 ]]; then - complain "$insert_contribution_result" - return "$ret" # EINVAL + if [[ "$ret" -ne 0 ]]; then + complain "$insert_contribution_result" + return "$ret" # EINVAL + fi fi get_contribution_id_result=$(get_patch_info_by_commit_hash 'id' "$_commit_hash") @@ -226,7 +223,12 @@ function check_patch_existence_by_unique_attributes() local _contribution_id="$3" local _commit_hash="$4" - if [[ -z "$_patch_title" && -z "$_patch_author" && -z "$_contribution_id" && -z "$_commit_hash" ]]; then + if [[ -z "$_commit_hash" ]]; then + printf '%s\n' 0 + return 0 # Not safe to check if the patch is the same if there is no commit hash + fi + + if [[ -z "$_patch_title" || -z "$_patch_author" || -z "$_contribution_id" ]]; then complain "($LINENO): no attributes provided for check_patch_existence_by_unique_attributes" return 22 # EINVAL fi diff --git a/src/lib/patch_track_utils/submission_utils.sh b/src/lib/patch_track_utils/submission_utils.sh index bfcf4620d..e9533655d 100644 --- a/src/lib/patch_track_utils/submission_utils.sh +++ b/src/lib/patch_track_utils/submission_utils.sh @@ -25,7 +25,7 @@ function insert_submission() return 0 } -function new_submission() +function create_submission() { local _contribution_id="$1" local _submission_author="$2" @@ -46,11 +46,11 @@ function new_submission() ret="$?" if [[ "$ret" -ne 0 ]]; then - complain "$get_contribution_id_result" + complain "$get_submission_id_result" return "$ret" # EINVAL fi - printf '%s\n' "$get_contribution_id_result" + printf '%s\n' "$get_submission_id_result" return 0 } @@ -94,7 +94,7 @@ function get_submission_info() return 0 } -function new_patch_submission() +function create_patch_submission() { local _patch_id="$1" local _submission_id="$2" diff --git a/src/patch_track.sh b/src/patch_track.sh index e97098df9..01e4f2794 100644 --- a/src/patch_track.sh +++ b/src/patch_track.sh @@ -1,5 +1,8 @@ include "${KW_LIB_DIR}/lib/kwlib.sh" include "${KW_LIB_DIR}/lib/kw_string.sh" +include "${KW_LIB_DIR}/lib/patch_track_utils/contribution_utils.sh" +include "${KW_LIB_DIR}/lib/patch_track_utils/patch_utils.sh" +include "${KW_LIB_DIR}/lib/patch_track_utils/submission_utils.sh" declare -gA options_values declare -gA condition_array @@ -45,72 +48,6 @@ function patch_track_main() return 0 } -# This function inserts each patch subject into the database and handles -# errors related to empty subjects or database insertion failures. -# -# @patches_subjects: Array of patch subjects to be registered. -# -# Return: -# Returns 0 if successful; 22 if there is an invalid argument or -# an error during insertion. -function register_patch_track() -{ - local -n _patches_commit_hash="$1" - local sql_operation_result - local ret - - for commit_hash in "${_patches_commit_hash[@]}"; do - #if [[ -z "$commit_hash" ]]; then - # complain 'Patch hash is empty' - # return 61 # ENODATA - #fi - echo "$commit_hash" - echo "1:" - #insert_patch_result=$(new_patch "$patch_metadata") - #if [[ "$ret" -ne 0 ]]; then - # return $ret # EINVAL - #fi - done - - success "Patch registered successfully." -} - -function extract_commit_hash_from_patch_files() { - local patches_dir="$1" - local -n _commits_array="$2" - - for patch_path in "${patches_dir}/"*; do - if is_a_patch "$patch_path"; then - local sha - sha=$(head -n1 "$patch_path" | awk '{print $2}') - _commits_array+=("AAA$sha") - fi - done -} - -function extract_patches_from_file() -{ - _total_patches_extracted="$1" - _send_email_log_file="$2" - - for n in $(seq 1 "$_total_patches_extracted"); do - header_block=$(grep -Poz '^From:[^\n]*\nTo:[^\n]*\nSubject:[^\n]*\nDate:[^\n]*\nMessage-ID:[^\n]*\nX-Commit-SHA:[^\n]*' "$_send_email_log_file" | head -n "$n" | tail -n 1) - - if [[ -z "$header_block" ]]; then - #echo "Erro: patch número $n não encontrado" >&2 - continue - fi - - # extrai cada campo do bloco - from=$(grep '^From:' <<< "$header_block" | sed 's/^From:[[:space:]]*//') - to=$(grep '^To:' <<< "$header_block" | sed 's/^To:[[:space:]]*//') - subject=$(grep '^Subject:' <<< "$header_block" | sed 's/^Subject:[[:space:]]*//') - date=$(grep '^Date:' <<< "$header_block" | sed 's/^Date:[[:space:]]*//') - message_id=$(grep '^Message-ID:' <<< "$header_block" | sed 's/^Message-ID:[[:space:]]*//') - xcommit_sha=$(grep '^X-Commit-SHA:' <<< "$header_block" | sed 's/^X-Commit-SHA:[[:space:]]*//') - done -} - # This function displays the patches dashboard based on provided filters. # It fetches patches from the database according to the conditions # and prints them in a formatted table. @@ -146,6 +83,192 @@ function show_patches_dashboard() print_patches_dashboard 'patches_array' "$columns" } +# This function inserts each patch subject into the database and handles +# errors related to empty subjects or database insertion failures. +# +# @patches_subjects: Array of patch subjects to be registered. +# +# Return: +# Returns 0 if successful; 22 if there is an invalid argument or +# an error during insertion. + +function register_patch_track() +{ + local patch_cache="$1" + local send_patch_output_dir="$2" + local contribution_name="$3" + local ret + local -A patches_message_id_array + + from="joaosouzaaa12@gmail.com" + get_or_create_contribution_result="$(get_or_create_contribution "contribution_name" "$from" '')" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$ret" + return "$ret" + fi + + register_patches "$patch_cache" "$send_patch_output_dir" "$get_or_create_contribution_result" 'patches_message_id_array' + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "($LINENO): Error while trying to register patches" + return "$ret" + fi + + create_submission_result="$(create_submission "$get_or_create_contribution_result" "$from")" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$ret" + return "$ret" + fi + + register_patch_submissions_result="$(register_patch_submissions "$create_submission_result" 'patches_message_id_array')" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$ret" + return "$ret" + fi + + success "Patch registered successfully." + return 0 +} + +function register_contribution() +{ + local contribution_name="$1" + local from + local get_or_create_contribution_result + + #from="$(grep -m 1 '^From:' <<< "$header_block" | tail -n 1 | sed 's/^From:[[:space:]]*//')" + from="joaosouzaaa12@gmail.com" + get_or_create_contribution_result="$(get_or_create_contribution "contribution_name" "$from")" + ret="$?" + + echo "começou contribution" > /dev/tty + + if [[ "$ret" -ne 0 ]]; then + complain "$get_or_create_contribution_result" + return "$ret" + fi + + echo "registrou contribution" > /dev/tty + printf '%s\n' "$get_or_create_contribution_result" + return 0 +} + +function register_patches() +{ + local patch_cache="$1" + local send_patch_output_dir="$2" + local contribution_id="$3" + local -n patches_output_array="$4" + local patch_num=1 + local -A patch_metadata + local get_or_create_patch_result + + patch_numbers="$(find "${patch_cache}" -maxdepth 1 -type f -name "*.patch" | wc -l)" + + patches_output_array=() + + if [[ "$patch_numbers" -ne 1 ]]; then #if there is a cover letter it wouldn't be in the patch_cache dir + extract_patch_from_file "$patch_num" "$send_patch_output_dir" 'patch_metadata' + get_or_create_patch_result="$(get_or_create_patch "${patch_metadata["subject"]}" '' "${patch_metadata["from"]}"\ + "$contribution_id" '' 1)" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$get_or_create_patch_result" + return "$ret" + fi + + patches_output_array["$get_or_create_patch_result"]='' + + ((patch_num++)) + echo "${patch_metadata["subject"]}" + fi + + for patch_path in "${patch_cache}/"*; do + if is_a_patch "$patch_path"; then + extract_patch_from_file "$patch_num" "$send_patch_output_dir" 'patch_metadata' + patch_metadata["commit_hash"]="$(get_patch_commit_hash "$patch_path")" + + get_or_create_patch_result="$(get_or_create_patch "${patch_metadata["subject"]}" '' "${patch_metadata["from"]}"\ + "$contribution_id" "${patch_metadata["commit_hash"]}" 1)" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$get_or_create_patch_result" + return "$ret" + fi + + patches_output_array["$get_or_create_patch_result"]="${patch_metadata["message_id"]}" + echo "${patch_metadata["subject"]}" + echo "${patch_metadata["commit_hash"]}" + ((patch_num++)) + fi + done + + return 0; +} + +function register_patch_submissions() +{ + local submission_id="$1" + local -n patches_message_id="$2" + + echo "começou patch submission register " > /dev/tty + + for patch_id in "${!patches_message_id[@]}"; do + message_id="${patches_message_id[$patch_id]}" + create_patch_submission_result="$(create_patch_submission "$patch_id" "$submission_id" "$message_id")" + ret="$?" + echo "${patch_id} EE ${message_id}" > /dev/tty + if [[ "$ret" -ne 0 ]]; then + complain "$create_patch_submission_result" + return "$ret" + fi + done + + return 0 +} + +#function register_submission() +#{ +# local contribution_id="$1" +# +#} + +function extract_patch_from_file() +{ + local _patch_extracted_num="$1" + local _send_email_log_file="$2" + local -n _output_metadata_array="$3" + local header_block + + echo "PATCH NUM ${_patch_extracted_num} LOG FILE: ${_send_email_log_file}" + header_block=$(cat "$_send_email_log_file" \ + | grep -Pzo \ + 'MAIL FROM:[^\n]*\nRCPT TO:[^\n]*\nFrom:[^\n]*\nTo:[^\n]*\nSubject:[^\n]*\nDate:[^\n]*\nMessage-ID:[^\n]*(?!X-Mailer:)' | tr '\0' '\n') + + if [[ -z "$header_block" ]]; then + echo "Erro: patch número $_patch_extracted_num não encontrado" >&2 + return 1 + fi + + _output_metadata_array=() + _output_metadata_array["from"]="$(grep -m "$_patch_extracted_num" '^From:' <<< "$header_block" | tail -n 1 | sed 's/^From:[[:space:]]*//')" + _output_metadata_array["to"]="$(grep -m "$_patch_extracted_num" '^To:' <<< "$header_block" | tail -n 1 | sed 's/^To:[[:space:]]*//')" + _output_metadata_array["subject"]="$(grep -m "$_patch_extracted_num" '^Subject:' <<< "$header_block" | tail -n 1 | sed 's/^Subject:[[:space:]]*//')" + _output_metadata_array["date"]="$(grep -m "$_patch_extracted_num" '^Date:' <<< "$header_block" | tail -n 1 | sed 's/^Date:[[:space:]]*//')" + _output_metadata_array["message_id"]="$(grep -m "$_patch_extracted_num" '^Message-ID:' <<< "$header_block" | tail -n 1 | sed 's/^Message-ID:[[:space:]]*//')" + + return 0 +} + # Displays the patches dashboard based on provided filters. It # fetches patches from the database according to the conditions # and prints them in a formatted table. @@ -375,4 +498,4 @@ function patch_track_help() printf '%s\n' 'kw patch-track:' \ ' patch-track (-d|--dashboard) [[--from ] | [--after ] [--before ]] - Show patches dashboard in chronological order ' \ ' patch-track (--id ) [-s[=]| --set-status[=]] - Set the patch status ' -} +} \ No newline at end of file diff --git a/src/send_patch.sh b/src/send_patch.sh index 97040008e..f6afee5cd 100644 --- a/src/send_patch.sh +++ b/src/send_patch.sh @@ -1,3 +1,4 @@ + # This file handles all the interactions with git send-email. Currently it # provides functions to configure the options used by git send-email. # It's also able to verify if the configurations required to use git send-email @@ -20,12 +21,12 @@ declare -ga essential_config_options=('user.name' 'user.email' declare -ga optional_config_options=('sendemail.smtpencryption' 'sendemail.smtppass') declare -gr email_regex='[A-Za-z0-9_\.-]+@[A-Za-z0-9_-]+(\.[A-Za-z0-9]+)+' +declare -g output_file="${KW_CACHE_DIR}/send_patch_output.log" #shellcheck disable=SC2119 function send_patch_main() { local flag - declare -a patches_subjects=() flag=${flag:-'SILENT'} @@ -44,8 +45,8 @@ function send_patch_main() [[ -n "${options_values['VERBOSE']}" ]] && flag='VERBOSE' if [[ -n "${options_values['SEND']}" ]]; then - mail_send "$flag" patches_subjects - register_patch_track patches_subjects + mail_send "$flag" + register_patch_track "${KW_CACHE_DIR}/patches" "$output_file" return 0 fi @@ -91,7 +92,6 @@ function send_patch_main() function mail_send() { local flag="$1" - local -n patches_titles="$2" local opts="${send_patch_config[send_opts]}" local to_recipients="${options_values['TO']}" local cc_recipients="${options_values['CC']}" @@ -101,16 +101,12 @@ function mail_send() local extra_opts="${options_values['PASS_OPTION_TO_SEND_EMAIL']}" local private="${options_values['PRIVATE']}" local rfc="${options_values['RFC']}" - local use_default_to_cc_approach="${send_patch_config['use_default_to_cc_approach']}" local kernel_root local patch_count=0 local cmd='git send-email' - local cover_letter='cover-letter' flag=${flag:-'SILENT'} - [[ "$use_default_to_cc_approach" == 'yes' ]] && cover_letter='' - [[ -n "$dryrun" ]] && cmd+=" $dryrun" if [[ -n "$to_recipients" ]]; then @@ -127,15 +123,14 @@ function mail_send() patch_count="$(pre_generate_patches "$commit_range" "$version" 'patches_titles')" if [[ "$patch_count" -eq 1 ]]; then opts="$(sed 's/--cover-letter//g' <<< "$opts")" - cover_letter='' fi kernel_root="$(find_kernel_root "$PWD")" # if inside a kernel repo use get_maintainer to populate recipients if [[ -z "$private" && -n "$kernel_root" ]]; then generate_kernel_recipients "$kernel_root" - cmd+=" --to-cmd='bash ${KW_PLUGINS_DIR}/kw_mail/to_cc_cmd.sh ${KW_CACHE_DIR} to ${cover_letter}'" - cmd+=" --cc-cmd='bash ${KW_PLUGINS_DIR}/kw_mail/to_cc_cmd.sh ${KW_CACHE_DIR} cc ${cover_letter}'" + cmd+=" --to-cmd='bash ${KW_PLUGINS_DIR}/kw_mail/to_cc_cmd.sh ${KW_CACHE_DIR} to'" + cmd+=" --cc-cmd='bash ${KW_PLUGINS_DIR}/kw_mail/to_cc_cmd.sh ${KW_CACHE_DIR} cc'" fi [[ -n "$opts" ]] && cmd+=" $opts" @@ -201,8 +196,6 @@ function pre_generate_patches() for patch_path in "${patch_cache}/"*; do if is_a_patch "$patch_path"; then ((count++)) - title=("aaaa") - _patches_titles+=("$title") fi done @@ -233,7 +226,7 @@ function generate_kernel_recipients() local default_to_recipients="${send_patch_config[default_to_recipients]}" local default_cc_recipients="${send_patch_config[default_cc_recipients]}" local get_maintainer_cmd="perl ${kernel_root}/scripts/get_maintainer.pl" - get_maintainer_cmd+=" --nogit --nogit-fallback --no-n --multiline" + get_maintainer_cmd+=" --nogit --nogit-fallback --no-r --no-n --multiline" get_maintainer_cmd+=" --nokeywords --norolestats --remove-duplicates" mkdir -p "${patch_cache}/to/" "${patch_cache}/cc/" @@ -260,15 +253,10 @@ function generate_kernel_recipients() cc="$(remove_blocked_recipients "$cc" "$blocked")" fi - if [[ -n "$to" ]]; then - printf '%s\n' "$to" > "${patch_cache}/to/${patch}" - printf '%s\n' "$to" >> "$cover_letter_to" - fi - - if [[ -n "$cc" ]]; then - printf '%s\n' "$cc" > "${patch_cache}/cc/${patch}" - printf '%s\n' "$cc" >> "$cover_letter_cc" - fi + printf '%s\n' "$to" > "${patch_cache}/to/${patch}" + printf '%s\n' "$to" >> "$cover_letter_to" + printf '%s\n' "$cc" > "${patch_cache}/cc/${patch}" + printf '%s\n' "$cc" >> "$cover_letter_cc" done to_list="$(sort -u "$cover_letter_to")" diff --git a/test.sh b/test.sh new file mode 100755 index 000000000..80a2e79b8 --- /dev/null +++ b/test.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash +set -euo pipefail + +PATTERN="${1:-[PATCH]}" # filtro, padrão "[PATCH]" +DEST="$HOME/Maildir/patches" +mkdir -p "$DEST" + +MUTTRC="$HOME/.muttrc" + +# função para extrair valor de .muttrc +get_muttrc_value() { + local key="$1" + grep -E "^set[[:space:]]+$key" "$MUTTRC" 2>/dev/null \ + | sed -E 's/set[[:space:]]+'"$key"'[[:space:]]*=[[:space:]]*"(.*)"/\1/' +} + +IMAP_USER=$(get_muttrc_value imap_user) +IMAP_PASS=$(get_muttrc_value imap_pass) +IMAP_FOLDER=$(get_muttrc_value folder) +SPOOLFILE=$(get_muttrc_value spoolfile) + +IMAP_SERVER=$(echo "$IMAP_FOLDER" | sed -E 's|imaps?://([^/]+)/.*|\1|; s|imaps?://([^/]+)|\1|') +MAILBOX="${SPOOLFILE#\+}" +MAILBOX="${MAILBOX:-INBOX}" + +echo "Conectando a $IMAP_SERVER/$MAILBOX para buscar mensagens com Subject =~ $PATTERN" + +# 1) SEARCH +UIDS=$(curl -s -u "$IMAP_USER:$IMAP_PASS" \ + --url "imaps://$IMAP_SERVER/$MAILBOX" \ + -X "UID SEARCH SUBJECT \"$PATTERN\"" \ + | tr -d '\r' | awk '{for(i=2;i<=NF;i++) print $i}') + +if [ -z "$UIDS" ]; then + echo "Nenhuma mensagem encontrada." + exit 0 +fi + +echo "UIDs encontrados: $UIDS" + +# 2) FETCH BODY[TEXT] (somente body) e salvar +for uid in $UIDS; do + out="$DEST/mail_body_$uid.txt" + echo "Baixando body UID $uid -> $out" + curl -s -u "$IMAP_USER:$IMAP_PASS" \ + --url "imaps://$IMAP_SERVER/$MAILBOX" \ + -X "UID FETCH $uid BODY[TEXT]" \ + | sed -n '/FETCH/,/)/{ /FETCH/d; /\)/d; p }' > "$out" +done + +echo "Concluído. Bodies salvos em $DEST" diff --git a/tests/unit/patch_track_test.sh b/tests/unit/patch_track_test.sh index 88f47d89e..9ef1714c1 100755 --- a/tests/unit/patch_track_test.sh +++ b/tests/unit/patch_track_test.sh @@ -188,16 +188,16 @@ function test_get_patch_info_by_commit_hash() { assertContains "$result" "PATCH_BY_HASH" } -function test_new_patch() { +function test_get_or_create_patch() { local result local ret local patch_id # Criar novo patch - result="$(new_patch 'PATCH_OK' '' 'authorx@email' 1 'hash777' 1)" + result="$(get_or_create_patch 'PATCH_OK' '' 'authorx@email' 1 'hash777' 1)" ret="$?" - assert_equals_helper 'Expected new_patch to succeed' "$LINENO" 0 "$ret" + assert_equals_helper 'Expected get_or_create_patch to succeed' "$LINENO" 0 "$ret" # Conferir que id retornado corresponde a um patch no banco patch_id="$(sqlite3 "${KW_DATA_DIR}/kw.db" "SELECT id FROM patch WHERE commit_hash='hash777';")" @@ -354,26 +354,26 @@ function test_insert_contribution_success() { assertEquals "Contribution should exist in DB" 1 "$db_count" } -function test_new_contribution_success() { - output="$(new_contribution "Contrib Test 2" "user2@example.com" 1)" +function test_get_or_create_contribution_success() { + output="$(get_or_create_contribution "Contrib Test 2" "user2@example.com" 1)" ret="$?" db_id="$(sqlite3 "${KW_DATA_DIR}/kw.db" "SELECT id FROM contribution WHERE title='Contrib Test 2';")" assert_equals_helper "Returned ID should match DB" "$LINENO" "$db_id" "$output" - assert_equals_helper "new_contribution_success" "$LINENO" "$ret" 0 + assert_equals_helper "get_or_create_contribution_success" "$LINENO" "$ret" 0 } -function test_check_contribution_existence_by_title_success() { - output="$(check_contribution_existence_by_title "TEST_CONTRIBUTION")" +function test_check_contribution_existence_by_unique_attributes_success() { + output="$(check_contribution_existence_by_unique_attributes "TEST_CONTRIBUTION" 'test@example.com')" ret="$?" - assert_equals_helper "check_contribution_existence_by_title should succeed" "$LINENO" 0 "$ret" + assert_equals_helper "check_contribution_existence_by_unique_attributes should succeed" "$LINENO" 0 "$ret" assert_equals_helper "Existence check should return 1" "$LINENO" 1 "$output" } -function test_get_contribution_info_by_title_success() { - output="$(get_contribution_info_by_title 'id' 'TEST_CONTRIBUTION')" +function test_get_contribution_info_by_unique_attributes_success() { + output="$(get_contribution_info_by_unique_attributes 'id' 'TEST_CONTRIBUTION' "test@example.com")" ret="$?" - assert_equals_helper "get_contribution_info_by_title should succeed" "$LINENO" 0 "$ret" + assert_equals_helper "get_contribution_info_by_unique_attributes should succeed" "$LINENO" 0 "$ret" assert_equals_helper "Output ID should not be null" "$LINENO" 1 "$output" } From d64b015ee2225b44157f3a7d7699313c770ec880 Mon Sep 17 00:00:00 2001 From: JGBSouza Date: Mon, 22 Dec 2025 22:03:36 -0300 Subject: [PATCH 08/33] fix: remove database unused attributes Signed-off-by: JGBSouza --- database/kwdb.sql | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) diff --git a/database/kwdb.sql b/database/kwdb.sql index 52cf07024..66cbb714f 100644 --- a/database/kwdb.sql +++ b/database/kwdb.sql @@ -50,15 +50,14 @@ CREATE TABLE IF NOT EXISTS "event" ( -- to the executed commands that are saved and the pomodoro sessions created CREATE TABLE IF NOT EXISTS "patch" ( "id" INTEGER, - "created_at" TEXT DEFAULT (datetime('now','localtime')), - "status" VARCHAR(50) DEFAULT ('SENT') NOT NULL, - "title" TEXT NOT NULL, + "title" TEXT NOT NULL, "author_email" TEXT, - "contribution_id" INTEGER NOT NULL, + "status" VARCHAR(50) DEFAULT ('SENT') NOT NULL, "commit_hash" TEXT, + "created_at" TEXT DEFAULT (datetime('now','localtime')), + "contribution_id" INTEGER NOT NULL, CHECK ("status" IN ('SENT', 'APPROVED', 'MERGED', 'REVIEWED', 'REJECTED')), PRIMARY KEY("id"), - FOREIGN KEY ("last_patch_id") REFERENCES "patch"("id") ON DELETE CASCADE, FOREIGN KEY ("contribution_id") REFERENCES "contribution"("id") ON DELETE CASCADE ); @@ -80,6 +79,7 @@ CREATE TABLE IF NOT EXISTS "submission" ( "id" INTEGER, "contribution_id" INTEGER NOT NULL, "send_by" TEXT NOT NULL, + "created_at" TEXT DEFAULT (datetime('now','localtime')), PRIMARY KEY("id"), FOREIGN KEY ("contribution_id") REFERENCES "contribution"("id") ON DELETE CASCADE ); @@ -87,8 +87,8 @@ CREATE TABLE IF NOT EXISTS "submission" ( CREATE TABLE IF NOT EXISTS "patch_submission" ( "patch_id" INTEGER NOT NULL, "submission_id" INTEGER NOT NULL, - "created_at" TEXT DEFAULT (datetime('now','localtime')), "message_id" TEXT NOT NULL, + "created_at" TEXT DEFAULT (datetime('now','localtime')), PRIMARY KEY ("patch_id", "submission_id", "message_id"), FOREIGN KEY ("submission_id") REFERENCES "submission"("id") ON DELETE CASCADE, FOREIGN KEY ("patch_id") REFERENCES "patch"("id") ON DELETE CASCADE @@ -96,13 +96,13 @@ CREATE TABLE IF NOT EXISTS "patch_submission" ( CREATE TABLE IF NOT EXISTS "contribution" ( "id" INTEGER, + "title" TEXT NOT NULL, "created_at" TEXT DEFAULT (datetime('now','localtime')), - "status" VARCHAR(50) DEFAULT ('SENT') NOT NULL, - "title" TEXT NOT NULL UNIQUE, "last_interaction_at" TEXT DEFAULT (datetime('now','localtime')), - "active" INTEGER NOT NULL CHECK ("active" IN (0, 1)) DEFAULT 1, + "status" VARCHAR(50) DEFAULT ('SENT') NOT NULL, "author_email" TEXT NOT NULL, "repository_id" INTEGER, + UNIQUE("title", "author_email") PRIMARY KEY("id"), FOREIGN KEY ("repository_id") REFERENCES "repository"("id") ON DELETE CASCADE ); @@ -111,18 +111,27 @@ CREATE TABLE IF NOT EXISTS "repository" ( "id" INTEGER, "created_at" TEXT DEFAULT (datetime('now','localtime')), "name" TEXT NOT NULL, - "origin_url" TEXT NOT NULL, - "branch_name" TEXT NOT NULL, + "origin_url" TEXT NOT NULL UNIQUE, PRIMARY KEY("id") - UNIQUE("origin_url", "branch_name") ); CREATE TABLE IF NOT EXISTS "repository_maintainer" ( "id" INTEGER, "repository_id" INTEGER NOT NULL, "contact_id" INTEGER NOT NULL, - PRIMARY KEY ("repository_id", "contact_id"), - FOREIGN KEY ("repository_id") REFERENCES "repository"("id") ON DELETE CASCADE + PRIMARY KEY("id"), + UNIQUE ("repository_id", "contact_id"), + FOREIGN KEY ("repository_id") REFERENCES "repository"("id") ON DELETE CASCADE, + FOREIGN KEY ("contact_id") REFERENCES "email_contact"("id") ON DELETE CASCADE +); + +-- Table containing the kw email contacts infos +CREATE TABLE IF NOT EXISTS "email_contact" ( + "id" INTEGER NOT NULL UNIQUE, + "name" VARCHAR(100) NOT NULL, + "email" VARCHAR(100) NOT NULL UNIQUE, + "created_at" TEXT DEFAULT (date('now', 'localtime')), + PRIMARY KEY("id") ); -- This is the relationship between an "event" that executes a given From 6cde43fa53a09084744f2ebf962f90b3aa94272f Mon Sep 17 00:00:00 2001 From: JGBSouza Date: Mon, 22 Dec 2025 22:04:46 -0300 Subject: [PATCH 09/33] feat: adiciona novas dependencias para o mutt Signed-off-by: JGBSouza --- documentation/dependencies/arch.dependencies | 3 +++ documentation/dependencies/debian.dependencies | 4 ++++ documentation/dependencies/fedora.dependencies | 4 ++++ 3 files changed, 11 insertions(+) diff --git a/documentation/dependencies/arch.dependencies b/documentation/dependencies/arch.dependencies index 78778528a..aa29348f8 100644 --- a/documentation/dependencies/arch.dependencies +++ b/documentation/dependencies/arch.dependencies @@ -30,3 +30,6 @@ procps-ng pciutils libnotify python-sphinx +mutt +xvfb +xterm \ No newline at end of file diff --git a/documentation/dependencies/debian.dependencies b/documentation/dependencies/debian.dependencies index c61a818e9..bf1ae5b65 100644 --- a/documentation/dependencies/debian.dependencies +++ b/documentation/dependencies/debian.dependencies @@ -32,3 +32,7 @@ procps pciutils libnotify-bin python3-sphinx +mutt +mutt +xvfb +xterm \ No newline at end of file diff --git a/documentation/dependencies/fedora.dependencies b/documentation/dependencies/fedora.dependencies index ea113c827..f9a6d43b6 100644 --- a/documentation/dependencies/fedora.dependencies +++ b/documentation/dependencies/fedora.dependencies @@ -28,3 +28,7 @@ procps-ng pciutils libnotify python3-sphinx +mutt +mutt +xvfb +xterm \ No newline at end of file From 0ba85a96910cb06cd4739d69c907268abee54d0c Mon Sep 17 00:00:00 2001 From: JGBSouza Date: Mon, 22 Dec 2025 22:07:12 -0300 Subject: [PATCH 10/33] feat: add new directories for mutt managment Signed-off-by: JGBSouza --- kw | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/kw b/kw index cda51574e..669c08d37 100755 --- a/kw +++ b/kw @@ -15,6 +15,9 @@ KW_DOC_DIR="${KW_SHARE_DIR}/doc" KW_MAN_DIR="${KW_SHARE_DIR}/man" KW_SOUND_DIR="${KW_SHARE_DIR}/sound" KW_DB_DIR="${KW_SHARE_DIR}/database" +KW_MUTT_DIR="${KW_SHARE_DIR}/mutt" +KW_MUTT_MESSAGES_DIR="${KW_MUTT_DIR}/messages" +KW_MUTT_HEADERS_DIR="${KW_MUTT_DIR}/headers" # Configuration files KW_ETC_DIR="${XDG_CONFIG_HOME:-"${HOME}/.config"}/${KWORKFLOW}" @@ -61,6 +64,9 @@ if [ -f "${KW_BASE_DIR}/src/lib/kwlib.sh" ]; then KW_ETC_DIR="${KW_BASE_DIR}/etc" KW_PLUGINS_DIR="${KW_LIB_DIR}/plugins" KW_SRC_LIB_DIR="${KW_LIB_DIR}/lib" + KW_MUTT_DIR="${KW_BASE_DIR}/mutt" + KW_MUTT_MESSAGES_DIR="${KW_MUTT_DIR}/messages" + KW_MUTT_HEADERS_DIR="${KW_MUTT_DIR}/messages" # KW_CACHE_DIR # use default cache folder fi ##END-REPO-MODE## @@ -85,6 +91,10 @@ if [[ "${KW_REPO_MODE}" == 'y' ]]; then repo_mode_msg+=$'\t'"KW_CACHE_DIR=${KW_CACHE_DIR}"$'\n' repo_mode_msg+=$'\t'"KW_DATA_DIR=${KW_DATA_DIR}"$'\n' repo_mode_msg+=$'\t'"KW_DB_DIR=${KW_DB_DIR}"$'\n' + repo_mode_msg+=$'\t'"KW_MUTT_DIR=${KW_MUTT_DIR}"$'\n' + repo_mode_msg+=$'\t'"KW_MUTT_DIR=${KW_MUTT_MESSAGES_DIR}"$'\n' + repo_mode_msg+=$'\t'"KW_MUTT_DIR=${KW_MUTT_HEADERS_DIR}"$'\n' + repo_mode_msg+=$'\t'"KW_MUTT_MAIL_DIR=${KW_MUTT_MAIL_DIR}"$'\n' repo_mode_msg+=$'\t'"KW_DOC_DIR=${KW_DOC_DIR}"$'\n' repo_mode_msg+=$'\t'"KW_ETC_DIR=${KW_ETC_DIR}"$'\n' repo_mode_msg+=$'\t'"KW_LIB_DIR=${KW_LIB_DIR}"$'\n' From eecde68f55d897365fd411a5207e96f8ee36d093 Mon Sep 17 00:00:00 2001 From: JGBSouza Date: Mon, 22 Dec 2025 22:12:25 -0300 Subject: [PATCH 11/33] feat: add setup configs for mutt Signed-off-by: JGBSouza --- setup.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/setup.sh b/setup.sh index 0b60db62b..7e59c66fa 100755 --- a/setup.sh +++ b/setup.sh @@ -31,6 +31,8 @@ declare -r etcdir="${XDG_CONFIG_HOME:-"$HOME/.config"}/$app_name" declare -r cachedir="${XDG_CACHE_HOME:-"$HOME/.cache/$app_name"}" declare -r tracingdir="${datadir}/tracing" declare -r dot_configs_dir="${datadir}/configs" +declare -r muttdir="${datadir}/mutt" +declare -r muttmaildir="${muttdir}/mail" ## ## Source code references @@ -557,6 +559,10 @@ function synchronize_files() mkdir -p "$datadir" mkdir -p "$datadir/statistics" mkdir -p "$datadir/configs" + + mkdir -p "$muttdir" + mkdir -p "$muttmaildir" + if [[ -x "${databasedir}/migrate_legacy_data_20220101.sh" ]]; then eval "${databasedir}/migrate_legacy_data_20220101.sh" else From 8d24b07c0d58201f4d2e3eaf44d9cc5058722a51 Mon Sep 17 00:00:00 2001 From: JGBSouza Date: Mon, 22 Dec 2025 22:17:21 -0300 Subject: [PATCH 12/33] feat: add init configs for mutt Signed-off-by: JGBSouza --- src/init.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/init.sh b/src/init.sh index dcc1eec5d..35ce1a026 100644 --- a/src/init.sh +++ b/src/init.sh @@ -24,6 +24,7 @@ function init_main() local mail_name='send_patch.config' local notification_name='notification.config' local remote_name='remote.config' + local patch_track_mutt_name='patch_track_mutt.config' local config_file_template local deploy_config_file_template local remote_file_template @@ -66,6 +67,7 @@ function init_main() mail_config_file_template="${KW_ETC_DIR}/send_patch.config" notification_config_file_template="${KW_ETC_DIR}/notification_template.config" remote_file_template="${KW_ETC_DIR}/remote.config" + patch_track_mutt_config_file_template="${KW_ETC_DIR}/patch_track_mutt.config" if [[ ! -f "$config_file_template" || ! -f "$build_config_file_template" ]]; then complain "No such: ${config_file_template}" @@ -80,6 +82,8 @@ function init_main() cmd_manager "$flag" "cp ${mail_config_file_template} ${PWD}/${KW_DIR}/${mail_name}" cmd_manager "$flag" "cp ${notification_config_file_template} ${PWD}/${KW_DIR}/${notification_name}" cmd_manager "$flag" "cp ${remote_file_template} ${PWD}/${KW_DIR}/${remote_name}" + cmd_manager "$flag" "cp ${patch_track_mutt_config_file_template} ${PWD}/${KW_DIR}/${patch_track_mutt_name}" + cmd_manager "$flag" "sed --in-place --expression \"s/USERKW/${USER}/g\" -e '/^#?.*/d' ${PWD}/${KW_DIR}/${vm_name}" cmd_manager "$flag" "sed --in-place --expression \"s,SOUNDPATH,${KW_SOUND_DIR},g\" -e '/^#?.*/d' ${PWD}/${KW_DIR}/${notification_name}" From cf92a9e87c69ce2310ae50617ee4702b857a4b85 Mon Sep 17 00:00:00 2001 From: JGBSouza Date: Mon, 22 Dec 2025 22:17:48 -0300 Subject: [PATCH 13/33] feat: add config loader to load mutt configs Signed-off-by: JGBSouza --- src/lib/kw_config_loader.sh | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/lib/kw_config_loader.sh b/src/lib/kw_config_loader.sh index 15341761c..75937d793 100644 --- a/src/lib/kw_config_loader.sh +++ b/src/lib/kw_config_loader.sh @@ -8,6 +8,8 @@ DEPLOY_CONFIG_FILENAME='deploy.config' VM_CONFIG_FILENAME='vm.config' SEND_PATCH_CONFIG_FILENAME='send_patch.config' SEND_PATCH_CONFIG_FILENAME='lore.config' +PATCH_TRACK_MUTT_CONFIG_FILENAME='patch_track_mutt.config' + KW_DIR='.kw' # Basic targets @@ -53,6 +55,11 @@ declare -gA lore_config declare -gA lore_config_global declare -gA lore_config_local +# Notification configuration +declare -gA patch_track_mutt_config +declare -gA patch_track_mutt_config_global +declare -gA patch_track_mutt_config_local + # Default target option from kworkflow.config declare -gA deploy_target_opt=(['local']=2 ['remote']=3) @@ -325,12 +332,16 @@ function load_configuration() 'lore') target_array='lore_config' ;; + 'patch_track_mutt') + target_array='patch_track_mutt_config' + ;; esac target_array_global="${target_array}_global" target_array_local="${target_array}_local" target_config_file="${target_config}.config" + parse_configuration "${KW_ETC_DIR}/${target_config_file}" "$target_array" "$target_array_global" # XDG_CONFIG_DIRS is a colon-separated list of directories for config @@ -399,6 +410,11 @@ load_lore_config() load_configuration 'lore' } +load_patch_track_mutt_config() +{ + load_configuration 'patch_track_mutt' +} + load_all_config() { load_notification_config @@ -408,4 +424,5 @@ load_all_config() load_send_patch_config load_lore_config load_vm_config + load_patch_track_mutt_config } From 4e23fe10075674c2805ffbafc9ef9a6052f6d459 Mon Sep 17 00:00:00 2001 From: JGBSouza Date: Mon, 22 Dec 2025 22:19:04 -0300 Subject: [PATCH 14/33] fix: fix select from and remove test prints Signed-off-by: JGBSouza --- src/lib/kw_db.sh | 97 ++++++++++++++++++++++++------------------------ 1 file changed, 48 insertions(+), 49 deletions(-) diff --git a/src/lib/kw_db.sh b/src/lib/kw_db.sh index 0941e25ef..34c59f926 100644 --- a/src/lib/kw_db.sh +++ b/src/lib/kw_db.sh @@ -95,7 +95,6 @@ function insert_into() [[ -n "$entries" && ! "$entries" =~ ^\(.*\)$ ]] && entries="($entries)" cmd="sqlite3 -init "${KW_DB_DIR}/pre_cmd.sql" \"${db_path}\" -batch \"INSERT INTO ${table} ${entries} VALUES ${values};\"" - echo "$cmd" > /dev/tty cmd_manager "$flag" "$cmd" } @@ -209,7 +208,7 @@ function select_from() local flag=${7:-'SILENT'} local db="${8:-"$DB_NAME"}" local db_folder="${9:-"$KW_DATA_DIR"}" - local where_clause + local where_clause='' local db_path local query @@ -225,12 +224,13 @@ function select_from() return 61 # ENODATA fi - if [[ -n "$_condition_array" ]]; then + query="SELECT ${columns} FROM ${table} ;" + + if [[ -n "$_condition_array" && ${#_condition_array[@]} -gt 0 ]]; then where_clause="$(generate_where_clause "$_condition_array")" + query="${query::-2} ${where_clause} ;" fi - query="SELECT ${columns} FROM ${table} ${where_clause} ;" - if [[ -n "${order_by}" ]]; then query="${query::-2} ORDER BY ${order_by} ;" fi @@ -240,7 +240,6 @@ function select_from() fi cmd="sqlite3 -init "${KW_DB_DIR}/pre_cmd.sql" -cmd \"${pre_cmd}\" \"${db_path}\" -batch \"${query}\"" - echo "$cmd" > /dev/tty cmd_manager "$flag" "$cmd" } @@ -399,52 +398,52 @@ function format_values_db() function get_database_table_info() { - local _database_table_name="$1" - local _entity_infos="$2" - local -n _table_info_condition_array="$3" - local _order_by="${4:-}" - local _limit="${5:-}" - - sql_operation_result="$(select_from "$_database_table_name" "$_entity_infos" '' '_table_info_condition_array' "$_order_by" "$_limit")" - ret="$?" - - if [[ "$ret" -eq 2 || "$ret" -eq 61 ]]; then - complain "$sql_operation_result" - return "$ret" # EINVAL - elif [[ "$ret" -ne 0 ]]; then - complain "($LINENO): Error while trying to get ${_database_table_name} info from the database with the command:"$'\n'"${sql_operation_result}" - return "$ret" # EINVAL - fi + local _database_table_name="$1" + local _entity_infos="$2" + local -n _table_info_condition_array="$3" + local _order_by="${4:-}" + local _limit="${5:-}" + + sql_operation_result="$(select_from "$_database_table_name" "$_entity_infos" '' '_table_info_condition_array' "$_order_by" "$_limit")" + ret="$?" + + if [[ "$ret" -eq 2 || "$ret" -eq 61 ]]; then + complain "$sql_operation_result" + return "$ret" # EINVAL + elif [[ "$ret" -ne 0 ]]; then + complain "($LINENO): Error while trying to get ${_database_table_name} info from the database with the command:"$'\n'"${sql_operation_result}" + return "$ret" # EINVAL + fi - printf '%s\n' "$sql_operation_result" - return 0 + printf '%s\n' "$sql_operation_result" + return 0 } function check_existence() { - local _database_table_name="$1" - local -n _check_existence_condition_array="$2" - local sql_operation_result - local ret - - sql_operation_result="$(get_database_table_info "$_database_table_name" 'COUNT(*)' '_check_existence_condition_array')" - ret="$?" - - if [[ "$ret" -ne 0 ]]; then - complain "$sql_operation_result" - return "$ret" # EINVAL - fi - - if [[ -z "$sql_operation_result" ]]; then - complain "Error while trying to check entity existence in ${_database_table_name}" - return "$ret" # EINVAL - fi - - if [[ "$sql_operation_result" -eq 0 ]]; then - printf '%s\n' 0 - else - printf '%s\n' 1 - fi + local _database_table_name="$1" + local -n _check_existence_condition_array="$2" + local sql_operation_result + local ret + + sql_operation_result="$(get_database_table_info "$_database_table_name" 'COUNT(*)' '_check_existence_condition_array')" + ret="$?" - return 0 -} \ No newline at end of file + if [[ "$ret" -ne 0 ]]; then + complain "$sql_operation_result" + return "$ret" # EINVAL + fi + + if [[ -z "$sql_operation_result" ]]; then + complain "Error while trying to check entity existence in ${_database_table_name}" + return "$ret" # EINVAL + fi + + if [[ "$sql_operation_result" -eq 0 ]]; then + printf '%s\n' 0 + else + printf '%s\n' 1 + fi + + return 0 +} From 527977b85677ebd3d102cc760c7e9b96ecaf5e9e Mon Sep 17 00:00:00 2001 From: JGBSouza Date: Mon, 22 Dec 2025 22:19:26 -0300 Subject: [PATCH 15/33] fix: flex string verification and add new function to check for valid database string Signed-off-by: JGBSouza --- src/lib/kw_string.sh | 81 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 79 insertions(+), 2 deletions(-) diff --git a/src/lib/kw_string.sh b/src/lib/kw_string.sh index 7380a4128..dbf0208ea 100644 --- a/src/lib/kw_string.sh +++ b/src/lib/kw_string.sh @@ -1,3 +1,5 @@ +declare -gr email_regex='[A-Za-z0-9_\.-]+@[A-Za-z0-9_-]+(\.[A-Za-z0-9]+)+' + # @@ Expect a string # # Return: @@ -217,7 +219,7 @@ function concatenate_with_commas() } # This function check if a string has some special character associated with -# it. By special character, we refer to: !, @, #, $, %, ^, &, (, ), and +. +# it. By special character, we refer to: !, #, $, %, ^, &, (, ), and +. # # @str: Target string # @@ -227,7 +229,7 @@ function str_has_special_characters() { local str="$*" - [[ "$str" == *['!'@#\$%^\&*\(\)+]* ]] && return 0 + [[ "$str" == *['!'#\$%^\&*\(\)+]* ]] && return 0 return 1 # EPERM } @@ -302,3 +304,78 @@ function string_to_unix_filename() printf '%s' "$filename" } + +# Generate random string with letters and numbers using urandom +# @size: size of the random string +# +# Return: +# Returns a random string with the given size +function get_random_string() +{ + local size="${1:-5}" + local random_string="" + + random_string="$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | head -c "$size")" + + # Usa printf para imprimir a string sem uma nova linha final implícita + printf '%s' "$random_string" +} + +# This function validate a string name for db inputs +# +# @string: the group name that will be checked +# +# Return: +# returns 0 if successful, 61 if group name is empty, +# 75 if size of the group name is over 50 characters +# and 22 if group name has invalid characters as: +# [!, @, #, $, %, ^, &, (, ), (' ), (" ) and +]. +function check_valid_db_string() +{ + local string="$1" + local max_length="$2" + local string_length + local has_special_character + local ret + + if [[ -z "$string" ]]; then + complain 'The group name is empty' + return 61 # ENODATA + fi + + string_length="$(str_length "$string")" + + if [[ -n "$max_length" && "$string_length" -ge "$max_length" ]]; then + complain "The string must be less than ${max_length} characters" + return 75 # OVERFLOW + fi + + str_has_special_characters "$string" + + if [[ "$?" -eq 0 ]]; then + complain 'The string must not contain special characters' + return 22 #EINVAL + fi + + return 0 +} + +# This function validates the encryption. If the passed encryption is not valid +# this will warn the user and clear the option. +# +# @option: The option to determine if it should be an email +# @value: The value being passed +# +# Return: +# Returns 0 if valid; 22 if invalid +function validate_email() +{ + local value="$1" + + if [[ ! "$value" =~ ^${email_regex}$ ]]; then + complain "Invalid email: $value" + return 22 #EINVAL + fi + + return 0 +} From 849a4764282a66a8299802f5990bccb6a0a7f744 Mon Sep 17 00:00:00 2001 From: JGBSouza Date: Mon, 22 Dec 2025 22:21:19 -0300 Subject: [PATCH 16/33] feat: add new function ask with no default to kwio Signed-off-by: JGBSouza --- src/lib/kwio.sh | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/lib/kwio.sh b/src/lib/kwio.sh index c05f1043f..90a75e859 100644 --- a/src/lib/kwio.sh +++ b/src/lib/kwio.sh @@ -196,6 +196,37 @@ function ask_with_default() printf '%s\n' "$response" } +# Asks for user input +# +# @message A string with the message to be displayed to the user. +# @default_option String with default answer to be used if no input is given. +# @flag String to mark special conditions in this function. To activate the test +# mode, set this to 'TEST_MODE'. +# +# Return: +# The user answer, guaranteed not to be empty. +# Note: this function does not verify the given answer. You have to handle this +# somewhere else. +function ask_without_default() +{ + local message="$1" + local flag="$2" + local value + + message+=': ' + + [[ "$flag" == 'TEST_MODE' ]] && printf '%s\n' "$message" + + read -r -p "$message" response + + if [[ "$?" -ne 0 || -z "$response" ]]; then + printf '%s\n' "$message" + return + fi + + printf '%s\n' "$response" +} + # Load text from a file into a dictionary. The file that will be read must have # a key before a text body to name that particular text as part of the key, for # example: From c9403d457a145f57056895a99a4f8a9f7e9c73c2 Mon Sep 17 00:00:00 2001 From: JGBSouza Date: Mon, 22 Dec 2025 22:22:34 -0300 Subject: [PATCH 17/33] fix: fix contribution_uutils functions Signed-off-by: JGBSouza --- .../patch_track_utils/contribution_utils.sh | 295 +++++++++--------- 1 file changed, 154 insertions(+), 141 deletions(-) diff --git a/src/lib/patch_track_utils/contribution_utils.sh b/src/lib/patch_track_utils/contribution_utils.sh index 9e281b687..092644746 100644 --- a/src/lib/patch_track_utils/contribution_utils.sh +++ b/src/lib/patch_track_utils/contribution_utils.sh @@ -1,174 +1,187 @@ include "${KW_LIB_DIR}/lib/kw_db.sh" declare -gr DATABASE_CONTRIBUTION_TABLE='contribution' -declare -Ag condition_array +declare -gA condition_array +declare -gA updates_array function insert_contribution() { - local _contribution_title="$1" - local _author_email="$2" - local _repository_id="$3" - - echo "começou insert" > /dev/tty - - # Checagem de valores nulos - if [[ -z "$_contribution_title" || -z "$_author_email" ]]; then - complain "($LINENO): missing mandatory field(s) for insert_contribution" - return 22 # EINVAL - fi - - columns='"title", "author_email"' - values="'${_contribution_title}', '${_author_email}'" - - if [[ -n "${_repository_id}" ]]; then - columns="$columns, \"repository_id\"" - values="$values, '${_repository_id}'" - fi - - sql_operation_result=$(insert_into "$DATABASE_CONTRIBUTION_TABLE" \ - "($columns)" \ - "($values)") - ret="$?" - - if [[ "$ret" -eq 2 || "$ret" -eq 61 ]]; then - complain "$sql_operation_result" - return 22 # EINVAL - elif [[ "$ret" -ne 0 ]]; then - complain "($LINENO):" $'Error while trying to insert new contribution:\n'"$sql_operation_result" - return 22 # EINVAL - fi - - echo "terminou insert" > /dev/tty - - return 0 + local _contribution_title="$1" + local _author_email="$2" + local _repository_id="$3" + + # Checagem de valores nulos + if [[ -z "$_contribution_title" || -z "$_author_email" ]]; then + complain "($LINENO): missing mandatory field(s) for insert_contribution" + return 22 # EINVAL + fi + + columns='"title", "author_email"' + values="'${_contribution_title}', '${_author_email}'" + + if [[ -n "${_repository_id}" ]]; then + columns="$columns, \"repository_id\"" + values="$values, '${_repository_id}'" + fi + + sql_operation_result=$(insert_into "$DATABASE_CONTRIBUTION_TABLE" \ + "($columns)" \ + "($values)") + ret="$?" + + if [[ "$ret" -eq 2 || "$ret" -eq 61 ]]; then + complain "$sql_operation_result" + return 22 # EINVAL + elif [[ "$ret" -ne 0 ]]; then + complain "($LINENO):" $'Error while trying to insert new contribution:\n'"$sql_operation_result" + return 22 # EINVAL + fi + + return 0 } function get_or_create_contribution() { - local _contribution_title="$1" - local _author_email="$2" - local _repository_id="$3" - local sql_operation_result - local insert_contribution_result - local get_contribution_id_resultget_or_create_contribution - - # Checagem de valores nulos - if [[ -z "$_contribution_title" || -z "$_author_email" ]]; then - complain "($LINENO): missing mandatory field(s) for new_contribution" - return 22 # EINVAL - fi - - echo "check existence ${_contribution_title} ee ${_author_email}" > /dev/tty - - patch_existent_result="$(check_contribution_existence_by_unique_attributes "$_contribution_title" "$_author_email")" + local _contribution_title="$1" + local _author_email="$2" + local _repository_id="$3" + local sql_operation_result + local insert_contribution_result + local get_contribution_id_resultget_or_create_contribution + + # Checagem de valores nulos + if [[ -z "$_contribution_title" || -z "$_author_email" ]]; then + complain "($LINENO): missing mandatory field(s) for new_contribution" + return 22 # EINVAL + fi + + patch_existent_result="$(check_contribution_existence_by_unique_attributes "$_contribution_title" "$_author_email")" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$patch_existent_result" + return "$ret" + fi + + if [[ "$patch_existent_result" -eq 0 ]]; then + + insert_contribution_result=$(insert_contribution "$_contribution_title" "$_author_email" "$_repository_id") ret="$?" if [[ "$ret" -ne 0 ]]; then - complain "$patch_existent_result" - return "$ret" + complain "$insert_contribution_result" + return "$ret" fi + fi - if [[ "$patch_existent_result" -eq 0 ]]; then - echo "insert" > /dev/tty - - insert_contribution_result=$(insert_contribution "$_contribution_title" "$_author_email" "$_repository_id") - ret="$?" - - if [[ "$ret" -ne 0 ]]; then - complain "$insert_contribution_result" - return "$ret" - fi - fi + get_contribution_id_result=$(get_contribution_info_by_unique_attributes 'id' "$_contribution_title" "$_author_email") + ret="$?" - echo "get id" > /dev/tty - get_contribution_id_result=$(get_contribution_info_by_unique_attributes 'id' "$_contribution_title" "$_author_email") - ret="$?" + if [[ "$ret" -ne 0 ]]; then + complain "$get_contribution_id_result" + return "$ret" + fi - if [[ "$ret" -ne 0 ]]; then - complain "$get_contribution_id_result" - return "$ret" - fi - - printf '%s\n' "$get_contribution_id_result" - return 0 + printf '%s\n' "$get_contribution_id_result" + return 0 } function check_contribution_existence_by_unique_attributes() { - local _contribution_title="$1" - local _author_email="$2" - local sql_operation_result - local ret - - # Checagem de valor nulo - if [[ -z "$_contribution_title" || -z "$_author_email" ]]; then - complain "($LINENO): empty contribution infos for check_contribution_existence_by_unique_attributes" - return 22 # EINVAL - fi - - condition_array=(['title']="${_contribution_title}") - condition_array=(['author_email']="${_author_email}") - - sql_operation_result="$(check_existence "$DATABASE_CONTRIBUTION_TABLE" 'condition_array')" - ret="$?" - - if [[ "$ret" -ne 0 ]]; then - complain "$sql_operation_result" - return "$ret" - fi - - if [[ -z "$sql_operation_result" ]]; then - complain "($LINENO): check existence returned null" - return 61 # ENODATA - fi - - printf '%s\n' "$sql_operation_result" - return 0 + local _contribution_title="$1" + local _author_email="$2" + local sql_operation_result + local ret + + # Checagem de valor nulo + if [[ -z "$_contribution_title" || -z "$_author_email" ]]; then + complain "($LINENO): empty contribution infos for check_contribution_existence_by_unique_attributes" + return 22 # EINVAL + fi + + condition_array=(['title']="${_contribution_title}" + ['author_email']="${_author_email}") + + sql_operation_result="$(check_existence "$DATABASE_CONTRIBUTION_TABLE" 'condition_array')" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$sql_operation_result" + return "$ret" + fi + + if [[ -z "$sql_operation_result" ]]; then + complain "($LINENO): check existence returned null" + return 61 # ENODATA + fi + + printf '%s\n' "$sql_operation_result" + return 0 } function get_contribution_info_by_unique_attributes() { - local _contribution_infos="$1" - local _contribution_title="$2" - local _author_email="$3" - local sql_operation_result - local ret - - if [[ -z "$_contribution_title" || -z "$_author_email" ]]; then - complain "($LINENO): empty contribution title for get_contribution_info_by_title" - return 22 # EINVAL - fi - - condition_array=(['title']="${_contribution_title}") - condition_array=(['author_email']="${_author_email}") - - sql_operation_result="$(get_contribution_info "$_contribution_infos" 'condition_array')" - ret="$?" - - if [[ "$ret" -ne 0 ]]; then - complain "$sql_operation_result" - return "$ret" - fi - - printf '%s\n' "$sql_operation_result" - return 0 + local _contribution_infos="$1" + local _contribution_title="$2" + local _author_email="$3" + local sql_operation_result + local ret + + if [[ -z "$_contribution_title" || -z "$_author_email" ]]; then + complain "($LINENO): empty contribution title for get_contribution_info_by_title" + return 22 # EINVAL + fi + + condition_array=(['title']="${_contribution_title}" + ['author_email']="${_author_email}") + + sql_operation_result="$(get_contribution_info "$_contribution_infos" 'condition_array')" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$sql_operation_result" + return "$ret" + fi + + printf '%s\n' "$sql_operation_result" + return 0 } function get_contribution_info() { - local _contribution_infos="$1" - local -n _contrib_info_condition_array="$2" - local sql_operation_result - local ret + local _contribution_infos="$1" + local -n _contrib_info_condition_array="$2" + local _order_by="${3:-}" + local sql_operation_result + local ret + + sql_operation_result="$(get_database_table_info "$DATABASE_CONTRIBUTION_TABLE" "$_contribution_infos" '_contrib_info_condition_array' "$_order_by")" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$sql_operation_result" + return 22 # EINVAL + fi + + printf '%s\n' "$sql_operation_result" + return 0 +} - sql_operation_result="$(get_database_table_info "$DATABASE_CONTRIBUTION_TABLE" "$_contribution_infos" '_contrib_info_condition_array')" - ret="$?" +function set_contribution_repository() +{ + local contribution_id="$1" + local repository_id="$2" - if [[ "$ret" -ne 0 ]]; then - complain "$sql_operation_result" - return 22 # EINVAL - fi + updates_array=(['repository_id']="$repository_id") + condition_array=(['id']="$contribution_id") + + update_into_result=$(update_into "$DATABASE_CONTRIBUTION_TABLE" 'updates_array' '' 'condition_array') + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$update_into_result" + return 22 # EINVAL + fi - printf '%s\n' "$sql_operation_result" - return 0 + return 0 } From 033950be4e79e642cf437332c6325517c6811367 Mon Sep 17 00:00:00 2001 From: JGBSouza Date: Mon, 22 Dec 2025 22:22:52 -0300 Subject: [PATCH 18/33] fix: fix patch_utils functions Signed-off-by: JGBSouza --- src/lib/patch_track_utils/patch_utils.sh | 484 +++++++++++------------ 1 file changed, 241 insertions(+), 243 deletions(-) diff --git a/src/lib/patch_track_utils/patch_utils.sh b/src/lib/patch_track_utils/patch_utils.sh index c24fcc446..e4ea54e54 100644 --- a/src/lib/patch_track_utils/patch_utils.sh +++ b/src/lib/patch_track_utils/patch_utils.sh @@ -5,280 +5,278 @@ declare -Ag condition_array function insert_patch() { - local _patch_title="$1" - local _last_patch_id="$2" - local _patch_author="$3" - local _contribution_id="$4" - local _commit_hash="$5" - local _patch_version="$6" - local get_last_patch_version_result - local sql_operation_result - local ret - - if [[ -z "$_patch_title" || -z "$_patch_author" || -z "$_contribution_id" || -z "$_commit_hash" ]]; then - complain "($LINENO): missing mandatory field for a new patch" - return 22 # EINVAL - fi - - columns='"title", "author", "contribution_id", "version", "commit_hash"' - values="'${_patch_title}', '${_patch_author}', '${_contribution_id}', '${_patch_version}', '${_commit_hash}'" - - if [[ -n "${_last_patch_id}" ]]; then - columns="$columns, \"last_patch_id\"" - values="$values, '${_last_patch_id}'" - fi - - sql_operation_result=$(insert_into "$DATABASE_PATCH_TABLE" \ - "($columns)" \ - "($values)") - ret="$?" - - if [[ "$ret" -eq 2 || "$ret" -eq 61 ]]; then - complain "$sql_operation_result" - return 22 # EINVAL - elif [[ "$ret" -ne 0 ]]; then - complain "($LINENO):" $'Error while trying to insert new patch:\n'"$sql_operation_result" - return 22 # EINVAL - fi - - return 0 -} - -function replace_patch() -{ - local _patch_title="$1" - local _last_patch_id="$2" - local _patch_author="$3" - local _contribution_id="$4" - local _submission_id="$5" - local _commit_hash="$6" - - if [[ -z "$_last_patch_id" || -z "$_patch_title" || -z "$_commit_hash" ]]; then - complain "($LINENO): missing mandatory field for a new patch" - return 22 # EINVAL - fi - - - get_patch_version_result="$(decide_patch_version "$_last_patch_id")" - ret="$?" - - if [[ "$ret" -ne 0 ]]; then - complain "$get_patch_version_result" - return 22 # EINVAL - fi - - new_patch_result="$(new_patch "$_patch_title" "$_last_patch_id" "$_patch_author" "$_contribution_id"\ - "$_submission_id" "$_commit_hash" "$get_patch_version_result")" - ret="$?" - - if [[ "$ret" -ne 0 ]]; then - complain "$new_patch_result" - return 22 # EINVAL - fi - - printf "$new_patch_result" - return 0 + local _patch_title="$1" + local _patch_author="$2" + local _contribution_id="$3" + local _commit_hash="$4" + local sql_operation_result + local ret + + if [[ -z "$_patch_title" || -z "$_patch_author" || -z "$_contribution_id" || -z "$_commit_hash" ]]; then + complain "($LINENO): missing mandatory field for a new patch" + return 22 # EINVAL + fi + + columns='"title", "author_email", "contribution_id", "commit_hash"' + values="'${_patch_title}', '${_patch_author}', '${_contribution_id}', '${_commit_hash}'" + + sql_operation_result=$(insert_into "$DATABASE_PATCH_TABLE" \ + "($columns)" \ + "($values)") + ret="$?" + + if [[ "$ret" -eq 2 || "$ret" -eq 61 ]]; then + complain "$sql_operation_result" + return 22 # EINVAL + elif [[ "$ret" -ne 0 ]]; then + complain "($LINENO):" $'Error while trying to insert new patch:\n'"$sql_operation_result" + return 22 # EINVAL + fi + + return 0 } function get_or_create_patch() { - local _patch_title="$1" - local _last_patch_id="$2" - local _patch_author="$3" - local _contribution_id="$4" - local _commit_hash="$5" - local _patch_version="$6" - local ret - local patch_existent_result - local insert_contribution_result - local get_contribution_id_result - - if [[ -z "$_patch_title" || -z "$_patch_author" || -z "$_contribution_id" ]]; then - complain "($LINENO): missing mandatory field for new_patch" - return 22 # EINVAL - fi - - if [[ -z "$_patch_version" ]]; then - _patch_version=1 - fi - - patch_existent_result="$(check_patch_existence_by_unique_attributes "$_patch_title" "$_patch_author" "$_contribution_id" "$_commit_hash")" + local _patch_title="$1" + local _patch_author="$2" + local _contribution_id="$3" + local _commit_hash="$4" + local ret + local patch_existent_result + local insert_contribution_result + local get_contribution_id_result + + if [[ -z "$_patch_title" || -z "$_patch_author" || -z "$_contribution_id" ]]; then + complain "($LINENO): missing mandatory field for new_patch title ${_patch_title}, author: ${_patch_author}, contribution_id ${_contribution_id}" + return 22 # EINVAL + fi + + patch_existent_result="$(check_patch_existence_by_unique_attributes "$_patch_title" "$_patch_author" "$_contribution_id" "$_commit_hash")" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$patch_existent_result" + return "$ret" # EINVAL + fi + + if [[ -z "$patch_existent_result" ]]; then + complain "($LINENO): unexpected empty result from check_patch_existence_by_unique_attributes" + return 22 # EINVAL + fi + + if [[ "$patch_existent_result" -eq 0 ]]; then + insert_contribution_result=$(insert_patch "$_patch_title" "$_patch_author" "$_contribution_id" \ + "$_commit_hash") ret="$?" if [[ "$ret" -ne 0 ]]; then - complain "$patch_existent_result" - return "$ret" # EINVAL + complain "$insert_contribution_result" + return "$ret" # EINVAL fi + fi - if [[ -z "$patch_existent_result" ]]; then - complain "($LINENO): unexpected empty result from check_patch_existence_by_unique_attributes" - return 22 # EINVAL - fi + get_contribution_id_result=$(get_patch_infos_by_unique_attributes 'id' "$_patch_title" \ + "$_patch_author" "$_contribution_id" "$_commit_hash") + ret="$?" - if [[ "$patch_existent_result" -eq 0 ]]; then - insert_contribution_result=$(insert_patch "$_patch_title" "$_last_patch_id" "$_patch_author" "$_contribution_id"\ - "$_commit_hash" "$_patch_version") - ret="$?" - - if [[ "$ret" -ne 0 ]]; then - complain "$insert_contribution_result" - return "$ret" # EINVAL - fi - fi + if [[ "$ret" -ne 0 ]]; then + complain "$get_contribution_id_result" + return "$ret" # EINVAL + fi - get_contribution_id_result=$(get_patch_info_by_commit_hash 'id' "$_commit_hash") - ret="$?" - - if [[ "$ret" -ne 0 ]]; then - complain "$get_contribution_id_result" - return "$ret" # EINVAL - fi - - printf '%s\n' "$get_contribution_id_result" - return 0 + printf '%s\n' "$get_contribution_id_result" + return 0 } function get_patch_info_by_id() { - local _patch_infos="$1" - local _patch_id="$2" - local sql_operation_result - local ret - - if [[ -z "$_patch_id" ]]; then - complain "($LINENO): empty patch id" - return 22 # EINVAL - fi - - - condition_array=(['id']="${_patch_id}") - sql_operation_result="$(get_patch_info "$_patch_infos" 'condition_array')" - ret="$?" - - if [[ "$ret" -ne 0 ]]; then - complain "$sql_operation_result" - return "$ret" # EINVAL - fi - - if [[ -z "$sql_operation_result" ]]; then - complain "($LINENO): no result found for patch id $_patch_id" - return 61 # ENODATA - fi - - printf '%s\n' "$sql_operation_result" - return 0 + local _patch_infos="$1" + local _patch_id="$2" + local sql_operation_result + local ret + + if [[ -z "$_patch_id" ]]; then + complain "($LINENO): empty patch id" + return 22 # EINVAL + fi + + condition_array=(['id']="${_patch_id}") + sql_operation_result="$(get_patch_info "$_patch_infos" 'condition_array')" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$sql_operation_result" + return "$ret" # EINVAL + fi + + if [[ -z "$sql_operation_result" ]]; then + complain "($LINENO): no result found for patch id $_patch_id" + return 61 # ENODATA + fi + + printf '%s\n' "$sql_operation_result" + return 0 } function get_patch_info_by_commit_hash() { - local _patch_infos="$1" - local _commit_hash="$2" - local sql_operation_result - local ret - - if [[ -z "$_commit_hash" ]]; then - complain "($LINENO): empty commit hash" - return 22 # EINVAL - fi - - condition_array=(['commit_hash']="${_commit_hash}") - sql_operation_result="$(get_patch_info "$_patch_infos" 'condition_array')" - ret="$?" - - if [[ "$ret" -ne 0 ]]; then - complain "$sql_operation_result" - return "$ret" # EINVAL - fi - - if [[ -z "$sql_operation_result" ]]; then - complain "($LINENO): no result found for commit hash $_commit_hash" - return 61 # ENODATA - fi - - printf '%s\n' "$sql_operation_result" - return 0 + local _patch_infos="$1" + local _commit_hash="$2" + local sql_operation_result + local ret + + if [[ -z "$_commit_hash" ]]; then + complain "($LINENO): empty commit hash" + return 22 # EINVAL + fi + + condition_array=(['commit_hash']="${_commit_hash}") + sql_operation_result="$(get_patch_info "$_patch_infos" 'condition_array')" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$sql_operation_result" + return "$ret" # EINVAL + fi + + if [[ -z "$sql_operation_result" ]]; then + complain "($LINENO): no result found for commit hash $_commit_hash" + return 61 # ENODATA + fi + + printf '%s\n' "$sql_operation_result" + return 0 } function get_patch_info() { - local _patch_infos="$1" - local -n _patch_condition_array="$2" + local _patch_infos="$1" + local -n _patch_condition_array="$2" - sql_operation_result="$(get_database_table_info "$DATABASE_PATCH_TABLE" "$_patch_infos" '_patch_condition_array')" - ret="$?" + sql_operation_result="$(get_database_table_info "$DATABASE_PATCH_TABLE" "$_patch_infos" '_patch_condition_array')" + ret="$?" - if [[ "$ret" -ne 0 ]]; then - complain "$sql_operation_result" - return "$ret" # EINVAL - fi + if [[ "$ret" -ne 0 ]]; then + complain "$sql_operation_result" + return "$ret" # EINVAL + fi - printf '%s\n' "$sql_operation_result" - return 0 + printf '%s\n' "$sql_operation_result" + return 0 } function check_patch_existence_by_unique_attributes() { - local _patch_title="$1" - local _patch_author="$2" - local _contribution_id="$3" - local _commit_hash="$4" - - if [[ -z "$_commit_hash" ]]; then - printf '%s\n' 0 - return 0 # Not safe to check if the patch is the same if there is no commit hash - fi - - if [[ -z "$_patch_title" || -z "$_patch_author" || -z "$_contribution_id" ]]; then - complain "($LINENO): no attributes provided for check_patch_existence_by_unique_attributes" - return 22 # EINVAL - fi - - condition_array=(['title']="${_patch_title}" - ['author']="${_patch_author}" - ['contribution_id']="${_contribution_id}" - ['commit_hash']="${_commit_hash}") - - - sql_operation_result="$(check_existence "$DATABASE_PATCH_TABLE" 'condition_array')" - ret="$?" - - if [[ "$ret" -ne 0 ]]; then - complain "$sql_operation_result" - return "$ret" # EINVAL - fi - - if [[ -z "$sql_operation_result" ]]; then - complain "($LINENO): check existence returned null" - return 61 # ENODATA - fi - - printf '%s\n' "$sql_operation_result" - return 0 + local _patch_title="$1" + local _patch_author="$2" + local _contribution_id="$3" + local _commit_hash="$4" + + if [[ -z "$_commit_hash" ]]; then + printf '%s\n' 0 + return 0 # Not safe to check if the patch is the same if there is no commit hash + fi + + if [[ -z "$_patch_title" || -z "$_patch_author" || -z "$_contribution_id" ]]; then + complain "($LINENO): no attributes provided for check_patch_existence_by_unique_attributes" + return 22 # EINVAL + fi + + condition_array=(['title']="${_patch_title}" + ['author_email']="${_patch_author}" + ['contribution_id']="${_contribution_id}" + ['commit_hash']="${_commit_hash}") + + sql_operation_result="$(check_existence "$DATABASE_PATCH_TABLE" 'condition_array')" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$sql_operation_result" + return "$ret" # EINVAL + fi + + if [[ -z "$sql_operation_result" ]]; then + complain "($LINENO): check existence returned null" + return 61 # ENODATA + fi + + printf '%s\n' "$sql_operation_result" + return 0 } -function decide_patch_version() +function get_patch_infos_by_unique_attributes() { - local _last_patch_id="$1" - local get_last_patch_version_result - local ret - - if [[ -z "$_last_patch_id" ]]; then - return 22 # EINVAL - fi - - get_last_patch_version_result="$(get_patch_info_by_id 'version' "$_last_patch_id")" - ret="$?" - - if [[ "$ret" -ne 0 ]]; then - complain "$get_last_patch_version_result" - return 22 # EINVAL - fi - - if [[ -z "$get_last_patch_version_result" ]]; then - complain "($LINENO): empty version returned for patch id $_last_patch_id" - return 61 # ENODATA - fi + local _patch_infos="$1" + local _patch_title="$2" + local _patch_author="$3" + local _contribution_id="$4" + local _commit_hash="$5" + + if [[ -z "$_commit_hash" ]]; then + printf '%s\n' 0 + return 0 # Not safe to check if the patch is the same if there is no commit hash + fi + + if [[ -z "$_patch_title" || -z "$_patch_author" || -z "$_contribution_id" ]]; then + complain "($LINENO): no attributes provided for check_patch_existence_by_unique_attributes" + return 22 # EINVAL + fi + + condition_array=(['title']="${_patch_title}" + ['author_email']="${_patch_author}" + ['contribution_id']="${_contribution_id}" + ['commit_hash']="${_commit_hash}") + + sql_operation_result="$(get_patch_info "$_patch_infos" 'condition_array')" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$sql_operation_result" + return "$ret" # EINVAL + fi + + if [[ -z "$sql_operation_result" ]]; then + complain "($LINENO): no result found for patch title _${_patch_title}, patch author ${_patch_author},\ + contribution id ${_contribution_id} commit hash ${_commit_hash}" + return 61 # ENODATA + fi + + printf '%s\n' "$sql_operation_result" + return 0 +} - printf '%d\n' $((get_last_patch_version_result + 1)) - return 0 -} \ No newline at end of file +# Função: formatar_data_patch +# Descrição: Recebe uma string de data no formato RFC 2822 (como em cabeçalhos de patch) +# e a formata para o padrão YYYY-MM-DD HH:MM:SS, seguro para a maioria +# dos campos DATETIME/TIMESTAMP de bancos de dados. +# +# @date_string: A string de data bruta (Ex: "Wed, 8 Oct 2025 00:37:32 -0300") +# +# Retorna: A data formatada no stdout. +formatar_data_patch() +{ + local date_string="$1" + local formatted_date + + if [ -z "$date_string" ]; then + echo "Erro: Nenhuma string de data fornecida." >&2 + return 1 + fi + + # Usa 'date -d' para interpretar a string de data complexa. + # O formato de saída é %Y-%m-%d %H:%M:%S. + # Nota: A data é convertida para o fuso horário LOCAL do sistema que está rodando o script. + # Se você quiser manter o fuso horário original, o formato seria diferente. + formatted_date=$(date -d "$date_string" +"%Y-%m-%d %H:%M:%S") + + # Verifica se o comando date foi bem-sucedido + if [ "$?" -ne 0 ]; then + echo "Erro: Falha ao processar a string de data: '$date_string'" >&2 + return 1 + fi + + # Imprime a data formatada no stdout + printf '%s' "$formatted_date" + return 0 +} From 3bc9caab8e87df89230b56c615672e475a82d521 Mon Sep 17 00:00:00 2001 From: JGBSouza Date: Mon, 22 Dec 2025 22:23:15 -0300 Subject: [PATCH 19/33] fix: fix submission_utils functions Signed-off-by: JGBSouza --- src/lib/patch_track_utils/submission_utils.sh | 270 +++++++++--------- 1 file changed, 135 insertions(+), 135 deletions(-) diff --git a/src/lib/patch_track_utils/submission_utils.sh b/src/lib/patch_track_utils/submission_utils.sh index e9533655d..9083d8e00 100644 --- a/src/lib/patch_track_utils/submission_utils.sh +++ b/src/lib/patch_track_utils/submission_utils.sh @@ -6,188 +6,188 @@ declare -Ag patch_track_condition_array function insert_submission() { - local _contribution_id="$1" - local _send_by="$2" - local sql_operation_result - local ret - - sql_operation_result="$(insert_into "$DATABASE_SUBMISSION_TABLE" '("contribution_id", "send_by")' "('${_contribution_id}', '${_send_by}')")" - ret="$?" - - if [[ "$ret" -eq 2 || "$ret" -eq 61 ]]; then - complain "$sql_operation_result" - return "$ret" # EINVAL - elif [[ "$ret" -ne 0 ]]; then - complain "($LINENO):" $'Error while trying to insert new submission:\n'"$sql_operation_result" - return "$ret" # EINVAL - fi - - return 0 + local _contribution_id="$1" + local _send_by="$2" + local sql_operation_result + local ret + + sql_operation_result="$(insert_into "$DATABASE_SUBMISSION_TABLE" '("contribution_id", "send_by")' "('${_contribution_id}', '${_send_by}')")" + ret="$?" + + if [[ "$ret" -eq 2 || "$ret" -eq 61 ]]; then + complain "$sql_operation_result" + return "$ret" # EINVAL + elif [[ "$ret" -ne 0 ]]; then + complain "($LINENO):" $'Error while trying to insert new submission:\n'"$sql_operation_result" + return "$ret" # EINVAL + fi + + return 0 } function create_submission() { - local _contribution_id="$1" - local _submission_author="$2" - local ret - local patch_existent_result - local insert_submission_result - local get_submission_id_result - - insert_submission_result="$(insert_submission "$_contribution_id" "$_submission_author")" - ret="$?" - - if [[ "$ret" -ne 0 ]]; then - complain "$insert_submission_result" - return "$ret" # EINVAL - fi - - get_submission_id_result="$(get_last_submission_infos_by_contribution_id 'id' "$_contribution_id")" - ret="$?" - - if [[ "$ret" -ne 0 ]]; then - complain "$get_submission_id_result" - return "$ret" # EINVAL - fi - - printf '%s\n' "$get_submission_id_result" - return 0 + local _contribution_id="$1" + local _submission_author="$2" + local ret + local patch_existent_result + local insert_submission_result + local get_submission_id_result + + insert_submission_result="$(insert_submission "$_contribution_id" "$_submission_author")" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$insert_submission_result" + return "$ret" # EINVAL + fi + + get_submission_id_result="$(get_last_submission_infos_by_contribution_id 'id' "$_contribution_id")" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$get_submission_id_result" + return "$ret" # EINVAL + fi + + printf '%s\n' "$get_submission_id_result" + return 0 } function get_last_submission_infos_by_contribution_id() { - local _submission_infos="$1" - local _contribution_id="$2" - local sql_operation_result - local ret + local _submission_infos="$1" + local _contribution_id="$2" + local sql_operation_result + local ret - patch_track_condition_array=(['contribution_id']="${_contribution_id}") + patch_track_condition_array=(['contribution_id']="${_contribution_id}") - sql_operation_result="$(get_submission_info "$_submission_infos" 'patch_track_condition_array' 'ID DESC' '1')" - ret="$?" + sql_operation_result="$(get_submission_info "$_submission_infos" 'patch_track_condition_array' 'ID DESC' '1')" + ret="$?" - if [[ "$ret" -ne 0 ]]; then - complain "$sql_operation_result" - return "$ret" # EINVAL - fi + if [[ "$ret" -ne 0 ]]; then + complain "$sql_operation_result" + return "$ret" # EINVAL + fi - printf '%s\n' "$sql_operation_result" - return 0 + printf '%s\n' "$sql_operation_result" + return 0 } function get_submission_info() { - local _submission_infos="$1" - local -n _submission_condition_array="$2" - local _order_by="${3:-}" - local _limit="${4:-}" + local _submission_infos="$1" + local -n _submission_condition_array="$2" + local _order_by="${3:-}" + local _limit="${4:-}" - sql_operation_result="$(get_database_table_info "$DATABASE_SUBMISSION_TABLE" "$_submission_infos" '_submission_condition_array' "$_order_by" "$_limit")" - ret="$?" + sql_operation_result="$(get_database_table_info "$DATABASE_SUBMISSION_TABLE" "$_submission_infos" '_submission_condition_array' "$_order_by" "$_limit")" + ret="$?" - if [[ "$ret" -ne 0 ]]; then - complain "$sql_operation_result" - return "$ret" # EINVAL - fi + if [[ "$ret" -ne 0 ]]; then + complain "$sql_operation_result" + return "$ret" # EINVAL + fi - printf '%s\n' "$sql_operation_result" - return 0 + printf '%s\n' "$sql_operation_result" + return 0 } function create_patch_submission() { - local _patch_id="$1" - local _submission_id="$2" - local _message_id="$3" + local _patch_id="$1" + local _submission_id="$2" + local _message_id="$3" - patch_existent_result="$(check_patch_submission_existence_by_unique_attributes "$_patch_id" "$_submission_id" "$_message_id")" - ret="$?" + patch_existent_result="$(check_patch_submission_existence_by_unique_attributes "$_patch_id" "$_submission_id" "$_message_id")" + ret="$?" - if [[ "$ret" -ne 0 ]]; then - complain "$patch_existent_result" - return "$ret" # EINVAL - fi + if [[ "$ret" -ne 0 ]]; then + complain "$patch_existent_result" + return "$ret" # EINVAL + fi - if [[ "$patch_existent_result" -ne 0 ]]; then - complain "($LINENO): error while trying to create new patch submission for patch-id {$_patch_id} into submission {$_submission_id}" - return 22 # EINVAL - fi + if [[ "$patch_existent_result" -ne 0 ]]; then + complain "($LINENO): error while trying to create new patch submission for patch-id {$_patch_id} into submission {$_submission_id}" + return 22 # EINVAL + fi - insert_patch_submission_result="$(insert_patch_submission "$_patch_id" "$_submission_id" "$_message_id")" - ret="$?" + insert_patch_submission_result="$(insert_patch_submission "$_patch_id" "$_submission_id" "$_message_id")" + ret="$?" - if [[ "$ret" -ne 0 ]]; then - complain "$insert_patch_submission_result" - return "$ret" # EINVAL - fi + if [[ "$ret" -ne 0 ]]; then + complain "$insert_patch_submission_result" + return "$ret" # EINVAL + fi - return 0 + return 0 } function check_patch_submission_existence_by_unique_attributes() { - local _patch_id="$1" - local _submission_id="$2" - local _message_id="$3" + local _patch_id="$1" + local _submission_id="$2" + local _message_id="$3" - if [[ -z "$_patch_id" || -z "$_submission_id" || -z "$_message_id" ]]; then - complain "($LINENO): missing mandatory field(s) for new_patch_submission" - return 22 # EINVAL - fi + if [[ -z "$_patch_id" || -z "$_submission_id" || -z "$_message_id" ]]; then + complain "($LINENO): missing mandatory field(s) for new_patch_submission" + return 22 # EINVAL + fi - patch_track_condition_array=(['patch_id']="${_patch_id}" - ['submission_id']="${_submission_id}" - ['message_id']="${_message_id}") + patch_track_condition_array=(['patch_id']="${_patch_id}" + ['submission_id']="${_submission_id}" + ['message_id']="${_message_id}") - sql_operation_result="$(check_existence "$DATABASE_PATCH_SUBMISSION_TABLE" 'patch_track_condition_array')" - ret="$?" + sql_operation_result="$(check_existence "$DATABASE_PATCH_SUBMISSION_TABLE" 'patch_track_condition_array')" + ret="$?" - if [[ "$ret" -ne 0 ]]; then - complain "$sql_operation_result" - return "$ret" # EINVAL - fi + if [[ "$ret" -ne 0 ]]; then + complain "$sql_operation_result" + return "$ret" # EINVAL + fi - printf '%s\n' "${sql_operation_result}" - return 0 + printf '%s\n' "${sql_operation_result}" + return 0 } function get_patch_submission_info() { - local _patch_submission_infos="$1" - local -n _patch_submission_condition_array="$2" - local _order_by="${3:-}" - local _limit="${4:-}" + local _patch_submission_infos="$1" + local -n _patch_submission_condition_array="$2" + local _order_by="${3:-}" + local _limit="${4:-}" - sql_operation_result="$(get_database_table_info "$DATABASE_PATCH_SUBMISSION_TABLE" "$_patch_submission_infos" '_patch_submission_condition_array' "$_order_by" "$_limit")" - ret="$?" + sql_operation_result="$(get_database_table_info "$DATABASE_PATCH_SUBMISSION_TABLE" "$_patch_submission_infos" '_patch_submission_condition_array' "$_order_by" "$_limit")" + ret="$?" - if [[ "$ret" -ne 0 ]]; then - complain "$sql_operation_result" - return "$ret" # EINVAL - fi + if [[ "$ret" -ne 0 ]]; then + complain "$sql_operation_result" + return "$ret" # EINVAL + fi - printf '%s\n' "$sql_operation_result" - return 0 + printf '%s\n' "$sql_operation_result" + return 0 } function insert_patch_submission() { - local _patch_id="$1" - local _submission_id="$2" - local _message_id="$3" - local ret - - sql_operation_result="$(insert_into "$DATABASE_PATCH_SUBMISSION_TABLE" '("patch_id", "submission_id", "message_id")' \ - "('${_patch_id}', '${_submission_id}', '${_message_id}')" '' 'VERBOSE')" - ret="$?" - - if [[ "$ret" -eq 2 || "$ret" -eq 61 ]]; then - complain "$sql_operation_result" - return 22 # EINVAL - elif [[ "$ret" -ne 0 ]]; then - complain "($LINENO):" $'Error while trying to insert new patch submission:\n'"${sql_operation_result}" - return 22 # EINVAL - fi - - return 0 + local _patch_id="$1" + local _submission_id="$2" + local _message_id="$3" + local ret + + sql_operation_result="$(insert_into "$DATABASE_PATCH_SUBMISSION_TABLE" '("patch_id", "submission_id", "message_id")' \ + "('${_patch_id}', '${_submission_id}', '${_message_id}')" '' 'VERBOSE')" + ret="$?" + + if [[ "$ret" -eq 2 || "$ret" -eq 61 ]]; then + complain "$sql_operation_result" + return 22 # EINVAL + elif [[ "$ret" -ne 0 ]]; then + complain "($LINENO):" $'Error while trying to insert new patch submission:\n'"${sql_operation_result}" + return 22 # EINVAL + fi + + return 0 } From a3fd18bd8f2d962c3b9e21fd83e649eded857c15 Mon Sep 17 00:00:00 2001 From: JGBSouza Date: Mon, 22 Dec 2025 22:24:15 -0300 Subject: [PATCH 20/33] feat: ask contribution name in send_patch Signed-off-by: JGBSouza --- src/send_patch.sh | 25 ++----------------------- 1 file changed, 2 insertions(+), 23 deletions(-) diff --git a/src/send_patch.sh b/src/send_patch.sh index f6afee5cd..0444e120c 100644 --- a/src/send_patch.sh +++ b/src/send_patch.sh @@ -1,4 +1,3 @@ - # This file handles all the interactions with git send-email. Currently it # provides functions to configure the options used by git send-email. # It's also able to verify if the configurations required to use git send-email @@ -20,7 +19,6 @@ declare -ga essential_config_options=('user.name' 'user.email' 'sendemail.smtpuser' 'sendemail.smtpserver' 'sendemail.smtpserverport') declare -ga optional_config_options=('sendemail.smtpencryption' 'sendemail.smtppass') -declare -gr email_regex='[A-Za-z0-9_\.-]+@[A-Za-z0-9_-]+(\.[A-Za-z0-9]+)+' declare -g output_file="${KW_CACHE_DIR}/send_patch_output.log" #shellcheck disable=SC2119 @@ -45,8 +43,9 @@ function send_patch_main() [[ -n "${options_values['VERBOSE']}" ]] && flag='VERBOSE' if [[ -n "${options_values['SEND']}" ]]; then + contribution_name="$(ask_contribution_name)" mail_send "$flag" - register_patch_track "${KW_CACHE_DIR}/patches" "$output_file" + register_patch_track "${KW_CACHE_DIR}/patches" "$output_file" "$contribution_name" return 0 fi @@ -450,26 +449,6 @@ function validate_encryption() return 22 # EINVAL } -# This function validates the encryption. If the passed encryption is not valid -# this will warn the user and clear the option. -# -# @option: The option to determine if it should be an email -# @value: The value being passed -# -# Return: -# Returns 0 if valid; 22 if invalid -function validate_email() -{ - local value="$1" - - if [[ ! "$value" =~ ^${email_regex}$ ]]; then - complain "Invalid email: $value" - return 22 #EINVAL - fi - - return 0 -} - # Gets the values associated to a certain config option and puts them in the # given array. # From eadbac32368f2c377ebbc047e5cadb36843f0a4f Mon Sep 17 00:00:00 2001 From: JGBSouza Date: Mon, 22 Dec 2025 22:24:55 -0300 Subject: [PATCH 21/33] feat: add mutt integration and open contribution and update contribution_status Signed-off-by: JGBSouza --- src/patch_track.sh | 1062 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 1000 insertions(+), 62 deletions(-) diff --git a/src/patch_track.sh b/src/patch_track.sh index 01e4f2794..39bcca6cb 100644 --- a/src/patch_track.sh +++ b/src/patch_track.sh @@ -3,11 +3,27 @@ include "${KW_LIB_DIR}/lib/kw_string.sh" include "${KW_LIB_DIR}/lib/patch_track_utils/contribution_utils.sh" include "${KW_LIB_DIR}/lib/patch_track_utils/patch_utils.sh" include "${KW_LIB_DIR}/lib/patch_track_utils/submission_utils.sh" - +include "${KW_LIB_DIR}/lib/patch_track_utils/repository_utils.sh" +include "${KW_LIB_DIR}/lib/kw_config_loader.sh" + +declare -g MUTT_CONFIG_NAME='patch_track_mutt.config' +declare -g MUTT_CONFIG_PATH="${KW_ETC_DIR}/${MUTT_CONFIG_NAME}" +declare -g KW_MUTT_DIR="${KW_SHARE_DIR}/mutt" +declare -g MUTT_RC_PATH="${KW_MUTT_DIR}/.muttrc-kw-patch-track" +declare -g ISYNC_RC_PATH="${KW_MUTT_DIR}/.mbsyncrc" +declare -g KW_MUTT_MAIL_DIR="${KW_MUTT_DIR}/mail" +declare -g KW_MUTT_MESSAGES_DIR="${KW_MUTT_DIR}/messages" +declare -g KW_MUTT_HEADERS_DIR="${KW_MUTT_DIR}/headers" +declare -g KW_MUTT_MAIL_CHECK='60' + +declare -gA set_confs declare -gA options_values declare -gA condition_array declare -gA updates_array +declare -ga essential_config_options=('imap_user' 'imap_pass' + 'folder' 'spoolfile' 'record') + function patch_track_main() { local flag @@ -27,8 +43,8 @@ function patch_track_main() exit 22 # EINVAL fi - if [[ -n "${options_values['DASHBOARD']}" ]]; then - show_patches_dashboard "${options_values['FROM']}" "${options_values['BEFORE']}" "${options_values['AFTER']}" "$flag" + if [[ -n "${options_values['SHOW_PATCHES']}" ]]; then + show_patches_dashboard "$flag" return 0 fi @@ -40,11 +56,77 @@ function patch_track_main() set_patch_status "${options_values['PATCH_ID']}" "${options_values['STATUS']}" "$flag" if [[ "$?" -eq 0 ]]; then - echo "Patch status updated successfully." + success "Patch status updated successfully." fi return 0 fi + if [[ -n "${options_values['SET_REPOSITORY']}" ]]; then + if [[ -z "${options_values['CONTRIBUTION_ID']}" ]]; then + complain 'Contribution id not specified' + return 22 # EINVAL + fi + + if [[ -z "${options_values['ORIGIN_URL']}" ]]; then + complain 'Branch name not specified' + return 22 # EINVAL + fi + + register_contribution_repository "${options_values['CONTRIBUTION_ID']}" "${options_values['SET_REPOSITORY']}" "${options_values['ORIGIN_URL']}" "$flag" + if [[ "$?" -eq 0 ]]; then + success "Patch repository updated successfully." + fi + return 0 + fi + + if [[ -n "${options_values['SET_MAINTAINER']}" ]]; then + if [[ -z "${options_values['REPOSITORY_ID']}" ]]; then + complain 'Repository id not specified' + return 22 # EINVAL + fi + + if [[ -z "${options_values['MAINTAINER_EMAIL']}" ]]; then + complain 'Maintainer email not specified' + return 22 # EINVAL + fi + + register_repository_maintainer "${options_values['REPOSITORY_ID']}" "${options_values['SET_MAINTAINER']}" "${options_values['MAINTAINER_EMAIL']}" "$flag" + if [[ "$?" -eq 0 ]]; then + success "Repository Maintainer updated successfully." + fi + return 0 + fi + + if [[ -n "${options_values['SHOW_CONTRIBUTIONS']}" ]]; then + show_contributions_dashboard "$flag" '' "${options_values['CONTRIBUTION_ID']}" + return 0 + fi + + verify_mutt_minimal_config + + if [[ -n "${options_values['UPDATE_STATUS']}" ]]; then + if [[ -z "${options_values['CONTRIBUTION_ID']}" ]]; then + complain 'Contribution id not specified' + return 22 # EINVAL + fi + + update_contribution_status ${options_values['CONTRIBUTION_ID']} + if [[ "$?" -eq 0 ]]; then + success "Contribution Status updated successfully." + fi + return 0 + fi + + if [[ -n "${options_values['OPEN_CONTRIBUTION_ON_MUTT']}" ]]; then + if [[ -z "${options_values['CONTRIBUTION_ID']}" ]]; then + complain 'Contribution id not specified' + return 22 # EINVAL + fi + + open_contribution_on_mutt ${options_values['CONTRIBUTION_ID']} + return 0 + fi + return 0 } @@ -60,12 +142,20 @@ function show_patches_dashboard() { local flag="$1" local columns="$2" + local submission_id="$3" + local caption="${4:-'Patches infos'}" local from="${options_values['FROM']}" local before="${options_values['BEFORE']}" local after="${options_values['AFTER']}" local patches_info declare -a patches_array + if [[ -z $columns ]]; then + columns="$(tput cols)" + fi + + condition_array=() + if [[ -n "$from" ]]; then condition_array=(['date,=']="${from}") else @@ -77,10 +167,17 @@ function show_patches_dashboard() fi fi - patches_info=$(select_from "$DATABASE_PATCH_TABLE" '' '' 'condition_array') + if [[ -n "$submission_id" ]]; then + condition_array+=(["${DATABASE_PATCH_SUBMISSION_TABLE}.submission_id"]="$submission_id") + patches_info=$(select_from "$DATABASE_PATCH_TABLE JOIN $DATABASE_PATCH_SUBMISSION_TABLE ON ${DATABASE_PATCH_TABLE}.id = ${DATABASE_PATCH_SUBMISSION_TABLE}.patch_id" 'patch.id, patch.created_at, patch.status, patch.title' '' 'condition_array') + else + patches_info=$(select_from "$DATABASE_PATCH_TABLE" 'id, created_at, status, title' '' 'condition_array') + fi + + patches_info=$(select_from "$DATABASE_PATCH_TABLE JOIN $DATABASE_PATCH_SUBMISSION_TABLE ON ${DATABASE_PATCH_TABLE}.id = ${DATABASE_PATCH_SUBMISSION_TABLE}.patch_id" 'patch.id, patch.created_at, patch.status, patch.title' '' 'condition_array') readarray -t patches_array <<< "$patches_info" - print_patches_dashboard 'patches_array' "$columns" + print_patches_dashboard 'patches_array' "$columns" "$caption" } # This function inserts each patch subject into the database and handles @@ -98,10 +195,13 @@ function register_patch_track() local send_patch_output_dir="$2" local contribution_name="$3" local ret + local email_from local -A patches_message_id_array - from="joaosouzaaa12@gmail.com" - get_or_create_contribution_result="$(get_or_create_contribution "contribution_name" "$from" '')" + email_from=$(cat "$send_patch_output_dir" | + grep -m 1 '^MAIL FROM:' | tail -n 1 | cut -d'<' -f2 | cut -d'>' -f1) + + get_or_create_contribution_result="$(get_or_create_contribution "$contribution_name" "$email_from" '')" ret="$?" if [[ "$ret" -ne 0 ]]; then @@ -117,14 +217,14 @@ function register_patch_track() return "$ret" fi - create_submission_result="$(create_submission "$get_or_create_contribution_result" "$from")" + create_submission_result="$(create_submission "$get_or_create_contribution_result" "$email_from")" ret="$?" if [[ "$ret" -ne 0 ]]; then complain "$ret" return "$ret" fi - + register_patch_submissions_result="$(register_patch_submissions "$create_submission_result" 'patches_message_id_array')" ret="$?" @@ -137,25 +237,68 @@ function register_patch_track() return 0 } +function ask_contribution_name() +{ + local existent_contributions=() + local formatted_string='' + local total_contributions=0 + local contribution + local contribution_index + + condition_array=() + mapfile -t existent_contributions < <(get_contribution_info 'title' 'condition_array' 'id') + total_contributions=${#existent_contributions[@]} + + if [ "$total_contributions" -gt 0 ]; then + for i in "${!existent_contributions[@]}"; do + formatted_string+="$((i + 1)) - ${existent_contributions[i]}" + + if [ "$i" -lt "$((total_contributions - 1))" ]; then + formatted_string+=$'\n' + fi + done + fi + + contribution=$(ask_with_default $'Select one of the contributions bellow or write a new title:\n' "$formatted_string") + + if [[ "$contribution" =~ ^[0-9]+$ ]]; then + contribution_index="$contribution" + + if [[ "$contribution_index" -ge 1 ]] && [[ "$contribution_index" -le "$total_contributions" ]]; then + + local array_index=$((contribution_index - 1)) + + printf '%s\n' "${existent_contributions[array_index]}" + return 0 + fi + fi + + printf '%s\n' "$contribution" + return 0 +} + function register_contribution() { local contribution_name="$1" + local _send_email_log_file="$2" local from local get_or_create_contribution_result - #from="$(grep -m 1 '^From:' <<< "$header_block" | tail -n 1 | sed 's/^From:[[:space:]]*//')" - from="joaosouzaaa12@gmail.com" - get_or_create_contribution_result="$(get_or_create_contribution "contribution_name" "$from")" - ret="$?" + email_from=$(cat "$_send_email_log_file" | + grep -m 1 '^MAIL FROM:' | tail -n 1 | cut -d'<' -f2 | cut -d'>' -f1) - echo "começou contribution" > /dev/tty + #echo "$email_from" + #echo "começou contribution" > /dev/tty + + get_or_create_contribution_result="$(get_or_create_contribution "$contribution_name" "$email_from" '')" + ret="$?" if [[ "$ret" -ne 0 ]]; then complain "$get_or_create_contribution_result" return "$ret" fi - echo "registrou contribution" > /dev/tty + #echo "registrou contribution" > /dev/tty printf '%s\n' "$get_or_create_contribution_result" return 0 } @@ -174,10 +317,10 @@ function register_patches() patches_output_array=() - if [[ "$patch_numbers" -ne 1 ]]; then #if there is a cover letter it wouldn't be in the patch_cache dir + if [[ "$patch_numbers" -ne 1 ]]; then # if there is a cover letter it wouldn't be in the patch_cache dir extract_patch_from_file "$patch_num" "$send_patch_output_dir" 'patch_metadata' - get_or_create_patch_result="$(get_or_create_patch "${patch_metadata["subject"]}" '' "${patch_metadata["from"]}"\ - "$contribution_id" '' 1)" + get_or_create_patch_result="$(get_or_create_patch "${patch_metadata["subject"]}" "${patch_metadata["from"]}" \ + "$contribution_id" '')" ret="$?" if [[ "$ret" -ne 0 ]]; then @@ -188,7 +331,7 @@ function register_patches() patches_output_array["$get_or_create_patch_result"]='' ((patch_num++)) - echo "${patch_metadata["subject"]}" + #echo "${patch_metadata["subject"]}" fi for patch_path in "${patch_cache}/"*; do @@ -196,23 +339,175 @@ function register_patches() extract_patch_from_file "$patch_num" "$send_patch_output_dir" 'patch_metadata' patch_metadata["commit_hash"]="$(get_patch_commit_hash "$patch_path")" - get_or_create_patch_result="$(get_or_create_patch "${patch_metadata["subject"]}" '' "${patch_metadata["from"]}"\ - "$contribution_id" "${patch_metadata["commit_hash"]}" 1)" + get_or_create_patch_result="$(get_or_create_patch "${patch_metadata["subject"]}" "${patch_metadata["from"]}" \ + "$contribution_id" "${patch_metadata["commit_hash"]}")" ret="$?" if [[ "$ret" -ne 0 ]]; then complain "$get_or_create_patch_result" return "$ret" fi - + patches_output_array["$get_or_create_patch_result"]="${patch_metadata["message_id"]}" - echo "${patch_metadata["subject"]}" - echo "${patch_metadata["commit_hash"]}" + #echo "${patch_metadata["subject"]}" + #echo "${patch_metadata["commit_hash"]}" ((patch_num++)) fi done - return 0; + return 0 +} + +function update_contribution_status() +{ + local contribution_id="$1" + + local submission_infos + local submission_id + + local patch_submission_infos + local patch_id + + local patch_infos + local patch_commit_hash + local patch_message_id + local patch_author_email + local patch_current_status + local final_status + local repository_origin_url + local maintainer_email + declare -A patches_status + declare -A patch_submission_condition_array + declare -A patch_condition_array + declare -a maintainers_emails=() + + condition_array=(['id']="$contribution_id") + contribution_repository_id="$(get_contribution_info 'repository_id' 'condition_array')" + + condition_array=(['id']="$contribution_repository_id") + repository_origin_url="$(get_repository_info 'origin_url' 'condition_array')" + + cmd_manager git -C "$repository_origin_url" fetch origin > /dev/null 2>&1 + + submission_id="$(get_last_submission_infos_by_contribution_id 'id' "$contribution_id")" || return 22 + condition_array=(['submission_id']="$submission_id") + + patch_submission_infos="$( + get_patch_submission_info 'patch_id, submission_id, message_id' 'condition_array' + )" || return 22 + + #condition_array=(['repository_id']="$contribution_repository_id") + #maintainers_ids_result="$(get_maintainers_info 'contact_id' 'condition_array')" + + #for maintainer_id in $maintainers_ids_result; do + # condition_array=(['id']="$maintainer_id") + # maintainer_email_result="$(get_email_contact_info 'email' 'condition_array')" + + # maintainers_emails+=("maintainer_email_result") + #done + + #echo "$patch_submission_ids" + + for patch_id in $patch_submission_infos; do + IFS='|' read -r patch_id submission_id message_id <<< "$patch_submission_infos" + + condition_array=(['patch_id']="$patch_id" + ['submission_id']="$submission_id" + ['message_id']="$message_id") + message_id="$(get_patch_submission_info 'message_id' 'condition_array')" || continue + + condition_array=(['id']="$patch_id") + patch_infos="$(get_patch_info 'commit_hash, status' 'condition_array')" || continue + + IFS='|' read -r commit_hash status <<< "$patch_infos" + + #echo "AAAAAAAAAA $patch_infos" AA "$commit_hash" AA "$status" AA "$message_id" AA "$send_by" + + final_status="$( + update_patch_status \ + "$patch_id" \ + "$message_id" \ + "$commit_hash" \ + '' \ + "$status" + )" + patches_status["$patch_id"]="$final_status" + done + + decide_patch_status "$contribution_id" 'patches_status' + show_contributions_dashboard '' '' "$contribution_id" +} + +function update_patch_status() +{ + local patch_id="$1" + local patch_message_id="$2" + local patch_commit_hash="$3" + local patch_maintainer_email="$4" + local patch_current_status="$5" + local flag="${6:-'SILENT'}" + + local last_msg_file + local last_author + local reply_files + + if [[ "$patch_current_status" == 'MERGED' ]]; then + printf '%s' 'MERGED' + return 0 + fi + + [[ -f /tmp/mutt-status ]] && rm -f /tmp/mutt-status + + xterm -iconic -e \ + sh -c "mutt -F ${MUTT_RC_PATH} \ + -e 'push \"l ((~i ${patch_message_id})|(~x ${patch_message_id})) ~b .* q\"' \ + ; echo finished > /tmp/mutt-status" \ + > /dev/null 2>&1 & + + while [[ ! -s /tmp/mutt-status ]]; do + sleep 0.2 + done + + mapfile -t reply_files < <(grep -R -l "In-Reply-To:.*${patch_message_id}" "$KW_MUTT_MESSAGES_DIR") + + if [[ "${#reply_files[@]}" -eq 0 ]]; then + set_patch_status "$patch_id" 'SENT' + printf '%s' 'SENT' + return 0 + fi + + for file in "${reply_files[@]}"; do + if grep -Eiw "Approved|Reviewed-by" "$file" > /dev/null 2>&1; then + set_patch_status "$patch_id" 'APPROVED' + printf '%s' 'APPROVED' + return 0 + fi + done + + last_msg_file=$(printf '%s\n' "${reply_files[@]}" | + xargs -d '\n' stat --format='%Y %n' 2> /dev/null | + sort -n | + tail -n1 | + cut -d' ' -f2-) + + if [[ -z "$last_msg_file" ]]; then + set_patch_status "$patch_id" 'SENT' + printf '%s' 'SENT' + return 0 + fi + + last_author=$(grep -i '^From:' "$last_msg_file" | + sed -E 's/From:.*<([^>]+)>.*/\1/') + + if [[ "$last_author" == "${patch_track_mutt_config[imap_user]}" ]]; then + set_patch_status "$patch_id" 'SENT' + printf '%s' 'SENT' + else + set_patch_status "$patch_id" 'REVIEWED' + printf '%s' 'REVIEWED' + fi + + return 0 } function register_patch_submissions() @@ -220,13 +515,13 @@ function register_patch_submissions() local submission_id="$1" local -n patches_message_id="$2" - echo "começou patch submission register " > /dev/tty + #echo "começou patch submission register " > /dev/tty for patch_id in "${!patches_message_id[@]}"; do message_id="${patches_message_id[$patch_id]}" create_patch_submission_result="$(create_patch_submission "$patch_id" "$submission_id" "$message_id")" ret="$?" - echo "${patch_id} EE ${message_id}" > /dev/tty + #echo "${patch_id} EE ${message_id}" > /dev/tty if [[ "$ret" -ne 0 ]]; then complain "$create_patch_submission_result" return "$ret" @@ -236,11 +531,47 @@ function register_patch_submissions() return 0 } -#function register_submission() -#{ -# local contribution_id="$1" -# -#} +function open_contribution_on_mutt() +{ + local contribution_id="$1" + local -a messages_ids=() + local mutt_query='' + + if [[ -z "$contribution_id" ]]; then + complain 'Contribution id not informed' + return 22 + fi + + condition_array=(['contribution_id']="$contribution_id") + submission_ids_result="$(get_submission_info 'id' 'condition_array')" + + if [[ "$?" -ne 0 ]]; then + complain "$submission_ids_result" + return 61 # ENODATA + fi + + for submission_id in $submission_ids_result; do + condition_array=(['submission_id']="$submission_id") + message_id_result="$(get_patch_submission_info 'message_id' 'condition_array')" + + if [[ "$?" -ne 0 ]]; then + continue + fi + + mutt_query+="(~i $message_id_result | ~x $message_id_result) | " + done + + mutt_query="${mutt_query% | }" + + if [[ -z "$mutt_query" ]]; then + complain 'no submission message-id found for this contribution' + return 61 # ENODATA + fi + + mutt -F "$MUTT_RC_PATH" -e "push \"l ${mutt_query} \"" + + return 0 +} function extract_patch_from_file() { @@ -249,21 +580,35 @@ function extract_patch_from_file() local -n _output_metadata_array="$3" local header_block - echo "PATCH NUM ${_patch_extracted_num} LOG FILE: ${_send_email_log_file}" - header_block=$(cat "$_send_email_log_file" \ - | grep -Pzo \ - 'MAIL FROM:[^\n]*\nRCPT TO:[^\n]*\nFrom:[^\n]*\nTo:[^\n]*\nSubject:[^\n]*\nDate:[^\n]*\nMessage-ID:[^\n]*(?!X-Mailer:)' | tr '\0' '\n') + #echo "PATCH NUM ${_patch_extracted_num} LOG FILE: ${_send_email_log_file}" + header_block=$( + awk ' + /^MAIL FROM:/ { + in_block=1 + block = $0 "\n" + next + } + in_block { + block = block $0 "\n" + if ($0 ~ /^X-Mailer:/) { + printf "%s", block + in_block=0 + block="" + } + } + ' "$_send_email_log_file" + ) if [[ -z "$header_block" ]]; then - echo "Erro: patch número $_patch_extracted_num não encontrado" >&2 - return 1 + #echo "Erro: patch número $_patch_extracted_num não encontrado" >&2 + return 22 fi _output_metadata_array=() - _output_metadata_array["from"]="$(grep -m "$_patch_extracted_num" '^From:' <<< "$header_block" | tail -n 1 | sed 's/^From:[[:space:]]*//')" + _output_metadata_array["from"]="$(grep -m "$_patch_extracted_num" '^From:' <<< "$header_block" | tail -n 1 | sed 's/^From:[[:space:]]*//' | cut -d'<' -f2 | cut -d'>' -f1)" _output_metadata_array["to"]="$(grep -m "$_patch_extracted_num" '^To:' <<< "$header_block" | tail -n 1 | sed 's/^To:[[:space:]]*//')" _output_metadata_array["subject"]="$(grep -m "$_patch_extracted_num" '^Subject:' <<< "$header_block" | tail -n 1 | sed 's/^Subject:[[:space:]]*//')" - _output_metadata_array["date"]="$(grep -m "$_patch_extracted_num" '^Date:' <<< "$header_block" | tail -n 1 | sed 's/^Date:[[:space:]]*//')" + _output_metadata_array["submission_date"]="$(grep -m "$_patch_extracted_num" '^Date:' <<< "$header_block" | tail -n 1 | sed 's/^Date:[[:space:]]*//')" _output_metadata_array["message_id"]="$(grep -m "$_patch_extracted_num" '^Message-ID:' <<< "$header_block" | tail -n 1 | sed 's/^Message-ID:[[:space:]]*//')" return 0 @@ -281,28 +626,28 @@ function print_patches_dashboard() { local -n _patches_array="$1" local columns="$2" + local caption="$3" local id local date local time local status local title local id_width=6 - local date_width=12 - local time_width=10 + local date_width=22 local status_width=10 - local title_width=$(("$columns" - id_width - date_width - time_width - status_width - 6)) + local title_width=$(("$columns" - id_width - date_width - status_width - 6)) + local caption_width=${#caption} + local trim_width=$(((columns - caption_width) / 2)) + local remaining_width=$((columns - caption_width - trim_width)) - if [[ -z $columns ]]; then - columns="$(tput cols)" - fi - - printf "%-${id_width}s|%-${date_width}s|%-${time_width}s|%-${status_width}s|%s\n" "ID" "Date" "Time" "Status" "Title" + printf "%*s%s%*s\n" "$trim_width" "" "$caption" "$remaining_width" "" | tr ' ' '-' + printf "%-${id_width}s|%-${date_width}s|%-${status_width}s|%-${title_width}s%s\n" "ID" "Date" "Status" "Title" printf "%-${columns}s\n" | tr ' ' '-' # Print rows for patch in "${!_patches_array[@]}"; do - IFS='|' read -r id date time status title <<< "${_patches_array[$patch]}" - printf "%-${id_width}s|%-${date_width}s|%-${time_width}s|%-${status_width}s|%s\n" "$id" "$date" "$time" "$status" "$title" + IFS='|' read -r id date status title <<< "${_patches_array[$patch]}" + printf "%-${id_width}s|%-${date_width}s|%-${status_width}s|%-${title_width}s%s\n" "$id" "$date" "$status" "$title" done tput cnorm > /dev/tty @@ -340,7 +685,7 @@ function set_patch_status() formatted_status=$(check_valid_status "$patch_new_status") if [[ "$?" -ne 0 ]]; then - formatted_status=$(get_patch_status) + formatted_status=$(get_valid_status) fi condition_array=(['id']="${patch_id}") @@ -360,13 +705,86 @@ function set_patch_status() return 0 } +function set_contribution_status() +{ + local contribution_id="$1" + local contribution_new_status="$2" + local formatted_status + local sql_operation_result + local ret + + if [[ -z "$contribution_id" ]]; then + complain 'Contribution ID is empty' + return 61 # ENODATA + fi + + if [[ -z "$contribution_new_status" ]]; then + complain 'New status is empty' + return 61 # ENODATA + fi + + formatted_status=$(check_valid_status "$contribution_new_status") + + if [[ "$?" -ne 0 ]]; then + formatted_status=$(get_valid_status) + fi + + condition_array=(['id']="${contribution_id}") + updates_array=(['status']="${formatted_status}") + + sql_operation_result=$(update_into "$DATABASE_CONTRIBUTION_TABLE" 'updates_array' '' 'condition_array' 'VERBOSE') + ret="$?" + + if [[ "$ret" -eq 2 || "$ret" -eq 61 ]]; then + complain "$sql_operation_result" + return 22 # EINVAL + elif [[ "$ret" -ne 0 ]]; then + complain "($LINENO):" $'Error while trying to update contribution status in the database with the command:\n'"${sql_operation_result}" + return 22 # EINVAL + fi + + return 0 +} + +function decide_patch_status() +{ + local contribution_id="$1" + local -n _contribution_patches_status="$2" + local has_revisado=0 + local has_aprovado=0 + local has_mergeado=0 + + for pid in "${!_contribution_patches_status[@]}"; do + case "${_contribution_patches_status[$pid]}" in + REVIEWED) has_revisado=1 ;; + APPROVED) has_aprovado=1 ;; + MERGED) has_mergeado=1 ;; + esac + done + + if [[ $has_revisado -eq 1 ]]; then + contribution_status="REVIEWED" + elif [[ $has_aprovado -eq 1 ]]; then + contribution_status="APPROVED" + elif [[ $has_mergeado -eq 1 ]]; then + contribution_status="MERGED" + else + contribution_status="SENT" + fi + + condition_array=(['id']="$contribution_id") + set_contribution_status "$contribution_id" "$contribution_status" + + return 0 +} + # This function prompts the user for the current patch status and validates # it. It returns the validated status or prompts again if the status # is invalid. # # Return: # Returns the formatted status. -function get_patch_status() +function get_valid_status() { local status local formatted_status @@ -377,7 +795,7 @@ function get_patch_status() formatted_status=$(check_valid_status "$status") if [[ "$?" -ne 0 ]]; then - formatted_status=$(get_patch_status) + formatted_status=$(get_valid_status) fi printf '%s' "$formatted_status" @@ -413,6 +831,484 @@ function check_valid_status() return 0 } +# This sets the status of a specified patch. It updates the status +# of a patch identified by its ID and handles errors related to +# empty IDs or statuses, as well as database update failures. +# +# @patch_id: ID of the patch to be updated. +# @patch_new_status: New status to be set for the patch. +# +# Return: +# Returns 0 if successful; 22 if there is an invalid argument or +# an error during the update. +function register_contribution_repository() +{ + local contribution_id="$1" + local repository="$2" + local origin_url="$3" + local sql_operation_result + local ret + + if [[ -z "$contribution_id" ]]; then + complain 'Contribution ID is empty' + return 61 # ENODATA + fi + + if [[ -z "$repository" ]]; then + complain 'repository name is empty' + return 61 # ENODATA + fi + + if [[ -z "$origin_url" ]]; then + complain 'origin_url is empty' + return 61 # ENODATA + fi + + check_valid_repository_name="$(check_valid_db_string "$repository")" + + if [[ "$?" -ne 0 ]]; then + complain 'repository name is invalid' + return 61 # ENODATA + fi + + check_valid_origin_url_name="$(check_valid_db_string "$origin_url")" + + if [[ "$?" -ne 0 ]]; then + complain 'origin_url is invalid' + return 61 # ENODATA + fi + + get_or_create_repository_result="$(get_or_create_repository "$repository" "$origin_url")" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$ret" + return "$ret" + fi + + set_contribution_repository_result="$(set_contribution_repository "$contribution_id" "$get_or_create_repository_result")" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$set_contribution_repository_result" + return 22 # EINVAL + fi + + return 0 +} + +# This sets the status of a specified patch. It updates the status +# of a patch identified by its ID and handles errors related to +# empty IDs or statuses, as well as database update failures. +# +# @patch_id: ID of the patch to be updated. +# @patch_new_status: New status to be set for the patch. +# +# Return: +# Returns 0 if successful; 22 if there is an invalid argument or +# an error during the update. +function register_repository_maintainer() +{ + local repository_id="$1" + local maintainer_name="$2" + local maintainer_email="$3" + local sql_operation_result + local ret + + if [[ -z "$repository_id" ]]; then + complain 'repository_id ID is empty' + return 61 # ENODATA + fi + + if [[ -z "$maintainer_name" ]]; then + complain 'maintainer_name name is empty' + return 61 # ENODATA + fi + + if [[ -z "$maintainer_email" ]]; then + complain 'maintainer_email is empty' + return 61 # ENODATA + fi + + check_valid_maintainer_name="$(check_valid_db_string "$maintainer_name")" + + if [[ "$?" -ne 0 ]]; then + complain 'maintainer name is invalid' + return 61 # ENODATA + fi + + check_valid_maintainer_email="$(validate_email "$maintainer_email")" + + if [[ "$?" -ne 0 ]]; then + complain 'maintainer email is invalid' + return 61 # ENODATA + fi + + get_or_create_repository_maintainer_result="$(get_or_create_repository_maintainer "$repository_id" "$maintainer_name" "$maintainer_email")" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$get_or_create_repository_maintainer_result" + return 22 # EINVAL + fi + + return 0 +} + +function verify_mutt_minimal_config() +{ + local -a missing_options=() + local option + + #print_array 'essential_config_options' + #print_array 'patch_track_mutt_config' + + for option in "${essential_config_options[@]}"; do + if [[ -z "${patch_track_mutt_config[$option]}" || "${patch_track_mutt_config[$option]}" == '' ]]; then + missing_options+=("$option") + fi + done + + if ((${#missing_options[@]} > 0)); then + handle_missing_mutt_options 'missing_options' + load_patch_track_mutt_config + verify_mutt_minimal_config + fi + + generate_mutt_rc +} + +print_array() +{ + local array_name="$1" + local -n arr="$array_name" + local key + + #echo "$KW_ETC_DIR" + #echo "Array '$array_name':" + for key in "${!arr[@]}"; do + printf " [%s] = %s\n" "$key" "${arr[$key]}" + done +} + +function handle_missing_mutt_options() +{ + local -n _missing_options="$1" + local option + local value + + for option in "${_missing_options[@]}"; do + while true; do + read -rp "Insert a new value for '${option}': " value + [[ -n "$value" ]] && break + warning "Value can not be empty" + done + + save_mutt_config "$option" "$value" + done +} + +function save_mutt_config() +{ + local key="$1" + local value="$2" + local escaped_key + local escaped_value + + escaped_key="$(printf '%s\n' "$key" | sed 's/[.[\*^$(){}+?|\\/]/\\&/g')" + escaped_value="${value//\"/\\\"}" + + if grep -qE "^${escaped_key}=" "$MUTT_CONFIG_PATH" 2> /dev/null; then + sed -i \ + "s|^${escaped_key}=.*|${key}=\"${escaped_value}\"|" \ + "$MUTT_CONFIG_PATH" + else + printf '%s="%s"\n' "$key" "$escaped_value" >> "$MUTT_CONFIG_PATH" + fi +} + +set_or_replace_mutt_option() +{ + local file="$1" + local key="$2" + local value="$3" + + [[ -z "$value" ]] && return 0 + + if grep -qE "^[[:space:]]*(set[[:space:]]+)?${key}[[:space:]]*=" "$file"; then + sed -i \ + -E "s|^[[:space:]]*(set[[:space:]]+)?${key}[[:space:]]*=.*|set ${key}=${value}|" \ + "$file" + else + echo "set ${key}=${value}" >> "$file" + fi +} + +generate_mutt_rc() +{ + touch "$MUTT_RC_PATH" || return 1 + + set_or_replace_mutt_option "$MUTT_RC_PATH" imap_user "${patch_track_mutt_config[imap_user]}" + set_or_replace_mutt_option "$MUTT_RC_PATH" imap_pass "${patch_track_mutt_config[imap_pass]}" + set_or_replace_mutt_option "$MUTT_RC_PATH" folder "${patch_track_mutt_config[folder]}" + set_or_replace_mutt_option "$MUTT_RC_PATH" spoolfile "${patch_track_mutt_config[spoolfile]}" + set_or_replace_mutt_option "$MUTT_RC_PATH" record "${patch_track_mutt_config[record]}" + set_or_replace_mutt_option "$MUTT_RC_PATH" mbox_type "${patch_track_mutt_config[mbox_type]}" + + set_or_replace_mutt_option "$MUTT_RC_PATH" header_cache "\"$KW_MUTT_HEADERS_DIR\"" + set_or_replace_mutt_option "$MUTT_RC_PATH" message_cachedir "\"$KW_MUTT_MESSAGES_DIR\"" + set_or_replace_mutt_option "$MUTT_RC_PATH" mail_check "\"$KW_MUTT_MAIL_CHECK\"" +} + +# Show contributions dashboard with optional filter by contribution_id. +# +# @flag: Display mode flag (e.g., SILENT) +# @columns: terminal width +# @contribution_id: optional filter (shows only that contribution if set) +# +function show_contributions_dashboard() +{ + local flag="$1" + local columns="$2" + local contribution_id="$3" + local id + local status + local title + local author_email + local repository_id + local created_at + local repo_name + local repo_origin_url + local contributions_info_result + local last_submission_id_result + declare -a contributions_infos_array + + if [[ -z "$columns" ]]; then + columns="$(tput cols)" + fi + + if [[ -n "$contribution_id" ]]; then + + condition_array=(['id']="$contribution_id") + contributions_info_result=$(get_contribution_info 'status, title, author_email, created_at, repository_id' 'condition_array') + + if [[ "$?" -ne 0 ]]; then + complain "$contributions_info_result" + return 22 + fi + + IFS='|' read -r c_status c_title c_author_email c_created_at repository_id <<< "$contributions_info_result" + + condition_array=(['id']="$repository_id") + repo_infos_result="$(get_repository_info 'name, origin_url' 'condition_array')" + + if [[ "$?" -ne 0 ]]; then + complain "$repo_infos_result" + return 22 + fi + + IFS='|' read -r repo_name repo_url <<< "$repo_infos_result" + + last_submission_id_result="$(get_last_submission_infos_by_contribution_id 'id' "$contribution_id")" + + if [[ "$?" -ne 0 ]]; then + complain "$last_submission_id_result" + return 22 + fi + + contributions_infos_array[0]="$contribution_id|$c_status|$c_title|$c_author_email|$c_created_at|$repo_name|$repo_url|$repository_id|$last_submission_id_result" + print_single_contribution_dashboard 'contributions_infos_array' "$columns" + show_submissions_dashboard "$flag" "$columns" "$contribution_id" + show_patches_dashboard "$flag" "$columns" "$last_submission_id_result" 'Last submission patches' + + else + condition_array=() + contributions_info_result=$(select_from "$DATABASE_CONTRIBUTION_TABLE" 'id, title, created_at, status' '' '') + + if [[ "$?" -ne 0 ]]; then + complain "Erro ao buscar contributions" + return 22 + fi + + readarray -t contributions_infos_array <<< "$contributions_info_result" + print_contributions_dashboard 'contributions_infos_array' "$columns" + fi + + return 0 +} + +function print_single_contribution_dashboard() +{ + local -n _contribs_array="$1" + local columns="$2" + local name_width=20 + local date_width=22 + local status_width=10 + local sendby_width=20 + local remaining_width + local maintainers_ids + local trim_width + local c_title_width + local item="${_contribs_array[0]}" + + IFS='|' read -r c_id c_status c_title c_author_email c_created_at repo_name repo_url repo_id submission_id <<< "$item" + + c_title_width=${#c_title} + trim_width=$(((columns - c_title_width) / 2)) + remaining_width=$((columns - c_title_width - trim_width)) + + printf "%*s%s%*s\n" "$trim_width" "" "$c_title" "$remaining_width" "" | tr ' ' '-' + printf "ID: %s\n" "$c_id" + printf "Send-By: %s\n" "$c_author_email" + printf "Date: %s\n" "$c_created_at" + printf "Status: %s\n" "$c_status" + + if [[ -n "$repo_id" && -n "$repo_name" && -n "$repo_url" ]]; then + printf "Repository: %s\n" "(id: ${repo_id}) ${repo_name} - ${repo_url}" + fi + + condition_array=(['repository_id']="$repo_id") + maintainers_ids_result="$(get_maintainers_info 'contact_id' 'condition_array')" + + if [[ "$?" -ne 0 ]]; then + complain "$maintainers_ids_result" + return 22 + fi + + for maintainer_id in $maintainers_ids_result; do + condition_array=(['id']="$maintainer_id") + email_contact_infos_result="$(get_email_contact_info 'name, email' 'condition_array')" + + IFS='|' read -r m_name m_email <<< "$email_contact_infos_result" + + printf "Maintainer: %s - %s\n" "$m_name" "$m_email" + done + + tput cnorm > /dev/tty + printf "%-${columns}s\n" | tr ' ' '-' +} + +function print_contributions_dashboard() +{ + local -n _contribs_array="$1" + local columns="$2" + local id_width=6 + local date_width=22 + local status_width=10 + local name_width=$(("$columns" - id_width - date_width - status_width - 6)) + local maintainers_ids + local id + local name + local date + local status + local caption='Contributions infos' + local c_caption_width=${#caption} + + trim_width=$(((columns - c_caption_width) / 2)) + remaining_width=$((columns - c_caption_width - trim_width)) + + printf "%*s%s%*s\n" "$trim_width" "" "$caption" "$remaining_width" "" | tr ' ' '-' + + printf "%-${id_width}s|%-${name_width}s|%-${date_width}s|%-${status_width}s\n" \ + "ID" "Contribution" "Date" "Status" + printf "%-${columns}s\n" | tr ' ' '-' + + for item in "${_contribs_array[@]}"; do + IFS='|' read -r id name date status <<< "$item" + printf "%-${id_width}s|%-${name_width}s|%-${date_width}s|%-${status_width}s\n" \ + "$id" "$name" "$date" "$status" + done + + tput cnorm > /dev/tty + printf "%-${columns}s\n" | tr ' ' '-' +} + +# Show submissions dashboard with optional filter by contribution_id. +# +# @flag: Display mode flag (e.g., SILENT) +# @columns: terminal width +# @contribution_id: optional filter (shows only that contribution if set) +# +function show_submissions_dashboard() +{ + local flag="$1" + local columns="$2" + local contribution_id="$3" + local id + local status + local title + local author_email + local repository_id + local created_at + local repo_name + local repo_origin_url + local submission_infos_result + declare -a submissions_infos_array + + if [[ -z "$columns" ]]; then + columns="$(tput cols)" + fi + + if [[ -n "$contribution_id" ]]; then + + condition_array=(['contribution_id']="$contribution_id") + submission_infos_result=$(get_submission_info 'id, send_by, created_at' 'condition_array') + + if [[ "$?" -ne 0 ]]; then + complain "$submission_infos_result" + return 22 + fi + + readarray -t submissions_infos_array <<< "$submission_infos_result" + else + condition_array=() + submission_infos_result=$(select_from "$DATABASE_SUBMISSION_TABLE" 'id, send_by, created_at' '' '') + + if [[ "$?" -ne 0 ]]; then + complain "$submission_infos_result" + return 22 + fi + + readarray -t submissions_infos_array <<< "$submission_infos_result" + fi + + print_submissions_dashboard 'submissions_infos_array' "$columns" + return 0 +} + +function print_submissions_dashboard() +{ + local -n _contribs_array="$1" + local columns="$2" + local id_width=6 + local date_width=22 + local send_by_with=$(("$columns" - id_width - date_width - 6)) + local id + local name + local date + local status + local caption='Submissions infos' + local c_caption_width=${#caption} + + trim_width=$(((columns - c_caption_width) / 2)) + remaining_width=$((columns - c_caption_width - trim_width)) + + printf "%*s%s%*s\n" "$trim_width" "" "$caption" "$remaining_width" "" | tr ' ' '-' + + printf "%-${id_width}s|%-${send_by_with}s|%-${date_width}s\n" \ + "ID" "Send By" "Date" + printf "%-${columns}s\n" | tr ' ' '-' + + for item in "${_contribs_array[@]}"; do + IFS='|' read -r id send_by date <<< "$item" + printf "%-${id_width}s|%-${send_by_with}s|%-${date_width}s\n" \ + "$id" "$send_by" "$date" + done + + tput cnorm > /dev/tty + printf "%-${columns}s\n" | tr ' ' '-' +} + # Parses the command-line arguments for the patch track operation. # It populates the options_values associative array with parsed options. # @@ -420,8 +1316,8 @@ function check_valid_status() # Returns 22 if there are invalid arguments. function parse_patch_track() { - local long_options='help,dashboard,from:,before:,after:,id:,set-status:' - local short_options='d,f:,b:,a:,s:' + local long_options='help,show-patches,show-contributions,from:,before:,after:,id:,set-status:,set-repository:,contribution-id:,set-maintainer:,repository-id:,update,open-contribution' + local short_options='d,f:,b:,a:,s:,r:,c:,m:,u,o' local options local option @@ -436,18 +1332,31 @@ function parse_patch_track() eval "set -- ${options}" # Default values - options_values['DASHBOARD']='' + options_values['SHOW_PATCHES']='' + options_values['SHOW_CONTRIBUTIONS']='' options_values['PATCH_ID']='' options_values['SET_STATUS']= options_values['STATUS']='' options_values['FROM']='' options_values['BEFORE']='' options_values['AFTER']='' + options_values['CONTRIBUTION_ID']='' + options_values['SET_REPOSITORY']='' + options_values['ORIGIN_URL']='' + options_values['REPOSITORY_ID']='' + options_values['SET_MAINTAINER']='' + options_values['MAINTAINER_EMAIL']='' + options_values['INTERACTIVE']='' + options_values['OPEN_CONTRIBUTION_ON_MUTT']='' while [[ "$#" -gt 0 ]]; do case "$1" in - --dashboard | -d) - options_values['DASHBOARD']=1 + --show-patches) + options_values['SHOW_PATCHES']=1 + shift + ;; + --show-contributions | -d) + options_values['SHOW_CONTRIBUTIONS']=1 shift ;; --id) @@ -472,6 +1381,32 @@ function parse_patch_track() options_values['AFTER']="$2" shift 2 ;; + --set-repository) + options_values['SET_REPOSITORY']=$(printf "%s\n" "$2" | cut -d':' -f1) + options_values['ORIGIN_URL']=$(printf "%s\n" "$2" | cut -d':' -f2-) + shift 2 + ;; + --contribution-id | -c) + options_values['CONTRIBUTION_ID']="$2" + shift 2 + ;; + --set-maintainer | -m) + options_values['SET_MAINTAINER']=$(printf "%s\n" "$2" | cut -d':' -f1) + options_values['MAINTAINER_EMAIL']=$(printf "%s\n" "$2" | cut -d':' -f2-) + shift 2 + ;; + --repository-id | -r) + options_values['REPOSITORY_ID']="$2" + shift 2 + ;; + --update | -u) + options_values['UPDATE_STATUS']=1 + shift + ;; + --open-contribution | -o) + options_values['OPEN_CONTRIBUTION_ON_MUTT']=1 + shift + ;; --help | -h) patch_track_help "$1" exit @@ -496,6 +1431,9 @@ function patch_track_help() fi printf '%s\n' 'kw patch-track:' \ - ' patch-track (-d|--dashboard) [[--from ] | [--after ] [--before ]] - Show patches dashboard in chronological order ' \ - ' patch-track (--id ) [-s[=]| --set-status[=]] - Set the patch status ' -} \ No newline at end of file + ' patch-track (--show-patches) [[--from ] | [--after ] [--before ]] - Show patches dashboard in chronological order ' \ + ' patch-track (--id ) [-s[=]| --set-status[=]] - Set the patch status ' \ + ' patch-track (-r|--set-repository ] - Set the contribution repository and branch ' +} + +load_patch_track_mutt_config From 61bc3a45923611c246a49d511312b0241ef4c7b7 Mon Sep 17 00:00:00 2001 From: JGBSouza Date: Mon, 22 Dec 2025 22:25:20 -0300 Subject: [PATCH 22/33] feat: add contact_utils functions Signed-off-by: JGBSouza --- src/lib/patch_track_utils/contact_utils.sh | 156 +++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 src/lib/patch_track_utils/contact_utils.sh diff --git a/src/lib/patch_track_utils/contact_utils.sh b/src/lib/patch_track_utils/contact_utils.sh new file mode 100644 index 000000000..509aa7d55 --- /dev/null +++ b/src/lib/patch_track_utils/contact_utils.sh @@ -0,0 +1,156 @@ +include "${KW_LIB_DIR}/lib/kw_db.sh" + +declare -Ag condition_array +declare -gr DATABASE_EMAIL_CONTACT_TABLE='email_contact' + +function insert_email_contact() +{ + local _contact_name="$1" + local _contact_email="$2" + local columns + local values + local sql_operation_result + local ret + + if [[ -z "$_contact_name" || -z "$_contact_email" ]]; then + complain "($LINENO): missing mandatory field(s) for insert_email_contact (name, email)" + return 22 # EINVAL + fi + + columns='"name", "email"' + values="'${_contact_name}', '${_contact_email}'" + + sql_operation_result=$(insert_into "$DATABASE_EMAIL_CONTACT_TABLE" \ + "($columns)" \ + "($values)") + ret="$?" + + if [[ "$ret" -eq 2 || "$ret" -eq 61 ]]; then + complain "$sql_operation_result" + return 22 # EINVAL + elif [[ "$ret" -ne 0 ]]; then + complain "($LINENO):" $'Error while trying to insert new email contact:\n'"$sql_operation_result" + return 22 # EINVAL + fi + + return 0 +} + +function check_email_contact_existence_by_unique_attributes() +{ + local _contact_email="$1" + local sql_operation_result + local ret + + if [[ -z "$_contact_email" ]]; then + complain "($LINENO): empty unique attribute for check_email_contact_existence_by_unique_attributes (email)" + return 22 # EINVAL + fi + + condition_array['email']="${_contact_email}" + + sql_operation_result="$(check_existence "$DATABASE_EMAIL_CONTACT_TABLE" 'condition_array')" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$sql_operation_result" + return "$ret" + fi + + if [[ -z "$sql_operation_result" ]]; then + complain "($LINENO): check existence returned null" + return 61 # ENODATA + fi + + printf '%s\n' "$sql_operation_result" + return 0 +} + +function get_email_contact_info() +{ + local _contact_infos="$1" + local -n _contact_info_condition_array="$2" + local sql_operation_result + local ret + + sql_operation_result="$(get_database_table_info "$DATABASE_EMAIL_CONTACT_TABLE" "$_contact_infos" '_contact_info_condition_array')" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$sql_operation_result" + return 22 # EINVAL + fi + + printf '%s\n' "$sql_operation_result" + return 0 +} + +function get_email_contact_info_by_unique_attributes() +{ + local _contact_infos="$1" + local _contact_email="$2" + local sql_operation_result + local ret + + if [[ -z "$_contact_email" ]]; then + complain "($LINENO): empty unique attribute for get_email_contact_info_by_unique_attributes (email)" + return 22 # EINVAL + fi + + condition_array['email']="${_contact_email}" + + sql_operation_result="$(get_email_contact_info "$_contact_infos" 'condition_array')" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$sql_operation_result" + return "$ret" + fi + + printf '%s\n' "$sql_operation_result" + return 0 +} + +function get_or_create_email_contact() +{ + local _contact_name="$1" + local _contact_email="$2" + local contact_existent_result + local insert_contact_result + local get_contact_id_result + local ret + + if [[ -z "$_contact_name" || -z "$_contact_email" ]]; then + complain "($LINENO): missing mandatory field(s) for get_or_create_email_contact" + return 22 # EINVAL + fi + + contact_existent_result="$(check_email_contact_existence_by_unique_attributes "$_contact_email")" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$contact_existent_result" + return "$ret" + fi + + if [[ "$contact_existent_result" -eq 0 ]]; then + insert_contact_result=$(insert_email_contact "$_contact_name" "$_contact_email") + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$insert_contact_result" + return "$ret" + fi + fi + + get_contact_id_result=$(get_email_contact_info_by_unique_attributes 'id' "$_contact_email") + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$get_contact_id_result" + return "$ret" + fi + + printf '%s\n' "$get_contact_id_result" + return 0 +} From 3d83cf10f194dd9d7c7596b540d08b383a5c1435 Mon Sep 17 00:00:00 2001 From: JGBSouza Date: Mon, 22 Dec 2025 22:25:34 -0300 Subject: [PATCH 23/33] feat: add repository_utils functions Signed-off-by: JGBSouza --- src/lib/patch_track_utils/repository_utils.sh | 307 ++++++++++++++++++ 1 file changed, 307 insertions(+) create mode 100644 src/lib/patch_track_utils/repository_utils.sh diff --git a/src/lib/patch_track_utils/repository_utils.sh b/src/lib/patch_track_utils/repository_utils.sh new file mode 100644 index 000000000..3c8816284 --- /dev/null +++ b/src/lib/patch_track_utils/repository_utils.sh @@ -0,0 +1,307 @@ +include "${KW_LIB_DIR}/lib/kw_db.sh" +include "${KW_LIB_DIR}/lib/patch_track_utils/contact_utils.sh" + +declare -gr DATABASE_REPOSITORY_TABLE='repository' +declare -gr DATABASE_REPO_MAINTAINER_TABLE='repository_maintainer' +declare -Ag condition_array + +function insert_repository() +{ + local _repository_name="$1" + local _origin_url="$2" + local columns + local values + local sql_operation_result + local ret + + if [[ -z "$_repository_name" || -z "$_origin_url" ]]; then + complain "($LINENO): missing mandatory field(s) for insert_repository (name, url)" + return 22 # EINVAL + fi + + columns='"name", "origin_url"' + values="'${_repository_name}', '${_origin_url}'" + + sql_operation_result=$(insert_into "$DATABASE_REPOSITORY_TABLE" \ + "($columns)" \ + "($values)") + ret="$?" + + if [[ "$ret" -eq 2 || "$ret" -eq 61 ]]; then + complain "$sql_operation_result" + return 22 # EINVAL + elif [[ "$ret" -ne 0 ]]; then + complain "($LINENO):" $'Error while trying to insert new repository:\n'"$sql_operation_result" + return 22 # EINVAL + fi + + return 0 +} + +function check_repository_existence_by_unique_attributes() +{ + local _origin_url="$1" + local sql_operation_result + local ret + + if [[ -z "$_origin_url" ]]; then + complain "($LINENO): empty unique attributes for check_repository_existence_by_unique_attributes" + return 22 # EINVAL + fi + + condition_array['origin_url']="${_origin_url}" + + sql_operation_result="$(check_existence "$DATABASE_REPOSITORY_TABLE" 'condition_array')" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$sql_operation_result" + return "$ret" + fi + + if [[ -z "$sql_operation_result" ]]; then + complain "($LINENO): check existence returned null" + return 61 # ENODATA + fi + + printf '%s\n' "$sql_operation_result" + return 0 +} + +function get_repository_info() +{ + local _repository_infos="$1" + local -n _repo_info_condition_array="$2" + local sql_operation_result + local ret + + sql_operation_result="$(get_database_table_info "$DATABASE_REPOSITORY_TABLE" "$_repository_infos" '_repo_info_condition_array')" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$sql_operation_result" + return 22 # EINVAL + fi + + printf '%s\n' "$sql_operation_result" + return 0 +} + +function get_repository_info_by_unique_attributes() +{ + local _repository_infos="$1" + local _origin_url="$2" + local sql_operation_result + local ret + + if [[ -z "$_origin_url" ]]; then + complain "($LINENO): empty unique attributes for get_repository_info_by_unique_attributes" + return 22 # EINVAL + fi + + condition_array['origin_url']="${_origin_url}" + + sql_operation_result="$(get_repository_info "$_repository_infos" 'condition_array')" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$sql_operation_result" + return "$ret" + fi + + printf '%s\n' "$sql_operation_result" + return 0 +} + +function get_or_create_repository() +{ + local _repository_name="$1" + local _origin_url="$2" + local repo_existent_result + local insert_repo_result + local get_repo_id_result + local ret + + if [[ -z "$_repository_name" || -z "$_origin_url" ]]; then + complain "($LINENO): missing mandatory field(s) for get_or_create_repository" + return 22 # EINVAL + fi + + repo_existent_result="$(check_repository_existence_by_unique_attributes "$_origin_url")" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$repo_existent_result" + return "$ret" + fi + + if [[ "$repo_existent_result" -eq 0 ]]; then + insert_repo_result=$(insert_repository "$_repository_name" "$_origin_url") + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$insert_repo_result" + return "$ret" + fi + fi + + get_repo_id_result=$(get_repository_info_by_unique_attributes 'id' "$_origin_url") + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$get_repo_id_result" + return "$ret" + fi + + printf '%s\n' "$get_repo_id_result" + return 0 +} + +function insert_repository_maintainer() +{ + local _repository_id="$1" + local _contact_id="$2" + local columns + local values + local sql_operation_result + local ret + + if [[ -z "$_repository_id" || -z "$_contact_id" ]]; then + complain "($LINENO): missing mandatory field(s) for insert_repository_maintainer" + return 22 # EINVAL + fi + + columns='"repository_id", "contact_id"' + values="'${_repository_id}', '${_contact_id}'" + + sql_operation_result=$(insert_into "$DATABASE_REPO_MAINTAINER_TABLE" \ + "($columns)" \ + "($values)") + ret="$?" + + if [[ "$ret" -eq 2 || "$ret" -eq 61 ]]; then + complain "$sql_operation_result" + return 22 # EINVAL + elif [[ "$ret" -ne 0 ]]; then + complain "($LINENO):" $'Error while trying to insert new repository maintainer relation:\n'"$sql_operation_result" + return 22 # EINVAL + fi + + return 0 +} + +function check_repository_maintainer_existence() +{ + local _repository_id="$1" + local _contact_id="$2" + local sql_operation_result + local ret + + if [[ -z "$_repository_id" || -z "$_contact_id" ]]; then + complain "($LINENO): missing mandatory fields for check_repository_maintainer_existence" + return 22 # EINVAL + fi + + condition_array['repository_id']="${_repository_id}" + condition_array['contact_id']="${_contact_id}" + + sql_operation_result="$(check_existence "$DATABASE_REPO_MAINTAINER_TABLE" 'condition_array')" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$sql_operation_result" + return "$ret" + fi + + if [[ -z "$sql_operation_result" ]]; then + complain "($LINENO): check existence returned null" + return 61 # ENODATA + fi + + printf '%s\n' "$sql_operation_result" + return 0 +} + +function get_or_create_repository_maintainer() +{ + local _repository_id="$1" + local _maintainer_name="$2" + local _maintainer_email="$3" + local contact_id + local maintainer_relation_exists_result + local ret + + if [[ -z "$_repository_id" || -z "$_maintainer_name" || -z "$_maintainer_email" ]]; then + complain "($LINENO): missing mandatory field(s) for get_or_create_repository_maintainer" + return 22 # EINVAL + fi + + contact_id=$(get_or_create_email_contact "$_maintainer_name" "$_maintainer_email") + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$contact_id" + return "$ret" + fi + + maintainer_relation_exists_result="$(check_repository_maintainer_existence "$_repository_id" "$contact_id")" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$maintainer_relation_exists_result" + return "$ret" + fi + + if [[ "$maintainer_relation_exists_result" -eq 0 ]]; then + local insert_result + + insert_result=$(insert_repository_maintainer "$_repository_id" "$contact_id") + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$insert_result" + return "$ret" + fi + fi + + printf '%s\n' "$contact_id" + return 0 +} + +function get_maintainers_info() +{ + local _maintainers_infos="$1" + local -n _maintainers_condition_array="$2" + local sql_operation_result + local ret + + sql_operation_result="$(get_database_table_info "$DATABASE_REPO_MAINTAINER_TABLE" "$_maintainers_infos" '_maintainers_condition_array')" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$sql_operation_result" + return 22 # EINVAL + fi + + printf '%s\n' "$sql_operation_result" + return 0 +} + +function get_repository_maintainers_info() +{ + local _repository_maintainers_infos="$1" + local -n _repository_maintainers_condition_array="$2" + local sql_operation_result + local ret + + sql_operation_result="$(get_database_table_info "$DATABASE_REPO_MAINTAINER_TABLE" "$_repository_maintainers_infos" '_repository_maintainers_condition_array')" + ret="$?" + + if [[ "$ret" -ne 0 ]]; then + complain "$sql_operation_result" + return 22 # EINVAL + fi + + printf '%s\n' "$sql_operation_result" + return 0 +} From 74d9f3848990aa4fb52b389432a135e707810d37 Mon Sep 17 00:00:00 2001 From: JGBSouza Date: Mon, 22 Dec 2025 22:25:55 -0300 Subject: [PATCH 24/33] feat: add file with template configs for mutt Signed-off-by: JGBSouza --- etc/patch_track_mutt.config | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 etc/patch_track_mutt.config diff --git a/etc/patch_track_mutt.config b/etc/patch_track_mutt.config new file mode 100644 index 000000000..a985870b5 --- /dev/null +++ b/etc/patch_track_mutt.config @@ -0,0 +1,8 @@ +# Mutt options to be used with patch-track +imap_user= +imap_pass= + +folder="imaps://imap.gmail.com" +spoolfile="+[Gmail]/Todos os e-mails" +record="+[Gmail]/Sent Mail" +mbox_type="Maildir" From d9e5a0738ff8e8a1aaefd8bd7a4ec052c1a45057 Mon Sep 17 00:00:00 2001 From: JGBSouza Date: Sat, 27 Dec 2025 00:51:02 -0300 Subject: [PATCH 25/33] fix: remove arquivos desnecessarios Signed-off-by: JoaoGBSouza --- message1.eml | 0 output.log | 0 .../patch_track_utils/patch_track_utils.sh | 625 ------------------ 3 files changed, 625 deletions(-) delete mode 100644 message1.eml delete mode 100644 output.log delete mode 100644 src/lib/patch_track_utils/patch_track_utils.sh diff --git a/message1.eml b/message1.eml deleted file mode 100644 index e69de29bb..000000000 diff --git a/output.log b/output.log deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/lib/patch_track_utils/patch_track_utils.sh b/src/lib/patch_track_utils/patch_track_utils.sh deleted file mode 100644 index 721779b58..000000000 --- a/src/lib/patch_track_utils/patch_track_utils.sh +++ /dev/null @@ -1,625 +0,0 @@ -include "${KW_LIB_DIR}/lib/kwdb.sh" - -declare -gr DATABASE_PATCH_TABLE='patch' -declare -Ag condition_array - -function insert_patch() -{ - local _patch_title="$1" - local _last_patch_id="$2" - local _patch_author="$3" - local _contribution_id="$4" - local _submission_id="$5" - local _commit_hash="$6" - local _patch_version="$7" - local get_last_patch_version_result - local sql_operation_result - local ret - - sql_operation_result=$(insert_into "$DATABASE_PATCH_TABLE" \ - '("title", "last_patch_id", "author", "contribution_id", "_submission_id", "version", "_commit_hash")' \ - "('${patch_title}', '${_last_patch_id}', '${_patch_author}', '${_contribution_id}', '${_submission_id}',\ - '${_patch_version}', '${_commit_hash}')"\ - '' 'VERBOSE') - ret="$?" - - if [[ "$ret" -eq 2 || "$ret" -eq 61 ]]; then - complain "$sql_operation_result" - return 22 # EINVAL - elif [[ "$ret" -ne 0 ]]; then - complain "($LINENO):" $'Error while trying to insert new patch:\n'"${sql_operation_result}" - return 22 # EINVAL - fi - - return 0 -} - -function insert_contribution() -{ - local _contribution_title="$1" - local _author_email="$2" - local _repository_id="$3" - - sql_operation_result=$(insert_into "$DATABASE_PATCH_TABLE" \ - '("title", "author_email", "repository_id")' \ - "('${_contribution_title}', '${_author_email}', '${_repository_id}')" '' 'VERBOSE') - - ret="$?" - - if [[ "$ret" -eq 2 || "$ret" -eq 61 ]]; then - complain "$sql_operation_result" - return 22 # EINVAL - elif [[ "$ret" -ne 0 ]]; then - complain "($LINENO):" $'Error while tr lhklying to insert new patch:\n'"${sql_operation_result}" - return 22 # EINVAL - fi - - return 0 -} - -function insert_submission() -{ - local _contribution_id="$1" - local _send_by="$2" - local sql_operation_result - local ret - - sql_operation_result=$(insert_into "$DATABASE_SUBMISSION_TABLE" '("contribution_id", "send_by")' "('${patch_title}', '${_send_by}')" '' 'VERBOSE') - ret="$?" - - if [[ "$ret" -eq 2 || "$ret" -eq 61 ]]; then - complain "$sql_operation_result" - return 22 # EINVAL - elif [[ "$ret" -ne 0 ]]; then - complain "($LINENO):" $'Error while trying to insert new patch:\n'"${sql_operation_result}" - return 22 # EINVAL - fi - - return 0 -} - -function replace_patch() -{ - local _patch_title="$1" - local _last_patch_id="$2" - local _patch_author="$3" - local _contribution_id="$4" - local _submission_id="$5" - local _commit_hash="$6" - - get_patch_version_result="$(decide_patch_version "$_last_patch_id")" - ret="$?" - - if [[ "$ret" -new 0 ]]; then - complain "$get_patch_version_result" - return 22 # EINVAL - fi - - new_patch_result="$(new_patch "$_patch_title" "$_last_patch_id" "$_patch_author" "$_contribution_id"\ - "$_submission_id" "$_commit_hash" "$get_patch_version_result")" - ret="$?" - - if [[ "$ret" -new 0 ]]; then - complain "$new_patch_result" - return 22 # EINVAL - fi - - printf "$new_patch_result" - return 0 -} - -function new_patch() -{ - # TODO NEW PATCH - # TODO CREATE_NEW_PATCH_OR_GET_EXISTENT - # CRIAR NEW_SUBMISSION - # CRIAR SUBMETER PATCH - # ALTERAR A ENTIDADE QUE RELACIONA PATCH E SUBMISSION PARA TER CAMPOS COMO MESSAGE-ID - # OUTDATED DO PATCH APENAS QUANDO NOVO PATCH SOBRESCREVER ELE OU ELE FOR "DESLIGADO DA SÉRIE" - # CHECAR ESTADO DO PATCH SEMPRE OLHA PARA ÚLTIMA SUBMISSÃO DELE - - local _patch_title="$1" - local _last_patch_id="$2" - local _patch_author="$3" - local _contribution_id="$4" - local _submission_id="$5" - local _commit_hash="$6" - local _patch_version="$7" - local ret - local patch_existent_result - local insert_contribution_result - local get_contribution_id_result - - if [[ -z "$_patch_version" ]]; then - _patch_version=1 - fi - - patch_existent_result="$(check_patch_existence_by_unique_attributes "$contribution_name")" - ret="$?" - - if [[ "$ret" -ne 0 ]]; then - complain "$patch_existent_result" - return "$ret" # EINVAL - fi - - if [[ "$patch_existent_result" -ne 0 ]]; then - complain "($LINENO): error while trying to create new patch serie ${contribution_name}, patch serie already exists" - return 22 # EINVAL - fi - - insert_contribution_result=$(insert_patch "$_contribution_title" "$_author_email" "$_repository_id") - ret="$?" - - if [[ "$ret" -ne 0 ]]; then - complain "$insert_contribution_result" - return "$ret" # EINVAL - fi - - get_contribution_id_result=$(get_patch_info_by_commit_hash 'id' "$_commit_hash") - ret="$?" - - if [[ "$ret" -ne 0 ]]; then - complain "$get_contribution_id_result" - return "$ret" # EINVAL - fi - - printf '%s\n' "$get_contribution_id_result" - return 0 -} - -function new_contribution() -{ - local _contribution_title="$1" - local _author_email="$2" - local _repository_id="$3" - local sql_operation_result - local insert_contribution_result - local get_contribution_id_result - - patch_existent_result="$(check_contribution_existence_by_title "$contribution_name")" - - ret="$?" - - if [[ "$ret" -ne 0 ]]; then - complain "$patch_existent_result" - return "$ret" # EINVAL - fi - - if [[ "$patch_existent_result" -ne 0 ]]; then - complain "($LINENO): error while trying to create new patch serie ${contribution_name}, patch serie already exists" - return 22 # EINVAL - fi - - insert_contribution_result=$(insert_contribution "$_contribution_title" "$_author_email" "$_repository_id") - ret="$?" - - if [[ "$ret" -ne 0 ]]; then - complain "$insert_contribution_result" - return "$ret" # EINVAL - fi - - get_contribution_id_result=$(get_contribution_info_by_title "$_contribution_title") - ret="$?" - - if [[ "$ret" -ne 0 ]]; then - complain "$get_contribution_id_result" - return "$ret" # EINVAL - fi - - printf '%s\n' "$get_contribution_id_result" - return 0 -} - -function new_submission() -{ - local _submission_author="$3" - local _contribution_id="$4" - local ret - local patch_existent_result - local insert_submission_result - local get_submission_id_result - - insert_contribution_result=$(insert_submission "$_contribution_id" "$_submission_author") - ret="$?" - - if [[ "$ret" -ne 0 ]]; then - complain "$insert_contribution_result" - return "$ret" # EINVAL - fi - - get_submission_id_result=$(get_last_submission_by_contribution_id 'id' "$_commit_hash") - ret="$?" - - if [[ "$ret" -ne 0 ]]; then - complain "$get_contribution_id_result" - return "$ret" # EINVAL - fi - - printf '%s\n' "$get_contribution_id_result" - return 0 -} - -function get_patch_info_by_id() -{ - local _patch_infos="$1" - local _patch_id="$2" - local sql_operation_result - local ret - - condition_array=(['id']="${_patch_id}") - sql_operation_result="$(get_patch_info "$_patch_infos" 'condition_array')" - ret="$?" - - if [[ "$ret" -ne 0 ]]; then - complain "$sql_operation_result" - return "$ret" # EINVAL - fi - - printf '%s\n' "$sql_operation_result" - return 0 -} - -function get_last_submission_infos_by_contribution_id() -{ - local _submission_infos="$1" - local _contribution_id="$2" - local sql_operation_result - local ret - - condition_array=(['contribution_id']="${_contribution_id}") - sql_operation_result="$(get_submission_info "$_submission_infos" 'condition_array' 'ID DESC' '1')" - ret="$?" - - if [[ "$ret" -ne 0 ]]; then - complain "$sql_operation_result" - return "$ret" # EINVAL - fi - - printf '%s\n' "$sql_operation_result" - return 0 -} - -function get_submission_info() -{ - local _submission_infos="$1" - local -n _condition_array="$2" - local _order_by="$3" - local _limit="$4" - - sql_operation_result="$(get_database_table_info "$DATABASE_SUBMISSION_TABLE" "$_patch_infos" '_condition_array' "$_order_by" "$_limit")" - ret="$?" - - if [[ "$ret" -ne 0 ]]; then - complain "$sql_operation_result" - return "$ret" # EINVAL - fi - - printf '%s\n' "$sql_operation_result" - return 0 -} - -function new_patch_submission() -{ - local _patch_id="$1" - local _submission_id="$2" - local _message_id="$3" - - if [[ -z "$_patch_version" ]]; then - _patch_version=1 - fi - - patch_existent_result="$(check_patch_submission_existence_by_unique_attributes "$_patch_id" "$_submission_id" "$_message_id")" - ret="$?" - - if [[ "$ret" -ne 0 ]]; then - complain "$patch_existent_result" - return "$ret" # EINVAL - fi - - if [[ "$patch_existent_result" -ne 0 ]]; then - complain "($LINENO): error while trying to create new patch serie ${contribution_name}, patch serie already exists" - return 22 # EINVAL - fi - - insert_contribution_result=$(insert_patch "$_contribution_title" "$_author_email" "$_repository_id") - ret="$?" - - if [[ "$ret" -ne 0 ]]; then - complain "$insert_contribution_result" - return "$ret" # EINVAL - fi - - get_contribution_id_result=$(get_patch_info_by_commit_hash 'id' "$_commit_hash") - ret="$?" - - if [[ "$ret" -ne 0 ]]; then - complain "$get_contribution_id_result" - return "$ret" # EINVAL - fi - - printf '%s\n' "$get_contribution_id_result" - return 0 -} - -function check_patch_submission_existence_by_unique_attributes() -{ - local _patch_id="$1" - local _submission_id="$2" - local _message_id="$3" - - # CHECK campos não null, ex, se commit_hash é null não é seguro considerar que é o mesmo patch - condition_array=(['patch_id']="${_patch_id}") - condition_array=(['_submission_id']="${_submission_id}") - condition_array=(['_message_id']="${_message_id}") - - sql_operation_result="$(check_patch_submission_existence '_contribution_title')" - ret="$?" - - if [[ "$ret" -ne 0 ]]; then - complain "${sql_operation_result}" - return "$ret" # EINVAL - fi - - printf '%s\n' "${sql_operation_result}" - - return 0 -} - -function check_patch_submission_existence() -{ - local _condition_array="$1" - local sql_operation_result - local ret - - sql_operation_result="$(get_patch_submission_info 'id' '_condition_array')" - ret="$?" - - if [[ "$ret" -ne 0 && "$ret" -ne 61 ]]; then - complain "${sql_operation_result}" - return "$ret" # EINVAL - fi - - if [[ "$ret" -eq 61 ]]; then - printf '%s\n' 0 - else - printf '%s\n' 1 - fi - - return 0 -} - -function get_patch_submission_info() -{ - local _patch_submission_infos="$1" - local -n _condition_array="$2" - - sql_operation_result="$(get_database_table_info "$DATABASE_PATCH_SUBMISSION_TABLE" "$_patch_submission_infos" '_condition_array')" - ret="$?" - - if [[ "$ret" -ne 0 ]]; then - complain "$sql_operation_result" - return "$ret" # EINVAL - fi - - printf '%s\n' "$sql_operation_result" - return 0 -} - -function insert_patch_submission() -{ - local _patch_id="$1" - local _submission_id="$2" - local _message_id="$3" - local ret - - sql_operation_result=$(insert_into "$DATABASE_PATCH_SUBMISSION_TABLE" '("patch_id", "submission_id", "message_id")' "('${_patch_id}', '${_submission_id}', '${_message_id}')" '' 'VERBOSE') - ret="$?" - - if [[ "$ret" -eq 2 || "$ret" -eq 61 ]]; then - complain "$sql_operation_result" - return 22 # EINVAL - elif [[ "$ret" -ne 0 ]]; then - complain "($LINENO):" $'Error while trying to insert new patch:\n'"${sql_operation_result}" - return 22 # EINVAL - fi - - return 0 -} - -function get_patch_info_by_commit_hash() -{ - local _patch_infos="$1" - local _commit_hash="$2" - local sql_operation_result - local ret - - condition_array=(['commit_hash']="${_commit_hash}") - sql_operation_result="$(get_patch_info "$_patch_infos" 'condition_array')" - ret="$?" - - if [[ "$ret" -ne 0 ]]; then - complain "$sql_operation_result" - return "$ret" # EINVAL - fi - - printf '%s\n' "$sql_operation_result" - return 0 -} - -function get_patch_info() -{ - local _patch_infos="$1" - local -n _condition_array="$2" - - sql_operation_result="$(get_database_table_info "$DATABASE_PATCH_TABLE" "$_patch_infos" '' '_condition_array' '' 'VERBOSE')" - ret="$?" - - if [[ "$ret" -ne 0 ]]; then - complain "$sql_operation_result" - return "$ret" # EINVAL - fi - - printf '%s\n' "$sql_operation_result" - return 0 -} - -function check_patch_existence_by_unique_attributes() -{ - local _patch_title="$1" - local _patch_author="$2" - local _contribution_id="$3" - local _commit_hash="$4" - - # CHECK campos não null, ex, se commit_hash é null não é seguro considerar que é o mesmo patch - condition_array=(['title']="${_patch_title}") - condition_array=(['author']="${_patch_author}") - condition_array=(['contribution_id']="${_contribution_id}") - condition_array=(['commit_hash']="${_commit_hash}") - - sql_operation_result="$(check_patch_existence '_contribution_title')" - ret="$?" - - if [[ "$ret" -ne 0 ]]; then - complain "${sql_operation_result}" - return "$ret" # EINVAL - fi - - printf '%s\n' "${sql_operation_result}" - - return 0 -} - -function check_patch_existence() -{ - local _condition_array="$1" - local sql_operation_result - local ret - - sql_operation_result="$(get_patch_info '_condition_array')" - ret="$?" - - if [[ "$ret" -ne 0 && "$ret" -ne 61 ]]; then - complain "${sql_operation_result}" - return "$ret" # EINVAL - fi - - if [[ "$ret" -eq 61 ]]; then - printf '%s\n' 0 - else - printf '%s\n' 1 - fi - - return 0 -} - -function check_contribution_existence_by_title() -{ - local _contribution_title="$1" - local sql_operation_result - local ret - - sql_operation_result="$(get_contribution_info_by_title "$_contribution_title")" - ret="$?" - - if [[ "$ret" -ne 0 && "$ret" -ne 61 ]]; then - complain "${sql_operation_result}" - return "$ret" # EINVAL - fi - - if [[ "$ret" -eq 61 ]]; then - printf '%s\n' 0 - else - printf '%s\n' 1 - fi - - return 0 -} - -function get_contribution_info_by_title() -{ - local _contribution_title="$1" - local sql_operation_result - local ret - - condition_array=(['title']="${_contribution_title}") - - sql_operation_result="$(get_contribution_info 'condition_array')" - ret="$?" - - if [[ "$ret" -ne 0 && "$ret" -ne 61 ]]; then - complain "${sql_operation_result}" - return "$ret" # EINVAL - fi - - if [[ "$ret" -eq 61 ]]; then - printf '%s\n' 0 - else - printf '%s\n' 1 - fi - - return 0 -} - -function get_contribution_info() -{ - local _condition_array="$1" - local "$_contribution_infos" - local sql_operation_result - local ret - - sql_operation_result="$(get_database_table_info "$DATABASE_contribution_TABLE" "$_contribution_infos" '' 'condition_array' '' 'VERBOSE')" - ret="$?" - - if [[ "$ret" -ne 0 ]]; then - complain "${get_last_patch_version_result}" - return 22 # EINVAL - fi - - printf '%s\n' "$sql_operation_result" - return 0 -} - -function get_database_table_info() -{ - local _database_table_name="$1" - local _contribution_infos="$2" - local -n _condition_array="$3" - local _order_by="${4:-''}" - local _limit="${5:-''}" - - sql_operation_result="$(select_from "$DATABASE_CONTRIBUTION_TABLE" "$_contribution_infos" '' '_condition_array' "$_order_by" "$_limit" 'VERBOSE')" - ret="$?" - - if [[ "$ret" -eq 2 || "$ret" -eq 61 ]]; then - complain "$sql_operation_result" - return 22 # EINVAL - elif [[ "$ret" -ne 0 ]]; then - complain "($LINENO): Error while trying to get ${_database_table_name} info from the database with the command:"$'\n'"${sql_operation_result}" - return 22 # EINVAL - elif [[ -z "$sql_operation_resul" ]]; then - complain "($LINENO): Error while trying to get ${_database_table_name} info from the database: no patch found for id: ${_patch_id}" - return 61 # ENODATA - fi -} - -function decide_patch_version() -{ - local _last_patch_id="$1" - local get_last_patch_version_result - local ret - - if [[ -z "$_last_patch_id" ]]; then - return 22 # EINVAL - fi - - get_last_patch_version_result="$(get_patch_info_by_id 'version' "$_last_patch_id")" - ret="$?" - - if [[ "$ret" -ne 0 ]]; then - complain "${get_last_patch_version_result}" - return 22 # EINVAL - fi - - printf '%d\n' $((get_last_patch_version_result + 1)) - return 0 -} \ No newline at end of file From 1d8c268d131428f09ba9695412aec4ff714650ff Mon Sep 17 00:00:00 2001 From: JGBSouza Date: Sat, 27 Dec 2025 00:51:32 -0300 Subject: [PATCH 26/33] fix: adiciona nova documentacao para o kw patch track Signed-off-by: JoaoGBSouza --- documentation/man/features/kw-patch-track.rst | 91 +++++++++++++------ 1 file changed, 63 insertions(+), 28 deletions(-) diff --git a/documentation/man/features/kw-patch-track.rst b/documentation/man/features/kw-patch-track.rst index e4fc99c1a..ac80854f9 100644 --- a/documentation/man/features/kw-patch-track.rst +++ b/documentation/man/features/kw-patch-track.rst @@ -6,54 +6,89 @@ kw-patch-track - Track and display patches sent with kw-send-patch SYNOPSIS ======== -| *kw patch-track* +| *kw patch-track* [OPTIONS] DESCRIPTION =========== -The `kw patch-track` feature deal with tracking the patches submissions using the -feature `kw send-patch`, keeping notes of it's shipping date, status, title, -making it possible to view shipments, and update their status as needed. +The `kw patch-track` feature manages local tracking of patch submissions sent via +`kw send-patch`. It works by scraping metadata from submission logs to populate +a local database, keeping record of shipping dates, status, and titles. + +Beyond simple visualization, it provides a heuristic engine to automatically +update patch statuses based on mailing list activity and allows for deep +integration with the `mutt` email client to facilitate review follow-ups. OPTIONS ======= --d, \--dashboard: - Displays the patches dashboard, showing the patches id, created date-time, - status and title. +--show-patches: + Displays the patches dashboard in chronological order, showing patch ID, + creation date, current status, and title. + +-d, --show-contributions: + Displays the contributions dashboard, grouping sets of patches into higher-level + contribution entities. + +-f , --from , -a , --after : + Filters the dashboard to display only patches created on or after the specified date. + +-b , --before : + Filters the dashboard to display only patches created before the specified date. + +--id : + Specifies a unique patch ID for status modification. + +-s , --set-status : + Manually sets a new status for a specific patch (specified via --id). + Available statuses: SUBMITTED, REVIEWED, APPROVED, MERGED, REJECTED. --a , \--after : - Specify a date to look up for the patches after this date. +-u, --update: + Triggers the automated heuristic engine to update patch statuses by analyzing + associated email threads. --b ', \--before : - Specify a date to look up for the patches before this date. +-c , --contribution-id : + Specifies the contribution ID to be used with repository or mutt operations. --f , \--from : - Specify a date to look up for the patches from this date. +--set-repository : + Associates a repository name and its origin URL to a contribution. + Requires --contribution-id. -\--id '' - Specify a patch id +-r , --repository-id : + Specifies the repository ID to be used with maintainer operations. ---s='', \--set-status='' - Set a new status for an specific patch as SENT, APPROVED, - REJECTED, MERGED or REVIEWED. +-m , --set-maintainer : + Associates a maintainer's name and email to a specific repository. + Requires --repository-id. + +-o, --open-contribution: + Opens the email thread associated with a contribution directly in the `mutt` + terminal client. Requires --contribution-id. EXAMPLES ======== -To check your patches dashboard use: +To view the chronological dashboard of all patches: + + kw patch-track --show-patches + +To view grouped contributions: + + kw patch-track --show-contributions + +To filter patches submitted within a specific timeframe: - kw patch-track --dashboard + kw patch-track --show-patches --after 2023-01-01 --before 2023-12-31 -For checking your patches within an specific date use: +To manually update a patch status after a maintainer's feedback: - kw patch-track --dashboard --from + kw patch-track --id 42 --set-status APPROVED -To check your patches after an specific date: +To run the automatic status update heuristics: - kw patch-track --dashboard --after + kw patch-track --update -And to check the patches before an specific date, use: +To link a contribution to a specific subsystem repository: - kw patch-track --dashboard --before + kw patch-track --contribution-id 10 --set-repository "linux-usb:https://git.kernel.org/..." -To change the patch status for an specific patch +To open a specific contribution thread in mutt for review: - kw patch-track --id --set-status \ No newline at end of file + kw patch-track --contribution-id 10 --open-contribution \ No newline at end of file From 43561f404ec3b878883301d2eaf690ea5f5b6287 Mon Sep 17 00:00:00 2001 From: JGBSouza Date: Sat, 27 Dec 2025 00:51:51 -0300 Subject: [PATCH 27/33] fix: remove trechos de codigo desnecessarios Signed-off-by: JoaoGBSouza --- .../dependencies/fedora.dependencies | 1 - src/patch_track.sh | 39 +++---------------- 2 files changed, 6 insertions(+), 34 deletions(-) diff --git a/documentation/dependencies/fedora.dependencies b/documentation/dependencies/fedora.dependencies index f9a6d43b6..84d541d41 100644 --- a/documentation/dependencies/fedora.dependencies +++ b/documentation/dependencies/fedora.dependencies @@ -29,6 +29,5 @@ pciutils libnotify python3-sphinx mutt -mutt xvfb xterm \ No newline at end of file diff --git a/src/patch_track.sh b/src/patch_track.sh index 39bcca6cb..52bd8a61c 100644 --- a/src/patch_track.sh +++ b/src/patch_track.sh @@ -287,9 +287,6 @@ function register_contribution() email_from=$(cat "$_send_email_log_file" | grep -m 1 '^MAIL FROM:' | tail -n 1 | cut -d'<' -f2 | cut -d'>' -f1) - #echo "$email_from" - #echo "começou contribution" > /dev/tty - get_or_create_contribution_result="$(get_or_create_contribution "$contribution_name" "$email_from" '')" ret="$?" @@ -298,7 +295,6 @@ function register_contribution() return "$ret" fi - #echo "registrou contribution" > /dev/tty printf '%s\n' "$get_or_create_contribution_result" return 0 } @@ -331,7 +327,6 @@ function register_patches() patches_output_array["$get_or_create_patch_result"]='' ((patch_num++)) - #echo "${patch_metadata["subject"]}" fi for patch_path in "${patch_cache}/"*; do @@ -349,8 +344,6 @@ function register_patches() fi patches_output_array["$get_or_create_patch_result"]="${patch_metadata["message_id"]}" - #echo "${patch_metadata["subject"]}" - #echo "${patch_metadata["commit_hash"]}" ((patch_num++)) fi done @@ -396,18 +389,6 @@ function update_contribution_status() get_patch_submission_info 'patch_id, submission_id, message_id' 'condition_array' )" || return 22 - #condition_array=(['repository_id']="$contribution_repository_id") - #maintainers_ids_result="$(get_maintainers_info 'contact_id' 'condition_array')" - - #for maintainer_id in $maintainers_ids_result; do - # condition_array=(['id']="$maintainer_id") - # maintainer_email_result="$(get_email_contact_info 'email' 'condition_array')" - - # maintainers_emails+=("maintainer_email_result") - #done - - #echo "$patch_submission_ids" - for patch_id in $patch_submission_infos; do IFS='|' read -r patch_id submission_id message_id <<< "$patch_submission_infos" @@ -421,8 +402,6 @@ function update_contribution_status() IFS='|' read -r commit_hash status <<< "$patch_infos" - #echo "AAAAAAAAAA $patch_infos" AA "$commit_hash" AA "$status" AA "$message_id" AA "$send_by" - final_status="$( update_patch_status \ "$patch_id" \ @@ -515,13 +494,10 @@ function register_patch_submissions() local submission_id="$1" local -n patches_message_id="$2" - #echo "começou patch submission register " > /dev/tty - for patch_id in "${!patches_message_id[@]}"; do message_id="${patches_message_id[$patch_id]}" create_patch_submission_result="$(create_patch_submission "$patch_id" "$submission_id" "$message_id")" ret="$?" - #echo "${patch_id} EE ${message_id}" > /dev/tty if [[ "$ret" -ne 0 ]]; then complain "$create_patch_submission_result" return "$ret" @@ -580,7 +556,6 @@ function extract_patch_from_file() local -n _output_metadata_array="$3" local header_block - #echo "PATCH NUM ${_patch_extracted_num} LOG FILE: ${_send_email_log_file}" header_block=$( awk ' /^MAIL FROM:/ { @@ -600,7 +575,6 @@ function extract_patch_from_file() ) if [[ -z "$header_block" ]]; then - #echo "Erro: patch número $_patch_extracted_num não encontrado" >&2 return 22 fi @@ -960,9 +934,6 @@ function verify_mutt_minimal_config() local -a missing_options=() local option - #print_array 'essential_config_options' - #print_array 'patch_track_mutt_config' - for option in "${essential_config_options[@]}"; do if [[ -z "${patch_track_mutt_config[$option]}" || "${patch_track_mutt_config[$option]}" == '' ]]; then missing_options+=("$option") @@ -984,8 +955,6 @@ print_array() local -n arr="$array_name" local key - #echo "$KW_ETC_DIR" - #echo "Array '$array_name':" for key in "${!arr[@]}"; do printf " [%s] = %s\n" "$key" "${arr[$key]}" done @@ -1432,8 +1401,12 @@ function patch_track_help() printf '%s\n' 'kw patch-track:' \ ' patch-track (--show-patches) [[--from ] | [--after ] [--before ]] - Show patches dashboard in chronological order ' \ - ' patch-track (--id ) [-s[=]| --set-status[=]] - Set the patch status ' \ - ' patch-track (-r|--set-repository ] - Set the contribution repository and branch ' + ' patch-track (-d | --show-contributions) - Show all contributions ' \ + ' patch-track (--id ) [-s | --set-status ] - Set a patch status ' \ + ' patch-track (-u | --update) - Update patch statuses using heuristics ' \ + ' patch-track (-c | --contribution-id ) (--set-repository ) - Associate a repository to a contribution ' \ + ' patch-track (-r | --repository-id ) (-m | --set-maintainer ) - Associate a maintainer to a repository ' \ + ' patch-track (-c | --contribution-id ) (-o | --open-contribution) - Open contribution email thread in mutt ' } load_patch_track_mutt_config From 0a4e26b35e52da841ee4e7a79d3b70d1b3c53436 Mon Sep 17 00:00:00 2001 From: JGBSouza Date: Sat, 27 Dec 2025 01:01:20 -0300 Subject: [PATCH 28/33] =?UTF-8?q?feat:=20adiciona=20documenta=C3=A7=C3=A3o?= =?UTF-8?q?=20para=20o=20kw=20patch=5Ftrack?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: JoaoGBSouza --- src/patch_track.sh | 277 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 274 insertions(+), 3 deletions(-) diff --git a/src/patch_track.sh b/src/patch_track.sh index 52bd8a61c..c90a07d84 100644 --- a/src/patch_track.sh +++ b/src/patch_track.sh @@ -24,6 +24,18 @@ declare -gA updates_array declare -ga essential_config_options=('imap_user' 'imap_pass' 'folder' 'spoolfile' 'record') +# This is the main entry point for the patch-track feature. It processes +# command-line arguments through the parser and dispatches the execution +# to specific sub-modules, such as dashboards, status management, metadata +# registration, and external tool integration. It handles validations for +# mandatory identifiers (patch, contribution, or repository IDs) and +# manages errors related to missing arguments or invalid configurations. +# +# @$@: Arguments passed from the command line. +# +# Return: +# Returns 0 if the operation is successful; 22 if no arguments are provided, +# if parsing fails, or if mandatory parameters are missing. function patch_track_main() { local flag @@ -237,6 +249,15 @@ function register_patch_track() return 0 } +# This interactive function prompts the user to select an existing +# contribution or define a new one. It retrieves a list of available +# contributions from the database, formats them into a numbered list +# for selection, and handles the user input. It validates if the input +# corresponds to a valid list index or treats it as a new contribution title. +# +# Return: +# Returns 0 after printing the chosen or newly created contribution title +# to the standard output. function ask_contribution_name() { local existent_contributions=() @@ -277,6 +298,18 @@ function ask_contribution_name() return 0 } +# This registers a new contribution or retrieves an existing one based on +# a specified name and metadata extracted from email logs. It scrapes +# the sender address from the provided log file and attempts to persist +# or fetch the contribution in the database. It handles errors related +# to database operations and ensures the operation result is reported. +# +# @contribution_name: The title or identifier for the contribution. +# @_send_email_log_file: Path to the log file containing email metadata. +# +# Return: +# Returns 0 if the contribution is successfully registered or retrieved; +# otherwise, returns the non-zero error code from the database operation. function register_contribution() { local contribution_name="$1" @@ -299,6 +332,22 @@ function register_contribution() return 0 } +# This function registers multiple patches associated with a specific +# contribution. It iterates through cached patch files, extracts +# metadata such as subjects, authors, and commit hashes, and ensures +# each patch is persisted in the database. It specifically handles +# scenarios involving cover letters and stores the resulting patch +# identifiers and message IDs in an associative array. It manages +# errors related to metadata extraction or database persistence failures. +# +# @patch_cache: Directory containing the cached .patch files. +# @send_patch_output_dir: Directory with logs generated by kw send-patch. +# @contribution_id: Identifier of the contribution these patches belong to. +# @patches_output_array: Reference to an associative array to store patch IDs. +# +# Return: +# Returns 0 if all patches are successfully registered; otherwise, returns +# the non-zero error code from the failed database or extraction operation. function register_patches() { local patch_cache="$1" @@ -351,6 +400,20 @@ function register_patches() return 0 } +# This function orchestrates the automatic status update for all patches +# within a specific contribution. It synchronizes the local repository +# with the remote origin, retrieves metadata for the most recent +# submission, and iterates through each associated patch to trigger +# the heuristic evaluation engine. Finally, it consolidates the +# individual patch results to determine the overall contribution status +# and displays the updated dashboard. It handles errors related to +# missing submission data or failed metadata retrieval. +# +# @contribution_id: Identifier of the contribution to be updated. +# +# Return: +# Returns 0 if the update process completes successfully; 22 if the +# last submission information or patch metadata cannot be retrieved. function update_contribution_status() { local contribution_id="$1" @@ -417,6 +480,26 @@ function update_contribution_status() show_contributions_dashboard '' '' "$contribution_id" } +# This function evaluates and updates the status of an individual patch +# using automated heuristics. It leverages the mutt email client to +# search for and download relevant message threads, then analyzes +# local message files for specific indicators such as approval tags +# or last reply authorship. It handles state transitions between +# SENT, REVIEWED, and APPROVED, while respecting existing terminal +# states like MERGED. It manages synchronization with external +# processes through temporary status files and handles scenarios +# where no replies are found. +# +# @patch_id: Identifier of the patch to be updated in the database. +# @patch_message_id: The unique Message-ID used to track email threads. +# @patch_commit_hash: Hash associated with the patch for future merge checks. +# @patch_maintainer_email: Email address of the maintainer (for future logic). +# @patch_current_status: The current state of the patch to determine logic flow. +# @flag: Execution mode flag (defaults to 'SILENT'). +# +# Return: +# Returns 0 after updating the patch status and printing the new status +# string to the standard output. function update_patch_status() { local patch_id="$1" @@ -425,7 +508,6 @@ function update_patch_status() local patch_maintainer_email="$4" local patch_current_status="$5" local flag="${6:-'SILENT'}" - local last_msg_file local last_author local reply_files @@ -489,6 +571,21 @@ function update_patch_status() return 0 } +# This function creates records for patch submissions by associating +# individual patch identifiers with a specific submission and its +# corresponding email Message-ID. It iterates through an associative +# array of patches, ensuring each entry is persisted in the database +# relationship table. It handles errors during the creation process +# and ensures that any failure in database persistence is reported +# to the user. +# +# @submission_id: Identifier of the submission to which the patches belong. +# @patches_message_id: Reference to an associative array mapping patch IDs +# to their respective Message-IDs. +# +# Return: +# Returns 0 if all patch submissions are successfully registered; +# otherwise, returns the non-zero error code from the failed operation. function register_patch_submissions() { local submission_id="$1" @@ -507,6 +604,21 @@ function register_patch_submissions() return 0 } +# This function facilitates the exploration of a contribution by opening +# its related email threads in the mutt terminal client. It retrieves +# all submission identifiers linked to a contribution and extracts +# their unique Message-IDs from the database. It then constructs a +# complex search query and invokes mutt, providing an integrated +# interface for reviewing mailing list interactions. It handles +# errors related to missing contribution IDs or the absence of +# submission metadata in the database. +# +# @contribution_id: Identifier of the contribution to be explored. +# +# Return: +# Returns 0 if mutt is successfully launched with the constructed query; +# 22 if the contribution ID is missing; 61 if no submission data +# or Message-IDs are found. function open_contribution_on_mutt() { local contribution_id="$1" @@ -549,6 +661,21 @@ function open_contribution_on_mutt() return 0 } +# This function extracts metadata from a specific patch within a log file +# generated by the email submission process. It uses text processing +# tools to isolate header blocks and retrieve essential information +# such as sender, recipient, subject, submission date, and the unique +# Message-ID. The extracted data is stored in a referenced associative +# array for further processing. It handles scenarios where the +# expected header structure is missing or malformed. +# +# @_patch_extracted_num: The index of the patch to be extracted from the log. +# @_send_email_log_file: Path to the log file containing the email headers. +# @_output_metadata_array: Reference to an associative array to store metadata. +# +# Return: +# Returns 0 if metadata is successfully extracted; 22 if the header +# block cannot be found or isolated. function extract_patch_from_file() { local _patch_extracted_num="$1" @@ -679,6 +806,18 @@ function set_patch_status() return 0 } +# This sets the status of a specified contribution. It updates the state +# of a contribution identified by its ID, ensuring that the new status +# is validated against a predefined list of allowed values. It manages +# errors related to empty identifiers, invalid status strings, or +# database persistence failures. +# +# @contribution_id: ID of the contribution to be updated. +# @contribution_new_status: The new status string to be applied. +# +# Return: +# Returns 0 if the update is successful; 22 if an error occurs during +# the database operation; 61 if mandatory arguments are missing. function set_contribution_status() { local contribution_id="$1" @@ -720,6 +859,19 @@ function set_contribution_status() return 0 } +# This function aggregates the statuses of all individual patches to +# determine the overall state of a contribution. It implements a +# hierarchical logic where certain states (like REVIEWED) take +# precedence over others to reflect the most critical stage of the +# lifecycle. After evaluating the patches through an associative array, +# it persists the resulting contribution status in the database. +# +# @contribution_id: Identifier of the contribution to be updated. +# @_contribution_patches_status: Reference to an associative array mapping +# patch IDs to their current status. +# +# Return: +# Returns 0 after successfully determining and updating the contribution status. function decide_patch_status() { local contribution_id="$1" @@ -929,6 +1081,17 @@ function register_repository_maintainer() return 0 } +# This function ensures that the minimum required configuration for mutt +# integration is met. It iterates through a list of essential options +# and identifies any missing or empty values within the configuration +# array. If discrepancies are found, it triggers an interactive handler +# to resolve them, reloads the configuration, and recursively validates +# the settings until completion. Finally, it invokes the generation +# of the mutt runtime configuration file. +# +# Return: +# Returns 0 after validating the configuration and generating the +# necessary mutt resources. function verify_mutt_minimal_config() { local -a missing_options=() @@ -949,6 +1112,17 @@ function verify_mutt_minimal_config() generate_mutt_rc } +# This helper function provides a formatted visualization of an array's +# content. It utilizes a nameref to access the specified array by its +# name and iterates through all keys, printing each key-value pair +# in a structured format to the standard output. It is primarily +# intended for debugging and displaying associative or indexed +# array metadata. +# +# @array_name: Name of the array to be printed. +# +# Return: +# Returns 0 after iterating through and printing all array elements. print_array() { local array_name="$1" @@ -960,6 +1134,19 @@ print_array() done } +# This interactive function resolves missing configuration parameters by +# prompting the user for input. It iterates through a provided list of +# absent options, ensuring that each entry receives a non-empty value +# via a validation loop. Once a valid input is obtained, it triggers +# the persistence of the setting into the configuration file. It manages +# user feedback through warning messages when empty strings are provided. +# +# @_missing_options: Reference to an array containing the names of the +# missing configuration keys. +# +# Return: +# Returns 0 after all specified missing options have been successfully +# prompted and saved. function handle_missing_mutt_options() { local -n _missing_options="$1" @@ -977,6 +1164,18 @@ function handle_missing_mutt_options() done } +# This function persists a specific key-value pair into the mutt +# configuration file. It handles string escaping for both keys and +# values to ensure safe processing by sed and shell utilities. It +# checks for the existence of the key within the file; if the key +# exists, it performs an in-place update of its value; otherwise, +# it appends the new configuration entry to the end of the file. +# +# @key: The configuration parameter name to be saved. +# @value: The setting value associated with the key. +# +# Return: +# Returns 0 after successfully updating or appending the configuration record. function save_mutt_config() { local key="$1" @@ -996,6 +1195,22 @@ function save_mutt_config() fi } +# This function manages the assignment of options within a mutt +# configuration file. It ensures that a specific key is either +# updated with a new value or appended to the file if it does +# not already exist. It utilizes regular expressions to handle +# various formatting styles, such as the presence or absence +# of the 'set' keyword and flexible whitespace. It silently +# handles empty values by bypassing the update to maintain +# file integrity. +# +# @file: Path to the mutt configuration file to be modified. +# @key: The specific mutt option name to set or replace. +# @value: The value to be assigned to the specified key. +# +# Return: +# Returns 0 after successfully updating or appending the option record, +# or if the provided value is empty. set_or_replace_mutt_option() { local file="$1" @@ -1013,6 +1228,17 @@ set_or_replace_mutt_option() fi } +# This function orchestrates the creation and population of the mutt +# runtime configuration file (muttrc). It ensures the target file +# exists and systematically applies user-defined settings for IMAP +# authentication, mailbox paths, and local caching directories. It +# leverages a helper utility to manage the assignment of each option, +# ensuring that credentials and performance-related parameters like +# header and message caches are correctly structured. +# +# Return: +# Returns 0 after successfully generating or updating the configuration +# file; 1 if the file creation fails due to filesystem permissions. generate_mutt_rc() { touch "$MUTT_RC_PATH" || return 1 @@ -1034,7 +1260,6 @@ generate_mutt_rc() # @flag: Display mode flag (e.g., SILENT) # @columns: terminal width # @contribution_id: optional filter (shows only that contribution if set) -# function show_contributions_dashboard() { local flag="$1" @@ -1106,6 +1331,22 @@ function show_contributions_dashboard() return 0 } +# This function renders a detailed dashboard for a single contribution +# to the standard output. It calculates dynamic widths based on the +# terminal's column count to format a centered title header and +# displays comprehensive metadata, including contribution ID, author +# email, creation date, and status. Additionally, it retrieves and +# lists associated repository details and maintainer contact +# information from the database, ensuring a structured and +# professional visual representation. +# +# @_contribs_array: Reference to an array containing the contribution +# metadata delimited by pipe characters. +# @columns: The total width of the terminal display for formatting purposes. +# +# Return: +# Returns 0 after successfully formatting and printing the dashboard; +# 22 if an error occurs while retrieving maintainer information. function print_single_contribution_dashboard() { local -n _contribs_array="$1" @@ -1157,6 +1398,21 @@ function print_single_contribution_dashboard() printf "%-${columns}s\n" | tr ' ' '-' } +# This function renders a tabular dashboard displaying a list of +# contributions. It dynamically calculates the width of the name +# column based on the terminal's dimensions and the fixed sizes of +# the ID, date, and status fields. It formats a centered header +# section and iterates through the provided array to print each +# contribution's metadata in a structured, column-aligned layout. +# It ensures visual consistency by utilizing terminal escape +# sequences and horizontal separators. +# +# @_contribs_array: Reference to an array containing contribution +# records delimited by pipe characters. +# @columns: The total width of the terminal display for formatting purposes. +# +# Return: +# Returns 0 after successfully rendering the formatted contributions table. function print_contributions_dashboard() { local -n _contribs_array="$1" @@ -1197,7 +1453,6 @@ function print_contributions_dashboard() # @flag: Display mode flag (e.g., SILENT) # @columns: terminal width # @contribution_id: optional filter (shows only that contribution if set) -# function show_submissions_dashboard() { local flag="$1" @@ -1245,6 +1500,22 @@ function show_submissions_dashboard() return 0 } +# This function renders a tabular dashboard displaying a list of +# submission records. It dynamically calculates the width of the +# author column based on the terminal's dimensions and fixed-width +# fields for ID and date. The interface features a centered title +# and a structured grid where each submission's metadata is +# aligned for clear visualization. It ensures the terminal cursor +# is properly managed and uses horizontal delimiters to separate +# the header, data, and footer sections. +# +# @_contribs_array: Reference to an associative or indexed array +# containing submission metadata (ID, sender, and date). +# @columns: The total width of the terminal display used for +# calculating dynamic column spacing. +# +# Return: +# Returns 0 after successfully rendering the formatted submissions table. function print_submissions_dashboard() { local -n _contribs_array="$1" From 6ded46cefab160312924eb6ce9f812202b3e8db5 Mon Sep 17 00:00:00 2001 From: JGBSouza Date: Sat, 27 Dec 2025 01:13:39 -0300 Subject: [PATCH 29/33] =?UTF-8?q?feat:=20adiciona=20coment=C3=A1rios=20par?= =?UTF-8?q?a=20o=20contact=5Futils?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: JoaoGBSouza --- src/lib/patch_track_utils/contact_utils.sh | 72 ++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/src/lib/patch_track_utils/contact_utils.sh b/src/lib/patch_track_utils/contact_utils.sh index 509aa7d55..b3ea5b7c3 100644 --- a/src/lib/patch_track_utils/contact_utils.sh +++ b/src/lib/patch_track_utils/contact_utils.sh @@ -3,6 +3,19 @@ include "${KW_LIB_DIR}/lib/kw_db.sh" declare -Ag condition_array declare -gr DATABASE_EMAIL_CONTACT_TABLE='email_contact' +# This function persists a new email contact into the database. It enforces +# the presence of both a contact name and an email address as mandatory +# fields before proceeding with the insertion. It constructs the SQL +# command dynamically and interfaces with the database management layer, +# providing robust error handling to intercept and report schema +# violations, data inconsistencies, or connectivity issues. +# +# @_contact_name: The display name of the contact. +# @_contact_email: The unique email address of the contact. +# +# Return: +# Returns 0 after a successful database insertion; 22 if mandatory +# parameters are missing or if the database operation fails. function insert_email_contact() { local _contact_name="$1" @@ -36,6 +49,20 @@ function insert_email_contact() return 0 } +# This function verifies the existence of an email contact within the +# database using the email address as a unique identifier. It enforces +# a validation check to ensure the email string is not empty before +# querying the persistent storage. By interfacing with the lower-level +# existence utility, it provides a boolean result that indicates +# whether the contact is already registered, while handling potential +# database communication errors or null responses. +# +# @_contact_email: The unique email address to search for in the contact table. +# +# Return: +# Returns 0 and prints '1' if the contact exists, or '0' if it does not; +# 22 if the email parameter is missing or the query fails; 61 if the +# database returns an unexpected null result. function check_email_contact_existence_by_unique_attributes() { local _contact_email="$1" @@ -66,6 +93,21 @@ function check_email_contact_existence_by_unique_attributes() return 0 } +# This function serves as a specialized abstraction layer for retrieving +# data from the email contact table. It maps high-level requests to the +# underlying database engine, utilizing a condition array for filtering +# specific records. It centralizes error management for contact queries, +# ensuring that database failures are intercepted and reported +# consistently to the caller while returning the requested metadata fields. +# +# @_contact_infos: A string specifying the columns or data fields to +# be retrieved from the contact table. +# @_contact_info_condition_array: Reference to an associative array +# defining the query's WHERE clause. +# +# Return: +# Returns 0 and prints the retrieved contact data to the standard output +# on success; 22 if the database operation fails. function get_email_contact_info() { local _contact_infos="$1" @@ -85,6 +127,21 @@ function get_email_contact_info() return 0 } +# This function retrieves specific metadata from an email contact record +# using the email address as a unique search key. It validates that the +# email parameter is not empty before performing a lookup through the +# contact information module. The function handles error reporting for +# invalid identifiers and ensures that database execution failures are +# intercepted, returning the requested fields in a standardized format. +# +# @_contact_infos: A string specifying the columns or data fields to +# be retrieved from the database. +# @_contact_email: The unique email address used to identify the contact. +# +# Return: +# Returns 0 if the query is executed successfully and data is printed; +# 22 if the email parameter is missing; otherwise, returns the +# specific error code from the database operation. function get_email_contact_info_by_unique_attributes() { local _contact_infos="$1" @@ -111,6 +168,21 @@ function get_email_contact_info_by_unique_attributes() return 0 } +# This function implements an idempotent workflow to manage email contact +# records. it first validates that both the name and email are provided, +# then checks for the record's existence based on the unique email +# address. If the contact does not exist, it triggers a new insertion +# into the database. Finally, it retrieves and returns the primary +# identifier (ID) of the contact. This ensures that the caller always +# receives a valid reference ID while preventing duplicate entries. +# +# @_contact_name: The display name of the contact to be ensured. +# @_contact_email: The unique email address used for identification. +# +# Return: +# Returns 0 and prints the contact ID on success; returns a non-zero +# error code (e.g., 22) if validation fails or a database operation +# encounters an error. function get_or_create_email_contact() { local _contact_name="$1" From 14435c99502521cfd12848a183e5edd9ed63a88f Mon Sep 17 00:00:00 2001 From: JGBSouza Date: Sat, 27 Dec 2025 01:14:13 -0300 Subject: [PATCH 30/33] =?UTF-8?q?feat:=20adiciona=20coment=C3=A1rios=20de?= =?UTF-8?q?=20c=C3=B3digo=20para=20contribution=5Futils?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: JoaoGBSouza --- .../patch_track_utils/contribution_utils.sh | 95 +++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/src/lib/patch_track_utils/contribution_utils.sh b/src/lib/patch_track_utils/contribution_utils.sh index 092644746..201b0e437 100644 --- a/src/lib/patch_track_utils/contribution_utils.sh +++ b/src/lib/patch_track_utils/contribution_utils.sh @@ -4,6 +4,21 @@ declare -gr DATABASE_CONTRIBUTION_TABLE='contribution' declare -gA condition_array declare -gA updates_array +# This function handles the creation of a new contribution record within +# the database. It validates mandatory fields such as the title and +# author email before initiating the persistence process. The function +# features dynamic SQL construction to optionally include a repository +# identifier if provided. It manages database transactions through a +# specialized wrapper and provides detailed error reporting for missing +# arguments, schema constraints, or execution failures. +# +# @_contribution_title: The descriptive title of the contribution. +# @_author_email: The email address of the individual submitting the work. +# @_repository_id: (Optional) The identifier of the associated repository. +# +# Return: +# Returns 0 after a successful database insertion; 22 if mandatory +# parameters are missing or if the database operation fails. function insert_contribution() { local _contribution_title="$1" @@ -40,6 +55,23 @@ function insert_contribution() return 0 } +# This function implements an idempotent workflow to ensure a contribution +# record exists in the database. It validates mandatory fields and +# performs an existence check based on unique attributes—specifically +# the contribution title and author email. If no matching record is +# found, it triggers the insertion of a new entry, optionally linking +# it to a repository. Finally, it retrieves and returns the primary +# identifier of the contribution. It provides robust error handling +# for missing arguments, failed lookups, or persistence errors. +# +# @_contribution_title: The descriptive title of the contribution. +# @_author_email: The email address of the contribution author. +# @_repository_id: (Optional) The identifier of the associated repository. +# +# Return: +# Returns 0 after printing the contribution ID to the standard output; +# otherwise, returns the specific error code (22 or 61) encountered +# during validation or database interaction. function get_or_create_contribution() { local _contribution_title="$1" @@ -86,6 +118,22 @@ function get_or_create_contribution() return 0 } +# This function determines if a contribution record exists in the database +# by evaluating its unique identifying attributes: the title and the +# author's email. It enforces a strict validation check to ensure neither +# attribute is empty before proceeding with the query. The function +# interfaces with a low-level existence checker and manages various +# failure modes, including database connectivity issues or unexpected +# null results, returning a boolean-style output to indicate the +# record's presence. +# +# @_contribution_title: The specific title of the contribution to check. +# @_author_email: The email address associated with the contribution. +# +# Return: +# Returns 0 and prints '1' if the contribution exists, or '0' if it +# does not; 22 if mandatory parameters are missing; 61 if the +# database query returns an invalid or null result. function check_contribution_existence_by_unique_attributes() { local _contribution_title="$1" @@ -119,6 +167,23 @@ function check_contribution_existence_by_unique_attributes() return 0 } +# This function retrieves specific contribution metadata from the database +# using a combination of unique identifying attributes: the contribution +# title and the author's email. It validates the presence of these +# mandatory fields before constructing a filtered query. The function +# centralizes the retrieval logic for contribution records and handles +# potential database errors, ensuring that the requested information +# is returned in a format suitable for further processing. +# +# @_contribution_infos: A string specifying the columns or data fields +# to be retrieved from the database. +# @_contribution_title: The specific title of the contribution to match. +# @_author_email: The author's email address associated with the record. +# +# Return: +# Returns 0 if the query is executed successfully and data is printed; +# 22 if mandatory parameters are missing; otherwise, returns the +# non-zero error code from the database operation. function get_contribution_info_by_unique_attributes() { local _contribution_infos="$1" @@ -147,6 +212,22 @@ function get_contribution_info_by_unique_attributes() return 0 } +# This function serves as a specialized abstraction layer for retrieving +# data from the contributions table. It maps high-level requests to +# the underlying database engine, utilizing a condition array for +# filtering and an optional parameter for result sorting. It centralizes +# error management for contribution queries, ensuring that database +# failures are intercepted and reported consistently to the caller. +# +# @_contribution_infos: A string specifying the columns or data fields +# to be retrieved. +# @_contrib_info_condition_array: Reference to an associative array +# defining the query's WHERE clause. +# @_order_by: (Optional) SQL clause to define the sort order of the results. +# +# Return: +# Returns 0 and prints the retrieved data to the standard output on success; +# 22 if the database operation fails. function get_contribution_info() { local _contribution_infos="$1" @@ -167,6 +248,20 @@ function get_contribution_info() return 0 } +# This function updates the repository association for a specific +# contribution record. It maps a contribution identifier to a +# repository identifier within the persistent storage, ensuring +# the relational link is established. It utilizes an update wrapper +# to modify the database state and provides error handling to +# manage failures in the persistence layer, ensuring that invalid +# operations are reported to the caller. +# +# @contribution_id: The unique identifier of the contribution to be updated. +# @repository_id: The unique identifier of the repository to be linked. +# +# Return: +# Returns 0 upon a successful database update; 22 if the operation +# fails due to a database error. function set_contribution_repository() { local contribution_id="$1" From 99b16ee07778e843fd9284842a349bfcfcc9d698 Mon Sep 17 00:00:00 2001 From: JGBSouza Date: Sat, 27 Dec 2025 01:14:28 -0300 Subject: [PATCH 31/33] =?UTF-8?q?feat:=20adiciona=20coment=C3=A1rios=20de?= =?UTF-8?q?=20c=C3=B3digo=20para=20patch=5Futils?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: JoaoGBSouza --- src/lib/patch_track_utils/patch_utils.sh | 133 +++++++++++++++++++++-- 1 file changed, 121 insertions(+), 12 deletions(-) diff --git a/src/lib/patch_track_utils/patch_utils.sh b/src/lib/patch_track_utils/patch_utils.sh index e4ea54e54..5773d1761 100644 --- a/src/lib/patch_track_utils/patch_utils.sh +++ b/src/lib/patch_track_utils/patch_utils.sh @@ -3,6 +3,22 @@ include "${KW_LIB_DIR}/lib/kw_db.sh" declare -gr DATABASE_PATCH_TABLE='patch' declare -Ag condition_array +# This function performs the physical insertion of a new patch record into +# the database. It enforces strict validation of all mandatory fields, +# including the title, author email, parent contribution ID, and commit +# hash. Once validated, it constructs the SQL insertion statement and +# manages the transaction through a database wrapper. It includes robust +# error handling to catch and report schema violations, empty datasets, +# or general execution failures during the persistence process. +# +# @_patch_title: The title or subject of the patch. +# @_patch_author: The email address of the patch author. +# @_contribution_id: Identifier of the contribution to which the patch belongs. +# @_commit_hash: The unique Git commit hash associated with the patch. +# +# Return: +# Returns 0 after a successful database insertion; 22 if mandatory +# parameters are missing or if the database operation fails. function insert_patch() { local _patch_title="$1" @@ -36,6 +52,24 @@ function insert_patch() return 0 } +# This function implements an idempotent logic to ensure a patch record +# exists in the database. It first validates mandatory fields and checks +# for the existence of the patch using its unique attributes—including +# the title, author, and commit hash. If no matching record is found, +# it triggers the insertion of a new entry. Finally, it retrieves and +# returns the primary identifier of the record, regardless of whether +# it was newly created or previously stored. It manages errors for +# missing fields, failed existence checks, and database persistence issues. +# +# @_patch_title: The title or subject of the patch. +# @_patch_author: The email address of the patch author. +# @_contribution_id: Identifier of the contribution to link the patch to. +# @_commit_hash: The unique Git commit hash for reliable identification. +# +# Return: +# Returns 0 after printing the patch ID to the standard output; +# otherwise, returns the non-zero error code from the failed +# validation or database operation. function get_or_create_patch() { local _patch_title="$1" @@ -89,6 +123,21 @@ function get_or_create_patch() return 0 } +# This function retrieves specific metadata from a patch record using its +# primary database identifier. It validates that the provided ID is +# not empty before performing a lookup through the patch information +# module. The function handles error reporting for invalid identifiers +# or database execution failures and explicitly manages cases where +# the ID does not correspond to any existing entry in the persistent +# storage. +# +# @_patch_infos: The specific columns or data attributes to be retrieved. +# @_patch_id: The unique primary key identifier of the patch. +# +# Return: +# Returns 0 if the patch record is found and the data is successfully +# printed; 22 if the patch ID is empty or the query fails; 61 if no +# record matches the provided identifier. function get_patch_info_by_id() { local _patch_infos="$1" @@ -119,6 +168,21 @@ function get_patch_info_by_id() return 0 } +# This function retrieves specific patch data using a unique Git commit +# hash as the primary search criterion. It serves as a specialized +# lookup utility that validates the presence of the hash before +# querying the database through the patch information module. It +# handles errors for empty input strings and manages cases where +# no corresponding record exists for the provided hash, ensuring +# data integrity during the retrieval process. +# +# @_patch_infos: The specific columns or metadata fields to be retrieved. +# @_commit_hash: The unique commit hash identifying the target patch. +# +# Return: +# Returns 0 if the patch information is successfully found and printed; +# 22 if the commit hash is empty or the query fails; 61 if no record +# matches the provided hash. function get_patch_info_by_commit_hash() { local _patch_infos="$1" @@ -149,6 +213,19 @@ function get_patch_info_by_commit_hash() return 0 } +# This function acts as a specialized wrapper to retrieve information from +# the patches table in the database. It utilizes a provided condition +# array to filter records and returns the requested data fields. It +# centralizes error handling for patch-specific queries, ensuring that +# any database operation failures are reported to the user. +# +# @_patch_infos: A string specifying the columns or attributes to retrieve. +# @_patch_condition_array: Reference to an associative array containing +# the key-value pairs for the SQL WHERE clause. +# +# Return: +# Returns 0 after printing the requested patch data to the standard output; +# otherwise, returns the non-zero error code from the database operation. function get_patch_info() { local _patch_infos="$1" @@ -166,6 +243,23 @@ function get_patch_info() return 0 } +# This function verifies whether a specific patch already exists in the +# database by checking a set of unique identifying attributes. It +# validates the patch title, author email, and contribution ID against +# the local storage. A critical safety check ensures that a commit hash +# is present before proceeding, as identification without a hash is +# considered unreliable. It handles error reporting for missing +# parameters and processes the boolean result of the existence check. +# +# @_patch_title: The subject or title of the patch to verify. +# @_patch_author: The email address of the patch author. +# @_contribution_id: Identifier of the parent contribution. +# @_commit_hash: The unique commit hash required for reliable identification. +# +# Return: +# Returns 0 if the check is performed successfully (printing 1 for +# existence and 0 for non-existence); 22 if mandatory parameters are +# missing; 61 if the query returns a null result. function check_patch_existence_by_unique_attributes() { local _patch_title="$1" @@ -205,6 +299,24 @@ function check_patch_existence_by_unique_attributes() return 0 } +# This function retrieves specific patch information from the database by +# validating a set of unique attributes, including the title, author email, +# contribution identifier, and commit hash. It implements a safety check +# to ensure that commit hashes are present before attempting a match, +# preventing false positives in the identification process. It handles +# errors related to missing mandatory attributes and manages scenarios +# where no matching records are found in the persistent storage. +# +# @_patch_infos: The specific columns or data fields to be retrieved. +# @_patch_title: The title of the patch used as a search criterion. +# @_patch_author: The email address of the patch author. +# @_contribution_id: Identifier of the contribution the patch belongs to. +# @_commit_hash: The unique Git commit hash associated with the patch. +# +# Return: +# Returns 0 if the query is successful and a record is found; 22 if +# mandatory arguments are missing or the query fails; 61 if no +# matching data is found. function get_patch_infos_by_unique_attributes() { local _patch_infos="$1" @@ -246,14 +358,17 @@ function get_patch_infos_by_unique_attributes() return 0 } -# Função: formatar_data_patch -# Descrição: Recebe uma string de data no formato RFC 2822 (como em cabeçalhos de patch) -# e a formata para o padrão YYYY-MM-DD HH:MM:SS, seguro para a maioria -# dos campos DATETIME/TIMESTAMP de bancos de dados. +# This function normalizes a date string from the RFC 2822 format—commonly +# found in patch headers—into a standardized YYYY-MM-DD HH:MM:SS format. +# This conversion ensures compatibility with DATETIME and TIMESTAMP +# schema requirements in most database systems. It handles the +# transformation of raw date metadata into a structured, sortable, +# and persistent string representation. # -# @date_string: A string de data bruta (Ex: "Wed, 8 Oct 2025 00:37:32 -0300") +# @date_string: The raw date string (e.g., "Wed, 8 Oct 2025 00:37:32 -0300"). # -# Retorna: A data formatada no stdout. +# Return: +# Returns 0 after printing the formatted date string to the standard output. formatar_data_patch() { local date_string="$1" @@ -264,19 +379,13 @@ formatar_data_patch() return 1 fi - # Usa 'date -d' para interpretar a string de data complexa. - # O formato de saída é %Y-%m-%d %H:%M:%S. - # Nota: A data é convertida para o fuso horário LOCAL do sistema que está rodando o script. - # Se você quiser manter o fuso horário original, o formato seria diferente. formatted_date=$(date -d "$date_string" +"%Y-%m-%d %H:%M:%S") - # Verifica se o comando date foi bem-sucedido if [ "$?" -ne 0 ]; then echo "Erro: Falha ao processar a string de data: '$date_string'" >&2 return 1 fi - # Imprime a data formatada no stdout printf '%s' "$formatted_date" return 0 } From effa47a87b1e62661d45efee1dc42d9317face40 Mon Sep 17 00:00:00 2001 From: JGBSouza Date: Sat, 27 Dec 2025 01:14:45 -0300 Subject: [PATCH 32/33] =?UTF-8?q?feat:=20adiciona=20coment=C3=A1rios=20de?= =?UTF-8?q?=20c=C3=B3digo=20para=20repository=5Futils?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: JoaoGBSouza --- src/lib/patch_track_utils/repository_utils.sh | 157 ++++++++++++++++++ 1 file changed, 157 insertions(+) diff --git a/src/lib/patch_track_utils/repository_utils.sh b/src/lib/patch_track_utils/repository_utils.sh index 3c8816284..9dc0b8739 100644 --- a/src/lib/patch_track_utils/repository_utils.sh +++ b/src/lib/patch_track_utils/repository_utils.sh @@ -5,6 +5,20 @@ declare -gr DATABASE_REPOSITORY_TABLE='repository' declare -gr DATABASE_REPO_MAINTAINER_TABLE='repository_maintainer' declare -Ag condition_array +# This function handles the physical insertion of a new repository record +# into the database. It enforces mandatory validation for the repository +# name and its origin URL before constructing the SQL command. It +# interfaces with the low-level database wrapper to persist the data +# and includes specific error handling to manage schema violations, +# empty results, or general execution failures, ensuring that the +# repository's identity and location are correctly recorded. +# +# @_repository_name: The descriptive name of the repository. +# @_origin_url: The source URL (e.g., Git remote URL) of the repository. +# +# Return: +# Returns 0 after a successful database insertion; 22 if mandatory +# parameters are missing or if the database operation fails. function insert_repository() { local _repository_name="$1" @@ -38,6 +52,21 @@ function insert_repository() return 0 } +# This function verifies the existence of a repository in the database +# by using its origin URL as a unique identifier. It ensures that the +# URL parameter is not empty before querying the persistent storage +# through the existence utility. It handles error reporting for +# database communication failures and provides a boolean output +# indicating whether the repository record is already registered, +# preventing the creation of duplicate entries. +# +# @_origin_url: The unique source URL (e.g., Git origin) used to identify +# the repository. +# +# Return: +# Returns 0 and prints '1' if the repository exists, or '0' if it does +# not; 22 if the URL parameter is missing; 61 if the query returns an +# unexpected null result. function check_repository_existence_by_unique_attributes() { local _origin_url="$1" @@ -68,6 +97,22 @@ function check_repository_existence_by_unique_attributes() return 0 } +# This function serves as a specialized abstraction layer for retrieving +# data from the repository table. It maps high-level requests to the +# underlying database engine, utilizing a condition array for filtering +# specific records based on defined criteria. It centralizes error +# management for repository-specific queries, ensuring that database +# failures are intercepted and reported consistently to the caller +# while returning the requested metadata fields. +# +# @_repository_infos: A string specifying the columns or data fields to +# be retrieved from the repository table. +# @_repo_info_condition_array: Reference to an associative array +# defining the query's WHERE clause. +# +# Return: +# Returns 0 and prints the retrieved repository data to the standard +# output on success; 22 if the database operation fails. function get_repository_info() { local _repository_infos="$1" @@ -87,6 +132,22 @@ function get_repository_info() return 0 } +# This function retrieves specific repository metadata using the origin +# URL as the unique search criterion. It validates that the URL +# parameter is not empty before executing a lookup through the +# repository information module. The function serves as a targeted +# retrieval utility, handling error reporting for missing identifiers +# and ensuring that database execution failures or missing records +# are managed appropriately during the data fetching process. +# +# @_repository_infos: A string specifying the columns or data fields to +# be retrieved from the database. +# @_origin_url: The unique source URL used to identify the target repository. +# +# Return: +# Returns 0 if the repository record is found and data is printed; +# 22 if the origin URL is empty; otherwise, returns the specific +# error code from the database operation. function get_repository_info_by_unique_attributes() { local _repository_infos="$1" @@ -113,6 +174,22 @@ function get_repository_info_by_unique_attributes() return 0 } +# This function implements an idempotent workflow to ensure a repository +# record exists in the persistent storage. It validates that both the +# repository name and origin URL are provided, then performs an +# existence check based on the unique URL. If no matching record is +# found, it triggers the creation of a new repository entry. Finally, +# it retrieves and returns the primary identifier (ID) of the record. +# This logic prevents duplicate repository entries while ensuring the +# caller receives a valid reference for relational linking. +# +# @_repository_name: The descriptive name of the repository. +# @_origin_url: The unique source URL used for identification and persistence. +# +# Return: +# Returns 0 and prints the repository ID on success; returns a non-zero +# error code (e.g., 22) if validation fails or a database operation +# encounters an error. function get_or_create_repository() { local _repository_name="$1" @@ -157,6 +234,20 @@ function get_or_create_repository() return 0 } +# This function establishes a relational link between a repository and +# an email contact in the database, designating the contact as a +# maintainer. It enforces mandatory validation for both the repository +# and contact identifiers before executing the insertion. By +# interfacing with a join-table structure, it persists the many-to-many +# relationship and provides detailed error handling for schema +# constraint violations or database execution failures. +# +# @_repository_id: The unique identifier of the target repository. +# @_contact_id: The unique identifier of the email contact to be linked. +# +# Return: +# Returns 0 after a successful database insertion; 22 if mandatory +# parameters are missing or if the database operation fails. function insert_repository_maintainer() { local _repository_id="$1" @@ -190,6 +281,21 @@ function insert_repository_maintainer() return 0 } +# This function verifies whether a specific maintenance relationship exists +# between a repository and a contact within the join table. It validates +# that both the repository and contact identifiers are provided before +# querying the persistent storage. By evaluating the composite link +# through the existence utility, it provides a boolean result that +# confirms if the association is already registered, effectively +# preventing the creation of duplicate maintainer assignments. +# +# @_repository_id: The unique identifier of the repository to check. +# @_contact_id: The unique identifier of the email contact to check. +# +# Return: +# Returns 0 and prints '1' if the association exists, or '0' if it does +# not; 22 if mandatory parameters are missing; 61 if the database +# returns an unexpected null result. function check_repository_maintainer_existence() { local _repository_id="$1" @@ -222,6 +328,24 @@ function check_repository_maintainer_existence() return 0 } +# This function ensures that a specific contact is registered and linked as +# a maintainer to a repository, implementing an idempotent multi-step +# workflow. It first secures a valid contact identifier by calling the +# contact management module. It then verifies if a relationship already +# exists between the repository and that contact in the association table. +# If no link is found, it persists the new maintainer-repository +# relationship. It manages data dependencies across different tables +# and provides comprehensive error handling for missing arguments or +# database failures at any stage of the process. +# +# @_repository_id: The identifier of the repository to link. +# @_maintainer_name: The display name of the maintainer. +# @_maintainer_email: The unique email address of the maintainer. +# +# Return: +# Returns 0 and prints the contact ID on success; returns a non-zero +# error code if validation fails, contact creation fails, or the +# relationship cannot be persisted. function get_or_create_repository_maintainer() { local _repository_id="$1" @@ -268,6 +392,22 @@ function get_or_create_repository_maintainer() return 0 } +# This function serves as a specialized abstraction layer for retrieving +# data from the repository maintainer join table. It maps high-level +# requests to the underlying database engine, utilizing a condition +# array to filter records based on specific repository or contact +# identifiers. It centralizes error management for maintenance-relation +# queries, ensuring that database execution failures are intercepted and +# reported consistently while returning the requested metadata fields. +# +# @_maintainers_infos: A string specifying the columns or data fields to +# be retrieved from the join table. +# @_maintainers_condition_array: Reference to an associative array +# defining the query's WHERE clause. +# +# Return: +# Returns 0 and prints the retrieved association data to the standard +# output on success; 22 if the database operation fails. function get_maintainers_info() { local _maintainers_infos="$1" @@ -287,6 +427,23 @@ function get_maintainers_info() return 0 } +# This function acts as a specialized query interface for the repository +# maintainer association table. It abstracts the underlying database +# retrieval logic, using a passed condition array to filter specific +# maintenance relationships. It serves as a dedicated wrapper that +# ensures consistent error handling and standardizes the output format +# for metadata requests involving the link between repositories and +# their assigned contacts. +# +# @_repository_maintainers_infos: A string defining the specific columns +# or fields to extract from the table. +# @_repository_maintainers_condition_array: Reference to an associative +# array used to filter the +# query results (WHERE clause). +# +# Return: +# Returns 0 and prints the resulting dataset to standard output on +# success; 22 if the database retrieval fails. function get_repository_maintainers_info() { local _repository_maintainers_infos="$1" From a68b93b8401a0ba479174ee74e825287762142fc Mon Sep 17 00:00:00 2001 From: JGBSouza Date: Sat, 27 Dec 2025 01:15:02 -0300 Subject: [PATCH 33/33] =?UTF-8?q?feat:=20adiciona=20coment=C3=A1rios=20de?= =?UTF-8?q?=20c=C3=B3digo=20para=20submission=5Futils?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: JoaoGBSouza --- src/lib/patch_track_utils/submission_utils.sh | 139 ++++++++++++++++++ 1 file changed, 139 insertions(+) diff --git a/src/lib/patch_track_utils/submission_utils.sh b/src/lib/patch_track_utils/submission_utils.sh index 9083d8e00..45e5de5f4 100644 --- a/src/lib/patch_track_utils/submission_utils.sh +++ b/src/lib/patch_track_utils/submission_utils.sh @@ -4,6 +4,22 @@ declare -gr DATABASE_SUBMISSION_TABLE='submission' declare -gr DATABASE_PATCH_SUBMISSION_TABLE='patch_submission' declare -Ag patch_track_condition_array +# This function performs the physical insertion of a new submission record +# into the database. It establishes a historical entry for a specific +# contribution, logging both the parent contribution identifier and the +# timestamp or versioning context represented by the sender metadata. +# It interfaces with the persistence layer to record the event and +# includes error handling to manage schema constraint violations or +# database execution failures, ensuring that the submission lifecycle +# is accurately tracked. +# +# @_contribution_id: The unique identifier of the parent contribution. +# @_send_by: Metadata indicating the origin or timestamp of the submission. +# +# Return: +# Returns 0 after a successful database insertion; otherwise, returns +# the specific error code (2 || 61) if the operation fails due to +# data or execution errors. function insert_submission() { local _contribution_id="$1" @@ -25,6 +41,21 @@ function insert_submission() return 0 } +# This function orchestrates the creation of a submission event and +# retrieves its resulting identifier. It functions as a high-level +# transactional wrapper that first persists a new record in the +# submissions table—linking it to a parent contribution—and then +# immediately queries the database to obtain the most recent +# submission ID for that contribution. This ensures the caller receives +# the auto-generated primary key necessary for subsequent operations, +# such as linking patches to this specific submission instance. +# +# @_contribution_id: The unique identifier of the parent contribution. +# @_submission_author: The email or name of the entity submitting the work. +# +# Return: +# Returns 0 and prints the new submission ID on success; returns a +# non-zero error code if the insertion or ID retrieval fails. function create_submission() { local _contribution_id="$1" @@ -54,6 +85,22 @@ function create_submission() return 0 } +# This function retrieves metadata from the most recent submission +# associated with a specific contribution. It filters the submission +# table by the contribution identifier and applies a descending sort +# on the primary key (ID) to isolate the latest entry. By limiting +# the result set to a single record, it provides a reliable way to +# access the current state or latest identifier of a contribution's +# lifecycle. It handles query execution and returns the requested +# fields or reports an error if the retrieval fails. +# +# @_submission_infos: A string specifying the columns or data fields +# to be retrieved from the submission table. +# @_contribution_id: The unique identifier of the parent contribution. +# +# Return: +# Returns 0 and prints the requested submission metadata on success; +# returns a non-zero error code if the database query fails. function get_last_submission_infos_by_contribution_id() { local _submission_infos="$1" @@ -75,6 +122,25 @@ function get_last_submission_infos_by_contribution_id() return 0 } +# This function serves as a specialized abstraction layer for retrieving +# data from the submission table. It maps high-level requests to the +# underlying database engine, utilizing a condition array for record +# filtering, and optional parameters for result sorting and pagination +# (LIMIT). It centralizes error management for submission-specific +# queries, ensuring that database execution failures are intercepted +# and reported consistently while returning the requested metadata fields. +# +# @_submission_infos: A string specifying the columns or data fields to +# be retrieved from the submission table. +# @_submission_condition_array: Reference to an associative array +# defining the query's WHERE clause. +# @_order_by: (Optional) SQL clause to define the sort order of the results. +# @_limit: (Optional) Integer value to restrict the number of records returned. +# +# Return: +# Returns 0 and prints the retrieved submission data to the standard +# output on success; returns a non-zero error code if the database +# operation fails. function get_submission_info() { local _submission_infos="$1" @@ -94,6 +160,24 @@ function get_submission_info() return 0 } +# This function establishes the formal association between a specific +# patch and a submission instance, optionally including a message +# identifier for tracking. It implements an existence check to prevent +# duplicate entries within the patch-submission join table. If the +# relationship does not already exist, it triggers the persistence +# logic to link the entities. It provides detailed error reporting +# should validation fail, duplicates be detected, or the database +# insertion encounter an execution error. +# +# @_patch_id: The unique identifier of the patch to be linked. +# @_submission_id: The identifier of the submission instance. +# @_message_id: The mailing list or system message identifier associated +# with this specific patch delivery. +# +# Return: +# Returns 0 upon successful verification and insertion; returns 22 or +# a relevant database error code if the record already exists, +# parameters are invalid, or the insertion fails. function create_patch_submission() { local _patch_id="$1" @@ -124,6 +208,23 @@ function create_patch_submission() return 0 } +# This function verifies the existence of a specific patch-to-submission +# association using a composite set of unique attributes: the patch ID, +# the submission ID, and the message ID. It enforces strict validation +# to ensure all three identifiers are provided before querying the +# association table. By interfacing with the lower-level existence +# utility, it determines if this specific delivery of a patch has +# already been recorded, providing a safeguard against duplicate +# relational entries in the tracking system. +# +# @_patch_id: The unique identifier of the patch. +# @_submission_id: The identifier of the submission instance. +# @_message_id: The unique message identifier (e.g., Message-ID header). +# +# Return: +# Returns 0 and prints '1' if the specific association exists, or '0' +# if it does not; 22 if mandatory parameters are missing; otherwise, +# returns the specific error code from the database operation. function check_patch_submission_existence_by_unique_attributes() { local _patch_id="$1" @@ -151,6 +252,26 @@ function check_patch_submission_existence_by_unique_attributes() return 0 } +# This function serves as a specialized abstraction layer for retrieving +# data from the patch-submission association table. It maps high-level +# requests to the underlying database engine, utilizing a condition +# array for precise filtering of links between patches and their +# respective submission instances. It supports optional sorting and +# record limiting, centralizing error management to ensure that +# failures in the tracking layer are reported consistently while +# returning the requested relational metadata. +# +# @_patch_submission_infos: A string specifying the columns or data +# fields to be retrieved. +# @_patch_submission_condition_array: Reference to an associative array +# defining the query's WHERE clause. +# @_order_by: (Optional) SQL clause to define the sort order of results. +# @_limit: (Optional) Integer value to restrict the result set size. +# +# Return: +# Returns 0 and prints the retrieved association data to the standard +# output on success; returns a non-zero error code if the database +# operation fails. function get_patch_submission_info() { local _patch_submission_infos="$1" @@ -170,6 +291,24 @@ function get_patch_submission_info() return 0 } +# This function performs the physical insertion of a record into the +# patch-submission join table, formally linking a patch to a specific +# submission event. It persists the relationship along with the unique +# message identifier used during the delivery process. The function +# operates in a verbose mode to capture detailed transaction logs and +# includes comprehensive error handling to manage unique constraint +# violations or database execution failures, ensuring the integrity of +# the patch tracking history. +# +# @_patch_id: The unique identifier of the patch to be associated. +# @_submission_id: The identifier of the submission instance. +# @_message_id: The unique message identifier (e.g., Message-ID) +# linking the patch to its communication source. +# +# Return: +# Returns 0 after a successful database insertion; 22 if the +# operation fails due to schema violations, data errors, or general +# database connectivity issues. function insert_patch_submission() { local _patch_id="$1"