Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
		
							parent
							
								
									ea3306a15e
								
							
						
					
					
						commit
						8f534e1e96
					
				|  | @ -1,5 +1,5 @@ | |||
| <script> | ||||
| import { GlLink, GlPopover, GlSprintf, GlTooltipDirective } from '@gitlab/ui'; | ||||
| import { GlLink, GlPopover, GlSprintf, GlTooltipDirective, GlBadge } from '@gitlab/ui'; | ||||
| import { SCHEDULE_ORIGIN } from '../../constants'; | ||||
| 
 | ||||
| export default { | ||||
|  | @ -7,6 +7,7 @@ export default { | |||
|     GlLink, | ||||
|     GlPopover, | ||||
|     GlSprintf, | ||||
|     GlBadge, | ||||
|   }, | ||||
|   directives: { | ||||
|     GlTooltip: GlTooltipDirective, | ||||
|  | @ -57,46 +58,49 @@ export default { | |||
|     </gl-link> | ||||
|     <div class="label-container"> | ||||
|       <gl-link v-if="isScheduled" :href="pipelineScheduleUrl" target="__blank"> | ||||
|         <span | ||||
|         <gl-badge | ||||
|           v-gl-tooltip | ||||
|           :title="__('This pipeline was triggered by a schedule.')" | ||||
|           class="badge badge-info" | ||||
|           variant="info" | ||||
|           size="sm" | ||||
|           data-testid="pipeline-url-scheduled" | ||||
|           >{{ __('Scheduled') }}</span | ||||
|           >{{ __('Scheduled') }}</gl-badge | ||||
|         > | ||||
|       </gl-link> | ||||
|       <span | ||||
|       <gl-badge | ||||
|         v-if="pipeline.flags.latest" | ||||
|         v-gl-tooltip | ||||
|         :title="__('Latest pipeline for the most recent commit on this branch')" | ||||
|         class="badge badge-success" | ||||
|         variant="success" | ||||
|         size="sm" | ||||
|         data-testid="pipeline-url-latest" | ||||
|         >{{ __('latest') }}</span | ||||
|         >{{ __('latest') }}</gl-badge | ||||
|       > | ||||
|       <span | ||||
|       <gl-badge | ||||
|         v-if="pipeline.flags.yaml_errors" | ||||
|         v-gl-tooltip | ||||
|         :title="pipeline.yaml_errors" | ||||
|         class="badge badge-danger" | ||||
|         variant="danger" | ||||
|         size="sm" | ||||
|         data-testid="pipeline-url-yaml" | ||||
|         >{{ __('yaml invalid') }}</span | ||||
|         >{{ __('yaml invalid') }}</gl-badge | ||||
|       > | ||||
|       <span | ||||
|       <gl-badge | ||||
|         v-if="pipeline.flags.failure_reason" | ||||
|         v-gl-tooltip | ||||
|         :title="pipeline.failure_reason" | ||||
|         class="badge badge-danger" | ||||
|         variant="danger" | ||||
|         size="sm" | ||||
|         data-testid="pipeline-url-failure" | ||||
|         >{{ __('error') }}</span | ||||
|         >{{ __('error') }}</gl-badge | ||||
|       > | ||||
|       <gl-link | ||||
|         v-if="pipeline.flags.auto_devops" | ||||
|         :id="`pipeline-url-autodevops-${pipeline.id}`" | ||||
|         tabindex="0" | ||||
|         class="badge badge-info autodevops-badge" | ||||
|         data-testid="pipeline-url-autodevops" | ||||
|         role="button" | ||||
|         >{{ __('Auto DevOps') }}</gl-link | ||||
|         ><gl-badge variant="info" size="sm">{{ __('Auto DevOps') }}</gl-badge></gl-link | ||||
|       > | ||||
|       <gl-popover | ||||
|         :target="`pipeline-url-autodevops-${pipeline.id}`" | ||||
|  | @ -122,13 +126,14 @@ export default { | |||
|           __('Learn more about Auto DevOps') | ||||
|         }}</gl-link> | ||||
|       </gl-popover> | ||||
|       <span | ||||
|       <gl-badge | ||||
|         v-if="pipeline.flags.stuck" | ||||
|         class="badge badge-warning" | ||||
|         variant="warning" | ||||
|         size="sm" | ||||
|         data-testid="pipeline-url-stuck" | ||||
|         >{{ __('stuck') }}</span | ||||
|         >{{ __('stuck') }}</gl-badge | ||||
|       > | ||||
|       <span | ||||
|       <gl-badge | ||||
|         v-if="pipeline.flags.detached_merge_request_pipeline" | ||||
|         v-gl-tooltip | ||||
|         :title=" | ||||
|  | @ -136,17 +141,19 @@ export default { | |||
|             'Pipelines for merge requests are configured. A detached pipeline runs in the context of the merge request, and not against the merged result. Learn more in the documentation for Pipelines for Merged Results.', | ||||
|           ) | ||||
|         " | ||||
|         class="badge badge-info" | ||||
|         variant="info" | ||||
|         size="sm" | ||||
|         data-testid="pipeline-url-detached" | ||||
|         >{{ __('detached') }}</span | ||||
|         >{{ __('detached') }}</gl-badge | ||||
|       > | ||||
|       <span | ||||
|       <gl-badge | ||||
|         v-if="isInFork" | ||||
|         v-gl-tooltip | ||||
|         :title="__('Pipeline ran in fork of project')" | ||||
|         class="badge badge-info" | ||||
|         variant="info" | ||||
|         size="sm" | ||||
|         data-testid="pipeline-url-fork" | ||||
|         >{{ __('fork') }}</span | ||||
|         >{{ __('fork') }}</gl-badge | ||||
|       > | ||||
|     </div> | ||||
|   </div> | ||||
|  |  | |||
|  | @ -1,80 +0,0 @@ | |||
| <script> | ||||
| import { GlTabs, GlTab } from '@gitlab/ui'; | ||||
| import PipelineCharts from './pipeline_charts.vue'; | ||||
| 
 | ||||
| export default { | ||||
|   components: { | ||||
|     GlTabs, | ||||
|     GlTab, | ||||
|     PipelineCharts, | ||||
|     DeploymentFrequencyCharts: () => | ||||
|       import('ee_component/projects/pipelines/charts/components/deployment_frequency_charts.vue'), | ||||
|   }, | ||||
|   inject: { | ||||
|     shouldRenderDeploymentFrequencyCharts: { | ||||
|       type: Boolean, | ||||
|       default: false, | ||||
|     }, | ||||
|   }, | ||||
|   props: { | ||||
|     counts: { | ||||
|       type: Object, | ||||
|       required: true, | ||||
|     }, | ||||
|     timesChartData: { | ||||
|       type: Object, | ||||
|       required: true, | ||||
|     }, | ||||
|     lastWeekChartData: { | ||||
|       type: Object, | ||||
|       required: true, | ||||
|     }, | ||||
|     lastMonthChartData: { | ||||
|       type: Object, | ||||
|       required: true, | ||||
|     }, | ||||
|     lastYearChartData: { | ||||
|       type: Object, | ||||
|       required: true, | ||||
|     }, | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       // this loading flag gives the echarts library just enough time | ||||
|       // to ensure all DOM nodes have been mounted. | ||||
|       // | ||||
|       // https://gitlab.com/gitlab-org/gitlab-ui/-/issues/1131 | ||||
|       loading: true, | ||||
|     }; | ||||
|   }, | ||||
|   async mounted() { | ||||
|     await this.$nextTick(); | ||||
|     this.loading = false; | ||||
|   }, | ||||
| }; | ||||
| </script> | ||||
| <template> | ||||
|   <gl-tabs v-if="shouldRenderDeploymentFrequencyCharts"> | ||||
|     <gl-tab :title="__('Pipelines')"> | ||||
|       <pipeline-charts | ||||
|         :counts="counts" | ||||
|         :last-week="lastWeekChartData" | ||||
|         :last-month="lastMonthChartData" | ||||
|         :last-year="lastYearChartData" | ||||
|         :times-chart="timesChartData" | ||||
|         :loading="loading" | ||||
|       /> | ||||
|     </gl-tab> | ||||
|     <gl-tab :title="__('Deployments')"> | ||||
|       <deployment-frequency-charts /> | ||||
|     </gl-tab> | ||||
|   </gl-tabs> | ||||
|   <pipeline-charts | ||||
|     v-else | ||||
|     :counts="counts" | ||||
|     :last-week="lastWeekChartData" | ||||
|     :last-month="lastMonthChartData" | ||||
|     :last-year="lastYearChartData" | ||||
|     :times-chart="timesChartData" | ||||
|   /> | ||||
| </template> | ||||
|  | @ -2,7 +2,6 @@ import Vue from 'vue'; | |||
| import VueApollo from 'vue-apollo'; | ||||
| import createDefaultClient from '~/lib/graphql'; | ||||
| import { parseBoolean } from '~/lib/utils/common_utils'; | ||||
| import ProjectPipelinesChartsLegacy from './components/app_legacy.vue'; | ||||
| import ProjectPipelinesCharts from './components/app.vue'; | ||||
| 
 | ||||
| Vue.use(VueApollo); | ||||
|  | @ -12,107 +11,24 @@ const apolloProvider = new VueApollo({ | |||
| }); | ||||
| 
 | ||||
| const mountPipelineChartsApp = (el) => { | ||||
|   // Not all of the values will be defined since some them will be
 | ||||
|   // empty depending on the value of the graphql_pipeline_analytics
 | ||||
|   // feature flag, once the rollout of the feature flag is completed
 | ||||
|   // the undefined values will be deleted
 | ||||
|   const { | ||||
|     countsFailed, | ||||
|     countsSuccess, | ||||
|     countsTotal, | ||||
|     countsTotalDuration, | ||||
|     successRatio, | ||||
|     timesChartLabels, | ||||
|     timesChartValues, | ||||
|     lastWeekChartLabels, | ||||
|     lastWeekChartTotals, | ||||
|     lastWeekChartSuccess, | ||||
|     lastMonthChartLabels, | ||||
|     lastMonthChartTotals, | ||||
|     lastMonthChartSuccess, | ||||
|     lastYearChartLabels, | ||||
|     lastYearChartTotals, | ||||
|     lastYearChartSuccess, | ||||
|     projectPath, | ||||
|   } = el.dataset; | ||||
|   const { projectPath } = el.dataset; | ||||
| 
 | ||||
|   const shouldRenderDeploymentFrequencyCharts = parseBoolean( | ||||
|     el.dataset.shouldRenderDeploymentFrequencyCharts, | ||||
|   ); | ||||
| 
 | ||||
|   const parseAreaChartData = (labels, totals, success) => { | ||||
|     let parsedData = {}; | ||||
| 
 | ||||
|     try { | ||||
|       parsedData = { | ||||
|         labels: JSON.parse(labels), | ||||
|         totals: JSON.parse(totals), | ||||
|         success: JSON.parse(success), | ||||
|       }; | ||||
|     } catch { | ||||
|       parsedData = {}; | ||||
|     } | ||||
| 
 | ||||
|     return parsedData; | ||||
|   }; | ||||
| 
 | ||||
|   if (gon?.features?.graphqlPipelineAnalytics) { | ||||
|     return new Vue({ | ||||
|       el, | ||||
|       name: 'ProjectPipelinesChartsApp', | ||||
|       components: { | ||||
|         ProjectPipelinesCharts, | ||||
|       }, | ||||
|       apolloProvider, | ||||
|       provide: { | ||||
|         projectPath, | ||||
|         shouldRenderDeploymentFrequencyCharts, | ||||
|       }, | ||||
|       render: (createElement) => createElement(ProjectPipelinesCharts, {}), | ||||
|     }); | ||||
|   } | ||||
| 
 | ||||
|   return new Vue({ | ||||
|     el, | ||||
|     name: 'ProjectPipelinesChartsAppLegacy', | ||||
|     name: 'ProjectPipelinesChartsApp', | ||||
|     components: { | ||||
|       ProjectPipelinesChartsLegacy, | ||||
|       ProjectPipelinesCharts, | ||||
|     }, | ||||
|     apolloProvider, | ||||
|     provide: { | ||||
|       projectPath, | ||||
|       shouldRenderDeploymentFrequencyCharts, | ||||
|     }, | ||||
|     render: (createElement) => | ||||
|       createElement(ProjectPipelinesChartsLegacy, { | ||||
|         props: { | ||||
|           counts: { | ||||
|             failed: countsFailed, | ||||
|             success: countsSuccess, | ||||
|             total: countsTotal, | ||||
|             successRatio, | ||||
|             totalDuration: countsTotalDuration, | ||||
|           }, | ||||
|           timesChartData: { | ||||
|             labels: JSON.parse(timesChartLabels), | ||||
|             values: JSON.parse(timesChartValues), | ||||
|           }, | ||||
|           lastWeekChartData: parseAreaChartData( | ||||
|             lastWeekChartLabels, | ||||
|             lastWeekChartTotals, | ||||
|             lastWeekChartSuccess, | ||||
|           ), | ||||
|           lastMonthChartData: parseAreaChartData( | ||||
|             lastMonthChartLabels, | ||||
|             lastMonthChartTotals, | ||||
|             lastMonthChartSuccess, | ||||
|           ), | ||||
|           lastYearChartData: parseAreaChartData( | ||||
|             lastYearChartLabels, | ||||
|             lastYearChartTotals, | ||||
|             lastYearChartSuccess, | ||||
|           ), | ||||
|         }, | ||||
|       }), | ||||
|     render: (createElement) => createElement(ProjectPipelinesCharts, {}), | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -17,7 +17,6 @@ class Projects::PipelinesController < Projects::ApplicationController | |||
|     push_frontend_feature_flag(:new_pipeline_form, project, default_enabled: true) | ||||
|     push_frontend_feature_flag(:graphql_pipeline_header, project, type: :development, default_enabled: false) | ||||
|     push_frontend_feature_flag(:graphql_pipeline_details, project, type: :development, default_enabled: false) | ||||
|     push_frontend_feature_flag(:graphql_pipeline_analytics, project, type: :development) | ||||
|     push_frontend_feature_flag(:new_pipeline_form_prefilled_vars, project, type: :development, default_enabled: true) | ||||
|   end | ||||
|   before_action :ensure_pipeline, only: [:show] | ||||
|  | @ -189,23 +188,6 @@ class Projects::PipelinesController < Projects::ApplicationController | |||
|     end | ||||
|   end | ||||
| 
 | ||||
|   def charts | ||||
|     @charts = {} | ||||
|     @counts = {} | ||||
| 
 | ||||
|     return if Feature.enabled?(:graphql_pipeline_analytics) | ||||
| 
 | ||||
|     @charts[:week] = Gitlab::Ci::Charts::WeekChart.new(project) | ||||
|     @charts[:month] = Gitlab::Ci::Charts::MonthChart.new(project) | ||||
|     @charts[:year] = Gitlab::Ci::Charts::YearChart.new(project) | ||||
|     @charts[:pipeline_times] = Gitlab::Ci::Charts::PipelineTime.new(project) | ||||
| 
 | ||||
|     @counts[:total] = @project.all_pipelines.count(:all) | ||||
|     @counts[:success] = @project.all_pipelines.success.count(:all) | ||||
|     @counts[:failed] = @project.all_pipelines.failed.count(:all) | ||||
|     @counts[:total_duration] = @project.all_pipelines.total_duration | ||||
|   end | ||||
| 
 | ||||
|   def test_report | ||||
|     respond_to do |format| | ||||
|       format.html do | ||||
|  |  | |||
|  | @ -20,7 +20,7 @@ module Mutations | |||
|                description: copy_field_description(Types::MergeRequestType, :description) | ||||
| 
 | ||||
|       def resolve(args) | ||||
|         merge_request = authorized_find!(args.slice(:project_path, :iid)) | ||||
|         merge_request = authorized_find!(**args.slice(:project_path, :iid)) | ||||
|         attributes = args.slice(:title, :description, :target_branch).compact | ||||
| 
 | ||||
|         ::MergeRequests::UpdateService | ||||
|  |  | |||
|  | @ -111,6 +111,10 @@ class DiffNote < Note | |||
|     super.merge(suggestions_filter_enabled: true) | ||||
|   end | ||||
| 
 | ||||
|   def multiline? | ||||
|     position&.multiline? | ||||
|   end | ||||
| 
 | ||||
|   private | ||||
| 
 | ||||
|   def enqueue_diff_file_creation_job | ||||
|  |  | |||
|  | @ -122,7 +122,7 @@ module Notes | |||
|     end | ||||
| 
 | ||||
|     def track_note_creation_usage_for_merge_requests(note) | ||||
|       Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter.track_create_comment_action(user: note.author) | ||||
|       Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter.track_create_comment_action(note: note) | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -19,7 +19,7 @@ module Notes | |||
|     end | ||||
| 
 | ||||
|     def track_note_removal_usage_for_merge_requests(note) | ||||
|       Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter.track_remove_comment_action(user: note.author) | ||||
|       Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter.track_remove_comment_action(note: note) | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -98,7 +98,7 @@ module Notes | |||
|     end | ||||
| 
 | ||||
|     def track_note_edit_usage_for_merge_requests(note) | ||||
|       Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter.track_edit_comment_action(user: note.author) | ||||
|       Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter.track_edit_comment_action(note: note) | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -57,6 +57,8 @@ | |||
|               = render "projects/merge_requests/awards_block" | ||||
|               - if mr_action === "show" | ||||
|                 - add_page_startup_api_call discussions_path(@merge_request) | ||||
|                 - add_page_startup_api_call widget_project_json_merge_request_path(@project, @merge_request, format: :json) | ||||
|                 - add_page_startup_api_call cached_widget_project_json_merge_request_path(@project, @merge_request, format: :json) | ||||
|               #js-vue-mr-discussions{ data: { notes_data: notes_data(@merge_request).to_json, | ||||
|                 noteable_data: serialize_issuable(@merge_request, serializer: 'noteable'), | ||||
|                 noteable_type: 'MergeRequest', | ||||
|  |  | |||
|  | @ -1,13 +1,4 @@ | |||
| - page_title _('CI / CD Analytics') | ||||
| 
 | ||||
| - if Feature.enabled?(:graphql_pipeline_analytics) | ||||
|   #js-project-pipelines-charts-app{ data: { project_path: @project.full_path, | ||||
|     should_render_deployment_frequency_charts: should_render_deployment_frequency_charts.to_s } } | ||||
| - else | ||||
|   #js-project-pipelines-charts-app{ data: { counts: @counts, success_ratio: success_ratio(@counts), | ||||
|     times_chart: { labels: @charts[:pipeline_times].labels, values: @charts[:pipeline_times].pipeline_times }, | ||||
|     last_week_chart: { labels: @charts[:week].labels, totals: @charts[:week].total, success: @charts[:week].success }, | ||||
|     last_month_chart: { labels: @charts[:month].labels, totals: @charts[:month].total, success: @charts[:month].success }, | ||||
|     last_year_chart: { labels: @charts[:year].labels, totals: @charts[:year].total, success: @charts[:year].success }, | ||||
|     project_path: @project.full_path, | ||||
|     should_render_deployment_frequency_charts: should_render_deployment_frequency_charts.to_s } } | ||||
| #js-project-pipelines-charts-app{ data: { project_path: @project.full_path, | ||||
|   should_render_deployment_frequency_charts: should_render_deployment_frequency_charts.to_s } } | ||||
|  |  | |||
|  | @ -1,2 +1,2 @@ | |||
| = link_to project_settings_operations_path(@project), title: _('Configure Tracing'), class: 'btn btn-success' do | ||||
| = link_to project_settings_operations_path(@project), title: _('Configure Tracing'), class: 'gl-button btn btn-success' do | ||||
|   = _('Add Jaeger URL') | ||||
|  |  | |||
|  | @ -23,6 +23,6 @@ | |||
|         .clearable-input | ||||
|           = text_field_tag :expires_at, nil, class: 'form-control js-access-expiration-date', placeholder: 'Expiration date' | ||||
|           = sprite_icon('close', size: 16, css_class: 'clear-icon js-clear-input gl-text-gray-200') | ||||
|       = submit_tag _("Invite"), class: "btn btn-success", data: { qa_selector: 'invite_member_button' } | ||||
|       = submit_tag _("Invite"), class: "gl-button btn btn-success", data: { qa_selector: 'invite_member_button' } | ||||
|       - if can_import_members | ||||
|         = link_to _("Import"), import_path, class: "btn btn-default", title: _("Import members from another project") | ||||
|         = link_to _("Import"), import_path, class: "gl-button btn btn-default", title: _("Import members from another project") | ||||
|  |  | |||
|  | @ -0,0 +1,5 @@ | |||
| --- | ||||
| title: Add metrics to creating, editing or removing multiline comments on merge requests | ||||
| merge_request: 51098 | ||||
| author: | ||||
| type: other | ||||
|  | @ -0,0 +1,5 @@ | |||
| --- | ||||
| title: Use GlBadge for badges in pipeline_url.vue | ||||
| merge_request: 51058 | ||||
| author: Kev @KevSlashNull | ||||
| type: changed | ||||
|  | @ -0,0 +1,5 @@ | |||
| --- | ||||
| title: Update pipeline graphs on CI/CD Analytics page to use GraphQL endpoint | ||||
| merge_request: 51504 | ||||
| author: | ||||
| type: changed | ||||
|  | @ -0,0 +1,5 @@ | |||
| --- | ||||
| title: Add delete metric image REST API endpoint | ||||
| merge_request: 50043 | ||||
| author: | ||||
| type: added | ||||
|  | @ -0,0 +1,5 @@ | |||
| --- | ||||
| title: Add gl-button to Add Jaeger URL | ||||
| merge_request: 51553 | ||||
| author: Yogi (@yo) | ||||
| type: other | ||||
|  | @ -0,0 +1,5 @@ | |||
| --- | ||||
| title: Update to new GitLab UI button in members invite page | ||||
| merge_request: 51300 | ||||
| author: Yogi (@yo) | ||||
| type: other | ||||
|  | @ -1,8 +0,0 @@ | |||
| --- | ||||
| name: graphql_pipeline_analytics | ||||
| introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/48267 | ||||
| rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/290153 | ||||
| milestone: '13.7' | ||||
| type: development | ||||
| group: group::continuos integration | ||||
| default_enabled: false | ||||
|  | @ -4,5 +4,5 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/44622 | |||
| rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/267162 | ||||
| milestone: '13.5' | ||||
| type: development | ||||
| group: group::knowledge | ||||
| group: group::editor | ||||
| default_enabled: true | ||||
|  |  | |||
|  | @ -0,0 +1,8 @@ | |||
| --- | ||||
| name: usage_data_i_code_review_user_create_multiline_mr_comment | ||||
| introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/51098 | ||||
| rollout_issue_url:  | ||||
| milestone: '13.8' | ||||
| type: development | ||||
| group: group::code review | ||||
| default_enabled: true | ||||
|  | @ -0,0 +1,8 @@ | |||
| --- | ||||
| name: usage_data_i_code_review_user_edit_multiline_mr_comment | ||||
| introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/51098 | ||||
| rollout_issue_url:  | ||||
| milestone: '13.8' | ||||
| type: development | ||||
| group: group::code review | ||||
| default_enabled: true | ||||
|  | @ -0,0 +1,8 @@ | |||
| --- | ||||
| name: usage_data_i_code_review_user_remove_multiline_mr_comment | ||||
| introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/51098 | ||||
| rollout_issue_url:  | ||||
| milestone: '13.8' | ||||
| type: development | ||||
| group: group::code review | ||||
| default_enabled: true | ||||
|  | @ -0,0 +1,8 @@ | |||
| --- | ||||
| name: usage_data_i_testing_full_code_quality_report_total | ||||
| introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49079 | ||||
| rollout_issue_url: | ||||
| milestone: '13.8' | ||||
| type: development | ||||
| group: group::testing | ||||
| default_enabled: true | ||||
|  | @ -4,5 +4,5 @@ introduced_by_url: | |||
| rollout_issue_url:  | ||||
| milestone:  | ||||
| type: development | ||||
| group:  | ||||
| group: group::editor | ||||
| default_enabled: true | ||||
|  |  | |||
|  | @ -1,8 +1,8 @@ | |||
| --- | ||||
| name: wiki_front_matter | ||||
| introduced_by_url:  | ||||
| introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/27706 | ||||
| rollout_issue_url:  | ||||
| milestone:  | ||||
| milestone: '12.10' | ||||
| type: development | ||||
| group:  | ||||
| group: group::editor | ||||
| default_enabled: false | ||||
|  |  | |||
|  | @ -226,10 +226,10 @@ class BackportEnterpriseSchema < ActiveRecord::Migration[5.0] | |||
|     end | ||||
|   end | ||||
| 
 | ||||
|   def create_table_if_not_exists(name, *args, &block) | ||||
|   def create_table_if_not_exists(name, **args, &block) | ||||
|     return if table_exists?(name) | ||||
| 
 | ||||
|     create_table(name, *args, &block) | ||||
|     create_table(name, **args, &block) | ||||
|   end | ||||
| 
 | ||||
|   def add_concurrent_foreign_key(source, target, column:, on_delete: nil, name: nil) | ||||
|  |  | |||
|  | @ -94,6 +94,7 @@ crosslinking | |||
| crosslinks | ||||
| Crossplane | ||||
| CrowdIn | ||||
| CSV | ||||
| Dangerfile | ||||
| datetime | ||||
| Debian | ||||
|  |  | |||
|  | @ -2211,3 +2211,26 @@ Example response: | |||
|     } | ||||
| ] | ||||
| ``` | ||||
| 
 | ||||
| ## Delete metric image | ||||
| 
 | ||||
| Available only for Incident issues. | ||||
| 
 | ||||
| ```plaintext | ||||
| DELETE /projects/:id/issues/:issue_iid/metric_images/:image_id | ||||
| ``` | ||||
| 
 | ||||
| | Attribute   | Type    | Required | Description                          | | ||||
| |-------------|---------|----------|--------------------------------------| | ||||
| | `id`        | integer/string | yes      | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding) owned by the authenticated user  | | ||||
| | `issue_iid` | integer | yes      | The internal ID of a project's issue | | ||||
| | `image_id` | integer | yes      | The ID of the image | | ||||
| 
 | ||||
| ```shell | ||||
| curl --header "PRIVATE-TOKEN: <your_access_token>" --request DELETE "https://gitlab.example.com/api/v4/projects/5/issues/93/metric_images/1" | ||||
| ``` | ||||
| 
 | ||||
| Can return the following status codes: | ||||
| 
 | ||||
| - `204 No Content`, if the image was deleted successfully. | ||||
| - `400 Bad Request`, if the image could not be deleted. | ||||
|  |  | |||
|  | @ -269,6 +269,15 @@ by a project that has jobs with a long timeout (for example, one week). | |||
| 
 | ||||
| When not configured, runners do not override the project timeout. | ||||
| 
 | ||||
| On GitLab.com, you cannot override the job timeout for shared runners and must use the [project defined timeout](../pipelines/settings.md#timeout). | ||||
| 
 | ||||
| To set the maximum job timeout: | ||||
| 
 | ||||
| 1. In a project, go to **Settings > CI/CD > Runners**. | ||||
| 1. Select your specific runner to edit the settings.  | ||||
| 1. Enter a value under **Maximum job timeout**. | ||||
| 1. Select **Save changes**. | ||||
| 
 | ||||
| How this feature works: | ||||
| 
 | ||||
| **Example 1 - Runner timeout bigger than project timeout** | ||||
|  |  | |||
|  | @ -529,11 +529,11 @@ You can use these fake tokens as examples: | |||
| | Usage                 | Guidance | | ||||
| |-----------------------|----------| | ||||
| | above                 | Try to avoid extra words when referring to an example or table in a documentation page, but if required, use **previously** instead. | | ||||
| | admin, admin area     | Use **administration**, **administrator**, **administer**, or **Admin Area** instead. | | ||||
| | admin, admin area     | Use **administration**, **administrator**, **administer**, or **Admin Area** instead. ([Vale](../testing.md#vale) rule: [`Admin.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/Admin.yml)) | | ||||
| | allow, enable         | Try to avoid, unless you are talking about security-related features. For example, instead of "This feature allows you to create a pipeline," use "Use this feature to create a pipeline." This phrasing is more active and is from the user perspective, rather than the person who implemented the feature. [View details](https://docs.microsoft.com/en-us/style-guide/a-z-word-list-term-collections/a/allow-allows). | | ||||
| | and/or                | Use **or** instead, or another sensible construction. | | ||||
| | below                 | Try to avoid extra words when referring to an example or table in a documentation page, but if required, use **following** instead. | | ||||
| | currently             | Do not use when talking about the product or its features. The documentation describes the product as it is today. | | ||||
| | currently             | Do not use when talking about the product or its features. The documentation describes the product as it is today. ([Vale](../testing.md#vale) rule: [`CurrentStatus.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/CurrentStatus.yml)) | | ||||
| | easily                | Do not use. If the user doesn't find the process to be these things, we lose their trust. | | ||||
| | e.g.                  | Do not use Latin abbreviations. Use **for example**, **such as**, **for instance**, or **like** instead. ([Vale](../testing.md#vale) rule: [`LatinTerms.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/LatinTerms.yml)) | | ||||
| | future tense          | When possible, use present tense instead. For example, use `after you execute this command, GitLab displays the result` instead of `after you execute this command, GitLab will display the result`. ([Vale](../testing.md#vale) rule: [`FutureTense.yml`](https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/.vale/gitlab/FutureTense.yml)) | | ||||
|  |  | |||
|  | @ -61,6 +61,7 @@ including: | |||
| - Container Registry images | ||||
| - GitLab Pages content | ||||
| - Snippets | ||||
| - Group wikis **(PREMIUM)** | ||||
| 
 | ||||
| WARNING: | ||||
| GitLab does not back up any configuration files, SSL certificates, or system | ||||
|  |  | |||
|  | @ -68,6 +68,10 @@ the official analyzers. | |||
| 
 | ||||
| ### Selecting specific analyzers | ||||
| 
 | ||||
| WARNING: | ||||
| `SAST_DEFAULT_ANALYZERS` is [deprecated](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/50872) in GitLab 13.8, | ||||
| and is scheduled for [removal in GitLab 14.0](https://gitlab.com/gitlab-org/gitlab/-/issues/290777). | ||||
| 
 | ||||
| You can select the official analyzers you want to run. Here's how to enable | ||||
| `bandit` and `flawfinder` while disabling all the other default ones. | ||||
| In `.gitlab-ci.yml` define: | ||||
|  | @ -83,9 +87,9 @@ variables: | |||
| `bandit` runs first. When merging the reports, SAST | ||||
| removes the duplicates and keeps the `bandit` entries. | ||||
| 
 | ||||
| ### Disabling default analyzers | ||||
| ### Disabling all default analyzers | ||||
| 
 | ||||
| Setting `SAST_DEFAULT_ANALYZERS` to an empty string disables all the official | ||||
| Setting `SAST_DISABLED` to `true` disables all the official | ||||
| default analyzers. In `.gitlab-ci.yml` define: | ||||
| 
 | ||||
| ```yaml | ||||
|  | @ -93,11 +97,25 @@ include: | |||
|   - template: Security/SAST.gitlab-ci.yml | ||||
| 
 | ||||
| variables: | ||||
|   SAST_DEFAULT_ANALYZERS: "" | ||||
|   SAST_DISABLED: true | ||||
| ``` | ||||
| 
 | ||||
| That's needed when one totally relies on [custom analyzers](#custom-analyzers). | ||||
| 
 | ||||
| ### Disabling specific default analyzers | ||||
| 
 | ||||
| Set `SAST_EXCLUDED_ANALYZERS` to a comma-delimited string that includes the official | ||||
| default analyzers that you want to avoid running. In `.gitlab-ci.yml` define the | ||||
| following to prevent the `eslint` analyzer from running: | ||||
| 
 | ||||
| ```yaml | ||||
| include: | ||||
|   - template: Security/SAST.gitlab-ci.yml | ||||
| 
 | ||||
| variables: | ||||
|   SAST_EXCLUDED_ANALYZERS: "eslint" | ||||
| ``` | ||||
| 
 | ||||
| ## Custom Analyzers | ||||
| 
 | ||||
| You can provide your own analyzers by | ||||
|  |  | |||
|  | @ -431,7 +431,8 @@ The following are Docker image-related variables. | |||
| |---------------------------|---------------------------------------------------------------------------------------------------------------------------------------| | ||||
| | `SECURE_ANALYZERS_PREFIX` | Override the name of the Docker registry providing the default images (proxy). Read more about [customizing analyzers](analyzers.md). | | ||||
| | `SAST_ANALYZER_IMAGE_TAG` | **DEPRECATED:** Override the Docker tag of the default images. Read more about [customizing analyzers](analyzers.md).                 | | ||||
| | `SAST_DEFAULT_ANALYZERS`  | Override the names of default images. Read more about [customizing analyzers](analyzers.md).                                          | | ||||
| | `SAST_DEFAULT_ANALYZERS`  | **DEPRECATED:** Override the names of default images. Scheduled for [removal in GitLab 14.0](https://gitlab.com/gitlab-org/gitlab/-/issues/290777).                          | | ||||
| | `SAST_EXCLUDED_ANALYZERS` | Names of default images that should never run. Read more about [customizing analyzers](analyzers.md).                                 | | ||||
| 
 | ||||
| #### Vulnerability filters | ||||
| 
 | ||||
|  |  | |||
|  | @ -53,6 +53,7 @@ The [default ruleset provided by Gitleaks](https://gitlab.com/gitlab-org/securit | |||
|   - Twitter API | ||||
| - Cloud SaaS vendors: | ||||
|   - GitHub API | ||||
|   - Shopify API | ||||
|   - Slack Token | ||||
|   - Slack Webhook | ||||
|   - Stripe API | ||||
|  |  | |||
|  | @ -460,7 +460,7 @@ and above. | |||
| There are a few limitations compared to project wikis: | ||||
| 
 | ||||
| - Git LFS is not supported. | ||||
| - Group wikis are not included in global search, group exports, backups, and Geo replication. | ||||
| - Group wikis are not included in global search, group exports, and Geo replication. | ||||
| - Changes to group wikis don't show up in the group's activity feed. | ||||
| - Group wikis [can't be moved](../../api/project_repository_storage_moves.md#limitations) using the project | ||||
|   repository moves API. | ||||
|  |  | |||
|  | @ -272,6 +272,6 @@ Output indicates that the package has been successfully installed. | |||
| 
 | ||||
| WARNING: | ||||
| Never commit the `auth.json` file to your repository. To install packages from a CI/CD job, | ||||
| consider using the [`composer config`](https://getcomposer.org/doc/articles/handling-private-packages-with-satis.md#authentication) tool with your personal access token | ||||
| consider using the [`composer config`](https://getcomposer.org/doc/articles/handling-private-packages.md#satis) tool with your personal access token | ||||
| stored in a [GitLab CI/CD environment variable](../../../ci/variables/README.md) or in | ||||
| [HashiCorp Vault](../../../ci/secrets/index.md). | ||||
|  |  | |||
|  | @ -95,7 +95,7 @@ The following table depicts the various user permission levels in a project. | |||
| | View metrics dashboard annotations                |         | ✓          | ✓           | ✓        | ✓      | | ||||
| | Archive/reopen requirements **(ULTIMATE)**        |         | ✓          | ✓           | ✓        | ✓      | | ||||
| | Create/edit requirements **(ULTIMATE)**           |         | ✓          | ✓           | ✓        | ✓      | | ||||
| | Import requirements **(ULTIMATE)**                |         | ✓          | ✓           | ✓        | ✓      | | ||||
| | Import/export requirements **(ULTIMATE)**         |         | ✓          | ✓           | ✓        | ✓      | | ||||
| | Create new [test case](../ci/test_cases/index.md) |         | ✓          | ✓           | ✓        | ✓      | | ||||
| | Archive [test case](../ci/test_cases/index.md)    |         | ✓          | ✓           | ✓        | ✓      | | ||||
| | Move [test case](../ci/test_cases/index.md)       |         | ✓          | ✓           | ✓        | ✓      | | ||||
|  |  | |||
|  | @ -225,6 +225,52 @@ the rules for "Groups" and "Documentation" sections: | |||
| 
 | ||||
|  | ||||
| 
 | ||||
| #### Optional Code Owners Sections **(PREMIUM)** | ||||
| 
 | ||||
| > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/232995) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.8 behind a feature flag, enabled by default. | ||||
| 
 | ||||
| When you want to make a certain section optional, you can do so by adding a code owners section prepended with the caret `^` character. Approvals from owners listed in the section will **not** be required. For example: | ||||
| 
 | ||||
| ```plaintext | ||||
| [Documentation] | ||||
| *.md @root | ||||
| 
 | ||||
| [Ruby] | ||||
| *.rb @root | ||||
| 
 | ||||
| ^[Go] | ||||
| *.go @root | ||||
| ``` | ||||
| 
 | ||||
| The optional code owners section will be displayed in merge requests under the **Approval Rules** area: | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| If a section is duplicated in the file, and one of them is marked as optional and the other isn't, the requirement prevails.  | ||||
| 
 | ||||
| For example, the code owners of the "Documentation" section below will still be required to approve merge requests:  | ||||
| 
 | ||||
| ```plaintext | ||||
| [Documentation] | ||||
| *.md @root | ||||
| 
 | ||||
| [Ruby] | ||||
| *.rb @root | ||||
| 
 | ||||
| ^[Go] | ||||
| *.go @root | ||||
| 
 | ||||
| ^[Documentation] | ||||
| *.txt @root | ||||
| ``` | ||||
| 
 | ||||
| Optional sections in the code owners file are currently treated as optional only | ||||
| when changes are submitted via merge requests. If a change is submitted directly | ||||
| to the protected branch, approval from code owners will still be required, even if the | ||||
| section is marked as optional. We plan to change this in a | ||||
| [future release](https://gitlab.com/gitlab-org/gitlab/-/issues/297638), | ||||
| where direct pushes to the protected branch will be allowed for sections marked as optional. | ||||
| 
 | ||||
| ## Example `CODEOWNERS` file | ||||
| 
 | ||||
| ```plaintext | ||||
|  |  | |||
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 102 KiB | 
|  | @ -179,7 +179,8 @@ for the issue. Notifications are automatically enabled after you participate in | |||
| 
 | ||||
| > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/18816) in GitLab 13.8. | ||||
| 
 | ||||
| Guest users can see a button to copy the email address for the issue. Sending an email to this address creates a comment containing the email body. | ||||
| Guest users can see a button in the right sidebar to copy the email address for the issue. | ||||
| Sending an email to this address creates a comment containing the email body. | ||||
| 
 | ||||
| ### Edit | ||||
| 
 | ||||
|  |  | |||
|  | @ -62,7 +62,7 @@ request's page at the top-right side: | |||
| - Enable the [squash commits when merge request is accepted](squash_and_merge.md) option to combine all the commits into one before merging, thus keep a clean commit history in your repository. | ||||
| - Set the merge request as a [**Draft**](work_in_progress_merge_requests.md) to avoid accidental merges before it is ready. | ||||
| 
 | ||||
| Once you have created the merge request, you can also: | ||||
| After you have created the merge request, you can also: | ||||
| 
 | ||||
| - [Discuss](../../discussions/index.md) your implementation with your team in the merge request thread. | ||||
| - [Perform inline code reviews](reviewing_and_managing_merge_requests.md#perform-inline-code-reviews). | ||||
|  | @ -70,7 +70,7 @@ Once you have created the merge request, you can also: | |||
| - Preview continuous integration [pipelines on the merge request widget](reviewing_and_managing_merge_requests.md#pipeline-status-in-merge-requests-widgets). | ||||
| - Preview how your changes look directly on your deployed application with [Review Apps](reviewing_and_managing_merge_requests.md#live-preview-with-review-apps). | ||||
| - [Allow collaboration on merge requests across forks](allow_collaboration.md). | ||||
| - Perform a [Review](../../discussions/index.md#merge-request-reviews) in order to create multiple comments on a diff and publish them once you're ready. | ||||
| - Perform a [Review](../../discussions/index.md#merge-request-reviews) to create multiple comments on a diff and publish them when you're ready. | ||||
| - Add [code suggestions](../../discussions/index.md#suggest-changes) to change the content of merge requests directly into merge request threads, and easily apply them to the codebase directly from the UI. | ||||
| - Add a time estimation and the time spent with that merge request with [Time Tracking](../time_tracking.md#time-tracking). | ||||
| 
 | ||||
|  | @ -161,6 +161,53 @@ Feature.disable(:merge_request_reviewers) | |||
| Feature.disable(:merge_request_reviewers, Project.find(<project id>)) | ||||
| ``` | ||||
| 
 | ||||
| #### Reviewer approval rules | ||||
| 
 | ||||
| > - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/233736) in GitLab 13.8. | ||||
| > - It was [deployed behind a feature flag](../../../user/feature_flags.md), disabled by default. | ||||
| > - [Became enabled by default](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/51183) in GitLab 13.8. | ||||
| > - It's enabled on GitLab.com. | ||||
| > - It's recommended for production use. | ||||
| > - It can be enabled or disabled for a single project. | ||||
| > - For GitLab self-managed instances, GitLab administrators can opt to [disable it](#enable-or-disable-reviewer-approval-rules). **(CORE ONLY)** | ||||
| 
 | ||||
| When editing the **Reviewers** field in a new or existing merge request, this feature | ||||
| displays the name of the matching [approval rule](merge_request_approvals.md#approval-rules) | ||||
| below the name of each suggested reviewer. [Code Owners](../code_owners.md) are displayed as `Codeowner` without group detail. We intend to iterate on this feature in future releases. | ||||
| 
 | ||||
| This example shows reviewers and approval rules when creating a new merge request: | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| This example shows reviewers and approval rules in a merge request sidebar: | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| ##### Enable or disable Reviewer Approval Rules **(CORE ONLY)** | ||||
| 
 | ||||
| Merge Request Reviewers is under development and ready for production use. | ||||
| It is deployed behind a feature flag that is **enabled by default**. | ||||
| [GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md) | ||||
| can opt to disable it. | ||||
| 
 | ||||
| To enable it: | ||||
| 
 | ||||
| ```ruby | ||||
| # For the instance | ||||
| Feature.enable(:reviewer_approval_rules) | ||||
| # For a single project | ||||
| Feature.enable(:reviewer_approval_rules, Project.find(<project id>)) | ||||
| ``` | ||||
| 
 | ||||
| To disable it: | ||||
| 
 | ||||
| ```ruby | ||||
| # For the instance | ||||
| Feature.disable(:reviewer_approval_rules) | ||||
| # For a single project | ||||
| Feature.disable(:reviewer_approval_rules, Project.find(<project id>)) | ||||
| ``` | ||||
| 
 | ||||
| ### Merge requests to close issues | ||||
| 
 | ||||
| If the merge request is being created to resolve an issue, you can | ||||
|  | @ -200,5 +247,5 @@ is set for deletion, the merge request widget displays the | |||
|   at once. By doing so, you save pipeline minutes. | ||||
| - Delete feature branches on merge or after merging them to keep your repository clean. | ||||
| - Take one thing at a time and ship the smallest changes possible. By doing so, | ||||
|   you'll have faster reviews and your changes will be less prone to errors. | ||||
|   reviews are faster and your changes are less prone to errors. | ||||
| - Do not use capital letters nor special chars in branch names. | ||||
|  |  | |||
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 41 KiB | 
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 38 KiB | 
|  | @ -34,7 +34,7 @@ Users with Reporter or higher [permissions](../../permissions.md) can create req | |||
| 
 | ||||
| To create a requirement: | ||||
| 
 | ||||
| 1. From your project page, go to **Requirements**. | ||||
| 1. In a project, go to **Requirements**. | ||||
| 1. Select **New requirement**. | ||||
| 1. Enter a title and description and select **Create requirement**. | ||||
| 
 | ||||
|  | @ -200,10 +200,10 @@ requirements_confirmation: | |||
| 
 | ||||
| > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/246857) in GitLab 13.7. | ||||
| 
 | ||||
| You can import requirements to a project by uploading a CSV file with the columns | ||||
| `title` and `description`. | ||||
| You can import requirements to a project by uploading a [CSV file](https://en.wikipedia.org/wiki/Comma-separated_values) | ||||
| with the columns `title` and `description`. | ||||
| 
 | ||||
| The user uploading the CSV file will be set as the author of the imported requirements. | ||||
| After the import, the user uploading the CSV file is set as the author of the imported requirements. | ||||
| 
 | ||||
| Users with Reporter or higher [permissions](../../permissions.md) can import requirements. | ||||
| 
 | ||||
|  | @ -213,20 +213,20 @@ Before you import your file: | |||
| 
 | ||||
| - Consider importing a test file containing only a few requirements. There is no way to undo a large | ||||
|   import without using the GitLab API. | ||||
| - Ensure your CSV file meets the [file format](#csv-file-format) requirements. | ||||
| - Ensure your CSV file meets the [file format](#imported-csv-file-format) requirements. | ||||
| 
 | ||||
| To import requirements: | ||||
| 
 | ||||
| 1. Navigate to a project's Requirements page. | ||||
|    - If the project already has existing requirements, click the import icon (**{import}**) at the | ||||
| 1. In a project, go to **Requirements**. | ||||
|    - If the project already has existing requirements, select the import icon (**{import}**) in the | ||||
|      top right. | ||||
|    - For a project without any requirements, click **Import CSV** in the middle of the page. | ||||
| 1. Select the file and click **Import requirements**. | ||||
|    - For a project without any requirements, select **Import CSV** in the middle of the page. | ||||
| 1. Select the file and select **Import requirements**. | ||||
| 
 | ||||
| The file is processed in the background and a notification email is sent | ||||
| to you after the import is complete. | ||||
| 
 | ||||
| ### CSV file format | ||||
| ### Imported CSV file format | ||||
| 
 | ||||
| When importing requirements from a CSV file, it must be formatted in a certain way: | ||||
| 
 | ||||
|  | @ -257,3 +257,37 @@ Another Title,"A description, with a comma" | |||
| The limit depends on the configuration value of Max Attachment Size for the GitLab instance. | ||||
| 
 | ||||
| For GitLab.com, it is set to 10 MB. | ||||
| 
 | ||||
| ## Export requirements to a CSV file | ||||
| 
 | ||||
| > [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/290813) in GitLab 13.8. | ||||
| 
 | ||||
| You can export GitLab requirements to a | ||||
| [CSV file](https://en.wikipedia.org/wiki/Comma-separated_values) sent to your default notification | ||||
| email as an attachment. | ||||
| 
 | ||||
| By exporting requirements, you and your team can import them into another tool or share them with | ||||
| your customers. Exporting requirements can aid collaboration with higher-level systems, as well as | ||||
| audit and regulatory compliance tasks. | ||||
| 
 | ||||
| Users with Reporter or higher [permissions](../../permissions.md) can export requirements. | ||||
| 
 | ||||
| To export requirements: | ||||
| 
 | ||||
| 1. In a project, go to **Requirements**. | ||||
| 1. Select the **Export as CSV** icon (**{export}**) in the top right. A confirmation modal appears. | ||||
| 1. Select **Export requirements**. The exported CSV file is sent to the email address associated with your user. | ||||
| 
 | ||||
| ### Exported CSV file format | ||||
| 
 | ||||
| You can preview the exported CSV file in a spreadsheet editor, such as Microsoft Excel, | ||||
| OpenOffice Calc, or Google Sheets. | ||||
| 
 | ||||
| The exported CSV file contains the following columns: | ||||
| 
 | ||||
| - Requirement ID | ||||
| - Title | ||||
| - Description | ||||
| - Author Username | ||||
| - Latest Test Report State | ||||
| - Latest Test Report Created At (UTC) | ||||
|  |  | |||
|  | @ -40,31 +40,42 @@ module Backup | |||
|     end | ||||
| 
 | ||||
|     def restore | ||||
|       Project.find_each(batch_size: 1000) do |project| | ||||
|         restore_repository(project, Gitlab::GlRepository::PROJECT) | ||||
|         restore_repository(project, Gitlab::GlRepository::WIKI) | ||||
|         restore_repository(project, Gitlab::GlRepository::DESIGN) | ||||
|       end | ||||
| 
 | ||||
|       invalid_ids = Snippet.find_each(batch_size: 1000) | ||||
|         .map { |snippet| restore_snippet_repository(snippet) } | ||||
|         .compact | ||||
| 
 | ||||
|       cleanup_snippets_without_repositories(invalid_ids) | ||||
|       restore_project_repositories | ||||
|       restore_snippets | ||||
| 
 | ||||
|       restore_object_pools | ||||
|     end | ||||
| 
 | ||||
|     private | ||||
| 
 | ||||
|     def restore_project_repositories | ||||
|       Project.find_each(batch_size: 1000) do |project| | ||||
|         restore_repository(project, Gitlab::GlRepository::PROJECT) | ||||
|         restore_repository(project, Gitlab::GlRepository::WIKI) | ||||
|         restore_repository(project, Gitlab::GlRepository::DESIGN) | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     def restore_snippets | ||||
|       invalid_ids = Snippet.find_each(batch_size: 1000) | ||||
|         .map { |snippet| restore_snippet_repository(snippet) } | ||||
|         .compact | ||||
| 
 | ||||
|       cleanup_snippets_without_repositories(invalid_ids) | ||||
|     end | ||||
| 
 | ||||
|     def check_valid_storages! | ||||
|       [ProjectRepository, SnippetRepository].each do |klass| | ||||
|       repository_storage_klasses.each do |klass| | ||||
|         if klass.excluding_repository_storage(Gitlab.config.repositories.storages.keys).exists? | ||||
|           raise Error, "repositories.storages in gitlab.yml does not include all storages used by #{klass}" | ||||
|         end | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     def repository_storage_klasses | ||||
|       [ProjectRepository, SnippetRepository] | ||||
|     end | ||||
| 
 | ||||
|     def backup_repos_path | ||||
|       @backup_repos_path ||= File.join(Gitlab.config.backup.path, 'repositories') | ||||
|     end | ||||
|  | @ -103,12 +114,7 @@ module Backup | |||
|               end | ||||
| 
 | ||||
|               begin | ||||
|                 case container | ||||
|                 when Project | ||||
|                   dump_project(container) | ||||
|                 when Snippet | ||||
|                   dump_snippet(container) | ||||
|                 end | ||||
|                 dump_container(container) | ||||
|               rescue => e | ||||
|                 errors << e | ||||
|                 break | ||||
|  | @ -130,6 +136,15 @@ module Backup | |||
|       end | ||||
|     end | ||||
| 
 | ||||
|     def dump_container(container) | ||||
|       case container | ||||
|       when Project | ||||
|         dump_project(container) | ||||
|       when Snippet | ||||
|         dump_snippet(container) | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     def dump_project(project) | ||||
|       backup_repository(project, Gitlab::GlRepository::PROJECT) | ||||
|       backup_repository(project, Gitlab::GlRepository::WIKI) | ||||
|  | @ -308,3 +323,5 @@ module Backup | |||
|     end | ||||
|   end | ||||
| end | ||||
| 
 | ||||
| Backup::Repositories.prepend_if_ee('EE::Backup::Repositories') | ||||
|  |  | |||
|  | @ -1165,9 +1165,9 @@ module Gitlab | |||
|         Arel::Nodes::SqlLiteral.new(replace.to_sql) | ||||
|       end | ||||
| 
 | ||||
|       def remove_foreign_key_if_exists(*args) | ||||
|         if foreign_key_exists?(*args) | ||||
|           remove_foreign_key(*args) | ||||
|       def remove_foreign_key_if_exists(...) | ||||
|         if foreign_key_exists?(...) | ||||
|           remove_foreign_key(...) | ||||
|         end | ||||
|       end | ||||
| 
 | ||||
|  |  | |||
|  | @ -19,6 +19,7 @@ module Gitlab | |||
|                :height, | ||||
|                :x, | ||||
|                :y, | ||||
|                :line_range, | ||||
|                :position_type, to: :formatter | ||||
| 
 | ||||
|       # A position can belong to a text line or to an image coordinate | ||||
|  | @ -167,6 +168,12 @@ module Gitlab | |||
|         end | ||||
|       end | ||||
| 
 | ||||
|       def multiline? | ||||
|         return unless on_text? && line_range | ||||
| 
 | ||||
|         line_range['start'] != line_range['end'] | ||||
|       end | ||||
| 
 | ||||
|       private | ||||
| 
 | ||||
|       def find_diff_file(repository) | ||||
|  |  | |||
|  | @ -258,6 +258,11 @@ | |||
|   redis_slot: testing | ||||
|   aggregation: weekly | ||||
|   feature_flag: usage_data_i_testing_group_code_coverage_visit_total | ||||
| - name: i_testing_full_code_quality_report_total | ||||
|   category: testing | ||||
|   redis_slot: testing | ||||
|   aggregation: weekly | ||||
|   feature_flag: usage_data_i_testing_full_code_quality_report_total | ||||
| # Project Management group | ||||
| - name: g_project_management_issue_title_changed | ||||
|   category: issues_edit | ||||
|  | @ -496,6 +501,21 @@ | |||
|   category: code_review | ||||
|   aggregation: weekly | ||||
|   feature_flag: usage_data_i_code_review_user_publish_review | ||||
| - name: i_code_review_user_create_multiline_mr_comment | ||||
|   redis_slot: code_review | ||||
|   category: code_review | ||||
|   aggregation: weekly | ||||
|   feature_flag: usage_data_i_code_review_user_create_multiline_mr_comment | ||||
| - name: i_code_review_user_edit_multiline_mr_comment | ||||
|   redis_slot: code_review | ||||
|   category: code_review | ||||
|   aggregation: weekly | ||||
|   feature_flag: usage_data_i_code_review_user_edit_multiline_mr_comment | ||||
| - name: i_code_review_user_remove_multiline_mr_comment | ||||
|   redis_slot: code_review | ||||
|   category: code_review | ||||
|   aggregation: weekly | ||||
|   feature_flag: usage_data_i_code_review_user_remove_multiline_mr_comment | ||||
| # Terraform | ||||
| - name: p_terraform_state_api_unique_users | ||||
|   category: terraform | ||||
|  |  | |||
|  | @ -15,6 +15,9 @@ module Gitlab | |||
|       MR_REMOVE_COMMENT_ACTION = 'i_code_review_user_remove_mr_comment' | ||||
|       MR_CREATE_REVIEW_NOTE_ACTION = 'i_code_review_user_create_review_note' | ||||
|       MR_PUBLISH_REVIEW_ACTION = 'i_code_review_user_publish_review' | ||||
|       MR_CREATE_MULTILINE_COMMENT_ACTION = 'i_code_review_user_create_multiline_mr_comment' | ||||
|       MR_EDIT_MULTILINE_COMMENT_ACTION = 'i_code_review_user_edit_multiline_mr_comment' | ||||
|       MR_REMOVE_MULTILINE_COMMENT_ACTION = 'i_code_review_user_remove_multiline_mr_comment' | ||||
| 
 | ||||
|       class << self | ||||
|         def track_mr_diffs_action(merge_request:) | ||||
|  | @ -42,16 +45,19 @@ module Gitlab | |||
|           track_unique_action_by_user(MR_REOPEN_ACTION, user) | ||||
|         end | ||||
| 
 | ||||
|         def track_create_comment_action(user:) | ||||
|           track_unique_action_by_user(MR_CREATE_COMMENT_ACTION, user) | ||||
|         def track_create_comment_action(note:) | ||||
|           track_unique_action_by_user(MR_CREATE_COMMENT_ACTION, note.author) | ||||
|           track_multiline_unique_action(MR_CREATE_MULTILINE_COMMENT_ACTION, note) | ||||
|         end | ||||
| 
 | ||||
|         def track_edit_comment_action(user:) | ||||
|           track_unique_action_by_user(MR_EDIT_COMMENT_ACTION, user) | ||||
|         def track_edit_comment_action(note:) | ||||
|           track_unique_action_by_user(MR_EDIT_COMMENT_ACTION, note.author) | ||||
|           track_multiline_unique_action(MR_EDIT_MULTILINE_COMMENT_ACTION, note) | ||||
|         end | ||||
| 
 | ||||
|         def track_remove_comment_action(user:) | ||||
|           track_unique_action_by_user(MR_REMOVE_COMMENT_ACTION, user) | ||||
|         def track_remove_comment_action(note:) | ||||
|           track_unique_action_by_user(MR_REMOVE_COMMENT_ACTION, note.author) | ||||
|           track_multiline_unique_action(MR_REMOVE_MULTILINE_COMMENT_ACTION, note) | ||||
|         end | ||||
| 
 | ||||
|         def track_create_review_note_action(user:) | ||||
|  | @ -77,6 +83,12 @@ module Gitlab | |||
|         def track_unique_action(action, value) | ||||
|           Gitlab::UsageDataCounters::HLLRedisCounter.track_usage_event(action, value) | ||||
|         end | ||||
| 
 | ||||
|         def track_multiline_unique_action(action, note) | ||||
|           return unless note.is_a?(DiffNote) && note.multiline? | ||||
| 
 | ||||
|           track_unique_action_by_user(action, note.author) | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|   end | ||||
|  |  | |||
|  | @ -15042,6 +15042,9 @@ msgstr "" | |||
| msgid "Incidents|Must start with http or https" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "Incidents|There was an issue deleting the image." | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "Incidents|There was an issue loading metric images." | ||||
| msgstr "" | ||||
| 
 | ||||
|  | @ -15054,6 +15057,12 @@ msgstr "" | |||
| msgid "Incident|Alert details" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "Incident|Are you sure you wish to delete this image?" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "Incident|Deleting %{filename}" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "Incident|Metrics" | ||||
| msgstr "" | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,53 +0,0 @@ | |||
| import { shallowMount } from '@vue/test-utils'; | ||||
| import Component from '~/projects/pipelines/charts/components/app_legacy.vue'; | ||||
| import PipelineCharts from '~/projects/pipelines/charts/components/pipeline_charts.vue'; | ||||
| import { | ||||
|   counts, | ||||
|   timesChartData, | ||||
|   areaChartData as lastWeekChartData, | ||||
|   areaChartData as lastMonthChartData, | ||||
|   lastYearChartData, | ||||
| } from '../mock_data'; | ||||
| 
 | ||||
| describe('ProjectsPipelinesChartsApp', () => { | ||||
|   let wrapper; | ||||
| 
 | ||||
|   beforeEach(() => { | ||||
|     wrapper = shallowMount(Component, { | ||||
|       propsData: { | ||||
|         counts, | ||||
|         timesChartData, | ||||
|         lastWeekChartData, | ||||
|         lastMonthChartData, | ||||
|         lastYearChartData, | ||||
|       }, | ||||
|       provide: { | ||||
|         projectPath: 'test/project', | ||||
|         shouldRenderDeploymentFrequencyCharts: true, | ||||
|       }, | ||||
|       stubs: { | ||||
|         DeploymentFrequencyCharts: true, | ||||
|       }, | ||||
|     }); | ||||
|   }); | ||||
| 
 | ||||
|   afterEach(() => { | ||||
|     wrapper.destroy(); | ||||
|     wrapper = null; | ||||
|   }); | ||||
| 
 | ||||
|   describe('pipelines charts', () => { | ||||
|     it('displays the pipeline charts', () => { | ||||
|       const chart = wrapper.find(PipelineCharts); | ||||
| 
 | ||||
|       expect(chart.exists()).toBe(true); | ||||
|       expect(chart.props()).toMatchObject({ | ||||
|         counts, | ||||
|         lastWeek: lastWeekChartData, | ||||
|         lastMonth: lastMonthChartData, | ||||
|         lastYear: lastYearChartData, | ||||
|         timesChart: timesChartData, | ||||
|       }); | ||||
|     }); | ||||
|   }); | ||||
| }); | ||||
|  | @ -5,7 +5,7 @@ require 'spec_helper' | |||
| RSpec.describe Gitlab::Ci::Config::Entry::Variables do | ||||
|   let(:metadata) { {} } | ||||
| 
 | ||||
|   subject { described_class.new(config, metadata) } | ||||
|   subject { described_class.new(config, **metadata) } | ||||
| 
 | ||||
|   shared_examples 'valid config' do | ||||
|     describe '#value' do | ||||
|  |  | |||
|  | @ -752,4 +752,62 @@ RSpec.describe Gitlab::Diff::Position do | |||
|       expect(subject.file_hash).to eq(Digest::SHA1.hexdigest(subject.file_path)) | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   describe '#multiline?' do | ||||
|     let(:end_line_code) { "ab09011fa121d0a2bb9fa4ca76094f2482b902b7_#{end_old_line}_#{end_new_line}" } | ||||
| 
 | ||||
|     let(:line_range) do | ||||
|       { | ||||
|         "start" => { | ||||
|           "line_code" => "ab09011fa121d0a2bb9fa4ca76094f2482b902b7_18_18", | ||||
|           "type" => nil, | ||||
|           "old_line" => 18, | ||||
|           "new_line" => 18 | ||||
|         }, | ||||
|          "end" => { | ||||
|           "line_code" => end_line_code, | ||||
|           "type" => nil, | ||||
|           "old_line" => end_old_line, | ||||
|           "new_line" => end_new_line | ||||
|         } | ||||
|       } | ||||
|     end | ||||
| 
 | ||||
|     subject(:multiline) do | ||||
|       described_class.new( | ||||
|         line_range: line_range, | ||||
|         position_type: position_type | ||||
|       ) | ||||
|     end | ||||
| 
 | ||||
|     let(:end_old_line) { 20 } | ||||
|     let(:end_new_line) { 20 } | ||||
| 
 | ||||
|     context 'when the position type is text' do | ||||
|       let(:position_type) { "text" } | ||||
| 
 | ||||
|       context 'when the start lines equal the end lines' do | ||||
|         let(:end_old_line) { 18 } | ||||
|         let(:end_new_line) { 18 } | ||||
| 
 | ||||
|         it "returns true" do | ||||
|           expect(subject.multiline?).to be_falsey | ||||
|         end | ||||
|       end | ||||
| 
 | ||||
|       context 'when the start lines do not equal the end lines' do | ||||
|         it "returns true" do | ||||
|           expect(subject.multiline?).to be_truthy | ||||
|         end | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     context 'when the position type is not text' do | ||||
|       let(:position_type) { "image" } | ||||
| 
 | ||||
|       it "returns false" do | ||||
|         expect(subject.multiline?).to be_falsey | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ require 'spec_helper' | |||
| RSpec.describe Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter, :clean_gitlab_redis_shared_state do | ||||
|   let(:merge_request) { build(:merge_request, id: 1) } | ||||
|   let(:user) { build(:user, id: 1) } | ||||
|   let(:note) { build(:note, author: user) } | ||||
| 
 | ||||
|   shared_examples_for 'a tracked merge request unique event' do | ||||
|     specify do | ||||
|  | @ -73,27 +74,63 @@ RSpec.describe Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter, :cl | |||
|   end | ||||
| 
 | ||||
|   describe '.track_create_comment_action' do | ||||
|     subject { described_class.track_create_comment_action(user: user) } | ||||
|     subject { described_class.track_create_comment_action(note: note) } | ||||
| 
 | ||||
|     it_behaves_like 'a tracked merge request unique event' do | ||||
|       let(:action) { described_class::MR_CREATE_COMMENT_ACTION } | ||||
|     end | ||||
| 
 | ||||
|     context 'when the note is multiline diff note' do | ||||
|       let(:note) { build(:diff_note_on_merge_request, author: user) } | ||||
| 
 | ||||
|       before do | ||||
|         allow(note).to receive(:multiline?).and_return(true) | ||||
|       end | ||||
| 
 | ||||
|       it_behaves_like 'a tracked merge request unique event' do | ||||
|         let(:action) { described_class::MR_CREATE_MULTILINE_COMMENT_ACTION } | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   describe '.track_edit_comment_action' do | ||||
|     subject { described_class.track_edit_comment_action(user: user) } | ||||
|     subject { described_class.track_edit_comment_action(note: note) } | ||||
| 
 | ||||
|     it_behaves_like 'a tracked merge request unique event' do | ||||
|       let(:action) { described_class::MR_EDIT_COMMENT_ACTION } | ||||
|     end | ||||
| 
 | ||||
|     context 'when the note is multiline diff note' do | ||||
|       let(:note) { build(:diff_note_on_merge_request, author: user) } | ||||
| 
 | ||||
|       before do | ||||
|         allow(note).to receive(:multiline?).and_return(true) | ||||
|       end | ||||
| 
 | ||||
|       it_behaves_like 'a tracked merge request unique event' do | ||||
|         let(:action) { described_class::MR_EDIT_MULTILINE_COMMENT_ACTION } | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   describe '.track_remove_comment_action' do | ||||
|     subject { described_class.track_remove_comment_action(user: user) } | ||||
|     subject { described_class.track_remove_comment_action(note: note) } | ||||
| 
 | ||||
|     it_behaves_like 'a tracked merge request unique event' do | ||||
|       let(:action) { described_class::MR_REMOVE_COMMENT_ACTION } | ||||
|     end | ||||
| 
 | ||||
|     context 'when the note is multiline diff note' do | ||||
|       let(:note) { build(:diff_note_on_merge_request, author: user) } | ||||
| 
 | ||||
|       before do | ||||
|         allow(note).to receive(:multiline?).and_return(true) | ||||
|       end | ||||
| 
 | ||||
|       it_behaves_like 'a tracked merge request unique event' do | ||||
|         let(:action) { described_class::MR_REMOVE_MULTILINE_COMMENT_ACTION } | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   describe '.track_create_review_note_action' do | ||||
|  |  | |||
|  | @ -120,7 +120,7 @@ RSpec.describe Notes::CreateService do | |||
|           end | ||||
| 
 | ||||
|           it 'tracks merge request usage data' do | ||||
|             expect(Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter).to receive(:track_create_comment_action).with(user: user) | ||||
|             expect(Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter).to receive(:track_create_comment_action).with(note: kind_of(Note)) | ||||
| 
 | ||||
|             described_class.new(project_with_repo, user, new_opts).execute | ||||
|           end | ||||
|  |  | |||
|  | @ -38,7 +38,7 @@ RSpec.describe Notes::DestroyService do | |||
|     it 'tracks merge request usage data' do | ||||
|       mr = create(:merge_request, source_project: project) | ||||
|       note = create(:note, project: project, noteable: mr) | ||||
|       expect(Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter).to receive(:track_remove_comment_action).with(user: user) | ||||
|       expect(Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter).to receive(:track_remove_comment_action).with(note: note) | ||||
| 
 | ||||
|       described_class.new(project, user).execute(note) | ||||
|     end | ||||
|  |  | |||
|  | @ -69,7 +69,7 @@ RSpec.describe Notes::UpdateService do | |||
|       let(:note) { create(:note, project: project, noteable: merge_request, author: user, note: "Old note #{user2.to_reference}") } | ||||
| 
 | ||||
|       it 'tracks merge request usage data' do | ||||
|         expect(Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter).to receive(:track_edit_comment_action).with(user: user) | ||||
|         expect(Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter).to receive(:track_edit_comment_action).with(note: note) | ||||
| 
 | ||||
|         update_note(note: 'new text') | ||||
|       end | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue