diff options
44 files changed, 344 insertions, 228 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 4a1e486e3c3..b5a8e8e6d73 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -36,9 +36,13 @@ workflow: # they serve no purpose and will run anyway when the changes are merged. - if: '$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME =~ /^release-tools\/\d+\.\d+\.\d+-rc\d+$/ && $CI_MERGE_REQUEST_TARGET_BRANCH_NAME =~ /^[\d-]+-stable(-ee)?$/ && $CI_PROJECT_PATH == "gitlab-org/gitlab"' when: never - # For merge requests, create a pipeline. + # For merged result pipelines, set $QA_IMAGE, since $CI_MERGE_REQUEST_SOURCE_BRANCH_SHA is only available for merged result pipelines. + - if: '$CI_MERGE_REQUEST_EVENT_TYPE == "merged_result" || $CI_MERGE_REQUEST_EVENT_TYPE == "merge_train"' + variables: + QA_IMAGE: "${CI_REGISTRY}/${CI_PROJECT_PATH}/gitlab-ee-qa:${CI_MERGE_REQUEST_SOURCE_BRANCH_SHA}" + # Also run (detached) merge request pipelines. - if: '$CI_MERGE_REQUEST_IID' - # For the 2-hourly scheduled pipelines, we set specific variables + # For the 2-hourly scheduled pipelines, we set specific variables. - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH && $CI_PIPELINE_SOURCE == "schedule" && $FREQUENCY == "2-hourly"' variables: CRYSTALBALL: "true" @@ -88,6 +92,9 @@ variables: RSPEC_FAIL_FAST_ENABLED: "true" # Set it to "false" to disable RSpec fail-fast SIMPLECOV: "true" + # For the default QA image, we use $CI_COMMIT_SHA as tag since it's always available and we override it for specific workflow.rules (see above) + QA_IMAGE: "${CI_REGISTRY}/${CI_PROJECT_PATH}/gitlab-ee-qa:${CI_COMMIT_SHA}" + # Preparing custom clone path to reduce space used by all random forks # on GitLab.com's Shared Runners. Our main forks - especially the security # ones - will have this variable overwritten in the project settings, so that diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml index 82611ee713f..fb6d5533309 100644 --- a/.gitlab/ci/rules.gitlab-ci.yml +++ b/.gitlab/ci/rules.gitlab-ci.yml @@ -370,19 +370,11 @@ when: never - <<: *if-dot-com-gitlab-org-and-security-merge-request changes: *ci-build-images-patterns - variables: - QA_IMAGE: "${CI_REGISTRY}/${CI_PROJECT_PATH}/gitlab-ee-qa:${CI_MERGE_REQUEST_SOURCE_BRANCH_SHA}" - <<: *if-dot-com-gitlab-org-and-security-merge-request changes: *code-qa-patterns - variables: - QA_IMAGE: "${CI_REGISTRY}/${CI_PROJECT_PATH}/gitlab-ee-qa:${CI_MERGE_REQUEST_SOURCE_BRANCH_SHA}" - <<: *if-dot-com-gitlab-org-default-branch changes: *code-qa-patterns - variables: - QA_IMAGE: "${CI_REGISTRY}/${CI_PROJECT_PATH}/gitlab-ee-qa:${CI_COMMIT_SHA}" - <<: *if-dot-com-gitlab-org-schedule - variables: - QA_IMAGE: "${CI_REGISTRY}/${CI_PROJECT_PATH}/gitlab-ee-qa:${CI_COMMIT_SHA}" .build-images:rules:build-assets-image: rules: @@ -594,24 +586,16 @@ when: never - <<: *if-dot-com-gitlab-org-and-security-merge-request changes: *ci-qa-patterns - variables: - QA_IMAGE: "${CI_REGISTRY}/${CI_PROJECT_PATH}/gitlab-ee-qa:${CI_MERGE_REQUEST_SOURCE_BRANCH_SHA}" allow_failure: true - <<: *if-dot-com-gitlab-org-and-security-merge-request changes: *qa-patterns - variables: - QA_IMAGE: "${CI_REGISTRY}/${CI_PROJECT_PATH}/gitlab-ee-qa:${CI_MERGE_REQUEST_SOURCE_BRANCH_SHA}" allow_failure: true - <<: *if-dot-com-gitlab-org-and-security-merge-request changes: *code-patterns when: manual - variables: - QA_IMAGE: "${CI_REGISTRY}/${CI_PROJECT_PATH}/gitlab-ee-qa:${CI_MERGE_REQUEST_SOURCE_BRANCH_SHA}" allow_failure: true - <<: *if-dot-com-gitlab-org-schedule allow_failure: true - variables: - QA_IMAGE: "${CI_REGISTRY}/${CI_PROJECT_PATH}/gitlab-ee-qa:${CI_COMMIT_SHA}" ############### # Rails rules # @@ -1298,17 +1282,11 @@ when: never - <<: *if-dot-com-gitlab-org-merge-request changes: *ci-review-patterns - variables: - QA_IMAGE: "${CI_REGISTRY}/${CI_PROJECT_PATH}/gitlab-ee-qa:${CI_MERGE_REQUEST_SOURCE_BRANCH_SHA}" - <<: *if-dot-com-gitlab-org-merge-request changes: *frontend-patterns - variables: - QA_IMAGE: "${CI_REGISTRY}/${CI_PROJECT_PATH}/gitlab-ee-qa:${CI_MERGE_REQUEST_SOURCE_BRANCH_SHA}" allow_failure: true - <<: *if-dot-com-gitlab-org-merge-request changes: *code-qa-patterns - variables: - QA_IMAGE: "${CI_REGISTRY}/${CI_PROJECT_PATH}/gitlab-ee-qa:${CI_MERGE_REQUEST_SOURCE_BRANCH_SHA}" allow_failure: true # The rule needs to be duplicated between `on_success` and `on_failure` @@ -1344,13 +1322,9 @@ - <<: *if-dot-com-gitlab-org-merge-request changes: *code-patterns when: manual - variables: - QA_IMAGE: "${CI_REGISTRY}/${CI_PROJECT_PATH}/gitlab-ee-qa:${CI_MERGE_REQUEST_SOURCE_BRANCH_SHA}" allow_failure: true - <<: *if-dot-com-gitlab-org-merge-request changes: *qa-patterns - variables: - QA_IMAGE: "${CI_REGISTRY}/${CI_PROJECT_PATH}/gitlab-ee-qa:${CI_MERGE_REQUEST_SOURCE_BRANCH_SHA}" allow_failure: true # The rule needs to be duplicated between `on_success` and `on_failure` diff --git a/app/assets/javascripts/jobs/components/sidebar_job_details_container.vue b/app/assets/javascripts/jobs/components/sidebar_job_details_container.vue index a6eff743ce9..d90377029c5 100644 --- a/app/assets/javascripts/jobs/components/sidebar_job_details_container.vue +++ b/app/assets/javascripts/jobs/components/sidebar_job_details_container.vue @@ -46,7 +46,7 @@ export default { return timeIntervalInWords(this.job.queued); }, runnerHelpUrl() { - return helpPagePath('ci/runners/index.html', { + return helpPagePath('ci/runners/configure_runners.html', { anchor: 'set-maximum-job-timeout-for-a-runner', }); }, diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 700acc46d8d..145b4d10b16 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -304,7 +304,7 @@ class Admin::UsersController < Admin::ApplicationController end def user - @user ||= find_routable!(User, params[:id]) + @user ||= find_routable!(User, params[:id], request.path_info) end def build_canonical_path(user) diff --git a/app/controllers/concerns/project_unauthorized.rb b/app/controllers/concerns/project_unauthorized.rb index 7238840440f..b58f6589f9b 100644 --- a/app/controllers/concerns/project_unauthorized.rb +++ b/app/controllers/concerns/project_unauthorized.rb @@ -3,7 +3,7 @@ module ProjectUnauthorized module ControllerActions def self.on_routable_not_found - lambda do |routable| + lambda do |routable, path_info| return unless routable.is_a?(Project) label = routable.external_authorization_classification_label diff --git a/app/controllers/concerns/routable_actions.rb b/app/controllers/concerns/routable_actions.rb index 7257378f465..57108369c64 100644 --- a/app/controllers/concerns/routable_actions.rb +++ b/app/controllers/concerns/routable_actions.rb @@ -3,13 +3,13 @@ module RoutableActions extend ActiveSupport::Concern - def find_routable!(routable_klass, requested_full_path, extra_authorization_proc: nil) - routable = routable_klass.find_by_full_path(requested_full_path, follow_redirects: request.get?) + def find_routable!(routable_klass, routable_full_path, path_info, extra_authorization_proc: nil) + routable = routable_klass.find_by_full_path(routable_full_path, follow_redirects: request.get?) if routable_authorized?(routable, extra_authorization_proc) - ensure_canonical_path(routable, requested_full_path) + ensure_canonical_path(routable, routable_full_path) routable else - perform_not_found_actions(routable, not_found_actions) + perform_not_found_actions(routable, not_found_actions, path_info) route_not_found unless performed? @@ -21,11 +21,11 @@ module RoutableActions [ProjectUnauthorized::ControllerActions.on_routable_not_found] end - def perform_not_found_actions(routable, actions) + def perform_not_found_actions(routable, actions, path_info) actions.each do |action| break if performed? - instance_exec(routable, &action) + instance_exec(routable, path_info, &action) end end @@ -42,13 +42,13 @@ module RoutableActions end end - def ensure_canonical_path(routable, requested_full_path) + def ensure_canonical_path(routable, routable_full_path) return unless request.get? canonical_path = routable.full_path - if canonical_path != requested_full_path - if !request.xhr? && request.format.html? && canonical_path.casecmp(requested_full_path) != 0 - flash[:notice] = "#{routable.class.to_s.titleize} '#{requested_full_path}' was moved to '#{canonical_path}'. Please update any links and bookmarks that may still have the old path." + if canonical_path != routable_full_path + if !request.xhr? && request.format.html? && canonical_path.casecmp(routable_full_path) != 0 + flash[:notice] = "#{routable.class.to_s.titleize} '#{routable_full_path}' was moved to '#{canonical_path}'. Please update any links and bookmarks that may still have the old path." end redirect_to build_canonical_path(routable), status: :moved_permanently diff --git a/app/controllers/groups/application_controller.rb b/app/controllers/groups/application_controller.rb index f6c71ac8087..69081835c4d 100644 --- a/app/controllers/groups/application_controller.rb +++ b/app/controllers/groups/application_controller.rb @@ -24,7 +24,7 @@ class Groups::ApplicationController < ApplicationController end def group - @group ||= find_routable!(Group, params[:group_id] || params[:id]) + @group ||= find_routable!(Group, params[:group_id] || params[:id], request.path_info) end def group_projects diff --git a/app/controllers/groups/clusters/applications_controller.rb b/app/controllers/groups/clusters/applications_controller.rb index 8dd8a01cf40..ce6fda4143c 100644 --- a/app/controllers/groups/clusters/applications_controller.rb +++ b/app/controllers/groups/clusters/applications_controller.rb @@ -13,6 +13,6 @@ class Groups::Clusters::ApplicationsController < Clusters::ApplicationsControlle end def group - @group ||= find_routable!(Group, params[:group_id] || params[:id]) + @group ||= find_routable!(Group, params[:group_id] || params[:id], request.path_info) end end diff --git a/app/controllers/groups/clusters/integrations_controller.rb b/app/controllers/groups/clusters/integrations_controller.rb index e8c8a14c164..61b308f7d1b 100644 --- a/app/controllers/groups/clusters/integrations_controller.rb +++ b/app/controllers/groups/clusters/integrations_controller.rb @@ -13,6 +13,6 @@ class Groups::Clusters::IntegrationsController < Clusters::IntegrationsControlle end def group - @group ||= find_routable!(Group, params[:group_id] || params[:id]) + @group ||= find_routable!(Group, params[:group_id] || params[:id], request.path_info) end end diff --git a/app/controllers/groups/clusters_controller.rb b/app/controllers/groups/clusters_controller.rb index 33bfc24885f..6f3eecf8296 100644 --- a/app/controllers/groups/clusters_controller.rb +++ b/app/controllers/groups/clusters_controller.rb @@ -15,7 +15,7 @@ class Groups::ClustersController < Clusters::ClustersController end def group - @group ||= find_routable!(Group, params[:group_id] || params[:id]) + @group ||= find_routable!(Group, params[:group_id] || params[:id], request.path_info) end def metrics_dashboard_params diff --git a/app/controllers/profiles/groups_controller.rb b/app/controllers/profiles/groups_controller.rb index e76ee0a6cea..2571e92e071 100644 --- a/app/controllers/profiles/groups_controller.rb +++ b/app/controllers/profiles/groups_controller.rb @@ -6,7 +6,7 @@ class Profiles::GroupsController < Profiles::ApplicationController feature_category :users def update - group = find_routable!(Group, params[:id]) + group = find_routable!(Group, params[:id], request.path_info) notification_setting = current_user.notification_settings_for(group) if notification_setting.update(update_params) diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb index ca2692438e8..cf2ecb0673e 100644 --- a/app/controllers/projects/application_controller.rb +++ b/app/controllers/projects/application_controller.rb @@ -26,7 +26,7 @@ class Projects::ApplicationController < ApplicationController path = File.join(params[:namespace_id], params[:project_id] || params[:id]) auth_proc = ->(project) { !project.pending_delete? } - @project = find_routable!(Project, path, extra_authorization_proc: auth_proc) + @project = find_routable!(Project, path, request.path_info, extra_authorization_proc: auth_proc) end def build_canonical_path(project) diff --git a/app/controllers/projects/clusters/applications_controller.rb b/app/controllers/projects/clusters/applications_controller.rb index 2a04b007304..6c5778124e8 100644 --- a/app/controllers/projects/clusters/applications_controller.rb +++ b/app/controllers/projects/clusters/applications_controller.rb @@ -10,6 +10,6 @@ class Projects::Clusters::ApplicationsController < Clusters::ApplicationsControl end def project - @project ||= find_routable!(Project, File.join(params[:namespace_id], params[:project_id])) + @project ||= find_routable!(Project, File.join(params[:namespace_id], params[:project_id]), request.path_info) end end diff --git a/app/controllers/projects/clusters/integrations_controller.rb b/app/controllers/projects/clusters/integrations_controller.rb index 94b8dd653e6..eed6c1dccc4 100644 --- a/app/controllers/projects/clusters/integrations_controller.rb +++ b/app/controllers/projects/clusters/integrations_controller.rb @@ -10,6 +10,6 @@ class Projects::Clusters::IntegrationsController < ::Clusters::IntegrationsContr end def project - @project ||= find_routable!(Project, File.join(params[:namespace_id], params[:project_id])) + @project ||= find_routable!(Project, File.join(params[:namespace_id], params[:project_id]), request.path_info) end end diff --git a/app/controllers/projects/clusters_controller.rb b/app/controllers/projects/clusters_controller.rb index 8acf5235c1a..0aef497d28d 100644 --- a/app/controllers/projects/clusters_controller.rb +++ b/app/controllers/projects/clusters_controller.rb @@ -17,7 +17,7 @@ class Projects::ClustersController < Clusters::ClustersController end def project - @project ||= find_routable!(Project, File.join(params[:namespace_id], params[:project_id])) + @project ||= find_routable!(Project, File.join(params[:namespace_id], params[:project_id]), request.path_info) end def repository diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 7282fc26121..1b927afdcf5 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -172,7 +172,7 @@ class UsersController < ApplicationController private def user - @user ||= find_routable!(User, params[:username]) + @user ||= find_routable!(User, params[:username], request.path_info) end def personal_projects diff --git a/app/helpers/boards_helper.rb b/app/helpers/boards_helper.rb index f72f8bfd151..33d5bae88f4 100644 --- a/app/helpers/boards_helper.rb +++ b/app/helpers/boards_helper.rb @@ -119,14 +119,6 @@ module BoardsHelper } end - def boards_link_text - if current_board_parent.multiple_issue_boards_available? - s_("IssueBoards|Boards") - else - s_("IssueBoards|Board") - end - end - def recent_boards_path recent_project_boards_path(@project) if current_board_parent.is_a?(Project) end diff --git a/app/helpers/nav_helper.rb b/app/helpers/nav_helper.rb index 6c57a31f3db..9a6630ec3cb 100644 --- a/app/helpers/nav_helper.rb +++ b/app/helpers/nav_helper.rb @@ -67,15 +67,6 @@ module NavHelper %w(dev_ops_report usage_trends) end - def group_issues_sub_menu_items - %w[ - groups#issues - milestones#index - boards#index - boards#show - ] - end - private def get_header_links diff --git a/app/views/admin/application_settings/_prometheus.html.haml b/app/views/admin/application_settings/_prometheus.html.haml index f102b3d580b..59690fdee8b 100644 --- a/app/views/admin/application_settings/_prometheus.html.haml +++ b/app/views/admin/application_settings/_prometheus.html.haml @@ -2,26 +2,23 @@ = form_errors(@application_setting) %fieldset - %p - - link_to_restart = link_to(_('restart'), help_page_path('administration/restart_gitlab')) - = _('Enable a Prometheus metrics endpoint at %{metrics_path} to expose a variety of statistics on the health and performance of GitLab. Additional information on authenticating and connecting to the metrics endpoint is available %{link}.').html_safe % { metrics_path: "<code>#{metrics_path}</code>".html_safe, link: link_to(_('here'), admin_health_check_path) } - = _('This setting requires a %{link_to_restart} to take effect.').html_safe % { link_to_restart: link_to_restart } - = link_to sprite_icon('question-o'), help_page_path('administration/monitoring/prometheus/index') .form-group .form-check = f.check_box :prometheus_metrics_enabled, class: 'form-check-input' = f.label :prometheus_metrics_enabled, class: 'form-check-label' do - = _("Enable Prometheus Metrics") + = _("Enable health and performance metrics endpoint") + .form-text.text-muted + = _('Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required.') + = link_to _('Learn More.'), help_page_path('administration/monitoring/prometheus/gitlab_metrics.md'), target: '_blank' - unless Gitlab::Metrics.metrics_folder_present? .form-text.text-muted %strong.cred= _("WARNING:") = _("Environment variable %{code_start}%{environment_variable}%{code_end} does not exist or is not pointing to a valid directory.").html_safe % { environment_variable: prometheus_multiproc_dir, code_start: '<code>'.html_safe, code_end: '</code>'.html_safe } = link_to sprite_icon('question-o'), help_page_path('administration/monitoring/prometheus/gitlab_metrics', anchor: 'metrics-shared-directory') .form-group - = f.label :metrics_method_call_threshold, _('Method Call Threshold (ms)'), class: 'label-bold' + = f.label :metrics_method_call_threshold, _('Method call threshold (ms)'), class: 'label-bold' = f.number_field :metrics_method_call_threshold, class: 'form-control gl-form-input' .form-text.text-muted - A method call is only tracked when it takes longer to complete than - the given amount of milliseconds. + Only track method calls that take longer to complete than the given duration. = f.submit _('Save changes'), class: "gl-button btn btn-confirm" diff --git a/app/views/admin/application_settings/metrics_and_profiling.html.haml b/app/views/admin/application_settings/metrics_and_profiling.html.haml index 121f0e96f1d..f1e37c76130 100644 --- a/app/views/admin/application_settings/metrics_and_profiling.html.haml +++ b/app/views/admin/application_settings/metrics_and_profiling.html.haml @@ -11,7 +11,7 @@ %button.btn.gl-button.btn-default.js-settings-toggle{ type: 'button' } = expanded_by_default? ? _('Collapse') : _('Expand') %p - = _('Enable and configure Prometheus metrics.') + = _('Monitor the health and performance of GitLab with Prometheus.') .settings-content = render 'prometheus' diff --git a/app/views/admin/groups/_form.html.haml b/app/views/admin/groups/_form.html.haml index e7e0e58f6fb..91a018121c0 100644 --- a/app/views/admin/groups/_form.html.haml +++ b/app/views/admin/groups/_form.html.haml @@ -1,4 +1,4 @@ -= form_for [:admin, @group] do |f| += gitlab_ui_form_for [:admin, @group] do |f| = form_errors(@group) = render 'shared/group_form', f: f = render 'shared/group_form_description', f: f diff --git a/app/views/groups/settings/_project_access_token_creation.html.haml b/app/views/groups/settings/_project_access_token_creation.html.haml index 8be17c6cc30..948b25390ba 100644 --- a/app/views/groups/settings/_project_access_token_creation.html.haml +++ b/app/views/groups/settings/_project_access_token_creation.html.haml @@ -1,10 +1,9 @@ - return unless render_setting_to_allow_project_access_token_creation?(group) .form-group.gl-mb-3 - .gl-form-checkbox.custom-control.custom-checkbox - = f.check_box :resource_access_token_creation_allowed, checked: group.namespace_settings.resource_access_token_creation_allowed?, class: 'custom-control-input', data: { qa_selector: 'resource_access_token_creation_allowed_checkbox' } - = f.label :resource_access_token_creation_allowed, class: 'custom-control-label' do - %span= s_('GroupSettings|Allow project access token creation') - - project_access_tokens_link = help_page_path('user/project/settings/project_access_tokens') - - link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: project_access_tokens_link } - %p.help-text= s_('GroupSettings|Users can create %{link_start}project access tokens%{link_end} for projects in this group.').html_safe % { link_start: link_start, link_end: '</a>'.html_safe } + - project_access_tokens_link = help_page_path('user/project/settings/project_access_tokens') + - link_start = '<a href="%{url}" target="_blank" rel="noopener noreferrer">'.html_safe % { url: project_access_tokens_link } + = f.gitlab_ui_checkbox_component :resource_access_token_creation_allowed, + s_('GroupSettings|Allow project access token creation'), + checkbox_options: { checked: group.namespace_settings.resource_access_token_creation_allowed?, data: { qa_selector: 'resource_access_token_creation_allowed_checkbox' } }, + help_text: s_('GroupSettings|Users can create %{link_start}project access tokens%{link_end} for projects in this group.').html_safe % { link_start: link_start, link_end: '</a>'.html_safe } diff --git a/app/views/layouts/nav/sidebar/_group_menus.html.haml b/app/views/layouts/nav/sidebar/_group_menus.html.haml index 8e5e2322e55..085e660bae7 100644 --- a/app/views/layouts/nav/sidebar/_group_menus.html.haml +++ b/app/views/layouts/nav/sidebar/_group_menus.html.haml @@ -1,42 +1,5 @@ -- issues_count = cached_issuables_count(@group, type: :issues) - merge_requests_count = cached_issuables_count(@group, type: :merge_requests) -- if group_sidebar_link?(:issues) - = nav_link(path: group_issues_sub_menu_items, unless: -> { current_path?('issues_analytics#show') }) do - = link_to issues_group_path(@group), data: { qa_selector: 'group_issues_item' }, class: 'has-sub-items' do - .nav-icon-container - = sprite_icon('issues') - %span.nav-item-name - = _('Issues') - %span.badge.badge-pill.count= issues_count - - %ul.sidebar-sub-level-items{ data: { qa_selector: 'group_issues_sidebar_submenu'} } - = nav_link(path: group_issues_sub_menu_items, html_options: { class: "fly-out-top-item" } ) do - = link_to issues_group_path(@group) do - %strong.fly-out-top-item-name - = _('Issues') - %span.badge.badge-pill.count.issue_counter.fly-out-badge= issues_count - - %li.divider.fly-out-top-item - = nav_link(path: 'groups#issues', html_options: { class: 'home' }) do - = link_to issues_group_path(@group), title: _('List') do - %span - = _('List') - - - if group_sidebar_link?(:boards) - = nav_link(path: ['boards#index', 'boards#show']) do - = link_to group_boards_path(@group), title: boards_link_text, data: { qa_selector: 'group_issue_boards_link' } do - %span - = boards_link_text - - - if group_sidebar_link?(:milestones) - = nav_link(path: 'milestones#index') do - = link_to group_milestones_path(@group), title: _('Milestones'), data: { qa_selector: 'group_milestones_link' } do - %span - = _('Milestones') - - = render_if_exists 'layouts/nav/sidebar/group_iterations_link' - - if group_sidebar_link?(:merge_requests) = nav_link(path: 'groups#merge_requests') do = link_to merge_requests_group_path(@group) do diff --git a/app/views/shared/_allow_request_access.html.haml b/app/views/shared/_allow_request_access.html.haml index 93868f13e58..ca09fd39dc1 100644 --- a/app/views/shared/_allow_request_access.html.haml +++ b/app/views/shared/_allow_request_access.html.haml @@ -1,6 +1,6 @@ - label_class = local_assigns.fetch(:bold_label, false) ? 'font-weight-bold' : '' -.gl-form-checkbox.custom-control.custom-checkbox - = form.check_box :request_access_enabled, class: 'custom-control-input', data: { qa_selector: 'request_access_checkbox' } - = form.label :request_access_enabled, class: 'custom-control-label' do - %span{ class: label_class }= _('Allow users to request access (if visibility is public or internal)') += form.gitlab_ui_checkbox_component :request_access_enabled, + _('Allow users to request access (if visibility is public or internal)'), + label_options: { class: label_class }, + checkbox_options: { data: { qa_selector: 'request_access_checkbox' } } diff --git a/config/helpers/is_jh_env.js b/config/helpers/is_jh_env.js index 5afeee2f498..10251d3166e 100644 --- a/config/helpers/is_jh_env.js +++ b/config/helpers/is_jh_env.js @@ -12,4 +12,5 @@ const ROOT_PATH = path.resolve(__dirname, '../..'); // lib/gitlab.rb: Gitlab.jh? // Since IS_EE already satisifies the conditions of not being a FOSS_ONLY. // const isFossOnly = JSON.parse(process.env.FOSS_ONLY || 'false'); -module.exports = IS_EE && fs.existsSync(path.join(ROOT_PATH, 'jh')); +const isEEOnly = JSON.parse(process.env.EE_ONLY || 'false'); +module.exports = IS_EE && !isEEOnly && fs.existsSync(path.join(ROOT_PATH, 'jh')); diff --git a/db/migrate/20210721145029_add_state_to_members.rb b/db/migrate/20210721145029_add_state_to_members.rb new file mode 100644 index 00000000000..383ac518754 --- /dev/null +++ b/db/migrate/20210721145029_add_state_to_members.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class AddStateToMembers < ActiveRecord::Migration[6.1] + include Gitlab::Database::MigrationHelpers + + def up + with_lock_retries do + add_column :members, :state, :integer, limit: 2, default: 0 + end + end + + def down + with_lock_retries do + remove_column :members, :state + end + end +end diff --git a/db/schema_migrations/20210721145029 b/db/schema_migrations/20210721145029 new file mode 100644 index 00000000000..2c28570b609 --- /dev/null +++ b/db/schema_migrations/20210721145029 @@ -0,0 +1 @@ +1585d2912058ce3a9225233c23707a3679a3f8df2078c71b5cc48f28b7bb9392
\ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index c5d2b0806d6..cadaa69a87d 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -14707,7 +14707,8 @@ CREATE TABLE members ( requested_at timestamp without time zone, expires_at date, ldap boolean DEFAULT false NOT NULL, - override boolean DEFAULT false NOT NULL + override boolean DEFAULT false NOT NULL, + state smallint DEFAULT 0 ); CREATE SEQUENCE members_id_seq diff --git a/doc/administration/monitoring/prometheus/gitlab_metrics.md b/doc/administration/monitoring/prometheus/gitlab_metrics.md index a0034cb623f..10f0486a8c3 100644 --- a/doc/administration/monitoring/prometheus/gitlab_metrics.md +++ b/doc/administration/monitoring/prometheus/gitlab_metrics.md @@ -8,10 +8,10 @@ info: To determine the technical writer assigned to the Stage/Group associated w To enable the GitLab Prometheus metrics: -1. Log into GitLab as a user with [administrator permissions](../../../user/permissions.md). +1. Log in to GitLab as a user with [administrator permissions](../../../user/permissions.md). 1. On the top bar, select **Menu >** **{admin}** **Admin**. 1. On the left sidebar, select **Settings > Metrics and profiling**. -1. Find the **Metrics - Prometheus** section, and click **Enable Prometheus Metrics**. +1. Find the **Metrics - Prometheus** section, and select **Add link to Prometheus**. 1. [Restart GitLab](../../restart_gitlab.md#omnibus-gitlab-restart) for the changes to take effect. For installations from source you must configure it yourself. diff --git a/doc/user/application_security/dast/index.md b/doc/user/application_security/dast/index.md index 3cbc8ba1862..d694bb4fb1a 100644 --- a/doc/user/application_security/dast/index.md +++ b/doc/user/application_security/dast/index.md @@ -932,63 +932,6 @@ variables: The DAST job does not require the project's repository to be present when running, so by default [`GIT_STRATEGY`](../../../ci/runners/configure_runners.md#git-strategy) is set to `none`. -## Running DAST in an offline environment - -For self-managed GitLab instances in an environment with limited, restricted, or intermittent access -to external resources through the internet, some adjustments are required for the DAST job to -successfully run. For more information, see [Offline environments](../offline_deployments/index.md). - -### Requirements for offline DAST support - -To use DAST in an offline environment, you need: - -- GitLab Runner with the [`docker` or `kubernetes` executor](#prerequisites). -- Docker Container Registry with a locally available copy of the DAST - [container image](https://gitlab.com/security-products/dast), found in the - [DAST container registry](https://gitlab.com/security-products/dast/container_registry). - -Note that GitLab Runner has a [default `pull policy` of `always`](https://docs.gitlab.com/runner/executors/docker.html#using-the-always-pull-policy), -meaning the runner tries to pull Docker images from the GitLab container registry even if a local -copy is available. The GitLab Runner [`pull_policy` can be set to `if-not-present`](https://docs.gitlab.com/runner/executors/docker.html#using-the-if-not-present-pull-policy) -in an offline environment if you prefer using only locally available Docker images. However, we -recommend keeping the pull policy setting to `always` if not in an offline environment, as this -enables the use of updated scanners in your CI/CD pipelines. - -### Make GitLab DAST analyzer images available inside your Docker registry - -For DAST, import the following default DAST analyzer image from `registry.gitlab.com` to your [local Docker container registry](../../packages/container_registry/index.md): - -- `registry.gitlab.com/security-products/dast:latest` - -The process for importing Docker images into a local offline Docker registry depends on -**your network security policy**. Please consult your IT staff to find an accepted and approved -process by which external resources can be imported or temporarily accessed. -These scanners are [periodically updated](../vulnerabilities/index.md#vulnerability-scanner-maintenance) -with new definitions, and you may be able to make occasional updates on your own. - -For details on saving and transporting Docker images as a file, see Docker's documentation on -[`docker save`](https://docs.docker.com/engine/reference/commandline/save/), -[`docker load`](https://docs.docker.com/engine/reference/commandline/load/), -[`docker export`](https://docs.docker.com/engine/reference/commandline/export/), and -[`docker import`](https://docs.docker.com/engine/reference/commandline/import/). - -### Set DAST CI/CD job variables to use local DAST analyzers - -Add the following configuration to your `.gitlab-ci.yml` file. You must replace `image` to refer to -the DAST Docker image hosted on your local Docker container registry: - -```yaml -include: - - template: DAST.gitlab-ci.yml -dast: - image: registry.example.com/namespace/dast:latest -``` - -The DAST job should now use local copies of the DAST analyzers to scan your code and generate -security reports without requiring internet access. - -Alternatively, you can use the CI/CD variable `SECURE_ANALYZERS_PREFIX` to override the base registry address of the `dast` image. - ## On-demand scans > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/218465) in GitLab 13.2. diff --git a/doc/user/application_security/dast/run_dast_offline.md b/doc/user/application_security/dast/run_dast_offline.md new file mode 100644 index 00000000000..39747a5cbe5 --- /dev/null +++ b/doc/user/application_security/dast/run_dast_offline.md @@ -0,0 +1,63 @@ +--- +stage: Secure +group: Dynamic Analysis +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments +type: reference, howto +--- + +# Run DAST in an offline environment + +For self-managed GitLab instances in an environment with limited, restricted, or intermittent access +to external resources through the internet, some adjustments are required for the DAST job to +successfully run. For more information, see [Offline environments](../offline_deployments/index.md). + +## Requirements for offline DAST support + +To use DAST in an offline environment, you need: + +- GitLab Runner with the [`docker` or `kubernetes` executor](index.md#prerequisites). +- Docker Container Registry with a locally available copy of the DAST + [container image](https://gitlab.com/security-products/dast), found in the + [DAST container registry](https://gitlab.com/security-products/dast/container_registry). + +Note that GitLab Runner has a [default `pull policy` of `always`](https://docs.gitlab.com/runner/executors/docker.html#using-the-always-pull-policy), +meaning the runner tries to pull Docker images from the GitLab container registry even if a local +copy is available. The GitLab Runner [`pull_policy` can be set to `if-not-present`](https://docs.gitlab.com/runner/executors/docker.html#using-the-if-not-present-pull-policy) +in an offline environment if you prefer using only locally available Docker images. However, we +recommend keeping the pull policy setting to `always` if not in an offline environment, as this +enables the use of updated scanners in your CI/CD pipelines. + +## Make GitLab DAST analyzer images available inside your Docker registry + +For DAST, import the following default DAST analyzer image from `registry.gitlab.com` to your [local Docker container registry](../../packages/container_registry/index.md): + +- `registry.gitlab.com/security-products/dast:latest` + +The process for importing Docker images into a local offline Docker registry depends on +**your network security policy**. Please consult your IT staff to find an accepted and approved +process by which external resources can be imported or temporarily accessed. +These scanners are [periodically updated](../vulnerabilities/index.md#vulnerability-scanner-maintenance) +with new definitions, and you may be able to make occasional updates on your own. + +For details on saving and transporting Docker images as a file, see Docker's documentation on +[`docker save`](https://docs.docker.com/engine/reference/commandline/save/), +[`docker load`](https://docs.docker.com/engine/reference/commandline/load/), +[`docker export`](https://docs.docker.com/engine/reference/commandline/export/), and +[`docker import`](https://docs.docker.com/engine/reference/commandline/import/). + +## Set DAST CI/CD job variables to use local DAST analyzers + +Add the following configuration to your `.gitlab-ci.yml` file. You must replace `image` to refer to +the DAST Docker image hosted on your local Docker container registry: + +```yaml +include: + - template: DAST.gitlab-ci.yml +dast: + image: registry.example.com/namespace/dast:latest +``` + +The DAST job should now use local copies of the DAST analyzers to scan your code and generate +security reports without requiring internet access. + +Alternatively, you can use the CI/CD variable `SECURE_ANALYZERS_PREFIX` to override the base registry address of the `dast` image. diff --git a/doc/user/application_security/offline_deployments/index.md b/doc/user/application_security/offline_deployments/index.md index 64de53fe51f..3bf9d85cd0b 100644 --- a/doc/user/application_security/offline_deployments/index.md +++ b/doc/user/application_security/offline_deployments/index.md @@ -87,7 +87,7 @@ above. You can find more information at each of the pages below: - [Container scanning offline directions](../container_scanning/index.md#running-container-scanning-in-an-offline-environment) - [SAST offline directions](../sast/index.md#running-sast-in-an-offline-environment) -- [DAST offline directions](../dast/index.md#running-dast-in-an-offline-environment) +- [DAST offline directions](../dast/run_dast_offline.md#run-dast-in-an-offline-environment) - [License Compliance offline directions](../../compliance/license_compliance/index.md#running-license-compliance-in-an-offline-environment) - [Dependency Scanning offline directions](../dependency_scanning/index.md#running-dependency-scanning-in-an-offline-environment) diff --git a/lib/gitlab/experimentation.rb b/lib/gitlab/experimentation.rb index 0cf3969b490..1318d3831a4 100644 --- a/lib/gitlab/experimentation.rb +++ b/lib/gitlab/experimentation.rb @@ -52,9 +52,6 @@ module Gitlab tracking_category: 'Growth::Conversion::Experiment::ShowTrialStatusInSidebar', rollout_strategy: :group }, - trial_onboarding_issues: { - tracking_category: 'Growth::Conversion::Experiment::TrialOnboardingIssues' - }, learn_gitlab_a: { tracking_category: 'Growth::Conversion::Experiment::LearnGitLabA', rollout_strategy: :user diff --git a/lib/gitlab/import_export/project/import_export.yml b/lib/gitlab/import_export/project/import_export.yml index a84978a2a80..3454336273c 100644 --- a/lib/gitlab/import_export/project/import_export.yml +++ b/lib/gitlab/import_export/project/import_export.yml @@ -328,6 +328,9 @@ excluded_attributes: - :release_id project_members: - :source_id + - :state + group_members: + - :state metrics: - :merge_request_id - :pipeline_id diff --git a/lib/sidebars/groups/menus/issues_menu.rb b/lib/sidebars/groups/menus/issues_menu.rb new file mode 100644 index 00000000000..95641c09076 --- /dev/null +++ b/lib/sidebars/groups/menus/issues_menu.rb @@ -0,0 +1,101 @@ +# frozen_string_literal: true + +module Sidebars + module Groups + module Menus + class IssuesMenu < ::Sidebars::Menu + include Gitlab::Utils::StrongMemoize + + override :configure_menu_items + def configure_menu_items + return unless can?(context.current_user, :read_group_issues, context.group) + + add_item(list_menu_item) + add_item(boards_menu_item) + add_item(milestones_menu_item) + + true + end + + override :link + def link + issues_group_path(context.group) + end + + override :title + def title + _('Issues') + end + + override :sprite_icon + def sprite_icon + 'issues' + end + + override :has_pill? + def has_pill? + true + end + + override :pill_count + def pill_count + strong_memoize(:pill_count) do + count_service = ::Groups::OpenIssuesCountService + count = count_service.new(context.group, context.current_user).count + + format_cached_count(count_service, count) + end + end + + override :pill_html_options + def pill_html_options + { + class: 'issue_counter' + } + end + + private + + def list_menu_item + ::Sidebars::MenuItem.new( + title: _('List'), + link: issues_group_path(context.group), + active_routes: { path: 'groups#issues' }, + container_html_options: { aria: { label: _('Issues') } }, + item_id: :issue_list + ) + end + + def boards_menu_item + unless can?(context.current_user, :read_group_boards, context.group) + return ::Sidebars::NilMenuItem.new(item_id: :boards) + end + + title = context.group.multiple_issue_boards_available? ? s_('IssueBoards|Boards') : s_('IssueBoards|Board') + + ::Sidebars::MenuItem.new( + title: title, + link: group_boards_path(context.group), + active_routes: { path: %w[boards#index boards#show] }, + item_id: :boards + ) + end + + def milestones_menu_item + unless can?(context.current_user, :read_group_milestones, context.group) + return ::Sidebars::NilMenuItem.new(item_id: :milestones) + end + + ::Sidebars::MenuItem.new( + title: _('Milestones'), + link: group_milestones_path(context.group), + active_routes: { path: 'milestones#index' }, + item_id: :milestones + ) + end + end + end + end +end + +Sidebars::Groups::Menus::IssuesMenu.prepend_mod_with('Sidebars::Groups::Menus::IssuesMenu') diff --git a/lib/sidebars/groups/panel.rb b/lib/sidebars/groups/panel.rb index 44d1b11bfd3..e828905594f 100644 --- a/lib/sidebars/groups/panel.rb +++ b/lib/sidebars/groups/panel.rb @@ -8,6 +8,7 @@ module Sidebars set_scope_menu(Sidebars::Groups::Menus::ScopeMenu.new(context)) add_menu(Sidebars::Groups::Menus::GroupInformationMenu.new(context)) + add_menu(Sidebars::Groups::Menus::IssuesMenu.new(context)) end override :render_raw_menus_partial diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 5f136655a92..260922a00ca 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -12118,9 +12118,6 @@ msgstr "" msgid "Enable PlantUML" msgstr "" -msgid "Enable Prometheus Metrics" -msgstr "" - msgid "Enable Pseudonymizer data collection" msgstr "" @@ -12142,15 +12139,12 @@ msgstr "" msgid "Enable What's new: Current tier only" msgstr "" -msgid "Enable a Prometheus metrics endpoint at %{metrics_path} to expose a variety of statistics on the health and performance of GitLab. Additional information on authenticating and connecting to the metrics endpoint is available %{link}." +msgid "Enable a Prometheus endpoint that exposes health and performance statistics. The Health Check menu item appears in the Monitoring section of the Admin Area. Restart required." msgstr "" msgid "Enable access to the performance bar for non-administrators in a given group." msgstr "" -msgid "Enable and configure Prometheus metrics." -msgstr "" - msgid "Enable and disable Service Desk. Some additional configuration might be required. %{link_start}Learn more%{link_end}." msgstr "" @@ -12184,6 +12178,9 @@ msgstr "" msgid "Enable header and footer in emails" msgstr "" +msgid "Enable health and performance metrics endpoint" +msgstr "" + msgid "Enable in-product marketing emails" msgstr "" @@ -19260,6 +19257,9 @@ msgstr "" msgid "Learn More" msgstr "" +msgid "Learn More." +msgstr "" + msgid "Learn how to %{link_start}contribute to the built-in templates%{link_end}" msgstr "" @@ -20723,7 +20723,7 @@ msgstr "" msgid "Method" msgstr "" -msgid "Method Call Threshold (ms)" +msgid "Method call threshold (ms)" msgstr "" msgid "Metric was successfully added." @@ -21354,6 +21354,9 @@ msgstr "" msgid "Monitor Settings" msgstr "" +msgid "Monitor the health and performance of GitLab with Prometheus." +msgstr "" + msgid "Monitor your errors by integrating with Sentry." msgstr "" @@ -31806,6 +31809,9 @@ msgstr "" msgid "SuperSonics|Expires on" msgstr "" +msgid "SuperSonics|Export license usage file" +msgstr "" + msgid "SuperSonics|Free trial" msgstr "" @@ -33816,9 +33822,6 @@ msgstr "" msgid "This setting can be overridden in each project." msgstr "" -msgid "This setting requires a %{link_to_restart} to take effect." -msgstr "" - msgid "This subscription is for" msgstr "" @@ -38867,9 +38870,6 @@ msgstr "" msgid "help" msgstr "" -msgid "here" -msgstr "" - msgid "http:" msgstr "" @@ -39610,9 +39610,6 @@ msgstr "" msgid "reset it." msgstr "" -msgid "restart" -msgstr "" - msgid "revised" msgstr "" diff --git a/qa/qa/page/group/menu.rb b/qa/qa/page/group/menu.rb index a6f7b820857..fa323f973d8 100644 --- a/qa/qa/page/group/menu.rb +++ b/qa/qa/page/group/menu.rb @@ -8,8 +8,6 @@ module QA view 'app/views/layouts/nav/sidebar/_group_menus.html.haml' do element :general_settings_link - element :group_issues_item - element :group_milestones_link element :group_settings end @@ -63,7 +61,7 @@ module QA def go_to_milestones hover_issues do within_submenu do - click_element(:group_milestones_link) + click_element(:sidebar_menu_item_link, menu_item: 'Milestones') end end end @@ -81,8 +79,8 @@ module QA def hover_issues within_sidebar do - scroll_to_element(:group_issues_item) - find_element(:group_issues_item).hover + scroll_to_element(:sidebar_menu_link, menu_item: 'Issues') + find_element(:sidebar_menu_link, menu_item: 'Issues').hover yield end diff --git a/spec/controllers/concerns/routable_actions_spec.rb b/spec/controllers/concerns/routable_actions_spec.rb index f28f990ecbb..11eb2ab1c2a 100644 --- a/spec/controllers/concerns/routable_actions_spec.rb +++ b/spec/controllers/concerns/routable_actions_spec.rb @@ -10,7 +10,7 @@ RSpec.describe RoutableActions do def routable @klass = params[:type].constantize - @routable = find_routable!(params[:type].constantize, params[:id]) + @routable = find_routable!(params[:type].constantize, params[:id], '/') end def show @@ -135,7 +135,7 @@ RSpec.describe RoutableActions do end it 'performs checks in the context of the controller' do - check = lambda { |routable| redirect_to routable } + check = lambda { |routable, path_info| redirect_to routable } allow(subject).to receive(:not_found_actions).and_return([check]) get_routable(routable) diff --git a/spec/features/admin/admin_settings_spec.rb b/spec/features/admin/admin_settings_spec.rb index a7f3272c06d..fe4c532060b 100644 --- a/spec/features/admin/admin_settings_spec.rb +++ b/spec/features/admin/admin_settings_spec.rb @@ -490,7 +490,7 @@ RSpec.describe 'Admin updates settings' do it 'change Prometheus settings' do page.within('.as-prometheus') do - check 'Enable Prometheus Metrics' + check 'Enable health and performance metrics endpoint' click_button 'Save changes' end diff --git a/spec/helpers/nav_helper_spec.rb b/spec/helpers/nav_helper_spec.rb index 4c5f440b8a3..f0ad2038347 100644 --- a/spec/helpers/nav_helper_spec.rb +++ b/spec/helpers/nav_helper_spec.rb @@ -112,16 +112,6 @@ RSpec.describe NavHelper do it { is_expected.to all(be_a(String)) } end - describe '.group_issues_sub_menu_items' do - subject { helper.group_issues_sub_menu_items } - - before do - allow(helper).to receive(:current_user).and_return(nil) - end - - it { is_expected.to all(be_a(String)) } - end - describe '#page_has_markdown?' do using RSpec::Parameterized::TableSyntax diff --git a/spec/lib/sidebars/groups/menus/issues_menu_spec.rb b/spec/lib/sidebars/groups/menus/issues_menu_spec.rb new file mode 100644 index 00000000000..3d55eb3af40 --- /dev/null +++ b/spec/lib/sidebars/groups/menus/issues_menu_spec.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Sidebars::Groups::Menus::IssuesMenu do + let_it_be(:owner) { create(:user) } + let_it_be(:group) do + build(:group, :private).tap do |g| + g.add_owner(owner) + end + end + + let(:user) { owner } + let(:context) { Sidebars::Groups::Context.new(current_user: user, container: group) } + let(:menu) { described_class.new(context) } + + describe 'Menu Items' do + subject { menu.renderable_items.index { |e| e.item_id == item_id } } + + shared_examples 'menu access rights' do + specify { is_expected.not_to be_nil } + + describe 'when the user does not have access' do + let(:user) { nil } + + specify { is_expected.to be_nil } + end + end + + describe 'List' do + let(:item_id) { :issue_list } + + specify { is_expected.not_to be_nil } + + it_behaves_like 'menu access rights' + end + + describe 'Boards' do + let(:item_id) { :boards } + + it_behaves_like 'menu access rights' + end + + describe 'Milestones' do + let(:item_id) { :milestones } + + it_behaves_like 'menu access rights' + end + end + + it_behaves_like 'pill_count formatted results' do + let(:count_service) { ::Groups::OpenIssuesCountService } + end +end diff --git a/spec/requests/api/groups_spec.rb b/spec/requests/api/groups_spec.rb index 5b36403c247..661ca8d5f3c 100644 --- a/spec/requests/api/groups_spec.rb +++ b/spec/requests/api/groups_spec.rb @@ -10,7 +10,7 @@ RSpec.describe API::Groups do let_it_be(:user2) { create(:user) } let_it_be(:user3) { create(:user) } let_it_be(:admin) { create(:admin) } - let_it_be(:group1) { create(:group, avatar: File.open(uploaded_image_temp_path)) } + let_it_be(:group1) { create(:group, path: 'some_path', avatar: File.open(uploaded_image_temp_path)) } let_it_be(:group2) { create(:group, :private) } let_it_be(:project1) { create(:project, namespace: group1) } let_it_be(:project2) { create(:project, namespace: group2) } diff --git a/spec/views/layouts/nav/sidebar/_group.html.haml_spec.rb b/spec/views/layouts/nav/sidebar/_group.html.haml_spec.rb index 56e559aa70b..b34a2450af8 100644 --- a/spec/views/layouts/nav/sidebar/_group.html.haml_spec.rb +++ b/spec/views/layouts/nav/sidebar/_group.html.haml_spec.rb @@ -39,4 +39,30 @@ RSpec.describe 'layouts/nav/sidebar/_group' do expect(rendered).to have_link('Members', href: group_group_members_path(group)) end end + + describe 'Issues' do + it 'has a default link to the issue list path' do + render + + expect(rendered).to have_link('Issues', href: issues_group_path(group)) + end + + it 'has a link to the issue list page' do + render + + expect(rendered).to have_link('List', href: issues_group_path(group)) + end + + it 'has a link to the boards page' do + render + + expect(rendered).to have_link('Board', href: group_boards_path(group)) + end + + it 'has a link to the milestones page' do + render + + expect(rendered).to have_link('Milestones', href: group_milestones_path(group)) + end + end end |