From a35e8376553924b35d97a5648f5910da843eab49 Mon Sep 17 00:00:00 2001 From: Heavenfighter <33938595+Heavenfighter@users.noreply.github.com> Date: Wed, 15 Oct 2025 10:38:10 +0200 Subject: [PATCH] pre-commit checking adopted for current version --- Source/lang/strings_english.txt | 23 ++ Source/lang/strings_german.txt | 20 ++ Source/pages/pre_commit_check.php | 231 ++++++++++++++++++ Source/pages/repo_update.php | 19 ++ Source/pages/repo_update_page.php | 62 +++++ Source/pages/si_common.php | 8 + SourceHgWeb/lang/strings_english.txt | 11 + SourceHgWeb/lang/strings_german.txt | 32 +++ .../pre_commit.py.tmpl.mantis-checks-commit | 117 +++++++++ .../pre-commit.tmpl.mantis-checks-commit | 77 ++++++ 10 files changed, 600 insertions(+) create mode 100644 Source/pages/pre_commit_check.php create mode 100644 Source/pages/si_common.php create mode 100644 SourceHgWeb/lang/strings_german.txt create mode 100644 SourceHgWeb/pre_commit.py.tmpl.mantis-checks-commit create mode 100644 SourceSVN/pre-commit.tmpl.mantis-checks-commit diff --git a/Source/lang/strings_english.txt b/Source/lang/strings_english.txt index 6973b1fb1..464768021 100644 --- a/Source/lang/strings_english.txt +++ b/Source/lang/strings_english.txt @@ -35,6 +35,19 @@ $s_plugin_Source_filename = 'Filename'; $s_plugin_Source_ported = 'Ported'; $s_plugin_Source_vcs_username = 'Source Control Username'; +$s_plugin_Source_pre_commit_checks = 'Pre-Commit Checks'; +$s_plugin_Source_commit_needs_issue = 'Commit Requires Issue Reference(s)'; +$s_plugin_Source_commit_issues_must_exist = 'Referenced Issue(s) Must Exist'; +$s_plugin_Source_commit_ownership_must_match = 'Referenced Issue(s) Must Be Owned By Committer'; +$s_plugin_Source_commit_committer_must_be_member = 'Committer must be a member of the Mantis project'; +$s_plugin_Source_commit_committer_must_be_level = 'Committer must be a'; +$s_plugin_Source_commit_status_restricted = 'Referenced Issue(s) must be at a particular status'; +$s_plugin_Source_commit_status_restricted_list = 'Only allow commits when ticket is'; +$s_plugin_Source_commit_project_restricted = 'Referenced Issue(s) must be within a particular project'; +$s_plugin_Source_commit_project_restricted_list = 'Only allow commits when ticket is within'; +$s_plugin_Source_commit_on = 'On'; +$s_plugin_Source_commit_off = 'Off'; + $s_plugin_Source_issue = 'Issue'; $s_plugin_Source_issues = 'Issues'; $s_plugin_Source_actions = 'Actions'; @@ -147,6 +160,7 @@ $s_plugin_Source_invalid_checkin_url = 'Invalid remote check-in address'; $s_plugin_Source_invalid_import_url = 'Invalid remote import address'; $s_plugin_Source_invalid_repo = 'Invalid repository name'; $s_plugin_Source_invalid_changeset = 'Changeset information could not be loaded'; +$s_plugin_Source_invalid_key = 'Invalid API Key. Please check that you have set the API_KEY variable correctly in the hook script'; $s_plugin_Source_import_latest_failed = 'Repository latest data importing failed.'; $s_plugin_Source_import_full_failed = 'Full repository data importing failed.'; @@ -162,3 +176,12 @@ $s_plugin_Source_error_invalid_branch = 'Invalid characters in Branch name "%1$s $s_plugin_Source_error_productmatrix_not_loaded = 'Product Matrix integration is enabled, but the plugin is not loaded.'; $s_plugin_Source_error_repo_missing = 'Repository "%1$s" not found.'; $s_plugin_Source_error_repo_missing_changeset = 'No repositories found for Changeset "%1$s".'; + +$s_plugin_Source_error_commit_needs_issue = 'Commit comments needs to reference one or more issues'; +$s_plugin_Source_error_commit_nonexistent_issue = 'Commit comment references non-existent issue'; +$s_plugin_Source_error_commit_issue_ownership = 'Issue referenced in commit need to be assigned to the committer'; +$s_plugin_Source_error_commit_committer_not_member = 'Committing user is not a member of the Mantis project'; +$s_plugin_Source_error_commit_committer_not_fount = 'Committing user could not be found in Mantis'; +$s_plugin_Source_error_commit_committer_wrong_level = 'Committing user does not have appropriate access level in project'; +$s_plugin_Source_error_commit_issue_wrong_status = 'Issue referenced in commit is not at correct status to be committed against'; +$s_plugin_Source_error_commit_issue_wrong_project = 'Issue referenced in commit is within appropriate project'; \ No newline at end of file diff --git a/Source/lang/strings_german.txt b/Source/lang/strings_german.txt index 5c18aeae5..1135aedaa 100644 --- a/Source/lang/strings_german.txt +++ b/Source/lang/strings_german.txt @@ -25,6 +25,18 @@ $s_plugin_Source_username = 'Benutzer'; $s_plugin_Source_timestamp = 'Datum'; $s_plugin_Source_parent = 'Vorgänger'; $s_plugin_Source_url = 'URL'; +$s_plugin_Source_pre_commit_checks = 'Pre-Commit Checks'; +$s_plugin_Source_commit_needs_issue = 'Committext benötigt Beziehung zu mindestens einem Eintrag'; +$s_plugin_Source_commit_issues_must_exist = 'Eintrag muss existieren'; +$s_plugin_Source_commit_ownership_must_match = 'Eintrag muss Autor/Comitter gehören'; +$s_plugin_Source_commit_committer_must_be_member = 'Benutzer muss dem Projekt in Mantis angehören'; +$s_plugin_Source_commit_committer_must_be_level = 'Benutzer muss Rolle haben'; +$s_plugin_Source_commit_status_restricted = 'Referenzierte Einträge müssen einen bestimmten Status haben'; +$s_plugin_Source_commit_status_restricted_list = 'Commit nur zulassen für Einträge mit folgendem Status'; +$s_plugin_Source_commit_project_restricted = 'Referenzierte Einträge müssen zu einem bestimmten Projekt gehören'; +$s_plugin_Source_commit_project_restricted_list = 'Commit nur zulassen, wenn Eintrag aus Projekt'; +$s_plugin_Source_commit_on = 'An'; +$s_plugin_Source_commit_off = 'Aus'; $s_plugin_Source_info = 'Zusatzinfos'; $s_plugin_Source_revision = 'Revision'; $s_plugin_Source_date_begin = 'Beginn'; @@ -144,6 +156,14 @@ $s_plugin_Source_invalid_changeset = 'Die Informationen zu dem Änderungssatz ko $s_plugin_Source_import_latest_failed = 'Partieller Import des Projektarchivs fehlgeschlagen.'; $s_plugin_Source_import_full_failed = 'Vollständiger Import des Projektarchivs fehlgeschlagen.'; +$s_plugin_Source_error_commit_needs_issue = 'Kommentar muss einen oder mehrere Verweise auf Einträge enthalten'; +$s_plugin_Source_error_commit_nonexistent_issue = 'Committext verweist auf nicht existenten Eintrag'; +$s_plugin_Source_error_commit_issue_ownership = 'Verwiesener Eintrag muss dem Benutzer gehören'; +$s_plugin_Source_error_commit_committer_not_member = 'Benutzer ist nicht Mitglied des Mantisprojektes'; +$s_plugin_Source_error_commit_committer_not_found = 'Benutzer konnte nicht gefunden werden in Mantis'; +$s_plugin_Source_error_commit_committer_wrong_level = 'Benutzer hat unzureichende Rechte im Projekt'; +$s_plugin_Source_error_commit_issue_wrong_status = 'Verwiesener Eintrag hat nicht den korrekten Status'; +$s_plugin_Source_error_commit_issue_wrong_project = 'Verwiesener Eintrag ist nicht im zugelassenen Projekt'; $s_plugin_Source_changeset_column_title = 'C'; $s_plugin_Source_error_invalid_branch = 'Ungültige Zeichen in der Zweige "%1$s"'; diff --git a/Source/pages/pre_commit_check.php b/Source/pages/pre_commit_check.php new file mode 100644 index 000000000..39cf621a5 --- /dev/null +++ b/Source/pages/pre_commit_check.php @@ -0,0 +1,231 @@ +info['repo_commit_needs_issue'] ) ? $t_repo->info['repo_commit_needs_issue'] : false; +$t_repo_commit_issues_must_exist = isset( $t_repo->info['repo_commit_issues_must_exist'] ) ? $t_repo->info['repo_commit_issues_must_exist'] : false; +$t_repo_commit_ownership_must_match = isset( $t_repo->info['repo_commit_ownership_must_match'] ) ? $t_repo->info['repo_commit_ownership_must_match'] : false; +$t_repo_commit_status_restricted = isset( $t_repo->info['repo_commit_status_restricted'] ) ? $t_repo->info['repo_commit_status_restricted'] : false; +$t_repo_commit_status_allowed = isset( $t_repo->info['repo_commit_status_allowed'] ) ? $t_repo->info['repo_commit_status_allowed'] : ''; +$t_repo_commit_project_restricted = isset( $t_repo->info['repo_commit_project_restricted'] ) ? $t_repo->info['repo_commit_project_restricted'] : ''; +$t_repo_commit_project_allowed = isset( $t_repo->info['repo_commit_project_allowed'] ) ? $t_repo->info['repo_commit_project_allowed'] : ''; +$t_repo_commit_committer_must_be_member = isset( $t_repo->info['repo_commit_committer_must_be_member'] ) ? $t_repo->info['repo_commit_committer_must_be_member'] : ''; +$t_repo_commit_committer_must_be_level = isset( $t_repo->info['repo_commit_committer_must_be_level'] ) ? $t_repo->info['repo_commit_committer_must_be_level'] : MantisEnum::getValues( config_get( 'access_levels_enum_string' ) ) ; + +$t_all_ok = true; + +# Check number of bugs referenced in the commit comment +if(( sizeof( $t_bug_list ) == 0 ) && $t_repo_commit_needs_issue ) { + + # It was expected that the commit comment would reference one of more bug + # IDs but this was not the case + + printf( "Check-Message: '%s'\r\n",plugin_lang_get( 'error_commit_needs_issue' ) ); + $t_all_ok = false; + +} else { + + # Loop all the bug IDs referenced in the commit comment + foreach( $t_bug_list as $t_bug_id ) { + + # Check existence first to prevent API throwing an error + if( bug_exists( $t_bug_id ) ) { + + $t_bug = bug_get( $t_bug_id ); + + # Ownership of ticket must match committer? + if( $t_repo_commit_ownership_must_match ) { + + if( 0 == $t_bug->handler_id ) { + $t_user_name = 'none'; + $t_user_email = 'none'; + } else { + $t_user_name = user_get_name( $t_bug->handler_id ); + $t_user_email = user_get_email( $t_bug->handler_id ); + } + + # Check that the username of the committer matches the user name + # or e-mail address of the owner of the ticket + if( !( strlen( $f_committer_name ) && + (( $t_user_name == $f_committer_name ) || + ( $t_user_email == $f_committer_name )))) { + + printf( "Check-Message: '%s : %s %d", + plugin_lang_get( 'error_commit_issue_ownership' ), + plugin_lang_get( 'issue' ), + $t_bug_id ); + + if( $t_informational_errors ) { + + # Informative errors turned on so display the user to whom + # the ticket is assigned + printf( " (%s/%s vs %s)", + $t_user_name, $t_user_email, $f_committer_name ); + } + + printf( "'\r\n" ); + $t_all_ok = false; + } + } # End ownership must match ticket + + # Only allowed to commit against tickets with a specific status? + if( $t_repo_commit_status_restricted ) { + + # Check that the bug's status is at a level for which a commit + # is allowed + if( !in_array( $t_bug->status, $t_repo_commit_status_allowed )) { + + printf( "Check-Message: '%s : %s %d", + plugin_lang_get( 'error_commit_issue_wrong_status' ), + plugin_lang_get( 'issue' ), + $t_bug_id ); + + if( $t_informational_errors ) { + + # Informative errors turned on so display a list of statuses for which + # a commit would be accepted + + # Get an array of the names of the statuses for which commit is allowed + $t_statuses = array_map( function( $p_status ) { return get_enum_element( 'status', $p_status ); }, $t_repo_commit_status_allowed ); + + printf( " (%s vs %s)", + get_enum_element( 'status', $t_bug->status ), + implode( ", ", $t_statuses )); + } + printf( "'\r\n" ); + $t_all_ok = false; + } + } # End only allowed to commit against tickets with a specific status + + # Only allowed to commit against Mantis tickets within specific project(s) + if( $t_repo_commit_project_restricted ) { + + if( !in_array( 0, $t_repo_commit_project_allowed ) && + !in_array( $t_bug->project_id, $t_repo_commit_project_allowed )) { + + printf( "Check-Message: '%s : %s %d", + plugin_lang_get( 'error_commit_issue_wrong_project' ), + plugin_lang_get( 'issue' ), + $t_bug_id ); + + if( $t_informational_errors ) { + + # Informative errors turned on so display a list of Mantis projects to + # which referenced tickets must belong + + # Get an array of the names of all the projects + $t_projects = array_map( function( $p_proj ) { return project_get_field( $p_proj, 'name' ); }, $t_repo_commit_project_allowed ); + + printf( " (%s vs %s)", + project_get_field( $t_bug->project_id, 'name' ), + implode( $t_projects, ", " )); + } + + printf( "'\r\n" ); + $t_all_ok = false; + } + } # End only allowed to commit against tickets within specific projects + + # Committer must belong to the Mantis project? + if( $t_repo_commit_committer_must_be_member ) { + + $t_user_id = user_get_id_by_name( $f_committer_name ); + + # Didn't find the username? Try the e-mail address + if( $t_user_id == false ) { + + $t_user_id = user_get_id_by_email( $f_committer_name ); + } + + /* Check that the user exists in Mantis */ + if( $t_user_id == false ) { + + printf( "Check-Message: '%s : %s %d (%s)'\r\n", + plugin_lang_get( 'error_commit_committer_not_found' ), + plugin_lang_get( 'issue' ), + $t_bug_id, $f_committer_name ); + $t_all_ok = false; + + /* Check that the user is assigned to the project */ + } elseif( ! project_includes_user( $t_bug->project_id, $t_user_id )) { + printf( "Check-Message: '%s : %s %d (%s)'\r\n", + plugin_lang_get( 'error_commit_committer_not_member' ), + plugin_lang_get( 'issue' ), + $t_bug_id, $f_committer_name ); + $t_all_ok = false; + + } else { + + $t_user_access_level = access_get_local_level( $t_user_id, $t_bug->project_id ); + if( in_array( $t_user_access_level, $t_repo_commit_committer_must_be_level )) { + + printf( "Check-Message: '%s : %s %d", + plugin_lang_get( 'error_commit_committer_wrong_level' ), + plugin_lang_get( 'issue' ), + $t_bug_id ); + + if( $t_informational_errors ) { + + # Informative errors turned on so display a list of access levels + # for which commit is allowed + + $t_levels = MantisEnum::getAssocArrayIndexedByValues( config_get( 'access_levels_enum_string' ) ); + $t_allowed_levels = array_intersect_key( $t_levels, array_flip( $t_repo_commit_committer_must_be_level )); + + printf( " (%s vs %s)", + $t_levels[ $t_user_access_level ], + implode( ", ", array_values( $t_allowed_levels ) ) ); + } + printf( "'\r\n" ); + $t_all_ok = false; + } + } + } # End committer must belong to mantis project + } else { + + /* If the issue doesn't exist, then can't perform the checks */ + if( $t_repo_commit_issues_must_exist || + $t_repo_commit_ownership_must_match || + $t_repo_commit_status_restricted || + $t_repo_commit_project_restricted || + $t_repo_commit_committer_must_be_member ) { + + printf( "Check-Message: '%s : %s %d'\r\n", + plugin_lang_get( 'error_commit_nonexistent_issue' ), + plugin_lang_get( 'issue' ), + $t_bug_id ); + $t_all_ok = false; + } + } + } +} +printf( "Check-OK: %d\r\n",$t_all_ok ); + +?> \ No newline at end of file diff --git a/Source/pages/repo_update.php b/Source/pages/repo_update.php index f3d265a93..de172c7bf 100644 --- a/Source/pages/repo_update.php +++ b/Source/pages/repo_update.php @@ -10,12 +10,31 @@ $f_repo_name = gpc_get_string( 'repo_name' ); $f_repo_url = gpc_get_string( 'repo_url' ); +f_repo_commit_needs_issue = gpc_get_bool( 'repo_commit_needs_issue', false ); +$f_repo_commit_issues_must_exist = gpc_get_bool( 'repo_commit_issues_must_exist', false ); +$f_repo_commit_ownership_must_match = gpc_get_bool( 'repo_commit_ownership_must_match', false ); +$f_repo_commit_status_restricted = gpc_get_bool( 'repo_commit_status_restricted', false ); +$f_repo_commit_status_allowed = gpc_get_int_array( 'repo_commit_status_allowed', MantisEnum::getValues( config_get( 'status_enum_string' ) )); +$f_repo_commit_project_restricted = gpc_get_bool( 'repo_commit_project_restricted', false ); +$f_repo_commit_project_allowed = gpc_get_int_array( 'repo_commit_project_allowed', Array( 0 ) ); +$f_repo_commit_committer_must_be_member = gpc_get_bool( 'repo_commit_committer_must_be_member', false ); +$f_repo_commit_committer_must_be_level = gpc_get_int_array( 'repo_commit_committer_must_be_level', MantisEnum::getValues( config_get( 'access_levels_enum_string' ) )); + $t_repo = SourceRepo::load( $f_repo_id ); $t_vcs = SourceVCS::repo( $t_repo ); $t_type = SourceType($t_repo->type); $t_repo->name = $f_repo_name; $t_repo->url = $f_repo_url; +$t_repo->info['repo_commit_needs_issue'] = $f_repo_commit_needs_issue; +$t_repo->info['repo_commit_issues_must_exist'] = $f_repo_commit_issues_must_exist; +$t_repo->info['repo_commit_ownership_must_match'] = $f_repo_commit_ownership_must_match; +$t_repo->info['repo_commit_status_restricted'] = $f_repo_commit_status_restricted; +$t_repo->info['repo_commit_status_allowed'] = $f_repo_commit_status_allowed; +$t_repo->info['repo_commit_project_restricted'] = $f_repo_commit_project_restricted; +$t_repo->info['repo_commit_project_allowed'] = $f_repo_commit_project_allowed; +$t_repo->info['repo_commit_committer_must_be_member'] = $f_repo_commit_committer_must_be_member; +$t_repo->info['repo_commit_committer_must_be_level'] = $f_repo_commit_committer_must_be_level; /** @var SourceRepo $t_updated_repo */ $t_updated_repo = $t_vcs->update_repo( $t_repo ); diff --git a/Source/pages/repo_update_page.php b/Source/pages/repo_update_page.php index 19a16b120..893a8c002 100644 --- a/Source/pages/repo_update_page.php +++ b/Source/pages/repo_update_page.php @@ -12,6 +12,16 @@ $t_vcs = SourceVCS::repo( $t_repo ); $t_type = SourceType($t_repo->type); +$t_repo_commit_needs_issue = isset( $t_repo->info['repo_commit_needs_issue'] ) ? $t_repo->info['repo_commit_needs_issue'] : false; +$t_repo_commit_issues_must_exist = isset( $t_repo->info['repo_commit_issues_must_exist'] ) ? $t_repo->info['repo_commit_issues_must_exist'] : false; +$t_repo_commit_ownership_must_match = isset( $t_repo->info['repo_commit_ownership_must_match'] ) ? $t_repo->info['repo_commit_ownership_must_match'] : false; +$t_repo_commit_committer_must_be_member = isset( $t_repo->info['repo_commit_committer_must_be_member'] ) ? $t_repo->info['repo_commit_committer_must_be_member'] : false; +$t_repo_commit_committer_must_be_level = isset( $t_repo->info['repo_commit_committer_must_be_level'] ) ? $t_repo->info['repo_commit_committer_must_be_level'] : MantisEnum::getValues( config_get( 'access_levels_enum_string' ) ) ; +$t_repo_commit_status_restricted = isset( $t_repo->info['repo_commit_status_restricted'] ) ? $t_repo->info['repo_commit_status_restricted'] : false; +$t_repo_commit_status_allowed = isset( $t_repo->info['repo_commit_status_allowed'] ) ? $t_repo->info['repo_commit_status_allowed'] : MantisEnum::getValues( config_get( 'status_enum_string' )); +$t_repo_commit_project_restricted = isset( $t_repo->info['repo_commit_project_restricted'] ) ? $t_repo->info['repo_commit_project_restricted'] : false; +$t_repo_commit_project_allowed = isset( $t_repo->info['repo_commit_project_allowed'] ) ? $t_repo->info['repo_commit_project_allowed'] : Array( 0 ); + layout_page_header( plugin_lang_get( 'title' ) ); layout_page_begin(); @@ -69,6 +79,58 @@ update_repo_form( $t_repo ) ?> + +
| + | /> | +
| + | /> | +
| + | /> | +
| + | /> | +
| + | + |
| + | /> | +
| + | + |
| + | /> | +
| + | + |