diff --git a/.gitlab/merge_request_templates/Stable Branch.md b/.gitlab/merge_request_templates/Stable Branch.md index 28db9d4229f..6c923847f85 100644 --- a/.gitlab/merge_request_templates/Stable Branch.md +++ b/.gitlab/merge_request_templates/Stable Branch.md @@ -37,4 +37,5 @@ If you have questions about the patch release process, please: [patch release runbook for engineers and maintainers]: https://gitlab.com/gitlab-org/release/docs/-/blob/master/general/patch/engineers.md [`#releases`]: https://gitlab.slack.com/archives/C0XM5UU6B +/label ~backport /assign me diff --git a/.rubocop_todo/rspec/change_by_zero.yml b/.rubocop_todo/rspec/change_by_zero.yml index 2dd412a1d8c..334e0f0e58c 100644 --- a/.rubocop_todo/rspec/change_by_zero.yml +++ b/.rubocop_todo/rspec/change_by_zero.yml @@ -16,27 +16,6 @@ RSpec/ChangeByZero: - 'ee/spec/requests/api/graphql/mutations/work_items/create_spec.rb' - 'ee/spec/requests/api/graphql/mutations/work_items/linked_items/add_spec.rb' - 'ee/spec/requests/api/merge_request_approvals_spec.rb' - - 'ee/spec/services/app_sec/dast/profiles/create_service_spec.rb' - - 'ee/spec/services/application_settings/update_service_spec.rb' - - 'ee/spec/services/approval_rules/user_rules_destroy_service_spec.rb' - - 'ee/spec/services/ci/delete_project_subscription_service_spec.rb' - - 'ee/spec/services/ci/minutes/update_project_and_namespace_usage_service_spec.rb' - - 'ee/spec/services/ee/ci/update_instance_variables_service_spec.rb' - - 'ee/spec/services/ee/issues/create_service_spec.rb' - - 'ee/spec/services/ee/members/destroy_service_spec.rb' - - 'ee/spec/services/geo/container_repository_registry_removal_service_spec.rb' - - 'ee/spec/services/geo/file_registry_removal_service_spec.rb' - - 'ee/spec/services/geo/metrics_update_service_spec.rb' - - 'ee/spec/services/gitlab_subscriptions/add_on_purchases/gitlab_com/provision_service_spec.rb' - - 'ee/spec/services/push_rules/create_or_update_service_spec.rb' - - 'ee/spec/services/registrations/import_namespace_create_service_spec.rb' - - 'ee/spec/services/registrations/standard_namespace_create_service_spec.rb' - - 'ee/spec/services/sbom/ingestion/tasks/ingest_sources_spec.rb' - - 'ee/spec/services/security/ingestion/tasks/ingest_cvs_security_scanners_spec.rb' - - 'ee/spec/services/security/orchestration/create_bot_service_spec.rb' - - 'ee/spec/services/vulnerabilities/manually_create_service_spec.rb' - - 'ee/spec/services/vulnerabilities/security_finding/create_merge_request_service_spec.rb' - - 'ee/spec/services/work_items/legacy_epics/related_epic_links/create_service_spec.rb' - 'spec/controllers/admin/clusters_controller_spec.rb' - 'spec/controllers/groups/boards_controller_spec.rb' - 'spec/controllers/groups/clusters_controller_spec.rb' diff --git a/GITALY_SERVER_VERSION b/GITALY_SERVER_VERSION index cbcee7d4b72..0fd231905a4 100644 --- a/GITALY_SERVER_VERSION +++ b/GITALY_SERVER_VERSION @@ -1 +1 @@ -55f4fb94e39f60e1d4c76deac5d6f20c6202c68f +3292fb4882db9208602f3cd2cc61ba56bce8e6a9 diff --git a/app/assets/javascripts/projects/new/components/new_project_push_tip_popover.vue b/app/assets/javascripts/projects/new/components/new_project_push_tip_popover.vue index 1dcfd32e4ff..6bcdea78ac1 100644 --- a/app/assets/javascripts/projects/new/components/new_project_push_tip_popover.vue +++ b/app/assets/javascripts/projects/new/components/new_project_push_tip_popover.vue @@ -2,6 +2,7 @@ import { GlPopover, GlFormInputGroup } from '@gitlab/ui'; import { __ } from '~/locale'; import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; +import { helpPagePath } from '~/helpers/help_page_helper'; export default { components: { @@ -9,13 +10,14 @@ export default { GlFormInputGroup, ClipboardButton, }, - inject: ['pushToCreateProjectCommand', 'projectHelpPath'], + inject: ['pushToCreateProjectCommand'], props: { target: { type: [Function, HTMLElement], required: true, }, }, + helpPath: helpPagePath('topics/git/project.md'), i18n: { clipboardButtonTitle: __('Copy command'), commandInputAriaLabel: __('Push project from command line'), @@ -55,12 +57,9 @@ export default {

- {{ $options.i18n.helpLinkText }} + {{ + $options.i18n.helpLinkText + }}

diff --git a/app/assets/javascripts/projects/new/index.js b/app/assets/javascripts/projects/new/index.js index 6e9cb7b3336..58bc46566d1 100644 --- a/app/assets/javascripts/projects/new/index.js +++ b/app/assets/javascripts/projects/new/index.js @@ -16,7 +16,6 @@ export function initNewProjectCreation() { const { pushToCreateProjectCommand, - projectHelpPath, newProjectGuidelines, hasErrors, isCiCdAvailable, @@ -39,7 +38,6 @@ export function initNewProjectCreation() { }; const provide = { - projectHelpPath, pushToCreateProjectCommand, }; diff --git a/app/assets/javascripts/projects/new_v2/components/command_line.vue b/app/assets/javascripts/projects/new_v2/components/command_line.vue index c66fa12464a..d85bd2644e6 100644 --- a/app/assets/javascripts/projects/new_v2/components/command_line.vue +++ b/app/assets/javascripts/projects/new_v2/components/command_line.vue @@ -2,6 +2,7 @@ import { GlFormGroup, GlLink, GlFormInputGroup } from '@gitlab/ui'; import SafeHtml from '~/vue_shared/directives/safe_html'; import ClipboardButton from '~/vue_shared/components/clipboard_button.vue'; +import { helpPagePath } from '~/helpers/help_page_helper'; export default { components: { @@ -13,7 +14,8 @@ export default { directives: { SafeHtml, }, - inject: ['projectHelpPath', 'pushToCreateProjectCommand'], + helpPath: helpPagePath('topics/git/project.md'), + inject: ['pushToCreateProjectCommand'], }; @@ -30,7 +32,7 @@ export default { data-testid="new-project-with-command" > diff --git a/app/assets/javascripts/projects/new_v2/index.js b/app/assets/javascripts/projects/new_v2/index.js index fee9c87089e..e4de98776d5 100644 --- a/app/assets/javascripts/projects/new_v2/index.js +++ b/app/assets/javascripts/projects/new_v2/index.js @@ -22,7 +22,6 @@ export function initNewProjectForm() { projectsUrl, parentGroupUrl, parentGroupName, - projectHelpPath, isCiCdAvailable, canImportProjects, importSourcesEnabled, @@ -64,7 +63,6 @@ export function initNewProjectForm() { projectsUrl, parentGroupUrl, parentGroupName, - projectHelpPath, trackLabel, isCiCdAvailable: parseBoolean(isCiCdAvailable), importSourcesEnabled: parseBoolean(importSourcesEnabled), diff --git a/app/assets/javascripts/ref/format_refs.js b/app/assets/javascripts/ref/format_refs.js index df10c54c13b..d9fa45e3807 100644 --- a/app/assets/javascripts/ref/format_refs.js +++ b/app/assets/javascripts/ref/format_refs.js @@ -23,9 +23,7 @@ export const formatListBoxItems = ({ branches, tags, commits, selectedRef }) => const addToFinalResult = (items, header, shouldFilter = true) => { if (!items) return; const filteredItems = - shouldFilter && selectedRef - ? items.filter((item) => item.value !== selectedRef.value) - : items; // Filter out the selected + shouldFilter && selectedRef ? items.filter((item) => item.name !== selectedRef.name) : items; // Filter out the selected if (!filteredItems?.length) return; listBoxItems.push({ diff --git a/app/services/ci/register_job_service.rb b/app/services/ci/register_job_service.rb index c10b1cb6ac4..f4aaece7f32 100644 --- a/app/services/ci/register_job_service.rb +++ b/app/services/ci/register_job_service.rb @@ -98,8 +98,7 @@ module Ci next unless result if result.valid? - @metrics.register_success(result.build_presented) - @metrics.observe_queue_depth(:found, depth) + track_success(result, depth) return result # rubocop:disable Cop/AvoidReturnFromBlocks else @@ -109,10 +108,7 @@ module Ci end end - @metrics.increment_queue_operation(:queue_conflict) unless valid - @metrics.observe_queue_depth(:conflict, depth) unless valid - @metrics.observe_queue_depth(:not_found, depth) if valid - @metrics.register_failure + track_conflict(depth, valid) Result.new(nil, nil, nil, valid) end @@ -308,14 +304,40 @@ module Ci track_exception_for_build(ex, build) end + def track_success(result, depth) + @metrics.register_success(result.build_presented) + @metrics.observe_queue_depth(:found, depth) + rescue StandardError => ex + # We should know about any errors during tracking but we don't want them to prevent + # reporting a result to the runner + Gitlab::ErrorTracking.track_and_raise_for_dev_exception( + ex, **build_tracking_data(result.build) + ) + end + + def track_conflict(depth, valid) + @metrics.increment_queue_operation(:queue_conflict) unless valid + @metrics.observe_queue_depth(:conflict, depth) unless valid + @metrics.observe_queue_depth(:not_found, depth) if valid + @metrics.register_failure + rescue StandardError => ex + # We should know about any errors during tracking but we don't want them to prevent + # reporting a result to the runner + Gitlab::ErrorTracking.track_and_raise_for_dev_exception(ex) + end + def track_exception_for_build(ex, build) - Gitlab::ErrorTracking.track_exception(ex, + Gitlab::ErrorTracking.track_exception(ex, **build_tracking_data(build)) + end + + def build_tracking_data(build) + { build_id: build.id, build_name: build.name, build_stage: build.stage_name, pipeline_id: build.pipeline_id, project_id: build.project_id - ) + } end def pre_assign_runner_checks diff --git a/app/views/devise/sessions/_successful_verification.haml b/app/views/devise/sessions/_successful_verification.haml new file mode 100644 index 00000000000..8a5a3f6c423 --- /dev/null +++ b/app/views/devise/sessions/_successful_verification.haml @@ -0,0 +1,12 @@ += content_for :meta_tags do + %meta{ 'http-equiv': 'refresh', content: "3; url=#{@redirect_url}" } + +.gl-text-center.gl-max-w-62.gl-mx-auto{ data: local_assigns.fetch(:data, {}) } + .svg-content.svg-80 + = image_tag 'illustrations/success-sm.svg' + %h2 + = s_('IdentityVerification|Verification successful') + %p.gl-pt-2 + - redirect_url_start = ''.html_safe % { url: @redirect_url } + - redirect_url_end = ''.html_safe + = html_escape(s_("IdentityVerification|Your account has been successfully verified. You'll be redirected to your account in just a moment. You can also %{redirect_url_start}refresh the page%{redirect_url_end}.")) % { redirect_url_start: redirect_url_start, redirect_url_end: redirect_url_end } diff --git a/app/views/devise/sessions/_successful_verification_lightweight.haml b/app/views/devise/sessions/_successful_verification_lightweight.haml new file mode 100644 index 00000000000..fd870267cab --- /dev/null +++ b/app/views/devise/sessions/_successful_verification_lightweight.haml @@ -0,0 +1,13 @@ +- @html_class = 'gl-dark' += content_for :meta_tags do + %meta{ 'http-equiv': 'refresh', content: "3; url=#{@redirect_url}" } + +.gl-text-center.gl-max-w-xl.gl-mx-auto.gl-bg-subtle.gl-rounded-lg.gl-py-4{ data: local_assigns.fetch(:data, {}) } + .svg-content.svg-80 + = image_tag 'illustrations/secure-sm.svg' + %h2 + = s_('IdentityVerification|Verification successful') + %p.gl-pt-2.gl-px-6 + - redirect_url_start = ''.html_safe % { url: @redirect_url } + - redirect_url_end = ''.html_safe + = html_escape(s_("IdentityVerification|Your account has been successfully verified. You'll be redirected to your account in just a moment. You can also %{redirect_url_start}refresh the page%{redirect_url_end}.")) % { redirect_url_start: redirect_url_start, redirect_url_end: redirect_url_end } diff --git a/app/views/devise/sessions/successful_verification.haml b/app/views/devise/sessions/successful_verification.haml index e9be028ad9c..50751b3a2d7 100644 --- a/app/views/devise/sessions/successful_verification.haml +++ b/app/views/devise/sessions/successful_verification.haml @@ -1,12 +1,8 @@ -= content_for :meta_tags do - %meta{ 'http-equiv': 'refresh', content: "3; url=#{@redirect_url}" } - -.gl-text-center.gl-max-w-62.gl-mx-auto{ data: local_assigns.fetch(:data, {}) } - .svg-content.svg-80 - = image_tag 'illustrations/success-sm.svg' - %h2 - = s_('IdentityVerification|Verification successful') - %p.gl-pt-2 - - redirect_url_start = ''.html_safe % { url: @redirect_url } - - redirect_url_end = ''.html_safe - = html_escape(s_("IdentityVerification|Your account has been successfully verified. You'll be redirected to your account in just a moment. You can also %{redirect_url_start}refresh the page%{redirect_url_end}.")) % { redirect_url_start: redirect_url_start, redirect_url_end: redirect_url_end } +- if current_user.try(:onboarding_status_initial_registration_type) != "trial" + = render 'devise/sessions/successful_verification', local_assigns +- else + - experiment(:lightweight_trial_registration_redesign, actor: current_user) do |e| + - e.control do + = render 'devise/sessions/successful_verification', local_assigns + - e.candidate do + = render 'devise/sessions/successful_verification_lightweight', local_assigns diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index ebfef15ee08..71207981fdd 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -17,7 +17,6 @@ projects_url: dashboard_projects_url, parent_group_url: @project.parent && group_url(@project.parent), parent_group_name: @project.parent&.name, - project_help_path: help_page_path("user/project/_index.md"), is_ci_cd_available: remote_mirror_setting_enabled?.to_s, can_import_projects: params[:namespace_id].presence ? current_user.can?(:import_projects, @namespace).to_s : 'true', import_sources_enabled: import_sources_enabled?.to_s, @@ -56,7 +55,6 @@ has_errors: @project.errors.any?.to_s, new_project_guidelines: brand_new_project_guidelines, push_to_create_project_command: push_to_create_project_command, - project_help_path: help_page_path("user/project/_index.md"), root_path: root_path, parent_group_url: @project.parent && group_url(@project.parent), parent_group_name: @project.parent&.name, diff --git a/app/workers/gitlab/counters/flush_stale_counter_increments_worker.rb b/app/workers/gitlab/counters/flush_stale_counter_increments_worker.rb index f265312d936..38ba22f2a4b 100644 --- a/app/workers/gitlab/counters/flush_stale_counter_increments_worker.rb +++ b/app/workers/gitlab/counters/flush_stale_counter_increments_worker.rb @@ -28,17 +28,24 @@ module Gitlab feature_category :continuous_integration urgency :throttled idempotent! - deduplicate :until_executed + deduplicate :until_executing sidekiq_options retry: true def perform_work return unless ::Gitlab.com_except_jh? # rubocop:disable Gitlab/AvoidGitlabInstanceChecks -- we need to check on which instance this happens - ID_RANGES.each do |model, attributes| - min_id = start_id(model) - last_id = flush_stale_for_model(model, min_id, attributes[:end_id]) - # we need to add +1 here, because otherwise, we'd process the last record twice. - update_start_id(model, last_id + 1) + # Process up to 100 batches per job execution as a compromise between + # performance (avoiding too many small jobs) and efficiency (preventing + # jobs from running too long and potentially timing out) + 100.times do + ID_RANGES.each do |model, attributes| + min_id = start_id(model) + remaining_work = [(attributes[:end_id] - min_id), 0].max + last_id = flush_stale_for_model(model, min_id, attributes[:end_id]) + # we need to add +1 here, because otherwise, we'd process the last record twice. + update_start_id(model, last_id + 1) + return if remaining_work == 0 # rubocop:disable Lint/NonLocalExitFromIterator -- return if we don't have anymore work to do + end end end diff --git a/config/feature_flags/beta/migrate_user_access_tokens_ui.yml b/config/feature_flags/beta/migrate_user_access_tokens_ui.yml index 32d13fc0ca7..527032ded22 100644 --- a/config/feature_flags/beta/migrate_user_access_tokens_ui.yml +++ b/config/feature_flags/beta/migrate_user_access_tokens_ui.yml @@ -7,4 +7,4 @@ rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/538388 milestone: '18.0' group: group::authentication type: beta -default_enabled: false +default_enabled: true diff --git a/config/initializers/doorkeeper.rb b/config/initializers/doorkeeper.rb index 198203ab208..38a960ba5dd 100644 --- a/config/initializers/doorkeeper.rb +++ b/config/initializers/doorkeeper.rb @@ -173,7 +173,9 @@ Doorkeeper.configure do expires_in: configuration.device_code_expires_in, scopes: scopes.to_s, user_code: generate_user_code, - organization_id: Organizations::Organization::DEFAULT_ORGANIZATION_ID + # This will result in a fallback to Default Organizzation + # OAuth device grant flow doesn't support other organizations yet + organization_id: Gitlab::Current::Organization.new.organization.id } end end diff --git a/lib/gitlab/tracking/ai_tracking.rb b/lib/gitlab/tracking/ai_tracking.rb deleted file mode 100644 index 648347f5e4d..00000000000 --- a/lib/gitlab/tracking/ai_tracking.rb +++ /dev/null @@ -1,25 +0,0 @@ -# frozen_string_literal: true - -module Gitlab - module Tracking - class AiTracking - def self.track_event(*args, **kwargs) - new.track_event(*args, **kwargs) - end - - def self.track_user_activity(*args) - new.track_user_activity(*args) - end - - def track_event(_event_name, **_context_hash) - # no-op for CE - end - - def track_user_activity(_user) - # no-op for CE - end - end - end -end - -Gitlab::Tracking::AiTracking.prepend_mod diff --git a/spec/frontend/projects/new/components/new_project_push_tip_popover_spec.js b/spec/frontend/projects/new/components/new_project_push_tip_popover_spec.js index e4fe5678ebd..4e37e6c1683 100644 --- a/spec/frontend/projects/new/components/new_project_push_tip_popover_spec.js +++ b/spec/frontend/projects/new/components/new_project_push_tip_popover_spec.js @@ -8,7 +8,6 @@ describe('New project push tip popover', () => { let wrapper; const targetId = 'target'; const pushToCreateProjectCommand = 'command'; - const projectHelpPath = 'path'; const findPopover = () => wrapper.findComponent(GlPopover); const findClipboardButton = () => wrapper.findComponent(ClipboardButton); @@ -27,7 +26,6 @@ describe('New project push tip popover', () => { }, provide: { pushToCreateProjectCommand, - projectHelpPath, }, }); }; @@ -74,8 +72,6 @@ describe('New project push tip popover', () => { }); it('displays a link to open the push command help page reference', () => { - expect(findHelpLink().attributes().href).toBe( - `${projectHelpPath}#create-a-new-project-with-git-push`, - ); + expect(findHelpLink().attributes().href).toBe('/help/topics/git/project.md'); }); }); diff --git a/spec/frontend/ref/format_refs_spec.js b/spec/frontend/ref/format_refs_spec.js index 592978766b9..a2a94050674 100644 --- a/spec/frontend/ref/format_refs_spec.js +++ b/spec/frontend/ref/format_refs_spec.js @@ -13,9 +13,9 @@ import { describe('formatListBoxItems', () => { const FORMATTED_SELECTED = { text: DEFAULT_I18N.selected, - options: [{ text: 'main', value: 'main', default: undefined, protected: undefined }], + options: [{ text: 'selected', value: 'selected', default: undefined, protected: undefined }], }; - const selected = { name: 'main', value: 'main' }; + const selected = { name: 'selected', value: 'selected' }; it.each` branches | tags | commits | selectedRef | expectedResult diff --git a/spec/lib/gitlab/tracking/event_definition_spec.rb b/spec/lib/gitlab/tracking/event_definition_spec.rb index 5ea2f5a3c77..7fcc6eec733 100644 --- a/spec/lib/gitlab/tracking/event_definition_spec.rb +++ b/spec/lib/gitlab/tracking/event_definition_spec.rb @@ -144,6 +144,12 @@ RSpec.describe Gitlab::Tracking::EventDefinition, feature_category: :service_pin end describe '.extra_trackers' do + let(:dummy_tracking_class) { Class.new } + + before do + stub_const('Gitlab::Tracking::DummyTracking', dummy_tracking_class) + end + it 'returns an empty hash when no extra tracking classes are set' do expect(described_class.new(nil, {}).extra_trackers).to eq([]) end @@ -153,7 +159,7 @@ RSpec.describe Gitlab::Tracking::EventDefinition, feature_category: :service_pin extra_trackers: [ { - tracking_class: 'Gitlab::Tracking::AiTracking', + tracking_class: 'Gitlab::Tracking::DummyTracking', protected_properties: { prop: { description: 'description' } } } ] @@ -161,7 +167,7 @@ RSpec.describe Gitlab::Tracking::EventDefinition, feature_category: :service_pin config = attributes.merge(extra_trackers) expect(described_class.new(nil, config).extra_trackers) - .to eq({ Gitlab::Tracking::AiTracking => { protected_properties: [:prop] } }) + .to eq({ dummy_tracking_class => { protected_properties: [:prop] } }) end it 'returns the hash with extra tracking class with empty array when props are not set' do @@ -169,14 +175,14 @@ RSpec.describe Gitlab::Tracking::EventDefinition, feature_category: :service_pin extra_trackers: [ { - tracking_class: 'Gitlab::Tracking::AiTracking' + tracking_class: 'Gitlab::Tracking::DummyTracking' } ] } config = attributes.merge(extra_trackers) expect(described_class.new(nil, config).extra_trackers) - .to eq({ Gitlab::Tracking::AiTracking => { protected_properties: [] } }) + .to eq({ dummy_tracking_class => { protected_properties: [] } }) end end diff --git a/spec/requests/api/ml_model_packages_spec.rb b/spec/requests/api/ml_model_packages_spec.rb index 96434756e1e..f0748fddb2b 100644 --- a/spec/requests/api/ml_model_packages_spec.rb +++ b/spec/requests/api/ml_model_packages_spec.rb @@ -13,8 +13,10 @@ RSpec.describe ::API::MlModelPackages, feature_category: :mlops do let_it_be(:project, reload: true) { create(:project) } let_it_be(:personal_access_token) { create(:personal_access_token) } let_it_be(:job) { create(:ci_build, :running, user: personal_access_token.user, project: project) } - let_it_be(:deploy_token) { create(:deploy_token, read_package_registry: true, write_package_registry: true) } - let_it_be(:project_deploy_token) { create(:project_deploy_token, deploy_token: deploy_token, project: project) } + let_it_be(:deploy_token) do + create(:deploy_token, read_package_registry: true, write_package_registry: true, projects: [project]) + end + let_it_be(:another_project, reload: true) { create(:project) } let_it_be(:model) { create(:ml_models, user: project.owner, project: project) } let_it_be(:model_version) { create(:ml_model_versions, :with_package, model: model, version: '0.1.0') } diff --git a/spec/requests/api/oauth_tokens_spec.rb b/spec/requests/api/oauth_tokens_spec.rb index 139180d7873..8263bf4b81a 100644 --- a/spec/requests/api/oauth_tokens_spec.rb +++ b/spec/requests/api/oauth_tokens_spec.rb @@ -5,7 +5,11 @@ require 'spec_helper' RSpec.describe 'OAuth tokens', feature_category: :system_access do include HttpBasicAuthHelpers - let_it_be(:organization) { create(:organization, :default) } + let_it_be(:organization) { create(:organization) } + + before do + stub_current_organization(organization) + end context 'Resource Owner Password Credentials' do def request_oauth_token(user, headers = {}, password = user.password) diff --git a/spec/requests/oauth/flows/device_grant_spec.rb b/spec/requests/oauth/flows/device_grant_spec.rb index 2e91727d8d9..33ec7bb3019 100644 --- a/spec/requests/oauth/flows/device_grant_spec.rb +++ b/spec/requests/oauth/flows/device_grant_spec.rb @@ -2,8 +2,8 @@ require 'spec_helper' -RSpec.describe 'Gitlab OAuth2 Device Authorization Grant', feature_category: :system_access do - let_it_be(:organization) { create(:organization, :default) } +RSpec.describe 'Gitlab OAuth2 Device Authorization Grant', :with_current_organization, feature_category: :system_access do + let_it_be(:organization) { current_organization } let_it_be(:application) { create(:oauth_application, redirect_uri: 'urn:ietf:wg:oauth:2.0:oob', confidential: false) } let_it_be(:user) { create(:user, :with_namespace, organizations: [organization]) } let_it_be(:client_id) { application.uid } diff --git a/spec/services/ci/register_job_service_spec.rb b/spec/services/ci/register_job_service_spec.rb index 18acc56ff7b..91c0e901493 100644 --- a/spec/services/ci/register_job_service_spec.rb +++ b/spec/services/ci/register_job_service_spec.rb @@ -9,8 +9,8 @@ module Ci let_it_be_with_reload(:pipeline) { create(:ci_pipeline, project: project) } let_it_be(:shared_runner) { create(:ci_runner, :instance) } - let!(:project_runner) { create(:ci_runner, :project, projects: [project]) } - let!(:group_runner) { create(:ci_runner, :group, groups: [group]) } + let_it_be_with_reload(:project_runner) { create(:ci_runner, :project, projects: [project]) } + let_it_be_with_reload(:group_runner) { create(:ci_runner, :group, groups: [group]) } let!(:pending_job) { create(:ci_build, :pending, :queued, pipeline: pipeline) } describe '#execute' do @@ -463,9 +463,11 @@ module Ci context 'when first build is stalled' do before do - allow_any_instance_of(described_class).to receive(:assign_runner!).and_call_original - allow_any_instance_of(described_class).to receive(:assign_runner!) - .with(pending_job, anything).and_raise(ActiveRecord::StaleObjectError) + allow_next_instance_of(described_class) do |instance| + allow(instance).to receive(:assign_runner!).and_call_original + allow(instance).to receive(:assign_runner!) + .with(pending_job, anything).and_raise(ActiveRecord::StaleObjectError) + end end subject { described_class.new(project_runner, nil).execute } @@ -474,9 +476,10 @@ module Ci let!(:other_build) { create(:ci_build, :pending, :queued, pipeline: pipeline) } before do - allow_any_instance_of(::Ci::Queue::BuildQueueService) - .to receive(:execute) - .and_return(Ci::Build.where(id: [pending_job, other_build]).pluck(:id, :partition_id)) + allow_next_instance_of(::Ci::Queue::BuildQueueService) do |instance| + allow(instance).to receive(:execute) + .and_return(Ci::Build.where(id: [pending_job, other_build]).pluck(:id, :partition_id)) + end end it "receives second build from the queue" do @@ -487,9 +490,10 @@ module Ci context 'when single build is in queue' do before do - allow_any_instance_of(::Ci::Queue::BuildQueueService) - .to receive(:execute) - .and_return(Ci::Build.where(id: pending_job).pluck(:id, :partition_id)) + allow_next_instance_of(::Ci::Queue::BuildQueueService) do |instance| + allow(instance).to receive(:execute) + .and_return(Ci::Build.where(id: pending_job).pluck(:id, :partition_id)) + end end it "does not receive any valid result" do @@ -499,9 +503,10 @@ module Ci context 'when there is no build in queue' do before do - allow_any_instance_of(::Ci::Queue::BuildQueueService) - .to receive(:execute) - .and_return([]) + allow_next_instance_of(::Ci::Queue::BuildQueueService) do |instance| + allow(instance).to receive(:execute) + .and_return([]) + end end it "does not receive builds but result is valid" do @@ -893,6 +898,66 @@ module Ci end end end + + context 'when metrics recording fails' do + before do + project.update!(shared_runners_enabled: true) + pending_job.reload.create_queuing_entry! + + allow_next_instance_of(::Gitlab::Ci::Queue::Metrics) do |metric| + allow(metric).to receive(:register_success).and_raise(StandardError, 'metrics failure') + end + end + + it 'continues assignment when register_success fails' do + expect(Gitlab::ErrorTracking).to receive(:track_and_raise_for_dev_exception) + .with( + instance_of(StandardError), + hash_including( + build_id: pending_job.id, + build_name: pending_job.name, + build_stage: pending_job.stage_name, + pipeline_id: pending_job.pipeline_id, + project_id: pending_job.project_id + ) + ) + + result = described_class.new(shared_runner, nil).execute + + expect(result).to be_valid + expect(result.build).to eq(pending_job) + expect(result.build_json).to be_present + expect(pending_job.reload).to be_running + end + + it 'continues assignment when observe_queue_depth fails' do + allow_next_instance_of(::Gitlab::Ci::Queue::Metrics) do |metric| + allow(metric).to receive(:register_success).and_call_original + allow(metric).to receive(:observe_queue_depth) + .with(:found, anything) + .and_raise(StandardError, 'queue depth metrics failure') + end + + expect(Gitlab::ErrorTracking).to receive(:track_and_raise_for_dev_exception) + .with( + instance_of(StandardError), + hash_including( + build_id: pending_job.id, + build_name: pending_job.name, + build_stage: pending_job.stage_name, + pipeline_id: pending_job.pipeline_id, + project_id: pending_job.project_id + ) + ) + + result = described_class.new(shared_runner, nil).execute + + expect(result).to be_valid + expect(result.build).to eq(pending_job) + expect(result.build_json).to be_present + expect(pending_job.reload).to be_running + end + end end context 'when using pending builds table' do @@ -1137,6 +1202,25 @@ module Ci expect(result.build).to be_nil expect(result.build_json).to be_nil end + + context 'when metrics tracking raises an error during conflict' do + let(:error) { StandardError.new('Metrics tracking failed') } + + before do + allow_next_instance_of(Gitlab::Ci::Queue::Metrics) do |metric| + allow(metric).to receive(:observe_queue_depth).with(:conflict, any_args).and_raise(error) + end + end + + it 'tracks and raises the exception for dev without affecting the result' do + expect(Gitlab::ErrorTracking).to receive(:track_and_raise_for_dev_exception).with(error) + + result = described_class.new(project_runner, nil).execute + + expect(result).not_to be_valid + expect(result.build).to be_nil + end + end end def build_on(runner, runner_manager: nil, params: {}) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 7ccdf9f166e..1b32198cb31 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -224,6 +224,7 @@ RSpec.configure do |config| config.include_context 'when rendered has no HTML escapes', type: :view config.include_context 'with STI disabled', type: :model + include StubCurrentOrganization include StubFeatureFlags include StubSnowplow include StubMember diff --git a/spec/support/helpers/current_organization_helper.rb b/spec/support/helpers/current_organization_helper.rb new file mode 100644 index 00000000000..b87555f67e4 --- /dev/null +++ b/spec/support/helpers/current_organization_helper.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +module StubCurrentOrganization + def stub_current_organization(organization) + allow_next_instance_of(Gitlab::Current::Organization) do |instance| + allow(instance).to receive(:organization).and_return(organization) + end + end +end diff --git a/spec/support/shared_contexts/current_organization_context.rb b/spec/support/shared_contexts/current_organization_context.rb index 227e6a97647..92381eaa5fd 100644 --- a/spec/support/shared_contexts/current_organization_context.rb +++ b/spec/support/shared_contexts/current_organization_context.rb @@ -8,9 +8,7 @@ RSpec.shared_context 'with current_organization setting' do end before do - allow_next_instance_of(::Gitlab::Current::Organization) do |organization| - allow(organization).to receive(:organization).and_return(current_organization) - end + stub_current_organization(current_organization) end end diff --git a/spec/support/shared_contexts/requests/api/conan_packages_shared_context.rb b/spec/support/shared_contexts/requests/api/conan_packages_shared_context.rb index bf87671f591..16f0e28dff5 100644 --- a/spec/support/shared_contexts/requests/api/conan_packages_shared_context.rb +++ b/spec/support/shared_contexts/requests/api/conan_packages_shared_context.rb @@ -8,11 +8,7 @@ RSpec.shared_context 'with conan api setup' do let_it_be(:user) { create(:user, developer_of: [project]) } let_it_be(:personal_access_token) { create(:personal_access_token, user: user) } let_it_be(:deploy_token) do - create(:deploy_token, read_package_registry: true, write_package_registry: true) - end - - let_it_be(:project_deploy_token, freeze: true) do - create(:project_deploy_token, deploy_token: deploy_token, project: project) + create(:deploy_token, read_package_registry: true, write_package_registry: true, projects: [project]) end let_it_be(:job, freeze: true) { create(:ci_build, :running, user: user, project: project) } diff --git a/spec/workers/gitlab/counters/flush_stale_counter_increments_cron_worker_spec.rb b/spec/workers/gitlab/counters/flush_stale_counter_increments_cron_worker_spec.rb index a478b51af74..42d44292a66 100644 --- a/spec/workers/gitlab/counters/flush_stale_counter_increments_cron_worker_spec.rb +++ b/spec/workers/gitlab/counters/flush_stale_counter_increments_cron_worker_spec.rb @@ -9,7 +9,6 @@ RSpec.describe ::Gitlab::Counters::FlushStaleCounterIncrementsCronWorker, featur context 'when we are on gitlab.com' do before do allow(Gitlab).to receive(:com_except_jh?).and_return(true) - allow(::Gitlab::Counters::FlushStaleCounterIncrementsWorker).to receive(:perform_with_capacity) end it 'calls FlushStaleCounterIncrementsWorker.perform_with_capacity' do diff --git a/spec/workers/gitlab/counters/flush_stale_counter_increments_worker_spec.rb b/spec/workers/gitlab/counters/flush_stale_counter_increments_worker_spec.rb index af42f475aa6..773aa1ac323 100644 --- a/spec/workers/gitlab/counters/flush_stale_counter_increments_worker_spec.rb +++ b/spec/workers/gitlab/counters/flush_stale_counter_increments_worker_spec.rb @@ -78,18 +78,26 @@ RSpec.describe ::Gitlab::Counters::FlushStaleCounterIncrementsWorker, :saas, :cl before do Gitlab::Redis::SharedState.with do |redis| redis.set(redis_key, project_daily_statistic.id) + stub_const("#{described_class}::ID_RANGES", { ProjectDailyStatistic => { + end_id: project_daily_statistic_three.id + } }) + stub_const("#{described_class}::BATCH_LIMIT", 1) end - allow(Gitlab::Saas).to receive(:feature_available?).with(:purchases_additional_minutes).and_return(true) end it "flushes stale counters and updates the redis start id" do Gitlab::Redis::SharedState.with do |redis| expect(redis.get(redis_key).to_i).to eq(project_daily_statistic.id) end + + # Test that the service is called multiple times (batching behavior) + # without running the full 100 iterations, and verify early termination + # when no more work is available + expect_next_instance_of(Gitlab::Counters::FlushStaleCounterIncrements) do |service| expect(service).to receive(:execute) .and_call_original - end + end.thrice expect_initial_counts worker.perform_work