diff options
78 files changed, 871 insertions, 549 deletions
diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml index 7f0de878744..5d551c2564b 100644 --- a/.gitlab/ci/rules.gitlab-ci.yml +++ b/.gitlab/ci/rules.gitlab-ci.yml @@ -1465,24 +1465,6 @@ - <<: *if-merge-request changes: *static-analysis-patterns -.semgrep-appsec-custom-rules:rules: - rules: - - <<: *if-not-ee - when: never - - <<: *if-merge-request - changes: *code-backstage-qa-patterns - -.ping-appsec-for-sast-findings:rules: - rules: - # Requiring $CUSTOM_SAST_RULES_BOT_PAT prevents the bot from running on forks or CE - # Without it the script would fail too. - - if: "$CUSTOM_SAST_RULES_BOT_PAT == null" - when: never - - <<: *if-not-ee - when: never - - <<: *if-merge-request - changes: *code-backstage-qa-patterns - ####################### # Vendored gems rules # ####################### @@ -1569,6 +1551,7 @@ - '**/*.tsx' - '**/*.c' - '**/*.go' + - '**/*.rb' .reports:rules:secret_detection: rules: diff --git a/.gitlab/ci/static-analysis.gitlab-ci.yml b/.gitlab/ci/static-analysis.gitlab-ci.yml index cb3a9706a18..b4efd9e49bf 100644 --- a/.gitlab/ci/static-analysis.gitlab-ci.yml +++ b/.gitlab/ci/static-analysis.gitlab-ci.yml @@ -7,7 +7,6 @@ variables: SETUP_DB: "false" ENABLE_SPRING: "1" - SKIP_LOG_INITIALIZER_CONNECTIONS: "1" # Disable warnings in browserslist which can break on backports # https://github.com/browserslist/browserslist/blob/a287ec6/node.js#L367-L384 BROWSERSLIST_IGNORE_OLD_DATA: "true" @@ -160,39 +159,3 @@ feature-flags-usage: when: always paths: - tmp/feature_flags/ - -semgrep-appsec-custom-rules: - stage: lint - extends: - - .semgrep-appsec-custom-rules:rules - image: returntocorp/semgrep - needs: [] - script: - # Required to avoid a timeout https://github.com/returntocorp/semgrep/issues/5395 - - git fetch origin master - # Include/exclude list isn't ideal https://github.com/returntocorp/semgrep/issues/5399 - - | - semgrep ci --gitlab-sast --metrics off --config $CUSTOM_RULES_URL \ - --include app --include lib --include workhorse \ - --exclude '*_test.go' --exclude spec --exclude qa > gl-sast-report.json || true - variables: - CUSTOM_RULES_URL: https://gitlab.com/gitlab-com/gl-security/appsec/sast-custom-rules/-/raw/main/appsec-pings/rules.yml - artifacts: - paths: - - gl-sast-report.json - reports: - sast: gl-sast-report.json - -ping-appsec-for-sast-findings: - stage: lint - image: alpine:latest - extends: - - .ping-appsec-for-sast-findings:rules - variables: - # Project Access Token bot ID for /gitlab-com/gl-security/appsec/sast-custom-rules - BOT_USER_ID: 11727358 - needs: - - semgrep-appsec-custom-rules - script: - - apk add jq curl - - scripts/process_custom_semgrep_results.sh diff --git a/.gitlab/sast-ruleset.toml b/.gitlab/sast-ruleset.toml new file mode 100644 index 00000000000..6bfb4618b73 --- /dev/null +++ b/.gitlab/sast-ruleset.toml @@ -0,0 +1,10 @@ +[semgrep] + description = 'semgrep custom rules configuration' + targetdir = "/sgrules" + validate = true + + [[semgrep.passthrough]] + type = "git" + value = "https://gitlab.com/gitlab-com/gl-security/appsec/sast-custom-rules.git" + ref = "refs/heads/main" + subdir = "appsec-pings" diff --git a/app/assets/javascripts/work_items/components/work_item_assignees.vue b/app/assets/javascripts/work_items/components/work_item_assignees.vue index 5349b40da8e..9ff424aa20f 100644 --- a/app/assets/javascripts/work_items/components/work_item_assignees.vue +++ b/app/assets/javascripts/work_items/components/work_item_assignees.vue @@ -15,10 +15,11 @@ import currentUserQuery from '~/graphql_shared/queries/current_user.query.graphq import userSearchQuery from '~/graphql_shared/queries/users_search.query.graphql'; import InviteMembersTrigger from '~/invite_members/components/invite_members_trigger.vue'; import { n__, s__ } from '~/locale'; +import Tracking from '~/tracking'; import SidebarParticipant from '~/sidebar/components/assignees/sidebar_participant.vue'; import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants'; import localUpdateWorkItemMutation from '../graphql/local_update_work_item.mutation.graphql'; -import { i18n } from '../constants'; +import { i18n, TRACKING_CATEGORY_SHOW } from '../constants'; function isTokenSelectorElement(el) { return el?.classList.contains('gl-token-close') || el?.classList.contains('dropdown-item'); @@ -44,6 +45,7 @@ export default { GlDropdownItem, GlDropdownDivider, }, + mixins: [Tracking.mixin()], inject: ['fullPath'], props: { workItemId: { @@ -58,6 +60,15 @@ export default { type: Boolean, required: true, }, + workItemType: { + type: String, + required: true, + }, + canUpdate: { + type: Boolean, + required: false, + default: false, + }, }, data() { return { @@ -95,6 +106,13 @@ export default { }, }, computed: { + tracking() { + return { + category: TRACKING_CATEGORY_SHOW, + label: 'item_assignees', + property: `type_${this.workItemType}`, + }; + }, assigneeListEmpty() { return this.assignees.length === 0; }, @@ -163,6 +181,7 @@ export default { }, }, }); + this.track('updated_assignees'); }, handleFocus() { this.isEditing = true; @@ -208,9 +227,11 @@ export default { ref="tokenSelector" :selected-tokens="localAssignees" :container-class="containerClass" - class="assignees-selector gl-flex-grow-1 gl-border gl-border-white gl-hover-border-gray-200 gl-rounded-base col-9 gl-align-self-start gl-px-0!" + class="assignees-selector gl-flex-grow-1 gl-border gl-border-white gl-rounded-base col-9 gl-align-self-start gl-px-0!" + :class="{ 'gl-hover-border-gray-200': canUpdate }" :dropdown-items="dropdownItems" :loading="isLoadingUsers" + :view-only="!canUpdate" @input="handleAssigneesInput" @text-input="debouncedSearchKeyUpdate" @focus="handleFocus" diff --git a/app/assets/javascripts/work_items/components/work_item_detail.vue b/app/assets/javascripts/work_items/components/work_item_detail.vue index 7314b0afc54..ad90fe88947 100644 --- a/app/assets/javascripts/work_items/components/work_item_detail.vue +++ b/app/assets/javascripts/work_items/components/work_item_detail.vue @@ -216,9 +216,11 @@ export default { <template v-if="workItemsMvc2Enabled"> <work-item-assignees v-if="workItemAssignees" + :can-update="canUpdate" :work-item-id="workItem.id" :assignees="workItemAssignees.assignees.nodes" :allows-multiple-assignees="workItemAssignees.allowsMultipleAssignees" + :work-item-type="workItemType" @error="error = $event" /> <work-item-labels diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss index 04e0ef6631e..c0a283ec643 100644 --- a/app/assets/stylesheets/pages/issues.scss +++ b/app/assets/stylesheets/pages/issues.scss @@ -124,8 +124,16 @@ ul.related-merge-requests > li gl-emoji { .new-branch-col { .discussion-filter-container { - &:not(:only-child) { - margin-right: $gl-padding-8; + &:not(:last-child) { + margin-right: $gl-spacing-scale-3; + } + } + + @include media-breakpoint-down(xs) { + width: 100%; + + > div:not(:last-child) { + margin-bottom: $gl-spacing-scale-3; } } } @@ -147,6 +155,16 @@ ul.related-merge-requests > li gl-emoji { .btn-group:not(.hidden) { display: flex; + + @include media-breakpoint-down(xs) { + .btn.btn-confirm { + @include gl-justify-content-start; + + &.dropdown-toggle { + @include gl-flex-grow-0; + } + } + } } .js-create-merge-request { diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb index 6b5fb266fd8..7a7e63f5fc4 100644 --- a/app/controllers/search_controller.rb +++ b/app/controllers/search_controller.rb @@ -42,13 +42,19 @@ class SearchController < ApplicationController @sort = params[:sort] || default_sort @search_service = Gitlab::View::Presenter::Factory.new(search_service, current_user: current_user).fabricate! - @scope = @search_service.scope - @without_count = @search_service.without_count? - @show_snippets = @search_service.show_snippets? - @search_results = @search_service.search_results - @search_objects = @search_service.search_objects - @search_highlight = @search_service.search_highlight - @aggregations = @search_service.search_aggregations + + @search_level = @search_service.level + @search_type = search_type + + @global_search_duration_s = Benchmark.realtime do + @scope = @search_service.scope + @without_count = @search_service.without_count? + @show_snippets = @search_service.show_snippets? + @search_results = @search_service.search_results + @search_objects = @search_service.search_objects + @search_highlight = @search_service.search_highlight + @aggregations = @search_service.search_aggregations + end increment_search_counters end @@ -144,7 +150,9 @@ class SearchController < ApplicationController payload[:metadata]['meta.search.filters.state'] = params[:state] payload[:metadata]['meta.search.force_search_results'] = params[:force_search_results] payload[:metadata]['meta.search.project_ids'] = params[:project_ids] - payload[:metadata]['meta.search.search_level'] = params[:search_level] + payload[:metadata]['meta.search.type'] = @search_type if @search_type.present? + payload[:metadata]['meta.search.level'] = @search_level if @search_level.present? + payload[:metadata][:global_search_duration_s] = @global_search_duration_s if @global_search_duration_s.present? if search_service.abuse_detected? payload[:metadata]['abuse.confidence'] = Gitlab::Abuse.confidence(:certain) @@ -207,6 +215,10 @@ class SearchController < ApplicationController def tracking_namespace_source search_service.project&.namespace || search_service.group end + + def search_type + 'basic' + end end SearchController.prepend_mod_with('SearchController') diff --git a/app/graphql/mutations/concerns/mutations/work_items/widgetable.rb b/app/graphql/mutations/concerns/mutations/work_items/widgetable.rb index 22f782514c9..445b2eb6441 100644 --- a/app/graphql/mutations/concerns/mutations/work_items/widgetable.rb +++ b/app/graphql/mutations/concerns/mutations/work_items/widgetable.rb @@ -5,11 +5,17 @@ module Mutations module Widgetable extend ActiveSupport::Concern - def extract_widget_params(work_item_type, attributes) + def extract_widget_params!(work_item_type, attributes) # Get the list of widgets for the work item's type to extract only the supported attributes - widget_keys = work_item_type.widgets.map(&:api_symbol) + widget_keys = ::WorkItems::Type.available_widgets.map(&:api_symbol) widget_params = attributes.extract!(*widget_keys) + not_supported_keys = widget_params.keys - work_item_type.widgets.map(&:api_symbol) + if not_supported_keys.present? + raise Gitlab::Graphql::Errors::ArgumentError, + "Following widget keys are not supported by #{work_item_type.name} type: #{not_supported_keys}" + end + # Cannot use prepare to use `.to_h` on each input due to # https://gitlab.com/gitlab-org/gitlab/-/merge_requests/87472#note_945199865 widget_params.transform_values { |values| values.to_h } diff --git a/app/graphql/mutations/work_items/create.rb b/app/graphql/mutations/work_items/create.rb index dcd7d58737a..350153eaf19 100644 --- a/app/graphql/mutations/work_items/create.rb +++ b/app/graphql/mutations/work_items/create.rb @@ -43,7 +43,7 @@ module Mutations spam_params = ::Spam::SpamParams.new_from_request(request: context[:request]) params = global_id_compatibility_params(attributes).merge(author_id: current_user.id) type = ::WorkItems::Type.find(attributes[:work_item_type_id]) - widget_params = extract_widget_params(type, params) + widget_params = extract_widget_params!(type, params) create_result = ::WorkItems::CreateService.new( project: project, diff --git a/app/graphql/mutations/work_items/update.rb b/app/graphql/mutations/work_items/update.rb index c9f733223b5..5d8c574877a 100644 --- a/app/graphql/mutations/work_items/update.rb +++ b/app/graphql/mutations/work_items/update.rb @@ -25,7 +25,7 @@ module Mutations end spam_params = ::Spam::SpamParams.new_from_request(request: context[:request]) - widget_params = extract_widget_params(work_item.work_item_type, attributes) + widget_params = extract_widget_params!(work_item.work_item_type, attributes) update_result = ::WorkItems::UpdateService.new( project: work_item.project, diff --git a/app/graphql/types/work_items/widgets/hierarchy_update_input_type.rb b/app/graphql/types/work_items/widgets/hierarchy_update_input_type.rb index 1c0833d1e6c..e1a9ebb76e9 100644 --- a/app/graphql/types/work_items/widgets/hierarchy_update_input_type.rb +++ b/app/graphql/types/work_items/widgets/hierarchy_update_input_type.rb @@ -14,7 +14,8 @@ module Types argument :children_ids, [::Types::GlobalIDType[::WorkItem]], required: false, description: 'Global IDs of children work items.', - prepare: ->(ids, _) { ids.map(&:model_id) } + loads: ::Types::WorkItemType, + as: :children end end end diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index 1c01a81ee41..791bae17271 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -1064,7 +1064,11 @@ module Ci end def has_reports?(reports_scope) - complete? && latest_report_builds(reports_scope).exists? + if Feature.enabled?(:mr_show_reports_immediately, project, type: :development) + latest_report_builds(reports_scope).exists? + else + complete? && latest_report_builds(reports_scope).exists? + end end def has_coverage_reports? diff --git a/app/models/namespace.rb b/app/models/namespace.rb index 8517ec2e6d0..f23a859b119 100644 --- a/app/models/namespace.rb +++ b/app/models/namespace.rb @@ -343,6 +343,10 @@ class Namespace < ApplicationRecord end end + def emails_enabled? + !emails_disabled? + end + def lfs_enabled? # User namespace will always default to the global setting Gitlab.config.lfs.enabled diff --git a/app/models/notification_recipient.rb b/app/models/notification_recipient.rb index 79a84231083..b3eaed154e2 100644 --- a/app/models/notification_recipient.rb +++ b/app/models/notification_recipient.rb @@ -125,6 +125,10 @@ class NotificationRecipient @project ? @project.emails_disabled? : @group&.emails_disabled? end + def emails_enabled? + !emails_disabled? + end + def read_ability return if @skip_read_ability return @read_ability if instance_variable_defined?(:@read_ability) diff --git a/app/models/project.rb b/app/models/project.rb index f5a69494237..46e25564eab 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -1038,6 +1038,9 @@ class Project < ApplicationRecord end end + def emails_enabled? + !emails_disabled? + end override :lfs_enabled? def lfs_enabled? return namespace.lfs_enabled? if self[:lfs_enabled].nil? diff --git a/app/services/search_service.rb b/app/services/search_service.rb index 7b16c7eee42..cea7fc5769e 100644 --- a/app/services/search_service.rb +++ b/app/services/search_service.rb @@ -91,6 +91,17 @@ class SearchService end end + def level + @level ||= + if project + 'project' + elsif group + 'group' + else + 'global' + end + end + private def page diff --git a/app/services/work_items/parent_links/create_service.rb b/app/services/work_items/parent_links/create_service.rb index 78013f081c8..9940776e367 100644 --- a/app/services/work_items/parent_links/create_service.rb +++ b/app/services/work_items/parent_links/create_service.rb @@ -38,12 +38,7 @@ module WorkItems end def extract_references - params[:issuable_references].map do |id| - ::WorkItem.find(id) - rescue ActiveRecord::RecordNotFound - @errors << _("Task with ID: %{id} could not be found.") % { id: id } - next - end.compact + params[:issuable_references] end # TODO: Create system notes when work item's parent or children are updated diff --git a/app/services/work_items/widgets/hierarchy_service/base_service.rb b/app/services/work_items/widgets/hierarchy_service/base_service.rb index 6466c815bdf..085d6c6b0e7 100644 --- a/app/services/work_items/widgets/hierarchy_service/base_service.rb +++ b/app/services/work_items/widgets/hierarchy_service/base_service.rb @@ -12,8 +12,8 @@ module WorkItems if params.key?(:parent) update_work_item_parent(params.delete(:parent)) - elsif params.key?(:children_ids) - update_work_item_children(params.delete(:children_ids)) + elsif params.key?(:children) + update_work_item_children(params.delete(:children)) else invalid_args_error end @@ -42,9 +42,9 @@ module WorkItems end # rubocop: enable CodeReuse/ActiveRecord - def update_work_item_children(children_ids) + def update_work_item_children(children) ::WorkItems::ParentLinks::CreateService - .new(widget.work_item, current_user, { issuable_references: children_ids }) + .new(widget.work_item, current_user, { issuable_references: children }) .execute end @@ -53,7 +53,7 @@ module WorkItems end def incompatible_args?(params) - params[:children_ids] && params[:parent] + params[:children] && params[:parent] end def feature_flag_error diff --git a/config/application.rb b/config/application.rb index 61f38787c1d..b758f2df857 100644 --- a/config/application.rb +++ b/config/application.rb @@ -504,19 +504,6 @@ module Gitlab end end - # We know Rails closes database connections in the - # active_record.clear_active_connections initializer, so only log database - # connections opened after that. - initializer :start_logging_new_postgresql_connections, after: :finisher_hook do - ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.warn_on_new_connection = true - end - - # It is legitimate to open database connections after initializers so stop - # logging - initializer :stop_logging_new_postgresql_connections, after: :set_routes_reloader_hook do - ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.warn_on_new_connection = false - end - # Add assets for variants of GitLab. They should take precedence over CE. # This means if multiple files exist, e.g.: # diff --git a/config/feature_flags/development/mr_show_reports_immediately.yml b/config/feature_flags/development/mr_show_reports_immediately.yml new file mode 100644 index 00000000000..23ac381a521 --- /dev/null +++ b/config/feature_flags/development/mr_show_reports_immediately.yml @@ -0,0 +1,8 @@ +--- +name: mr_show_reports_immediately +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/76612 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/367027 +milestone: '15.2' +type: development +group: group::pipeline insights +default_enabled: false diff --git a/config/initializers/00_connection_logger.rb b/config/initializers/00_connection_logger.rb deleted file mode 100644 index 9f03d13f95f..00000000000 --- a/config/initializers/00_connection_logger.rb +++ /dev/null @@ -1,26 +0,0 @@ -# frozen_string_literal: true - -module NewConnectionLogger - extend ActiveSupport::Concern - - prepended do |base| - base.class_attribute :warn_on_new_connection, default: false - end - - class_methods do - def new_client(...) - if warn_on_new_connection && !ENV['SKIP_LOG_INITIALIZER_CONNECTIONS'] - cleaned_caller = Gitlab::BacktraceCleaner.clean_backtrace(caller) - message = "Database connection should not be called during initializers. Read more at https://docs.gitlab.com/ee/development/rails_initializers.html#database-connections-in-initializers" - - ActiveSupport::Deprecation.warn(message, cleaned_caller) - - warn caller if ENV['DEBUG_INITIALIZER_CONNECTIONS'] - end - - super - end - end -end - -ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(NewConnectionLogger) diff --git a/config/initializers/database_config.rb b/config/initializers/database_config.rb index 84ef0bc9f16..31666c884bc 100644 --- a/config/initializers/database_config.rb +++ b/config/initializers/database_config.rb @@ -1,6 +1,12 @@ # frozen_string_literal: true Gitlab.ee do + if Gitlab::Geo.geo_database_configured? + # Make sure connects_to for geo gets called outside of config/routes.rb first + # See InitializerConnections.with_disabled_database_connections + Geo::TrackingBase + end + if Gitlab::Runtime.sidekiq? && Gitlab::Geo.geo_database_configured? # The Geo::TrackingBase model does not yet use connects_to. So, # this will not properly support geo: from config/databse.yml diff --git a/config/routes.rb b/config/routes.rb index 652b50e3928..97e6983e0bc 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -4,336 +4,338 @@ require 'sidekiq/web' require 'sidekiq/cron/web' require 'product_analytics/collector_app' -Rails.application.routes.draw do - concern :access_requestable do - post :request_access, on: :collection - post :approve_access_request, on: :member - end +InitializerConnections.with_disabled_database_connections do + Rails.application.routes.draw do + concern :access_requestable do + post :request_access, on: :collection + post :approve_access_request, on: :member + end - concern :awardable do - post :toggle_award_emoji, on: :member - end + concern :awardable do + post :toggle_award_emoji, on: :member + end - favicon_redirect = redirect do |_params, _request| - ActionController::Base.helpers.asset_url(Gitlab::Favicon.main) - end - get 'favicon.png', to: favicon_redirect - get 'favicon.ico', to: favicon_redirect + favicon_redirect = redirect do |_params, _request| + ActionController::Base.helpers.asset_url(Gitlab::Favicon.main) + end + get 'favicon.png', to: favicon_redirect + get 'favicon.ico', to: favicon_redirect - draw :development + draw :development - use_doorkeeper do - controllers applications: 'oauth/applications', - authorized_applications: 'oauth/authorized_applications', - authorizations: 'oauth/authorizations', - token_info: 'oauth/token_info', - tokens: 'oauth/tokens' - end + use_doorkeeper do + controllers applications: 'oauth/applications', + authorized_applications: 'oauth/authorized_applications', + authorizations: 'oauth/authorizations', + token_info: 'oauth/token_info', + tokens: 'oauth/tokens' + end - # This prefixless path is required because Jira gets confused if we set it up with a path - # More information: https://gitlab.com/gitlab-org/gitlab/issues/6752 - scope path: '/login/oauth', controller: 'oauth/jira_dvcs/authorizations', as: :oauth_jira_dvcs do - get :authorize, action: :new - get :callback - post :access_token + # This prefixless path is required because Jira gets confused if we set it up with a path + # More information: https://gitlab.com/gitlab-org/gitlab/issues/6752 + scope path: '/login/oauth', controller: 'oauth/jira_dvcs/authorizations', as: :oauth_jira_dvcs do + get :authorize, action: :new + get :callback + post :access_token - match '*all', via: [:get, :post], to: proc { [404, {}, ['']] } - end + match '*all', via: [:get, :post], to: proc { [404, {}, ['']] } + end - draw :oauth + draw :oauth - use_doorkeeper_openid_connect do - controllers discovery: 'jwks' - end + use_doorkeeper_openid_connect do + controllers discovery: 'jwks' + end - # Add OPTIONS method for CORS preflight requests - match '/oauth/userinfo' => 'doorkeeper/openid_connect/userinfo#show', via: :options - match '/oauth/discovery/keys' => 'jwks#keys', via: :options - match '/.well-known/openid-configuration' => 'jwks#provider', via: :options - match '/.well-known/webfinger' => 'jwks#webfinger', via: :options + # Add OPTIONS method for CORS preflight requests + match '/oauth/userinfo' => 'doorkeeper/openid_connect/userinfo#show', via: :options + match '/oauth/discovery/keys' => 'jwks#keys', via: :options + match '/.well-known/openid-configuration' => 'jwks#provider', via: :options + match '/.well-known/webfinger' => 'jwks#webfinger', via: :options - match '/oauth/token' => 'oauth/tokens#create', via: :options - match '/oauth/revoke' => 'oauth/tokens#revoke', via: :options + match '/oauth/token' => 'oauth/tokens#create', via: :options + match '/oauth/revoke' => 'oauth/tokens#revoke', via: :options - match '/-/jira_connect/oauth_application_id' => 'jira_connect/oauth_application_ids#show', via: :options + match '/-/jira_connect/oauth_application_id' => 'jira_connect/oauth_application_ids#show', via: :options - # Sign up - scope path: '/users/sign_up', module: :registrations, as: :users_sign_up do - resource :welcome, only: [:show, :update], controller: 'welcome' do - Gitlab.ee do - get :trial_getting_started, on: :collection - get :trial_onboarding_board, on: :collection - get :continuous_onboarding_getting_started, on: :collection + # Sign up + scope path: '/users/sign_up', module: :registrations, as: :users_sign_up do + resource :welcome, only: [:show, :update], controller: 'welcome' do + Gitlab.ee do + get :trial_getting_started, on: :collection + get :trial_onboarding_board, on: :collection + get :continuous_onboarding_getting_started, on: :collection + end end - end - Gitlab.ee do - resource :company, only: [:new, :create], controller: 'company' - resources :groups, only: [:new, :create] - resources :projects, only: [:new, :create] - resources :groups_projects, only: [:new, :create] do - collection do - post :import - put :exit + Gitlab.ee do + resource :company, only: [:new, :create], controller: 'company' + resources :groups, only: [:new, :create] + resources :projects, only: [:new, :create] + resources :groups_projects, only: [:new, :create] do + collection do + post :import + put :exit + end end + draw :verification end - draw :verification end - end - # Search - get 'search' => 'search#show', as: :search - get 'search/autocomplete' => 'search#autocomplete', as: :search_autocomplete - get 'search/count' => 'search#count', as: :search_count - get 'search/opensearch' => 'search#opensearch', as: :search_opensearch + # Search + get 'search' => 'search#show', as: :search + get 'search/autocomplete' => 'search#autocomplete', as: :search_autocomplete + get 'search/count' => 'search#count', as: :search_count + get 'search/opensearch' => 'search#opensearch', as: :search_opensearch - # JSON Web Token - get 'jwt/auth' => 'jwt#auth' + # JSON Web Token + get 'jwt/auth' => 'jwt#auth' - # Health check - get 'health_check(/:checks)' => 'health_check#index', as: :health_check + # Health check + get 'health_check(/:checks)' => 'health_check#index', as: :health_check - # Terraform service discovery - get '.well-known/terraform.json' => 'terraform/services#index', as: :terraform_services + # Terraform service discovery + get '.well-known/terraform.json' => 'terraform/services#index', as: :terraform_services - # Begin of the /-/ scope. - # Use this scope for all new global routes. - scope path: '-' do - # Autocomplete - get '/autocomplete/users' => 'autocomplete#users' - get '/autocomplete/users/:id' => 'autocomplete#user' - get '/autocomplete/projects' => 'autocomplete#projects' - get '/autocomplete/award_emojis' => 'autocomplete#award_emojis' - get '/autocomplete/merge_request_target_branches' => 'autocomplete#merge_request_target_branches' - get '/autocomplete/deploy_keys_with_owners' => 'autocomplete#deploy_keys_with_owners' + # Begin of the /-/ scope. + # Use this scope for all new global routes. + scope path: '-' do + # Autocomplete + get '/autocomplete/users' => 'autocomplete#users' + get '/autocomplete/users/:id' => 'autocomplete#user' + get '/autocomplete/projects' => 'autocomplete#projects' + get '/autocomplete/award_emojis' => 'autocomplete#award_emojis' + get '/autocomplete/merge_request_target_branches' => 'autocomplete#merge_request_target_branches' + get '/autocomplete/deploy_keys_with_owners' => 'autocomplete#deploy_keys_with_owners' - Gitlab.ee do - get '/autocomplete/project_groups' => 'autocomplete#project_groups' - get '/autocomplete/project_routes' => 'autocomplete#project_routes' - get '/autocomplete/namespace_routes' => 'autocomplete#namespace_routes' - get '/autocomplete/group_subgroups' => 'autocomplete#group_subgroups' - end + Gitlab.ee do + get '/autocomplete/project_groups' => 'autocomplete#project_groups' + get '/autocomplete/project_routes' => 'autocomplete#project_routes' + get '/autocomplete/namespace_routes' => 'autocomplete#namespace_routes' + get '/autocomplete/group_subgroups' => 'autocomplete#group_subgroups' + end - # sandbox - get '/sandbox/mermaid' => 'sandbox#mermaid' + # sandbox + get '/sandbox/mermaid' => 'sandbox#mermaid' - get '/whats_new' => 'whats_new#index' + get '/whats_new' => 'whats_new#index' - get 'offline' => "pwa#offline" - get 'manifest' => "pwa#manifest", constraints: lambda { |req| req.format == :json } + get 'offline' => "pwa#offline" + get 'manifest' => "pwa#manifest", constraints: lambda { |req| req.format == :json } - # '/-/health' implemented by BasicHealthCheck middleware - get 'liveness' => 'health#liveness' - get 'readiness' => 'health#readiness' - controller :metrics do - get 'metrics', action: :index - get 'metrics/system', action: :system - end - mount Peek::Railtie => '/peek', as: 'peek_routes' + # '/-/health' implemented by BasicHealthCheck middleware + get 'liveness' => 'health#liveness' + get 'readiness' => 'health#readiness' + controller :metrics do + get 'metrics', action: :index + get 'metrics/system', action: :system + end + mount Peek::Railtie => '/peek', as: 'peek_routes' - get 'runner_setup/platforms' => 'runner_setup#platforms' + get 'runner_setup/platforms' => 'runner_setup#platforms' - # Boards resources shared between group and projects - resources :boards, only: [] do - resources :lists, module: :boards, only: [:index, :create, :update, :destroy] do - collection do - post :generate + # Boards resources shared between group and projects + resources :boards, only: [] do + resources :lists, module: :boards, only: [:index, :create, :update, :destroy] do + collection do + post :generate + end + + resources :issues, only: [:index, :create, :update] end - resources :issues, only: [:index, :create, :update] - end + resources :issues, module: :boards, only: [:index, :update] do + collection do + put :bulk_move, format: :json + end + end - resources :issues, module: :boards, only: [:index, :update] do - collection do - put :bulk_move, format: :json + Gitlab.ee do + resources :users, module: :boards, only: [:index] + resources :milestones, module: :boards, only: [:index] end end - Gitlab.ee do - resources :users, module: :boards, only: [:index] - resources :milestones, module: :boards, only: [:index] - end - end + get 'acme-challenge/' => 'acme_challenges#show' - get 'acme-challenge/' => 'acme_challenges#show' + scope :ide, as: :ide, format: false do + get '/', to: 'ide#index' + get '/project', to: 'ide#index' + + scope path: 'project/:project_id', as: :project, constraints: { project_id: Gitlab::PathRegex.full_namespace_route_regex } do + %w[edit tree blob].each do |action| + get "/#{action}", to: 'ide#index' + get "/#{action}/*branch/-/*path", to: 'ide#index' + get "/#{action}/*branch/-", to: 'ide#index' + get "/#{action}/*branch", to: 'ide#index' + end + + get '/merge_requests/:merge_request_id', to: 'ide#index', constraints: { merge_request_id: /\d+/ } + get '/', to: 'ide#index' + end + end - scope :ide, as: :ide, format: false do - get '/', to: 'ide#index' - get '/project', to: 'ide#index' + draw :operations + draw :jira_connect - scope path: 'project/:project_id', as: :project, constraints: { project_id: Gitlab::PathRegex.full_namespace_route_regex } do - %w[edit tree blob].each do |action| - get "/#{action}", to: 'ide#index' - get "/#{action}/*branch/-/*path", to: 'ide#index' - get "/#{action}/*branch/-", to: 'ide#index' - get "/#{action}/*branch", to: 'ide#index' + Gitlab.ee do + draw :security + draw :smartcard + draw :trial + draw :trial_registration + draw :country + draw :country_state + draw :subscription + + scope '/push_from_secondary/:geo_node_id' do + draw :git_http end - get '/merge_requests/:merge_request_id', to: 'ide#index', constraints: { merge_request_id: /\d+/ } - get '/', to: 'ide#index' + # Used for survey responses + resources :survey_responses, only: :index end - end - - draw :operations - draw :jira_connect - Gitlab.ee do - draw :security - draw :smartcard - draw :trial - draw :trial_registration - draw :country - draw :country_state - draw :subscription - - scope '/push_from_secondary/:geo_node_id' do - draw :git_http + Gitlab.jh do + draw :global_jh end - # Used for survey responses - resources :survey_responses, only: :index - end - - Gitlab.jh do - draw :global_jh - end - - if ENV['GITLAB_CHAOS_SECRET'] || Rails.env.development? || Rails.env.test? - resource :chaos, only: [] do - get :leakmem - get :cpu_spin - get :db_spin - get :sleep - get :kill - get :quit - post :gc + if ENV['GITLAB_CHAOS_SECRET'] || Rails.env.development? || Rails.env.test? + resource :chaos, only: [] do + get :leakmem + get :cpu_spin + get :db_spin + get :sleep + get :kill + get :quit + post :gc + end end - end - resources :invites, only: [:show], constraints: { id: /[A-Za-z0-9_-]+/ } do - member do - post :accept - match :decline, via: [:get, :post] + resources :invites, only: [:show], constraints: { id: /[A-Za-z0-9_-]+/ } do + member do + post :accept + match :decline, via: [:get, :post] + end end - end - resources :sent_notifications, only: [], constraints: { id: /\h{32}/ } do - member do - get :unsubscribe + resources :sent_notifications, only: [], constraints: { id: /\h{32}/ } do + member do + get :unsubscribe + end end - end - # Spam reports - resources :abuse_reports, only: [:new, :create] + # Spam reports + resources :abuse_reports, only: [:new, :create] - # JWKS (JSON Web Key Set) endpoint - # Used by third parties to verify CI_JOB_JWT - get 'jwks' => 'jwks#index' + # JWKS (JSON Web Key Set) endpoint + # Used by third parties to verify CI_JOB_JWT + get 'jwks' => 'jwks#index' - draw :snippets - draw :profile + draw :snippets + draw :profile - post '/mailgun/webhooks' => 'mailgun/webhooks#process_webhook' + post '/mailgun/webhooks' => 'mailgun/webhooks#process_webhook' - # Deprecated route for permanent failures - # https://gitlab.com/gitlab-org/gitlab/-/issues/362606 - post '/members/mailgun/permanent_failures' => 'mailgun/webhooks#process_webhook' + # Deprecated route for permanent failures + # https://gitlab.com/gitlab-org/gitlab/-/issues/362606 + post '/members/mailgun/permanent_failures' => 'mailgun/webhooks#process_webhook' - # Product analytics collector - match '/collector/i', to: ProductAnalytics::CollectorApp.new, via: :all - end - # End of the /-/ scope. - - concern :clusterable do - resources :clusters, only: [:index, :show, :update, :destroy] do - collection do - get :connect - get :new_cluster_docs - post :create_user - end + # Product analytics collector + match '/collector/i', to: ProductAnalytics::CollectorApp.new, via: :all + end + # End of the /-/ scope. - resource :integration, controller: 'clusters/integrations', only: [] do + concern :clusterable do + resources :clusters, only: [:index, :show, :update, :destroy] do collection do - post :create_or_update + get :connect + get :new_cluster_docs + post :create_user end - end - member do - Gitlab.ee do - get :metrics, format: :json - get :environments, format: :json + resource :integration, controller: 'clusters/integrations', only: [] do + collection do + post :create_or_update + end end - get :metrics_dashboard - get :'/prometheus/api/v1/*proxy_path', to: 'clusters#prometheus_proxy', as: :prometheus_api - get :cluster_status, format: :json - delete :clear_cache + member do + Gitlab.ee do + get :metrics, format: :json + get :environments, format: :json + end + + get :metrics_dashboard + get :'/prometheus/api/v1/*proxy_path', to: 'clusters#prometheus_proxy', as: :prometheus_api + get :cluster_status, format: :json + delete :clear_cache + end end end - end - resources :groups, only: [:index, :new, :create] do - post :preview_markdown - end + resources :groups, only: [:index, :new, :create] do + post :preview_markdown + end - draw :group - - resources :projects, only: [:index, :new, :create] - - get '/projects/:id' => 'projects/redirect#redirect_from_id' - - draw :git_http - draw :api - draw :customers_dot - draw :sidekiq - draw :help - draw :google_api - draw :import - draw :uploads - draw :explore - draw :admin - draw :dashboard - draw :user - draw :project - draw :unmatched_project - - # Issue https://gitlab.com/gitlab-org/gitlab/-/issues/210024 - scope as: 'deprecated' do - # Issue https://gitlab.com/gitlab-org/gitlab/-/issues/223719 - get '/snippets/:id/raw', - to: 'snippets#raw', - format: false, - constraints: { id: /\d+/ } - - Gitlab::Routing.redirect_legacy_paths(self, :snippets) - end + draw :group + + resources :projects, only: [:index, :new, :create] + + get '/projects/:id' => 'projects/redirect#redirect_from_id' + + draw :git_http + draw :api + draw :customers_dot + draw :sidekiq + draw :help + draw :google_api + draw :import + draw :uploads + draw :explore + draw :admin + draw :dashboard + draw :user + draw :project + draw :unmatched_project + + # Issue https://gitlab.com/gitlab-org/gitlab/-/issues/210024 + scope as: 'deprecated' do + # Issue https://gitlab.com/gitlab-org/gitlab/-/issues/223719 + get '/snippets/:id/raw', + to: 'snippets#raw', + format: false, + constraints: { id: /\d+/ } + + Gitlab::Routing.redirect_legacy_paths(self, :snippets) + end - Gitlab.ee do - get '/sitemap' => 'sitemap#show', format: :xml - end + Gitlab.ee do + get '/sitemap' => 'sitemap#show', format: :xml + end - # Creates shorthand helper methods for project resources. - # For example; for the `namespace_project_path` this also creates `project_path`. - # - # TODO: We don't need the `Gitlab::Routing` module at all as we can use - # the `direct` DSL method of Rails to define url helpers. Move all the - # custom url helpers to use the `direct` DSL method and remove the `Gitlab::Routing`. - # For more information: https://gitlab.com/gitlab-org/gitlab/-/issues/299583 - Gitlab::Application.routes.set.filter_map { |route| route.name if route.name&.include?('namespace_project') }.each do |name| - new_name = name.sub('namespace_project', 'project') - - direct(new_name) do |project, *args| - # This is due to a bug I've found in Rails. - # For more information: https://gitlab.com/gitlab-org/gitlab/-/issues/299591 - args.pop if args.last == {} - - send("#{name}_url", project&.namespace, project, *args) + # Creates shorthand helper methods for project resources. + # For example; for the `namespace_project_path` this also creates `project_path`. + # + # TODO: We don't need the `Gitlab::Routing` module at all as we can use + # the `direct` DSL method of Rails to define url helpers. Move all the + # custom url helpers to use the `direct` DSL method and remove the `Gitlab::Routing`. + # For more information: https://gitlab.com/gitlab-org/gitlab/-/issues/299583 + Gitlab::Application.routes.set.filter_map { |route| route.name if route.name&.include?('namespace_project') }.each do |name| + new_name = name.sub('namespace_project', 'project') + + direct(new_name) do |project, *args| + # This is due to a bug I've found in Rails. + # For more information: https://gitlab.com/gitlab-org/gitlab/-/issues/299591 + args.pop if args.last == {} + + send("#{name}_url", project&.namespace, project, *args) + end end - end - root to: "root#index" + root to: "root#index" - get '*unmatched_route', to: 'application#route_not_found', format: false -end + get '*unmatched_route', to: 'application#route_not_found', format: false + end -Gitlab::Routing.add_helpers(TimeboxesRoutingHelper) + Gitlab::Routing.add_helpers(TimeboxesRoutingHelper) +end diff --git a/doc/api/dora/metrics.md b/doc/api/dora/metrics.md index 510d40db2d7..99473ca7b4c 100644 --- a/doc/api/dora/metrics.md +++ b/doc/api/dora/metrics.md @@ -21,15 +21,15 @@ Get project-level DORA metrics. GET /projects/:id/dora/metrics ``` -| Attribute | Type | Required | Description | -|----------------------|------------------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `id` | integer/string | yes | The ID or [URL-encoded path of the project](../index.md#namespaced-path-encoding) can be accessed by the authenticated user. | -| `metric` | string | yes | One of `deployment_frequency`, `lead_time_for_changes`, `time_to_restore_service` or `change_failure_rate`. | -| `start_date` | string | no | Date range to start from. ISO 8601 Date format, for example `2021-03-01`. Default is 3 months ago. | -| `end_date` | string | no | Date range to end at. ISO 8601 Date format, for example `2021-03-01`. Default is the current date. | -| `interval` | string | no | The bucketing interval. One of `all`, `monthly` or `daily`. Default is `daily`. | +| Attribute | Type | Required | Description | +|:---------------------|:-----------------|:---------|:------------| +| `id` | integer/string | yes | The ID or [URL-encoded path of the project](../index.md#namespaced-path-encoding) can be accessed by the authenticated user. | +| `metric` | string | yes | One of `deployment_frequency`, `lead_time_for_changes`, `time_to_restore_service` or `change_failure_rate`. | +| `end_date` | string | no | Date range to end at. ISO 8601 Date format, for example `2021-03-01`. Default is the current date. | | `environment_tier` | string | no | The [tier of the environment](../../ci/environments/index.md#deployment-tier-of-environments). Default is `production`. Deprecated, please use `environment_tiers`. | -| `environment_tiers` | array of strings | no | The [tiers of the environments](../../ci/environments/index.md#deployment-tier-of-environments). Default is `production`. | +| `environment_tiers` | array of strings | no | The [tiers of the environments](../../ci/environments/index.md#deployment-tier-of-environments). Default is `production`. | +| `interval` | string | no | The bucketing interval. One of `all`, `monthly` or `daily`. Default is `daily`. | +| `start_date` | string | no | Date range to start from. ISO 8601 Date format, for example `2021-03-01`. Default is 3 months ago. | Example request: @@ -62,15 +62,15 @@ Get group-level DORA metrics. GET /groups/:id/dora/metrics ``` -| Attribute | Type | Required | Description | -|---------------------|------------------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `id` | integer/string | yes | The ID or [URL-encoded path of the project](../index.md#namespaced-path-encoding) can be accessed by the authenticated user. | -| `metric` | string | yes | One of `deployment_frequency`, `lead_time_for_changes`, `time_to_restore_service` or `change_failure_rate`. | -| `start_date` | string | no | Date range to start from. ISO 8601 Date format, for example `2021-03-01`. Default is 3 months ago. | -| `end_date` | string | no | Date range to end at. ISO 8601 Date format, for example `2021-03-01`. Default is the current date. | -| `interval` | string | no | The bucketing interval. One of `all`, `monthly` or `daily`. Default is `daily`. | +| Attribute | Type | Required | Description | +|:--------------------|:-----------------|:---------|:------------| +| `id` | integer/string | yes | The ID or [URL-encoded path of the project](../index.md#namespaced-path-encoding) can be accessed by the authenticated user. | +| `metric` | string | yes | One of `deployment_frequency`, `lead_time_for_changes`, `time_to_restore_service` or `change_failure_rate`. | +| `end_date` | string | no | Date range to end at. ISO 8601 Date format, for example `2021-03-01`. Default is the current date. | | `environment_tier` | string | no | The [tier of the environment](../../ci/environments/index.md#deployment-tier-of-environments). Default is `production`. Deprecated, please use `environment_tiers`. | -| `environment_tiers` | array of strings | no | The [tiers of the environments](../../ci/environments/index.md#deployment-tier-of-environments). Default is `production`. | +| `environment_tiers` | array of strings | no | The [tiers of the environments](../../ci/environments/index.md#deployment-tier-of-environments). Default is `production`. | +| `interval` | string | no | The bucketing interval. One of `all`, `monthly` or `daily`. Default is `daily`. | +| `start_date` | string | no | Date range to start from. ISO 8601 Date format, for example `2021-03-01`. Default is 3 months ago. | Example request: @@ -99,9 +99,9 @@ For both the project and group-level endpoints above, the `value` field in the API response has a different meaning depending on the provided `metric` query parameter: -| `metric` query parameter | Description of `value` in response | -| ------------------------ |--------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `deployment_frequency` | The number of successful deployments during the time period. | -| `lead_time_for_changes` | The median number of seconds between the merge of the merge request (MR) and the deployment of the MR's commits for all MRs deployed during the time period. | -| `time_to_restore_service` | The median number of seconds an incident was open during the time period. Available only for production environment. | -| `change_failure_rate` | The number of incidents divided by the number of deployments during the time period. Available only for production environment. | +| `metric` query parameter | Description of `value` in response | +|:---------------------------|:-----------------------------------| +| `change_failure_rate` | The number of incidents divided by the number of deployments during the time period. Available only for production environment. | +| `deployment_frequency` | The number of successful deployments during the time period. | +| `lead_time_for_changes` | The median number of seconds between the merge of the merge request (MR) and the deployment of the MR's commits for all MRs deployed during the time period. | +| `time_to_restore_service` | The median number of seconds an incident was open during the time period. Available only for production environment. | diff --git a/doc/ci/yaml/index.md b/doc/ci/yaml/index.md index 6d8ca7fe8cf..3bb2007d6e3 100644 --- a/doc/ci/yaml/index.md +++ b/doc/ci/yaml/index.md @@ -1099,6 +1099,8 @@ that use the same cache key use the same cache, including in different pipelines If not set, the default key is `default`. All jobs with the `cache` keyword but no `cache:key` share the `default` cache. +Must be used with `cache: path`, or nothing is cached. + **Keyword type**: Job keyword. You can use it only as part of a job or in the [`default` section](#default). @@ -1263,6 +1265,8 @@ rspec: Use `cache:when` to define when to save the cache, based on the status of the job. +Must be used with `cache: path`, or nothing is cached. + **Keyword type**: Job keyword. You can use it only as part of a job or in the [`default` section](#default). @@ -1301,6 +1305,8 @@ Use the `pull` policy when you have many jobs executing in parallel that use the This policy speeds up job execution and reduces load on the cache server. You can use a job with the `push` policy to build the cache. +Must be used with `cache: path`, or nothing is cached. + **Keyword type**: Job keyword. You can use it only as part of a job or in the [`default` section](#default). diff --git a/doc/development/cicd/templates.md b/doc/development/cicd/templates.md index eafb1817a0a..4ea7a9d960c 100644 --- a/doc/development/cicd/templates.md +++ b/doc/development/cicd/templates.md @@ -289,9 +289,32 @@ Please read [versioning](#versioning) section for introducing breaking change sa ## Versioning -Versioning allows you to introduce a new template without modifying the existing -one. This process is useful when we need to introduce a breaking change, -but don't want to affect the existing projects that depends on the current template. +To introduce a breaking change without affecting the existing projects that depend on +the current template, use [stable](#stable-version) and [latest](#latest-version) versioning. + +Stable templates usually only receive breaking changes in major version releases, while +latest templates can receive breaking changes in any release. In major release milestones, +the latest template is made the new stable template (and the latest template might be deleted). + +Adding a latest template is safe, but comes with a maintenance burden: + +- GitLab has to choose a DRI to overwrite the stable template with the contents of the + latest template at the next major release of GitLab. The DRI is responsible for + supporting users who have trouble with the change. +- When we make a new non-breaking change, both the stable and latest templates must be updated + to match, as must as possible. +- A latest template could remain for longer than planned because many users could + directly depend on it continuing to exist. + +Before adding a new latest template, see if the change can be made to the stable +template instead, even if it's a breaking change. If the template is intended for copy-paste +usage only, it might be possible to directly change the stable version. Before changing +the stable template with a breaking change in a minor milestone, make sure: + +- It's a [pipeline template](#template-types) and it has a [code comment](#explain-requirements-and-expectations) + explaining that it's not designed to be used with the `includes`. +- The [CI/CD template usage metrics](#add-metrics) doesn't show any usage. If the metrics + show zero usage for the template, the template is not actively being used with `include`. ### Stable version @@ -393,7 +416,9 @@ is updated in a major version GitLab release. ### Add metrics -Every CI/CD template must also have metrics defined to track their use. The CI/CD template monthly usage report can be found in [Sisense (GitLab team members only)](https://app.periscopedata.com/app/gitlab/785953/Pipeline-Authoring-Dashboard?widget=14910475&udv=0). +Every CI/CD template must also have metrics defined to track their use. The CI/CD template monthly usage report +can be found in [Sisense (GitLab team members only)](https://app.periscopedata.com/app/gitlab/785953/Pipeline-Authoring-Dashboard?widget=13440051&udv=0). +Double click a template to see the graph for that single template. To add a metric definition for a new template: diff --git a/doc/development/rails_initializers.md b/doc/development/rails_initializers.md index 68f3c07e45a..fca24cf8c01 100644 --- a/doc/development/rails_initializers.md +++ b/doc/development/rails_initializers.md @@ -29,12 +29,18 @@ query) from an initializer means that tasks like `db:drop`, and `db:test:prepare` will fail because an active session prevents the database from being dropped. -To help detect when database connections are opened from initializers, we now -warn in `STDERR`. For example: +To prevent this, we stop database connections from being opened during +routes loading. Doing will result in an error: ```shell -DEPRECATION WARNING: Database connection should not be called during initializers (called from block in <module:HasVariable> at app/models/concerns/ci/has_variable.rb:22) +RuntimeError: + Database connection should not be called during initializers. +# ./config/initializers/00_connection_logger.rb:15:in `new_client' +# ./lib/gitlab/database/load_balancing/load_balancer.rb:112:in `block in read_write' +# ./lib/gitlab/database/load_balancing/load_balancer.rb:184:in `retry_with_backoff' +# ./lib/gitlab/database/load_balancing/load_balancer.rb:111:in `read_write' +# ./lib/gitlab/database/load_balancing/connection_proxy.rb:119:in `write_using_load_balancer' +# ./lib/gitlab/database/load_balancing/connection_proxy.rb:89:in `method_missing' +# ./config/routes.rb:10:in `block in <main>' +# ./config/routes.rb:9:in `<main>' ``` - -If you wish to print out the full backtrace, set the -`DEBUG_INITIALIZER_CONNECTIONS` environment variable. diff --git a/doc/user/project/merge_requests/approvals/img/mr_approvals_by_code_owners_v12_7.png b/doc/user/project/merge_requests/approvals/img/mr_approvals_by_code_owners_v12_7.png Binary files differdeleted file mode 100644 index 669148a41d8..00000000000 --- a/doc/user/project/merge_requests/approvals/img/mr_approvals_by_code_owners_v12_7.png +++ /dev/null diff --git a/doc/user/project/merge_requests/approvals/img/mr_approvals_by_code_owners_v15_2.png b/doc/user/project/merge_requests/approvals/img/mr_approvals_by_code_owners_v15_2.png Binary files differnew file mode 100644 index 00000000000..37dad4e5ae8 --- /dev/null +++ b/doc/user/project/merge_requests/approvals/img/mr_approvals_by_code_owners_v15_2.png diff --git a/doc/user/project/merge_requests/approvals/rules.md b/doc/user/project/merge_requests/approvals/rules.md index 21cf5cca4d1..b79c8ee867f 100644 --- a/doc/user/project/merge_requests/approvals/rules.md +++ b/doc/user/project/merge_requests/approvals/rules.md @@ -152,9 +152,9 @@ become eligible approvers in the project. To enable this merge request approval 1. Go to your project and select **Settings > General**. 1. Expand **Merge request (MR) approvals**. -1. Locate **Eligible users** and select the number of approvals required: +1. Locate **All eligible users** and select the number of approvals required: - ![MR approvals by Code Owners](img/mr_approvals_by_code_owners_v12_7.png) +![MR approvals by Code Owners](img/mr_approvals_by_code_owners_v15_2.png) You can also [require code owner approval](../../protected_branches.md#require-code-owner-approval-on-a-protected-branch) diff --git a/doc/user/project/milestones/burndown_and_burnup_charts.md b/doc/user/project/milestones/burndown_and_burnup_charts.md index 298eea8639c..0f36747a547 100644 --- a/doc/user/project/milestones/burndown_and_burnup_charts.md +++ b/doc/user/project/milestones/burndown_and_burnup_charts.md @@ -9,7 +9,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w [Burndown](#burndown-charts) and [burnup](#burnup-charts) charts show the progress of completing a milestone. -![burndown and burnup chart](img/burndown_and_burnup_charts_v15_1.png) +![burndown and burnup chart](img/burndown_and_burnup_charts_v15_3.png) ## Burndown charts @@ -19,7 +19,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w Burndown charts show the number of issues over the course of a milestone. -![burndown chart](img/burndown_chart_v15_1.png) +![burndown chart](img/burndown_chart_v15_3.png) At a glance, you see the current state for the completion a given milestone. Without them, you would have to organize the data from the milestone and plot it @@ -106,7 +106,7 @@ Reopened issues are considered as having been opened on the day after they were Burnup charts show the assigned and completed work for a milestone. -![burnup chart](img/burnup_chart_v15_1.png) +![burnup chart](img/burnup_chart_v15_3.png) To view a project's burnup chart: diff --git a/doc/user/project/milestones/img/burndown_and_burnup_charts_v15_1.png b/doc/user/project/milestones/img/burndown_and_burnup_charts_v15_1.png Binary files differdeleted file mode 100644 index 58c0ddf892f..00000000000 --- a/doc/user/project/milestones/img/burndown_and_burnup_charts_v15_1.png +++ /dev/null diff --git a/doc/user/project/milestones/img/burndown_and_burnup_charts_v15_3.png b/doc/user/project/milestones/img/burndown_and_burnup_charts_v15_3.png Binary files differnew file mode 100644 index 00000000000..1420123500c --- /dev/null +++ b/doc/user/project/milestones/img/burndown_and_burnup_charts_v15_3.png diff --git a/doc/user/project/milestones/img/burndown_chart_v15_1.png b/doc/user/project/milestones/img/burndown_chart_v15_1.png Binary files differdeleted file mode 100644 index 2953380292d..00000000000 --- a/doc/user/project/milestones/img/burndown_chart_v15_1.png +++ /dev/null diff --git a/doc/user/project/milestones/img/burndown_chart_v15_3.png b/doc/user/project/milestones/img/burndown_chart_v15_3.png Binary files differnew file mode 100644 index 00000000000..9e1c7ccd4dd --- /dev/null +++ b/doc/user/project/milestones/img/burndown_chart_v15_3.png diff --git a/doc/user/project/milestones/img/burnup_chart_v15_1.png b/doc/user/project/milestones/img/burnup_chart_v15_1.png Binary files differdeleted file mode 100644 index e89b76344ed..00000000000 --- a/doc/user/project/milestones/img/burnup_chart_v15_1.png +++ /dev/null diff --git a/doc/user/project/milestones/img/burnup_chart_v15_3.png b/doc/user/project/milestones/img/burnup_chart_v15_3.png Binary files differnew file mode 100644 index 00000000000..2e85c0abe87 --- /dev/null +++ b/doc/user/project/milestones/img/burnup_chart_v15_3.png diff --git a/doc/user/project/milestones/index.md b/doc/user/project/milestones/index.md index 3ecee86b978..ba48876d4fd 100644 --- a/doc/user/project/milestones/index.md +++ b/doc/user/project/milestones/index.md @@ -196,7 +196,7 @@ There are also tabs below these that show the following: The milestone view contains a [burndown and burnup chart](burndown_and_burnup_charts.md), showing the progress of completing a milestone. -![burndown chart](img/burndown_and_burnup_charts_v15_1.png) +![burndown chart](img/burndown_and_burnup_charts_v15_3.png) ### Milestone sidebar diff --git a/lib/api/users.rb b/lib/api/users.rb index d089ce1b499..d66d86a9055 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -1284,3 +1284,5 @@ module API end end end + +API::Users.prepend_mod diff --git a/lib/gitlab/ci/templates/Bash.gitlab-ci.yml b/lib/gitlab/ci/templates/Bash.gitlab-ci.yml index f39a84bceec..004c2897b60 100644 --- a/lib/gitlab/ci/templates/Bash.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Bash.gitlab-ci.yml @@ -1,3 +1,6 @@ +# You can copy and paste this template into a new `.gitlab-ci.yml` file. +# You should not add this template to an existing `.gitlab-ci.yml` file by using the `include:` keyword. +# # To contribute improvements to CI/CD templates, please follow the Development guide at: # https://docs.gitlab.com/ee/development/cicd/templates.html # This specific template is located at: diff --git a/lib/gitlab/ci/templates/C++.gitlab-ci.yml b/lib/gitlab/ci/templates/C++.gitlab-ci.yml index c078c99f352..3096af1b173 100644 --- a/lib/gitlab/ci/templates/C++.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/C++.gitlab-ci.yml @@ -1,3 +1,6 @@ +# You can copy and paste this template into a new `.gitlab-ci.yml` file. +# You should not add this template to an existing `.gitlab-ci.yml` file by using the `include:` keyword. +# # To contribute improvements to CI/CD templates, please follow the Development guide at: # https://docs.gitlab.com/ee/development/cicd/templates.html # This specific template is located at: diff --git a/lib/gitlab/ci/templates/Chef.gitlab-ci.yml b/lib/gitlab/ci/templates/Chef.gitlab-ci.yml index f166da9bdd6..a64f87193a9 100644 --- a/lib/gitlab/ci/templates/Chef.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Chef.gitlab-ci.yml @@ -1,14 +1,17 @@ -# To contribute improvements to CI/CD templates, please follow the Development guide at: -# https://docs.gitlab.com/ee/development/cicd/templates.html -# This specific template is located at: -# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Chef.gitlab-ci.yml - # This template uses Test Kitchen with the kitchen-dokken driver to # perform functional testing. Doing so requires that your runner be a # Docker runner configured for privileged mode. Please see # https://docs.gitlab.com/runner/executors/docker.html#use-docker-in-docker-with-privileged-mode # for help configuring your runner properly, or, if you want to switch # to a different driver, see http://kitchen.ci/docs/drivers +# +# You can copy and paste this template into a new `.gitlab-ci.yml` file. +# You should not add this template to an existing `.gitlab-ci.yml` file by using the `include:` keyword. +# +# To contribute improvements to CI/CD templates, please follow the Development guide at: +# https://docs.gitlab.com/ee/development/cicd/templates.html +# This specific template is located at: +# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Chef.gitlab-ci.yml image: "chef/chefdk" services: diff --git a/lib/gitlab/ci/templates/Clojure.gitlab-ci.yml b/lib/gitlab/ci/templates/Clojure.gitlab-ci.yml index 0f9e28c9a8e..4fe37ceaeaa 100644 --- a/lib/gitlab/ci/templates/Clojure.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Clojure.gitlab-ci.yml @@ -1,3 +1,6 @@ +# You can copy and paste this template into a new `.gitlab-ci.yml` file. +# You should not add this template to an existing `.gitlab-ci.yml` file by using the `include:` keyword. +# # To contribute improvements to CI/CD templates, please follow the Development guide at: # https://docs.gitlab.com/ee/development/cicd/templates.html # This specific template is located at: diff --git a/lib/gitlab/ci/templates/Crystal.gitlab-ci.yml b/lib/gitlab/ci/templates/Crystal.gitlab-ci.yml index 8886929646d..68b55b782cd 100644 --- a/lib/gitlab/ci/templates/Crystal.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Crystal.gitlab-ci.yml @@ -1,3 +1,6 @@ +# You can copy and paste this template into a new `.gitlab-ci.yml` file. +# You should not add this template to an existing `.gitlab-ci.yml` file by using the `include:` keyword. +# # To contribute improvements to CI/CD templates, please follow the Development guide at: # https://docs.gitlab.com/ee/development/cicd/templates.html # This specific template is located at: diff --git a/lib/gitlab/ci/templates/Dart.gitlab-ci.yml b/lib/gitlab/ci/templates/Dart.gitlab-ci.yml index 6354db38f58..35401e62fe2 100644 --- a/lib/gitlab/ci/templates/Dart.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Dart.gitlab-ci.yml @@ -1,3 +1,6 @@ +# You can copy and paste this template into a new `.gitlab-ci.yml` file. +# You should not add this template to an existing `.gitlab-ci.yml` file by using the `include:` keyword. +# # To contribute improvements to CI/CD templates, please follow the Development guide at: # https://docs.gitlab.com/ee/development/cicd/templates.html # This specific template is located at: diff --git a/lib/gitlab/ci/templates/Elixir.gitlab-ci.yml b/lib/gitlab/ci/templates/Elixir.gitlab-ci.yml index 1eb920c7747..83ddce936e6 100644 --- a/lib/gitlab/ci/templates/Elixir.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Elixir.gitlab-ci.yml @@ -1,3 +1,6 @@ +# You can copy and paste this template into a new `.gitlab-ci.yml` file. +# You should not add this template to an existing `.gitlab-ci.yml` file by using the `include:` keyword. +# # To contribute improvements to CI/CD templates, please follow the Development guide at: # https://docs.gitlab.com/ee/development/cicd/templates.html # This specific template is located at: diff --git a/lib/gitlab/ci/templates/Flutter.gitlab-ci.yml b/lib/gitlab/ci/templates/Flutter.gitlab-ci.yml index a5c261e367a..021662ab416 100644 --- a/lib/gitlab/ci/templates/Flutter.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Flutter.gitlab-ci.yml @@ -1,3 +1,6 @@ +# You can copy and paste this template into a new `.gitlab-ci.yml` file. +# You should not add this template to an existing `.gitlab-ci.yml` file by using the `include:` keyword. +# # To contribute improvements to CI/CD templates, please follow the Development guide at: # https://docs.gitlab.com/ee/development/cicd/templates.html # This specific template is located at: diff --git a/lib/gitlab/ci/templates/Getting-Started.gitlab-ci.yml b/lib/gitlab/ci/templates/Getting-Started.gitlab-ci.yml index 21a599fc78d..464b81965f2 100644 --- a/lib/gitlab/ci/templates/Getting-Started.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Getting-Started.gitlab-ci.yml @@ -1,8 +1,3 @@ -# To contribute improvements to CI/CD templates, please follow the Development guide at: -# https://docs.gitlab.com/ee/development/cicd/templates.html -# This specific template is located at: -# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Getting-Started.gitlab-ci.yml - # This is a sample GitLab CI/CD configuration file that should run without any modifications. # It demonstrates a basic 3 stage CI/CD pipeline. Instead of real tests or scripts, # it uses echo commands to simulate the pipeline execution. @@ -11,6 +6,14 @@ # Stages run in sequential order, but jobs within stages run in parallel. # # For more information, see: https://docs.gitlab.com/ee/ci/yaml/index.html#stages +# +# You can copy and paste this template into a new `.gitlab-ci.yml` file. +# You should not add this template to an existing `.gitlab-ci.yml` file by using the `include:` keyword. +# +# To contribute improvements to CI/CD templates, please follow the Development guide at: +# https://docs.gitlab.com/ee/development/cicd/templates.html +# This specific template is located at: +# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Getting-Started.gitlab-ci.yml stages: # List of stages for jobs, and their order of execution - build diff --git a/lib/gitlab/ci/templates/Go.gitlab-ci.yml b/lib/gitlab/ci/templates/Go.gitlab-ci.yml index bd8e1020c4e..603aede4d46 100644 --- a/lib/gitlab/ci/templates/Go.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Go.gitlab-ci.yml @@ -1,3 +1,6 @@ +# You can copy and paste this template into a new `.gitlab-ci.yml` file. +# You should not add this template to an existing `.gitlab-ci.yml` file by using the `include:` keyword. +# # To contribute improvements to CI/CD templates, please follow the Development guide at: # https://docs.gitlab.com/ee/development/cicd/templates.html # This specific template is located at: diff --git a/lib/gitlab/ci/templates/Grails.gitlab-ci.yml b/lib/gitlab/ci/templates/Grails.gitlab-ci.yml index 7e59354c4a1..03c8941169f 100644 --- a/lib/gitlab/ci/templates/Grails.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Grails.gitlab-ci.yml @@ -1,8 +1,3 @@ -# To contribute improvements to CI/CD templates, please follow the Development guide at: -# https://docs.gitlab.com/ee/development/cicd/templates.html -# This specific template is located at: -# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Grails.gitlab-ci.yml - # This template uses the java:8 docker image because there isn't any # official Grails image at this moment # @@ -12,6 +7,14 @@ # Feel free to change GRAILS_VERSION version with your project version (3.0.1, 3.1.1,...) # Feel free to change GRADLE_VERSION version with your gradle project version (2.13, 2.14,...) # If you use Angular profile, this yml it's prepared to work with it +# +# You can copy and paste this template into a new `.gitlab-ci.yml` file. +# You should not add this template to an existing `.gitlab-ci.yml` file by using the `include:` keyword. +# +# To contribute improvements to CI/CD templates, please follow the Development guide at: +# https://docs.gitlab.com/ee/development/cicd/templates.html +# This specific template is located at: +# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Grails.gitlab-ci.yml image: java:8 diff --git a/lib/gitlab/ci/templates/Julia.gitlab-ci.yml b/lib/gitlab/ci/templates/Julia.gitlab-ci.yml index 4687a07d05b..34084272b29 100644 --- a/lib/gitlab/ci/templates/Julia.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Julia.gitlab-ci.yml @@ -1,8 +1,3 @@ -# To contribute improvements to CI/CD templates, please follow the Development guide at: -# https://docs.gitlab.com/ee/development/cicd/templates.html -# This specific template is located at: -# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Julia.gitlab-ci.yml - # This is an example .gitlab-ci.yml file to test (and optionally report the coverage # results of) your [Julia][1] packages. Please refer to the [documentation][2] # for more information about package development in Julia. @@ -12,6 +7,14 @@ # # [1]: http://julialang.org/ # [2]: https://docs.julialang.org/en/v1/manual/documentation/index.html +# +# You can copy and paste this template into a new `.gitlab-ci.yml` file. +# You should not add this template to an existing `.gitlab-ci.yml` file by using the `include:` keyword. +# +# To contribute improvements to CI/CD templates, please follow the Development guide at: +# https://docs.gitlab.com/ee/development/cicd/templates.html +# This specific template is located at: +# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Julia.gitlab-ci.yml # Below is the template to run your tests in Julia .test_template: &test_definition diff --git a/lib/gitlab/ci/templates/Laravel.gitlab-ci.yml b/lib/gitlab/ci/templates/Laravel.gitlab-ci.yml index 0ec67526234..3a490012f3d 100644 --- a/lib/gitlab/ci/templates/Laravel.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Laravel.gitlab-ci.yml @@ -1,3 +1,6 @@ +# You can copy and paste this template into a new `.gitlab-ci.yml` file. +# You should not add this template to an existing `.gitlab-ci.yml` file by using the `include:` keyword. +# # To contribute improvements to CI/CD templates, please follow the Development guide at: # https://docs.gitlab.com/ee/development/cicd/templates.html # This specific template is located at: diff --git a/lib/gitlab/ci/templates/Mono.gitlab-ci.yml b/lib/gitlab/ci/templates/Mono.gitlab-ci.yml index 2f214347ec3..65db649e22f 100644 --- a/lib/gitlab/ci/templates/Mono.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Mono.gitlab-ci.yml @@ -1,8 +1,3 @@ -# To contribute improvements to CI/CD templates, please follow the Development guide at: -# https://docs.gitlab.com/ee/development/cicd/templates.html -# This specific template is located at: -# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Mono.gitlab-ci.yml - # This is a simple gitlab continuous integration template (compatible with the shared runner provided on gitlab.com) # using the official mono docker image to build a visual studio project. # @@ -15,6 +10,14 @@ # # Please find the full example project here: # https://gitlab.com/tobiaskoch/gitlab-ci-example-mono +# +# You can copy and paste this template into a new `.gitlab-ci.yml` file. +# You should not add this template to an existing `.gitlab-ci.yml` file by using the `include:` keyword. +# +# To contribute improvements to CI/CD templates, please follow the Development guide at: +# https://docs.gitlab.com/ee/development/cicd/templates.html +# This specific template is located at: +# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Mono.gitlab-ci.yml # see https://hub.docker.com/_/mono/ image: mono:latest diff --git a/lib/gitlab/ci/templates/Nodejs.gitlab-ci.yml b/lib/gitlab/ci/templates/Nodejs.gitlab-ci.yml index 44370f896a7..7a4f7ed628b 100644 --- a/lib/gitlab/ci/templates/Nodejs.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Nodejs.gitlab-ci.yml @@ -1,3 +1,6 @@ +# You can copy and paste this template into a new `.gitlab-ci.yml` file. +# You should not add this template to an existing `.gitlab-ci.yml` file by using the `include:` keyword. +# # To contribute improvements to CI/CD templates, please follow the Development guide at: # https://docs.gitlab.com/ee/development/cicd/templates.html # This specific template is located at: diff --git a/lib/gitlab/ci/templates/OpenShift.gitlab-ci.yml b/lib/gitlab/ci/templates/OpenShift.gitlab-ci.yml index 7c8bbe464af..0eb3b483067 100644 --- a/lib/gitlab/ci/templates/OpenShift.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/OpenShift.gitlab-ci.yml @@ -1,3 +1,6 @@ +# You can copy and paste this template into a new `.gitlab-ci.yml` file. +# You should not add this template to an existing `.gitlab-ci.yml` file by using the `include:` keyword. +# # To contribute improvements to CI/CD templates, please follow the Development guide at: # https://docs.gitlab.com/ee/development/cicd/templates.html # This specific template is located at: diff --git a/lib/gitlab/ci/templates/PHP.gitlab-ci.yml b/lib/gitlab/ci/templates/PHP.gitlab-ci.yml index 4edc003a638..12640d28d29 100644 --- a/lib/gitlab/ci/templates/PHP.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/PHP.gitlab-ci.yml @@ -1,3 +1,6 @@ +# You can copy and paste this template into a new `.gitlab-ci.yml` file. +# You should not add this template to an existing `.gitlab-ci.yml` file by using the `include:` keyword. +# # To contribute improvements to CI/CD templates, please follow the Development guide at: # https://docs.gitlab.com/ee/development/cicd/templates.html # This specific template is located at: diff --git a/lib/gitlab/ci/templates/Ruby.gitlab-ci.yml b/lib/gitlab/ci/templates/Ruby.gitlab-ci.yml index 690a5a291e1..aab408aa830 100644 --- a/lib/gitlab/ci/templates/Ruby.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Ruby.gitlab-ci.yml @@ -1,3 +1,6 @@ +# You can copy and paste this template into a new `.gitlab-ci.yml` file. +# You should not add this template to an existing `.gitlab-ci.yml` file by using the `include:` keyword. +# # To contribute improvements to CI/CD templates, please follow the Development guide at: # https://docs.gitlab.com/ee/development/cicd/templates.html # This specific template is located at: diff --git a/lib/gitlab/ci/templates/Rust.gitlab-ci.yml b/lib/gitlab/ci/templates/Rust.gitlab-ci.yml index dafe3ca7bc7..a83f84da818 100644 --- a/lib/gitlab/ci/templates/Rust.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Rust.gitlab-ci.yml @@ -1,3 +1,6 @@ +# You can copy and paste this template into a new `.gitlab-ci.yml` file. +# You should not add this template to an existing `.gitlab-ci.yml` file by using the `include:` keyword. +# # To contribute improvements to CI/CD templates, please follow the Development guide at: # https://docs.gitlab.com/ee/development/cicd/templates.html # This specific template is located at: diff --git a/lib/gitlab/ci/templates/Scala.gitlab-ci.yml b/lib/gitlab/ci/templates/Scala.gitlab-ci.yml index de54d64dc42..26efe7a8908 100644 --- a/lib/gitlab/ci/templates/Scala.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Scala.gitlab-ci.yml @@ -1,3 +1,6 @@ +# You can copy and paste this template into a new `.gitlab-ci.yml` file. +# You should not add this template to an existing `.gitlab-ci.yml` file by using the `include:` keyword. +# # To contribute improvements to CI/CD templates, please follow the Development guide at: # https://docs.gitlab.com/ee/development/cicd/templates.html # This specific template is located at: diff --git a/lib/gitlab/ci/templates/Swift.gitlab-ci.yml b/lib/gitlab/ci/templates/Swift.gitlab-ci.yml index eedb3b7a310..3c4533d603e 100644 --- a/lib/gitlab/ci/templates/Swift.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/Swift.gitlab-ci.yml @@ -1,3 +1,6 @@ +# You can copy and paste this template into a new `.gitlab-ci.yml` file. +# You should not add this template to an existing `.gitlab-ci.yml` file by using the `include:` keyword. +# # To contribute improvements to CI/CD templates, please follow the Development guide at: # https://docs.gitlab.com/ee/development/cicd/templates.html # This specific template is located at: diff --git a/lib/gitlab/ci/templates/dotNET.gitlab-ci.yml b/lib/gitlab/ci/templates/dotNET.gitlab-ci.yml index 841f17767eb..50ce181095e 100644 --- a/lib/gitlab/ci/templates/dotNET.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/dotNET.gitlab-ci.yml @@ -23,6 +23,9 @@ # You need to have the network drive mapped as Local System user for gitlab-runner service to see it # The best way to persist the mapping is via a scheduled task # running the following batch command: net use P: \\x.x.x.x\Projects /u:your_user your_pass /persistent:yes +# +# You can copy and paste this template into a new `.gitlab-ci.yml` file. +# You should not add this template to an existing `.gitlab-ci.yml` file by using the `include:` keyword. # place project specific paths in variables to make the rest of the script more generic variables: diff --git a/lib/gitlab/ci/templates/iOS-Fastlane.gitlab-ci.yml b/lib/gitlab/ci/templates/iOS-Fastlane.gitlab-ci.yml index 0b75c298167..58e840da713 100644 --- a/lib/gitlab/ci/templates/iOS-Fastlane.gitlab-ci.yml +++ b/lib/gitlab/ci/templates/iOS-Fastlane.gitlab-ci.yml @@ -1,8 +1,3 @@ -# To contribute improvements to CI/CD templates, please follow the Development guide at: -# https://docs.gitlab.com/ee/development/cicd/templates.html -# This specific template is located at: -# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/iOS-Fastlane.gitlab-ci.yml - # This is a very simple template that mainly relies on FastLane to build and distribute your app. # Read more about how to use this template on the blog post https://about.gitlab.com/2019/03/06/ios-publishing-with-gitlab-and-fastlane/ # You will also need fastlane and signing configuration for this to work, along with a MacOS runner. @@ -15,6 +10,14 @@ # https://docs.gitlab.com/runner/security/#usage-of-shell-executor for additional # detail on what to keep in mind in this scenario. +# You can copy and paste this template into a new `.gitlab-ci.yml` file. +# You should not add this template to an existing `.gitlab-ci.yml` file by using the `include:` keyword. + +# To contribute improvements to CI/CD templates, please follow the Development guide at: +# https://docs.gitlab.com/ee/development/cicd/templates.html +# This specific template is located at: +# https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/iOS-Fastlane.gitlab-ci.yml + stages: - build - test diff --git a/lib/initializer_connections.rb b/lib/initializer_connections.rb new file mode 100644 index 00000000000..c8a6bb6c511 --- /dev/null +++ b/lib/initializer_connections.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +module InitializerConnections + # Prevents any database connections within the block + # by using an empty connection handler + # rubocop:disable Database/MultipleDatabases + def self.with_disabled_database_connections + return yield if Gitlab::Utils.to_boolean(ENV['SKIP_RAISE_ON_INITIALIZE_CONNECTIONS']) + + original_handler = ActiveRecord::Base.connection_handler + + dummy_handler = ActiveRecord::ConnectionAdapters::ConnectionHandler.new + ActiveRecord::Base.connection_handler = dummy_handler + + yield + + if dummy_handler&.connection_pool_names&.present? + raise "Unxpected connection_pools (#{dummy_handler.connection_pool_names}) ! Call `connects_to` before this block" + end + rescue ActiveRecord::ConnectionNotEstablished + message = "Database connection should not be called during initializers. Read more at https://docs.gitlab.com/ee/development/rails_initializers.html#database-connections-in-initializers" + + raise message + ensure + ActiveRecord::Base.connection_handler = original_handler + dummy_handler&.clear_all_connections! + end + # rubocop:enable Database/MultipleDatabases +end diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 035792e23df..b5422614ca9 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -3758,6 +3758,9 @@ msgstr "" msgid "All changes are committed" msgstr "" +msgid "All eligible users" +msgstr "" + msgid "All email addresses will be used to identify your commits." msgstr "" @@ -14125,9 +14128,6 @@ msgstr "" msgid "Elastic|None. Select projects to index." msgstr "" -msgid "Eligible users" -msgstr "" - msgid "Email" msgstr "" @@ -21916,6 +21916,9 @@ msgstr "" msgid "Iterations|All" msgstr "" +msgid "Iterations|Automatic scheduling" +msgstr "" + msgid "Iterations|Cadence configuration is invalid." msgstr "" @@ -21937,6 +21940,9 @@ msgstr "" msgid "Iterations|Create iteration" msgstr "" +msgid "Iterations|Create iterations automatically on a regular schedule." +msgstr "" + msgid "Iterations|Delete cadence" msgstr "" @@ -21967,6 +21973,9 @@ msgstr "" msgid "Iterations|Edit iteration cadence" msgstr "" +msgid "Iterations|Enable roll over" +msgstr "" + msgid "Iterations|Error loading iteration cadences." msgstr "" @@ -21979,6 +21988,9 @@ msgstr "" msgid "Iterations|Iterations are a way to track issues over a period of time, allowing teams to also track velocity and volatility metrics." msgstr "" +msgid "Iterations|Iterations are scheduled to start on %{weekday}s." +msgstr "" + msgid "Iterations|Learn more about automatic scheduling" msgstr "" @@ -22006,9 +22018,6 @@ msgstr "" msgid "Iterations|No iterations in cadence." msgstr "" -msgid "Iterations|No one can change this date after the cadence has begun." -msgstr "" - msgid "Iterations|No open iterations." msgstr "" @@ -23225,7 +23234,7 @@ msgstr "" msgid "License Compliance" msgstr "" -msgid "License Compliance| Used by" +msgid "License Compliance| Used by %{dependencies}" msgstr "" msgid "License compliance" @@ -38225,9 +38234,6 @@ msgstr "" msgid "Task list" msgstr "" -msgid "Task with ID: %{id} could not be found." -msgstr "" - msgid "TasksToBeDone|Create/import code into a project (repository)" msgstr "" @@ -42054,6 +42060,11 @@ msgstr "" msgid "Used" msgstr "" +msgid "Used by %d package" +msgid_plural "Used by %d packages" +msgstr[0] "" +msgstr[1] "" + msgid "Used by members to sign in to your group in GitLab" msgstr "" diff --git a/scripts/process_custom_semgrep_results.sh b/scripts/process_custom_semgrep_results.sh index 1fdd8e486f3..8370c95efca 100755 --- a/scripts/process_custom_semgrep_results.sh +++ b/scripts/process_custom_semgrep_results.sh @@ -5,7 +5,7 @@ echo "Processing vuln report" # Preparing the message for the comment that will be posted by the bot # Empty string if there are no findings jq -crM '.vulnerabilities | - map( select( .identifiers[0].name | test( "glappsec_" ) ) | + map( select( .identifiersprocess_custom_semgrep_results[0].name | test( "glappsec_" ) ) | "- `" + .location.file + "` line " + ( .location.start_line | tostring ) + ( if .location.start_line = .location.end_line then "" diff --git a/spec/controllers/search_controller_spec.rb b/spec/controllers/search_controller_spec.rb index e4a7a2fc410..b4d4e01e972 100644 --- a/spec/controllers/search_controller_spec.rb +++ b/spec/controllers/search_controller_spec.rb @@ -408,10 +408,11 @@ RSpec.describe SearchController do expect(payload[:metadata]['meta.search.filters.confidential']).to eq('true') expect(payload[:metadata]['meta.search.filters.state']).to eq('true') expect(payload[:metadata]['meta.search.project_ids']).to eq(%w(456 789)) - expect(payload[:metadata]['meta.search.search_level']).to eq('multi-project') + expect(payload[:metadata]['meta.search.type']).to eq('basic') + expect(payload[:metadata]['meta.search.level']).to eq('global') end - get :show, params: { scope: 'issues', search: 'hello world', group_id: '123', project_id: '456', project_ids: %w(456 789), search_level: 'multi-project', confidential: true, state: true, force_search_results: true } + get :show, params: { scope: 'issues', search: 'hello world', group_id: '123', project_id: '456', project_ids: %w(456 789), confidential: true, state: true, force_search_results: true } end it 'appends the default scope in meta.search.scope' do @@ -423,6 +424,16 @@ RSpec.describe SearchController do get :show, params: { search: 'hello world', group_id: '123', project_id: '456' } end + + it 'appends the search time based on the search' do + expect(controller).to receive(:append_info_to_payload).and_wrap_original do |method, payload| + method.call(payload) + + expect(payload[:metadata][:global_search_duration_s]).to be_a_kind_of(Numeric) + end + + get :show, params: { search: 'hello world', group_id: '123', project_id: '456' } + end end context 'abusive searches', :aggregate_failures do diff --git a/spec/features/merge_request/user_sees_versions_spec.rb b/spec/features/merge_request/user_sees_versions_spec.rb index 4465d7e29be..2c2a2dfd4a8 100644 --- a/spec/features/merge_request/user_sees_versions_spec.rb +++ b/spec/features/merge_request/user_sees_versions_spec.rb @@ -24,23 +24,21 @@ RSpec.describe 'Merge request > User sees versions', :js do visit diffs_project_merge_request_path(project, merge_request, params) end - shared_examples 'allows commenting' do |file_id:, line_code:, comment:| + shared_examples 'allows commenting' do |file_name:, line_text:, comment:| it do - diff_file_selector = ".diff-file[id='#{file_id}']" - line_code = "#{file_id}_#{line_code}" + page.within find_by_scrolling('.diff-file', text: file_name) do + line_code_element = page.find('.diff-grid-row', text: line_text) - page.within find_by_scrolling(diff_file_selector) do - line_code_element = first("[id='#{line_code}']") # scrolling to element's bottom is required in order for .hover action to work # otherwise, the element could be hidden underneath a sticky header scroll_to_elements_bottom(line_code_element) line_code_element.hover - first("[id='#{line_code}'] [role='button']").click + page.find("[data-testid='left-comment-button']", visible: true).click - page.within("form[data-line-code='#{line_code}']") do - fill_in "note[note]", with: comment - click_button('Add comment now') - end + expect(page).to have_selector("form", count: 1) + + fill_in("note[note]", with: comment) + click_button('Add comment now') wait_for_requests @@ -59,8 +57,8 @@ RSpec.describe 'Merge request > User sees versions', :js do end it_behaves_like 'allows commenting', - file_id: '7445606fbf8f3683cd42bdc54b05d7a0bc2dfc44', - line_code: '1_1', + file_name: '.gitmodules', + line_text: '[submodule "six"]', comment: 'Typo, please fix.' end @@ -107,8 +105,8 @@ RSpec.describe 'Merge request > User sees versions', :js do end it_behaves_like 'allows commenting', - file_id: '7445606fbf8f3683cd42bdc54b05d7a0bc2dfc44', - line_code: '2_2', + file_name: '.gitmodules', + line_text: 'path = six', comment: 'Typo, please fix.' end @@ -174,9 +172,9 @@ RSpec.describe 'Merge request > User sees versions', :js do end it_behaves_like 'allows commenting', - file_id: '7445606fbf8f3683cd42bdc54b05d7a0bc2dfc44', - line_code: '4_4', - comment: 'Typo, please fix.' + file_name: '.gitmodules', + line_text: '[submodule "gitlab-shell"]', + comment: 'Typo, please fix.' end describe 'compare with same version' do @@ -241,8 +239,8 @@ RSpec.describe 'Merge request > User sees versions', :js do end it_behaves_like 'allows commenting', - file_id: '2f6fcd96b88b36ce98c38da085c795a27d92a3dd', - line_code: '6_6', + file_name: 'files/ruby/popen.rb', + line_text: 'RuntimeError', comment: 'Typo, please fix.' end end diff --git a/spec/frontend/work_items/components/work_item_assignees_spec.js b/spec/frontend/work_items/components/work_item_assignees_spec.js index 591102fb572..299949a4baa 100644 --- a/spec/frontend/work_items/components/work_item_assignees_spec.js +++ b/spec/frontend/work_items/components/work_item_assignees_spec.js @@ -4,13 +4,14 @@ import VueApollo from 'vue-apollo'; import createMockApollo from 'helpers/mock_apollo_helper'; import waitForPromises from 'helpers/wait_for_promises'; import { mountExtended } from 'helpers/vue_test_utils_helper'; +import { mockTracking } from 'helpers/tracking_helper'; import { stripTypenames } from 'helpers/graphql_helpers'; import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants'; import userSearchQuery from '~/graphql_shared/queries/users_search.query.graphql'; import currentUserQuery from '~/graphql_shared/queries/current_user.query.graphql'; import workItemQuery from '~/work_items/graphql/work_item.query.graphql'; import WorkItemAssignees from '~/work_items/components/work_item_assignees.vue'; -import { i18n } from '~/work_items/constants'; +import { i18n, TASK_TYPE_NAME, TRACKING_CATEGORY_SHOW } from '~/work_items/constants'; import { temporaryConfig, resolvers } from '~/work_items/graphql/provider'; import { projectMembersResponseWithCurrentUser, @@ -50,6 +51,7 @@ describe('WorkItemAssignees component', () => { searchQueryHandler = successSearchQueryHandler, currentUserQueryHandler = successCurrentUserQueryHandler, allowsMultipleAssignees = true, + canUpdate = true, } = {}) => { const apolloProvider = createMockApollo( [ @@ -78,6 +80,8 @@ describe('WorkItemAssignees component', () => { assignees, workItemId, allowsMultipleAssignees, + workItemType: TASK_TYPE_NAME, + canUpdate, }, attachTo: document.body, apolloProvider, @@ -125,6 +129,18 @@ describe('WorkItemAssignees component', () => { expect(findTokenSelector().props('selectedTokens')).toEqual([mockAssignees[0]]); }); + it('passes `false` to `viewOnly` token selector prop if user can update assignees', () => { + createComponent(); + + expect(findTokenSelector().props('viewOnly')).toBe(false); + }); + + it('passes `true` to `viewOnly` token selector prop if user can not update assignees', () => { + createComponent({ canUpdate: false }); + + expect(findTokenSelector().props('viewOnly')).toBe(true); + }); + describe('when searching for users', () => { beforeEach(() => { createComponent(); @@ -357,4 +373,36 @@ describe('WorkItemAssignees component', () => { expect(findTokenSelector().props('containerClass')).toBe('gl-shadow-none!'); }); }); + + describe('tracking', () => { + let trackingSpy; + + beforeEach(() => { + createComponent(); + trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn); + }); + + afterEach(() => { + trackingSpy = null; + }); + + it('does not track updating assignees until token selector blur event', async () => { + findTokenSelector().vm.$emit('input', [mockAssignees[0]]); + await waitForPromises(); + + expect(trackingSpy).not.toHaveBeenCalled(); + }); + + it('tracks editing the assignees on token selector blur', async () => { + findTokenSelector().vm.$emit('input', [mockAssignees[0]]); + findTokenSelector().vm.$emit('blur', new FocusEvent({ relatedTarget: null })); + await waitForPromises(); + + expect(trackingSpy).toHaveBeenCalledWith(TRACKING_CATEGORY_SHOW, 'updated_assignees', { + category: TRACKING_CATEGORY_SHOW, + label: 'item_assignees', + property: 'type_Task', + }); + }); + }); }); diff --git a/spec/initializers/00_connection_logger_spec.rb b/spec/initializers/00_connection_logger_spec.rb deleted file mode 100644 index 8b288b463c4..00000000000 --- a/spec/initializers/00_connection_logger_spec.rb +++ /dev/null @@ -1,39 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -RSpec.describe ActiveRecord::ConnectionAdapters::PostgreSQLAdapter do # rubocop:disable RSpec/FilePath - before do - allow(PG).to receive(:connect) - end - - let(:conn_params) { PG::Connection.conndefaults_hash } - - context 'when warn_on_new_connection is enabled' do - before do - described_class.warn_on_new_connection = true - end - - it 'warns on new connection' do - expect(ActiveSupport::Deprecation) - .to receive(:warn).with(/Database connection should not be called during initializers/, anything) - - expect(PG).to receive(:connect).with(conn_params) - - described_class.new_client(conn_params) - end - end - - context 'when warn_on_new_connection is disabled' do - before do - described_class.warn_on_new_connection = false - end - - it 'does not warn on new connection' do - expect(ActiveSupport::Deprecation).not_to receive(:warn) - expect(PG).to receive(:connect).with(conn_params) - - described_class.new_client(conn_params) - end - end -end diff --git a/spec/lib/initializer_connections_spec.rb b/spec/lib/initializer_connections_spec.rb new file mode 100644 index 00000000000..4ca283c4f22 --- /dev/null +++ b/spec/lib/initializer_connections_spec.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe InitializerConnections do + describe '.with_disabled_database_connections', :reestablished_active_record_base do + def block_with_database_call + described_class.with_disabled_database_connections do + Project.first + end + end + + def block_with_error + described_class.with_disabled_database_connections do + raise "oops, an error" + end + end + + it 'prevents any database connection within the block' do + expect { block_with_database_call }.to raise_error(/Database connection should not be called during initializer/) + end + + it 'does not prevent database connection if SKIP_RAISE_ON_INITIALIZE_CONNECTIONS is set' do + stub_env('SKIP_RAISE_ON_INITIALIZE_CONNECTIONS', '1') + + expect { block_with_database_call }.not_to raise_error + end + + it 'prevents any database connection if SKIP_RAISE_ON_INITIALIZE_CONNECTIONS is false' do + stub_env('SKIP_RAISE_ON_INITIALIZE_CONNECTIONS', 'false') + + expect { block_with_database_call }.to raise_error(/Database connection should not be called during initializer/) + end + + it 'restores original connection handler' do + # rubocop:disable Database/MultipleDatabases + original_handler = ActiveRecord::Base.connection_handler + + expect { block_with_database_call }.to raise_error(/Database connection should not be called during initializer/) + + expect(ActiveRecord::Base.connection_handler).to eq(original_handler) + # rubocop:enabled Database/MultipleDatabases + end + + it 'restores original connection handler even there is an error' do + # rubocop:disable Database/MultipleDatabases + original_handler = ActiveRecord::Base.connection_handler + + expect { block_with_error }.to raise_error(/an error/) + + expect(ActiveRecord::Base.connection_handler).to eq(original_handler) + # rubocop:enabled Database/MultipleDatabases + end + + it 'raises if any new connection_pools are established in the block' do + expect do + described_class.with_disabled_database_connections do + ApplicationRecord.connects_to database: { writing: :main, reading: :main } + end + end.to raise_error(/Unxpected connection_pools/) + end + end +end diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index 5a50ce0911f..081fa6cbbae 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -3953,7 +3953,21 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do context 'when pipeline status is running' do let(:pipeline) { create(:ci_pipeline, :running) } - it { is_expected.to be_falsey } + context 'with mr_show_reports_immediately flag enabled' do + before do + stub_feature_flags(mr_show_reports_immediately: project) + end + + it { expect(subject).to be_truthy } + end + + context 'with mr_show_reports_immediately flag disabled' do + before do + stub_feature_flags(mr_show_reports_immediately: false) + end + + it { expect(subject).to be_falsey } + end end context 'when pipeline status is success' do @@ -4027,7 +4041,21 @@ RSpec.describe Ci::Pipeline, :mailer, factory_default: :keep do context 'when pipeline status is running' do let(:pipeline) { create(:ci_pipeline, :running) } - it { expect(subject).to be_falsey } + context 'with mr_show_reports_immediately flag enabled' do + before do + stub_feature_flags(mr_show_reports_immediately: project) + end + + it { expect(subject).to be_truthy } + end + + context 'with mr_show_reports_immediately flag disabled' do + before do + stub_feature_flags(mr_show_reports_immediately: false) + end + + it { expect(subject).to be_falsey } + end end context 'when pipeline status is success' do diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index de478edf96a..664cdb27290 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -1884,6 +1884,14 @@ RSpec.describe Namespace do end end + describe '#emails_enabled?' do + it "is the opposite of emails_disabled" do + group = create(:group, emails_disabled: false) + + expect(group.emails_enabled?).to be_truthy + end + end + describe '#pages_virtual_domain' do let(:project) { create(:project, namespace: namespace) } let(:virtual_domain) { namespace.pages_virtual_domain } diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 74a4a023a20..2171ee752fd 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -3595,6 +3595,14 @@ RSpec.describe Project, factory_default: :keep do end end + describe '#emails_enabled?' do + let(:project) { build(:project, emails_disabled: false) } + + it "is the opposite of emails_disabled" do + expect(project.emails_enabled?).to be_truthy + end + end + describe '#lfs_enabled?' do let(:namespace) { create(:namespace) } let(:project) { build(:project, namespace: namespace) } diff --git a/spec/requests/api/graphql/mutations/work_items/create_spec.rb b/spec/requests/api/graphql/mutations/work_items/create_spec.rb index 3496ab38ee8..911568bc39f 100644 --- a/spec/requests/api/graphql/mutations/work_items/create_spec.rb +++ b/spec/requests/api/graphql/mutations/work_items/create_spec.rb @@ -136,6 +136,20 @@ RSpec.describe 'Create a work item' do end end end + + context 'when unsupported widget input is sent' do + let(:input) do + { + 'title' => 'new title', + 'description' => 'new description', + 'workItemTypeId' => WorkItems::Type.default_by_type(:test_case).to_global_id.to_s, + 'hierarchyWidget' => {} + } + end + + it_behaves_like 'a mutation that returns top-level errors', + errors: ['Following widget keys are not supported by Test Case type: [:hierarchy_widget]'] + end end context 'when the work_items feature flag is disabled' do diff --git a/spec/requests/api/graphql/mutations/work_items/update_spec.rb b/spec/requests/api/graphql/mutations/work_items/update_spec.rb index b5f0eb7fbf6..77f7b9bacef 100644 --- a/spec/requests/api/graphql/mutations/work_items/update_spec.rb +++ b/spec/requests/api/graphql/mutations/work_items/update_spec.rb @@ -71,6 +71,20 @@ RSpec.describe 'Update a work item' do end end + context 'when unsupported widget input is sent' do + let_it_be(:test_case) { create(:work_item_type, :default, :test_case, name: 'some_test_case_name') } + let_it_be(:work_item) { create(:work_item, work_item_type: test_case, project: project) } + + let(:input) do + { + 'hierarchyWidget' => {} + } + end + + it_behaves_like 'a mutation that returns top-level errors', + errors: ["Following widget keys are not supported by some_test_case_name type: [:hierarchy_widget]"] + end + it_behaves_like 'has spam protection' do let(:mutation_class) { ::Mutations::WorkItems::Update } end @@ -295,6 +309,19 @@ RSpec.describe 'Update a work item' do end end + context 'when there is a mix of existing and non existing work items' do + let(:children_ids) { [valid_child1.to_global_id.to_s, "gid://gitlab/WorkItem/#{non_existing_record_id}"] } + + it 'returns a top level error and does not add valid work item' do + expect do + post_graphql_mutation(mutation, current_user: current_user) + work_item.reload + end.not_to change(work_item.work_item_children, :count) + + expect(graphql_errors.first['message']).to include('No object found for `childrenIds') + end + end + context 'when child work item type is valid' do let(:children_ids) { [valid_child1.to_global_id.to_s, valid_child2.to_global_id.to_s] } diff --git a/spec/services/work_items/parent_links/create_service_spec.rb b/spec/services/work_items/parent_links/create_service_spec.rb index a2f695900ae..85b0ee040cd 100644 --- a/spec/services/work_items/parent_links/create_service_spec.rb +++ b/spec/services/work_items/parent_links/create_service_spec.rb @@ -49,19 +49,19 @@ RSpec.describe WorkItems::ParentLinks::CreateService do end context 'when work item not found' do - let(:params) { { issuable_references: [invalid_task.id] } } + let(:params) { { issuable_references: [invalid_task] } } it_behaves_like 'returns not found error' end context 'when user has no permission to link work items' do - let(:params) { { issuable_references: [guest_task.id] } } + let(:params) { { issuable_references: [guest_task] } } it_behaves_like 'returns not found error' end context 'child and parent are the same work item' do - let(:params) { { issuable_references: [work_item.id] } } + let(:params) { { issuable_references: [work_item] } } it 'no relationship is created' do expect { subject }.not_to change(parent_link_class, :count) @@ -69,7 +69,7 @@ RSpec.describe WorkItems::ParentLinks::CreateService do end context 'when there are tasks to relate' do - let(:params) { { issuable_references: [task1.id, task2.id] } } + let(:params) { { issuable_references: [task1, task2] } } it 'creates relationships', :aggregate_failures do expect { subject }.to change(parent_link_class, :count).by(2) @@ -85,7 +85,7 @@ RSpec.describe WorkItems::ParentLinks::CreateService do end context 'when task is already assigned' do - let(:params) { { issuable_references: [task.id, task2.id] } } + let(:params) { { issuable_references: [task, task2] } } it 'creates links only for non related tasks' do expect { subject }.to change(parent_link_class, :count).by(1) @@ -97,7 +97,7 @@ RSpec.describe WorkItems::ParentLinks::CreateService do context 'when there are invalid children' do let_it_be(:issue) { create(:work_item, project: project) } - let(:params) { { issuable_references: [task1.id, issue.id, other_project_task.id] } } + let(:params) { { issuable_references: [task1, issue, other_project_task] } } it 'creates links only for valid children' do expect { subject }.to change { parent_link_class.count }.by(1) @@ -124,7 +124,7 @@ RSpec.describe WorkItems::ParentLinks::CreateService do end context 'when max depth is reached' do - let(:params) { { issuable_references: [task2.id] } } + let(:params) { { issuable_references: [task2] } } before do stub_const("#{parent_link_class}::MAX_CHILDREN", 1) @@ -138,17 +138,11 @@ RSpec.describe WorkItems::ParentLinks::CreateService do end context 'when params include invalid ids' do - let(:params) { { issuable_references: [task1.id, invalid_task.id] } } + let(:params) { { issuable_references: [task1, invalid_task] } } it 'creates links only for valid IDs' do expect { subject }.to change(parent_link_class, :count).by(1) end - - it 'returns error for invalid ID' do - message = "Task with ID: #{invalid_task.id} could not be found." - - expect(subject).to eq(service_error(message, http_status: 422)) - end end context 'when user is a guest' do diff --git a/spec/services/work_items/update_service_spec.rb b/spec/services/work_items/update_service_spec.rb index 3423b92e35c..b17c9ffb4fb 100644 --- a/spec/services/work_items/update_service_spec.rb +++ b/spec/services/work_items/update_service_spec.rb @@ -148,7 +148,7 @@ RSpec.describe WorkItems::UpdateService do let(:opts) { { title: 'changed' } } let_it_be(:child_work_item) { create(:work_item, :task, project: project) } - let(:widget_params) { { hierarchy_widget: { children_ids: [child_work_item.id] } } } + let(:widget_params) { { hierarchy_widget: { children: [child_work_item] } } } it 'updates the children of the work item' do expect do diff --git a/spec/services/work_items/widgets/hierarchy_service/update_service_spec.rb b/spec/services/work_items/widgets/hierarchy_service/update_service_spec.rb index c48e17431bf..4f6ff1b8676 100644 --- a/spec/services/work_items/widgets/hierarchy_service/update_service_spec.rb +++ b/spec/services/work_items/widgets/hierarchy_service/update_service_spec.rb @@ -21,8 +21,8 @@ RSpec.describe WorkItems::Widgets::HierarchyService::UpdateService do describe '#update' do subject { described_class.new(widget: widget, current_user: user).before_update_in_transaction(params: params) } - context 'when parent and children_ids params are present' do - let(:params) { { parent: parent_work_item, children_ids: [child_work_item.id] } } + context 'when parent and children params are present' do + let(:params) { { parent: parent_work_item, children: [child_work_item] } } it_behaves_like 'raises a WidgetError' do let(:message) { 'A Work Item can be a parent or a child, but not both.' } @@ -35,7 +35,7 @@ RSpec.describe WorkItems::Widgets::HierarchyService::UpdateService do let_it_be(:child_work_item4) { create(:work_item, :task, project: project) } context 'when work_items_hierarchy feature flag is disabled' do - let(:params) { { children_ids: [child_work_item4.id] }} + let(:params) { { children: [child_work_item4] }} before do stub_feature_flags(work_items_hierarchy: false) @@ -47,7 +47,7 @@ RSpec.describe WorkItems::Widgets::HierarchyService::UpdateService do end context 'when user has insufficient permissions to link work items' do - let(:params) { { children_ids: [child_work_item4.id] }} + let(:params) { { children: [child_work_item4] }} it_behaves_like 'raises a WidgetError' do let(:message) { not_found_error } @@ -60,7 +60,7 @@ RSpec.describe WorkItems::Widgets::HierarchyService::UpdateService do end context 'with valid params' do - let(:params) { { children_ids: [child_work_item2.id, child_work_item3.id] }} + let(:params) { { children: [child_work_item2, child_work_item3] }} it 'correctly sets work item parent' do subject @@ -71,7 +71,7 @@ RSpec.describe WorkItems::Widgets::HierarchyService::UpdateService do end context 'when child is already assigned' do - let(:params) { { children_ids: [child_work_item.id] }} + let(:params) { { children: [child_work_item] }} it_behaves_like 'raises a WidgetError' do let(:message) { 'Task(s) already assigned' } @@ -81,7 +81,7 @@ RSpec.describe WorkItems::Widgets::HierarchyService::UpdateService do context 'when child type is invalid' do let_it_be(:child_issue) { create(:work_item, project: project) } - let(:params) { { children_ids: [child_issue.id] }} + let(:params) { { children: [child_issue] }} it_behaves_like 'raises a WidgetError' do let(:message) do |