diff --git a/debian/patches/fix-mismatch-by-manual-merge.patch b/debian/patches/fix-mismatch-by-manual-merge.patch deleted file mode 100644 index 1d4fbeb9e9..0000000000 --- a/debian/patches/fix-mismatch-by-manual-merge.patch +++ /dev/null @@ -1,2302 +0,0 @@ -Description: manual merge caused a mismatch - this patch fixes the mismatch -Author: Pirate Praveen - ---- -Last-Update: 2021-01-30 - ---- /dev/null -+++ gitlab-13.6.5/app/assets/javascripts/analytics/instance_statistics/components/pipelines_chart.vue -@@ -0,0 +1,215 @@ -+ -+ ---- /dev/null -+++ gitlab-13.6.5/app/assets/javascripts/analytics/instance_statistics/graphql/queries/pipeline_stats.query.graphql -@@ -0,0 +1,76 @@ -+#import "~/graphql_shared/fragments/pageInfo.fragment.graphql" -+#import "./count.fragment.graphql" -+ -+query pipelineStats( -+ $firstTotal: Int -+ $firstSucceeded: Int -+ $firstFailed: Int -+ $firstCanceled: Int -+ $firstSkipped: Int -+ $endCursorTotal: String -+ $endCursorSucceeded: String -+ $endCursorFailed: String -+ $endCursorCanceled: String -+ $endCursorSkipped: String -+) { -+ pipelinesTotal: instanceStatisticsMeasurements( -+ identifier: PIPELINES -+ first: $firstTotal -+ after: $endCursorTotal -+ ) { -+ nodes { -+ ...Count -+ } -+ pageInfo { -+ ...PageInfo -+ } -+ } -+ pipelinesSucceeded: instanceStatisticsMeasurements( -+ identifier: PIPELINES_SUCCEEDED -+ first: $firstSucceeded -+ after: $endCursorSucceeded -+ ) { -+ nodes { -+ ...Count -+ } -+ pageInfo { -+ ...PageInfo -+ } -+ } -+ pipelinesFailed: instanceStatisticsMeasurements( -+ identifier: PIPELINES_FAILED -+ first: $firstFailed -+ after: $endCursorFailed -+ ) { -+ nodes { -+ ...Count -+ } -+ pageInfo { -+ ...PageInfo -+ } -+ } -+ pipelinesCanceled: instanceStatisticsMeasurements( -+ identifier: PIPELINES_CANCELED -+ first: $firstCanceled -+ after: $endCursorCanceled -+ ) { -+ nodes { -+ ...Count -+ } -+ pageInfo { -+ ...PageInfo -+ } -+ } -+ pipelinesSkipped: instanceStatisticsMeasurements( -+ identifier: PIPELINES_SKIPPED -+ first: $firstSkipped -+ after: $endCursorSkipped -+ ) { -+ nodes { -+ ...Count -+ } -+ pageInfo { -+ ...PageInfo -+ } -+ } -+} ---- /dev/null -+++ gitlab-13.6.5/app/assets/javascripts/performance_utils.js -@@ -0,0 +1,10 @@ -+export const performanceMarkAndMeasure = ({ mark, measures = [] } = {}) => { -+ window.requestAnimationFrame(() => { -+ if (mark && !performance.getEntriesByName(mark).length) { -+ performance.mark(mark); -+ } -+ measures.forEach(measure => { -+ performance.measure(measure.name, measure.start, measure.end); -+ }); -+ }); -+}; ---- /dev/null -+++ gitlab-13.6.5/app/assets/javascripts/search/dropdown_filter/components/dropdown_filter.vue -@@ -0,0 +1,100 @@ -+ -+ -+ ---- /dev/null -+++ gitlab-13.6.5/app/assets/javascripts/search/dropdown_filter/constants/confidential_filter_data.js -@@ -0,0 +1,36 @@ -+import { __ } from '~/locale'; -+ -+const header = __('Confidentiality'); -+ -+const filters = { -+ ANY: { -+ label: __('Any'), -+ value: null, -+ }, -+ CONFIDENTIAL: { -+ label: __('Confidential'), -+ value: 'yes', -+ }, -+ NOT_CONFIDENTIAL: { -+ label: __('Not confidential'), -+ value: 'no', -+ }, -+}; -+ -+const scopes = { -+ ISSUES: 'issues', -+}; -+ -+const filterByScope = { -+ [scopes.ISSUES]: [filters.ANY, filters.CONFIDENTIAL, filters.NOT_CONFIDENTIAL], -+}; -+ -+const filterParam = 'confidential'; -+ -+export default { -+ header, -+ filters, -+ scopes, -+ filterByScope, -+ filterParam, -+}; ---- /dev/null -+++ gitlab-13.6.5/app/assets/javascripts/search/dropdown_filter/constants/state_filter_data.js -@@ -0,0 +1,42 @@ -+import { __ } from '~/locale'; -+ -+const header = __('Status'); -+ -+const filters = { -+ ANY: { -+ label: __('Any'), -+ value: 'all', -+ }, -+ OPEN: { -+ label: __('Open'), -+ value: 'opened', -+ }, -+ CLOSED: { -+ label: __('Closed'), -+ value: 'closed', -+ }, -+ MERGED: { -+ label: __('Merged'), -+ value: 'merged', -+ }, -+}; -+ -+const scopes = { -+ ISSUES: 'issues', -+ MERGE_REQUESTS: 'merge_requests', -+}; -+ -+const filterByScope = { -+ [scopes.ISSUES]: [filters.ANY, filters.OPEN, filters.CLOSED], -+ [scopes.MERGE_REQUESTS]: [filters.ANY, filters.OPEN, filters.MERGED, filters.CLOSED], -+}; -+ -+const filterParam = 'state'; -+ -+export default { -+ header, -+ filters, -+ scopes, -+ filterByScope, -+ filterParam, -+}; ---- /dev/null -+++ gitlab-13.6.5/app/assets/javascripts/search/dropdown_filter/index.js -@@ -0,0 +1,38 @@ -+import Vue from 'vue'; -+import Translate from '~/vue_shared/translate'; -+import DropdownFilter from './components/dropdown_filter.vue'; -+import stateFilterData from './constants/state_filter_data'; -+import confidentialFilterData from './constants/confidential_filter_data'; -+ -+Vue.use(Translate); -+ -+const mountDropdownFilter = (store, { id, filterData }) => { -+ const el = document.getElementById(id); -+ -+ if (!el) return false; -+ -+ return new Vue({ -+ el, -+ store, -+ render(createElement) { -+ return createElement(DropdownFilter, { -+ props: { -+ filterData, -+ }, -+ }); -+ }, -+ }); -+}; -+ -+const dropdownFilters = [ -+ { -+ id: 'js-search-filter-by-state', -+ filterData: stateFilterData, -+ }, -+ { -+ id: 'js-search-filter-by-confidential', -+ filterData: confidentialFilterData, -+ }, -+]; -+ -+export default store => [...dropdownFilters].map(filter => mountDropdownFilter(store, filter)); ---- /dev/null -+++ gitlab-13.6.5/app/assets/stylesheets/page_bundles/experimental_separate_sign_up.scss -@@ -0,0 +1,96 @@ -+@import 'mixins_and_variables_and_functions'; -+ -+.signup-page { -+ .page-wrap { -+ background-color: var(--gray-10, $gray-10); -+ } -+ -+ .signup-box-container { -+ max-width: 960px; -+ } -+ -+ .signup-box { -+ background-color: var(--white, $white); -+ box-shadow: 0 0 0 1px var(--border-color, $border-color); -+ border-radius: $border-radius; -+ } -+ -+ .form-control { -+ &:active, -+ &:focus { -+ background-color: var(--white, $white); -+ } -+ } -+ -+ .devise-errors { -+ h2 { -+ font-size: $gl-font-size; -+ color: var(--red-700, $red-700); -+ } -+ } -+ -+ .omniauth-divider { -+ &::before, -+ &::after { -+ content: ''; -+ flex: 1; -+ border-bottom: 1px solid var(--gray-100, $gray-100); -+ margin: $gl-padding-24 0; -+ } -+ -+ &::before { -+ margin-right: $gl-padding; -+ } -+ -+ &::after { -+ margin-left: $gl-padding; -+ } -+ } -+ -+ .omniauth-btn { -+ width: 48%; -+ -+ @include media-breakpoint-down(md) { -+ width: 100%; -+ } -+ -+ img { -+ width: $default-icon-size; -+ height: $default-icon-size; -+ } -+ } -+ -+ .decline-page { -+ width: 350px; -+ } -+} -+ -+.signup-page[data-page^='registrations:experience_levels'] { -+ $card-shadow-color: rgba(var(--black, $black), 0.2); -+ -+ .page-wrap { -+ background-color: var(--white, $white); -+ } -+ -+ .card-deck { -+ max-width: 828px; -+ } -+ -+ .card { -+ transition: box-shadow 0.3s ease-in-out; -+ } -+ -+ .card:hover { -+ box-shadow: 0 $gl-spacing-scale-3 $gl-spacing-scale-5 $card-shadow-color; -+ } -+ -+ @media (min-width: $breakpoint-sm) { -+ .card-deck .card { -+ margin: 0 $gl-spacing-scale-3; -+ } -+ } -+ -+ .stretched-link:hover { -+ text-decoration: none; -+ } -+} ---- /dev/null -+++ gitlab-13.6.5/app/models/ci/build_trace_chunks/legacy_fog.rb -@@ -0,0 +1,77 @@ -+# frozen_string_literal: true -+ -+module Ci -+ module BuildTraceChunks -+ class LegacyFog -+ def available? -+ object_store.enabled -+ end -+ -+ def data(model) -+ connection.get_object(bucket_name, key(model))[:body] -+ rescue Excon::Error::NotFound -+ # If the object does not exist in the object storage, this method returns nil. -+ end -+ -+ def set_data(model, new_data) -+ connection.put_object(bucket_name, key(model), new_data) -+ end -+ -+ def append_data(model, new_data, offset) -+ if offset > 0 -+ truncated_data = data(model).to_s.byteslice(0, offset) -+ new_data = truncated_data + new_data -+ end -+ -+ set_data(model, new_data) -+ new_data.bytesize -+ end -+ -+ def size(model) -+ data(model).to_s.bytesize -+ end -+ -+ def delete_data(model) -+ delete_keys([[model.build_id, model.chunk_index]]) -+ end -+ -+ def keys(relation) -+ return [] unless available? -+ -+ relation.pluck(:build_id, :chunk_index) -+ end -+ -+ def delete_keys(keys) -+ keys.each do |key| -+ connection.delete_object(bucket_name, key_raw(*key)) -+ end -+ end -+ -+ private -+ -+ def key(model) -+ key_raw(model.build_id, model.chunk_index) -+ end -+ -+ def key_raw(build_id, chunk_index) -+ "tmp/builds/#{build_id.to_i}/chunks/#{chunk_index.to_i}.log" -+ end -+ -+ def bucket_name -+ return unless available? -+ -+ object_store.remote_directory -+ end -+ -+ def connection -+ return unless available? -+ -+ @connection ||= ::Fog::Storage.new(object_store.connection.to_hash.deep_symbolize_keys) -+ end -+ -+ def object_store -+ Gitlab.config.artifacts.object_store -+ end -+ end -+ end -+end ---- /dev/null -+++ gitlab-13.6.5/app/views/search/results/_filters.html.haml -@@ -0,0 +1,7 @@ -+.d-lg-flex.align-items-end -+ #js-search-filter-by-state{ 'v-cloak': true } -+ - if Feature.enabled?(:search_filter_by_confidential, @group) -+ #js-search-filter-by-confidential{ 'v-cloak': true } -+ -+ - if %w(issues merge_requests).include?(@scope) -+ %hr.gl-mt-4.gl-mb-4 ---- /dev/null -+++ gitlab-13.6.5/config/feature_flags/development/admin_approval_for_new_user_signups.yml -@@ -0,0 +1,7 @@ -+--- -+name: admin_approval_for_new_user_signups -+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/43827 -+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/258980 -+type: development -+group: group::access -+default_enabled: true ---- /dev/null -+++ gitlab-13.6.5/config/feature_flags/development/ci_always_refresh_merge_requests_from_beginning.yml -@@ -0,0 +1,7 @@ -+--- -+name: ci_always_refresh_merge_requests_from_beginning -+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45232 -+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/268215 -+type: development -+group: group::continuous integration -+default_enabled: false ---- /dev/null -+++ gitlab-13.6.5/config/feature_flags/development/ci_delete_objects_low_concurrency.yml -@@ -0,0 +1,7 @@ -+--- -+name: ci_delete_objects_low_concurrency -+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39464 -+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/247103 -+group: group::continuous integration -+type: development -+default_enabled: false -\ No newline at end of file ---- /dev/null -+++ gitlab-13.6.5/config/feature_flags/development/ci_send_deployment_hook_when_start.yml -@@ -0,0 +1,7 @@ -+--- -+name: ci_send_deployment_hook_when_start -+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/41214 -+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/247137 -+group: group::progressive delivery -+type: development -+default_enabled: false ---- /dev/null -+++ gitlab-13.6.5/config/feature_flags/development/ci_trace_new_fog_store.yml -@@ -0,0 +1,7 @@ -+--- -+name: ci_trace_new_fog_store -+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/46209 -+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/273405 -+type: development -+group: group::testing -+default_enabled: true ---- /dev/null -+++ gitlab-13.6.5/config/feature_flags/development/deploy_boards_dedupe_instances.yml -@@ -0,0 +1,7 @@ -+--- -+name: deploy_boards_dedupe_instances -+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40768 -+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/258214 -+type: development -+group: group::progressive delivery -+default_enabled: false ---- /dev/null -+++ gitlab-13.6.5/config/feature_flags/development/deployment_filters.yml -@@ -0,0 +1,7 @@ -+--- -+name: deployment_filters -+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/44041 -+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/267561 -+type: development -+group: group::source code -+default_enabled: false ---- /dev/null -+++ gitlab-13.6.5/config/feature_flags/development/disable_shared_runners_on_group.yml -@@ -0,0 +1,7 @@ -+--- -+name: disable_shared_runners_on_group -+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/36080 -+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/258991 -+type: development -+group: group::runner -+default_enabled: true ---- /dev/null -+++ gitlab-13.6.5/config/feature_flags/development/expose_environment_path_in_alert_details.yml -@@ -0,0 +1,7 @@ -+--- -+name: expose_environment_path_in_alert_details -+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/43414 -+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/258638 -+type: development -+group: group::progressive delivery -+default_enabled: false ---- /dev/null -+++ gitlab-13.6.5/config/feature_flags/development/kubernetes_cluster_namespace_role_admin.yml -@@ -0,0 +1,7 @@ -+--- -+name: kubernetes_cluster_namespace_role_admin -+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/45479 -+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/270030 -+type: development -+group: group::configure -+default_enabled: false ---- /dev/null -+++ gitlab-13.6.5/config/feature_flags/development/merge_base_pipelines.yml -@@ -0,0 +1,7 @@ -+--- -+name: merge_base_pipelines -+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/44648 -+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/263724 -+type: development -+group: group::testing -+default_enabled: false ---- /dev/null -+++ gitlab-13.6.5/config/feature_flags/development/one_dimensional_matrix.yml -@@ -0,0 +1,7 @@ -+--- -+name: one_dimensional_matrix -+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/42170 -+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/256062 -+type: development -+group: group::pipeline authoring -+default_enabled: true ---- /dev/null -+++ gitlab-13.6.5/config/feature_flags/development/search_filter_by_confidential.yml -@@ -0,0 +1,7 @@ -+--- -+name: search_filter_by_confidential -+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40793 -+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/244923 -+group: group::global search -+type: development -+default_enabled: false -\ No newline at end of file ---- /dev/null -+++ gitlab-13.6.5/config/feature_flags/development/soft_fail_count_by_state.yml -@@ -0,0 +1,7 @@ -+--- -+name: soft_fail_count_by_state -+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/44184 -+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/263222 -+type: development -+group: group::source code -+default_enabled: false ---- /dev/null -+++ gitlab-13.6.5/config/feature_flags/development/sync_metrics_dashboards.yml -@@ -0,0 +1,7 @@ -+--- -+name: sync_metrics_dashboards -+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/39658 -+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/241793 -+group: group::apm -+type: development -+default_enabled: false ---- /dev/null -+++ gitlab-13.6.5/config/feature_flags/development/track_unique_test_cases_parsed.yml -@@ -0,0 +1,7 @@ -+--- -+name: track_unique_test_cases_parsed -+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/41918 -+rollout_issue_url: -+group: group::testing -+type: development -+default_enabled: false ---- /dev/null -+++ gitlab-13.6.5/config/feature_flags/licensed/incident_sla.yml -@@ -0,0 +1,7 @@ -+--- -+name: incident_sla -+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/43648 -+rollout_issue_url: -+group: group::health -+type: licensed -+default_enabled: true ---- /dev/null -+++ gitlab-13.6.5/config/feature_flags/licensed/minimal_access_role.yml -@@ -0,0 +1,7 @@ -+--- -+name: minimal_access_role -+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/40942 -+rollout_issue_url: -+group: group::access -+type: licensed -+default_enabled: true ---- /dev/null -+++ gitlab-13.6.5/config/feature_flags/licensed/resource_access_token.yml -@@ -0,0 +1,7 @@ -+--- -+name: resource_access_token -+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/29622 -+rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/235765 -+group: group::access -+type: licensed -+default_enabled: true ---- /dev/null -+++ gitlab-13.6.5/doc/user/admin_area/analytics/instance_statistics.md -@@ -0,0 +1,18 @@ -+# Instance Statistics -+ -+> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/235754) in GitLab 13.4. -+ -+Instance Statistics gives you an overview of how much data your instance contains, and how quickly this volume is changing over time. -+ -+## Total counts -+ -+At the top of the page, Instance Statistics shows total counts for: -+ -+- Users -+- Projects -+- Groups -+- Issues -+- Merge Requests -+- Pipelines -+ -+These figures can be useful for understanding how much data your instance contains in total. ---- /dev/null -+++ gitlab-13.6.5/lib/gitlab/bulk_import/client.rb -@@ -0,0 +1,72 @@ -+# frozen_string_literal: true -+ -+module Gitlab -+ module BulkImport -+ class Client -+ API_VERSION = 'v4'.freeze -+ DEFAULT_PAGE = 1.freeze -+ DEFAULT_PER_PAGE = 30.freeze -+ -+ ConnectionError = Class.new(StandardError) -+ -+ def initialize(uri:, token:, page: DEFAULT_PAGE, per_page: DEFAULT_PER_PAGE, api_version: API_VERSION) -+ @uri = URI.parse(uri) -+ @token = token&.strip -+ @page = page -+ @per_page = per_page -+ @api_version = api_version -+ end -+ -+ def get(resource, query = {}) -+ response = with_error_handling do -+ Gitlab::HTTP.get( -+ resource_url(resource), -+ headers: request_headers, -+ follow_redirects: false, -+ query: query.merge(request_query) -+ ) -+ end -+ -+ response.parsed_response -+ end -+ -+ private -+ -+ def request_query -+ { -+ page: @page, -+ per_page: @per_page -+ } -+ end -+ -+ def request_headers -+ { -+ 'Content-Type' => 'application/json', -+ 'Authorization' => "Bearer #{@token}" -+ } -+ end -+ -+ def with_error_handling -+ response = yield -+ -+ raise ConnectionError.new("Error #{response.code}") unless response.success? -+ -+ response -+ rescue *Gitlab::HTTP::HTTP_ERRORS => e -+ raise ConnectionError, e -+ end -+ -+ def base_uri -+ @base_uri ||= "#{@uri.scheme}://#{@uri.host}:#{@uri.port}" -+ end -+ -+ def api_url -+ Gitlab::Utils.append_path(base_uri, "/api/#{@api_version}") -+ end -+ -+ def resource_url(resource) -+ Gitlab::Utils.append_path(api_url, resource) -+ end -+ end -+ end -+end ---- /dev/null -+++ gitlab-13.6.5/lib/gitlab/import_export/project/sample/sample_data_relation_tree_restorer.rb -@@ -0,0 +1,51 @@ -+# frozen_string_literal: true -+ -+module Gitlab -+ module ImportExport -+ module Project -+ module Sample -+ class SampleDataRelationTreeRestorer < RelationTreeRestorer -+ DATE_MODELS = %i[issues milestones].freeze -+ -+ def initialize(*args) -+ super -+ -+ date_calculator -+ end -+ -+ private -+ -+ def build_relation(relation_key, relation_definition, data_hash) -+ # Override due date attributes in data hash for Sample Data templates -+ # Dates are moved by taking the closest one to average and moving that (and rest around it) to the date of import -+ # TODO: To move this logic to RelationFactory (see: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/41699#note_430465333) -+ override_date_attributes!(relation_key, data_hash) -+ super -+ end -+ -+ def override_date_attributes!(relation_key, data_hash) -+ return unless DATE_MODELS.include?(relation_key.to_sym) -+ -+ data_hash['start_date'] = date_calculator.calculate_by_closest_date_to_average(data_hash['start_date'].to_time) unless data_hash['start_date'].nil? -+ data_hash['due_date'] = date_calculator.calculate_by_closest_date_to_average(data_hash['due_date'].to_time) unless data_hash['due_date'].nil? -+ end -+ -+ # TODO: Move clear logic into main comsume_relation method (see https://gitlab.com/gitlab-org/gitlab/-/merge_requests/41699#note_430465330) -+ def dates -+ unless relation_reader.legacy? -+ DATE_MODELS.map do |tag| -+ relation_reader.consume_relation(@importable_path, tag).map { |model| model.first['due_date'] }.tap do -+ relation_reader.clear_consumed_relations -+ end -+ end -+ end -+ end -+ -+ def date_calculator -+ @date_calculator ||= Gitlab::ImportExport::Project::Sample::DateCalculator.new(dates) -+ end -+ end -+ end -+ end -+ end -+end ---- /dev/null -+++ gitlab-13.6.5/lib/gitlab/middleware/handle_null_bytes.rb -@@ -0,0 +1,61 @@ -+# frozen_string_literal: true -+ -+module Gitlab -+ module Middleware -+ # There is no valid reason for a request to contain a null byte (U+0000) -+ # so just return HTTP 400 (Bad Request) if we receive one -+ class HandleNullBytes -+ NULL_BYTE_REGEX = Regexp.new(Regexp.escape("\u0000")).freeze -+ -+ attr_reader :app -+ -+ def initialize(app) -+ @app = app -+ end -+ -+ def call(env) -+ return [400, {}, ["Bad Request"]] if request_has_null_byte?(env) -+ -+ app.call(env) -+ end -+ -+ private -+ -+ def request_has_null_byte?(request) -+ return false if ENV['REJECT_NULL_BYTES'] == "1" -+ -+ request = Rack::Request.new(request) -+ -+ request.params.values.any? do |value| -+ param_has_null_byte?(value) -+ end -+ end -+ -+ def param_has_null_byte?(value, depth = 0) -+ # Guard against possible attack sending large amounts of nested params -+ # Should be safe as deeply nested params are highly uncommon. -+ return false if depth > 2 -+ -+ depth += 1 -+ -+ if value.respond_to?(:match) -+ string_contains_null_byte?(value) -+ elsif value.respond_to?(:values) -+ value.values.any? do |hash_value| -+ param_has_null_byte?(hash_value, depth) -+ end -+ elsif value.is_a?(Array) -+ value.any? do |array_value| -+ param_has_null_byte?(array_value, depth) -+ end -+ else -+ false -+ end -+ end -+ -+ def string_contains_null_byte?(string) -+ string.match?(NULL_BYTE_REGEX) -+ end -+ end -+ end -+end ---- /dev/null -+++ gitlab-13.6.5/spec/fixtures/packages/debian/libsample0_1.2.3~alpha2-1_amd64.deb -@@ -0,0 +1 @@ -+empty ---- /dev/null -+++ gitlab-13.6.5/spec/frontend/alert_settings/alerts_integrations_list_spec.js -@@ -0,0 +1,89 @@ -+import { GlTable, GlIcon } from '@gitlab/ui'; -+import { mount } from '@vue/test-utils'; -+import Tracking from '~/tracking'; -+import AlertIntegrationsList, { -+ i18n, -+} from '~/alerts_settings/components/alerts_integrations_list.vue'; -+import { trackAlertIntergrationsViewsOptions } from '~/alerts_settings/constants'; -+ -+const mockIntegrations = [ -+ { -+ activated: true, -+ name: 'Integration 1', -+ type: 'HTTP endpoint', -+ }, -+ { -+ activated: false, -+ name: 'Integration 2', -+ type: 'HTTP endpoint', -+ }, -+]; -+ -+describe('AlertIntegrationsList', () => { -+ let wrapper; -+ -+ function mountComponent(propsData = {}) { -+ wrapper = mount(AlertIntegrationsList, { -+ propsData: { -+ integrations: mockIntegrations, -+ ...propsData, -+ }, -+ stubs: { -+ GlIcon: true, -+ }, -+ }); -+ } -+ -+ afterEach(() => { -+ if (wrapper) { -+ wrapper.destroy(); -+ wrapper = null; -+ } -+ }); -+ -+ beforeEach(() => { -+ mountComponent(); -+ }); -+ -+ const findTableComponent = () => wrapper.find(GlTable); -+ const finsStatusCell = () => wrapper.findAll('[data-testid="integration-activated-status"]'); -+ -+ it('renders a table', () => { -+ expect(findTableComponent().exists()).toBe(true); -+ }); -+ -+ it('renders an empty state when no integrations provided', () => { -+ mountComponent({ integrations: [] }); -+ expect(findTableComponent().text()).toContain(i18n.emptyState); -+ }); -+ -+ describe('integration status', () => { -+ it('enabled', () => { -+ const cell = finsStatusCell().at(0); -+ const activatedIcon = cell.find(GlIcon); -+ expect(cell.text()).toBe(i18n.status.enabled.name); -+ expect(activatedIcon.attributes('name')).toBe('check-circle-filled'); -+ expect(activatedIcon.attributes('title')).toBe(i18n.status.enabled.tooltip); -+ }); -+ -+ it('disabled', () => { -+ const cell = finsStatusCell().at(1); -+ const notActivatedIcon = cell.find(GlIcon); -+ expect(cell.text()).toBe(i18n.status.disabled.name); -+ expect(notActivatedIcon.attributes('name')).toBe('warning-solid'); -+ expect(notActivatedIcon.attributes('title')).toBe(i18n.status.disabled.tooltip); -+ }); -+ }); -+ -+ describe('Snowplow tracking', () => { -+ beforeEach(() => { -+ jest.spyOn(Tracking, 'event'); -+ mountComponent(); -+ }); -+ -+ it('should track alert list page views', () => { -+ const { category, action } = trackAlertIntergrationsViewsOptions; -+ expect(Tracking.event).toHaveBeenCalledWith(category, action); -+ }); -+ }); -+}); ---- /dev/null -+++ gitlab-13.6.5/spec/frontend/analytics/instance_statistics/components/__snapshots__/pipelines_chart_spec.js.snap -@@ -0,0 +1,161 @@ -+// Jest Snapshot v1, https://goo.gl/fbAQLP -+ -+exports[`PipelinesChart when fetching more data when the fetchMore query returns data passes the data to the line chart 1`] = ` -+Array [ -+ Object { -+ "data": Array [ -+ Array [ -+ "2020-06-01", -+ 21, -+ ], -+ Array [ -+ "2020-07-01", -+ 10, -+ ], -+ Array [ -+ "2020-08-01", -+ 5, -+ ], -+ ], -+ "name": "Total", -+ }, -+ Object { -+ "data": Array [ -+ Array [ -+ "2020-06-01", -+ 21, -+ ], -+ Array [ -+ "2020-07-01", -+ 10, -+ ], -+ Array [ -+ "2020-08-01", -+ 5, -+ ], -+ ], -+ "name": "Succeeded", -+ }, -+ Object { -+ "data": Array [ -+ Array [ -+ "2020-06-01", -+ 22, -+ ], -+ Array [ -+ "2020-07-01", -+ 41, -+ ], -+ Array [ -+ "2020-08-01", -+ 5, -+ ], -+ ], -+ "name": "Failed", -+ }, -+ Object { -+ "data": Array [ -+ Array [ -+ "2020-06-01", -+ 21, -+ ], -+ Array [ -+ "2020-07-01", -+ 10, -+ ], -+ Array [ -+ "2020-08-01", -+ 5, -+ ], -+ ], -+ "name": "Canceled", -+ }, -+ Object { -+ "data": Array [ -+ Array [ -+ "2020-06-01", -+ 21, -+ ], -+ Array [ -+ "2020-07-01", -+ 10, -+ ], -+ Array [ -+ "2020-08-01", -+ 5, -+ ], -+ ], -+ "name": "Skipped", -+ }, -+] -+`; -+ -+exports[`PipelinesChart with data passes the data to the line chart 1`] = ` -+Array [ -+ Object { -+ "data": Array [ -+ Array [ -+ "2020-06-01", -+ 22, -+ ], -+ Array [ -+ "2020-07-01", -+ 41, -+ ], -+ ], -+ "name": "Total", -+ }, -+ Object { -+ "data": Array [ -+ Array [ -+ "2020-06-01", -+ 21, -+ ], -+ Array [ -+ "2020-07-01", -+ 10, -+ ], -+ ], -+ "name": "Succeeded", -+ }, -+ Object { -+ "data": Array [ -+ Array [ -+ "2020-06-01", -+ 21, -+ ], -+ Array [ -+ "2020-07-01", -+ 10, -+ ], -+ ], -+ "name": "Failed", -+ }, -+ Object { -+ "data": Array [ -+ Array [ -+ "2020-06-01", -+ 22, -+ ], -+ Array [ -+ "2020-07-01", -+ 41, -+ ], -+ ], -+ "name": "Canceled", -+ }, -+ Object { -+ "data": Array [ -+ Array [ -+ "2020-06-01", -+ 22, -+ ], -+ Array [ -+ "2020-07-01", -+ 41, -+ ], -+ ], -+ "name": "Skipped", -+ }, -+] -+`; ---- /dev/null -+++ gitlab-13.6.5/spec/frontend/analytics/instance_statistics/components/pipelines_chart_spec.js -@@ -0,0 +1,189 @@ -+import { createLocalVue, shallowMount } from '@vue/test-utils'; -+import { GlLineChart } from '@gitlab/ui/dist/charts'; -+import { GlAlert } from '@gitlab/ui'; -+import VueApollo from 'vue-apollo'; -+import createMockApollo from 'jest/helpers/mock_apollo_helper'; -+import PipelinesChart from '~/analytics/instance_statistics/components/pipelines_chart.vue'; -+import pipelinesStatsQuery from '~/analytics/instance_statistics/graphql/queries/pipeline_stats.query.graphql'; -+import ChartSkeletonLoader from '~/vue_shared/components/resizable_chart/skeleton_loader.vue'; -+import { mockCountsData1, mockCountsData2 } from '../mock_data'; -+import { getApolloResponse } from '../apollo_mock_data'; -+ -+const localVue = createLocalVue(); -+localVue.use(VueApollo); -+ -+describe('PipelinesChart', () => { -+ let wrapper; -+ let queryHandler; -+ -+ const createApolloProvider = pipelineStatsHandler => { -+ return createMockApollo([[pipelinesStatsQuery, pipelineStatsHandler]]); -+ }; -+ -+ const createComponent = apolloProvider => { -+ return shallowMount(PipelinesChart, { -+ localVue, -+ apolloProvider, -+ }); -+ }; -+ -+ afterEach(() => { -+ wrapper.destroy(); -+ wrapper = null; -+ }); -+ -+ const findLoader = () => wrapper.find(ChartSkeletonLoader); -+ const findChart = () => wrapper.find(GlLineChart); -+ const findAlert = () => wrapper.find(GlAlert); -+ -+ describe('while loading', () => { -+ beforeEach(() => { -+ queryHandler = jest.fn().mockReturnValue(new Promise(() => {})); -+ const apolloProvider = createApolloProvider(queryHandler); -+ wrapper = createComponent(apolloProvider); -+ }); -+ -+ it('requests data', () => { -+ expect(queryHandler).toBeCalledTimes(1); -+ }); -+ -+ it('displays the skeleton loader', () => { -+ expect(findLoader().exists()).toBe(true); -+ }); -+ -+ it('hides the chart', () => { -+ expect(findChart().exists()).toBe(false); -+ }); -+ -+ it('does not show an error', () => { -+ expect(findAlert().exists()).toBe(false); -+ }); -+ }); -+ -+ describe('without data', () => { -+ beforeEach(() => { -+ const emptyResponse = getApolloResponse(); -+ queryHandler = jest.fn().mockResolvedValue(emptyResponse); -+ const apolloProvider = createApolloProvider(queryHandler); -+ wrapper = createComponent(apolloProvider); -+ }); -+ -+ it('renders an no data message', () => { -+ expect(findAlert().text()).toBe('There is no data available.'); -+ }); -+ -+ it('hides the skeleton loader', () => { -+ expect(findLoader().exists()).toBe(false); -+ }); -+ -+ it('renders the chart', () => { -+ expect(findChart().exists()).toBe(false); -+ }); -+ }); -+ -+ describe('with data', () => { -+ beforeEach(() => { -+ const response = getApolloResponse({ -+ pipelinesTotal: mockCountsData1, -+ pipelinesSucceeded: mockCountsData2, -+ pipelinesFailed: mockCountsData2, -+ pipelinesCanceled: mockCountsData1, -+ pipelinesSkipped: mockCountsData1, -+ }); -+ queryHandler = jest.fn().mockResolvedValue(response); -+ const apolloProvider = createApolloProvider(queryHandler); -+ wrapper = createComponent(apolloProvider); -+ }); -+ -+ it('requests data', () => { -+ expect(queryHandler).toBeCalledTimes(1); -+ }); -+ -+ it('hides the skeleton loader', () => { -+ expect(findLoader().exists()).toBe(false); -+ }); -+ -+ it('renders the chart', () => { -+ expect(findChart().exists()).toBe(true); -+ }); -+ -+ it('passes the data to the line chart', () => { -+ expect(findChart().props('data')).toMatchSnapshot(); -+ }); -+ -+ it('does not show an error', () => { -+ expect(findAlert().exists()).toBe(false); -+ }); -+ }); -+ -+ describe('when fetching more data', () => { -+ const recordedAt = '2020-08-01'; -+ describe('when the fetchMore query returns data', () => { -+ beforeEach(async () => { -+ const newData = { recordedAt, count: 5 }; -+ const firstResponse = getApolloResponse({ -+ pipelinesTotal: mockCountsData2, -+ pipelinesSucceeded: mockCountsData2, -+ pipelinesFailed: mockCountsData1, -+ pipelinesCanceled: mockCountsData2, -+ pipelinesSkipped: mockCountsData2, -+ hasNextPage: true, -+ }); -+ const secondResponse = getApolloResponse({ -+ pipelinesTotal: [newData], -+ pipelinesSucceeded: [newData], -+ pipelinesFailed: [newData], -+ pipelinesCanceled: [newData], -+ pipelinesSkipped: [newData], -+ hasNextPage: false, -+ }); -+ queryHandler = jest -+ .fn() -+ .mockResolvedValueOnce(firstResponse) -+ .mockResolvedValueOnce(secondResponse); -+ const apolloProvider = createApolloProvider(queryHandler); -+ wrapper = createComponent(apolloProvider); -+ -+ await wrapper.vm.$nextTick(); -+ }); -+ -+ it('requests data twice', () => { -+ expect(queryHandler).toBeCalledTimes(2); -+ }); -+ -+ it('passes the data to the line chart', () => { -+ expect(findChart().props('data')).toMatchSnapshot(); -+ }); -+ }); -+ -+ describe('when the fetchMore query throws an error', () => { -+ beforeEach(async () => { -+ const response = getApolloResponse({ -+ pipelinesTotal: mockCountsData2, -+ pipelinesSucceeded: mockCountsData2, -+ pipelinesFailed: mockCountsData1, -+ pipelinesCanceled: mockCountsData2, -+ pipelinesSkipped: mockCountsData2, -+ hasNextPage: true, -+ }); -+ queryHandler = jest.fn().mockResolvedValue(response); -+ const apolloProvider = createApolloProvider(queryHandler); -+ wrapper = createComponent(apolloProvider); -+ jest -+ .spyOn(wrapper.vm.$apollo.queries.pipelineStats, 'fetchMore') -+ .mockImplementation(jest.fn().mockRejectedValue()); -+ await wrapper.vm.$nextTick(); -+ }); -+ -+ it('calls fetchMore', () => { -+ expect(wrapper.vm.$apollo.queries.pipelineStats.fetchMore).toHaveBeenCalledTimes(1); -+ }); -+ -+ it('show an error message', () => { -+ expect(findAlert().text()).toBe( -+ 'Could not load the pipelines chart. Please refresh the page to try again.', -+ ); -+ }); -+ }); -+ }); -+}); ---- /dev/null -+++ gitlab-13.6.5/spec/frontend/search/dropdown_filter/components/dropdown_filter_spec.js -@@ -0,0 +1,196 @@ -+import Vuex from 'vuex'; -+import { createLocalVue, shallowMount } from '@vue/test-utils'; -+import { GlDropdown, GlDropdownItem } from '@gitlab/ui'; -+import * as urlUtils from '~/lib/utils/url_utility'; -+import initStore from '~/search/store'; -+import DropdownFilter from '~/search/dropdown_filter/components/dropdown_filter.vue'; -+import stateFilterData from '~/search/dropdown_filter/constants/state_filter_data'; -+import confidentialFilterData from '~/search/dropdown_filter/constants/confidential_filter_data'; -+import { MOCK_QUERY } from '../mock_data'; -+ -+jest.mock('~/lib/utils/url_utility', () => ({ -+ visitUrl: jest.fn(), -+ setUrlParams: jest.fn(), -+})); -+ -+const localVue = createLocalVue(); -+localVue.use(Vuex); -+ -+describe('DropdownFilter', () => { -+ let wrapper; -+ let store; -+ -+ const createStore = options => { -+ store = initStore({ query: MOCK_QUERY, ...options }); -+ }; -+ -+ const createComponent = (props = { filterData: stateFilterData }) => { -+ wrapper = shallowMount(DropdownFilter, { -+ localVue, -+ store, -+ propsData: { -+ ...props, -+ }, -+ }); -+ }; -+ -+ afterEach(() => { -+ wrapper.destroy(); -+ wrapper = null; -+ store = null; -+ }); -+ -+ const findGlDropdown = () => wrapper.find(GlDropdown); -+ const findGlDropdownItems = () => findGlDropdown().findAll(GlDropdownItem); -+ const findDropdownItemsText = () => findGlDropdownItems().wrappers.map(w => w.text()); -+ const firstDropDownItem = () => findGlDropdownItems().at(0); -+ -+ describe('StatusFilter', () => { -+ describe('template', () => { -+ describe.each` -+ scope | showDropdown -+ ${'issues'} | ${true} -+ ${'merge_requests'} | ${true} -+ ${'projects'} | ${false} -+ ${'milestones'} | ${false} -+ ${'users'} | ${false} -+ ${'notes'} | ${false} -+ ${'wiki_blobs'} | ${false} -+ ${'blobs'} | ${false} -+ `(`dropdown`, ({ scope, showDropdown }) => { -+ beforeEach(() => { -+ createStore({ query: { ...MOCK_QUERY, scope } }); -+ createComponent(); -+ }); -+ -+ it(`does${showDropdown ? '' : ' not'} render when scope is ${scope}`, () => { -+ expect(findGlDropdown().exists()).toBe(showDropdown); -+ }); -+ }); -+ -+ describe.each` -+ initialFilter | label -+ ${stateFilterData.filters.ANY.value} | ${`Any ${stateFilterData.header}`} -+ ${stateFilterData.filters.OPEN.value} | ${stateFilterData.filters.OPEN.label} -+ ${stateFilterData.filters.CLOSED.value} | ${stateFilterData.filters.CLOSED.label} -+ `(`filter text`, ({ initialFilter, label }) => { -+ describe(`when initialFilter is ${initialFilter}`, () => { -+ beforeEach(() => { -+ createStore({ query: { ...MOCK_QUERY, [stateFilterData.filterParam]: initialFilter } }); -+ createComponent(); -+ }); -+ -+ it(`sets dropdown label to ${label}`, () => { -+ expect(findGlDropdown().attributes('text')).toBe(label); -+ }); -+ }); -+ }); -+ }); -+ -+ describe('Filter options', () => { -+ beforeEach(() => { -+ createStore(); -+ createComponent(); -+ }); -+ -+ it('renders a dropdown item for each filterOption', () => { -+ expect(findDropdownItemsText()).toStrictEqual( -+ stateFilterData.filterByScope[stateFilterData.scopes.ISSUES].map(v => { -+ return v.label; -+ }), -+ ); -+ }); -+ -+ it('clicking a dropdown item calls setUrlParams', () => { -+ const filter = stateFilterData.filters[Object.keys(stateFilterData.filters)[0]].value; -+ firstDropDownItem().vm.$emit('click'); -+ -+ expect(urlUtils.setUrlParams).toHaveBeenCalledWith({ -+ [stateFilterData.filterParam]: filter, -+ }); -+ }); -+ -+ it('clicking a dropdown item calls visitUrl', () => { -+ firstDropDownItem().vm.$emit('click'); -+ -+ expect(urlUtils.visitUrl).toHaveBeenCalled(); -+ }); -+ }); -+ }); -+ -+ describe('ConfidentialFilter', () => { -+ describe('template', () => { -+ describe.each` -+ scope | showDropdown -+ ${'issues'} | ${true} -+ ${'merge_requests'} | ${false} -+ ${'projects'} | ${false} -+ ${'milestones'} | ${false} -+ ${'users'} | ${false} -+ ${'notes'} | ${false} -+ ${'wiki_blobs'} | ${false} -+ ${'blobs'} | ${false} -+ `(`dropdown`, ({ scope, showDropdown }) => { -+ beforeEach(() => { -+ createStore({ query: { ...MOCK_QUERY, scope } }); -+ createComponent({ filterData: confidentialFilterData }); -+ }); -+ -+ it(`does${showDropdown ? '' : ' not'} render when scope is ${scope}`, () => { -+ expect(findGlDropdown().exists()).toBe(showDropdown); -+ }); -+ }); -+ -+ describe.each` -+ initialFilter | label -+ ${confidentialFilterData.filters.ANY.value} | ${`Any ${confidentialFilterData.header}`} -+ ${confidentialFilterData.filters.CONFIDENTIAL.value} | ${confidentialFilterData.filters.CONFIDENTIAL.label} -+ ${confidentialFilterData.filters.NOT_CONFIDENTIAL.value} | ${confidentialFilterData.filters.NOT_CONFIDENTIAL.label} -+ `(`filter text`, ({ initialFilter, label }) => { -+ describe(`when initialFilter is ${initialFilter}`, () => { -+ beforeEach(() => { -+ createStore({ -+ query: { ...MOCK_QUERY, [confidentialFilterData.filterParam]: initialFilter }, -+ }); -+ createComponent({ filterData: confidentialFilterData }); -+ }); -+ -+ it(`sets dropdown label to ${label}`, () => { -+ expect(findGlDropdown().attributes('text')).toBe(label); -+ }); -+ }); -+ }); -+ }); -+ }); -+ -+ describe('Filter options', () => { -+ beforeEach(() => { -+ createStore(); -+ createComponent({ filterData: confidentialFilterData }); -+ }); -+ -+ it('renders a dropdown item for each filterOption', () => { -+ expect(findDropdownItemsText()).toStrictEqual( -+ confidentialFilterData.filterByScope[confidentialFilterData.scopes.ISSUES].map(v => { -+ return v.label; -+ }), -+ ); -+ }); -+ -+ it('clicking a dropdown item calls setUrlParams', () => { -+ const filter = -+ confidentialFilterData.filters[Object.keys(confidentialFilterData.filters)[0]].value; -+ firstDropDownItem().vm.$emit('click'); -+ -+ expect(urlUtils.setUrlParams).toHaveBeenCalledWith({ -+ [confidentialFilterData.filterParam]: filter, -+ }); -+ }); -+ -+ it('clicking a dropdown item calls visitUrl', () => { -+ firstDropDownItem().vm.$emit('click'); -+ -+ expect(urlUtils.visitUrl).toHaveBeenCalled(); -+ }); -+ }); -+}); ---- /dev/null -+++ gitlab-13.6.5/spec/frontend/search/dropdown_filter/mock_data.js -@@ -0,0 +1,5 @@ -+export const MOCK_QUERY = { -+ scope: 'issues', -+ state: 'all', -+ confidential: null, -+}; ---- /dev/null -+++ gitlab-13.6.5/spec/lib/gitlab/bulk_import/client_spec.rb -@@ -0,0 +1,95 @@ -+# frozen_string_literal: true -+ -+require 'spec_helper' -+ -+RSpec.describe Gitlab::BulkImport::Client do -+ include ImportSpecHelper -+ -+ let(:uri) { 'http://gitlab.example' } -+ let(:token) { 'token' } -+ let(:resource) { 'resource' } -+ -+ subject { described_class.new(uri: uri, token: token) } -+ -+ describe '#get' do -+ let(:response_double) { double(code: 200, success?: true, parsed_response: {}) } -+ -+ shared_examples 'performs network request' do -+ it 'performs network request' do -+ expect(Gitlab::HTTP).to receive(:get).with(*expected_args).and_return(response_double) -+ -+ subject.get(resource) -+ end -+ end -+ -+ describe 'parsed response' do -+ it 'returns parsed response' do -+ response_double = double(code: 200, success?: true, parsed_response: [{ id: 1 }, { id: 2 }]) -+ -+ allow(Gitlab::HTTP).to receive(:get).and_return(response_double) -+ -+ expect(subject.get(resource)).to eq(response_double.parsed_response) -+ end -+ end -+ -+ describe 'request query' do -+ include_examples 'performs network request' do -+ let(:expected_args) do -+ [ -+ anything, -+ hash_including( -+ query: { -+ page: described_class::DEFAULT_PAGE, -+ per_page: described_class::DEFAULT_PER_PAGE -+ } -+ ) -+ ] -+ end -+ end -+ end -+ -+ describe 'request headers' do -+ include_examples 'performs network request' do -+ let(:expected_args) do -+ [ -+ anything, -+ hash_including( -+ headers: { -+ 'Content-Type' => 'application/json', -+ 'Authorization' => "Bearer #{token}" -+ } -+ ) -+ ] -+ end -+ end -+ end -+ -+ describe 'request uri' do -+ include_examples 'performs network request' do -+ let(:expected_args) do -+ ['http://gitlab.example:80/api/v4/resource', anything] -+ end -+ end -+ end -+ -+ context 'error handling' do -+ context 'when error occurred' do -+ it 'raises ConnectionError' do -+ allow(Gitlab::HTTP).to receive(:get).and_raise(Errno::ECONNREFUSED) -+ -+ expect { subject.get(resource) }.to raise_exception(described_class::ConnectionError) -+ end -+ end -+ -+ context 'when response is not success' do -+ it 'raises ConnectionError' do -+ response_double = double(code: 503, success?: false) -+ -+ allow(Gitlab::HTTP).to receive(:get).and_return(response_double) -+ -+ expect { subject.get(resource) }.to raise_exception(described_class::ConnectionError) -+ end -+ end -+ end -+ end -+end ---- /dev/null -+++ gitlab-13.6.5/spec/lib/gitlab/import_export/project/sample/sample_data_relation_tree_restorer_spec.rb -@@ -0,0 +1,87 @@ -+# frozen_string_literal: true -+ -+# This spec is a lightweight version of: -+# * project/tree_restorer_spec.rb -+# -+# In depth testing is being done in the above specs. -+# This spec tests that restore of the sample project works -+# but does not have 100% relation coverage. -+ -+require 'spec_helper' -+ -+RSpec.describe Gitlab::ImportExport::Project::Sample::SampleDataRelationTreeRestorer do -+ include_context 'relation tree restorer shared context' -+ -+ let(:sample_data_relation_tree_restorer) do -+ described_class.new( -+ user: user, -+ shared: shared, -+ relation_reader: relation_reader, -+ object_builder: object_builder, -+ members_mapper: members_mapper, -+ relation_factory: relation_factory, -+ reader: reader, -+ importable: importable, -+ importable_path: importable_path, -+ importable_attributes: attributes -+ ) -+ end -+ -+ subject { sample_data_relation_tree_restorer.restore } -+ -+ shared_examples 'import project successfully' do -+ it 'restores project tree' do -+ expect(subject).to eq(true) -+ end -+ -+ describe 'imported project' do -+ let(:project) { Project.find_by_path('project') } -+ -+ before do -+ subject -+ end -+ -+ it 'has the project attributes and relations', :aggregate_failures do -+ expect(project.description).to eq('Nisi et repellendus ut enim quo accusamus vel magnam.') -+ expect(project.issues.count).to eq(10) -+ expect(project.milestones.count).to eq(3) -+ expect(project.labels.count).to eq(2) -+ expect(project.project_feature).not_to be_nil -+ end -+ -+ it 'has issues with correctly updated due dates' do -+ due_dates = due_dates(project.issues) -+ -+ expect(due_dates).to match_array([Date.today - 7.days, Date.today, Date.today + 7.days]) -+ end -+ -+ it 'has milestones with correctly updated due dates' do -+ due_dates = due_dates(project.milestones) -+ -+ expect(due_dates).to match_array([Date.today - 7.days, Date.today, Date.today + 7.days]) -+ end -+ -+ def due_dates(relations) -+ due_dates = relations.map { |relation| relation['due_date'] } -+ due_dates.compact! -+ due_dates.sort -+ end -+ end -+ end -+ -+ context 'when restoring a project' do -+ let(:importable) { create(:project, :builds_enabled, :issues_disabled, name: 'project', path: 'project') } -+ let(:importable_name) { 'project' } -+ let(:importable_path) { 'project' } -+ let(:object_builder) { Gitlab::ImportExport::Project::ObjectBuilder } -+ let(:relation_factory) { Gitlab::ImportExport::Project::RelationFactory } -+ let(:reader) { Gitlab::ImportExport::Reader.new(shared: shared) } -+ -+ context 'using ndjson reader' do -+ let(:path) { 'spec/fixtures/lib/gitlab/import_export/sample_data/tree' } -+ let(:relation_reader) { Gitlab::ImportExport::JSON::NdjsonReader.new(path) } -+ -+ it_behaves_like 'import project successfully' -+ end -+ end -+end ---- /dev/null -+++ gitlab-13.6.5/spec/lib/gitlab/middleware/handle_null_bytes_spec.rb -@@ -0,0 +1,88 @@ -+# frozen_string_literal: true -+ -+require 'spec_helper' -+require "rack/test" -+ -+RSpec.describe Gitlab::Middleware::HandleNullBytes do -+ let(:null_byte) { "\u0000" } -+ let(:error_400) { [400, {}, ["Bad Request"]] } -+ let(:app) { double(:app) } -+ -+ subject { described_class.new(app) } -+ -+ before do -+ allow(app).to receive(:call) do |args| -+ args -+ end -+ end -+ -+ def env_for(params = {}) -+ Rack::MockRequest.env_for('/', { params: params }) -+ end -+ -+ context 'with null bytes in params' do -+ it 'rejects null bytes in a top level param' do -+ env = env_for(name: "null#{null_byte}byte") -+ -+ expect(subject.call(env)).to eq error_400 -+ end -+ -+ it "responds with 400 BadRequest for hashes with strings" do -+ env = env_for(name: { inner_key: "I am #{null_byte} bad" }) -+ -+ expect(subject.call(env)).to eq error_400 -+ end -+ -+ it "responds with 400 BadRequest for arrays with strings" do -+ env = env_for(name: ["I am #{null_byte} bad"]) -+ -+ expect(subject.call(env)).to eq error_400 -+ end -+ -+ it "responds with 400 BadRequest for arrays containing hashes with string values" do -+ env = env_for(name: [ -+ { -+ inner_key: "I am #{null_byte} bad" -+ } -+ ]) -+ -+ expect(subject.call(env)).to eq error_400 -+ end -+ -+ it "gives up and does not 400 with too deeply nested params" do -+ env = env_for(name: [ -+ { -+ inner_key: { deeper_key: [{ hash_inside_array_key: "I am #{null_byte} bad" }] } -+ } -+ ]) -+ -+ expect(subject.call(env)).not_to eq error_400 -+ end -+ end -+ -+ context 'without null bytes in params' do -+ it "does not respond with a 400 for strings" do -+ env = env_for(name: "safe name") -+ -+ expect(subject.call(env)).not_to eq error_400 -+ end -+ -+ it "does not respond with a 400 with no params" do -+ env = env_for -+ -+ expect(subject.call(env)).not_to eq error_400 -+ end -+ end -+ -+ context 'when disabled via env flag' do -+ before do -+ stub_env('REJECT_NULL_BYTES', '1') -+ end -+ -+ it 'does not respond with a 400 no matter what' do -+ env = env_for(name: "null#{null_byte}byte") -+ -+ expect(subject.call(env)).not_to eq error_400 -+ end -+ end -+end ---- /dev/null -+++ gitlab-13.6.5/spec/migrations/schedule_blocked_by_links_replacement_spec.rb -@@ -0,0 +1,37 @@ -+# frozen_string_literal: true -+ -+require 'spec_helper' -+require Rails.root.join('db', 'post_migrate', '20201015073808_schedule_blocked_by_links_replacement') -+ -+RSpec.describe ScheduleBlockedByLinksReplacement do -+ let(:namespace) { table(:namespaces).create!(name: 'gitlab', path: 'gitlab-org') } -+ let(:project) { table(:projects).create!(namespace_id: namespace.id, name: 'gitlab') } -+ let(:issue1) { table(:issues).create!(project_id: project.id, title: 'a') } -+ let(:issue2) { table(:issues).create!(project_id: project.id, title: 'b') } -+ let(:issue3) { table(:issues).create!(project_id: project.id, title: 'c') } -+ let!(:issue_links) do -+ [ -+ table(:issue_links).create!(source_id: issue1.id, target_id: issue2.id, link_type: 1), -+ table(:issue_links).create!(source_id: issue2.id, target_id: issue1.id, link_type: 2), -+ table(:issue_links).create!(source_id: issue1.id, target_id: issue3.id, link_type: 2) -+ ] -+ end -+ -+ before do -+ stub_const("#{described_class.name}::BATCH_SIZE", 1) -+ end -+ -+ it 'schedules jobs for blocked_by links' do -+ Sidekiq::Testing.fake! do -+ freeze_time do -+ migrate! -+ -+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration( -+ 2.minutes, issue_links[1].id, issue_links[1].id) -+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration( -+ 4.minutes, issue_links[2].id, issue_links[2].id) -+ expect(BackgroundMigrationWorker.jobs.size).to eq(2) -+ end -+ end -+ end -+end ---- /dev/null -+++ gitlab-13.6.5/spec/models/ci/build_trace_chunks/legacy_fog_spec.rb -@@ -0,0 +1,164 @@ -+# frozen_string_literal: true -+ -+require 'spec_helper' -+ -+RSpec.describe Ci::BuildTraceChunks::LegacyFog do -+ let(:data_store) { described_class.new } -+ -+ before do -+ stub_artifacts_object_storage -+ end -+ -+ describe '#available?' do -+ subject { data_store.available? } -+ -+ context 'when object storage is enabled' do -+ it { is_expected.to be_truthy } -+ end -+ -+ context 'when object storage is disabled' do -+ before do -+ stub_artifacts_object_storage(enabled: false) -+ end -+ -+ it { is_expected.to be_falsy } -+ end -+ end -+ -+ describe '#data' do -+ subject { data_store.data(model) } -+ -+ context 'when data exists' do -+ let(:model) { create(:ci_build_trace_chunk, :fog_with_data, initial_data: 'sample data in fog') } -+ -+ it 'returns the data' do -+ is_expected.to eq('sample data in fog') -+ end -+ end -+ -+ context 'when data does not exist' do -+ let(:model) { create(:ci_build_trace_chunk, :fog_without_data) } -+ -+ it 'returns nil' do -+ expect(data_store.data(model)).to be_nil -+ end -+ end -+ end -+ -+ describe '#set_data' do -+ let(:new_data) { 'abc123' } -+ -+ context 'when data exists' do -+ let(:model) { create(:ci_build_trace_chunk, :fog_with_data, initial_data: 'sample data in fog') } -+ -+ it 'overwrites data' do -+ expect(data_store.data(model)).to eq('sample data in fog') -+ -+ data_store.set_data(model, new_data) -+ -+ expect(data_store.data(model)).to eq new_data -+ end -+ end -+ -+ context 'when data does not exist' do -+ let(:model) { create(:ci_build_trace_chunk, :fog_without_data) } -+ -+ it 'sets new data' do -+ expect(data_store.data(model)).to be_nil -+ -+ data_store.set_data(model, new_data) -+ -+ expect(data_store.data(model)).to eq new_data -+ end -+ end -+ end -+ -+ describe '#delete_data' do -+ subject { data_store.delete_data(model) } -+ -+ context 'when data exists' do -+ let(:model) { create(:ci_build_trace_chunk, :fog_with_data, initial_data: 'sample data in fog') } -+ -+ it 'deletes data' do -+ expect(data_store.data(model)).to eq('sample data in fog') -+ -+ subject -+ -+ expect(data_store.data(model)).to be_nil -+ end -+ end -+ -+ context 'when data does not exist' do -+ let(:model) { create(:ci_build_trace_chunk, :fog_without_data) } -+ -+ it 'does nothing' do -+ expect(data_store.data(model)).to be_nil -+ -+ subject -+ -+ expect(data_store.data(model)).to be_nil -+ end -+ end -+ end -+ -+ describe '#size' do -+ context 'when data exists' do -+ let(:model) { create(:ci_build_trace_chunk, :fog_with_data, initial_data: 'üabcd') } -+ -+ it 'returns data bytesize correctly' do -+ expect(data_store.size(model)).to eq 6 -+ end -+ end -+ -+ context 'when data does not exist' do -+ let(:model) { create(:ci_build_trace_chunk, :fog_without_data) } -+ -+ it 'returns zero' do -+ expect(data_store.size(model)).to be_zero -+ end -+ end -+ end -+ -+ describe '#keys' do -+ subject { data_store.keys(relation) } -+ -+ let(:build) { create(:ci_build) } -+ let(:relation) { build.trace_chunks } -+ -+ before do -+ create(:ci_build_trace_chunk, :fog_with_data, chunk_index: 0, build: build) -+ create(:ci_build_trace_chunk, :fog_with_data, chunk_index: 1, build: build) -+ end -+ -+ it 'returns keys' do -+ is_expected.to eq([[build.id, 0], [build.id, 1]]) -+ end -+ end -+ -+ describe '#delete_keys' do -+ subject { data_store.delete_keys(keys) } -+ -+ let(:build) { create(:ci_build) } -+ let(:relation) { build.trace_chunks } -+ let(:keys) { data_store.keys(relation) } -+ -+ before do -+ create(:ci_build_trace_chunk, :fog_with_data, chunk_index: 0, build: build) -+ create(:ci_build_trace_chunk, :fog_with_data, chunk_index: 1, build: build) -+ end -+ -+ it 'deletes multiple data' do -+ ::Fog::Storage.new(JobArtifactUploader.object_store_credentials).tap do |connection| -+ expect(connection.get_object('artifacts', "tmp/builds/#{build.id}/chunks/0.log")[:body]).to be_present -+ expect(connection.get_object('artifacts', "tmp/builds/#{build.id}/chunks/1.log")[:body]).to be_present -+ end -+ -+ subject -+ -+ ::Fog::Storage.new(JobArtifactUploader.object_store_credentials).tap do |connection| -+ expect { connection.get_object('artifacts', "tmp/builds/#{build.id}/chunks/0.log")[:body] }.to raise_error(Excon::Error::NotFound) -+ expect { connection.get_object('artifacts', "tmp/builds/#{build.id}/chunks/1.log")[:body] }.to raise_error(Excon::Error::NotFound) -+ end -+ end -+ end -+end ---- /dev/null -+++ gitlab-13.6.5/spec/requests/user_sends_null_bytes_spec.rb -@@ -0,0 +1,14 @@ -+# frozen_string_literal: true -+ -+require 'spec_helper' -+ -+RSpec.describe 'User sends null bytes as params' do -+ let(:null_byte) { "\u0000" } -+ -+ it 'raises a 400 error' do -+ post '/nonexistent', params: { a: "A #{null_byte} nasty string" } -+ -+ expect(response).to have_gitlab_http_status(:bad_request) -+ expect(response.body).to eq('Bad Request') -+ end -+end diff --git a/debian/patches/series b/debian/patches/series index f46f952298..0a02fb34c1 100644 --- a/debian/patches/series +++ b/debian/patches/series @@ -29,4 +29,3 @@ 0740-use-packaged-modules.patch 0750-fix-relative-paths.patch 0770-remove-capybara-screenshot-rspec.patch -fix-mismatch-by-manual-merge.patch