From 5187c8ac28a501d8a4fec7100c0b0633baacd0f2 Mon Sep 17 00:00:00 2001 From: michaelangeloio Date: Sun, 30 Nov 2025 14:09:14 -0500 Subject: [PATCH] feat: add project contributors panel feature This commit introduces a new feature to track and display project contributors, including their commit counts. Key changes include: - Creation of a `project_contributors` table to store contributor statistics. - Implementation of methods to fetch contributors from the repository and cache their data. - Updates to the user interface to display contributors in a GitHub-style panel. - Enhancements to the Gitaly client for listing contributors. - Addition of tests to ensure functionality and data integrity. This feature aims to improve visibility into project contributions and enhance user engagement with contributor statistics. --- Gemfile | 3 +- Gemfile.lock | 10 +- app/assets/javascripts/user_popovers.js | 3 +- .../components/user_popover/user_popover.vue | 8 + app/helpers/projects_helper.rb | 22 ++ app/models/projects/contributor.rb | 123 ++++++++++ app/models/repository.rb | 18 ++ app/services/git/branch_hooks_service.rb | 5 + .../projects/_contributors_panel.html.haml | 67 ++++++ app/views/projects/_sidebar.html.haml | 3 + app/workers/all_queues.yml | 10 + .../projects/contributors_cache_worker.rb | 36 +++ db/docs/project_contributors.yml | 13 ++ ...51129180216_create_project_contributors.rb | 27 +++ ...217_add_project_contributors_project_fk.rb | 17 ++ db/schema_migrations/20251129180216 | 1 + db/schema_migrations/20251129180217 | 1 + db/structure.sql | 221 +++++++++++++----- lib/gitlab/git/repository.rb | 16 ++ lib/gitlab/gitaly_client/commit_service.rb | 62 +++++ locale/gitlab.pot | 11 + spec/factories/project_contributors.rb | 10 + .../gitaly_client/commit_service_spec.rb | 74 ++++++ spec/models/projects/contributor_spec.rb | 95 ++++++++ 24 files changed, 798 insertions(+), 58 deletions(-) create mode 100644 app/models/projects/contributor.rb create mode 100644 app/views/projects/_contributors_panel.html.haml create mode 100644 app/workers/projects/contributors_cache_worker.rb create mode 100644 db/docs/project_contributors.yml create mode 100644 db/migrate/20251129180216_create_project_contributors.rb create mode 100644 db/migrate/20251129180217_add_project_contributors_project_fk.rb create mode 100644 db/schema_migrations/20251129180216 create mode 100644 db/schema_migrations/20251129180217 create mode 100644 spec/factories/project_contributors.rb create mode 100644 spec/models/projects/contributor_spec.rb diff --git a/Gemfile b/Gemfile index 52bfd45efa3082..75c4e52a4c70c2 100644 --- a/Gemfile +++ b/Gemfile @@ -634,7 +634,8 @@ gem 'ssh_data', '~> 2.0', feature_category: :shared gem 'spamcheck', '~> 1.3.0', feature_category: :insider_threat # Gitaly GRPC protocol definitions -gem 'gitaly', '~> 18.6.0', feature_category: :gitaly +# Using local gitaly gem for ListContributors RPC development +gem 'gitaly', path: '../gitaly/_build/gitaly', feature_category: :gitaly # KAS GRPC protocol definitions gem 'gitlab-kas-grpc', '~> 18.5.0-rc4', feature_category: :deployment_management diff --git a/Gemfile.lock b/Gemfile.lock index b430d961ddfc19..24323b37095567 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,3 +1,9 @@ +PATH + remote: ../gitaly/_build/gitaly + specs: + gitaly (18.6.0.pre.fa577c21b) + grpc (~> 1.0) + PATH remote: gems/activerecord-gitlab specs: @@ -732,8 +738,6 @@ GEM git (1.19.1) addressable (~> 2.8) rchardet (~> 1.8) - gitaly (18.6.0) - grpc (~> 1.0) gitlab (4.19.0) httparty (~> 0.20) terminal-table (>= 1.5.1) @@ -2175,7 +2179,7 @@ DEPENDENCIES gettext (~> 3.5, >= 3.5.1) gettext_i18n_rails (~> 1.13.0) git (~> 1.8) - gitaly (~> 18.6.0) + gitaly! gitlab-active-context! gitlab-backup-cli! gitlab-chronic (~> 0.10.5) diff --git a/app/assets/javascripts/user_popovers.js b/app/assets/javascripts/user_popovers.js index b9dcb097c849d0..8aeb916e511012 100644 --- a/app/assets/javascripts/user_popovers.js +++ b/app/assets/javascripts/user_popovers.js @@ -13,7 +13,7 @@ const removeTitle = (el) => { const getPreloadedUserInfo = (dataset) => { const userId = dataset.user || dataset.userId; - const { username, name, avatarUrl, email } = dataset; + const { username, name, avatarUrl, email, commitCountText } = dataset; return { userId, @@ -21,6 +21,7 @@ const getPreloadedUserInfo = (dataset) => { name, avatarUrl, email, + commitCountText, }; }; diff --git a/app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue b/app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue index b2756fc248353d..d7069aabce0b89 100644 --- a/app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue +++ b/app/assets/javascripts/vue_shared/components/user_popover/user_popover.vue @@ -275,6 +275,14 @@ export default {