diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-10-29 15:09:12 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-10-29 15:09:12 +0000 |
commit | 4dfd78cb55b08ab20124187d1aab6a431da3e302 (patch) | |
tree | a14b0086833e11b88bc023455b7c1bc8d8dbc5f4 | |
parent | 5a7d44a955572b912d13ba8949e976f61b5c7f1b (diff) | |
download | gitlab-ce-4dfd78cb55b08ab20124187d1aab6a431da3e302.tar.gz |
Add latest changes from gitlab-org/gitlab@master
84 files changed, 1069 insertions, 61 deletions
diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS index 5187ac01b58..a24fef5e44d 100644 --- a/.gitlab/CODEOWNERS +++ b/.gitlab/CODEOWNERS @@ -207,6 +207,35 @@ Dangerfile @gl-quality/eng-prod /ee/lib/gitlab/ci/reports/license_scanning/ @gitlab-org/secure/composition-analysis-be /ee/lib/gitlab/ci/reports/security/ @gitlab-org/secure/composition-analysis-be @gitlab-org/secure/dynamic-analysis-be @gitlab-org/secure/static-analysis-be @gitlab-org/secure/fuzzing-be +[Container Security] +/ee/app/views/projects/threat_monitoring/** @gitlab-org/threat-management/defend/container-security/frontend +/ee/app/assets/javascripts/pages/projects/threat_monitoring/** @gitlab-org/threat-management/defend/container-security/frontend +/ee/app/assets/javascripts/threat_monitoring/** @gitlab-org/threat-management/defend/container-security/frontend +/ee/spec/frontend/threat_monitoring/** @gitlab-org/threat-management/defend/container-security/frontend + +/ee/app/controllers/projects/threat_monitoring_controller.rb @gitlab-org/threat-management/defend/container-security/backend +/ee/spec/controllers/projects/threat_monitoring_controller_spec.rb @gitlab-org/threat-management/defend/container-security/backend +/lib/gitlab/kubernetes/cilium_network_policy.rb @gitlab-org/threat-management/defend/container-security/backend +/spec/lib/gitlab/kubernetes/cilium_network_policy_spec.rb @gitlab-org/threat-management/defend/container-security/backend +/lib/gitlab/kubernetes/network_policy_common.rb @gitlab-org/threat-management/defend/container-security/backend +/spec/support/shared_examples/lib/gitlab/kubernetes/network_policy_common_shared_examples.rb @gitlab-org/threat-management/defend/container-security/backend +/lib/gitlab/kubernetes/network_policy.rb @gitlab-org/threat-management/defend/container-security/backend +/spec/lib/gitlab/kubernetes/network_policy_spec.rb @gitlab-org/threat-management/defend/container-security/backend +/ee/app/services/network_policies/** @gitlab-org/threat-management/defend/container-security/backend +/ee/spec/services/network_policies/** @gitlab-org/threat-management/defend/container-security/backend +/ee/app/controllers/projects/security/waf_anomalies_controller.rb @gitlab-org/threat-management/defend/container-security/backend +/ee/spec/controllers/projects/security/waf_anomalies_controller_spec.rb @gitlab-org/threat-management/defend/container-security/backend +/app/models/clusters/applications/cilium.rb @gitlab-org/threat-management/defend/container-security/backend +/spec/models/clusters/applications/cilium_spec.rb @gitlab-org/threat-management/defend/container-security/backend +/ee/app/controllers/projects/security/network_policies_controller.rb @gitlab-org/threat-management/defend/container-security/backend +/ee/spec/controllers/projects/security/network_policies_controller_spec.rb @gitlab-org/threat-management/defend/container-security/backend +/ee/app/workers/network_policy_metrics_worker.rb @gitlab-org/threat-management/defend/container-security/backend +/ee/spec/workers/network_policy_metrics_worker_spec.rb @gitlab-org/threat-management/defend/container-security/backend +/ee/app/services/network_policies/** @gitlab-org/threat-management/defend/container-security/backend +/ee/spec/services/network_policies/** @gitlab-org/threat-management/defend/container-security/backend +/ee/lib/gitlab/usage_data_counters/network_policy_counter.rb @gitlab-org/threat-management/defend/container-security/backend +/ee/spec/lib/gitlab/usage_data_counters/network_policy_counter_spec.rb @gitlab-org/threat-management/defend/container-security/backend + [Code Owners] /ee/lib/gitlab/code_owners.rb @reprazent @kerrizor @garyh /ee/lib/gitlab/code_owners/ @reprazent @kerrizor @garyh diff --git a/app/assets/javascripts/reports/components/grouped_test_reports_app.vue b/app/assets/javascripts/reports/components/grouped_test_reports_app.vue index 47f04019595..29378aebdd2 100644 --- a/app/assets/javascripts/reports/components/grouped_test_reports_app.vue +++ b/app/assets/javascripts/reports/components/grouped_test_reports_app.vue @@ -1,5 +1,6 @@ <script> import { mapActions, mapGetters, mapState } from 'vuex'; +import { once } from 'lodash'; import { GlButton } from '@gitlab/ui'; import { sprintf, s__ } from '~/locale'; import { componentNames } from './issue_body'; @@ -8,6 +9,7 @@ import SummaryRow from './summary_row.vue'; import IssuesList from './issues_list.vue'; import Modal from './modal.vue'; import createStore from '../store'; +import Tracking from '~/tracking'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import { summaryTextBuilder, reportTextBuilder, statusIcon } from '../store/utils'; @@ -21,7 +23,7 @@ export default { Modal, GlButton, }, - mixins: [glFeatureFlagsMixin()], + mixins: [glFeatureFlagsMixin(), Tracking.mixin()], props: { endpoint: { type: String, @@ -58,6 +60,11 @@ export default { showViewFullReport() { return this.pipelinePath.length; }, + handleToggleEvent() { + return once(() => { + this.track(this.$options.expandEvent); + }); + }, }, created() { this.setEndpoint(this.endpoint); @@ -102,6 +109,7 @@ export default { return report.resolved_failures.concat(report.resolved_errors); }, }, + expandEvent: 'expand_test_report_widget', }; </script> <template> @@ -111,7 +119,9 @@ export default { :loading-text="groupedSummaryText" :error-text="groupedSummaryText" :has-issues="reports.length > 0" + :should-emit-toggle-event="true" class="mr-widget-section grouped-security-reports mr-report" + @toggleEvent="handleToggleEvent" > <template v-if="showViewFullReport" #actionButtons> <gl-button diff --git a/app/assets/javascripts/reports/components/report_section.vue b/app/assets/javascripts/reports/components/report_section.vue index 63af8a5a9ac..cf5c0ceadfe 100644 --- a/app/assets/javascripts/reports/components/report_section.vue +++ b/app/assets/javascripts/reports/components/report_section.vue @@ -189,6 +189,7 @@ export default { <button v-if="isCollapsible" type="button" + data-testid="report-section-expand-button" class="js-collapse-btn btn float-right btn-sm align-self-center qa-expand-report-button" @click="toggleCollapsed" > diff --git a/app/assets/javascripts/single_file_diff.js b/app/assets/javascripts/single_file_diff.js index 5fa6cef7195..3492f19c996 100644 --- a/app/assets/javascripts/single_file_diff.js +++ b/app/assets/javascripts/single_file_diff.js @@ -7,11 +7,14 @@ import { deprecatedCreateFlash as createFlash } from './flash'; import FilesCommentButton from './files_comment_button'; import initImageDiffHelper from './image_diff/helpers/init_image_diff'; import syntaxHighlight from './syntax_highlight'; +import { spriteIcon } from '~/lib/utils/common_utils'; const WRAPPER = '<div class="diff-content"></div>'; const LOADING_HTML = '<span class="spinner"></span>'; -const ERROR_HTML = - '<div class="nothing-here-block"><i class="fa fa-warning"></i> Could not load diff</div>'; +const ERROR_HTML = `<div class="nothing-here-block">${spriteIcon( + 'warning-solid', + 's16', +)} Could not load diff</div>`; const COLLAPSED_HTML = '<div class="nothing-here-block diff-collapsed">This diff is collapsed. <button class="click-to-expand btn btn-link">Click to expand it.</button></div>'; diff --git a/app/helpers/ci/runners_helper.rb b/app/helpers/ci/runners_helper.rb index 552acf61f47..432aad663e4 100644 --- a/app/helpers/ci/runners_helper.rb +++ b/app/helpers/ci/runners_helper.rb @@ -2,13 +2,15 @@ module Ci module RunnersHelper + include IconsHelper + def runner_status_icon(runner) status = runner.status case status when :not_connected - content_tag :i, nil, - class: "fa fa-warning", - title: "New runner. Has not connected yet" + content_tag(:span, title: "New runner. Has not connected yet") do + sprite_icon("warning-solid", size: 24, css_class: "gl-vertical-align-bottom!") + end when :online, :offline, :paused content_tag :i, nil, diff --git a/app/models/ci/daily_build_group_report_result.rb b/app/models/ci/daily_build_group_report_result.rb index c829ee57b37..e9f3366b939 100644 --- a/app/models/ci/daily_build_group_report_result.rb +++ b/app/models/ci/daily_build_group_report_result.rb @@ -4,6 +4,7 @@ module Ci class DailyBuildGroupReportResult < ApplicationRecord extend Gitlab::Ci::Model + REPORT_WINDOW = 90.days PARAM_TYPES = %w[coverage].freeze belongs_to :last_pipeline, class_name: 'Ci::Pipeline', foreign_key: :last_pipeline_id @@ -15,6 +16,7 @@ module Ci scope :by_projects, -> (ids) { where(project_id: ids) } scope :with_coverage, -> { where("(data->'coverage') IS NOT NULL") } scope :with_default_branch, -> { where(default_branch: true) } + scope :by_date, -> (start_date) { where(date: report_window(start_date)..Date.current) } store_accessor :data, :coverage @@ -26,6 +28,13 @@ module Ci def recent_results(attrs, limit: nil) where(attrs).order(date: :desc, group_name: :asc).limit(limit) end + + def report_window(start_date) + default_date = REPORT_WINDOW.ago.to_date + date = Date.parse(start_date) rescue default_date + + [date, default_date].max + end end end end diff --git a/app/models/pages_deployment.rb b/app/models/pages_deployment.rb index 08e541f3b93..fb13dbfb8ca 100644 --- a/app/models/pages_deployment.rb +++ b/app/models/pages_deployment.rb @@ -7,6 +7,8 @@ class PagesDeployment < ApplicationRecord belongs_to :project, optional: false belongs_to :ci_build, class_name: 'Ci::Build', optional: true + scope :older_than, -> (id) { where('id < ?', id) } + validates :file, presence: true validates :file_store, presence: true, inclusion: { in: ObjectStorage::SUPPORTED_STORES } validates :size, presence: true, numericality: { greater_than: 0, only_integer: true } diff --git a/app/models/project.rb b/app/models/project.rb index c43f78f3db9..7e4ec6c7036 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -346,7 +346,8 @@ class Project < ApplicationRecord # GitLab Pages has_many :pages_domains has_one :pages_metadatum, class_name: 'ProjectPagesMetadatum', inverse_of: :project - has_many :pages_deployments + # we need to clean up files, not only remove records + has_many :pages_deployments, dependent: :destroy # rubocop:disable Cop/ActiveRecordDependent # Can be too many records. We need to implement delete_all in batches. # Issue https://gitlab.com/gitlab-org/gitlab/-/issues/228637 @@ -1801,6 +1802,8 @@ class Project < ApplicationRecord mark_pages_as_not_deployed unless destroyed? + DestroyPagesDeploymentsWorker.perform_async(id) + # 1. We rename pages to temporary directory # 2. We wait 5 minutes, due to NFS caching # 3. We asynchronously remove pages with force @@ -1817,7 +1820,7 @@ class Project < ApplicationRecord end def mark_pages_as_not_deployed - ensure_pages_metadatum.update!(deployed: false, artifacts_archive: nil) + ensure_pages_metadatum.update!(deployed: false, artifacts_archive: nil, pages_deployment: nil) end def write_repository_config(gl_full_path: full_path) diff --git a/app/services/ci/append_build_trace_service.rb b/app/services/ci/append_build_trace_service.rb new file mode 100644 index 00000000000..602f8c5030d --- /dev/null +++ b/app/services/ci/append_build_trace_service.rb @@ -0,0 +1,65 @@ +# frozen_string_literal: true + +module Ci + class AppendBuildTraceService + Result = Struct.new(:status, :stream_size, keyword_init: true) + TraceRangeError = Class.new(StandardError) + + attr_reader :build, :params + + def initialize(build, params) + @build = build + @params = params + end + + def execute(body_data) + # TODO: + # it seems that `Content-Range` as formatted by runner is wrong, + # the `byte_end` should point to final byte, but it points byte+1 + # that means that we have to calculate end of body, + # as we cannot use `content_length[1]` + # Issue: https://gitlab.com/gitlab-org/gitlab-runner/issues/3275 + + content_range = stream_range.split('-') + body_start = content_range[0].to_i + body_end = body_start + body_data.bytesize + + stream_size = build.trace.append(body_data, body_start) + + unless stream_size == body_end + log_range_error(stream_size, body_end) + + return Result.new(status: 416, stream_size: stream_size) + end + + Result.new(status: 202, stream_size: stream_size) + end + + private + + def stream_range + params.fetch(:content_range) + end + + def log_range_error(stream_size, body_end) + extra = { + build_id: build.id, + body_end: body_end, + stream_size: stream_size, + stream_class: stream_size.class, + stream_range: stream_range + } + + build.trace_chunks.last.try do |chunk| + extra.merge!( + chunk_index: chunk.chunk_index, + chunk_store: chunk.data_store, + chunks_count: build.trace_chunks.count + ) + end + + ::Gitlab::ErrorTracking + .log_exception(TraceRangeError.new, extra) + end + end +end diff --git a/app/services/pages/destroy_deployments_service.rb b/app/services/pages/destroy_deployments_service.rb new file mode 100644 index 00000000000..45d906bec7a --- /dev/null +++ b/app/services/pages/destroy_deployments_service.rb @@ -0,0 +1,16 @@ +# frozen_string_literal: true + +module Pages + class DestroyDeploymentsService + def initialize(project, last_deployment_id = nil) + @project = project + @last_deployment_id = last_deployment_id + end + + def execute + deployments_to_destroy = @project.pages_deployments + deployments_to_destroy = deployments_to_destroy.older_than(@last_deployment_id) if @last_deployment_id + deployments_to_destroy.find_each(&:destroy) # rubocop: disable CodeReuse/ActiveRecord + end + end +end diff --git a/app/services/projects/update_pages_service.rb b/app/services/projects/update_pages_service.rb index d64ad693c6c..9a1e6595a76 100644 --- a/app/services/projects/update_pages_service.rb +++ b/app/services/projects/update_pages_service.rb @@ -12,6 +12,11 @@ module Projects # as it shares the namespace with groups TMP_EXTRACT_PATH = '@pages.tmp' + # old deployment can be cached by pages daemon + # so we need to give pages daemon some time update cache + # 10 minutes is enough, but 30 feels safer + OLD_DEPLOYMENTS_DESTRUCTION_DELAY = 30.minutes.freeze + attr_reader :build def initialize(project, build) @@ -128,6 +133,7 @@ module Projects entries_count = build.artifacts_metadata_entry("", recursive: true).entries.count sha256 = build.job_artifacts_archive.file_sha256 + deployment = nil File.open(artifacts_path) do |file| deployment = project.pages_deployments.create!(file: file, file_count: entries_count, @@ -135,7 +141,11 @@ module Projects project.pages_metadatum.update!(pages_deployment: deployment) end - # TODO: schedule old deployment removal https://gitlab.com/gitlab-org/gitlab/-/issues/235730 + DestroyPagesDeploymentsWorker.perform_in( + OLD_DEPLOYMENTS_DESTRUCTION_DELAY, + project.id, + deployment.id + ) rescue => e # we don't want to break current pages deployment process if something goes wrong # TODO: remove this rescue as part of https://gitlab.com/gitlab-org/gitlab/-/issues/245308 diff --git a/app/services/projects/update_repository_storage_service.rb b/app/services/projects/update_repository_storage_service.rb index a479d53a43a..e0d2398bc66 100644 --- a/app/services/projects/update_repository_storage_service.rb +++ b/app/services/projects/update_repository_storage_service.rb @@ -54,7 +54,7 @@ module Projects end def mirror_repositories - mirror_repository + mirror_repository if project.repository_exists? if project.wiki.repository_exists? mirror_repository(type: Gitlab::GlRepository::WIKI) @@ -92,12 +92,14 @@ module Projects end def remove_old_paths - Gitlab::Git::Repository.new( - source_storage_name, - "#{project.disk_path}.git", - nil, - nil - ).remove + if project.repository_exists? + Gitlab::Git::Repository.new( + source_storage_name, + "#{project.disk_path}.git", + nil, + nil + ).remove + end if project.wiki.repository_exists? Gitlab::Git::Repository.new( diff --git a/app/views/admin/health_check/show.html.haml b/app/views/admin/health_check/show.html.haml index 76e4fa971a3..78f0fd325fb 100644 --- a/app/views/admin/health_check/show.html.haml +++ b/app/views/admin/health_check/show.html.haml @@ -30,7 +30,7 @@ = sprite_icon('check', css_class: 'cgreen') #{ s_('HealthCheck|Healthy') } - else - = icon('warning', class: 'cred') + = sprite_icon('warning-solid', css_class: 'cred') #{ s_('HealthCheck|Unhealthy') } .card-body - if no_errors diff --git a/app/views/admin/system_info/show.html.haml b/app/views/admin/system_info/show.html.haml index 312ca62cfdf..ca6efe9b095 100644 --- a/app/views/admin/system_info/show.html.haml +++ b/app/views/admin/system_info/show.html.haml @@ -9,7 +9,7 @@ - if @cpus %h2= _('%{cores} cores') % { cores: @cpus.length } - else - = icon('warning', class: 'text-warning') + = sprite_icon('warning-solid', css_class: 'text-warning') = _('Unable to collect CPU info') .bg-light.light-well.gl-mt-3 %h4= _('Memory Usage') @@ -17,7 +17,7 @@ - if @memory %h2 #{number_to_human_size(@memory.active_bytes)} / #{number_to_human_size(@memory.total_bytes)} - else - = icon('warning', class: 'text-warning') + = sprite_icon('warning-solid', css_class: 'text-warning') = _('Unable to collect memory info') .bg-light.light-well.gl-mt-3 %h4= _('Uptime') diff --git a/app/views/projects/environments/show.html.haml b/app/views/projects/environments/show.html.haml index a9667cf8a2f..0cb44bd03fb 100644 --- a/app/views/projects/environments/show.html.haml +++ b/app/views/projects/environments/show.html.haml @@ -3,6 +3,7 @@ - page_title _("Environments") - add_page_specific_style 'page_bundles/xterm' - add_page_specific_style 'page_bundles/environments' +- add_page_specific_style 'page_bundles/ci_status' #environments-detail-view{ data: { name: @environment.name, id: @environment.id, delete_path: environment_delete_path(@environment)} } - if @environment.available? && can?(current_user, :stop_environment, @environment) diff --git a/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml b/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml index e341831e17d..1d9aad81a9e 100644 --- a/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml +++ b/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml @@ -19,7 +19,7 @@ - if ref - if generic_commit_status.ref .icon-container - = generic_commit_status.tags.any? ? icon('tag') : sprite_icon('fork', size: 10) + = generic_commit_status.tags.any? ? sprite_icon('tag', size: 10) : sprite_icon('fork', size: 10) = link_to generic_commit_status.ref, project_commits_path(generic_commit_status.project, generic_commit_status.ref) - else .light none @@ -30,7 +30,8 @@ = link_to generic_commit_status.short_sha, project_commit_path(generic_commit_status.project, generic_commit_status.sha), class: "commit-sha" - if retried - = icon('warning', class: 'text-warning has-tooltip', title: 'Status was retried.') + %span.has-tooltip{ title: _('Status was retried.') } + = sprite_icon('warning-solid', class: 'text-warning') .label-container - if generic_commit_status.tags.any? diff --git a/app/views/projects/no_repo.html.haml b/app/views/projects/no_repo.html.haml index c44d3da23bb..8631f03cc1b 100644 --- a/app/views/projects/no_repo.html.haml +++ b/app/views/projects/no_repo.html.haml @@ -1,9 +1,10 @@ - breadcrumb_title _("Details") - page_title _("Details") -%h2 - %i.fa.fa-warning - #{ _('No repository') } +%h2.gl-display-flex + .gl-display-flex.gl-align-items-center.gl-justify-content-center + = sprite_icon('warning-solid', size: 24, css_class: 'gl-mr-2') + = _('No repository') %p.slead #{ _('The repository for this project does not exist.') } diff --git a/app/views/projects/pages/_ssl_limitations_warning.html.haml b/app/views/projects/pages/_ssl_limitations_warning.html.haml index 7188e169824..1f2907d183e 100644 --- a/app/views/projects/pages/_ssl_limitations_warning.html.haml +++ b/app/views/projects/pages/_ssl_limitations_warning.html.haml @@ -1,5 +1,5 @@ .bs-callout.bs-callout-warning - %i.fa.fa-warning + = sprite_icon("warning-solid", css_class: "gl-text-orange-600") %strong= _("Warning:") - pages_host = Gitlab.config.pages.host = s_("GitLabPages|When using Pages under the general domain of a GitLab instance (%{pages_host}), you cannot use HTTPS with sub-subdomains. This means that if your username/groupname contains a dot it will not work. This is a limitation of the HTTP Over TLS protocol. HTTP pages will continue to work provided you don't redirect HTTP to HTTPS.").html_safe % { pages_host: pages_host } diff --git a/app/views/projects/pages_domains/_lets_encrypt_callout.html.haml b/app/views/projects/pages_domains/_lets_encrypt_callout.html.haml index a86637c36b3..9072312c100 100644 --- a/app/views/projects/pages_domains/_lets_encrypt_callout.html.haml +++ b/app/views/projects/pages_domains/_lets_encrypt_callout.html.haml @@ -6,7 +6,7 @@ .col-sm-10.offset-sm-2 .bs-callout.bs-callout-warning.mt-0 .row.align-items-center.mx-2 - = icon('warning', class: 'mr-2') + = sprite_icon('warning-solid', css_class: ' mr-2 gl-text-orange-600') = _("Something went wrong while obtaining the Let's Encrypt certificate.") .row.mx-0.mt-3 = link_to s_('GitLabPagesDomains|Retry'), retry_auto_ssl_project_pages_domain_path(@project, domain_presenter), class: "btn btn-sm btn-grouped btn-warning", method: :post diff --git a/app/views/shared/boards/components/sidebar/_assignee.html.haml b/app/views/shared/boards/components/sidebar/_assignee.html.haml index af6a519a967..e22a7807b3b 100644 --- a/app/views/shared/boards/components/sidebar/_assignee.html.haml +++ b/app/views/shared/boards/components/sidebar/_assignee.html.haml @@ -23,7 +23,7 @@ %button.dropdown-menu-toggle.js-user-search.js-author-search.js-multiselect.js-save-user-data.js-issue-board-sidebar{ type: 'button', ref: 'assigneeDropdown', data: board_sidebar_user_data, ":data-issuable-id" => "issue.iid" } = dropdown_options[:title] - = icon("chevron-down") + = sprite_icon('chevron-down', css_class: "dropdown-menu-toggle-icon gl-top-3") .dropdown-menu.dropdown-select.dropdown-menu-user.dropdown-menu-selectable.dropdown-menu-author = dropdown_title("Assign to") = dropdown_filter("Search users") diff --git a/app/views/shared/boards/components/sidebar/_due_date.html.haml b/app/views/shared/boards/components/sidebar/_due_date.html.haml index d8ed3b13bf1..ab4d22ac03d 100644 --- a/app/views/shared/boards/components/sidebar/_due_date.html.haml +++ b/app/views/shared/boards/components/sidebar/_due_date.html.haml @@ -24,7 +24,7 @@ %button.dropdown-menu-toggle.js-due-date-select.js-issue-boards-due-date{ type: 'button', data: { toggle: 'dropdown', field_name: "issue[due_date]", ability_name: "issue" } } %span.dropdown-toggle-text= _("Due date") - = icon('chevron-down') + = sprite_icon('chevron-down', css_class: "dropdown-menu-toggle-icon gl-top-3") .dropdown-menu.dropdown-menu-due-date = dropdown_title(_('Due date')) = dropdown_content do diff --git a/app/views/shared/boards/components/sidebar/_labels.html.haml b/app/views/shared/boards/components/sidebar/_labels.html.haml index 61f3ebcdba4..5af52d4de23 100644 --- a/app/views/shared/boards/components/sidebar/_labels.html.haml +++ b/app/views/shared/boards/components/sidebar/_labels.html.haml @@ -27,7 +27,7 @@ data: label_dropdown_data(@project, namespace_path: @namespace_path, field_name: "issue[label_names][]") } %span.dropdown-toggle-text {{ labelDropdownTitle }} - = icon('chevron-down') + = sprite_icon('chevron-down', css_class: "dropdown-menu-toggle-icon gl-top-3") .dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable.dropdown-extended-height = render partial: "shared/issuable/label_page_default" - if can?(current_user, :admin_label, current_board_parent) diff --git a/app/views/shared/boards/components/sidebar/_milestone.html.haml b/app/views/shared/boards/components/sidebar/_milestone.html.haml index 2c894e9b1b3..6143f1d5afe 100644 --- a/app/views/shared/boards/components/sidebar/_milestone.html.haml +++ b/app/views/shared/boards/components/sidebar/_milestone.html.haml @@ -21,7 +21,7 @@ ":data-issuable-id" => "issue.iid", ":data-project-id" => "issue.project_id" } = _("Milestone") - = icon("chevron-down") + = sprite_icon('chevron-down', css_class: "dropdown-menu-toggle-icon gl-top-3") .dropdown-menu.dropdown-select.dropdown-menu-selectable = dropdown_title(_("Assign milestone")) = dropdown_filter(_("Search milestones")) diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml index 7f5fca18c4c..f7ba8eb09a0 100644 --- a/app/workers/all_queues.yml +++ b/app/workers/all_queues.yml @@ -1425,6 +1425,14 @@ :weight: 1 :idempotent: :tags: [] +- :name: destroy_pages_deployments + :feature_category: :pages + :has_external_dependencies: + :urgency: :low + :resource_boundary: :unknown + :weight: 1 + :idempotent: true + :tags: [] - :name: detect_repository_languages :feature_category: :source_code_management :has_external_dependencies: diff --git a/app/workers/destroy_pages_deployments_worker.rb b/app/workers/destroy_pages_deployments_worker.rb new file mode 100644 index 00000000000..32b539325c9 --- /dev/null +++ b/app/workers/destroy_pages_deployments_worker.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +class DestroyPagesDeploymentsWorker + include ApplicationWorker + + idempotent! + + loggable_arguments 0, 1 + sidekiq_options retry: 3 + feature_category :pages + + def perform(project_id, last_deployment_id = nil) + project = Project.find_by_id(project_id) + + return unless project + + ::Pages::DestroyDeploymentsService.new(project, last_deployment_id).execute + end +end diff --git a/bin/mail_room b/bin/mail_room index 2539e3d388e..be608be4229 100755 --- a/bin/mail_room +++ b/bin/mail_room @@ -9,7 +9,8 @@ mail_room_config="$app_root/config/mail_room.yml" get_mail_room_pid() { - local pid=$(cat $mail_room_pidfile) + local pid + pid=$(cat $mail_room_pidfile) if [ -z "$pid" ] ; then echo "Could not find a PID in $mail_room_pidfile" exit 1 diff --git a/bin/web_unicorn b/bin/web_unicorn index ecd0bbd10b0..41e2ac44351 100755 --- a/bin/web_unicorn +++ b/bin/web_unicorn @@ -9,7 +9,8 @@ unicorn_cmd="bundle exec unicorn_rails -c $unicorn_config -E $RAILS_ENV" get_unicorn_pid() { - local pid=$(cat $unicorn_pidfile) + local pid + pid=$(cat $unicorn_pidfile) if [ -z "$pid" ] ; then echo "Could not find a PID in $unicorn_pidfile" exit 1 diff --git a/changelogs/unreleased/224509-replace-board-sidebar-chevron.yml b/changelogs/unreleased/224509-replace-board-sidebar-chevron.yml new file mode 100644 index 00000000000..395d0499abf --- /dev/null +++ b/changelogs/unreleased/224509-replace-board-sidebar-chevron.yml @@ -0,0 +1,5 @@ +--- +title: Replace chevron-down fa-icon in board sidebar +merge_request: 46075 +author: +type: other diff --git a/changelogs/unreleased/225180-replace-fa-warning-icons-with-gitlab-svg-warning-solid-icon.yml b/changelogs/unreleased/225180-replace-fa-warning-icons-with-gitlab-svg-warning-solid-icon.yml new file mode 100644 index 00000000000..67ecca873d8 --- /dev/null +++ b/changelogs/unreleased/225180-replace-fa-warning-icons-with-gitlab-svg-warning-solid-icon.yml @@ -0,0 +1,5 @@ +--- +title: Replace fa-warning icons with GitLab SVG warning-solid icon +merge_request: 46214 +author: +type: changed diff --git a/changelogs/unreleased/ImproveShellScriptsVariable.yml b/changelogs/unreleased/ImproveShellScriptsVariable.yml new file mode 100644 index 00000000000..d95f93b6f7d --- /dev/null +++ b/changelogs/unreleased/ImproveShellScriptsVariable.yml @@ -0,0 +1,5 @@ +--- +title: Declare and assign variable separately in Shell Script +merge_request: 46121 +author: Peter Dave Hello @PeterDaveHello +type: other diff --git a/changelogs/unreleased/add-ci-status-css-to-environments.yml b/changelogs/unreleased/add-ci-status-css-to-environments.yml new file mode 100644 index 00000000000..2ea47f432e1 --- /dev/null +++ b/changelogs/unreleased/add-ci-status-css-to-environments.yml @@ -0,0 +1,5 @@ +--- +title: Add CI Status CSS to the Environments Page +merge_request: 46382 +author: +type: fixed diff --git a/changelogs/unreleased/allow_repo_storage_moves_without_repo.yml b/changelogs/unreleased/allow_repo_storage_moves_without_repo.yml new file mode 100644 index 00000000000..4975480e349 --- /dev/null +++ b/changelogs/unreleased/allow_repo_storage_moves_without_repo.yml @@ -0,0 +1,5 @@ +--- +title: Allow project storage to be updated when no repositories exist +merge_request: 46385 +author: +type: fixed diff --git a/config/sidekiq_queues.yml b/config/sidekiq_queues.yml index 258a75f47fb..ded8b769ff1 100644 --- a/config/sidekiq_queues.yml +++ b/config/sidekiq_queues.yml @@ -86,6 +86,8 @@ - 1 - - design_management_new_version - 1 +- - destroy_pages_deployments + - 1 - - detect_repository_languages - 1 - - disallow_two_factor_for_group diff --git a/doc/api/graphql/reference/gitlab_schema.graphql b/doc/api/graphql/reference/gitlab_schema.graphql index cd134233f77..3999f982a36 100644 --- a/doc/api/graphql/reference/gitlab_schema.graphql +++ b/doc/api/graphql/reference/gitlab_schema.graphql @@ -2683,6 +2683,66 @@ Identifier of Clusters::Cluster scalar ClustersClusterID """ +Represents the code coverage activity for a group +""" +type CodeCoverageActivity { + """ + Average percentage of the different code coverage results available for the group. + """ + averageCoverage: Float + + """ + Number of different code coverage results available for the group. + """ + coverageCount: Int + + """ + Date when the code coverage was created. + """ + date: Date! + + """ + Number of projects with code coverage results for the group. + """ + projectCount: Int +} + +""" +The connection type for CodeCoverageActivity. +""" +type CodeCoverageActivityConnection { + """ + A list of edges. + """ + edges: [CodeCoverageActivityEdge] + + """ + A list of nodes. + """ + nodes: [CodeCoverageActivity] + + """ + Information to aid in pagination. + """ + pageInfo: PageInfo! +} + +""" +An edge in a connection. +""" +type CodeCoverageActivityEdge { + """ + A cursor for use in pagination. + """ + cursor: String! + + """ + The item at the end of the edge. + """ + node: CodeCoverageActivity +} + +""" Represents the code coverage summary for a project """ type CodeCoverageSummary { @@ -7966,6 +8026,37 @@ type Group { ): BoardConnection """ + Represents the code coverage activity for this group. Available only when + feature flag `group_coverage_data_report` is enabled + """ + codeCoverageActivities( + """ + Returns the elements in the list that come after the specified cursor. + """ + after: String + + """ + Returns the elements in the list that come before the specified cursor. + """ + before: String + + """ + Returns the first _n_ elements from the list. + """ + first: Int + + """ + Returns the last _n_ elements from the list. + """ + last: Int + + """ + First day for which to fetch code coverage activity (maximum time window is set to 90 days) + """ + startDate: Date! + ): CodeCoverageActivityConnection + + """ Container repositories of the project """ containerRepositories( diff --git a/doc/api/graphql/reference/gitlab_schema.json b/doc/api/graphql/reference/gitlab_schema.json index cfca138011a..0b4f3652cc8 100644 --- a/doc/api/graphql/reference/gitlab_schema.json +++ b/doc/api/graphql/reference/gitlab_schema.json @@ -7330,6 +7330,191 @@ }, { "kind": "OBJECT", + "name": "CodeCoverageActivity", + "description": "Represents the code coverage activity for a group", + "fields": [ + { + "name": "averageCoverage", + "description": "Average percentage of the different code coverage results available for the group.", + "args": [ + + ], + "type": { + "kind": "SCALAR", + "name": "Float", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "coverageCount", + "description": "Number of different code coverage results available for the group.", + "args": [ + + ], + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "date", + "description": "Date when the code coverage was created.", + "args": [ + + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Date", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "projectCount", + "description": "Number of projects with code coverage results for the group.", + "args": [ + + ], + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "CodeCoverageActivityConnection", + "description": "The connection type for CodeCoverageActivity.", + "fields": [ + { + "name": "edges", + "description": "A list of edges.", + "args": [ + + ], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "CodeCoverageActivityEdge", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "nodes", + "description": "A list of nodes.", + "args": [ + + ], + "type": { + "kind": "LIST", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "CodeCoverageActivity", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "pageInfo", + "description": "Information to aid in pagination.", + "args": [ + + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "OBJECT", + "name": "PageInfo", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", + "name": "CodeCoverageActivityEdge", + "description": "An edge in a connection.", + "fields": [ + { + "name": "cursor", + "description": "A cursor for use in pagination.", + "args": [ + + ], + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "String", + "ofType": null + } + }, + "isDeprecated": false, + "deprecationReason": null + }, + { + "name": "node", + "description": "The item at the end of the edge.", + "args": [ + + ], + "type": { + "kind": "OBJECT", + "name": "CodeCoverageActivity", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + } + ], + "inputFields": null, + "interfaces": [ + + ], + "enumValues": null, + "possibleTypes": null + }, + { + "kind": "OBJECT", "name": "CodeCoverageSummary", "description": "Represents the code coverage summary for a project", "fields": [ @@ -22009,6 +22194,73 @@ "deprecationReason": null }, { + "name": "codeCoverageActivities", + "description": "Represents the code coverage activity for this group. Available only when feature flag `group_coverage_data_report` is enabled", + "args": [ + { + "name": "startDate", + "description": "First day for which to fetch code coverage activity (maximum time window is set to 90 days)", + "type": { + "kind": "NON_NULL", + "name": null, + "ofType": { + "kind": "SCALAR", + "name": "Date", + "ofType": null + } + }, + "defaultValue": null + }, + { + "name": "after", + "description": "Returns the elements in the list that come after the specified cursor.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "before", + "description": "Returns the elements in the list that come before the specified cursor.", + "type": { + "kind": "SCALAR", + "name": "String", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "first", + "description": "Returns the first _n_ elements from the list.", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "defaultValue": null + }, + { + "name": "last", + "description": "Returns the last _n_ elements from the list.", + "type": { + "kind": "SCALAR", + "name": "Int", + "ofType": null + }, + "defaultValue": null + } + ], + "type": { + "kind": "OBJECT", + "name": "CodeCoverageActivityConnection", + "ofType": null + }, + "isDeprecated": false, + "deprecationReason": null + }, + { "name": "containerRepositories", "description": "Container repositories of the project", "args": [ diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index fb3086975b2..5e960f293bd 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -418,6 +418,17 @@ Autogenerated return type of ClusterAgentTokenDelete. | `clientMutationId` | String | A unique identifier for the client performing the mutation. | | `errors` | String! => Array | Errors encountered during execution of the mutation. | +### CodeCoverageActivity + +Represents the code coverage activity for a group. + +| Field | Type | Description | +| ----- | ---- | ----------- | +| `averageCoverage` | Float | Average percentage of the different code coverage results available for the group. | +| `coverageCount` | Int | Number of different code coverage results available for the group. | +| `date` | Date! | Date when the code coverage was created. | +| `projectCount` | Int | Number of projects with code coverage results for the group. | + ### CodeCoverageSummary Represents the code coverage summary for a project. diff --git a/doc/architecture/blueprints/cloud_native_build_logs/index.md b/doc/architecture/blueprints/cloud_native_build_logs/index.md index 25abfe36e88..f901a724653 100644 --- a/doc/architecture/blueprints/cloud_native_build_logs/index.md +++ b/doc/architecture/blueprints/cloud_native_build_logs/index.md @@ -1,4 +1,7 @@ --- +stage: none +group: unassigned +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/#designated-technical-writers comments: false description: 'Next iteration of build logs architecture at GitLab' --- diff --git a/doc/architecture/blueprints/cloud_native_gitlab_pages/index.md b/doc/architecture/blueprints/cloud_native_gitlab_pages/index.md index 37e69d46ae1..27d2f1362e5 100644 --- a/doc/architecture/blueprints/cloud_native_gitlab_pages/index.md +++ b/doc/architecture/blueprints/cloud_native_gitlab_pages/index.md @@ -1,4 +1,7 @@ --- +stage: none +group: unassigned +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/#designated-technical-writers comments: false description: 'Making GitLab Pages a Cloud Native application - architecture blueprint.' --- diff --git a/doc/architecture/blueprints/feature_flags_development/index.md b/doc/architecture/blueprints/feature_flags_development/index.md index 0aeb2b51b39..76fb5f5c7db 100644 --- a/doc/architecture/blueprints/feature_flags_development/index.md +++ b/doc/architecture/blueprints/feature_flags_development/index.md @@ -1,4 +1,7 @@ --- +stage: none +group: unassigned +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/#designated-technical-writers comments: false description: 'Internal usage of Feature Flags for GitLab development' --- diff --git a/doc/architecture/blueprints/image_resizing/index.md b/doc/architecture/blueprints/image_resizing/index.md index 964e935cbb1..ed2dc01b7ed 100644 --- a/doc/architecture/blueprints/image_resizing/index.md +++ b/doc/architecture/blueprints/image_resizing/index.md @@ -1,4 +1,7 @@ --- +stage: none +group: unassigned +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/#designated-technical-writers comments: false description: 'Image Resizing' --- diff --git a/doc/architecture/index.md b/doc/architecture/index.md index 0a2ade6b7b0..0cac646ea83 100644 --- a/doc/architecture/index.md +++ b/doc/architecture/index.md @@ -1,4 +1,7 @@ --- +stage: none +group: unassigned +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/#designated-technical-writers comments: false description: 'Architecture Practice at GitLab' --- diff --git a/doc/ci/lint.md b/doc/ci/lint.md index 716a4218d97..f6e0370b32b 100644 --- a/doc/ci/lint.md +++ b/doc/ci/lint.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +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/#designated-technical-writers +--- + # CI Lint If you want to test the validity of your GitLab CI/CD configuration before committing diff --git a/doc/downgrade_ee_to_ce/README.md b/doc/downgrade_ee_to_ce/README.md index cba21668816..2561ee875d2 100644 --- a/doc/downgrade_ee_to_ce/README.md +++ b/doc/downgrade_ee_to_ce/README.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +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/#designated-technical-writers +--- + # Downgrading from EE to CE If you ever decide to downgrade your Enterprise Edition back to the Community diff --git a/doc/install/digitaloceandocker.md b/doc/install/digitaloceandocker.md index fe32b37a9ed..deb8a8cc6ca 100644 --- a/doc/install/digitaloceandocker.md +++ b/doc/install/digitaloceandocker.md @@ -1,4 +1,7 @@ --- +stage: none +group: unassigned +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/#designated-technical-writers type: howto --- diff --git a/doc/install/pivotal/index.md b/doc/install/pivotal/index.md index 6a4b361c842..41a5ea82ea2 100644 --- a/doc/install/pivotal/index.md +++ b/doc/install/pivotal/index.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +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/#designated-technical-writers +--- + # GitLab Pivotal Tile **(PREMIUM ONLY)** CAUTION: **Discontinued:** diff --git a/doc/install/postgresql_extensions.md b/doc/install/postgresql_extensions.md index 9e5a1e3d627..6355806f067 100644 --- a/doc/install/postgresql_extensions.md +++ b/doc/install/postgresql_extensions.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +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/#designated-technical-writers +--- + # Managing PostgreSQL extensions This guide documents how to manage PostgreSQL extensions for installations with an external diff --git a/doc/integration/README.md b/doc/integration/README.md index c8ce367e99f..25e8c1a51c1 100644 --- a/doc/integration/README.md +++ b/doc/integration/README.md @@ -1,4 +1,7 @@ --- +stage: none +group: unassigned +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/#designated-technical-writers comments: false --- diff --git a/doc/integration/akismet.md b/doc/integration/akismet.md index 7cb8f8b70ce..8be410e97b9 100644 --- a/doc/integration/akismet.md +++ b/doc/integration/akismet.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +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/#designated-technical-writers +--- + # Akismet GitLab leverages [Akismet](https://akismet.com/) to protect against spam. Currently diff --git a/doc/integration/auth0.md b/doc/integration/auth0.md index d851b9f5dc7..d53fa6efc29 100644 --- a/doc/integration/auth0.md +++ b/doc/integration/auth0.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +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/#designated-technical-writers +--- + # Auth0 OmniAuth Provider To enable the Auth0 OmniAuth provider, you must create an Auth0 account, and an diff --git a/doc/integration/azure.md b/doc/integration/azure.md index 2059707e38c..2964b93055a 100644 --- a/doc/integration/azure.md +++ b/doc/integration/azure.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +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/#designated-technical-writers +--- + # Microsoft Azure OAuth2 OmniAuth Provider To enable the Microsoft Azure OAuth2 OmniAuth provider you must register your application with Azure. Azure will generate a client ID and secret key for you to use. diff --git a/doc/integration/bitbucket.md b/doc/integration/bitbucket.md index a151fbf50e7..52bd7aaecc5 100644 --- a/doc/integration/bitbucket.md +++ b/doc/integration/bitbucket.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +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/#designated-technical-writers +--- + # Integrate your GitLab server with Bitbucket Cloud NOTE: **Note:** diff --git a/doc/integration/cas.md b/doc/integration/cas.md index eee801350eb..e61988c3301 100644 --- a/doc/integration/cas.md +++ b/doc/integration/cas.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +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/#designated-technical-writers +--- + # CAS OmniAuth Provider To enable the CAS OmniAuth provider you must register your application with your CAS instance. This requires the service URL GitLab will supply to CAS. It should be something like: `https://gitlab.example.com:443/users/auth/cas3/callback?url`. By default handling for SLO is enabled, you only need to configure CAS for backchannel logout. diff --git a/doc/integration/external-issue-tracker.md b/doc/integration/external-issue-tracker.md index 96c9b9d7f62..a4fca36b154 100644 --- a/doc/integration/external-issue-tracker.md +++ b/doc/integration/external-issue-tracker.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +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/#designated-technical-writers +--- + # External issue tracker GitLab has a great [issue tracker](../user/project/issues/index.md) but you can also use an external diff --git a/doc/integration/facebook.md b/doc/integration/facebook.md index dbefb560fe7..bb699fa90b7 100644 --- a/doc/integration/facebook.md +++ b/doc/integration/facebook.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +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/#designated-technical-writers +--- + # Facebook OAuth2 OmniAuth Provider To enable the Facebook OmniAuth provider you must register your application with Facebook. Facebook will generate an app ID and secret key for you to use. diff --git a/doc/integration/github.md b/doc/integration/github.md index ce2b50acc54..cff919c902a 100644 --- a/doc/integration/github.md +++ b/doc/integration/github.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +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/#designated-technical-writers +--- + # Integrate your GitLab instance with GitHub You can integrate your GitLab instance with GitHub.com as well as GitHub Enterprise to enable users to import projects from GitHub and/or to login to your GitLab instance with your GitHub account. diff --git a/doc/integration/gitlab.md b/doc/integration/gitlab.md index a200f6b6470..c618d226290 100644 --- a/doc/integration/gitlab.md +++ b/doc/integration/gitlab.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +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/#designated-technical-writers +--- + # Integrate your server with GitLab.com Import projects from GitLab.com and login to your GitLab instance with your GitLab.com account. diff --git a/doc/integration/gmail_action_buttons_for_gitlab.md b/doc/integration/gmail_action_buttons_for_gitlab.md index 526db8a7338..72196fd0f52 100644 --- a/doc/integration/gmail_action_buttons_for_gitlab.md +++ b/doc/integration/gmail_action_buttons_for_gitlab.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +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/#designated-technical-writers +--- + # Gmail actions buttons for GitLab GitLab supports [Google actions in email](https://developers.google.com/gmail/markup/actions/actions-overview). diff --git a/doc/integration/google.md b/doc/integration/google.md index 4cf589c1da8..cd40aaff30a 100644 --- a/doc/integration/google.md +++ b/doc/integration/google.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +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/#designated-technical-writers +--- + # Google OAuth2 OmniAuth Provider To enable the Google OAuth2 OmniAuth provider you must register your application diff --git a/doc/integration/jenkins.md b/doc/integration/jenkins.md index 8fc638db95a..7eb147c1fe6 100644 --- a/doc/integration/jenkins.md +++ b/doc/integration/jenkins.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +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/#designated-technical-writers +--- + # Jenkins CI service **(STARTER)** NOTE: **Note:** diff --git a/doc/integration/jenkins_deprecated.md b/doc/integration/jenkins_deprecated.md index 5fc30bf3305..63d5ac48765 100644 --- a/doc/integration/jenkins_deprecated.md +++ b/doc/integration/jenkins_deprecated.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +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/#designated-technical-writers +--- + # Jenkins CI (deprecated) service NOTE: **Note:** diff --git a/doc/integration/oauth2_generic.md b/doc/integration/oauth2_generic.md index 8566134815a..5957af292ab 100644 --- a/doc/integration/oauth2_generic.md +++ b/doc/integration/oauth2_generic.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +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/#designated-technical-writers +--- + # Sign into GitLab with (almost) any OAuth2 provider The `omniauth-oauth2-generic` gem allows Single Sign On between GitLab and your own OAuth2 provider diff --git a/doc/integration/oauth_provider.md b/doc/integration/oauth_provider.md index fd1c21d725d..68d10a3135e 100644 --- a/doc/integration/oauth_provider.md +++ b/doc/integration/oauth_provider.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +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/#designated-technical-writers +--- + # GitLab as OAuth2 authentication service provider This document is about using GitLab as an OAuth authentication service provider diff --git a/doc/integration/omniauth.md b/doc/integration/omniauth.md index cf09c2f2803..535f97e9a4d 100644 --- a/doc/integration/omniauth.md +++ b/doc/integration/omniauth.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +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/#designated-technical-writers +--- + # OmniAuth GitLab leverages OmniAuth to allow users to sign in using Twitter, GitHub, and diff --git a/doc/integration/openid_connect_provider.md b/doc/integration/openid_connect_provider.md index b66262772da..bf33483f949 100644 --- a/doc/integration/openid_connect_provider.md +++ b/doc/integration/openid_connect_provider.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +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/#designated-technical-writers +--- + # GitLab as OpenID Connect identity provider This document is about using GitLab as an OpenID Connect identity provider diff --git a/doc/integration/recaptcha.md b/doc/integration/recaptcha.md index 1868711ca9c..545f60cddbf 100644 --- a/doc/integration/recaptcha.md +++ b/doc/integration/recaptcha.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +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/#designated-technical-writers +--- + # reCAPTCHA GitLab leverages [Google's reCAPTCHA](https://www.google.com/recaptcha/about/) diff --git a/doc/integration/salesforce.md b/doc/integration/salesforce.md index dbd0a03e3cf..3290f18e2cb 100644 --- a/doc/integration/salesforce.md +++ b/doc/integration/salesforce.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +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/#designated-technical-writers +--- + # Salesforce OmniAuth Provider You can integrate your GitLab instance with [Salesforce](https://www.salesforce.com/) to enable users to log in to your GitLab instance with their Salesforce account. diff --git a/doc/integration/shibboleth.md b/doc/integration/shibboleth.md index 1b645541cec..59374d8ad6f 100644 --- a/doc/integration/shibboleth.md +++ b/doc/integration/shibboleth.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +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/#designated-technical-writers +--- + # Shibboleth OmniAuth Provider NOTE: **Note:** diff --git a/doc/integration/slash_commands.md b/doc/integration/slash_commands.md index c73db32a42a..ea2c4b3e93f 100644 --- a/doc/integration/slash_commands.md +++ b/doc/integration/slash_commands.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +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/#designated-technical-writers +--- + # Slash Commands > The `run` command was [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/4466) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 10.6. [Moved](https://gitlab.com/gitlab-org/gitlab-foss/-/merge_requests/24780) to [GitLab Core](https://about.gitlab.com/pricing/) in 11.9. diff --git a/doc/integration/trello_power_up.md b/doc/integration/trello_power_up.md index fc55dbb9654..22481e14236 100644 --- a/doc/integration/trello_power_up.md +++ b/doc/integration/trello_power_up.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +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/#designated-technical-writers +--- + # Trello Power-Up GitLab's Trello Power-Up enables you to seamlessly attach diff --git a/doc/integration/twitter.md b/doc/integration/twitter.md index e501eac0c5f..bfe18c43e9d 100644 --- a/doc/integration/twitter.md +++ b/doc/integration/twitter.md @@ -1,3 +1,9 @@ +--- +stage: none +group: unassigned +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/#designated-technical-writers +--- + # Twitter OAuth2 OmniAuth Provider To enable the Twitter OmniAuth provider you must register your application with Twitter. Twitter will generate a client ID and secret key for you to use. diff --git a/doc/user/analytics/merge_request_analytics.md b/doc/user/analytics/merge_request_analytics.md index 04a5fa71e19..996e53328aa 100644 --- a/doc/user/analytics/merge_request_analytics.md +++ b/doc/user/analytics/merge_request_analytics.md @@ -57,9 +57,11 @@ Data table displaying a maximum of the 100 most recent merge requests merged for You can filter the data that is presented on the page based on the following parameters: - Author -- Assignees -- Labels -- Milestones +- Assignee +- Label +- Milestone +- Source branch +- Target branch To filter results: diff --git a/doc/user/group/saml_sso/index.md b/doc/user/group/saml_sso/index.md index 3cb566c7f77..deb197952ab 100644 --- a/doc/user/group/saml_sso/index.md +++ b/doc/user/group/saml_sso/index.md @@ -88,8 +88,6 @@ We intend to add a similar SSO requirement for [Git and API activity](https://gi When SSO enforcement is enabled for a group, users cannot share a project in the group outside the top-level group, even if the project is forked. -To disallow users to contribute outside of the top-level group, please see [Group Managed Accounts](group_managed_accounts.md). - ## Providers NOTE: **Note:** diff --git a/lib/api/ci/runner.rb b/lib/api/ci/runner.rb index c2e2b59d39f..85232b4ae1b 100644 --- a/lib/api/ci/runner.rb +++ b/lib/api/ci/runner.rb @@ -207,27 +207,18 @@ module API error!('400 Missing header Content-Range', 400) unless request.headers.key?('Content-Range') content_range = request.headers['Content-Range'] - content_range = content_range.split('-') - - # TODO: - # it seems that `Content-Range` as formatted by runner is wrong, - # the `byte_end` should point to final byte, but it points byte+1 - # that means that we have to calculate end of body, - # as we cannot use `content_length[1]` - # Issue: https://gitlab.com/gitlab-org/gitlab-runner/issues/3275 - - body_data = request.body.read - body_start = content_range[0].to_i - body_end = body_start + body_data.bytesize - - stream_size = job.trace.append(body_data, body_start) - unless stream_size == body_end - break error!('416 Range Not Satisfiable', 416, { 'Range' => "0-#{stream_size}" }) + + result = ::Ci::AppendBuildTraceService + .new(job, content_range: content_range) + .execute(request.body.read) + + if result.status == 416 + break error!('416 Range Not Satisfiable', 416, { 'Range' => "0-#{result.stream_size}" }) end - status 202 + status result.status header 'Job-Status', job.status - header 'Range', "0-#{stream_size}" + header 'Range', "0-#{result.stream_size}" header 'X-GitLab-Trace-Update-Interval', job.trace.update_interval.to_s end diff --git a/lib/gitlab/badge/coverage/report.rb b/lib/gitlab/badge/coverage/report.rb index 0df6e858bf4..40fcd2f89f2 100644 --- a/lib/gitlab/badge/coverage/report.rb +++ b/lib/gitlab/badge/coverage/report.rb @@ -17,8 +17,6 @@ module Gitlab key_width: opts[:key_width].to_i, key_text: opts[:key_text] } - - @pipeline = @project.ci_pipelines.latest_successful_for_ref(@ref) end def entity @@ -42,14 +40,18 @@ module Gitlab private + def pipeline + @pipeline ||= @project.ci_pipelines.latest_successful_for_ref(@ref) + end + # rubocop: disable CodeReuse/ActiveRecord def raw_coverage - return unless @pipeline + return unless pipeline if @job.blank? - @pipeline.coverage + pipeline.coverage else - @pipeline.builds + pipeline.builds .find_by(name: @job) .try(:coverage) end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 1f4a12b8b8c..5c31006122e 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -25420,6 +25420,9 @@ msgstr "" msgid "Status" msgstr "" +msgid "Status was retried." +msgstr "" + msgid "Status:" msgstr "" diff --git a/spec/frontend/reports/components/grouped_test_reports_app_spec.js b/spec/frontend/reports/components/grouped_test_reports_app_spec.js index 556904b7da5..14c984ce32f 100644 --- a/spec/frontend/reports/components/grouped_test_reports_app_spec.js +++ b/spec/frontend/reports/components/grouped_test_reports_app_spec.js @@ -1,5 +1,6 @@ import { mount, createLocalVue } from '@vue/test-utils'; import Vuex from 'vuex'; +import { mockTracking } from 'helpers/tracking_helper'; import GroupedTestReportsApp from '~/reports/components/grouped_test_reports_app.vue'; import { getStoreConfig } from '~/reports/store'; @@ -39,6 +40,7 @@ describe('Grouped test reports app', () => { }; const findHeader = () => wrapper.find('[data-testid="report-section-code-text"]'); + const findExpandButton = () => wrapper.find('[data-testid="report-section-expand-button"]'); const findFullTestReportLink = () => wrapper.find('[data-testid="group-test-reports-full-link"]'); const findSummaryDescription = () => wrapper.find('[data-testid="test-summary-row-description"]'); const findIssueDescription = () => wrapper.find('[data-testid="test-issue-body-description"]'); @@ -96,6 +98,35 @@ describe('Grouped test reports app', () => { }); }); + describe('`Expand` button', () => { + let trackingSpy; + + beforeEach(() => { + setReports(newFailedTestReports); + mountComponent(); + document.body.dataset.page = 'projects:merge_requests:show'; + trackingSpy = mockTracking('_category_', wrapper.element, jest.spyOn); + }); + + it('tracks an event on click', () => { + findExpandButton().trigger('click'); + + expect(trackingSpy).toHaveBeenCalledWith(undefined, 'expand_test_report_widget', {}); + }); + + it('only tracks the first expansion', () => { + expect(trackingSpy).not.toHaveBeenCalled(); + + const button = findExpandButton(); + + button.trigger('click'); + button.trigger('click'); + button.trigger('click'); + + expect(trackingSpy).toHaveBeenCalledTimes(1); + }); + }); + describe('with new failed result', () => { beforeEach(() => { setReports(newFailedTestReports); diff --git a/spec/models/ci/daily_build_group_report_result_spec.rb b/spec/models/ci/daily_build_group_report_result_spec.rb index 68c5d58cfd5..f16396d62c9 100644 --- a/spec/models/ci/daily_build_group_report_result_spec.rb +++ b/spec/models/ci/daily_build_group_report_result_spec.rb @@ -123,5 +123,39 @@ RSpec.describe Ci::DailyBuildGroupReportResult do end end end + + describe '.by_date' do + subject(:coverages) { described_class.by_date(start_date) } + + let!(:coverage_1) { create(:ci_daily_build_group_report_result, date: 1.week.ago) } + + context 'when project has several coverage' do + let!(:coverage_2) { create(:ci_daily_build_group_report_result, date: 2.weeks.ago) } + let(:start_date) { 1.week.ago.to_date.to_s } + + it 'returns the coverage from the start_date' do + expect(coverages).to contain_exactly(coverage_1) + end + end + + context 'when start_date is over 90 days' do + let!(:coverage_2) { create(:ci_daily_build_group_report_result, date: 90.days.ago) } + let!(:coverage_3) { create(:ci_daily_build_group_report_result, date: 91.days.ago) } + let(:start_date) { 1.year.ago.to_date.to_s } + + it 'returns the coverage in the last 90 days' do + expect(coverages).to contain_exactly(coverage_1, coverage_2) + end + end + + context 'when start_date is not a string' do + let!(:coverage_2) { create(:ci_daily_build_group_report_result, date: 90.days.ago) } + let(:start_date) { 1.week.ago } + + it 'returns the coverage in the last 90 days' do + expect(coverages).to contain_exactly(coverage_1, coverage_2) + end + end + end end end diff --git a/spec/models/pages_deployment_spec.rb b/spec/models/pages_deployment_spec.rb index 057c01d26cf..e059b477e0e 100644 --- a/spec/models/pages_deployment_spec.rb +++ b/spec/models/pages_deployment_spec.rb @@ -42,4 +42,17 @@ RSpec.describe PagesDeployment do deployment = create(:pages_deployment) expect(deployment.size).to eq(deployment.file.size) end + + describe '.older_than' do + it 'returns deployments with lower id' do + old_deployments = create_list(:pages_deployment, 2) + + deployment = create(:pages_deployment) + + # new deployment + create(:pages_deployment) + + expect(PagesDeployment.older_than(deployment.id)).to eq(old_deployments) + end + end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 5f643fb03df..5e2711c791e 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -3668,7 +3668,7 @@ RSpec.describe Project, factory_default: :keep do let(:project) { create(:project) } before do - project.namespace_id = 7 + project.namespace_id = project.namespace_id + 1 end it { expect(project.parent_changed?).to be_truthy } @@ -4219,6 +4219,27 @@ RSpec.describe Project, factory_default: :keep do expect { project.destroy }.not_to raise_error end + + context 'when there is an old pages deployment' do + let!(:old_deployment_from_another_project) { create(:pages_deployment) } + let!(:old_deployment) { create(:pages_deployment, project: project) } + + it 'schedules a destruction of pages deployments' do + expect(DestroyPagesDeploymentsWorker).to( + receive(:perform_async).with(project.id) + ) + + project.remove_pages + end + + it 'removes pages deployments', :sidekiq_inline do + expect do + project.remove_pages + end.to change { PagesDeployment.count }.by(-1) + + expect(PagesDeployment.find_by_id(old_deployment.id)).to be_nil + end + end end describe '#remove_export' do diff --git a/spec/services/ci/append_build_trace_service_spec.rb b/spec/services/ci/append_build_trace_service_spec.rb new file mode 100644 index 00000000000..aa1dd2bbc10 --- /dev/null +++ b/spec/services/ci/append_build_trace_service_spec.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Ci::AppendBuildTraceService do + let(:project) { create(:project) } + let(:pipeline) { create(:ci_pipeline, project: project) } + let(:build) { create(:ci_build, :running, pipeline: pipeline) } + + before do + stub_feature_flags(ci_enable_live_trace: true) + end + + context 'build trace append is successful' do + it 'returns a correct stream size and status code' do + stream_size = 192.kilobytes + body_data = 'x' * stream_size + content_range = "0-#{stream_size}" + + result = described_class + .new(build, content_range: content_range) + .execute(body_data) + + expect(result.status).to eq 202 + expect(result.stream_size).to eq stream_size + expect(build.trace_chunks.count).to eq 2 + end + end + + context 'when could not correctly append to a trace' do + it 'responds with content range violation and data stored' do + allow(build).to receive_message_chain(:trace, :append) { 16 } + + result = described_class + .new(build, content_range: '0-128') + .execute('x' * 128) + + expect(result.status).to eq 416 + expect(result.stream_size).to eq 16 + end + + it 'logs exception if build has live trace' do + build.trace.append('abcd', 0) + + expect(::Gitlab::ErrorTracking) + .to receive(:log_exception) + .with(anything, hash_including(chunk_index: 0, chunk_store: 'redis')) + + result = described_class + .new(build, content_range: '0-128') + .execute('x' * 128) + + expect(result.status).to eq 416 + expect(result.stream_size).to eq 4 + end + end +end diff --git a/spec/services/pages/destroy_deployments_service_spec.rb b/spec/services/pages/destroy_deployments_service_spec.rb new file mode 100644 index 00000000000..0f8e8b6573e --- /dev/null +++ b/spec/services/pages/destroy_deployments_service_spec.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Pages::DestroyDeploymentsService do + let(:project) { create(:project) } + let!(:old_deployments) { create_list(:pages_deployment, 2, project: project) } + let!(:last_deployment) { create(:pages_deployment, project: project) } + let!(:newer_deployment) { create(:pages_deployment, project: project) } + let!(:deployment_from_another_project) { create(:pages_deployment) } + + it 'destroys all deployments of the project' do + expect do + described_class.new(project).execute + end.to change { PagesDeployment.count }.by(-4) + + expect(deployment_from_another_project.reload).to be + end + + it 'destroy only deployments older than last deployment if it is provided' do + expect do + described_class.new(project, last_deployment.id).execute + end.to change { PagesDeployment.count }.by(-2) + + expect(last_deployment.reload).to be + expect(newer_deployment.reload).to be + expect(deployment_from_another_project.reload).to be + end +end diff --git a/spec/services/projects/update_pages_service_spec.rb b/spec/services/projects/update_pages_service_spec.rb index d8be4d47437..92772136d69 100644 --- a/spec/services/projects/update_pages_service_spec.rb +++ b/spec/services/projects/update_pages_service_spec.rb @@ -71,6 +71,29 @@ RSpec.describe Projects::UpdatePagesService do expect(project.pages_metadatum.reload.pages_deployment_id).to eq(deployment.id) end + context 'when there is an old pages deployment' do + let!(:old_deployment_from_another_project) { create(:pages_deployment) } + let!(:old_deployment) { create(:pages_deployment, project: project) } + + it 'schedules a destruction of older deployments' do + expect(DestroyPagesDeploymentsWorker).to( + receive(:perform_in).with(described_class::OLD_DEPLOYMENTS_DESTRUCTION_DELAY, + project.id, + instance_of(Integer)) + ) + + execute + end + + it 'removes older deployments', :sidekiq_inline do + expect do + execute + end.not_to change { PagesDeployment.count } # it creates one and deletes one + + expect(PagesDeployment.find_by_id(old_deployment.id)).to be_nil + end + end + it 'does not create deployment when zip_pages_deployments feature flag is disabled' do stub_feature_flags(zip_pages_deployments: false) diff --git a/spec/services/projects/update_repository_storage_service_spec.rb b/spec/services/projects/update_repository_storage_service_spec.rb index 0fcd14f3bc9..123f604e7a4 100644 --- a/spec/services/projects/update_repository_storage_service_spec.rb +++ b/spec/services/projects/update_repository_storage_service_spec.rb @@ -168,6 +168,24 @@ RSpec.describe Projects::UpdateRepositoryStorageService do end end + context 'project with no repositories' do + let(:project) { create(:project) } + let(:repository_storage_move) { create(:project_repository_storage_move, :scheduled, project: project, destination_storage_name: 'test_second_storage') } + + it 'updates the database' do + allow(Gitlab::GitalyClient).to receive(:filesystem_id).with('default').and_call_original + allow(Gitlab::GitalyClient).to receive(:filesystem_id).with('test_second_storage').and_return(SecureRandom.uuid) + + result = subject.execute + project.reload + + expect(result).to be_success + expect(project).not_to be_repository_read_only + expect(project.repository_storage).to eq('test_second_storage') + expect(project.project_repository.shard_name).to eq('test_second_storage') + end + end + context 'with wiki repository' do include_examples 'moves repository to another storage', 'wiki' do let(:project) { create(:project, :repository, wiki_enabled: true) } diff --git a/spec/workers/destroy_pages_deployments_worker_spec.rb b/spec/workers/destroy_pages_deployments_worker_spec.rb new file mode 100644 index 00000000000..2c20c9004ef --- /dev/null +++ b/spec/workers/destroy_pages_deployments_worker_spec.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe DestroyPagesDeploymentsWorker do + subject(:worker) { described_class.new } + + let(:project) { create(:project) } + let!(:old_deployment) { create(:pages_deployment, project: project) } + let!(:last_deployment) { create(:pages_deployment, project: project) } + let!(:another_deployment) { create(:pages_deployment) } + + it "doesn't fail if project is already removed" do + expect do + worker.perform(-1) + end.not_to raise_error + end + + it 'can be called without last_deployment_id' do + expect_next_instance_of(::Pages::DestroyDeploymentsService, project, nil) do |service| + expect(service).to receive(:execute).and_call_original + end + + expect do + worker.perform(project.id) + end.to change { PagesDeployment.count }.by(-2) + end + + it 'calls destroy service' do + expect_next_instance_of(::Pages::DestroyDeploymentsService, project, last_deployment.id) do |service| + expect(service).to receive(:execute).and_call_original + end + + expect do + worker.perform(project.id, last_deployment.id) + end.to change { PagesDeployment.count }.by(-1) + end +end |