diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-02-28 21:13:35 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-02-28 21:13:35 +0000 |
commit | 86ad1426d8a8f5d7f20bc5d8b536d3034d829d1f (patch) | |
tree | 7159021dd6fd3834a21096901bddb3b1915caa23 | |
parent | 36eff6e5089629619cc55f4771fa949d6ae2b29b (diff) | |
download | gitlab-ce-86ad1426d8a8f5d7f20bc5d8b536d3034d829d1f.tar.gz |
Add latest changes from gitlab-org/gitlab@master
64 files changed, 902 insertions, 189 deletions
diff --git a/.rubocop_todo/layout/line_length.yml b/.rubocop_todo/layout/line_length.yml index 30c47ed2ed0..5508542a599 100644 --- a/.rubocop_todo/layout/line_length.yml +++ b/.rubocop_todo/layout/line_length.yml @@ -4034,7 +4034,6 @@ Layout/LineLength: - 'spec/lib/error_tracking/sentry_client/projects_spec.rb' - 'spec/lib/event_filter_spec.rb' - 'spec/lib/feature_spec.rb' - - 'spec/lib/generators/gitlab/snowplow_event_definition_generator_spec.rb' - 'spec/lib/generators/gitlab/usage_metric_definition/redis_hll_generator_spec.rb' - 'spec/lib/generators/gitlab/usage_metric_definition_generator_spec.rb' - 'spec/lib/generators/gitlab/usage_metric_generator_spec.rb' diff --git a/.rubocop_todo/rspec/context_wording.yml b/.rubocop_todo/rspec/context_wording.yml index cb732ea307c..1ad85791e69 100644 --- a/.rubocop_todo/rspec/context_wording.yml +++ b/.rubocop_todo/rspec/context_wording.yml @@ -1636,7 +1636,6 @@ RSpec/ContextWording: - 'spec/lib/extracts_ref_spec.rb' - 'spec/lib/feature/definition_spec.rb' - 'spec/lib/feature_spec.rb' - - 'spec/lib/generators/gitlab/snowplow_event_definition_generator_spec.rb' - 'spec/lib/gitlab/alert_management/fingerprint_spec.rb' - 'spec/lib/gitlab/analytics/cycle_analytics/average_spec.rb' - 'spec/lib/gitlab/analytics/cycle_analytics/base_query_builder_spec.rb' @@ -566,7 +566,7 @@ gem 'oj-introspect', '~> 0.7' gem 'multi_json', '~> 1.14.1' gem 'yajl-ruby', '~> 1.4.3', require: 'yajl' -gem 'webauthn', '~> 2.3' +gem 'webauthn', '~> 3.0' # IPAddress utilities gem 'ipaddress', '~> 0.8.3' diff --git a/Gemfile.checksum b/Gemfile.checksum index 592d11baac2..2545eb99a5e 100644 --- a/Gemfile.checksum +++ b/Gemfile.checksum @@ -33,7 +33,7 @@ {"name":"attr_required","version":"1.0.1","platform":"ruby","checksum":"024e10393bd30901e1adf6769bd756b873a5ef7da60f86f8f11066116b5742bc"}, {"name":"autoprefixer-rails","version":"10.2.5.1","platform":"ruby","checksum":"3711d67f1112361c7628847ac192d8aa6f3b8abe47527aee8a69dc8985e798ee"}, {"name":"awesome_print","version":"1.9.2","platform":"ruby","checksum":"e99b32b704acff16d768b3468680793ced40bfdc4537eb07e06a4be11133786e"}, -{"name":"awrence","version":"1.1.1","platform":"ruby","checksum":"9be584c97408ed92d5e1ca11740853646fe270de675f2f8dd44e8233226dfc97"}, +{"name":"awrence","version":"1.2.1","platform":"ruby","checksum":"dd1d214c12a91f449d1ef81d7ee3babc2816944e450752e7522c65521872483e"}, {"name":"aws-eventstream","version":"1.2.0","platform":"ruby","checksum":"ffa53482c92880b001ff2fb06919b9bb82fd847cbb0fa244985d2ebb6dd0d1df"}, {"name":"aws-partitions","version":"1.703.0","platform":"ruby","checksum":"3d32fcdcb2799fe0472a9b30990035713d7a75ac8b77bd7767ef5ee2914ea748"}, {"name":"aws-sdk-cloudformation","version":"1.41.0","platform":"ruby","checksum":"31e47539719734413671edf9b1a31f8673fbf9688549f50c41affabbcb1c6b26"}, @@ -85,7 +85,7 @@ {"name":"concurrent-ruby","version":"1.2.0","platform":"ruby","checksum":"a5e799f71e7490f24a534d58c91380267d0ae306af0cdc518d6848b93475dae2"}, {"name":"connection_pool","version":"2.3.0","platform":"ruby","checksum":"677985be912f33c90f98f229aaa0c0ddb2ef8776f21929a36eeeb25251c944da"}, {"name":"cork","version":"0.3.0","platform":"ruby","checksum":"a0a0ac50e262f8514d1abe0a14e95e71c98b24e3378690e5d044daf0013ad4bc"}, -{"name":"cose","version":"1.0.0","platform":"ruby","checksum":"520ebaad97b56d2873de02ff4e2c973f5e77ce2f8edbda454af9ee3073643bc0"}, +{"name":"cose","version":"1.3.0","platform":"ruby","checksum":"63247c66a5bc76e53926756574fe3724cc0a88707e358c90532ae2a320e98601"}, {"name":"countries","version":"4.0.1","platform":"ruby","checksum":"d32e8a3c0b22949f1a41ea6d9005f5168ffce226f8fe077d1d6be785fffa81c5"}, {"name":"crack","version":"0.4.3","platform":"ruby","checksum":"5318ba8cd9cf7e0b5feb38948048503ba4b1fdc1b6ff30a39f0a00feb6036b29"}, {"name":"crass","version":"1.0.6","platform":"ruby","checksum":"dc516022a56e7b3b156099abc81b6d2b08ea1ed12676ac7a5657617f012bd45d"}, @@ -406,7 +406,7 @@ {"name":"open4","version":"1.3.4","platform":"ruby","checksum":"a1df037310624ecc1ea1d81264b11c83e96d0c3c1c6043108d37d396dcd0f4b1"}, {"name":"openid_connect","version":"1.3.0","platform":"ruby","checksum":"a796855096850cc01140e37ea6ae9fd14f2be818b9b5bc698418063dfe228770"}, {"name":"openssl","version":"2.2.2","platform":"ruby","checksum":"53f72382bac046c36c37049c7ec9d5597d42628d140b5cfbcd61e0226c0ca077"}, -{"name":"openssl-signature_algorithm","version":"0.4.0","platform":"ruby","checksum":"e53a225b773784935249cf4c61238c6cf0e1e464e78ae2f8ddaf995fb22ca991"}, +{"name":"openssl-signature_algorithm","version":"1.3.0","platform":"ruby","checksum":"a3b40b5e8276162d4a6e50c7c97cdaf1446f9b2c3946a6fa2c14628e0c957e80"}, {"name":"opentracing","version":"0.5.0","platform":"ruby","checksum":"deb5d7abe6b0e7631d866d8cb5ee7bb9352650a504a32f61591302bc510b9286"}, {"name":"optimist","version":"3.0.1","platform":"ruby","checksum":"336b753676d6117cad9301fac7e91dab4228f747d4e7179891ad3a163c64e2ed"}, {"name":"org-ruby","version":"0.9.12","platform":"ruby","checksum":"93cbec3a4470cb9dca6a4a98dc276a6434ea9d9e7bc2d42ea33c3aedd5d1c974"}, @@ -534,7 +534,6 @@ {"name":"sassc-rails","version":"2.1.0","platform":"ruby","checksum":"764dcc74e06930e3483caf0d595084d11f2b0fefd6539abf487cdddfba6cafa2"}, {"name":"sawyer","version":"0.9.2","platform":"ruby","checksum":"fa3a72d62a4525517b18857ddb78926aab3424de0129be6772a8e2ba240e7aca"}, {"name":"sd_notify","version":"0.1.1","platform":"ruby","checksum":"cbc7ac6caa7cedd26b30a72b5eeb6f36050dc0752df263452ea24fb5a4ad3131"}, -{"name":"securecompare","version":"1.0.0","platform":"ruby","checksum":"cb0c6599deaaedf6d28f8d88538b06e7198c4826b1b8edb1dbeb44a2162fc62b"}, {"name":"seed-fu","version":"2.3.7","platform":"ruby","checksum":"f19673443e9af799b730e3d4eca6a89b39e5a36825015dffd00d02ea3365cf74"}, {"name":"selenium-webdriver","version":"3.142.7","platform":"ruby","checksum":"dea0993e0e4fdb364f0453144814c0e6099a411d17396807c6cac666d0ddac29"}, {"name":"sentry-rails","version":"5.1.1","platform":"ruby","checksum":"906ef0a776ddc35884ab8b548856ba81c607e3fdee7c9c9f7c44efccc16a657f"}, @@ -609,7 +608,7 @@ {"name":"tins","version":"1.31.1","platform":"ruby","checksum":"51c4a347c25c630d310cbc2c040ffb84e266c8227f2ade881f1130ee4f9fbecf"}, {"name":"toml-rb","version":"2.2.0","platform":"ruby","checksum":"a1e2c54ac3cc9d49861004f75f0648b3622ac03a76abe105358c31553227d9a6"}, {"name":"tomlrb","version":"1.3.0","platform":"ruby","checksum":"68666bf53fa70ba686a48a7435ce7e086f5227c58c4c993bd9792f4760f2a503"}, -{"name":"tpm-key_attestation","version":"0.9.0","platform":"ruby","checksum":"e469ad9111a68dab4d04596e1c0621d7c877c2e3e247f765af3c04f1adf2b8cd"}, +{"name":"tpm-key_attestation","version":"0.12.0","platform":"ruby","checksum":"e133d80cf24fef0e7a7dfad00fd6aeff01fc79875fbfc66cd8537bbd622b1e6d"}, {"name":"trailblazer-option","version":"0.1.2","platform":"ruby","checksum":"20e4f12ea4e1f718c8007e7944ca21a329eee4eed9e0fa5dde6e8ad8ac4344a3"}, {"name":"train-core","version":"3.4.9","platform":"ruby","checksum":"d7ad8fa9a379c43a30baaaf1141af1cb28349d386c054f7fc81d169a625d6edd"}, {"name":"truncato","version":"0.7.12","platform":"ruby","checksum":"fed9e8a04fa35fd1a64506cd2089761bae4adfe47e756c3ce98a5c43856c9c4c"}, @@ -646,7 +645,7 @@ {"name":"vmstat","version":"2.3.0","platform":"ruby","checksum":"ab5446a3e3bd0a9cdb9d9ac69a0bbd119c4f161d945a0846a519dd7018af656d"}, {"name":"warden","version":"1.2.9","platform":"ruby","checksum":"46684f885d35a69dbb883deabf85a222c8e427a957804719e143005df7a1efd0"}, {"name":"warning","version":"1.3.0","platform":"ruby","checksum":"23695a5d8e50bd5c46068931b529bee0b28e4982cbcefbe77d867800dde8069e"}, -{"name":"webauthn","version":"2.3.0","platform":"ruby","checksum":"96fbee59f4a45219f1dae96f467b693de144f871be9ec6ea357168624dacd89e"}, +{"name":"webauthn","version":"3.0.0","platform":"ruby","checksum":"3f77d422c2a8a4b31e56cf42f83414bd066e0506e9896936e1730262dc4a20e6"}, {"name":"webfinger","version":"1.2.0","platform":"ruby","checksum":"7814ef1c85da47514f65c6e5ca14205fa9ce41ea2a70785e0c872842162852a2"}, {"name":"webmock","version":"3.9.1","platform":"ruby","checksum":"bcf6822456b234fb1bed2b0a89bff31fe0641214b44f6ba4ced2b824cf31337d"}, {"name":"webrick","version":"1.6.1","platform":"ruby","checksum":"0b4d1eab918f5f53333c690ad470825e51844ce9851e403a3fd47d6a84d9d67c"}, diff --git a/Gemfile.lock b/Gemfile.lock index c8c2c775198..d64be58596b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -198,7 +198,7 @@ GEM autoprefixer-rails (10.2.5.1) execjs (> 0) awesome_print (1.9.2) - awrence (1.1.1) + awrence (1.2.1) aws-eventstream (1.2.0) aws-partitions (1.703.0) aws-sdk-cloudformation (1.41.0) @@ -298,9 +298,9 @@ GEM connection_pool (2.3.0) cork (0.3.0) colored2 (~> 3.1) - cose (1.0.0) + cose (1.3.0) cbor (~> 0.5.9) - openssl-signature_algorithm (~> 0.4.0) + openssl-signature_algorithm (~> 1.0) countries (4.0.1) i18n_data (~> 0.13.0) sixarm_ruby_unaccent (~> 1.1) @@ -1041,7 +1041,8 @@ GEM webfinger (>= 1.0.1) openssl (2.2.2) ipaddr - openssl-signature_algorithm (0.4.0) + openssl-signature_algorithm (1.3.0) + openssl (> 2.0) opentracing (0.5.0) optimist (3.0.1) org-ruby (0.9.12) @@ -1335,7 +1336,6 @@ GEM addressable (>= 2.3.5) faraday (>= 0.17.3, < 3) sd_notify (0.1.1) - securecompare (1.0.0) seed-fu (2.3.7) activerecord (>= 3.1) activesupport (>= 3.1) @@ -1472,9 +1472,10 @@ GEM toml-rb (2.2.0) citrus (~> 3.0, > 3.0) tomlrb (1.3.0) - tpm-key_attestation (0.9.0) + tpm-key_attestation (0.12.0) bindata (~> 2.4) - openssl-signature_algorithm (~> 0.4.0) + openssl (> 2.0) + openssl-signature_algorithm (~> 1.0) trailblazer-option (0.1.2) train-core (3.4.9) addressable (~> 2.5) @@ -1547,16 +1548,15 @@ GEM warden (1.2.9) rack (>= 2.0.9) warning (1.3.0) - webauthn (2.3.0) + webauthn (3.0.0) android_key_attestation (~> 0.3.0) awrence (~> 1.1) bindata (~> 2.4) cbor (~> 0.5.9) - cose (~> 1.0) - openssl (~> 2.0) + cose (~> 1.1) + openssl (>= 2.2) safety_net_attestation (~> 0.4.0) - securecompare (~> 1.0) - tpm-key_attestation (~> 0.9.0) + tpm-key_attestation (~> 0.12.0) webfinger (1.2.0) activesupport httpclient (>= 2.4) @@ -1886,7 +1886,7 @@ DEPENDENCIES view_component (~> 2.74.1) vmstat (~> 2.3.0) warning (~> 1.3.0) - webauthn (~> 2.3) + webauthn (~> 3.0) webmock (~> 3.9.1) webrick (~> 1.6.1) wikicloth (= 0.8.1) diff --git a/app/assets/javascripts/diffs/components/app.vue b/app/assets/javascripts/diffs/components/app.vue index 35d1a564178..c8ba4a1676d 100644 --- a/app/assets/javascripts/diffs/components/app.vue +++ b/app/assets/javascripts/diffs/components/app.vue @@ -21,6 +21,7 @@ import PanelResizer from '~/vue_shared/components/panel_resizer.vue'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import notesEventHub from '~/notes/event_hub'; +import { DynamicScroller, DynamicScrollerItem } from 'vendor/vue-virtual-scroller'; import { TREE_LIST_WIDTH_STORAGE_KEY, INITIAL_TREE_WIDTH, @@ -53,15 +54,14 @@ import HiddenFilesWarning from './hidden_files_warning.vue'; import NoChanges from './no_changes.vue'; import TreeList from './tree_list.vue'; import VirtualScrollerScrollSync from './virtual_scroller_scroll_sync'; +import PreRenderer from './pre_renderer.vue'; export default { name: 'DiffsApp', components: { - DynamicScroller: () => - import('vendor/vue-virtual-scroller').then(({ DynamicScroller }) => DynamicScroller), - DynamicScrollerItem: () => - import('vendor/vue-virtual-scroller').then(({ DynamicScrollerItem }) => DynamicScrollerItem), - PreRenderer: () => import('./pre_renderer.vue').then((PreRenderer) => PreRenderer), + DynamicScroller, + DynamicScrollerItem, + PreRenderer, VirtualScrollerScrollSync, CompareVersions, DiffFile, diff --git a/app/assets/javascripts/diffs/store/actions.js b/app/assets/javascripts/diffs/store/actions.js index 9f90de9abde..9d7a540570e 100644 --- a/app/assets/javascripts/diffs/store/actions.js +++ b/app/assets/javascripts/diffs/store/actions.js @@ -14,6 +14,8 @@ import Poll from '~/lib/utils/poll'; import { mergeUrlParams, getLocationHash } from '~/lib/utils/url_utility'; import { __, s__ } from '~/locale'; import notesEventHub from '~/notes/event_hub'; +import { generateTreeList } from '~/diffs/utils/tree_worker_utils'; +import { sortTree } from '~/ide/stores/utils'; import { PARALLEL_DIFF_VIEW_TYPE, INLINE_DIFF_VIEW_TYPE, @@ -52,7 +54,6 @@ import { isCollapsed } from '../utils/diff_file'; import { markFileReview, setReviewsForMergeRequest } from '../utils/file_reviews'; import { getDerivedMergeRequestInformation } from '../utils/merge_request'; import { queueRedisHllEvents } from '../utils/queue_events'; -import TreeWorker from '../workers/tree_worker?worker'; import * as types from './mutation_types'; import { getDiffPositionByLineCode, @@ -199,21 +200,12 @@ export const fetchDiffFilesBatch = ({ commit, state, dispatch }) => { }; export const fetchDiffFilesMeta = ({ commit, state }) => { - const worker = new TreeWorker(); const urlParams = { view: 'inline', w: state.showWhitespace ? '0' : '1', }; commit(types.SET_LOADING, true); - eventHub.$emit(EVT_PERF_MARK_FILE_TREE_START); - - worker.addEventListener('message', ({ data }) => { - commit(types.SET_TREE_DATA, data); - eventHub.$emit(EVT_PERF_MARK_FILE_TREE_END); - - worker.terminate(); - }); return axios .get(mergeUrlParams(urlParams, state.endpointMetadata)) @@ -225,18 +217,24 @@ export const fetchDiffFilesMeta = ({ commit, state }) => { commit(types.SET_MERGE_REQUEST_DIFFS, data.merge_request_diffs || []); commit(types.SET_DIFF_METADATA, strippedData); - worker.postMessage(data.diff_files); + eventHub.$emit(EVT_PERF_MARK_FILE_TREE_START); + const { treeEntries, tree } = generateTreeList(data.diff_files); + eventHub.$emit(EVT_PERF_MARK_FILE_TREE_END); + commit(types.SET_TREE_DATA, { + treeEntries, + tree: sortTree(tree), + }); return data; }) .catch((error) => { - worker.terminate(); - if (error.response.status === HTTP_STATUS_NOT_FOUND) { createAlert({ message: __('Building your merge request. Wait a few moments, then refresh this page.'), variant: VARIANT_WARNING, }); + } else { + throw error; } }); }; diff --git a/app/assets/javascripts/diffs/workers/tree_worker.js b/app/assets/javascripts/diffs/workers/tree_worker.js deleted file mode 100644 index 04010a99b52..00000000000 --- a/app/assets/javascripts/diffs/workers/tree_worker.js +++ /dev/null @@ -1,19 +0,0 @@ -import { sortTree } from '~/ide/stores/utils'; -import { generateTreeList } from '../utils/tree_worker_utils'; - -// eslint-disable-next-line no-restricted-globals -self.addEventListener('message', (e) => { - const { data } = e; - - if (data === undefined) { - return; - } - - const { treeEntries, tree } = generateTreeList(data); - - // eslint-disable-next-line no-restricted-globals - self.postMessage({ - treeEntries, - tree: sortTree(tree), - }); -}); diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 21227d62023..37b76257128 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -21,6 +21,7 @@ class Projects::IssuesController < Projects::ApplicationController before_action :check_issues_available! before_action :issue, unless: ->(c) { ISSUES_EXCEPT_ACTIONS.include?(c.action_name.to_sym) } before_action :redirect_if_work_item, unless: ->(c) { ISSUES_EXCEPT_ACTIONS.include?(c.action_name.to_sym) } + before_action :require_incident_for_incident_routes, only: :show after_action :log_issue_show, only: :show @@ -449,6 +450,15 @@ class Projects::IssuesController < Projects::ApplicationController redirect_to project_work_items_path(project, issue.id, params: request.query_parameters) end end + + def require_incident_for_incident_routes + return unless params[:incident_tab].present? + return if issue.incident? + + # Redirect instead of 404 to gracefully handle + # issue type changes + redirect_to project_issue_path(project, issue) + end end Projects::IssuesController.prepend_mod_with('Projects::IssuesController') diff --git a/app/graphql/types/root_storage_statistics_type.rb b/app/graphql/types/root_storage_statistics_type.rb index 64aaf3e73a0..67ee0589882 100644 --- a/app/graphql/types/root_storage_statistics_type.rb +++ b/app/graphql/types/root_storage_statistics_type.rb @@ -12,6 +12,7 @@ module Types field :lfs_objects_size, GraphQL::Types::Float, null: false, description: 'LFS objects size in bytes.' field :packages_size, GraphQL::Types::Float, null: false, description: 'Packages size in bytes.' field :pipeline_artifacts_size, GraphQL::Types::Float, null: false, description: 'CI pipeline artifacts size in bytes.' + field :registry_size_estimated, GraphQL::Types::Boolean, null: false, description: 'Indicates whether the deduplicated Container Registry size for the namespace is an estimated value or not.' field :repository_size, GraphQL::Types::Float, null: false, description: 'Git repository size in bytes.' field :snippets_size, GraphQL::Types::Float, null: false, description: 'Snippets size in bytes.' field :storage_size, GraphQL::Types::Float, null: false, description: 'Total storage in bytes.' diff --git a/app/models/ci/daily_build_group_report_result.rb b/app/models/ci/daily_build_group_report_result.rb index 598d1456a48..ffb29b48f99 100644 --- a/app/models/ci/daily_build_group_report_result.rb +++ b/app/models/ci/daily_build_group_report_result.rb @@ -6,7 +6,7 @@ module Ci belongs_to :last_pipeline, class_name: 'Ci::Pipeline', foreign_key: :last_pipeline_id belongs_to :project - belongs_to :group + belongs_to :group, class_name: '::Group' validates :data, json_schema: { filename: "daily_build_group_report_result_data" } diff --git a/app/models/preloaders/user_max_access_level_in_groups_preloader.rb b/app/models/preloaders/user_max_access_level_in_groups_preloader.rb index 0c747ad9c84..16d46facb96 100644 --- a/app/models/preloaders/user_max_access_level_in_groups_preloader.rb +++ b/app/models/preloaders/user_max_access_level_in_groups_preloader.rb @@ -46,14 +46,10 @@ module Preloaders end def all_memberships - if Feature.enabled?(:include_memberships_from_group_shares_in_preloader) - [ - direct_memberships.select(*GroupMember.cached_column_list), - memberships_from_group_shares - ] - else - [direct_memberships] - end + [ + direct_memberships.select(*GroupMember.cached_column_list), + memberships_from_group_shares + ] end def direct_memberships diff --git a/app/models/project.rb b/app/models/project.rb index 96e3a9e0f8f..ae4cedb5af3 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1272,6 +1272,18 @@ class Project < ApplicationRecord import_state&.human_status_name || 'none' end + def beautified_import_status_name + if import_finished? + return 'completed' unless import_checksums.present? + + fetched = import_checksums['fetched'] + imported = import_checksums['imported'] + fetched.keys.any? { |key| fetched[key] != imported[key] } ? 'partially completed' : 'completed' + else + import_status + end + end + def add_import_job job_id = if forked? diff --git a/app/services/import/github/cancel_project_import_service.rb b/app/services/import/github/cancel_project_import_service.rb index 5dce5e73662..616ae54451f 100644 --- a/app/services/import/github/cancel_project_import_service.rb +++ b/app/services/import/github/cancel_project_import_service.rb @@ -9,6 +9,8 @@ module Import if project.import_in_progress? project.import_state.cancel + metrics.track_import_state + success(project: project) else error(cannot_cancel_error_message, :bad_request) @@ -31,6 +33,10 @@ module Import project_status: project.import_state.status ) end + + def metrics + @metrics ||= Gitlab::Import::Metrics.new(:github_importer, project) + end end end end diff --git a/config/feature_flags/development/include_memberships_from_group_shares_in_preloader.yml b/config/feature_flags/development/include_memberships_from_group_shares_in_preloader.yml deleted file mode 100644 index d7f2d1f5552..00000000000 --- a/config/feature_flags/development/include_memberships_from_group_shares_in_preloader.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: include_memberships_from_group_shares_in_preloader -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/111157 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/390780 -milestone: '15.9' -type: development -group: group::organization -default_enabled: false diff --git a/db/migrate/20230222153048_add_registry_size_estimated_to_namespace_root_storage_statistics.rb b/db/migrate/20230222153048_add_registry_size_estimated_to_namespace_root_storage_statistics.rb new file mode 100644 index 00000000000..50fcf6fd113 --- /dev/null +++ b/db/migrate/20230222153048_add_registry_size_estimated_to_namespace_root_storage_statistics.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +class AddRegistrySizeEstimatedToNamespaceRootStorageStatistics < Gitlab::Database::Migration[2.1] + INDEX_NAME = 'index_ns_root_stor_stats_on_registry_size_estimated' + + disable_ddl_transaction! + + def up + with_lock_retries do + add_column :namespace_root_storage_statistics, :registry_size_estimated, :boolean, default: false, null: false + end + + add_concurrent_index :namespace_root_storage_statistics, :registry_size_estimated, name: INDEX_NAME + end + + def down + with_lock_retries do + remove_column :namespace_root_storage_statistics, :registry_size_estimated + end + end +end diff --git a/db/migrate/20230224130315_add_constraint_type_to_postgres_async_constraint_validation.rb b/db/migrate/20230224130315_add_constraint_type_to_postgres_async_constraint_validation.rb new file mode 100644 index 00000000000..dee5810d0d1 --- /dev/null +++ b/db/migrate/20230224130315_add_constraint_type_to_postgres_async_constraint_validation.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class AddConstraintTypeToPostgresAsyncConstraintValidation < Gitlab::Database::Migration[2.1] + def change + add_column :postgres_async_foreign_key_validations, :constraint_type, :integer, null: false, default: 0, limit: 2 + end +end diff --git a/db/post_migrate/20230118144623_schedule_migration_for_remediation.rb b/db/post_migrate/20230118144623_schedule_migration_for_remediation.rb new file mode 100644 index 00000000000..af9b7d65a55 --- /dev/null +++ b/db/post_migrate/20230118144623_schedule_migration_for_remediation.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +class ScheduleMigrationForRemediation < Gitlab::Database::Migration[2.1] + restrict_gitlab_migration gitlab_schema: :gitlab_main + + MIGRATION = 'MigrateRemediationsForVulnerabilityFindings' + DELAY_INTERVAL = 2.minutes + SUB_BATCH_SIZE = 500 + BATCH_SIZE = 5000 + + def up + queue_batched_background_migration( + MIGRATION, + :vulnerability_occurrences, + :id, + job_interval: DELAY_INTERVAL, + batch_size: BATCH_SIZE, + sub_batch_size: SUB_BATCH_SIZE + ) + end + + def down + delete_batched_background_migration(MIGRATION, :vulnerability_occurrences, :id, []) + end +end diff --git a/db/post_migrate/20230125195503_queue_backfill_compliance_violations.rb b/db/post_migrate/20230125195503_queue_backfill_compliance_violations.rb new file mode 100644 index 00000000000..5f797421bd5 --- /dev/null +++ b/db/post_migrate/20230125195503_queue_backfill_compliance_violations.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +class QueueBackfillComplianceViolations < Gitlab::Database::Migration[2.1] + MIGRATION = 'BackfillComplianceViolations' + INTERVAL = 2.minutes + BATCH_SIZE = 10_000 + + disable_ddl_transaction! + restrict_gitlab_migration gitlab_schema: :gitlab_main + + def up + queue_batched_background_migration( + MIGRATION, + :merge_requests_compliance_violations, + :id, + job_interval: INTERVAL, + batch_size: BATCH_SIZE + ) + end + + def down + delete_batched_background_migration(MIGRATION, :merge_requests_compliance_violations, :id, []) + end +end diff --git a/db/schema_migrations/20230118144623 b/db/schema_migrations/20230118144623 new file mode 100644 index 00000000000..82c15116c45 --- /dev/null +++ b/db/schema_migrations/20230118144623 @@ -0,0 +1 @@ +3ceeeeabb4ebae0f38e446c044fe6e6d929661b8689b461bed87660afd2e223b
\ No newline at end of file diff --git a/db/schema_migrations/20230125195503 b/db/schema_migrations/20230125195503 new file mode 100644 index 00000000000..ee8f7d47e16 --- /dev/null +++ b/db/schema_migrations/20230125195503 @@ -0,0 +1 @@ +6321659d8f71127368dffd0bec122d4c32835da364a32cd6f276c641a70d10ff
\ No newline at end of file diff --git a/db/schema_migrations/20230222153048 b/db/schema_migrations/20230222153048 new file mode 100644 index 00000000000..66347d3252f --- /dev/null +++ b/db/schema_migrations/20230222153048 @@ -0,0 +1 @@ +ff11462b7e827b0ae66f54b131fa0d4099a6e7cc768fc9b400ee36346d1773fa
\ No newline at end of file diff --git a/db/schema_migrations/20230224130315 b/db/schema_migrations/20230224130315 new file mode 100644 index 00000000000..44960762a62 --- /dev/null +++ b/db/schema_migrations/20230224130315 @@ -0,0 +1 @@ +e54ddd26174440b453482d4c3d2dd8aa8cacbb2697162d9f976ed52a0d55f1a0
\ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index 96f828f7453..78adedc12eb 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -18533,7 +18533,8 @@ CREATE TABLE namespace_root_storage_statistics ( uploads_size bigint DEFAULT 0 NOT NULL, dependency_proxy_size bigint DEFAULT 0 NOT NULL, notification_level smallint DEFAULT 100 NOT NULL, - container_registry_size bigint DEFAULT 0 NOT NULL + container_registry_size bigint DEFAULT 0 NOT NULL, + registry_size_estimated boolean DEFAULT false NOT NULL ); CREATE TABLE namespace_settings ( @@ -20063,6 +20064,7 @@ CREATE TABLE postgres_async_foreign_key_validations ( table_name text NOT NULL, last_error text, attempts integer DEFAULT 0 NOT NULL, + constraint_type smallint DEFAULT 0 NOT NULL, CONSTRAINT check_536a40afbf CHECK ((char_length(last_error) <= 10000)), CONSTRAINT check_74fb7c8e57 CHECK ((char_length(name) <= 63)), CONSTRAINT check_cd435d6301 CHECK ((char_length(table_name) <= 63)) @@ -31010,6 +31012,8 @@ CREATE INDEX index_notification_settings_on_source_and_level_and_user ON notific CREATE UNIQUE INDEX index_notifications_on_user_id_and_source_id_and_source_type ON notification_settings USING btree (user_id, source_id, source_type); +CREATE INDEX index_ns_root_stor_stats_on_registry_size_estimated ON namespace_root_storage_statistics USING btree (registry_size_estimated); + CREATE UNIQUE INDEX index_ns_user_callouts_feature ON user_namespace_callouts USING btree (user_id, feature_name, namespace_id); CREATE INDEX index_oauth_access_grants_on_resource_owner_id ON oauth_access_grants USING btree (resource_owner_id, application_id, created_at); diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 2cc8ebf3897..d27c158e3e7 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -19914,6 +19914,7 @@ Counts of requirements by their state. | <a id="rootstoragestatisticslfsobjectssize"></a>`lfsObjectsSize` | [`Float!`](#float) | LFS objects size in bytes. | | <a id="rootstoragestatisticspackagessize"></a>`packagesSize` | [`Float!`](#float) | Packages size in bytes. | | <a id="rootstoragestatisticspipelineartifactssize"></a>`pipelineArtifactsSize` | [`Float!`](#float) | CI pipeline artifacts size in bytes. | +| <a id="rootstoragestatisticsregistrysizeestimated"></a>`registrySizeEstimated` | [`Boolean!`](#boolean) | Indicates whether the deduplicated Container Registry size for the namespace is an estimated value or not. | | <a id="rootstoragestatisticsrepositorysize"></a>`repositorySize` | [`Float!`](#float) | Git repository size in bytes. | | <a id="rootstoragestatisticssnippetssize"></a>`snippetsSize` | [`Float!`](#float) | Snippets size in bytes. | | <a id="rootstoragestatisticsstoragesize"></a>`storageSize` | [`Float!`](#float) | Total storage in bytes. | diff --git a/doc/development/snowplow/event_dictionary_guide.md b/doc/development/snowplow/event_dictionary_guide.md index 487e3e393d2..dc2214a40ed 100644 --- a/doc/development/snowplow/event_dictionary_guide.md +++ b/doc/development/snowplow/event_dictionary_guide.md @@ -79,14 +79,13 @@ tiers: Use the dedicated [event definition generator](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/generators/gitlab/snowplow_event_definition_generator.rb) to create new event definitions. -The `category` and `action` of each event are included in the filename to enforce uniqueness. +The `category` and `action` of each event are included in the filename to standardize file naming. The generator takes three options: - `--ee`: Indicates if the event is for EE. - `--category=CATEGORY`: Indicates the `category` of the event. - `--action=ACTION`: Indicates the `action` of the event. -- `--force`: Overwrites the existing event definition, if one already exists. ```shell bundle exec rails generate gitlab:snowplow_event_definition --category Groups::EmailCampaignsController --action click diff --git a/doc/integration/glab/index.md b/doc/integration/glab/index.md index 621472a2083..3e143b9f8c2 100644 --- a/doc/integration/glab/index.md +++ b/doc/integration/glab/index.md @@ -43,19 +43,24 @@ glab mr merge ## Core commands -- `glab alias` -- `glab api` -- `glab auth` -- `glab ci` -- `glab issue` -- `glab label` -- `glab mr` -- `glab project` -- `glab release` -- `glab snippet` -- `glab ssh-key` -- `glab user` -- `glab variable` +- [`glab alias`](https://gitlab.com/gitlab-org/cli/-/tree/main/docs/source/alias) +- [`glab api`](https://gitlab.com/gitlab-org/cli/-/tree/main/docs/source/api) +- [`glab auth`](https://gitlab.com/gitlab-org/cli/-/tree/main/docs/source/auth) +- [`glab check-update`](https://gitlab.com/gitlab-org/cli/-/tree/main/docs/source/check-update) +- [`glab ci`](https://gitlab.com/gitlab-org/cli/-/tree/main/docs/source/ci) +- [`glab completion`](https://gitlab.com/gitlab-org/cli/-/tree/main/docs/source/completion) +- [`glab config`](https://gitlab.com/gitlab-org/cli/-/tree/main/docs/source/config) +- [`glab incident`](https://gitlab.com/gitlab-org/cli/-/tree/main/docs/source/incident) +- [`glab issue`](https://gitlab.com/gitlab-org/cli/-/tree/main/docs/source/ci) +- [`glab label`](https://gitlab.com/gitlab-org/cli/-/tree/main/docs/source/label) +- [`glab mr`](https://gitlab.com/gitlab-org/cli/-/tree/main/docs/source/mr) +- [`glab release`](https://gitlab.com/gitlab-org/cli/-/tree/main/docs/source/release) +- [`glab repo`](https://gitlab.com/gitlab-org/cli/-/tree/main/docs/source/repo) +- [`glab schedule`](https://gitlab.com/gitlab-org/cli/-/tree/main/docs/source/schedule) +- [`glab snippet`](https://gitlab.com/gitlab-org/cli/-/tree/main/docs/source/snippet) +- [`glab ssh-key`](https://gitlab.com/gitlab-org/cli/-/tree/main/docs/source/ssh-key) +- [`glab user`](https://gitlab.com/gitlab-org/cli/-/tree/main/docs/source/user) +- [`glab variable`](https://gitlab.com/gitlab-org/cli/-/tree/main/docs/source/variable) ## Install the CLI diff --git a/doc/user/project/repository/tags/img/tag-display_v15_9.png b/doc/user/project/repository/tags/img/tag-display_v15_9.png Binary files differnew file mode 100644 index 00000000000..015df07d025 --- /dev/null +++ b/doc/user/project/repository/tags/img/tag-display_v15_9.png diff --git a/doc/user/project/repository/tags/index.md b/doc/user/project/repository/tags/index.md index d12a4e9f2e4..3d340789c2c 100644 --- a/doc/user/project/repository/tags/index.md +++ b/doc/user/project/repository/tags/index.md @@ -15,6 +15,25 @@ reference. Git supports two types of tags: Many projects combine an annotated release tag with a stable branch. Consider setting deployment or release tags automatically. +## View tags for a project + +1. On the top bar, select **Main menu > Projects** and find your project. +1. On the left sidebar, select **Repository > Tags**. + +![Example of a single tag](img/tag-display_v15_9.png) + +In the GitLab UI, each tag displays: + +- The tag name. (**{tag}**) +- Optional. If the tag is [protected](../../protected_tags.md), a **protected** badge. +- The commit SHA (**{commit}**), linked to the commit's contents. +- The commit's title and creation date. +- Optional. A link to the release (**{rocket}**). +- Optional. If a pipeline has been run, the current pipeline status. +- Download links to the source code and artifacts linked to the tag. +- A [**Create release**](../../releases/index.md#create-a-release) (**{pencil}**) link. +- A link to delete the tag. + ## Tags sample workflow 1. Create a lightweight tag. @@ -22,7 +41,7 @@ setting deployment or release tags automatically. 1. Push the tags to the remote repository. ```shell -git checkout master +git checkout main # Lightweight tag git tag my_lightweight_tag diff --git a/lib/generators/gitlab/snowplow_event_definition_generator.rb b/lib/generators/gitlab/snowplow_event_definition_generator.rb index 827e87dc313..8baeb6ba8c7 100644 --- a/lib/generators/gitlab/snowplow_event_definition_generator.rb +++ b/lib/generators/gitlab/snowplow_event_definition_generator.rb @@ -14,12 +14,11 @@ module Gitlab class_option :ee, type: :boolean, optional: true, default: false, desc: 'Indicates if event is for ee' class_option :category, type: :string, optional: false, desc: 'Category of the event' class_option :action, type: :string, optional: false, desc: 'Action of the event' - class_option :force, type: :boolean, optional: true, default: false, desc: 'Overwrite existing definition' def create_event_file - raise "Event definition already exists at #{file_path}" if definition_exists? && !force_definition_override? + raise "Event definition already exists at #{file_path}" if definition_exists? - template "event_definition.yml", file_path, force: force_definition_override? + template "event_definition.yml", file_path, force: false end def distributions @@ -42,10 +41,6 @@ module Gitlab options[:ee] end - def force_definition_override? - options[:force] - end - private def definition_exists? diff --git a/lib/gitlab/background_migration/backfill_compliance_violations.rb b/lib/gitlab/background_migration/backfill_compliance_violations.rb new file mode 100644 index 00000000000..131b4a05e41 --- /dev/null +++ b/lib/gitlab/background_migration/backfill_compliance_violations.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +module Gitlab + module BackgroundMigration + # rubocop: disable Style/Documentation + class BackfillComplianceViolations < Gitlab::BackgroundMigration::BatchedMigrationJob + feature_category :compliance_management + + def perform + # no-op. The logic is defined in EE module. + end + end + # rubocop: enable Style/Documentation + end +end + +::Gitlab::BackgroundMigration::BackfillComplianceViolations.prepend_mod diff --git a/lib/gitlab/background_migration/migrate_remediations_for_vulnerability_findings.rb b/lib/gitlab/background_migration/migrate_remediations_for_vulnerability_findings.rb new file mode 100644 index 00000000000..7a2f4dab742 --- /dev/null +++ b/lib/gitlab/background_migration/migrate_remediations_for_vulnerability_findings.rb @@ -0,0 +1,164 @@ +# frozen_string_literal: true + +module Vulnerabilities + # The class is mimicking Vulnerabilites::Remediation + class Remediation < ApplicationRecord + include FileStoreMounter + include ShaAttribute + + self.table_name = 'vulnerability_remediations' + + sha_attribute :checksum + + mount_file_store_uploader AttachmentUploader + + def retrieve_upload(_identifier, paths) + Upload.find_by(model: self, path: paths) + end + end +end + +module Gitlab + module BackgroundMigration + # The class to migrate the remediation data into their own records from the json attribute + class MigrateRemediationsForVulnerabilityFindings < BatchedMigrationJob + feature_category :vulnerability_management + operation_name :migrate_remediations_for_vulnerability_findings + + # The class to encapsulate checksum and file for uploading + class DiffFile < StringIO + # This method is used by the `carrierwave` gem + def original_filename + @original_filename ||= self.class.original_filename(checksum) + end + + def checksum + @checksum ||= self.class.checksum(string) + end + + def self.checksum(value) + Digest::SHA256.hexdigest(value) + end + + def self.original_filename(checksum) + "#{checksum}.diff" + end + end + + # The class is mimicking Vulnerabilites::Finding + class Finding < ApplicationRecord + self.table_name = 'vulnerability_occurrences' + + validates :details, json_schema: { filename: 'vulnerability_finding_details', draft: 7 }, if: false + end + + # The class is mimicking Vulnerabilites::FindingRemediation + class FindingRemediation < ApplicationRecord + self.table_name = 'vulnerability_findings_remediations' + end + + def perform + each_sub_batch do |sub_batch| + migrate_remediations(sub_batch) + end + end + + private + + def migrate_remediations(sub_batch) + sub_batch.each do |finding| + FindingRemediation.transaction do + remediations = append_remediations_diff_checksum(finding.raw_metadata) + + result_ids = create_remediations(finding, remediations) + + create_finding_remediations(finding.id, result_ids) + end + rescue StandardError => e + logger.error( + message: e.message, + class: self.class.name, + model_id: finding.id + ) + end + end + + def create_finding_remediations(finding_id, result_ids) + attrs = result_ids.map do |result_id| + build_finding_remediation_attrs(finding_id, result_id) + end + + return unless attrs.present? + + FindingRemediation.upsert_all( + attrs, + returning: false, + unique_by: [:vulnerability_occurrence_id, :vulnerability_remediation_id] + ) + end + + def create_remediations(finding, remediations) + attrs = remediations.map do |remediation| + build_remediation_attrs(finding, remediation) + end + + return [] unless attrs.present? + + ids_checksums = ::Vulnerabilities::Remediation.upsert_all( + attrs, + returning: %w[id checksum], + unique_by: [:project_id, :checksum] + ) + + ids_checksums.each do |id_checksum| + upload_file(id_checksum['id'], id_checksum['checksum'], remediations) + end + + ids_checksums.pluck('id') + end + + def upload_file(id, checksum, remediations) + deserialized_checksum = Gitlab::Database::ShaAttribute.new.deserialize(checksum) + diff = remediations.find { |rem| rem['checksum'] == deserialized_checksum }["diff"] + file = DiffFile.new(diff) + ::Vulnerabilities::Remediation.find_by(id: id).update!(file: file) + end + + def build_remediation_attrs(finding, remediation) + { + project_id: finding.project_id, + summary: remediation['summary'], + file: DiffFile.original_filename(remediation['checksum']), + checksum: remediation['checksum'], + created_at: Time.current, + updated_at: Time.current + } + end + + def build_finding_remediation_attrs(finding_id, remediation_id) + { + vulnerability_occurrence_id: finding_id, + vulnerability_remediation_id: remediation_id, + created_at: Time.current, + updated_at: Time.current + } + end + + def append_remediations_diff_checksum(metadata) + parsed_metadata = Gitlab::Json.parse(metadata) + + return [] unless parsed_metadata['remediations'] + + parsed_metadata['remediations'].filter_map do |remediation| + next unless remediation + + remediation.merge('checksum' => DiffFile.checksum(remediation['diff'])) + end.compact.uniq + end + + def logger + @logger ||= ::Gitlab::AppLogger + end + end + end +end diff --git a/lib/gitlab/ci/pipeline/duration.rb b/lib/gitlab/ci/pipeline/duration.rb index 15cc238fa84..11155280fdb 100644 --- a/lib/gitlab/ci/pipeline/duration.rb +++ b/lib/gitlab/ci/pipeline/duration.rb @@ -147,7 +147,6 @@ module Gitlab # rubocop: disable CodeReuse/ActiveRecord def self_and_downstreams_builds_of_pipeline(pipeline) ::Ci::Build - .unscoped # Will be removed in https://gitlab.com/gitlab-org/gitlab/-/issues/391186 .select(:id, :type, :started_at, :finished_at) .in_pipelines( pipeline.self_and_downstreams.select(:id) diff --git a/lib/gitlab/database/async_constraints.rb b/lib/gitlab/database/async_constraints.rb index c4e05a88430..6952115eca5 100644 --- a/lib/gitlab/database/async_constraints.rb +++ b/lib/gitlab/database/async_constraints.rb @@ -6,7 +6,7 @@ module Gitlab DEFAULT_ENTRIES_PER_INVOCATION = 2 def self.validate_pending_entries!(how_many: DEFAULT_ENTRIES_PER_INVOCATION) - PostgresAsyncConstraintValidation.ordered.limit(how_many).each do |record| + PostgresAsyncConstraintValidation.ordered.foreign_key_type.limit(how_many).each do |record| ForeignKeyValidator.new(record).perform end end diff --git a/lib/gitlab/database/async_constraints/migration_helpers.rb b/lib/gitlab/database/async_constraints/migration_helpers.rb index f51bb015c88..77ca78a4d5c 100644 --- a/lib/gitlab/database/async_constraints/migration_helpers.rb +++ b/lib/gitlab/database/async_constraints/migration_helpers.rb @@ -21,6 +21,7 @@ module Gitlab end async_validation = PostgresAsyncConstraintValidation + .foreign_key_type .find_or_create_by!(name: fk_name, table_name: table_name) Gitlab::AppLogger.info( @@ -39,6 +40,7 @@ module Gitlab fk_name = name || concurrent_foreign_key_name(table_name, column_name) PostgresAsyncConstraintValidation + .foreign_key_type .find_by(name: fk_name, table_name: table_name) .try(&:destroy!) end diff --git a/lib/gitlab/database/async_constraints/postgres_async_constraint_validation.rb b/lib/gitlab/database/async_constraints/postgres_async_constraint_validation.rb index ae996600f7c..3e71deb6855 100644 --- a/lib/gitlab/database/async_constraints/postgres_async_constraint_validation.rb +++ b/lib/gitlab/database/async_constraints/postgres_async_constraint_validation.rb @@ -14,7 +14,10 @@ module Gitlab validates :name, presence: true, uniqueness: { scope: :table_name }, length: { maximum: MAX_IDENTIFIER_LENGTH } validates :table_name, presence: true, length: { maximum: MAX_IDENTIFIER_LENGTH } + enum constraint_type: { foreign_key: 0 } + scope :ordered, -> { order(attempts: :asc, id: :asc) } + scope :foreign_key_type, -> { columns_hash.key?('constraint_type') ? foreign_key : all } def self.table_available? connection.table_exists?(table_name) diff --git a/lib/gitlab/import/import_failure_service.rb b/lib/gitlab/import/import_failure_service.rb index bebd64b29a9..2f6ffc4c844 100644 --- a/lib/gitlab/import/import_failure_service.rb +++ b/lib/gitlab/import/import_failure_service.rb @@ -52,8 +52,8 @@ module Gitlab track_exception persist_failure - track_metrics if metrics import_state.mark_as_failed(exception.message) if fail_import + track_metrics if metrics end private @@ -89,8 +89,13 @@ module Gitlab ) end + def import_metrics + @import_metrics ||= Gitlab::Import::Metrics.new("#{project.import_type}_importer", project) + end + def track_metrics - Gitlab::Import::Metrics.new("#{project.import_type}_importer", project).track_failed_import + import_metrics.track_failed_import + import_metrics.track_import_state end end end diff --git a/lib/gitlab/import/metrics.rb b/lib/gitlab/import/metrics.rb index 7a0cf1682a6..82f8cf8dbc6 100644 --- a/lib/gitlab/import/metrics.rb +++ b/lib/gitlab/import/metrics.rb @@ -26,6 +26,7 @@ module Gitlab observe_histogram projects_counter.increment track_finish_metric + track_import_state end def track_failed_import @@ -34,6 +35,18 @@ module Gitlab track_usage_event(:github_import_project_failure, project.id) end + def track_import_state + return unless project.github_import? + + Gitlab::Tracking.event( + importer, + 'create', + label: 'github_import_project_state', + project: project, + extra: { import_type: 'github', state: project.beautified_import_status_name } + ) + end + def issues_counter @issues_counter ||= Gitlab::Metrics.counter( :"#{importer}_imported_issues_total", diff --git a/lib/sidebars/projects/menus/deployments_menu.rb b/lib/sidebars/projects/menus/deployments_menu.rb index 4d4e65e9795..fa6c70cfd3d 100644 --- a/lib/sidebars/projects/menus/deployments_menu.rb +++ b/lib/sidebars/projects/menus/deployments_menu.rb @@ -34,6 +34,11 @@ module Sidebars 'deployments' end + override :serialize_as_menu_item_args + def serialize_as_menu_item_args + nil + end + private def feature_flags_menu_item @@ -44,6 +49,7 @@ module Sidebars ::Sidebars::MenuItem.new( title: _('Feature Flags'), link: project_feature_flags_path(context.project), + super_sidebar_parent: Sidebars::Projects::SuperSidebarMenus::OperationsMenu, active_routes: { controller: :feature_flags }, container_html_options: { class: 'shortcuts-feature-flags' }, item_id: :feature_flags @@ -58,6 +64,7 @@ module Sidebars ::Sidebars::MenuItem.new( title: _('Environments'), link: project_environments_path(context.project), + super_sidebar_parent: Sidebars::Projects::SuperSidebarMenus::OperationsMenu, active_routes: { controller: :environments }, container_html_options: { class: 'shortcuts-environments' }, item_id: :environments @@ -73,6 +80,7 @@ module Sidebars ::Sidebars::MenuItem.new( title: _('Releases'), link: project_releases_path(context.project), + super_sidebar_parent: Sidebars::Projects::SuperSidebarMenus::OperationsMenu, item_id: :releases, active_routes: { controller: :releases }, container_html_options: { class: 'shortcuts-deployments-releases' } @@ -87,6 +95,7 @@ module Sidebars ::Sidebars::MenuItem.new( title: _('Pages'), link: project_pages_path(context.project), + super_sidebar_parent: Sidebars::Projects::SuperSidebarMenus::OperationsMenu, active_routes: { path: 'pages#show' }, item_id: :pages ) diff --git a/lib/sidebars/projects/menus/infrastructure_menu.rb b/lib/sidebars/projects/menus/infrastructure_menu.rb index 390df0af1d7..701169e5cd3 100644 --- a/lib/sidebars/projects/menus/infrastructure_menu.rb +++ b/lib/sidebars/projects/menus/infrastructure_menu.rb @@ -32,6 +32,11 @@ module Sidebars 'cloud-gear' end + override :serialize_as_menu_item_args + def serialize_as_menu_item_args + nil + end + private def feature_enabled? @@ -46,6 +51,7 @@ module Sidebars ::Sidebars::MenuItem.new( title: _('Kubernetes clusters'), link: project_clusters_path(context.project), + super_sidebar_parent: Sidebars::Projects::SuperSidebarMenus::OperationsMenu, active_routes: { controller: [:cluster_agents, :clusters] }, container_html_options: { class: 'shortcuts-kubernetes' }, hint_html_options: kubernetes_hint_html_options, @@ -74,6 +80,7 @@ module Sidebars ::Sidebars::MenuItem.new( title: _('Terraform'), link: project_terraform_index_path(context.project), + super_sidebar_parent: Sidebars::Projects::SuperSidebarMenus::OperationsMenu, active_routes: { controller: :terraform }, item_id: :terraform ) @@ -95,6 +102,7 @@ module Sidebars ::Sidebars::MenuItem.new( title: _('Google Cloud'), link: project_google_cloud_configuration_path(context.project), + super_sidebar_parent: Sidebars::Projects::SuperSidebarMenus::OperationsMenu, active_routes: { controller: %w[ projects/google_cloud/configuration projects/google_cloud/service_accounts diff --git a/lib/sidebars/projects/menus/packages_registries_menu.rb b/lib/sidebars/projects/menus/packages_registries_menu.rb index fc7c564574a..d5b590a03aa 100644 --- a/lib/sidebars/projects/menus/packages_registries_menu.rb +++ b/lib/sidebars/projects/menus/packages_registries_menu.rb @@ -23,6 +23,11 @@ module Sidebars 'package' end + override :serialize_as_menu_item_args + def serialize_as_menu_item_args + nil + end + private def packages_registry_menu_item @@ -33,6 +38,7 @@ module Sidebars ::Sidebars::MenuItem.new( title: _('Package Registry'), link: project_packages_path(context.project), + super_sidebar_parent: Sidebars::Projects::SuperSidebarMenus::OperationsMenu, active_routes: { controller: :packages }, item_id: :packages_registry, container_html_options: { class: 'shortcuts-container-registry' } @@ -47,6 +53,7 @@ module Sidebars ::Sidebars::MenuItem.new( title: _('Container Registry'), link: project_container_registry_index_path(context.project), + super_sidebar_parent: Sidebars::Projects::SuperSidebarMenus::OperationsMenu, active_routes: { controller: 'projects/registry/repositories' }, item_id: :container_registry ) @@ -60,6 +67,7 @@ module Sidebars ::Sidebars::MenuItem.new( title: _('Infrastructure Registry'), link: project_infrastructure_registry_index_path(context.project), + super_sidebar_parent: Sidebars::Projects::SuperSidebarMenus::OperationsMenu, active_routes: { controller: :infrastructure_registry }, item_id: :infrastructure_registry ) @@ -75,6 +83,7 @@ module Sidebars ::Sidebars::MenuItem.new( title: _('Harbor Registry'), link: project_harbor_repositories_path(context.project), + super_sidebar_parent: Sidebars::Projects::SuperSidebarMenus::OperationsMenu, active_routes: { controller: :harbor_registry }, item_id: :harbor_registry ) diff --git a/lib/sidebars/projects/menus/snippets_menu.rb b/lib/sidebars/projects/menus/snippets_menu.rb index 060341b3c51..a1ba85e2b04 100644 --- a/lib/sidebars/projects/menus/snippets_menu.rb +++ b/lib/sidebars/projects/menus/snippets_menu.rb @@ -35,6 +35,14 @@ module Sidebars def active_routes { controller: :snippets } end + + override :serialize_as_menu_item_args + def serialize_as_menu_item_args + super.deep_merge({ + super_sidebar_parent: ::Sidebars::Projects::Menus::RepositoryMenu, + super_sidebar_before: :contributors + }) + end end end end diff --git a/lib/sidebars/projects/super_sidebar_menus/operations_menu.rb b/lib/sidebars/projects/super_sidebar_menus/operations_menu.rb new file mode 100644 index 00000000000..5490aac5a65 --- /dev/null +++ b/lib/sidebars/projects/super_sidebar_menus/operations_menu.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Sidebars + module Projects + module SuperSidebarMenus + class OperationsMenu < ::Sidebars::Menu + override :title + def title + _('Operations') + end + + override :sprite_icon + def sprite_icon + 'deployments' + end + end + end + end +end diff --git a/lib/sidebars/projects/super_sidebar_panel.rb b/lib/sidebars/projects/super_sidebar_panel.rb index f0ebea92525..2f2f3714cef 100644 --- a/lib/sidebars/projects/super_sidebar_panel.rb +++ b/lib/sidebars/projects/super_sidebar_panel.rb @@ -14,12 +14,11 @@ module Sidebars add_menu(Sidebars::StaticMenu.new(context)) add_menu(Sidebars::Projects::SuperSidebarMenus::PlanMenu.new(context)) + # "Develop" menu pick_from_old_menus(old_menus, Sidebars::Projects::Menus::RepositoryMenu) pick_from_old_menus(old_menus, Sidebars::Projects::Menus::CiCdMenu) pick_from_old_menus(old_menus, Sidebars::Projects::Menus::SecurityComplianceMenu) - pick_from_old_menus(old_menus, Sidebars::Projects::Menus::DeploymentsMenu) - pick_from_old_menus(old_menus, Sidebars::Projects::Menus::PackagesRegistriesMenu) - pick_from_old_menus(old_menus, Sidebars::Projects::Menus::InfrastructureMenu) + add_menu(Sidebars::Projects::SuperSidebarMenus::OperationsMenu.new(context)) pick_from_old_menus(old_menus, Sidebars::Projects::Menus::MonitorMenu) pick_from_old_menus(old_menus, Sidebars::Projects::Menus::AnalyticsMenu) add_menu(Sidebars::UncategorizedMenu.new(context)) diff --git a/qa/qa/specs/features/api/1_manage/user_inherited_access_spec.rb b/qa/qa/specs/features/api/1_manage/user_inherited_access_spec.rb index 2ae68fc64cf..124b6c9cd44 100644 --- a/qa/qa/specs/features/api/1_manage/user_inherited_access_spec.rb +++ b/qa/qa/specs/features/api/1_manage/user_inherited_access_spec.rb @@ -18,10 +18,7 @@ module QA end end - context 'when added to parent group', quarantine: { - issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/392816', - type: :investigating - } do + context 'when added to parent group' do let!(:parent_group_user) do Resource::User.fabricate_via_api! do |user| user.api_client = admin_api_client diff --git a/spec/frontend/diffs/components/app_spec.js b/spec/frontend/diffs/components/app_spec.js index 513e67ea247..3048a40809e 100644 --- a/spec/frontend/diffs/components/app_spec.js +++ b/spec/frontend/diffs/components/app_spec.js @@ -71,12 +71,6 @@ describe('diffs/components/app', () => { }, provide, store, - stubs: { - DynamicScroller: { - template: `<div><slot :item="$store.state.diffs.diffFiles[0]"></slot></div>`, - }, - DynamicScrollerItem: true, - }, }); } @@ -294,13 +288,13 @@ describe('diffs/components/app', () => { it('does not render empty state when diff files exist', () => { createComponent({}, ({ state }) => { - state.diffs.diffFiles.push({ - id: 1, - }); + state.diffs.diffFiles.push({ id: 1 }); }); expect(wrapper.findComponent(NoChanges).exists()).toBe(false); - expect(wrapper.findAllComponents(DiffFile).length).toBe(1); + expect(wrapper.findComponent({ name: 'DynamicScroller' }).props('items')).toBe( + store.state.diffs.diffFiles, + ); }); }); @@ -581,7 +575,10 @@ describe('diffs/components/app', () => { state.diffs.diffFiles.push({ sha: '123' }); }); - expect(wrapper.findComponent(DiffFile).exists()).toBe(true); + expect(wrapper.findComponent({ name: 'DynamicScroller' }).exists()).toBe(true); + expect(wrapper.findComponent({ name: 'DynamicScroller' }).props('items')).toBe( + store.state.diffs.diffFiles, + ); }); it("doesn't render tree list when no changes exist", () => { diff --git a/spec/frontend/diffs/store/actions_spec.js b/spec/frontend/diffs/store/actions_spec.js index 78765204322..614742d026f 100644 --- a/spec/frontend/diffs/store/actions_spec.js +++ b/spec/frontend/diffs/store/actions_spec.js @@ -236,13 +236,17 @@ describe('DiffsStoreActions', () => { it('should show no warning on any other status code', async () => { mock.onGet(endpointMetadata).reply(HTTP_STATUS_INTERNAL_SERVER_ERROR); - await testAction( - diffActions.fetchDiffFilesMeta, - {}, - { endpointMetadata, diffViewType: 'inline', showWhitespace: true }, - [{ type: types.SET_LOADING, payload: true }], - [], - ); + try { + await testAction( + diffActions.fetchDiffFilesMeta, + {}, + { endpointMetadata, diffViewType: 'inline', showWhitespace: true }, + [{ type: types.SET_LOADING, payload: true }], + [], + ); + } catch (error) { + expect(error.response.status).toBe(HTTP_STATUS_INTERNAL_SERVER_ERROR); + } expect(createAlert).not.toHaveBeenCalled(); }); diff --git a/spec/graphql/types/root_storage_statistics_type_spec.rb b/spec/graphql/types/root_storage_statistics_type_spec.rb index 07c8378e7a6..5dde6aa8b14 100644 --- a/spec/graphql/types/root_storage_statistics_type_spec.rb +++ b/spec/graphql/types/root_storage_statistics_type_spec.rb @@ -9,7 +9,7 @@ RSpec.describe GitlabSchema.types['RootStorageStatistics'] do expect(described_class).to have_graphql_fields(:storage_size, :repository_size, :lfs_objects_size, :build_artifacts_size, :packages_size, :wiki_size, :snippets_size, :pipeline_artifacts_size, :uploads_size, :dependency_proxy_size, - :container_registry_size) + :container_registry_size, :registry_size_estimated) end specify { expect(described_class).to require_graphql_authorizations(:read_statistics) } diff --git a/spec/lib/generators/gitlab/snowplow_event_definition_generator_spec.rb b/spec/lib/generators/gitlab/snowplow_event_definition_generator_spec.rb index d9fa6b931ad..4ccbf44af52 100644 --- a/spec/lib/generators/gitlab/snowplow_event_definition_generator_spec.rb +++ b/spec/lib/generators/gitlab/snowplow_event_definition_generator_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Gitlab::SnowplowEventDefinitionGenerator, :silence_stdout do +RSpec.describe Gitlab::SnowplowEventDefinitionGenerator, :silence_stdout, feature_category: :product_analytics do let(:ce_temp_dir) { Dir.mktmpdir } let(:ee_temp_dir) { Dir.mktmpdir } let(:timestamp) { Time.current.to_i } @@ -30,7 +30,8 @@ RSpec.describe Gitlab::SnowplowEventDefinitionGenerator, :silence_stdout do let(:file_name) { Dir.children(ce_temp_dir).first } it 'creates CE event definition file using the template' do - sample_event = ::Gitlab::Config::Loader::Yaml.new(fixture_file(File.join(sample_event_dir, 'sample_event.yml'))).load_raw! + sample_event = ::Gitlab::Config::Loader::Yaml + .new(fixture_file(File.join(sample_event_dir, 'sample_event.yml'))).load_raw! described_class.new([], generator_options).invoke_all @@ -62,25 +63,13 @@ RSpec.describe Gitlab::SnowplowEventDefinitionGenerator, :silence_stdout do end end - context 'event definition already exists' do + context 'when event definition with same file name already exists' do before do stub_const('Gitlab::VERSION', '12.11.0-pre') described_class.new([], generator_options).invoke_all end - it 'overwrites event definition --force flag set to true' do - sample_event = ::Gitlab::Config::Loader::Yaml.new(fixture_file(File.join(sample_event_dir, 'sample_event.yml'))).load_raw! - - stub_const('Gitlab::VERSION', '13.11.0-pre') - described_class.new([], generator_options.merge('force' => true)).invoke_all - - event_definition_path = File.join(ce_temp_dir, file_name) - event_data = ::Gitlab::Config::Loader::Yaml.new(File.read(event_definition_path)).load_raw! - - expect(event_data).to eq(sample_event) - end - - it 'raises error when --force flag set to false' do + it 'raises error' do expect { described_class.new([], generator_options.merge('force' => false)).invoke_all } .to raise_error(StandardError, /Event definition already exists at/) end @@ -90,7 +79,8 @@ RSpec.describe Gitlab::SnowplowEventDefinitionGenerator, :silence_stdout do let(:file_name) { Dir.children(ee_temp_dir).first } it 'creates EE event definition file using the template' do - sample_event = ::Gitlab::Config::Loader::Yaml.new(fixture_file(File.join(sample_event_dir, 'sample_event_ee.yml'))).load_raw! + sample_event = ::Gitlab::Config::Loader::Yaml + .new(fixture_file(File.join(sample_event_dir, 'sample_event_ee.yml'))).load_raw! described_class.new([], generator_options.merge('ee' => true)).invoke_all diff --git a/spec/lib/gitlab/background_migration/migrate_remediations_for_vulnerability_findings_spec.rb b/spec/lib/gitlab/background_migration/migrate_remediations_for_vulnerability_findings_spec.rb new file mode 100644 index 00000000000..84259505683 --- /dev/null +++ b/spec/lib/gitlab/background_migration/migrate_remediations_for_vulnerability_findings_spec.rb @@ -0,0 +1,161 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::BackgroundMigration::MigrateRemediationsForVulnerabilityFindings, + feature_category: :vulnerability_management do + let(:vulnerability_occurrences) { table(:vulnerability_occurrences) } + let(:vulnerability_findings_remediations) { table(:vulnerability_findings_remediations) } + let(:vulnerability_remediations) { table(:vulnerability_remediations) } + let(:remediation_hash) { { summary: 'summary', diff: "ZGlmZiAtLWdp" } } + let(:namespace1) { table(:namespaces).create!(name: 'namespace 1', path: 'namespace1') } + let(:project1) { table(:projects).create!(namespace_id: namespace1.id, project_namespace_id: namespace1.id) } + let(:user) { table(:users).create!(email: 'test1@example.com', projects_limit: 5) } + + let(:scanner1) do + table(:vulnerability_scanners).create!(project_id: project1.id, external_id: 'test 1', name: 'test scanner 1') + end + + let(:stating_id) { vulnerability_occurrences.pluck(:id).min } + let(:end_id) { vulnerability_occurrences.pluck(:id).max } + + let(:migration) do + described_class.new( + start_id: stating_id, + end_id: end_id, + batch_table: :vulnerability_occurrences, + batch_column: :id, + sub_batch_size: 2, + pause_ms: 2, + connection: ApplicationRecord.connection + ) + end + + subject(:perform_migration) { migration.perform } + + context 'without the presence of remediation key' do + before do + create_finding!(project1.id, scanner1.id, { other_keys: 'test' }) + end + + it 'does not create any remediation' do + expect(Gitlab::AppLogger).not_to receive(:error) + + expect { perform_migration }.not_to change { vulnerability_remediations.count } + end + end + + context 'with remediation equals to an array of nil element' do + before do + create_finding!(project1.id, scanner1.id, { remediations: [nil] }) + end + + it 'does not create any remediation' do + expect(Gitlab::AppLogger).not_to receive(:error) + + expect { perform_migration }.not_to change { vulnerability_remediations.count } + end + end + + context 'with remediation equals to an array of duplicated elements' do + let!(:finding) do + create_finding!(project1.id, scanner1.id, { remediations: [remediation_hash, remediation_hash] }) + end + + it 'creates new remediation' do + expect(Gitlab::AppLogger).not_to receive(:error) + + expect { perform_migration }.to change { vulnerability_remediations.count }.by(1) + expect(vulnerability_findings_remediations.where(vulnerability_occurrence_id: finding.id).length).to eq(1) + end + end + + context 'with existing remediations within raw_metadata' do + let!(:finding1) { create_finding!(project1.id, scanner1.id, { remediations: [remediation_hash] }) } + let!(:finding2) { create_finding!(project1.id, scanner1.id, { remediations: [remediation_hash] }) } + + it 'creates new remediation' do + expect(Gitlab::AppLogger).not_to receive(:error) + + expect { perform_migration }.to change { vulnerability_remediations.count }.by(1) + expect(vulnerability_findings_remediations.where(vulnerability_occurrence_id: finding1.id).length).to eq(1) + expect(vulnerability_findings_remediations.where(vulnerability_occurrence_id: finding2.id).length).to eq(1) + end + + context 'when create throws exception other than ActiveRecord::RecordNotUnique' do + before do + allow(migration).to receive(:create_finding_remediations).and_raise(StandardError) + end + + it 'rolls back all related transactions' do + expect(Gitlab::AppLogger).to receive(:error).with({ + class: described_class.name, message: StandardError.to_s, model_id: finding1.id + }) + expect(Gitlab::AppLogger).to receive(:error).with({ + class: described_class.name, message: StandardError.to_s, model_id: finding2.id + }) + expect { perform_migration }.not_to change { vulnerability_remediations.count } + expect(vulnerability_findings_remediations.where(vulnerability_occurrence_id: finding1.id).length).to eq(0) + expect(vulnerability_findings_remediations.where(vulnerability_occurrence_id: finding2.id).length).to eq(0) + end + end + end + + context 'with existing remediation records' do + let!(:finding) { create_finding!(project1.id, scanner1.id, { remediations: [remediation_hash] }) } + + before do + vulnerability_remediations.create!(project_id: project1.id, summary: remediation_hash[:summary], + checksum: checksum(remediation_hash[:diff]), file: Tempfile.new.path) + end + + it 'does not create new remediation' do + expect(Gitlab::AppLogger).not_to receive(:error) + + expect { perform_migration }.not_to change { vulnerability_remediations.count } + expect(vulnerability_findings_remediations.where(vulnerability_occurrence_id: finding.id).length).to eq(1) + end + end + + context 'with same raw_metadata for different projects' do + let(:namespace2) { table(:namespaces).create!(name: 'namespace 2', path: 'namespace2') } + let(:project2) { table(:projects).create!(namespace_id: namespace2.id, project_namespace_id: namespace2.id) } + let(:scanner2) do + table(:vulnerability_scanners).create!(project_id: project2.id, external_id: 'test 2', name: 'test scanner 2') + end + + let!(:finding1) { create_finding!(project1.id, scanner1.id, { remediations: [remediation_hash] }) } + let!(:finding2) { create_finding!(project2.id, scanner2.id, { remediations: [remediation_hash] }) } + + it 'creates new remediation for each project' do + expect(Gitlab::AppLogger).not_to receive(:error) + + expect { perform_migration }.to change { vulnerability_remediations.count }.by(2) + expect(vulnerability_findings_remediations.where(vulnerability_occurrence_id: finding1.id).length).to eq(1) + expect(vulnerability_findings_remediations.where(vulnerability_occurrence_id: finding2.id).length).to eq(1) + end + end + + private + + def create_finding!(project_id, scanner_id, raw_metadata) + vulnerability = table(:vulnerabilities).create!(project_id: project_id, author_id: user.id, title: 'test', + severity: 4, confidence: 4, report_type: 0) + + identifier = table(:vulnerability_identifiers).create!(project_id: project_id, external_type: 'uuid-v5', + external_id: 'uuid-v5', fingerprint: OpenSSL::Digest::SHA256.hexdigest(vulnerability.id.to_s), + name: 'Identifier for UUIDv5 2 2') + + table(:vulnerability_occurrences).create!( + vulnerability_id: vulnerability.id, project_id: project_id, scanner_id: scanner_id, + primary_identifier_id: identifier.id, name: 'test', severity: 4, confidence: 4, report_type: 0, + uuid: SecureRandom.uuid, project_fingerprint: '123qweasdzxc', location: { "image" => "alpine:3.4" }, + location_fingerprint: 'test', metadata_version: 'test', + raw_metadata: raw_metadata.to_json) + end + + def checksum(value) + sha = Digest::SHA256.hexdigest(value) + Gitlab::Database::ShaAttribute.new.serialize(sha) + end +end diff --git a/spec/lib/gitlab/database/async_constraints/migration_helpers_spec.rb b/spec/lib/gitlab/database/async_constraints/migration_helpers_spec.rb index ab06c7c7e82..08d255a4bb8 100644 --- a/spec/lib/gitlab/database/async_constraints/migration_helpers_spec.rb +++ b/spec/lib/gitlab/database/async_constraints/migration_helpers_spec.rb @@ -33,6 +33,7 @@ RSpec.describe Gitlab::Database::AsyncConstraints::MigrationHelpers, feature_cat record = constraint_model.find_by(table_name: table_name) expect(record.name).to start_with('fk_') + expect(record).to be_foreign_key end context 'when an explicit name is given' do @@ -46,6 +47,7 @@ RSpec.describe Gitlab::Database::AsyncConstraints::MigrationHelpers, feature_cat record = constraint_model.find_by(name: fk_name) expect(record.table_name).to eq(table_name) + expect(record).to be_foreign_key end end @@ -81,33 +83,50 @@ RSpec.describe Gitlab::Database::AsyncConstraints::MigrationHelpers, feature_cat end describe '#unprepare_async_foreign_key_validation' do - before do - migration.prepare_async_foreign_key_validation(table_name, column_name, name: fk_name) - end - - it 'destroys the record' do - expect do - migration.unprepare_async_foreign_key_validation(table_name, column_name) - end.to change { constraint_model.where(table_name: table_name).count }.by(-1) - end - - context 'when an explicit name is given' do - let(:fk_name) { 'my_test_async_fk' } + context 'with foreign keys' do + before do + migration.prepare_async_foreign_key_validation(table_name, column_name, name: fk_name) + end it 'destroys the record' do expect do - migration.unprepare_async_foreign_key_validation(table_name, name: fk_name) - end.to change { constraint_model.where(name: fk_name).count }.by(-1) + migration.unprepare_async_foreign_key_validation(table_name, column_name) + end.to change { constraint_model.where(table_name: table_name).count }.by(-1) + end + + context 'when an explicit name is given' do + let(:fk_name) { 'my_test_async_fk' } + + it 'destroys the record' do + expect do + migration.unprepare_async_foreign_key_validation(table_name, name: fk_name) + end.to change { constraint_model.where(name: fk_name).count }.by(-1) + end + end + + context 'when the async fk validation table does not exist' do + it 'does not raise an error' do + connection.drop_table(constraint_model.table_name) + + expect(constraint_model).not_to receive(:find_by) + + expect { migration.unprepare_async_foreign_key_validation(table_name, column_name) }.not_to raise_error + end end end - context 'when the async fk validation table does not exist' do - it 'does not raise an error' do - connection.drop_table(constraint_model.table_name) + context 'with other types of constraints' do + let(:name) { 'my_test_async_constraint' } + let(:constraint) { create(:postgres_async_constraint_validation, table_name: table_name, name: name) } - expect(constraint_model).not_to receive(:find_by) + it 'does not destroy the record' do + constraint.update_column(:constraint_type, 99) + + expect do + migration.unprepare_async_foreign_key_validation(table_name, name: name) + end.not_to change { constraint_model.where(name: name).count } - expect { migration.unprepare_async_foreign_key_validation(table_name, column_name) }.not_to raise_error + expect(constraint).to be_present end end end diff --git a/spec/lib/gitlab/database/async_constraints/postgres_async_constraint_validation_spec.rb b/spec/lib/gitlab/database/async_constraints/postgres_async_constraint_validation_spec.rb index a8e136dd22c..7fea2eb1bcc 100644 --- a/spec/lib/gitlab/database/async_constraints/postgres_async_constraint_validation_spec.rb +++ b/spec/lib/gitlab/database/async_constraints/postgres_async_constraint_validation_spec.rb @@ -30,6 +30,24 @@ RSpec.describe Gitlab::Database::AsyncConstraints::PostgresAsyncConstraintValida it { is_expected.to eq([new_validation, failed_validation]) } end + + describe '.foreign_key_type' do + before do + new_validation.update_column(:constraint_type, 99) + end + + subject { described_class.foreign_key_type } + + it { is_expected.to eq([failed_validation]) } + + it 'does not apply the filter if the column is not present' do + expect(described_class).to receive(:columns_hash).and_wrap_original do |method| + method.call.except('constraint_type') + end + + is_expected.to match_array([failed_validation, new_validation]) + end + end end describe '.table_available?' do diff --git a/spec/lib/gitlab/import/import_failure_service_spec.rb b/spec/lib/gitlab/import/import_failure_service_spec.rb index eb71b307b8d..fc02c6bd4ca 100644 --- a/spec/lib/gitlab/import/import_failure_service_spec.rb +++ b/spec/lib/gitlab/import/import_failure_service_spec.rb @@ -141,6 +141,7 @@ RSpec.describe Gitlab::Import::ImportFailureService, :aggregate_failures do .with("#{project.import_type}_importer", project) .and_return(metrics_double) expect(metrics_double).to receive(:track_failed_import) + expect(metrics_double).to receive(:track_import_state) service.execute end diff --git a/spec/lib/gitlab/import/metrics_spec.rb b/spec/lib/gitlab/import/metrics_spec.rb index 9b8b58d00f3..f0140a975b1 100644 --- a/spec/lib/gitlab/import/metrics_spec.rb +++ b/spec/lib/gitlab/import/metrics_spec.rb @@ -60,6 +60,41 @@ RSpec.describe Gitlab::Import::Metrics, :aggregate_failures do end end + describe '#track_import_state' do + context 'when project is not a github import' do + it 'does not emit importer metrics' do + subject.track_import_state + + expect_no_snowplow_event( + category: :test_importer, + action: 'create', + label: 'github_import_project_state', + project: project, + extra: { import_type: 'github', state: 'failed' } + ) + end + end + + context 'when project is a github import' do + before do + project.import_type = 'github' + allow(project).to receive(:import_status).and_return('failed') + end + + it 'emits importer metrics' do + subject.track_import_state + + expect_snowplow_event( + category: :test_importer, + action: 'create', + label: 'github_import_project_state', + project: project, + extra: { import_type: 'github', state: 'failed' } + ) + end + end + end + describe '#track_finished_import' do before do allow(Gitlab::Metrics).to receive(:histogram) { histogram } @@ -92,6 +127,33 @@ RSpec.describe Gitlab::Import::Metrics, :aggregate_failures do subject.track_finished_import expect(histogram).to have_received(:observe).with({ importer: :test_importer }, anything) + expect_no_snowplow_event( + category: :test_importer, + action: 'create', + label: 'github_import_project_state', + project: project, + extra: { import_type: 'github', state: 'completed' } + ) + end + end + + context 'when project is a github import' do + before do + project.import_type = 'github' + allow(project).to receive(:import_status).and_return('finished') + allow(project).to receive(:import_finished?).and_return(true) + end + + it 'emits snowplow metrics' do + subject.track_finished_import + + expect_snowplow_event( + category: :test_importer, + action: 'create', + label: 'github_import_project_state', + project: project, + extra: { import_type: 'github', state: 'completed' } + ) end end end diff --git a/spec/lib/sidebars/projects/super_sidebar_menus/operations_menu_spec.rb b/spec/lib/sidebars/projects/super_sidebar_menus/operations_menu_spec.rb new file mode 100644 index 00000000000..df3f7e6cdab --- /dev/null +++ b/spec/lib/sidebars/projects/super_sidebar_menus/operations_menu_spec.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Sidebars::Projects::SuperSidebarMenus::OperationsMenu, feature_category: :navigation do + subject { described_class.new({}) } + + it 'has title and sprite_icon' do + expect(subject.title).to eq(_("Operations")) + expect(subject.sprite_icon).to eq("deployments") + end +end diff --git a/spec/lib/sidebars/projects/super_sidebar_panel_spec.rb b/spec/lib/sidebars/projects/super_sidebar_panel_spec.rb index a4df46ca493..d6fc3fd8fe1 100644 --- a/spec/lib/sidebars/projects/super_sidebar_panel_spec.rb +++ b/spec/lib/sidebars/projects/super_sidebar_panel_spec.rb @@ -30,9 +30,7 @@ RSpec.describe Sidebars::Projects::SuperSidebarPanel, feature_category: :navigat Sidebars::Projects::Menus::RepositoryMenu, Sidebars::Projects::Menus::CiCdMenu, Sidebars::Projects::Menus::SecurityComplianceMenu, - Sidebars::Projects::Menus::DeploymentsMenu, - Sidebars::Projects::Menus::PackagesRegistriesMenu, - Sidebars::Projects::Menus::InfrastructureMenu, + Sidebars::Projects::SuperSidebarMenus::OperationsMenu, Sidebars::Projects::Menus::MonitorMenu, Sidebars::Projects::Menus::AnalyticsMenu, Sidebars::UncategorizedMenu, @@ -41,7 +39,7 @@ RSpec.describe Sidebars::Projects::SuperSidebarPanel, feature_category: :navigat end it "is exposed as a renderable menu" do - expect(subject.renderable_menus.map(&:class)).to eq(category_menu) + expect(subject.instance_variable_get(:@menus).map(&:class)).to eq(category_menu) end end end diff --git a/spec/migrations/20230118144623_schedule_migration_for_remediation_spec.rb b/spec/migrations/20230118144623_schedule_migration_for_remediation_spec.rb new file mode 100644 index 00000000000..d22aeeaa254 --- /dev/null +++ b/spec/migrations/20230118144623_schedule_migration_for_remediation_spec.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_migration! + +RSpec.describe ScheduleMigrationForRemediation, :migration, feature_category: :vulnerability_management do + let(:migration) { described_class::MIGRATION } + + describe '#up' do + it 'schedules a batched background migration' do + migrate! + + expect(migration).to have_scheduled_batched_migration( + table_name: :vulnerability_occurrences, + column_name: :id, + interval: described_class::DELAY_INTERVAL, + batch_size: described_class::BATCH_SIZE, + sub_batch_size: described_class::SUB_BATCH_SIZE + ) + end + end + + describe '#down' do + it 'deletes all batched migration records' do + migrate! + schema_migrate_down! + + expect(migration).not_to have_scheduled_batched_migration + end + end +end diff --git a/spec/migrations/20230125195503_queue_backfill_compliance_violations_spec.rb b/spec/migrations/20230125195503_queue_backfill_compliance_violations_spec.rb new file mode 100644 index 00000000000..a70f9820855 --- /dev/null +++ b/spec/migrations/20230125195503_queue_backfill_compliance_violations_spec.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_migration! + +RSpec.describe QueueBackfillComplianceViolations, feature_category: :compliance_management do + let(:migration) { described_class::MIGRATION } + + describe '#up' do + it 'schedules background jobs for each batch of merge_request_compliance_violations' do + migrate! + + expect(migration).to( + have_scheduled_batched_migration( + table_name: :merge_requests_compliance_violations, + column_name: :id, + interval: described_class::INTERVAL, + batch_size: described_class::BATCH_SIZE + ) + ) + end + end + + describe '#down' do + it 'deletes all batched migration records' do + migrate! + schema_migrate_down! + + expect(migration).not_to have_scheduled_batched_migration + end + end +end diff --git a/spec/models/preloaders/user_max_access_level_in_groups_preloader_spec.rb b/spec/models/preloaders/user_max_access_level_in_groups_preloader_spec.rb index 7d04817b621..3fba2ac003b 100644 --- a/spec/models/preloaders/user_max_access_level_in_groups_preloader_spec.rb +++ b/spec/models/preloaders/user_max_access_level_in_groups_preloader_spec.rb @@ -66,22 +66,6 @@ RSpec.describe Preloaders::UserMaxAccessLevelInGroupsPreloader do create(:group_group_link, :guest, shared_with_group: group1, shared_group: group4) end - context 'when `include_memberships_from_group_shares_in_preloader` feature flag is disabled' do - before do - stub_feature_flags(include_memberships_from_group_shares_in_preloader: false) - end - - it 'sets access_level to `NO_ACCESS` in cache for groups arising from group shares' do - described_class.new(groups, user).execute - - groups.each do |group| - cached_access_level = group.max_member_access_for_user(user) - - expect(cached_access_level).to eq(Gitlab::Access::NO_ACCESS) - end - end - end - it 'sets the right access level in cache for groups arising from group shares' do described_class.new(groups, user).execute diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index e0d46bab1f4..7d431de9f07 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -3594,6 +3594,44 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do end end + describe '#beautified_import_status_name' do + context 'when import not finished' do + it 'returns the right beautified import status' do + project = create(:project, :import_started) + + expect(project.beautified_import_status_name).to eq('started') + end + end + + context 'when import is finished' do + context 'when import is partially completed' do + it 'returns partially completed' do + project = create(:project) + + create(:import_state, project: project, status: 'finished', checksums: { + 'fetched' => { 'labels' => 10 }, + 'imported' => { 'labels' => 9 } + }) + + expect(project.beautified_import_status_name).to eq('partially completed') + end + end + + context 'when import is fully completed' do + it 'returns completed' do + project = create(:project) + + create(:import_state, project: project, status: 'finished', checksums: { + 'fetched' => { 'labels' => 10 }, + 'imported' => { 'labels' => 10 } + }) + + expect(project.beautified_import_status_name).to eq('completed') + end + end + end + end + describe '#add_import_job' do let(:import_jid) { '123' } diff --git a/spec/requests/projects/issues_controller_spec.rb b/spec/requests/projects/issues_controller_spec.rb index 2b9ff442f76..29ea20210c7 100644 --- a/spec/requests/projects/issues_controller_spec.rb +++ b/spec/requests/projects/issues_controller_spec.rb @@ -39,6 +39,11 @@ RSpec.describe Projects::IssuesController, feature_category: :team_planning do describe 'incident tabs' do let_it_be(:incident) { create(:incident, project: project) } + it 'redirects to the issues route for non-incidents' do + get incident_issue_project_issue_path(project, issue, 'timeline') + expect(response).to redirect_to project_issue_path(project, issue) + end + it 'responds with selected tab for incidents' do get incident_issue_project_issue_path(project, incident, 'timeline') expect(response.body).to match(/"currentTab":"timeline"/) diff --git a/spec/services/import/github/cancel_project_import_service_spec.rb b/spec/services/import/github/cancel_project_import_service_spec.rb index 77b8771ee65..114265315b0 100644 --- a/spec/services/import/github/cancel_project_import_service_spec.rb +++ b/spec/services/import/github/cancel_project_import_service_spec.rb @@ -14,6 +14,18 @@ RSpec.describe Import::Github::CancelProjectImportService do it 'update import state to be canceled' do expect(import_cancel.execute).to eq({ status: :success, project: project }) end + + it 'tracks canceled imports' do + metrics_double = instance_double('Gitlab::Import::Metrics') + + expect(Gitlab::Import::Metrics) + .to receive(:new) + .with(:github_importer, project) + .and_return(metrics_double) + expect(metrics_double).to receive(:track_import_state) + + import_cancel.execute + end end context 'when import is finished' do diff --git a/workhorse/go.mod b/workhorse/go.mod index 6e3f47b9afc..a14b3fd51be 100644 --- a/workhorse/go.mod +++ b/workhorse/go.mod @@ -26,7 +26,7 @@ require ( github.com/sirupsen/logrus v1.9.0 github.com/smartystreets/goconvey v1.7.2 github.com/stretchr/testify v1.8.1 - gitlab.com/gitlab-org/gitaly/v15 v15.9.0-rc43 + gitlab.com/gitlab-org/gitaly/v15 v15.9.0 gitlab.com/gitlab-org/golang-archive-zip v0.1.1 gitlab.com/gitlab-org/labkit v1.17.0 gocloud.dev v0.28.0 diff --git a/workhorse/go.sum b/workhorse/go.sum index 2240d85273e..dfd6f45f5a6 100644 --- a/workhorse/go.sum +++ b/workhorse/go.sum @@ -1847,8 +1847,8 @@ github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= -gitlab.com/gitlab-org/gitaly/v15 v15.9.0-rc43 h1:yHDCgmgeCC+R1F40HleRpPSPe3MVCmS8okvolxZ1Ack= -gitlab.com/gitlab-org/gitaly/v15 v15.9.0-rc43/go.mod h1:MLAmjPsXan0TixWBOnF2GUTjHcNLoAiYv1x1LRx7gHQ= +gitlab.com/gitlab-org/gitaly/v15 v15.9.0 h1:4kBEi+hyjTamWwbvnPMZTYuP4wnL8bwPXDFyDqTGuBc= +gitlab.com/gitlab-org/gitaly/v15 v15.9.0/go.mod h1:MLAmjPsXan0TixWBOnF2GUTjHcNLoAiYv1x1LRx7gHQ= gitlab.com/gitlab-org/golang-archive-zip v0.1.1 h1:35k9giivbxwF03+8A05Cm8YoxoakU8FBCj5gysjCTCE= gitlab.com/gitlab-org/golang-archive-zip v0.1.1/go.mod h1:ZDtqpWPGPB9qBuZnZDrKQjIdJtkN7ZAoVwhT6H2o2kE= gitlab.com/gitlab-org/labkit v1.17.0 h1:mEkoLzXorLNdt8NkfgYS5xMDhdqCsIJaeEVtSf7d8cU= |