diff options
Diffstat (limited to 'app/controllers/projects')
28 files changed, 366 insertions, 148 deletions
diff --git a/app/controllers/projects/alerting/notifications_controller.rb b/app/controllers/projects/alerting/notifications_controller.rb index 89e8a261288..281ac14d3ce 100644 --- a/app/controllers/projects/alerting/notifications_controller.rb +++ b/app/controllers/projects/alerting/notifications_controller.rb @@ -72,7 +72,7 @@ module Projects end def endpoint_identifier - params[:endpoint_identifier] || AlertManagement::HttpIntegration::LEGACY_IDENTIFIER + params[:endpoint_identifier] || AlertManagement::HttpIntegration::LEGACY_IDENTIFIERS end def notification_payload diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb index 28393e1f365..b41e4d11d24 100644 --- a/app/controllers/projects/blob_controller.rb +++ b/app/controllers/projects/blob_controller.rb @@ -49,6 +49,7 @@ class Projects::BlobController < Projects::ApplicationController before_action do push_frontend_feature_flag(:highlight_js, @project) + push_frontend_feature_flag(:highlight_js_worker, @project) push_frontend_feature_flag(:explain_code_chat, current_user) push_licensed_feature(:file_locks) if @project.licensed_feature_available?(:file_locks) end @@ -160,6 +161,8 @@ class Projects::BlobController < Projects::ApplicationController end def check_for_ambiguous_ref + return if Feature.enabled?(:redirect_with_ref_type, @project) + @ref_type = ref_type if @ref_type == ExtractsRef::BRANCH_REF_TYPE && ambiguous_ref?(@project, @ref) @@ -169,7 +172,17 @@ class Projects::BlobController < Projects::ApplicationController end def commit - @commit ||= @repository.commit(@ref) + if Feature.enabled?(:redirect_with_ref_type, @project) + response = ::ExtractsRef::RequestedRef.new(@repository, ref_type: ref_type, ref: @ref).find + @commit = response[:commit] + @ref_type = response[:ref_type] + + if response[:ambiguous] + return redirect_to(project_blob_path(@project, File.join(@ref_type ? @ref : @commit.id, @path), ref_type: @ref_type)) + end + else + @commit ||= @repository.commit(@ref) + end return render_404 unless @commit end diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb index 10d0d03e56d..4cc1ed092d2 100644 --- a/app/controllers/projects/environments_controller.rb +++ b/app/controllers/projects/environments_controller.rb @@ -12,12 +12,8 @@ class Projects::EnvironmentsController < Projects::ApplicationController push_frontend_feature_flag(:environment_details_vue, @project) end - before_action only: [:index] do - push_frontend_feature_flag(:kas_user_access_project, @project) - end - - before_action only: [:edit, :new] do - push_frontend_feature_flag(:environment_settings_to_graphql, @project) + before_action only: [:index, :edit, :new] do + push_frontend_feature_flag(:kubernetes_namespace_for_environment) end before_action :authorize_read_environment! @@ -28,7 +24,7 @@ class Projects::EnvironmentsController < Projects::ApplicationController before_action :environment, only: [:show, :edit, :update, :stop, :terminal, :terminal_websocket_authorize, :cancel_auto_stop] before_action :verify_api_request!, only: :terminal_websocket_authorize before_action :expire_etag_cache, only: [:index], unless: -> { request.format.json? } - before_action :set_kas_cookie, only: [:index], if: -> { current_user && request.format.html? } + before_action :set_kas_cookie, only: [:index, :edit, :new], if: -> { current_user && request.format.html? } after_action :expire_etag_cache, only: [:cancel_auto_stop] track_event :index, :folder, :show, :new, :edit, :create, :update, :stop, :cancel_auto_stop, :terminal, diff --git a/app/controllers/projects/forks_controller.rb b/app/controllers/projects/forks_controller.rb index ff3dc71b6cc..de2040afff3 100644 --- a/app/controllers/projects/forks_controller.rb +++ b/app/controllers/projects/forks_controller.rb @@ -65,9 +65,8 @@ class Projects::ForksController < Projects::ApplicationController end end - # rubocop: disable CodeReuse/ActiveRecord def create - @forked_project = fork_namespace.projects.find_by(path: project.path) + @forked_project = fork_namespace.projects.find_by(path: project.path) # rubocop: disable CodeReuse/ActiveRecord @forked_project = nil unless @forked_project && @forked_project.forked_from_project == project @forked_project ||= fork_service.execute @@ -96,7 +95,9 @@ class Projects::ForksController < Projects::ApplicationController current_user: current_user ).execute + # rubocop: disable CodeReuse/ActiveRecord forks.includes(:route, :creator, :group, :topics, namespace: [:route, :owner]) + # rubocop: enable CodeReuse/ActiveRecord end def fork_service @@ -130,15 +131,21 @@ class Projects::ForksController < Projects::ApplicationController end def load_namespaces_with_associations + # rubocop: disable CodeReuse/ActiveRecord @load_namespaces_with_associations ||= fork_service.valid_fork_targets(only_groups: true).preload(:route) + # rubocop: enable CodeReuse/ActiveRecord end def memberships_hash + # rubocop: disable CodeReuse/ActiveRecord current_user.members.where(source: load_namespaces_with_associations).index_by(&:source_id) + # rubocop: enable CodeReuse/ActiveRecord end def forked_projects_by_namespace(namespaces) + # rubocop: disable CodeReuse/ActiveRecord project.forks.where(namespace: namespaces).includes(:namespace).index_by(&:namespace_id) + # rubocop: enable CodeReuse/ActiveRecord end end diff --git a/app/controllers/projects/grafana_api_controller.rb b/app/controllers/projects/grafana_api_controller.rb deleted file mode 100644 index 2cc6c6c35ba..00000000000 --- a/app/controllers/projects/grafana_api_controller.rb +++ /dev/null @@ -1,46 +0,0 @@ -# frozen_string_literal: true - -class Projects::GrafanaApiController < Projects::ApplicationController - include RenderServiceResults - include MetricsDashboard - - before_action :authorize_read_grafana!, only: :proxy - - feature_category :metrics - urgency :low - - def proxy - return not_found if Feature.enabled?(:remove_monitor_metrics) - - result = ::Grafana::ProxyService.new( - project, - params[:datasource_id], - params[:proxy_path], - prometheus_params - ).execute - - return continue_polling_response if result.nil? - return error_response(result) if result[:status] == :error - - success_response(result) - end - - private - - def metrics_dashboard_params - params.permit(:embedded, :grafana_url) - end - - def query_params - params.permit(:query, :start_time, :end_time, :step) - end - - def prometheus_params - query_params.to_h - .except(:start_time, :end_time) - .merge( - start: query_params[:start_time], - end: query_params[:end_time] - ) - end -end diff --git a/app/controllers/projects/incidents_controller.rb b/app/controllers/projects/incidents_controller.rb index 7121096bd77..6109e29b169 100644 --- a/app/controllers/projects/incidents_controller.rb +++ b/app/controllers/projects/incidents_controller.rb @@ -11,6 +11,7 @@ class Projects::IncidentsController < Projects::ApplicationController push_force_frontend_feature_flag(:work_items_mvc, @project&.work_items_mvc_feature_flag_enabled?) push_force_frontend_feature_flag(:work_items_mvc_2, @project&.work_items_mvc_2_feature_flag_enabled?) push_frontend_feature_flag(:moved_mr_sidebar, project) + push_frontend_feature_flag(:move_close_into_dropdown, project) end feature_category :incident_management diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 6311907a859..6a45595580f 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -51,13 +51,14 @@ class Projects::IssuesController < Projects::ApplicationController push_frontend_feature_flag(:service_desk_new_note_email_native_attachments, project) push_frontend_feature_flag(:saved_replies, current_user) push_frontend_feature_flag(:issues_grid_view) + push_frontend_feature_flag(:service_desk_ticket) end before_action only: [:index, :show] do push_force_frontend_feature_flag(:work_items, project&.work_items_feature_flag_enabled?) end - before_action only: :index do + before_action only: [:index, :service_desk] do push_frontend_feature_flag(:or_issuable_queries, project) push_frontend_feature_flag(:frontend_caching, project&.group) end @@ -69,6 +70,7 @@ class Projects::IssuesController < Projects::ApplicationController push_force_frontend_feature_flag(:work_items_mvc_2, project&.work_items_mvc_2_feature_flag_enabled?) push_frontend_feature_flag(:epic_widget_edit_confirmation, project) push_frontend_feature_flag(:moved_mr_sidebar, project) + push_frontend_feature_flag(:move_close_into_dropdown, project) end around_action :allow_gitaly_ref_name_caching, only: [:discussions] diff --git a/app/controllers/projects/jobs_controller.rb b/app/controllers/projects/jobs_controller.rb index 79ddcbf732d..4e0b304a2ee 100644 --- a/app/controllers/projects/jobs_controller.rb +++ b/app/controllers/projects/jobs_controller.rb @@ -21,7 +21,7 @@ class Projects::JobsController < Projects::ApplicationController before_action :verify_proxy_request!, only: :proxy_websocket_authorize before_action :push_job_log_jump_to_failures, only: [:show] before_action :reject_if_build_artifacts_size_refreshing!, only: [:erase] - + before_action :push_ai_build_failure_cause, only: [:show] layout 'project' feature_category :continuous_integration @@ -258,4 +258,8 @@ class Projects::JobsController < Projects::ApplicationController def push_job_log_jump_to_failures push_frontend_feature_flag(:job_log_jump_to_failures, @project) end + + def push_ai_build_failure_cause + push_frontend_feature_flag(:ai_build_failure_cause, @project) + end end diff --git a/app/controllers/projects/merge_requests/conflicts_controller.rb b/app/controllers/projects/merge_requests/conflicts_controller.rb index 76a233afa13..66a358963e2 100644 --- a/app/controllers/projects/merge_requests/conflicts_controller.rb +++ b/app/controllers/projects/merge_requests/conflicts_controller.rb @@ -15,7 +15,8 @@ class Projects::MergeRequests::ConflictsController < Projects::MergeRequests::Ap respond_to do |format| format.html do @issuable_sidebar = serializer.represent(@merge_request, serializer: 'sidebar') - Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter.track_loading_conflict_ui_action(user: current_user) + Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter + .track_loading_conflict_ui_action(user: current_user) end format.json do @@ -23,12 +24,14 @@ class Projects::MergeRequests::ConflictsController < Projects::MergeRequests::Ap render json: @conflicts_list elsif @merge_request.can_be_merged? render json: { - message: _('The merge conflicts for this merge request have already been resolved. Please return to the merge request.'), + message: _('The merge conflicts for this merge request have already been resolved. ' \ + 'Please return to the merge request.'), type: 'error' } else render json: { - message: _('The merge conflicts for this merge request cannot be resolved through GitLab. Please try to resolve them locally.'), + message: _('The merge conflicts for this merge request cannot be resolved through GitLab. ' \ + 'Please try to resolve them locally.'), type: 'error' } end @@ -52,7 +55,8 @@ class Projects::MergeRequests::ConflictsController < Projects::MergeRequests::Ap Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter.track_resolve_conflict_action(user: current_user) if @merge_request.can_be_merged? - render status: :bad_request, json: { message: _('The merge conflicts for this merge request have already been resolved.') } + render status: :bad_request, + json: { message: _('The merge conflicts for this merge request have already been resolved.') } return end @@ -71,6 +75,8 @@ class Projects::MergeRequests::ConflictsController < Projects::MergeRequests::Ap private + alias_method :issuable, :merge_request + def authorize_can_resolve_conflicts! @conflicts_list = ::MergeRequests::Conflicts::ListService.new(@merge_request) diff --git a/app/controllers/projects/merge_requests/creations_controller.rb b/app/controllers/projects/merge_requests/creations_controller.rb index 06381315614..6a3523b82d9 100644 --- a/app/controllers/projects/merge_requests/creations_controller.rb +++ b/app/controllers/projects/merge_requests/creations_controller.rb @@ -11,6 +11,12 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap before_action :apply_diff_view_cookie!, only: [:diffs, :diff_for_path] before_action :build_merge_request, except: [:create] + before_action only: [:new] do + if can?(current_user, :fill_in_merge_request_template, project) + push_frontend_feature_flag(:fill_in_mr_template, project) + end + end + urgency :low, [ :new, :create, @@ -25,7 +31,9 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap end def create - @merge_request = ::MergeRequests::CreateService.new(project: project, current_user: current_user, params: merge_request_params).execute + @merge_request = ::MergeRequests::CreateService + .new(project: project, current_user: current_user, params: merge_request_params) + .execute if @merge_request.valid? incr_count_webide_merge_request @@ -82,7 +90,10 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap def branch_to @target_project = selected_target_project - if @target_project && params[:ref].present? && Ability.allowed?(current_user, :create_merge_request_in, @target_project) + if @target_project && + params[:ref].present? && + Ability.allowed?(current_user, :create_merge_request_in, @target_project) + @ref = params[:ref] @commit = @target_project.commit(Gitlab::Git::BRANCH_REF_PREFIX + @ref) end @@ -104,10 +115,13 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap def build_merge_request params[:merge_request] ||= ActionController::Parameters.new(source_project: @project) + new_params = merge_request_params.merge(diff_options: diff_options) # Gitaly N+1 issue: https://gitlab.com/gitlab-org/gitlab-foss/issues/58096 Gitlab::GitalyClient.allow_n_plus_1_calls do - @merge_request = ::MergeRequests::BuildService.new(project: project, current_user: current_user, params: merge_request_params.merge(diff_options: diff_options)).execute + @merge_request = ::MergeRequests::BuildService + .new(project: project, current_user: current_user, params: new_params) + .execute end end diff --git a/app/controllers/projects/merge_requests/diffs_controller.rb b/app/controllers/projects/merge_requests/diffs_controller.rb index f3a01fd3223..5bd0063ab95 100644 --- a/app/controllers/projects/merge_requests/diffs_controller.rb +++ b/app/controllers/projects/merge_requests/diffs_controller.rb @@ -49,7 +49,8 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic pagination_data: diffs.pagination_data } - # NOTE: Any variables that would affect the resulting json needs to be added to the cache_context to avoid stale cache issues. + # NOTE: Any variables that would affect the resulting json needs to be added to the cache_context + # to avoid stale cache issues. cache_context = [ current_user&.cache_key, unfoldable_positions.map(&:to_h), @@ -130,7 +131,8 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic # rubocop: disable CodeReuse/ActiveRecord def commit return unless commit_id = params[:commit_id].presence - return unless @merge_request.all_commits.exists?(sha: commit_id) || @merge_request.recent_context_commits.map(&:id).include?(commit_id) + return unless @merge_request.all_commits.exists?(sha: commit_id) || + @merge_request.recent_context_commits.map(&:id).include?(commit_id) @commit ||= @project.commit(commit_id) end @@ -160,7 +162,10 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic end end - return @merge_request.context_commits_diff if show_only_context_commits? && !@merge_request.context_commits_diff.empty? + if show_only_context_commits? && !@merge_request.context_commits_diff.empty? + return @merge_request.context_commits_diff + end + return @merge_request.merge_head_diff if render_merge_ref_head_diff? if @start_sha diff --git a/app/controllers/projects/merge_requests/drafts_controller.rb b/app/controllers/projects/merge_requests/drafts_controller.rb index ca6ab83b877..74c495261a3 100644 --- a/app/controllers/projects/merge_requests/drafts_controller.rb +++ b/app/controllers/projects/merge_requests/drafts_controller.rb @@ -27,17 +27,23 @@ class Projects::MergeRequests::DraftsController < Projects::MergeRequests::Appli draft_note = create_service.execute + if draft_note.errors.present? + render json: { errors: draft_note.errors.full_messages.to_sentence }, status: :unprocessable_entity + return + end + prepare_notes_for_rendering(draft_note) render json: DraftNoteSerializer.new(current_user: current_user).represent(draft_note) end def update - draft_note.update!(draft_note_params) - - prepare_notes_for_rendering(draft_note) - - render json: DraftNoteSerializer.new(current_user: current_user).represent(draft_note) + if draft_note.update(draft_note_params) + prepare_notes_for_rendering(draft_note) + render json: DraftNoteSerializer.new(current_user: current_user).represent(draft_note) + else + render json: { errors: draft_note.errors.full_messages.to_sentence }, status: :unprocessable_entity + end end def destroy @@ -57,10 +63,13 @@ class Projects::MergeRequests::DraftsController < Projects::MergeRequests::Appli if Gitlab::Utils.to_boolean(approve_params[:approve]) unless merge_request.approved_by?(current_user) - success = ::MergeRequests::ApprovalService.new(project: @project, current_user: current_user, params: approve_params).execute(merge_request) + success = ::MergeRequests::ApprovalService + .new(project: @project, current_user: current_user, params: approve_params) + .execute(merge_request) unless success - return render json: { message: _('An error occurred while approving, please try again.') }, status: :internal_server_error + return render json: { message: _('An error occurred while approving, please try again.') }, + status: :internal_server_error end end @@ -101,7 +110,9 @@ class Projects::MergeRequests::DraftsController < Projects::MergeRequests::Appli # rubocop: disable CodeReuse/ActiveRecord def merge_request - @merge_request ||= MergeRequestsFinder.new(current_user, project_id: @project.id).find_by!(iid: params[:merge_request_id]) + @merge_request ||= MergeRequestsFinder + .new(current_user, project_id: @project.id) + .find_by!(iid: params[:merge_request_id]) end # rubocop: enable CodeReuse/ActiveRecord diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 60f619a8d20..2172c91fc76 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -41,19 +41,23 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo push_force_frontend_feature_flag(:content_editor_on_issues, project&.content_editor_on_issues_feature_flag_enabled?) push_frontend_feature_flag(:core_security_mr_widget_counts, project) push_frontend_feature_flag(:issue_assignees_widget, @project) - push_frontend_feature_flag(:deprecate_vulnerabilities_feedback, @project) push_frontend_feature_flag(:moved_mr_sidebar, project) + push_frontend_feature_flag(:sast_reports_in_inline_diff, project) push_frontend_feature_flag(:mr_experience_survey, project) push_frontend_feature_flag(:saved_replies, current_user) push_frontend_feature_flag(:code_quality_inline_drawer, project) - push_frontend_feature_flag(:auto_merge_labels_mr_widget, project) push_force_frontend_feature_flag(:summarize_my_code_review, summarize_my_code_review_enabled?) push_frontend_feature_flag(:mr_activity_filters, current_user) push_frontend_feature_flag(:review_apps_redeploy_mr_widget, project) - push_frontend_feature_flag(:comment_on_files, current_user) push_frontend_feature_flag(:ci_job_failures_in_mr, project) end + before_action only: [:edit] do + if can?(current_user, :fill_in_merge_request_template, project) + push_frontend_feature_flag(:fill_in_mr_template, project) + end + end + around_action :allow_gitaly_ref_name_caching, only: [:index, :show, :diffs, :discussions] after_action :log_merge_request_show, only: [:show, :diffs] @@ -124,16 +128,31 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo @merge_request.recent_context_commits ) - per_page = [(params[:per_page] || MergeRequestDiff::COMMITS_SAFE_SIZE).to_i, MergeRequestDiff::COMMITS_SAFE_SIZE].min - recent_commits = @merge_request.recent_commits(load_from_gitaly: true, limit: per_page, page: params[:page]).with_latest_pipeline(@merge_request.source_branch).with_markdown_cache + per_page = [ + (params[:per_page] || MergeRequestDiff::COMMITS_SAFE_SIZE).to_i, + MergeRequestDiff::COMMITS_SAFE_SIZE + ].min + recent_commits = @merge_request + .recent_commits(load_from_gitaly: true, limit: per_page, page: params[:page]) + .with_latest_pipeline(@merge_request.source_branch) + .with_markdown_cache @next_page = recent_commits.next_page @commits = set_commits_for_rendering( recent_commits, commits_count: @merge_request.commits_count ) - commits_count = @merge_request.preparing? ? '-' : @merge_request.commits_count + @merge_request.context_commits_count - render json: { html: view_to_html_string('projects/merge_requests/_commits'), next_page: @next_page, count: commits_count } + commits_count = if @merge_request.preparing? + '-' + else + @merge_request.commits_count + @merge_request.context_commits_count + end + + render json: { + html: view_to_html_string('projects/merge_requests/_commits'), + next_page: @next_page, + count: commits_count + } end def pipelines @@ -221,7 +240,9 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo end def update - @merge_request = ::MergeRequests::UpdateService.new(project: project, current_user: current_user, params: merge_request_update_params).execute(@merge_request) + @merge_request = ::MergeRequests::UpdateService + .new(project: project, current_user: current_user, params: merge_request_update_params) + .execute(@merge_request) respond_to do |format| format.html do @@ -287,7 +308,9 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo end def assign_related_issues - result = ::MergeRequests::AssignIssuesService.new(project: project, current_user: current_user, params: { merge_request: @merge_request }).execute + result = ::MergeRequests::AssignIssuesService + .new(project: project, current_user: current_user, params: { merge_request: @merge_request }) + .execute case result[:count] when 0 @@ -317,7 +340,8 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo end def rebase - @merge_request.rebase_async(current_user.id, skip_ci: Gitlab::Utils.to_boolean(merge_params[:skip_ci], default: false)) + @merge_request + .rebase_async(current_user.id, skip_ci: Gitlab::Utils.to_boolean(merge_params[:skip_ci], default: false)) head :ok rescue MergeRequest::RebaseLockTimeout => e @@ -334,7 +358,8 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo IssuableExportCsvWorker.perform_async(:merge_request, current_user.id, project.id, finder_options.to_h) # rubocop:disable CodeReuse/Worker index_path = project_merge_requests_path(project) - message = _('Your CSV export has started. It will be emailed to %{email} when complete.') % { email: current_user.notification_email_or_default } + message = _('Your CSV export has started. It will be emailed to %{email} when complete.') % + { email: current_user.notification_email_or_default } redirect_to(index_path, notice: message) end @@ -432,10 +457,15 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo @commits_count = @merge_request.commits_count + @merge_request.context_commits_count @diffs_count = get_diffs_count @issuable_sidebar = serializer.represent(@merge_request, serializer: 'sidebar') - @current_user_data = Gitlab::Json.dump(UserSerializer.new(project: @project).represent(current_user, {}, MergeRequestCurrentUserEntity)) + @current_user_data = Gitlab::Json + .dump(UserSerializer.new(project: @project) + .represent(current_user, {}, MergeRequestCurrentUserEntity)) @show_whitespace_default = current_user.nil? || current_user.show_whitespace_in_diffs @file_by_file_default = current_user&.view_diffs_file_by_file - @coverage_path = coverage_reports_project_merge_request_path(@project, @merge_request, format: :json) if @merge_request.has_coverage_reports? + if @merge_request.has_coverage_reports? + @coverage_path = coverage_reports_project_merge_request_path(@project, @merge_request, format: :json) + end + @update_current_user_path = expose_path(api_v4_user_preferences_path) @endpoint_metadata_url = endpoint_metadata_url(@project, @merge_request) @endpoint_diff_batch_url = endpoint_diff_batch_url(@project, @merge_request) @@ -478,12 +508,18 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo def merge! # Disable the CI check if auto_merge_strategy is specified since we have # to wait until CI completes to know - unless @merge_request.mergeable?(skip_ci_check: auto_merge_requested?) + skipped_checks = @merge_request.skipped_mergeable_checks( + auto_merge_requested: auto_merge_requested?, + auto_merge_strategy: params[:auto_merge_strategy] + ) + + unless @merge_request.mergeable?(**skipped_checks) return :failed end squashing = params.fetch(:squash, false) - merge_service = ::MergeRequests::MergeService.new(project: @project, current_user: current_user, params: merge_params) + merge_service = ::MergeRequests::MergeService + .new(project: @project, current_user: current_user, params: merge_params) unless merge_service.hooks_validation_pass?(@merge_request, validate_squash_message: squashing) return :hook_validation_error @@ -500,7 +536,10 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo AutoMergeService.new(project, current_user, merge_params).update(merge_request) else AutoMergeService.new(project, current_user, merge_params) - .execute(merge_request, params[:auto_merge_strategy] || AutoMergeService::STRATEGY_MERGE_WHEN_PIPELINE_SUCCEEDS) + .execute( + merge_request, + params[:auto_merge_strategy] || AutoMergeService::STRATEGY_MERGE_WHEN_PIPELINE_SUCCEEDS + ) end else @merge_request.merge_async(current_user.id, merge_params) @@ -595,7 +634,9 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo def endpoint_diff_batch_url(project, merge_request) per_page = current_user&.view_diffs_file_by_file ? '1' : '5' - params = request.query_parameters.merge(view: 'inline', diff_head: true, w: show_whitespace, page: '0', per_page: per_page) + params = request + .query_parameters + .merge(view: 'inline', diff_head: true, w: show_whitespace, page: '0', per_page: per_page) params[:ck] = merge_request.merge_head_diff&.id if merge_request.diffs_batch_cache_with_max_age? diffs_batch_project_json_merge_request_path(project, merge_request, 'json', params) diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb index 35b65dbce7e..1f4e5b54500 100644 --- a/app/controllers/projects/milestones_controller.rb +++ b/app/controllers/projects/milestones_controller.rb @@ -24,6 +24,10 @@ class Projects::MilestonesController < Projects::ApplicationController feature_category :team_planning urgency :low + before_action do + push_frontend_feature_flag(:content_editor_on_issues, @project) + end + def index @sort = params[:sort] || 'due_date_asc' @milestones = milestones.sort_by_attribute(@sort) diff --git a/app/controllers/projects/ml/candidates_controller.rb b/app/controllers/projects/ml/candidates_controller.rb index ed7155fc5f4..9905e454acb 100644 --- a/app/controllers/projects/ml/candidates_controller.rb +++ b/app/controllers/projects/ml/candidates_controller.rb @@ -3,7 +3,9 @@ module Projects module Ml class CandidatesController < ApplicationController - before_action :check_feature_enabled, :set_candidate + before_action :set_candidate + before_action :check_read, only: [:show] + before_action :check_write, only: [:destroy] feature_category :mlops @@ -26,9 +28,13 @@ module Projects render_404 unless @candidate.present? end - def check_feature_enabled + def check_read render_404 unless can?(current_user, :read_model_experiments, @project) end + + def check_write + render_404 unless can?(current_user, :write_model_experiments, @project) + end end end end diff --git a/app/controllers/projects/ml/experiments_controller.rb b/app/controllers/projects/ml/experiments_controller.rb index a620e9919e7..85e7f63779c 100644 --- a/app/controllers/projects/ml/experiments_controller.rb +++ b/app/controllers/projects/ml/experiments_controller.rb @@ -5,7 +5,8 @@ module Projects class ExperimentsController < ::Projects::ApplicationController include Projects::Ml::ExperimentsHelper - before_action :check_feature_enabled + before_action :check_read, only: [:show, :index] + before_action :check_write, only: [:destroy] before_action :set_experiment, only: [:show, :destroy] feature_category :mlops @@ -55,10 +56,14 @@ module Projects private - def check_feature_enabled + def check_read render_404 unless can?(current_user, :read_model_experiments, @project) end + def check_write + render_404 unless can?(current_user, :write_model_experiments, @project) + end + def set_experiment @experiment = ::Ml::Experiment.by_project_id_and_iid(@project.id, params[:iid]) diff --git a/app/controllers/projects/ml/models_controller.rb b/app/controllers/projects/ml/models_controller.rb new file mode 100644 index 00000000000..77855b73cbd --- /dev/null +++ b/app/controllers/projects/ml/models_controller.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module Projects + module Ml + class ModelsController < ::Projects::ApplicationController + before_action :check_feature_enabled + feature_category :mlops + + def index + @models = ::Projects::Ml::ModelFinder.new(@project).execute + end + + private + + def check_feature_enabled + render_404 unless can?(current_user, :read_model_registry, @project) + end + end + end +end diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb index 054e8c302c9..7fcdf220bd2 100644 --- a/app/controllers/projects/notes_controller.rb +++ b/app/controllers/projects/notes_controller.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true class Projects::NotesController < Projects::ApplicationController + extend Gitlab::Utils::Override include RendersNotes include NotesActions include NotesHelper @@ -11,10 +12,30 @@ class Projects::NotesController < Projects::ApplicationController before_action :authorize_create_note!, only: [:create] before_action :authorize_resolve_note!, only: [:resolve, :unresolve] - feature_category :team_planning + feature_category :team_planning, [:index, :create, :update, :destroy, :delete_attachment, :toggle_award_emoji] + feature_category :code_review_workflow, [:resolve, :unresolve, :outdated_line_change] urgency :medium, [:index] urgency :low, [:create, :update, :destroy, :resolve, :unresolve, :toggle_award_emoji, :outdated_line_change] + override :feature_category + def feature_category + if %w[index create].include?(params[:action]) + category = feature_category_override_for_target_type(params[:target_type]) + return category if category + end + + super + end + + def feature_category_override_for_target_type(target_type) + case target_type + when 'merge_request' + 'code_review_workflow' + when 'commit', 'project_snippet' + 'source_code_management' + end + end + def delete_attachment note.remove_attachment! note.update_attribute(:attachment, nil) diff --git a/app/controllers/projects/pages_controller.rb b/app/controllers/projects/pages_controller.rb index 332d33b8e52..6cfbb61fbb2 100644 --- a/app/controllers/projects/pages_controller.rb +++ b/app/controllers/projects/pages_controller.rb @@ -1,8 +1,6 @@ # frozen_string_literal: true class Projects::PagesController < Projects::ApplicationController - layout :resolve_layout - before_action :require_pages_enabled! before_action :authorize_read_pages!, only: [:show] before_action :authorize_update_pages!, except: [:show, :destroy] @@ -10,10 +8,6 @@ class Projects::PagesController < Projects::ApplicationController feature_category :pages - before_action do - push_frontend_feature_flag(:show_pages_in_deployments_menu, current_user, type: :experiment) - end - def new @pipeline_wizard_data = { project_path: @project.full_path, @@ -66,10 +60,6 @@ class Projects::PagesController < Projects::ApplicationController private - def resolve_layout - 'project_settings' unless Feature.enabled?(:show_pages_in_deployments_menu, current_user, type: :experiment) - end - def project_params params.require(:project).permit(project_params_attributes) end diff --git a/app/controllers/projects/pipeline_schedules_controller.rb b/app/controllers/projects/pipeline_schedules_controller.rb index fb332fec3b5..4fd307b5105 100644 --- a/app/controllers/projects/pipeline_schedules_controller.rb +++ b/app/controllers/projects/pipeline_schedules_controller.rb @@ -25,14 +25,25 @@ class Projects::PipelineSchedulesController < Projects::ApplicationController end def create - @schedule = Ci::CreatePipelineScheduleService - .new(@project, current_user, schedule_params) - .execute - - if @schedule.persisted? - redirect_to pipeline_schedules_path(@project) + if ::Feature.enabled?(:ci_refactoring_pipeline_schedule_create_service, @project) + response = Ci::PipelineSchedules::CreateService.new(@project, current_user, schedule_params).execute + @schedule = response.payload + + if response.success? + redirect_to pipeline_schedules_path(@project) + else + render :new + end else - render :new + @schedule = Ci::CreatePipelineScheduleService + .new(@project, current_user, schedule_params) + .execute + + if @schedule.persisted? + redirect_to pipeline_schedules_path(@project) + else + render :new + end end end diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb index 98e6459b543..a96ee2215c2 100644 --- a/app/controllers/projects/pipelines_controller.rb +++ b/app/controllers/projects/pipelines_controller.rb @@ -22,7 +22,6 @@ class Projects::PipelinesController < Projects::ApplicationController before_action :authorize_update_pipeline!, only: [:retry, :cancel] before_action :ensure_pipeline, only: [:show, :downloadable_artifacts] before_action :reject_if_build_artifacts_size_refreshing!, only: [:destroy] - before_action :push_frontend_feature_flags, only: [:show, :builds, :dag, :failures, :test_report] # Will be removed with https://gitlab.com/gitlab-org/gitlab/-/issues/225596 before_action :redirect_for_legacy_scope_filter, only: [:index], if: -> { request.format.html? } @@ -350,10 +349,6 @@ class Projects::PipelinesController < Projects::ApplicationController def tracking_project_source project end - - def push_frontend_feature_flags - push_frontend_feature_flag(:pipeline_details_header_vue, @project) - end end Projects::PipelinesController.prepend_mod_with('Projects::PipelinesController') diff --git a/app/controllers/projects/runners_controller.rb b/app/controllers/projects/runners_controller.rb index 2b2c2cef8e2..db19ca23e9f 100644 --- a/app/controllers/projects/runners_controller.rb +++ b/app/controllers/projects/runners_controller.rb @@ -12,8 +12,7 @@ class Projects::RunnersController < Projects::ApplicationController redirect_to project_settings_ci_cd_path(@project, anchor: 'js-runners-settings') end - def edit - end + def edit; end def update if Ci::Runners::UpdateRunnerService.new(@runner).execute(runner_params).success? @@ -23,12 +22,10 @@ class Projects::RunnersController < Projects::ApplicationController end end - def new - render_404 unless create_runner_workflow_for_namespace_enabled? - end + def new; end def register - render_404 unless create_runner_workflow_for_namespace_enabled? && runner.registration_available? + render_404 unless runner.registration_available? end def destroy @@ -55,8 +52,7 @@ class Projects::RunnersController < Projects::ApplicationController end end - def show - end + def show; end def toggle_shared_runners update_params = { shared_runners_enabled: !project.shared_runners_enabled } @@ -84,8 +80,4 @@ class Projects::RunnersController < Projects::ApplicationController def runner_params params.require(:runner).permit(Ci::Runner::FORM_EDITABLE) end - - def create_runner_workflow_for_namespace_enabled? - Feature.enabled?(:create_runner_workflow_for_namespace, project.namespace) - end end diff --git a/app/controllers/projects/service_desk/custom_email_controller.rb b/app/controllers/projects/service_desk/custom_email_controller.rb new file mode 100644 index 00000000000..fb5e87f9a97 --- /dev/null +++ b/app/controllers/projects/service_desk/custom_email_controller.rb @@ -0,0 +1,84 @@ +# frozen_string_literal: true + +module Projects + module ServiceDesk + class CustomEmailController < Projects::ApplicationController + before_action :check_feature_flag_enabled + before_action :authorize_admin_project! + + feature_category :service_desk + urgency :low + + def create + response = ::ServiceDesk::CustomEmails::CreateService.new( + project: project, + current_user: current_user, + params: params + ).execute + + json_response(service_response: response) + end + + def update + response = ServiceDeskSettings::UpdateService.new(project, current_user, update_setting_params).execute + + if response.error? + json_response( + error_message: s_("ServiceDesk|Cannot update custom email"), + status: :unprocessable_entity + ) + return + end + + json_response + end + + def destroy + response = ::ServiceDesk::CustomEmails::DestroyService.new( + project: project, + current_user: current_user + ).execute + + json_response(service_response: response) + end + + def show + json_response + end + + private + + def update_setting_params + params.permit(:custom_email_enabled) + end + + def json_response(error_message: nil, status: :ok, service_response: nil) + if service_response.present? + status = service_response.success? ? :ok : :unprocessable_entity + error_message = service_response.message + end + + respond_to do |format| + format.json { render json: custom_email_attributes(error_message: error_message), status: status } + end + end + + def custom_email_attributes(error_message:) + setting = project.service_desk_setting + + { + custom_email: setting&.custom_email, + custom_email_enabled: setting&.custom_email_enabled || false, + custom_email_verification_state: setting&.custom_email_verification&.state, + custom_email_verification_error: setting&.custom_email_verification&.error, + custom_email_smtp_address: setting&.custom_email_credential&.smtp_address, + error_message: error_message + } + end + + def check_feature_flag_enabled + render_404 unless Feature.enabled?(:service_desk_custom_email, @project) + end + end + end +end diff --git a/app/controllers/projects/service_desk_controller.rb b/app/controllers/projects/service_desk_controller.rb index 8f576b8d72b..b1e30e7a45b 100644 --- a/app/controllers/projects/service_desk_controller.rb +++ b/app/controllers/projects/service_desk_controller.rb @@ -13,12 +13,12 @@ class Projects::ServiceDeskController < Projects::ApplicationController def update Projects::UpdateService.new(project, current_user, { service_desk_enabled: params[:service_desk_enabled] }).execute - result = ServiceDeskSettings::UpdateService.new(project, current_user, setting_params).execute + response = ServiceDeskSettings::UpdateService.new(project, current_user, setting_params).execute - if result[:status] == :success + if response.success? json_response else - render json: { message: result[:message] }, status: :unprocessable_entity + render json: { message: response.message }, status: :unprocessable_entity end end diff --git a/app/controllers/projects/settings/ci_cd_controller.rb b/app/controllers/projects/settings/ci_cd_controller.rb index ce760051f79..0e892ef3faa 100644 --- a/app/controllers/projects/settings/ci_cd_controller.rb +++ b/app/controllers/projects/settings/ci_cd_controller.rb @@ -14,10 +14,6 @@ module Projects before_action do push_frontend_feature_flag(:ci_variables_pages, current_user) - push_frontend_feature_flag(:ci_limit_environment_scope, @project) - push_frontend_feature_flag(:create_runner_workflow_for_namespace, @project.namespace) - push_frontend_feature_flag(:frozen_outbound_job_token_scopes, @project) - push_frontend_feature_flag(:frozen_outbound_job_token_scopes_override, @project) end helper_method :highlight_badge diff --git a/app/controllers/projects/tracing_controller.rb b/app/controllers/projects/tracing_controller.rb new file mode 100644 index 00000000000..d1218ebf344 --- /dev/null +++ b/app/controllers/projects/tracing_controller.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Projects + class TracingController < Projects::ApplicationController + include ::Observability::ContentSecurityPolicy + + feature_category :tracing + + before_action :check_tracing_enabled + + def index; end + + private + + def check_tracing_enabled + render_404 unless Gitlab::Observability.tracing_enabled?(project) + end + end +end diff --git a/app/controllers/projects/tree_controller.rb b/app/controllers/projects/tree_controller.rb index c8f698d6193..b961339111b 100644 --- a/app/controllers/projects/tree_controller.rb +++ b/app/controllers/projects/tree_controller.rb @@ -12,12 +12,14 @@ class Projects::TreeController < Projects::ApplicationController before_action :require_non_empty_project, except: [:new, :create] before_action :assign_ref_vars + before_action :find_requested_ref, only: [:show] before_action :assign_dir_vars, only: [:create_dir] before_action :authorize_read_code! before_action :authorize_edit_tree!, only: [:create_dir] before_action do push_frontend_feature_flag(:highlight_js, @project) + push_frontend_feature_flag(:highlight_js_worker, @project) push_frontend_feature_flag(:explain_code_chat, current_user) push_licensed_feature(:file_locks) if @project.licensed_feature_available?(:file_locks) end @@ -28,18 +30,20 @@ class Projects::TreeController < Projects::ApplicationController def show return render_404 unless @commit - @ref_type = ref_type - if @ref_type == BRANCH_REF_TYPE && ambiguous_ref?(@project, @ref) - branch = @project.repository.find_branch(@ref) - if branch - redirect_to project_tree_path(@project, branch.target) - return + unless Feature.enabled?(:redirect_with_ref_type, @project) + @ref_type = ref_type + if @ref_type == BRANCH_REF_TYPE && ambiguous_ref?(@project, @ref) + branch = @project.repository.find_branch(@ref) + if branch + redirect_to project_tree_path(@project, branch.target) + return + end end end if tree.entries.empty? if @repository.blob_at(@commit.id, @path) - redirect_to project_blob_path(@project, File.join(@ref, @path)) + redirect_to project_blob_path(@project, File.join(@ref, @path), ref_type: @ref_type) elsif @path.present? redirect_to_tree_root_for_missing_path(@project, @ref, @path) end @@ -59,6 +63,23 @@ class Projects::TreeController < Projects::ApplicationController private + def find_requested_ref + return unless Feature.enabled?(:redirect_with_ref_type, @project) + + @ref_type = ref_type + if @ref_type.present? + @tree = @repo.tree(@ref, @path, ref_type: @ref_type) + else + response = ExtractsPath::RequestedRef.new(@repository, ref_type: nil, ref: @ref).find + @ref_type = response[:ref_type] + @commit = response[:commit] + + if response[:ambiguous] + redirect_to(project_tree_path(@project, File.join(@ref_type ? @ref : @commit.id, @path), ref_type: @ref_type)) + end + end + end + def redirect_renamed_default_branch? action_name == 'show' end diff --git a/app/controllers/projects/uploads_controller.rb b/app/controllers/projects/uploads_controller.rb index 8f4987a07f6..48399e17b25 100644 --- a/app/controllers/projects/uploads_controller.rb +++ b/app/controllers/projects/uploads_controller.rb @@ -11,7 +11,7 @@ class Projects::UploadsController < Projects::ApplicationController before_action :authorize_upload_file!, only: [:create, :authorize] before_action :verify_workhorse_api!, only: [:authorize] - feature_category :not_owned # rubocop:todo Gitlab/AvoidFeatureCategoryNotOwned + feature_category :team_planning private |