diff --git a/.gitlab/ci/as-if-jh.gitlab-ci.yml b/.gitlab/ci/as-if-jh.gitlab-ci.yml index 1c7055b1f94..6c1c3357089 100644 --- a/.gitlab/ci/as-if-jh.gitlab-ci.yml +++ b/.gitlab/ci/as-if-jh.gitlab-ci.yml @@ -1,6 +1,7 @@ .as-if-jh-sandbox-variables: variables: AS_IF_JH_BRANCH: "as-if-jh/${CI_COMMIT_REF_NAME}" + JH_MIRROR_REPOSITORY: "https://dummy:${ADD_JH_FILES_TOKEN}@gitlab.com/gitlab-org/gitlab-jh-mirrors/gitlab.git" SANDBOX_REPOSITORY: "https://dummy:${AS_IF_JH_TOKEN}@gitlab.com/gitlab-org-sandbox/gitlab-jh-validation.git" .shared-as-if-jh: @@ -13,20 +14,12 @@ add-jh-files: extends: - .shared-as-if-jh - - .with_secret - .as-if-jh:rules:prepare-as-if-jh image: ${GITLAB_DEPENDENCY_PROXY_ADDRESS}ruby:${RUBY_VERSION} stage: prepare needs: [] - secrets: - ADD_JH_FILES_TOKEN: - gcp_secret_manager: - name: ADD_JH_FILES_TOKEN - token: $GCP_ID_TOKEN before_script: - source ./scripts/utils.sh - - export ADD_JH_FILES_TOKEN="$(cat ${ADD_JH_FILES_TOKEN})" - - export JH_MIRROR_REPOSITORY="https://dummy:${ADD_JH_FILES_TOKEN}@gitlab.com/gitlab-org/gitlab-jh-mirrors/gitlab.git" - source ./scripts/setup/as-if-jh.sh - install_gitlab_gem script: diff --git a/.gitlab/ci/global.gitlab-ci.yml b/.gitlab/ci/global.gitlab-ci.yml index 2e42283eff0..2974ace34c4 100644 --- a/.gitlab/ci/global.gitlab-ci.yml +++ b/.gitlab/ci/global.gitlab-ci.yml @@ -514,8 +514,3 @@ curl -f --header "Private-Token: ${PROJECT_TOKEN_FOR_CI_SCRIPTS_API_USAGE}" "${url}" --create-dirs --output scripts/utils.sh - source scripts/utils.sh - run_timed_command "download_files ${FILES_TO_DOWNLOAD}" - -.with_secret: - id_tokens: - GCP_ID_TOKEN: - aud: https://iam.googleapis.com/projects/${GCP_PROJECT_NUMBER}/locations/global/workloadIdentityPools/${GCP_WORKLOAD_IDENTITY_FEDERATION_POOL_ID}/providers/${GCP_WORKLOAD_IDENTITY_FEDERATION_PROVIDER_ID} diff --git a/.gitlab/issue_templates/SAST Rule Enhancement.md b/.gitlab/issue_templates/SAST Rule Enhancement.md index 211b4a13591..316997ace7a 100644 --- a/.gitlab/issue_templates/SAST Rule Enhancement.md +++ b/.gitlab/issue_templates/SAST Rule Enhancement.md @@ -1,5 +1,4 @@ - -<-- + ### Desired Change -<-- + @@ -21,7 +20,7 @@ REPLACE: This issue is aimed at creating, embedding, or enhancing a GitLab SAST ## Implementation Plan -### Assesment +### Assessment #### If Creating a New Rule @@ -95,14 +94,14 @@ If the addition, inclusion or adaptation of a rule addressing the Desired Change - [ ] Clone our [Real World Test Projects](https://gitlab.com/gitlab-org/security-products/tests/sast-rules-apps/) and extend it with your MRE demonstrating the problem. Alternatively, discuss the creation of a new folder or repository if none fits. - [ ] Push the changes to [gitlab-org/security-products/tests/sast-rules-apps](https://gitlab.com/gitlab-org/security-products/tests/sast-rules-apps/) as a feature branch if you have access; otherwise push it to a personal fork of the project - [ ] Create a new MR and mention this issue in it so they are linked. -- [ ] A member of the @gitlab-org/secure/vulnerability-research team will assign themselves as reviewer shortly, work with them to finalise and merge your work. +- [ ] A member of the `@gitlab-org/secure/vulnerability-research` team will assign themselves as reviewer shortly, work with them to finalise and merge your work. #### Merge the Rule - [ ] Push the changes to `sast-rules` as a feature branch to [gitlab-org/security-products/sast-rules](https://gitlab.com/gitlab-org/security-products/sast-rules/) if you have access; otherwise push it to a personal fork of the project. - [ ] Create the MR and mention this issue in it so they are linked. -- [ ] A member of the @gitlab-org/secure/vulnerability-research team will assign themselves as reviewer shortly, work with them to finalise and merge your work. +- [ ] A member of the `@gitlab-org/secure/vulnerability-research` team will assign themselves as reviewer shortly, work with them to finalise and merge your work. - [ ] Find the [latest sast-rules release MR](https://gitlab.com/gitlab-org/security-products/sast-rules/-/merge_requests?scope=all&state=opened&search=draft%3A+Release) and add a line to CHANGELOG.md detailing briefly the changes performed, their intent and the MR ID where this work was done. ``` diff --git a/Gemfile b/Gemfile index 01808e00091..6c452d26358 100644 --- a/Gemfile +++ b/Gemfile @@ -560,7 +560,7 @@ gem 'spamcheck', '~> 1.3.0' # rubocop:todo Gemfile/MissingFeatureCategory gem 'gitaly', '~> 16.9.0-rc3', feature_category: :gitaly # KAS GRPC protocol definitions -gem 'kas-grpc', '~> 0.3.0', feature_category: :deployment_management +gem 'kas-grpc', '~> 0.4.0', feature_category: :deployment_management gem 'grpc', '~> 1.60.0' # rubocop:todo Gemfile/MissingFeatureCategory diff --git a/Gemfile.checksum b/Gemfile.checksum index 6f0edbf9de8..6edc3cfb8f1 100644 --- a/Gemfile.checksum +++ b/Gemfile.checksum @@ -341,7 +341,7 @@ {"name":"kaminari-actionview","version":"1.2.2","platform":"ruby","checksum":"1330f6fc8b59a4a4ef6a549ff8a224797289ebf7a3a503e8c1652535287cc909"}, {"name":"kaminari-activerecord","version":"1.2.2","platform":"ruby","checksum":"0dd3a67bab356a356f36b3b7236bcb81cef313095365befe8e98057dd2472430"}, {"name":"kaminari-core","version":"1.2.2","platform":"ruby","checksum":"3bd26fec7370645af40ca73b9426a448d09b8a8ba7afa9ba3c3e0d39cdbb83ff"}, -{"name":"kas-grpc","version":"0.3.0","platform":"ruby","checksum":"78dfc454e6725e6354e66e9e65ea87b6ad0888f52d20bf41775e5400d74dad8a"}, +{"name":"kas-grpc","version":"0.4.0","platform":"ruby","checksum":"bb21845032b443289b20be8ff8ae22a35abedb3ed17e60ed10b6e5f05bc6738d"}, {"name":"knapsack","version":"1.21.1","platform":"ruby","checksum":"82f70422adebcacec1b514f6ebff65265fc85d836e3c320718a160d8ac41cf14"}, {"name":"kramdown","version":"2.3.2","platform":"ruby","checksum":"cb4530c2e9d16481591df2c9336723683c354e5416a5dd3e447fa48215a6a71c"}, {"name":"kramdown-parser-gfm","version":"1.1.0","platform":"ruby","checksum":"fb39745516427d2988543bf01fc4cf0ab1149476382393e0e9c48592f6581729"}, diff --git a/Gemfile.lock b/Gemfile.lock index 5953f0452c3..087da2cb58d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1009,7 +1009,7 @@ GEM activerecord kaminari-core (= 1.2.2) kaminari-core (1.2.2) - kas-grpc (0.3.0) + kas-grpc (0.4.0) grpc (~> 1.0) knapsack (1.21.1) rake @@ -1987,7 +1987,7 @@ DEPENDENCIES jsonb_accessor (~> 1.3.10) jwt (~> 2.5) kaminari (~> 1.2.2) - kas-grpc (~> 0.3.0) + kas-grpc (~> 0.4.0) knapsack (~> 1.21.1) kramdown (~> 2.3.1) kubeclient (~> 4.11.0) diff --git a/app/assets/javascripts/boards/components/board_app.vue b/app/assets/javascripts/boards/components/board_app.vue index 08a167e53aa..4683f47e60a 100644 --- a/app/assets/javascripts/boards/components/board_app.vue +++ b/app/assets/javascripts/boards/components/board_app.vue @@ -51,7 +51,7 @@ export default { }; }, result({ data: { activeBoardItem } }) { - if (activeBoardItem) { + if (activeBoardItem && activeBoardItem.listId !== null) { this.setActiveId(''); } }, diff --git a/app/assets/javascripts/boards/components/board_card.vue b/app/assets/javascripts/boards/components/board_card.vue index 6966a4e5d48..82633a28b33 100644 --- a/app/assets/javascripts/boards/components/board_card.vue +++ b/app/assets/javascripts/boards/components/board_card.vue @@ -108,6 +108,7 @@ export default { mutation: setActiveBoardItemMutation, variables: { boardItem: this.isActive ? null : this.item, + listId: this.list.id, isIssue: this.isActive ? undefined : this.isIssueBoard, }, }); @@ -122,7 +123,7 @@ export default { }); await this.$apollo.mutate({ mutation: setActiveBoardItemMutation, - variables: { boardItem: null }, + variables: { boardItem: null, listId: null }, }); } this.$apollo.mutate({ diff --git a/app/assets/javascripts/boards/components/board_content_sidebar.vue b/app/assets/javascripts/boards/components/board_content_sidebar.vue index 37f2d9ce459..eb5098e8f09 100644 --- a/app/assets/javascripts/boards/components/board_content_sidebar.vue +++ b/app/assets/javascripts/boards/components/board_content_sidebar.vue @@ -17,7 +17,7 @@ import SidebarSubscriptionsWidget from '~/sidebar/components/subscriptions/sideb import SidebarTodoWidget from '~/sidebar/components/todo_toggle/sidebar_todo_widget.vue'; import SidebarLabelsWidget from '~/sidebar/components/labels/labels_select_widget/labels_select_root.vue'; import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; -import { setError } from '../graphql/cache_updates'; +import { setError, updateListWeightCache } from '../graphql/cache_updates'; export default { components: { @@ -163,9 +163,14 @@ export default { mutation: setActiveBoardItemMutation, variables: { boardItem: null, + listId: null, }, }); }, + updateListTotalWeight({ weight }) { + const { cache } = this.$apollo.provider.clients.defaultClient; + updateListWeightCache({ weight, listId: this.activeBoardCard.listId, cache }); + }, }, }; @@ -280,6 +285,7 @@ export default { :iid="activeBoardIssuable.iid" :full-path="projectPathForActiveIssue" :issuable-type="issuableType" + @weightUpdated="updateListTotalWeight" /> ({ + boardList: { + ...boardList, + totalIssueWeight: toNumber(boardList.totalIssueWeight) + weight, + }, + }), + ); +} + export function setError({ message, error, captureError = true }) { defaultClient.mutate({ mutation: setErrorMutation, diff --git a/app/assets/javascripts/boards/graphql/client/active_board_item.query.graphql b/app/assets/javascripts/boards/graphql/client/active_board_item.query.graphql index 81b1b68a038..83c2aadccd6 100644 --- a/app/assets/javascripts/boards/graphql/client/active_board_item.query.graphql +++ b/app/assets/javascripts/boards/graphql/client/active_board_item.query.graphql @@ -3,5 +3,6 @@ query activeBoardItem { activeBoardItem @client { ...Issue + listId } } diff --git a/app/assets/javascripts/boards/graphql/client/set_active_board_item.mutation.graphql b/app/assets/javascripts/boards/graphql/client/set_active_board_item.mutation.graphql index cce558c649e..791e5f6de21 100644 --- a/app/assets/javascripts/boards/graphql/client/set_active_board_item.mutation.graphql +++ b/app/assets/javascripts/boards/graphql/client/set_active_board_item.mutation.graphql @@ -1,7 +1,8 @@ #import "ee_else_ce/boards/graphql/issue.fragment.graphql" -mutation setActiveBoardItem($boardItem: Issue) { - setActiveBoardItem(boardItem: $boardItem) @client { +mutation setActiveBoardItem($boardItem: Issue, $listId: ListID!) { + setActiveBoardItem(boardItem: $boardItem, listId: $listId) @client { ...Issue + listId } } diff --git a/app/assets/javascripts/graphql_shared/issuable_client.js b/app/assets/javascripts/graphql_shared/issuable_client.js index 8e19de9f7c2..c4e1a481d49 100644 --- a/app/assets/javascripts/graphql_shared/issuable_client.js +++ b/app/assets/javascripts/graphql_shared/issuable_client.js @@ -220,12 +220,12 @@ export const resolvers = { }); cache.writeQuery({ query: getIssueStateQuery, data }); }, - setActiveBoardItem(_, { boardItem }, { cache }) { + setActiveBoardItem(_, { boardItem, listId }, { cache }) { cache.writeQuery({ query: activeBoardItemQuery, - data: { activeBoardItem: boardItem }, + data: { activeBoardItem: { ...boardItem, listId } }, }); - return boardItem; + return { ...boardItem, listId }; }, setSelectedBoardItems(_, { itemId }, { cache }) { const sourceData = cache.readQuery({ query: selectedBoardItemsQuery }); diff --git a/app/assets/stylesheets/page_bundles/login.scss b/app/assets/stylesheets/page_bundles/login.scss index 3aefcabcf08..7b9950b360a 100644 --- a/app/assets/stylesheets/page_bundles/login.scss +++ b/app/assets/stylesheets/page_bundles/login.scss @@ -6,12 +6,6 @@ max-width: 960px; } - .borderless { - .login-box { - box-shadow: none; - } - } - .g-recaptcha { > div { margin-left: auto; @@ -20,16 +14,11 @@ } .new-session-tabs { - &.nav-links-unboxed { - border-color: transparent; - box-shadow: none; + display: flex; + border-color: transparent; - .nav-item { - border-left: 0; - border-right: 0; - border-bottom: 1px solid $gray-100; - background-color: transparent; - } + .nav-item { + border-bottom: 1px solid $gray-100; .nav-link.active { .gl-dark & { @@ -38,28 +27,9 @@ } } - display: flex; - box-shadow: 0 0 0 1px $border-color; - border-top-right-radius: $border-radius-default; - border-top-left-radius: $border-radius-default; - li { flex: 1; text-align: center; - border-left: 1px solid $border-color; - - &:first-of-type { - border-left: 0; - border-top-left-radius: $border-radius-default; - } - - &:last-of-type { - border-top-right-radius: $border-radius-default; - } - - &:not(.active) { - background-color: $gray-light; - } a { width: 100%; diff --git a/app/controllers/concerns/parse_commit_date.rb b/app/controllers/concerns/parse_commit_date.rb new file mode 100644 index 00000000000..3d881ae09c6 --- /dev/null +++ b/app/controllers/concerns/parse_commit_date.rb @@ -0,0 +1,10 @@ +# frozen_string_literal: true + +module ParseCommitDate + extend ActiveSupport::Concern + + def convert_date_to_epoch(date) + Date.strptime(date, "%Y-%m-%d")&.to_time&.to_i if date + rescue Date::Error, TypeError + end +end diff --git a/app/controllers/projects/commits_controller.rb b/app/controllers/projects/commits_controller.rb index 2d2712ebe4d..915b6b6e9b8 100644 --- a/app/controllers/projects/commits_controller.rb +++ b/app/controllers/projects/commits_controller.rb @@ -5,6 +5,7 @@ require "base64" class Projects::CommitsController < Projects::ApplicationController include ExtractsPath include RendersCommits + include ParseCommitDate COMMITS_DEFAULT_LIMIT = 40 prepend_before_action(only: [:show]) { authenticate_sessionless_user!(:rss) } @@ -75,6 +76,8 @@ class Projects::CommitsController < Projects::ApplicationController @offset = (permitted_params[:offset] || 0).to_i search = permitted_params[:search] author = permitted_params[:author] + committed_before = convert_date_to_epoch(permitted_params[:committed_before]) + committed_after = convert_date_to_epoch(permitted_params[:committed_after]) # fully_qualified_ref is available in some situations from ExtractsRef ref = @fully_qualified_ref || @ref @@ -89,6 +92,8 @@ class Projects::CommitsController < Projects::ApplicationController offset: @offset } options[:author] = author if author.present? + options[:before] = committed_before if committed_before.present? + options[:after] = committed_after if committed_after.present? @repository.commits(ref, **options) end @@ -101,6 +106,6 @@ class Projects::CommitsController < Projects::ApplicationController end def permitted_params - params.permit(:limit, :offset, :search, :author) + params.permit(:limit, :offset, :search, :author, :committed_before, :committed_after) end end diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 12984c36d4c..7755b346b81 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -12,6 +12,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo include DiffHelper include Gitlab::Cache::Helpers include MergeRequestsHelper + include ParseCommitDate prepend_before_action(only: [:index]) { authenticate_sessionless_user!(:rss) } skip_before_action :merge_request, only: [:index, :bulk_update, :export_csv] @@ -639,11 +640,6 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo diff_head: true ) end - - def convert_date_to_epoch(date) - Date.strptime(date, "%Y-%m-%d")&.to_time&.to_i if date - rescue Date::Error, TypeError - end end Projects::MergeRequestsController.prepend_mod_with('Projects::MergeRequestsController') diff --git a/app/finders/repositories/changelog_tag_finder.rb b/app/finders/repositories/changelog_tag_finder.rb index 7dd7404730f..d90ac6cc98f 100644 --- a/app/finders/repositories/changelog_tag_finder.rb +++ b/app/finders/repositories/changelog_tag_finder.rb @@ -33,40 +33,16 @@ module Repositories def execute(new_version) tags = {} - versions = [new_version] - begin - regex = Gitlab::UntrustedRegexp.new(@regex) - rescue RegexpError => e - # The error messages produced by default are not very helpful, so we - # raise a better one here. We raise the specific error here so its - # message is displayed in the API (where we catch this specific - # error). - raise( - Gitlab::Changelog::Error, - "The regular expression to use for finding the previous tag for a version is invalid: #{e.message}" - ) - end + requested_version = extract_version(new_version) + return unless requested_version + + versions = [requested_version] @project.repository.tags.each do |tag| - matches = regex.match(tag.name) + version = extract_version(tag.name) - next unless matches - - # When using this class for generating changelog data for a range of - # commits, we want to compare against the tag of the last _stable_ - # release; not some random RC that came after that. - next if matches[:pre] - - major = matches[:major] - minor = matches[:minor] - patch = matches[:patch] - build = matches[:meta] - - next unless major && minor && patch - - version = "#{major}.#{minor}.#{patch}" - version += "+#{build}" if build + next unless version tags[version] = tag versions << version @@ -74,9 +50,47 @@ module Repositories VersionSorter.sort!(versions) - index = versions.index(new_version) + index = versions.index(requested_version) tags[versions[index - 1]] if index&.positive? end + + private + + def version_matcher + @version_matcher ||= Gitlab::UntrustedRegexp.new(@regex) + rescue RegexpError => e + # The error messages produced by default are not very helpful, so we + # raise a better one here. We raise the specific error here so its + # message is displayed in the API (where we catch this specific + # error). + raise( + Gitlab::Changelog::Error, + "The regular expression to use for finding the previous tag for a version is invalid: #{e.message}" + ) + end + + def extract_version(string) + matches = version_matcher.match(string) + + return unless matches + + # When using this class for generating changelog data for a range of + # commits, we want to compare against the tag of the last _stable_ + # release; not some random RC that came after that. + return if matches[:pre] + + major = matches[:major] + minor = matches[:minor] + patch = matches[:patch] + build = matches[:meta] + + return unless major && minor && patch + + version = "#{major}.#{minor}.#{patch}" + version += "+#{build}" if build + + version + end end end diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index c1f349d9329..ff012368e85 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -581,9 +581,7 @@ module Ci end def self.use_partition_id_filter? - ::Gitlab::SafeRequestStore.fetch(:ci_builds_partition_id_query_filter) do - ::Feature.enabled?(:ci_builds_partition_id_query_filter) - end + true end def uses_needs? diff --git a/app/serializers/codequality_degradation_entity.rb b/app/serializers/codequality_degradation_entity.rb index b9049bc90b9..97f9abbbe98 100644 --- a/app/serializers/codequality_degradation_entity.rb +++ b/app/serializers/codequality_degradation_entity.rb @@ -4,7 +4,9 @@ class CodequalityDegradationEntity < Grape::Entity expose :description expose :fingerprint expose :severity do |degradation| - degradation.dig(:severity)&.downcase + severity = degradation.dig(:severity)&.downcase + + ::Gitlab::Ci::Reports::CodequalityReports::SEVERITY_PRIORITIES.key?(severity) ? severity : 'unknown' end expose :file_path do |degradation| diff --git a/app/services/ci/expire_pipeline_cache_service.rb b/app/services/ci/expire_pipeline_cache_service.rb index 15597eb7209..e0c98e17e8f 100644 --- a/app/services/ci/expire_pipeline_cache_service.rb +++ b/app/services/ci/expire_pipeline_cache_service.rb @@ -50,6 +50,10 @@ module Ci yield(pipelines_project_merge_request_path(merge_request)) yield(merge_request_widget_path(merge_request)) end + + pipeline.project.merge_requests.by_merged_or_merge_or_squash_commit_sha(pipeline.sha).each do |merge_request| + yield(merge_request_widget_path(merge_request)) + end end def graphql_pipeline_path(pipeline) diff --git a/app/services/projects/fork_service.rb b/app/services/projects/fork_service.rb index 304c752bea6..9b9144e675f 100644 --- a/app/services/projects/fork_service.rb +++ b/app/services/projects/fork_service.rb @@ -38,6 +38,10 @@ module Projects return ServiceResponse.error(message: _('Project already forked'), reason: :already_forked) end + if fork_to_project == @project + return ServiceResponse.error(message: _('Target project cannot be equal to source project'), reason: :self_fork) + end + build_fork_network_member(fork_to_project) if link_fork_network(fork_to_project) diff --git a/app/views/admin/sessions/new.html.haml b/app/views/admin/sessions/new.html.haml index 5a2e5b5cf95..304266c674a 100644 --- a/app/views/admin/sessions/new.html.haml +++ b/app/views/admin/sessions/new.html.haml @@ -4,16 +4,15 @@ .row.gl-mt-5.justify-content-center .col-md-5 .login-page - .borderless - - if any_form_based_providers_enabled? - = render 'devise/shared/tabs_ldap', admin_mode: true - - elsif allow_admin_mode_password_authentication_for_web? - = render 'devise/sessions/new_base', admin_mode: true + - if any_form_based_providers_enabled? + = render 'devise/shared/tabs_ldap', admin_mode: true + - elsif allow_admin_mode_password_authentication_for_web? + = render 'devise/sessions/new_base', admin_mode: true - -# Show a message if none of the mechanisms above are enabled - - if !allow_admin_mode_password_authentication_for_web? && !ldap_sign_in_enabled? && !omniauth_enabled? - .gl-mt-3.center - = _('No authentication methods configured.') + -# Show a message if none of the mechanisms above are enabled + - if !allow_admin_mode_password_authentication_for_web? && !ldap_sign_in_enabled? && !omniauth_enabled? + .gl-mt-3.center + = _('No authentication methods configured.') - - if omniauth_enabled? && button_based_providers_enabled? - = render 'devise/shared/omniauth_box', render_remember_me: false + - if omniauth_enabled? && button_based_providers_enabled? + = render 'devise/shared/omniauth_box', render_remember_me: false diff --git a/app/views/admin/sessions/two_factor.html.haml b/app/views/admin/sessions/two_factor.html.haml index 2c4a1c2456a..802470be71f 100644 --- a/app/views/admin/sessions/two_factor.html.haml +++ b/app/views/admin/sessions/two_factor.html.haml @@ -4,9 +4,8 @@ .row.justify-content-center .col-md-5 .login-page - .borderless - .login-box.gl-p-5 - - if current_user.two_factor_enabled? - = render 'admin/sessions/two_factor_otp' - - if current_user.two_factor_webauthn_enabled? - = render 'authentication/authenticate', render_remember_me: false, target_path: admin_session_path + .login-box.gl-p-5 + - if current_user.two_factor_enabled? + = render 'admin/sessions/two_factor_otp' + - if current_user.two_factor_webauthn_enabled? + = render 'authentication/authenticate', render_remember_me: false, target_path: admin_session_path diff --git a/app/views/layouts/devise.html.haml b/app/views/layouts/devise.html.haml index 6e8f93373e3..8a366d8e398 100644 --- a/app/views/layouts/devise.html.haml +++ b/app/views/layouts/devise.html.haml @@ -7,7 +7,7 @@ = header_message = render "layouts/init_client_detection_flags" = yield :sessions_broadcast - .gl-h-full.borderless.gl-display-flex.gl-flex-wrap + .gl-h-full.gl-display-flex.gl-flex-wrap .container.gl-align-self-center .content = render "layouts/flash" diff --git a/config/feature_flags/development/ci_builds_partition_id_query_filter.yml b/config/feature_flags/development/ci_builds_partition_id_query_filter.yml deleted file mode 100644 index ab4166c6c93..00000000000 --- a/config/feature_flags/development/ci_builds_partition_id_query_filter.yml +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: ci_builds_partition_id_query_filter -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/130073 -rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/431361 -milestone: '16.6' -type: development -group: group::pipeline execution -default_enabled: false diff --git a/doc/api/integrations.md b/doc/api/integrations.md index 2d85ea2c6be..d90aacd0818 100644 --- a/doc/api/integrations.md +++ b/doc/api/integrations.md @@ -700,6 +700,59 @@ Get the external wiki settings for a project. GET /projects/:id/integrations/external-wiki ``` +## GitGuardian + +DETAILS: +**Tier:** Premium, Ultimate +**Offering:** SaaS, self-managed +**Status:** Beta + +> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/435706) in GitLab 16.9 [with a flag](../administration/feature_flags.md) named `git_guardian_integration`. Disabled by default. + +FLAG: +On self-managed GitLab, by default this feature is not available. To make it available, an administrator can [enable the feature flag](../administration/feature_flags.md) named `git_guardian_integration`. +On GitLab.com, this feature is not available. +This feature is not ready for production use. + +[GitGuardian](https://www.gitguardian.com/) is a cybersecurity service that detects sensitive data such as API keys +and passwords in source code repositories. +It scans Git repositories, alerts on policy violations, and helps organizations +fix security issues before hackers can exploit them. + +You can configure GitLab to reject commits based on GitGuardian policies. + +This feature is in [Beta](../policy/experiment-beta-support.md#beta) and subject to change without notice. + +### Set up GitGuardian + +Set up the GitGuardian integration for a project. + +```plaintext +PUT /projects/:id/integrations/git-guardian +``` + +Parameters: + +| Parameter | Type | Required | Description | +| --------- | ---- | -------- |-----------------------------------------------| +| `token` | string | true | GitGuardian API token with `scan` scope. | + +### Disable GitGuardian + +Disable the GitGuardian integration for a project. Integration settings are reset. + +```plaintext +DELETE /projects/:id/integrations/git-guardian +``` + +### Get GitGuardian settings + +Get the GitGuardian integration settings for a project. + +```plaintext +GET /projects/:id/integrations/git-guardian +``` + ## GitHub DETAILS: diff --git a/doc/api/repositories.md b/doc/api/repositories.md index 638e3e57cf6..ced0e313df8 100644 --- a/doc/api/repositories.md +++ b/doc/api/repositories.md @@ -366,6 +366,9 @@ If the `version` attribute is `2.1.0`, GitLab uses tag `v2.0.0`. And when the version is `1.1.1`, or `1.2.0`, GitLab uses tag v1.1.0. The tag `v1.0.0-pre1` is never used, because pre-release tags are ignored. +The `version` attribute can start with `v`. For example: `v1.0.0`. +The response is the same as for `version` value `1.0.0`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/437616) in GitLab 16.9. + If `from` is unspecified and no tag to use is found, the API produces an error. To solve such an error, you must explicitly specify a value for the `from` attribute. diff --git a/doc/user/project/integrations/git_guardian.md b/doc/user/project/integrations/git_guardian.md new file mode 100644 index 00000000000..213332efd73 --- /dev/null +++ b/doc/user/project/integrations/git_guardian.md @@ -0,0 +1,77 @@ +--- +stage: Create +group: Source Code +info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://handbook.gitlab.com/handbook/product/ux/technical-writing/#assignments +--- + +# GitGuardian + +DETAILS: +**Tier:** Premium, Ultimate +**Offering:** SaaS, self-managed +**Status:** Beta + +> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/435706) in GitLab 16.9 [with a flag](../../../administration/feature_flags.md) named `git_guardian_integration`. Disabled by default. + +FLAG: +On self-managed GitLab, by default this feature is not available. To make it available, an administrator can [enable the feature flag](../../../administration/feature_flags.md) named `git_guardian_integration`. +On GitLab.com, this feature is not available. +This feature is not ready for production use. + +[GitGuardian](https://www.gitguardian.com/) is a cybersecurity service that detects sensitive data such as API keys +and passwords in source code repositories. +It scans Git repositories, alerts on policy violations, and helps organizations +fix security issues before hackers can exploit them. + +You can configure GitLab to reject commits based on GitGuardian policies. + +This feature is in [Beta](../../../policy/experiment-beta-support.md#beta) and subject to change without notice. + +To set up the GitGuardian integration: + +1. [Create a GitGuardian API token](#create-a-gitguardian-api-token). +1. [Set up the GitGuardian integration for your project](#set-up-the-gitguardian-integration-for-your-project). + +## Create a GitGuardian API token + +Prerequisites: + +- You must have a GitGuardian account. + +To create an API token: + +1. Sign in to your GitGuardian account. +1. Go to the **API** section in the sidebar. +1. In the API section sidebar go to **Personal access tokens** page. +1. Select **Create token**. The token creation dialog opens. +1. Provide your token information: + - Give your API token a meaningful name to identify its purpose. + For example, `GitLab integration token`. + - Select an appropriate expiration. + - Select the **scan scope** checkbox. + It is the only one needed for the integration. +1. Select **Create token**. +1. After you've generated a token, copy it to your clipboard. + This token is sensitive information, so keep it secure. + +Now you have successfully created a GitGuardian API token that you can use to for our integration. + +## Set up the GitGuardian integration for your project + +Prerequisites: + +- You must have at least the Maintainer role for the project. + +After you have created and copied your API token, configure GitLab to reject commits: + +To enable the integration for your project: + +1. On the left sidebar, select **Search or go to** and find your project or group. +1. Select **Settings > Integrations**. +1. Select **GitGuardian**. +1. In **Enable integration**, select the **Active** checkbox. +1. In **API token**, [paste the token value from GitGuardian](#create-a-gitguardian-api-token). +1. Optional. Select **Test settings**. +1. Select **Save changes**. + +GitLab is now ready to reject commits based on GitGuardian policies. diff --git a/doc/user/project/integrations/index.md b/doc/user/project/integrations/index.md index 225edff8125..f30ad4792a2 100644 --- a/doc/user/project/integrations/index.md +++ b/doc/user/project/integrations/index.md @@ -132,6 +132,7 @@ To use custom settings for a project or group integration: | [Emails on push](emails_on_push.md) | Send commits and diffs on push by email. | **{dotted-circle}** No | | [Engineering Workflow Management (EWM)](ewm.md) | Use EWM as an issue tracker. | **{dotted-circle}** No | | [External wiki](../wiki/index.md#link-an-external-wiki) | Link an external wiki. | **{dotted-circle}** No | +| [GitGuardian](git_guardian.md) | Reject commits based on GitGuardian policies. | **{dotted-circle}** No | | [GitHub](github.md) | Receive statuses for commits and pull requests. | **{dotted-circle}** No | | [GitLab for Slack app](gitlab_slack_application.md) | Use the native Slack app to receive notifications and run commands. | **{dotted-circle}** No | | [Google Chat](hangouts_chat.md) | Send notifications from your GitLab project to a room in Google Chat. | **{dotted-circle}** No | diff --git a/doc/user/project/pages/redirects.md b/doc/user/project/pages/redirects.md index bd139772def..5c199771979 100644 --- a/doc/user/project/pages/redirects.md +++ b/doc/user/project/pages/redirects.md @@ -126,6 +126,12 @@ rewrite the URL. ## Domain-level redirects > - [Introduced](https://gitlab.com/gitlab-org/gitlab-pages/-/merge_requests/936) in GitLab 16.8 [with a flag](../../../administration/feature_flags.md) named `FF_ENABLE_DOMAIN_REDIRECT`. Disabled by default. +> - [Enabled on GitLab.com](https://gitlab.com/gitlab-com/gl-infra/k8s-workloads/gitlab-com/-/merge_requests/3395) in GitLab 16.9. + +NOTE: +On self-managed GitLab, by default this feature is not available. +To make it available, an administrator can [enable the feature flag](../../../administration/feature_flags.md) named `FF_ENABLE_DOMAIN_REDIRECT`. +On GitLab.com, this feature is available. To create a domain-level redirect, add a domain-level path (beginning with `http://` or `https://`) to either: diff --git a/doc/user/project/repository/git_history.md b/doc/user/project/repository/git_history.md index 1a0f4203b19..4e090871dfd 100644 --- a/doc/user/project/repository/git_history.md +++ b/doc/user/project/repository/git_history.md @@ -28,6 +28,22 @@ The name and email information provided are retrieved from the [Git configuration](https://git-scm.com/book/en/v2/Customizing-Git-Git-Configuration) of the contributor when a commit is made. +## Limit history range + +> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/423108) in GitLab 16.9. + +In these cases you can constrain the search period by adding `committed_before` and `committed_after` dates as parameters. +To do this, add `&` and `committed_before=YYYY-MM-DD` or `committed_after=YYYY-MM-DD` parameters to the URL. + +For example: + +```plaintext +https://gitlab.com/gitlab-org/gitlab/-/commits/master/README.md?ref_type=heads&committed_before=2010-11-22&committed_after=2008-05-15 +``` + +Doing this might be necessary to fix [commit history requests timeouts](https://gitlab.com/gitlab-org/gitaly/-/issues/5426) +in very large repositories. + ## Associated `git` command If you're running `git` from the command line, the equivalent command diff --git a/lib/gitlab/kas/client.rb b/lib/gitlab/kas/client.rb index 464c049ee52..9efc001638d 100644 --- a/lib/gitlab/kas/client.rb +++ b/lib/gitlab/kas/client.rb @@ -20,7 +20,7 @@ module Gitlab end def get_connected_agents_by_agent_ids(agent_ids:) - request = Gitlab::Agent::AgentTracker::Rpc::GetConnectedAgentsByAgentIdsRequest.new(agent_ids: agent_ids) + request = Gitlab::Agent::AgentTracker::Rpc::GetConnectedAgentsByAgentIDsRequest.new(agent_ids: agent_ids) stub_for(:agent_tracker) .get_connected_agents_by_agent_ids(request, metadata: metadata) diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 625ed29bfc0..8d4ff6a7c9a 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -49029,6 +49029,9 @@ msgstr "" msgid "Target branch: %{target_branch}" msgstr "" +msgid "Target project cannot be equal to source project" +msgstr "" + msgid "Target roles" msgstr "" diff --git a/sidekiq_cluster/cli.rb b/sidekiq_cluster/cli.rb index ddedc63e458..4ea29a1333f 100644 --- a/sidekiq_cluster/cli.rb +++ b/sidekiq_cluster/cli.rb @@ -40,6 +40,8 @@ module Gitlab # https://ruby.social/@getajobmike/109326475545816363 @max_concurrency = 20 @min_concurrency = 0 + # TODO: to be set to 20 once max_concurrency and min_concurrency is removed https://gitlab.com/gitlab-com/gl-infra/scalability/-/issues/2760 + @concurrency = 0 @environment = ENV['RAILS_ENV'] || 'development' @metrics_dir = ENV["prometheus_multiproc_dir"] || File.absolute_path("tmp/prometheus_multiproc_dir/sidekiq") @pid = nil @@ -143,6 +145,7 @@ module Gitlab directory: @rails_path, max_concurrency: @max_concurrency, min_concurrency: @min_concurrency, + concurrency: @concurrency, dryrun: @dryrun, timeout: @soft_timeout_seconds ) @@ -220,6 +223,10 @@ module Gitlab abort opt.to_s end + opt.on('-c', '--concurrency INT', 'Number of threads to use with Sidekiq (default: 0)') do |int| + @concurrency = int.to_i + end + opt.on('-m', '--max-concurrency INT', 'Maximum threads to use with Sidekiq (default: 20, 0 to disable)') do |int| @max_concurrency = int.to_i end diff --git a/sidekiq_cluster/sidekiq_cluster.rb b/sidekiq_cluster/sidekiq_cluster.rb index 1ed08e7e839..579bb026413 100644 --- a/sidekiq_cluster/sidekiq_cluster.rb +++ b/sidekiq_cluster/sidekiq_cluster.rb @@ -36,12 +36,13 @@ module Gitlab # # Returns an Array containing the waiter threads (from Process.detach) of # the started processes. - def self.start(queues, env: :development, directory: Dir.pwd, max_concurrency: 20, min_concurrency: 0, timeout: DEFAULT_SOFT_TIMEOUT_SECONDS, dryrun: false) + def self.start(queues, env: :development, directory: Dir.pwd, max_concurrency: 20, min_concurrency: 0, concurrency: 0, timeout: DEFAULT_SOFT_TIMEOUT_SECONDS, dryrun: false) queues.map.with_index do |pair, index| start_sidekiq(pair, env: env, directory: directory, max_concurrency: max_concurrency, min_concurrency: min_concurrency, + concurrency: concurrency, worker_id: index, timeout: timeout, dryrun: dryrun) @@ -51,11 +52,13 @@ module Gitlab # Starts a Sidekiq process that processes _only_ the given queues. # # Returns the PID of the started process. - def self.start_sidekiq(queues, env:, directory:, max_concurrency:, min_concurrency:, worker_id:, timeout:, dryrun:) + # rubocop: disable Metrics/ParameterLists -- max_concurrency and min_concurrency will be removed in 17.0 + def self.start_sidekiq(queues, env:, directory:, max_concurrency:, min_concurrency:, concurrency:, worker_id:, timeout:, dryrun:) + # rubocop: enable Metrics/ParameterLists counts = count_by_queue(queues) cmd = %w[bundle exec sidekiq] - cmd << "-c#{self.concurrency(queues, min_concurrency, max_concurrency)}" + cmd << "-c#{self.concurrency(queues, min_concurrency, max_concurrency, concurrency)}" cmd << "-e#{env}" cmd << "-t#{timeout}" cmd << "-gqueues:#{proc_details(counts)}" @@ -101,7 +104,9 @@ module Gitlab end.join(',') end - def self.concurrency(queues, min_concurrency, max_concurrency) + def self.concurrency(queues, min_concurrency, max_concurrency, concurrency) + return concurrency if concurrency > 0 + concurrency_from_queues = queues.length + 1 max = max_concurrency > 0 ? max_concurrency : concurrency_from_queues min = [min_concurrency, max].min diff --git a/spec/commands/sidekiq_cluster/cli_spec.rb b/spec/commands/sidekiq_cluster/cli_spec.rb index ceee61fb302..47a42824591 100644 --- a/spec/commands/sidekiq_cluster/cli_spec.rb +++ b/spec/commands/sidekiq_cluster/cli_spec.rb @@ -12,7 +12,7 @@ RSpec.describe Gitlab::SidekiqCluster::CLI, feature_category: :gitlab_cli, stub_ let(:cli) { described_class.new('/dev/null') } let(:timeout) { Gitlab::SidekiqCluster::DEFAULT_SOFT_TIMEOUT_SECONDS } let(:default_options) do - { env: 'test', directory: Dir.pwd, max_concurrency: 20, min_concurrency: 0, dryrun: false, timeout: timeout } + { env: 'test', directory: Dir.pwd, max_concurrency: 20, min_concurrency: 0, dryrun: false, timeout: timeout, concurrency: 0 } end let(:sidekiq_exporter_enabled) { false } @@ -125,6 +125,18 @@ RSpec.describe Gitlab::SidekiqCluster::CLI, feature_category: :gitlab_cli, stub_ end end + context 'with --concurrency flag' do + it 'starts Sidekiq workers for specified queues with the fixed concurrency' do + expected_queues = [%w[foo bar baz], %w[solo]].each { |queues| queues.concat(described_class::DEFAULT_QUEUES) } + expect(Gitlab::SidekiqConfig::CliMethods).to receive(:worker_queues).and_return(%w[foo bar baz]) + expect(Gitlab::SidekiqCluster).to receive(:start) + .with(expected_queues, default_options.merge(concurrency: 2)) + .and_return([]) + + cli.run(%w[foo,bar,baz solo -c 2]) + end + end + context 'with --timeout flag' do it 'when given', 'starts Sidekiq workers with given timeout' do expect(Gitlab::SidekiqCluster).to receive(:start) diff --git a/spec/controllers/projects/commits_controller_spec.rb b/spec/controllers/projects/commits_controller_spec.rb index 956167ce838..80786427802 100644 --- a/spec/controllers/projects/commits_controller_spec.rb +++ b/spec/controllers/projects/commits_controller_spec.rb @@ -102,6 +102,77 @@ RSpec.describe Projects::CommitsController, feature_category: :source_code_manag end end + context 'date range' do + let(:id) { "master/README.md" } + let(:base_repository_params) do + { + path: "README.md", + limit: described_class::COMMITS_DEFAULT_LIMIT, + offset: 0 + } + end + + let(:base_request_params) do + { + namespace_id: project.namespace, + project_id: project, + id: id + } + end + + shared_examples 'repository commits call' do + it 'passes the correct params' do + expect_any_instance_of(Repository).to receive(:commits).with( + "master", + repository_params + ).and_call_original + + get :show, params: request_params + + expect(response).to be_successful + end + end + + context 'when committed_before param' do + context 'is valid' do + let(:request_params) { base_request_params.merge(committed_before: '2020-01-01') } + let(:repository_params) { base_repository_params.merge(before: 1577836800) } + + it_behaves_like 'repository commits call' + end + + context 'is invalid' do + let(:request_params) { base_request_params.merge(committed_before: 'xxx') } + let(:repository_params) { base_repository_params } + + it_behaves_like 'repository commits call' + end + + context 'is not provided' do + let(:request_params) { base_request_params } + let(:repository_params) { base_repository_params } + + it_behaves_like 'repository commits call' + end + end + + context 'with committed_after param' do + context 'is valid' do + let(:request_params) { base_request_params.merge(committed_after: '2020-01-01') } + let(:repository_params) { base_repository_params.merge(after: 1577836800) } + + it_behaves_like 'repository commits call' + end + + context 'is invalid' do + let(:request_params) { base_request_params.merge(committed_after: 'xxx') } + let(:repository_params) { base_repository_params } + + it_behaves_like 'repository commits call' + end + end + end + it 'loads tags for commits' do expect_next_instance_of(CommitCollection) do |collection| expect(collection).to receive(:load_tags) diff --git a/spec/finders/repositories/changelog_tag_finder_spec.rb b/spec/finders/repositories/changelog_tag_finder_spec.rb index cd79beb3e9e..73e302efd27 100644 --- a/spec/finders/repositories/changelog_tag_finder_spec.rb +++ b/spec/finders/repositories/changelog_tag_finder_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Repositories::ChangelogTagFinder do +RSpec.describe Repositories::ChangelogTagFinder, feature_category: :source_code_management do let(:project) { build_stubbed(:project) } let(:finder) { described_class.new(project) } @@ -35,6 +35,10 @@ RSpec.describe Repositories::ChangelogTagFinder do expect(finder.execute('1.0.0')).to eq(tag4) expect(finder.execute('0.9.0')).to eq(tag6) expect(finder.execute('0.6.0')).to eq(tag7) + + # with v at the beginning + expect(finder.execute('v2.1.0')).to eq(tag3) + expect(finder.execute('wrong_version')).to eq(nil) end end diff --git a/spec/frontend/boards/components/board_app_spec.js b/spec/frontend/boards/components/board_app_spec.js index 0296c1eeb91..6c292f0c2c8 100644 --- a/spec/frontend/boards/components/board_app_spec.js +++ b/spec/frontend/boards/components/board_app_spec.js @@ -28,7 +28,7 @@ describe('BoardApp', () => { mockApollo.clients.defaultClient.cache.writeQuery({ query: activeBoardItemQuery, data: { - activeBoardItem: issue, + activeBoardItem: { ...issue, listId: 'gid://gitlab/List/1' }, }, }); diff --git a/spec/frontend/boards/components/board_card_spec.js b/spec/frontend/boards/components/board_card_spec.js index 1781c58c11f..9afed0e9013 100644 --- a/spec/frontend/boards/components/board_card_spec.js +++ b/spec/frontend/boards/components/board_card_spec.js @@ -50,7 +50,7 @@ describe('Board card', () => { mockApollo.clients.defaultClient.cache.writeQuery({ query: activeBoardItemQuery, data: { - activeBoardItem, + activeBoardItem: { ...activeBoardItem, listId: 'gid://gitlab/List/1' }, }, }); @@ -113,7 +113,7 @@ describe('Board card', () => { }); it('should highlight the card with a correct style when selected', async () => { - mountComponent({ activeBoardItem: mockIssue }); + mountComponent({ activeBoardItem: { ...mockIssue, listId: 'gid://gitlab/List/1' } }); await waitForPromises(); expect(wrapper.classes()).toContain('is-active'); @@ -141,6 +141,7 @@ describe('Board card', () => { {}, { boardItem: mockIssue, + listId: 'gid://gitlab/List/2', }, expect.anything(), expect.anything(), diff --git a/spec/frontend/boards/components/board_content_sidebar_spec.js b/spec/frontend/boards/components/board_content_sidebar_spec.js index 5fffd4d0c23..ed0235fd3ed 100644 --- a/spec/frontend/boards/components/board_content_sidebar_spec.js +++ b/spec/frontend/boards/components/board_content_sidebar_spec.js @@ -34,7 +34,7 @@ describe('BoardContentSidebar', () => { mockApollo.clients.defaultClient.cache.writeQuery({ query: activeBoardItemQuery, data: { - activeBoardItem: issuable, + activeBoardItem: { ...issuable, listId: 'gid://gitlab/List/1' }, }, }); @@ -142,6 +142,7 @@ describe('BoardContentSidebar', () => { {}, { boardItem: null, + listId: null, }, expect.anything(), expect.anything(), diff --git a/spec/lib/gitlab/kas/client_spec.rb b/spec/lib/gitlab/kas/client_spec.rb index f2745d940de..5603060f07e 100644 --- a/spec/lib/gitlab/kas/client_spec.rb +++ b/spec/lib/gitlab/kas/client_spec.rb @@ -47,8 +47,8 @@ RSpec.describe Gitlab::Kas::Client do describe '#get_connected_agents_by_agent_ids' do let(:stub) { instance_double(Gitlab::Agent::AgentTracker::Rpc::AgentTracker::Stub) } - let(:request) { instance_double(Gitlab::Agent::AgentTracker::Rpc::GetConnectedAgentsByAgentIdsRequest) } - let(:response) { double(Gitlab::Agent::AgentTracker::Rpc::GetConnectedAgentsByAgentIdsResponse, agents: connected_agents) } + let(:request) { instance_double(Gitlab::Agent::AgentTracker::Rpc::GetConnectedAgentsByAgentIDsRequest) } + let(:response) { double(Gitlab::Agent::AgentTracker::Rpc::GetConnectedAgentsByAgentIDsResponse, agents: connected_agents) } let(:connected_agents) { [double] } @@ -59,7 +59,7 @@ RSpec.describe Gitlab::Kas::Client do .with('example.kas.internal', :this_channel_is_insecure, timeout: described_class::TIMEOUT) .and_return(stub) - expect(Gitlab::Agent::AgentTracker::Rpc::GetConnectedAgentsByAgentIdsRequest).to receive(:new) + expect(Gitlab::Agent::AgentTracker::Rpc::GetConnectedAgentsByAgentIDsRequest).to receive(:new) .with(agent_ids: [agent.id]) .and_return(request) diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index f855fe58eba..3423da68488 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -3533,6 +3533,15 @@ RSpec.describe API::Projects, :aggregate_failures, feature_category: :groups_and expect(project_fork_target).not_to be_forked end end + + context 'when fork target and source are the same' do + it 'returns an error' do + post api("/projects/#{project_fork_target.id}/fork/#{project_fork_target.id}", admin, admin_mode: true) + + expect(response).to have_gitlab_http_status(:bad_request) + expect(json_response['message']).to eq 'Target project cannot be equal to source project' + end + end end end diff --git a/spec/serializers/codequality_degradation_entity_spec.rb b/spec/serializers/codequality_degradation_entity_spec.rb index dc15fa02a21..c50619c9252 100644 --- a/spec/serializers/codequality_degradation_entity_spec.rb +++ b/spec/serializers/codequality_degradation_entity_spec.rb @@ -54,6 +54,17 @@ RSpec.describe CodequalityDegradationEntity, feature_category: :code_quality do expect(subject[:engine_name]).to eq('rubocop') end end + + context 'when severity is a non-codeclimate-standard severity' do + # See standard severities: https://docs.codeclimate.com/docs/issues#issue-severity + let(:codequality_degradation) { build(:codequality_degradation_3) } + + it 'returns severity as unknown', :aggregate_failures do + codequality_degradation[:severity] = 'warning' + + expect(subject[:severity]).to eq('unknown') + end + end end end end diff --git a/spec/services/ci/expire_pipeline_cache_service_spec.rb b/spec/services/ci/expire_pipeline_cache_service_spec.rb index a74b820de09..758f923ce92 100644 --- a/spec/services/ci/expire_pipeline_cache_service_spec.rb +++ b/spec/services/ci/expire_pipeline_cache_service_spec.rb @@ -6,6 +6,7 @@ RSpec.describe Ci::ExpirePipelineCacheService, feature_category: :continuous_int let_it_be(:user) { create(:user) } let_it_be(:project) { create(:project) } let_it_be(:pipeline) { create(:ci_pipeline, project: project) } + let_it_be(:merge_pipeline) { create(:ci_pipeline, :detached_merge_request_pipeline, project: project) } subject { described_class.new } @@ -45,6 +46,17 @@ RSpec.describe Ci::ExpirePipelineCacheService, feature_category: :continuous_int subject.execute(merge_request.all_pipelines.last) end + it 'invalidates Etag caching for merge request that pipeline runs on its merged commit' do + merge_request = create(:merge_request, merge_commit_sha: pipeline.sha, source_project: pipeline.project) + project = merge_request.target_project + + merge_request_widget_path = "/#{project.full_path}/-/merge_requests/#{merge_request.iid}/cached_widget.json" + + expect_touched_etag_caching_paths(merge_request_widget_path) + + subject.execute(pipeline) + end + it 'updates the cached status for a project' do expect(Gitlab::Cache::Ci::ProjectPipelineStatus).to receive(:update_for_pipeline).with(pipeline) diff --git a/spec/sidekiq_cluster/sidekiq_cluster_spec.rb b/spec/sidekiq_cluster/sidekiq_cluster_spec.rb index ec5e5d85eeb..d7963636664 100644 --- a/spec/sidekiq_cluster/sidekiq_cluster_spec.rb +++ b/spec/sidekiq_cluster/sidekiq_cluster_spec.rb @@ -42,7 +42,8 @@ RSpec.describe Gitlab::SidekiqCluster do # rubocop:disable RSpec/FilePath min_concurrency: 0, worker_id: an_instance_of(Integer), timeout: 25, - dryrun: false + dryrun: false, + concurrency: 0 } expect(described_class).to receive(:start_sidekiq).ordered.with(%w[foo bar baz], expected_options) @@ -55,7 +56,7 @@ RSpec.describe Gitlab::SidekiqCluster do # rubocop:disable RSpec/FilePath describe '.start_sidekiq' do let(:first_worker_id) { 0 } let(:options) do - { env: :production, directory: 'foo/bar', max_concurrency: 20, min_concurrency: 0, worker_id: first_worker_id, timeout: 10, dryrun: false } + { env: :production, directory: 'foo/bar', max_concurrency: 20, min_concurrency: 0, worker_id: first_worker_id, timeout: 10, dryrun: false, concurrency: 0 } end let(:env) { { "ENABLE_SIDEKIQ_CLUSTER" => "1", "SIDEKIQ_WORKER_ID" => first_worker_id.to_s } } @@ -102,21 +103,28 @@ RSpec.describe Gitlab::SidekiqCluster do # rubocop:disable RSpec/FilePath describe '.concurrency' do using RSpec::Parameterized::TableSyntax - where(:queue_count, :min, :max, :expected) do - 2 | 0 | 0 | 3 # No min or max specified - 2 | 0 | 9 | 3 # No min specified, value < max - 2 | 1 | 4 | 3 # Value between min and max - 2 | 4 | 5 | 4 # Value below range - 5 | 2 | 3 | 3 # Value above range - 2 | 1 | 1 | 1 # Value above explicit setting (min == max) - 0 | 3 | 3 | 3 # Value below explicit setting (min == max) - 1 | 4 | 3 | 3 # Min greater than max + where(:queue_count, :min, :max, :fixed_concurrency, :expected) do + # without fixed concurrency + 2 | 0 | 0 | 0 | 3 # No min or max specified + 2 | 0 | 9 | 0 | 3 # No min specified, value < max + 2 | 1 | 4 | 0 | 3 # Value between min and max + 2 | 4 | 5 | 0 | 4 # Value below range + 5 | 2 | 3 | 0 | 3 # Value above range + 2 | 1 | 1 | 0 | 1 # Value above explicit setting (min == max) + 0 | 3 | 3 | 0 | 3 # Value below explicit setting (min == max) + 1 | 4 | 3 | 0 | 3 # Min greater than max + + # with fixed concurrency, expected always equal to fixed_concurrency + 1 | 0 | 20 | 20 | 20 + 1 | 0 | 20 | 10 | 10 + 1 | 20 | 20 | 10 | 10 + 5 | 0 | 0 | 10 | 10 end with_them do let(:queues) { Array.new(queue_count) } - it { expect(described_class.concurrency(queues, min, max)).to eq(expected) } + it { expect(described_class.concurrency(queues, min, max, fixed_concurrency)).to eq(expected) } end end end diff --git a/workhorse/go.mod b/workhorse/go.mod index 8b401ec0dcf..27f8eaf32fc 100644 --- a/workhorse/go.mod +++ b/workhorse/go.mod @@ -6,7 +6,7 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.1 github.com/BurntSushi/toml v1.3.2 github.com/alecthomas/chroma/v2 v2.12.0 - github.com/aws/aws-sdk-go v1.49.3 + github.com/aws/aws-sdk-go v1.50.7 github.com/disintegration/imaging v1.6.2 github.com/getsentry/raven-go v0.2.0 github.com/golang-jwt/jwt/v5 v5.2.0 diff --git a/workhorse/go.sum b/workhorse/go.sum index c915e64a8b4..7cf7df9fb08 100644 --- a/workhorse/go.sum +++ b/workhorse/go.sum @@ -94,8 +94,8 @@ github.com/alecthomas/chroma/v2 v2.12.0/go.mod h1:4TQu7gdfuPjSh76j78ietmqh9LiurG github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/aws/aws-sdk-go v1.44.256/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= -github.com/aws/aws-sdk-go v1.49.3 h1:+UGwhC3kChk0pRCxSsbaQSNIc8MfFURQL44Ig6RRR3I= -github.com/aws/aws-sdk-go v1.49.3/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= +github.com/aws/aws-sdk-go v1.50.7 h1:odKb+uneeGgF2jgAerKjFzpljiyZxleV4SHB7oBK+YA= +github.com/aws/aws-sdk-go v1.50.7/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= github.com/aws/aws-sdk-go-v2 v1.24.0 h1:890+mqQ+hTpNuw0gGP6/4akolQkSToDJgHfQE7AwGuk= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4 h1:OCs21ST2LrepDfD3lwlQiOqIGp6JiEUqG84GzTDoyJs= github.com/aws/aws-sdk-go-v2/config v1.26.1 h1:z6DqMxclFGL3Zfo+4Q0rLnAZ6yVkzCRxhRMsiRQnD1o=