diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-08-02 00:10:20 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-08-02 00:10:20 +0300 |
commit | cc77bdd6f5f12bea0f50064f3feadcd9c87009a6 (patch) | |
tree | b9347c56663684eaa5939984c9a604dc767f214e /app | |
parent | 9cf7b70ac7b17ea3310ebdf83e94d1d5fd248b82 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
20 files changed, 202 insertions, 61 deletions
diff --git a/app/assets/javascripts/commit/pipelines/pipelines_table_wrapper.vue b/app/assets/javascripts/commit/pipelines/pipelines_table_wrapper.vue new file mode 100644 index 00000000000..e69de29bb2d --- /dev/null +++ b/app/assets/javascripts/commit/pipelines/pipelines_table_wrapper.vue diff --git a/app/assets/javascripts/diffs/store/mutations.js b/app/assets/javascripts/diffs/store/mutations.js index 402b1a82ca4..3af2d6ee6b1 100644 --- a/app/assets/javascripts/diffs/store/mutations.js +++ b/app/assets/javascripts/diffs/store/mutations.js @@ -167,7 +167,7 @@ export default { originalStartLineCode, ...(discussion.line_codes || []), ]; - const fileHash = discussion.diff_file.file_hash; + const fileHash = discussion.diff_file?.file_hash; const lineCheck = (line) => discussionLineCodes.some( (discussionLineCode) => diff --git a/app/assets/javascripts/diffs/utils/diff_file.js b/app/assets/javascripts/diffs/utils/diff_file.js index f2a3224d332..98e1c1cc849 100644 --- a/app/assets/javascripts/diffs/utils/diff_file.js +++ b/app/assets/javascripts/diffs/utils/diff_file.js @@ -77,7 +77,7 @@ export function prepareRawDiffFile({ file, allFiles, meta = false, index = -1 }) } export function collapsedType(file) { - const isManual = typeof file.viewer?.manuallyCollapsed === 'boolean'; + const isManual = typeof file?.viewer?.manuallyCollapsed === 'boolean'; return isManual ? DIFF_FILE_MANUAL_COLLAPSE : DIFF_FILE_AUTOMATIC_COLLAPSE; } @@ -85,8 +85,8 @@ export function collapsedType(file) { export function isCollapsed(file) { const type = collapsedType(file); const collapsedStates = { - [DIFF_FILE_AUTOMATIC_COLLAPSE]: file.viewer?.automaticallyCollapsed || false, - [DIFF_FILE_MANUAL_COLLAPSE]: file.viewer?.manuallyCollapsed, + [DIFF_FILE_AUTOMATIC_COLLAPSE]: file?.viewer?.automaticallyCollapsed || false, + [DIFF_FILE_MANUAL_COLLAPSE]: file?.viewer?.manuallyCollapsed, }; return collapsedStates[type]; diff --git a/app/assets/javascripts/notes/components/diff_with_note.vue b/app/assets/javascripts/notes/components/diff_with_note.vue index db32079e6b9..b1a2ab77fa8 100644 --- a/app/assets/javascripts/notes/components/diff_with_note.vue +++ b/app/assets/javascripts/notes/components/diff_with_note.vue @@ -41,7 +41,7 @@ export default { return getDiffMode(this.discussion.diff_file); }, diffViewerMode() { - return this.discussion.diff_file.viewer.name; + return this.discussion.diff_file?.viewer.name; }, fileDiffRefs() { return this.discussion.diff_file.diff_refs; @@ -96,6 +96,7 @@ export default { <template> <div :class="{ 'text-file': isTextFile }" class="diff-file file-holder"> <diff-file-header + v-if="discussion.diff_file" :discussion-path="discussion.discussion_path" :diff-file="discussion.diff_file" :can-current-user-fork="false" diff --git a/app/assets/javascripts/notes/components/noteable_discussion.vue b/app/assets/javascripts/notes/components/noteable_discussion.vue index a5939e1023c..7e79edfea15 100644 --- a/app/assets/javascripts/notes/components/noteable_discussion.vue +++ b/app/assets/javascripts/notes/components/noteable_discussion.vue @@ -169,7 +169,7 @@ export default { return !this.discussionResolved ? this.discussion.resolve_with_issue_path : ''; }, canShowReplyActions() { - if (this.shouldRenderDiffs && !this.discussion.diff_file.diff_refs) { + if (this.shouldRenderDiffs && !this.discussion.diff_file?.diff_refs) { return false; } diff --git a/app/controllers/projects/pipeline_schedules_controller.rb b/app/controllers/projects/pipeline_schedules_controller.rb index 7d69f3459fb..42b6d83ee85 100644 --- a/app/controllers/projects/pipeline_schedules_controller.rb +++ b/app/controllers/projects/pipeline_schedules_controller.rb @@ -21,7 +21,6 @@ class Projects::PipelineSchedulesController < Projects::ApplicationController end def new - @schedule = project.pipeline_schedules.new end def create @@ -101,6 +100,15 @@ class Projects::PipelineSchedulesController < Projects::ApplicationController variables_attributes: [:id, :variable_type, :key, :secret_value, :_destroy]) end + def new_schedule + # We need the `ref` here for `authorize_create_pipeline_schedule!` + @schedule ||= project.pipeline_schedules.new(ref: params.dig(:schedule, :ref)) + end + + def authorize_create_pipeline_schedule! + return access_denied! unless can?(current_user, :create_pipeline_schedule, new_schedule) + end + def authorize_play_pipeline_schedule! return access_denied! unless can?(current_user, :play_pipeline_schedule, schedule) end diff --git a/app/finders/work_items/namespace_work_items_finder.rb b/app/finders/work_items/namespace_work_items_finder.rb new file mode 100644 index 00000000000..9a59cf836b5 --- /dev/null +++ b/app/finders/work_items/namespace_work_items_finder.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module WorkItems + class NamespaceWorkItemsFinder < WorkItemsFinder + def execute + items = init_collection + + sort(items) + end + + override :with_confidentiality_access_check + def with_confidentiality_access_check + return klass.none unless parent && current_user&.can?("read_#{parent.to_ability_name}".to_sym, parent) + return model_class.all if params.user_can_see_all_issuables? + + # Only admins can see hidden issues, so for non-admins, we filter out any hidden issues + issues = model_class.without_hidden.in_namespaces(parent) + + return issues.all if params.user_can_see_all_confidential_issues? + + return issues.public_only if params.user_cannot_see_confidential_issues? + + issues.with_confidentiality_check(current_user) + end + end +end diff --git a/app/graphql/resolvers/concerns/work_items/look_ahead_preloads.rb b/app/graphql/resolvers/concerns/work_items/look_ahead_preloads.rb new file mode 100644 index 00000000000..92fb9ec5cef --- /dev/null +++ b/app/graphql/resolvers/concerns/work_items/look_ahead_preloads.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +module WorkItems + module LookAheadPreloads + extend ActiveSupport::Concern + + prepended do + include ::LooksAhead + end + + private + + def preloads + { + work_item_type: :work_item_type, + web_url: { namespace: :route, project: [:project_namespace, { namespace: :route }] }, + widgets: { work_item_type: :enabled_widget_definitions } + } + end + + def nested_preloads + { + widgets: widget_preloads, + user_permissions: { update_work_item: :assignees }, + project: { jira_import_status: { project: :jira_imports } }, + author: { + location: { author: :user_detail }, + gitpod_enabled: { author: :user_preference } + } + } + end + + def widget_preloads + { + last_edited_by: :last_edited_by, + assignees: :assignees, + parent: :work_item_parent, + children: { work_item_children_by_relative_position: [:author, { project: :project_feature }] }, + labels: :labels, + milestone: { milestone: [:project, :group] }, + subscribed: [:assignees, :award_emoji, { notes: [:author, :award_emoji] }], + award_emoji: { award_emoji: :awardable } + } + end + + def unconditional_includes + [ + { + project: [:project_feature, :group] + }, + :author + ] + end + end +end + +WorkItems::LookAheadPreloads.prepend_mod diff --git a/app/graphql/resolvers/namespaces/work_items_resolver.rb b/app/graphql/resolvers/namespaces/work_items_resolver.rb new file mode 100644 index 00000000000..c4a2d3886e6 --- /dev/null +++ b/app/graphql/resolvers/namespaces/work_items_resolver.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +module Resolvers + module Namespaces + class WorkItemsResolver < BaseResolver + prepend ::WorkItems::LookAheadPreloads + + type Types::WorkItemType.connection_type, null: true + + def resolve_with_lookahead(**args) + return unless Feature.enabled?(:namespace_level_work_items) + return WorkItem.none if resource_parent.nil? + + finder = ::WorkItems::NamespaceWorkItemsFinder.new(current_user, args) + + Gitlab::Graphql::Loaders::IssuableLoader.new(resource_parent, finder).batching_find_all do |q| + apply_lookahead(q) + end + end + + private + + def resource_parent + # The project could have been loaded in batch by `BatchLoader`. + # At this point we need the `id` of the project to query for work items, so + # make sure it's loaded and not `nil` before continuing. + object.respond_to?(:sync) ? object.sync : object + end + strong_memoize_attr :resource_parent + end + end +end diff --git a/app/graphql/resolvers/work_items_resolver.rb b/app/graphql/resolvers/work_items_resolver.rb index 14eec4f696a..d4f73361e05 100644 --- a/app/graphql/resolvers/work_items_resolver.rb +++ b/app/graphql/resolvers/work_items_resolver.rb @@ -2,8 +2,8 @@ module Resolvers class WorkItemsResolver < BaseResolver + prepend ::WorkItems::LookAheadPreloads include SearchArguments - include LooksAhead include ::WorkItems::SharedFilterArguments argument :iid, @@ -28,48 +28,6 @@ module Resolvers private - def preloads - { - work_item_type: :work_item_type, - web_url: { namespace: :route, project: [:project_namespace, { namespace: :route }] }, - widgets: { work_item_type: :enabled_widget_definitions } - } - end - - def nested_preloads - { - widgets: widget_preloads, - user_permissions: { update_work_item: :assignees }, - project: { jira_import_status: { project: :jira_imports } }, - author: { - location: { author: :user_detail }, - gitpod_enabled: { author: :user_preference } - } - } - end - - def widget_preloads - { - last_edited_by: :last_edited_by, - assignees: :assignees, - parent: :work_item_parent, - children: { work_item_children_by_relative_position: [:author, { project: :project_feature }] }, - labels: :labels, - milestone: { milestone: [:project, :group] }, - subscribed: [:assignees, :award_emoji, { notes: [:author, :award_emoji] }], - award_emoji: { award_emoji: :awardable } - } - end - - def unconditional_includes - [ - { - project: [:project_feature, :group] - }, - :author - ] - end - def prepare_finder_params(args) params = super(args) params[:iids] ||= [params.delete(:iid)].compact if params[:iid] @@ -88,4 +46,4 @@ module Resolvers end end -Resolvers::WorkItemsResolver.prepend_mod_with('Resolvers::WorkItemsResolver') +Resolvers::WorkItemsResolver.prepend_mod diff --git a/app/graphql/types/group_type.rb b/app/graphql/types/group_type.rb index 7b8192dd5df..e031958c799 100644 --- a/app/graphql/types/group_type.rb +++ b/app/graphql/types/group_type.rb @@ -262,6 +262,12 @@ module Types resolver: Resolvers::DataTransfer::GroupDataTransferResolver, description: 'Data transfer data point for a specific period. This is mocked data under a development feature flag.' + field :work_items, + null: true, + description: 'Work items that belong to the namespace.', + alpha: { milestone: '16.3' }, + resolver: ::Resolvers::Namespaces::WorkItemsResolver + def label(title:) BatchLoader::GraphQL.for(title).batch(key: group) do |titles, loader, args| LabelsFinder diff --git a/app/models/concerns/milestoneable.rb b/app/models/concerns/milestoneable.rb index e95a8a42aa6..b72d99d211c 100644 --- a/app/models/concerns/milestoneable.rb +++ b/app/models/concerns/milestoneable.rb @@ -52,7 +52,9 @@ module Milestoneable def milestone_available? return true if milestone_id.blank? - project_id == milestone&.project_id || project.ancestors_upto.compact.include?(milestone&.group) + (project_id.present? && project_id == milestone&.project_id) || + try(:namespace)&.self_and_ancestors&.include?(milestone&.group) || + project&.ancestors_upto&.compact&.include?(milestone&.group) end ## diff --git a/app/models/project.rb b/app/models/project.rb index 637f4001501..1f68daeef09 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -585,6 +585,8 @@ class Project < ApplicationRecord validates :max_artifacts_size, numericality: { only_integer: true, greater_than: 0, allow_nil: true } validates :suggestion_commit_message, length: { maximum: MAX_SUGGESTIONS_TEMPLATE_LENGTH } + validate :path_availability, if: :path_changed? + # Scopes scope :pending_delete, -> { where(pending_delete: true) } scope :without_deleted, -> { where(pending_delete: false) } @@ -3230,6 +3232,15 @@ class Project < ApplicationRecord group.crm_enabled? end + def path_availability + base, _, host = path.partition('.') + + return unless host == Gitlab.config.pages&.dig('host') + return unless ProjectSetting.where(pages_unique_domain: base).exists? + + errors.add(:path, s_('Project|already in use')) + end + private # overridden in EE diff --git a/app/models/project_setting.rb b/app/models/project_setting.rb index e8573d5c874..fec951eb7fe 100644 --- a/app/models/project_setting.rb +++ b/app/models/project_setting.rb @@ -59,6 +59,8 @@ class ProjectSetting < ApplicationRecord validate :validates_mr_default_target_self + validate :pages_unique_domain_availability, if: :pages_unique_domain_changed? + attribute :legacy_open_source_license_available, default: -> do Feature.enabled?(:legacy_open_source_license_available, type: :ops) end @@ -114,6 +116,15 @@ class ProjectSetting < ApplicationRecord pages_unique_domain_enabled || pages_unique_domain_in_database.present? end + + def pages_unique_domain_availability + host = Gitlab.config.pages&.dig('host') + + return if host.blank? + return unless Project.where(path: "#{pages_unique_domain}.#{host}").exists? + + errors.add(:pages_unique_domain, s_('ProjectSetting|already in use')) + end end ProjectSetting.prepend_mod diff --git a/app/models/work_item.rb b/app/models/work_item.rb index adf424a1d94..216f2ca81e8 100644 --- a/app/models/work_item.rb +++ b/app/models/work_item.rb @@ -22,6 +22,18 @@ class WorkItem < Issue foreign_key: :work_item_id, source: :work_item scope :inc_relations_for_permission_check, -> { includes(:author, project: :project_feature) } + scope :in_namespaces, ->(namespaces) { where(namespace: namespaces) } + + scope :with_confidentiality_check, ->(user) { + confidential_query = <<~SQL + issues.confidential = FALSE + OR (issues.confidential = TRUE + AND (issues.author_id = :user_id + OR EXISTS (SELECT TRUE FROM issue_assignees WHERE user_id = :user_id AND issue_id = issues.id))) + SQL + + where(confidential_query, user_id: user.id) + } class << self def assignee_association_name diff --git a/app/policies/ci/pipeline_schedule_policy.rb b/app/policies/ci/pipeline_schedule_policy.rb index 7b0d484f9f7..cbc60c4a30a 100644 --- a/app/policies/ci/pipeline_schedule_policy.rb +++ b/app/policies/ci/pipeline_schedule_policy.rb @@ -5,7 +5,18 @@ module Ci alias_method :pipeline_schedule, :subject condition(:protected_ref) do - ref_protected?(@user, @subject.project, @subject.project.repository.tag_exists?(@subject.ref), @subject.ref) + if full_ref?(@subject.ref) + is_tag = Gitlab::Git.tag_ref?(@subject.ref) + ref_name = Gitlab::Git.ref_name(@subject.ref) + else + # NOTE: this block should not be removed + # until the full ref validation is in place + # and all old refs are updated and validated + is_tag = @subject.project.repository.tag_exists?(@subject.ref) + ref_name = @subject.ref + end + + ref_protected?(@user, @subject.project, is_tag, ref_name) end condition(:owner_of_schedule) do @@ -31,6 +42,15 @@ module Ci enable :take_ownership_pipeline_schedule end - rule { protected_ref }.prevent :play_pipeline_schedule + rule { protected_ref }.policy do + prevent :play_pipeline_schedule + prevent :create_pipeline_schedule + end + + private + + def full_ref?(ref) + Gitlab::Git.tag_ref?(ref) || Gitlab::Git.branch_ref?(ref) + end end end diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb index e35312fbd09..f166946db29 100644 --- a/app/policies/group_policy.rb +++ b/app/policies/group_policy.rb @@ -148,6 +148,7 @@ class GroupPolicy < Namespaces::GroupProjectNamespaceSharedPolicy enable :read_group_member enable :read_custom_emoji enable :read_counts + enable :read_issue end rule { achievements_enabled }.policy do diff --git a/app/services/bulk_imports/archive_extraction_service.rb b/app/services/bulk_imports/archive_extraction_service.rb index 4485b19035b..bce2a67218a 100644 --- a/app/services/bulk_imports/archive_extraction_service.rb +++ b/app/services/bulk_imports/archive_extraction_service.rb @@ -49,11 +49,7 @@ module BulkImports end def validate_symlink - raise(BulkImports::Error, 'Invalid file') if symlink?(filepath) - end - - def symlink?(filepath) - File.lstat(filepath).symlink? + raise(BulkImports::Error, 'Invalid file') if Gitlab::Utils::FileInfo.linked?(filepath) end def extract_archive diff --git a/app/services/bulk_imports/file_decompression_service.rb b/app/services/bulk_imports/file_decompression_service.rb index 94573f6bb13..77638f10f54 100644 --- a/app/services/bulk_imports/file_decompression_service.rb +++ b/app/services/bulk_imports/file_decompression_service.rb @@ -53,7 +53,7 @@ module BulkImports end def validate_symlink(filepath) - raise(ServiceError, 'Invalid file') if File.lstat(filepath).symlink? + raise(ServiceError, 'Invalid file') if Gitlab::Utils::FileInfo.linked?(filepath) end def decompress_file diff --git a/app/services/discussions/capture_diff_note_positions_service.rb b/app/services/discussions/capture_diff_note_positions_service.rb index 3684a3f679a..f9b31e0f2f1 100644 --- a/app/services/discussions/capture_diff_note_positions_service.rb +++ b/app/services/discussions/capture_diff_note_positions_service.rb @@ -26,7 +26,7 @@ module Discussions active_diff_discussions = merge_request.notes.new_diff_notes.discussions.select do |discussion| discussion.active?(merge_request.diff_refs) end - paths = active_diff_discussions.flat_map { |n| n.diff_file.paths } + paths = active_diff_discussions.flat_map { |n| n.diff_file&.paths } [active_diff_discussions, paths] end |