Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
		
							parent
							
								
									2494b608a4
								
							
						
					
					
						commit
						aa542224bb
					
				|  | @ -1,5 +1,12 @@ | |||
| Please view this file on the master branch, on stable branches it's out of date. | ||||
| 
 | ||||
| ## 12.4.2 | ||||
| 
 | ||||
| ### Fixed (1 change) | ||||
| 
 | ||||
| - Fix feature flag check for productivity analytics. !19025 | ||||
| 
 | ||||
| 
 | ||||
| ## 12.4.1 | ||||
| 
 | ||||
| ### Security (6 changes) | ||||
|  |  | |||
|  | @ -1,4 +1,4 @@ | |||
| /* eslint-disable func-names, consistent-return, camelcase, class-methods-use-this */ | ||||
| /* eslint-disable consistent-return, camelcase, class-methods-use-this */ | ||||
| 
 | ||||
| // Zen Mode (full screen) textarea
 | ||||
| //
 | ||||
|  | @ -47,26 +47,16 @@ export default class ZenMode { | |||
|       e.preventDefault(); | ||||
|       return $(e.currentTarget).trigger('zen_mode:leave'); | ||||
|     }); | ||||
|     $(document).on( | ||||
|       'zen_mode:enter', | ||||
|       (function(_this) { | ||||
|         return function(e) { | ||||
|           return _this.enter( | ||||
|     $(document).on('zen_mode:enter', e => { | ||||
|       this.enter( | ||||
|         $(e.target) | ||||
|           .closest('.md-area') | ||||
|           .find('.zen-backdrop'), | ||||
|       ); | ||||
|         }; | ||||
|       })(this), | ||||
|     ); | ||||
|     $(document).on( | ||||
|       'zen_mode:leave', | ||||
|       (function(_this) { | ||||
|         return function() { | ||||
|           return _this.exit(); | ||||
|         }; | ||||
|       })(this), | ||||
|     ); | ||||
|     }); | ||||
|     $(document).on('zen_mode:leave', () => { | ||||
|       this.exit(); | ||||
|     }); | ||||
|     $(document).on('keydown', e => { | ||||
|       // Esc
 | ||||
|       if (e.keyCode === 27) { | ||||
|  |  | |||
|  | @ -99,3 +99,13 @@ | |||
|     color: $gl-text-color-disabled; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .group-variable-list { | ||||
|   color: $gray-700; | ||||
| 
 | ||||
|   .table-section:not(:first-child) { | ||||
|     @include media-breakpoint-down(sm) { | ||||
|       border-top: hidden; | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  |  | |||
|  | @ -30,7 +30,8 @@ $status-box-line-height: 26px; | |||
|       margin-bottom: $gl-padding-4; | ||||
|     } | ||||
| 
 | ||||
|     .milestone-progress { | ||||
|     .milestone-progress, | ||||
|     .milestone-release-links { | ||||
|       a { | ||||
|         color: $blue-600; | ||||
|       } | ||||
|  | @ -238,10 +239,6 @@ $status-box-line-height: 26px; | |||
|   } | ||||
| } | ||||
| 
 | ||||
| .milestone-range { | ||||
|   color: $gl-text-color-tertiary; | ||||
| } | ||||
| 
 | ||||
| @include media-breakpoint-down(xs) { | ||||
|   .milestone-banner-text, | ||||
|   .milestone-banner-link { | ||||
|  |  | |||
|  | @ -1,34 +1,38 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| class ContainerRepositoriesFinder | ||||
|   # id: group or project id | ||||
|   # container_type: :group or :project | ||||
|   def initialize(id:, container_type:) | ||||
|     @id = id | ||||
|     @type = container_type.to_sym | ||||
|   VALID_SUBJECTS = [Group, Project].freeze | ||||
| 
 | ||||
|   def initialize(user:, subject:) | ||||
|     @user = user | ||||
|     @subject = subject | ||||
|   end | ||||
| 
 | ||||
|   def execute | ||||
|     if project_type? | ||||
|       project.container_repositories | ||||
|     else | ||||
|       group.container_repositories | ||||
|     end | ||||
|     raise ArgumentError, "invalid subject_type" unless valid_subject_type? | ||||
|     return unless authorized? | ||||
| 
 | ||||
|     return project_repositories if @subject.is_a?(Project) | ||||
|     return group_repositories if @subject.is_a?(Group) | ||||
|   end | ||||
| 
 | ||||
|   private | ||||
| 
 | ||||
|   attr_reader :id, :type | ||||
| 
 | ||||
|   def project_type? | ||||
|     type == :project | ||||
|   def valid_subject_type? | ||||
|     VALID_SUBJECTS.include?(@subject.class) | ||||
|   end | ||||
| 
 | ||||
|   def project | ||||
|     Project.find(id) | ||||
|   def project_repositories | ||||
|     return unless @subject.container_registry_enabled | ||||
| 
 | ||||
|     @subject.container_repositories | ||||
|   end | ||||
| 
 | ||||
|   def group | ||||
|     Group.find(id) | ||||
|   def group_repositories | ||||
|     ContainerRepository.for_group_and_its_subgroups(@subject) | ||||
|   end | ||||
| 
 | ||||
|   def authorized? | ||||
|     Ability.allowed?(@user, :read_container_image, @subject) | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -170,6 +170,15 @@ module MilestonesHelper | |||
|     content.join('<br />').html_safe | ||||
|   end | ||||
| 
 | ||||
|   def recent_releases_with_counts(milestone) | ||||
|     total_count = milestone.releases.size | ||||
|     return [[], 0, 0] if total_count == 0 | ||||
| 
 | ||||
|     recent_releases = milestone.releases.recent.to_a | ||||
|     more_count = total_count - recent_releases.size | ||||
|     [recent_releases, total_count, more_count] | ||||
|   end | ||||
| 
 | ||||
|   def milestone_tooltip_due_date(milestone) | ||||
|     if milestone.due_date | ||||
|       "#{milestone.due_date.to_s(:medium)} (#{remaining_days_in_words(milestone.due_date, milestone.start_date)})" | ||||
|  |  | |||
|  | @ -12,6 +12,9 @@ class ContainerRepository < ApplicationRecord | |||
| 
 | ||||
|   scope :ordered, -> { order(:name) } | ||||
|   scope :with_api_entity_associations, -> { preload(project: [:route, { namespace: :route }]) } | ||||
|   scope :for_group_and_its_subgroups, ->(group) do | ||||
|     where(project_id: Project.for_group_and_its_subgroups(group).with_container_registry.select(:id)) | ||||
|   end | ||||
| 
 | ||||
|   # rubocop: disable CodeReuse/ServiceClass | ||||
|   def registry | ||||
|  |  | |||
|  | @ -11,7 +11,7 @@ class GlobalMilestone | |||
| 
 | ||||
|   delegate :title, :state, :due_date, :start_date, :participants, :project, | ||||
|            :group, :expires_at, :closed?, :iid, :group_milestone?, :safe_title, | ||||
|            :milestoneish_id, :resource_parent, to: :milestone | ||||
|            :milestoneish_id, :resource_parent, :releases, to: :milestone | ||||
| 
 | ||||
|   def to_hash | ||||
|     { | ||||
|  |  | |||
|  | @ -395,6 +395,7 @@ class Project < ApplicationRecord | |||
|   scope :with_project_feature, -> { joins('LEFT JOIN project_features ON projects.id = project_features.project_id') } | ||||
|   scope :with_statistics, -> { includes(:statistics) } | ||||
|   scope :with_shared_runners, -> { where(shared_runners_enabled: true) } | ||||
|   scope :with_container_registry, -> { where(container_registry_enabled: true) } | ||||
|   scope :inside_path, ->(path) do | ||||
|     # We need routes alias rs for JOIN so it does not conflict with | ||||
|     # includes(:route) which we use in ProjectsFinder. | ||||
|  |  | |||
|  | @ -28,12 +28,16 @@ class Release < ApplicationRecord | |||
| 
 | ||||
|   scope :sorted, -> { order(released_at: :desc) } | ||||
|   scope :preloaded, -> { includes(project: :namespace) } | ||||
|   scope :with_project_and_namespace, -> { includes(project: :namespace) } | ||||
|   scope :recent, -> { sorted.limit(MAX_NUMBER_TO_DISPLAY) } | ||||
| 
 | ||||
|   delegate :repository, to: :project | ||||
| 
 | ||||
|   after_commit :create_evidence!, on: :create | ||||
|   after_commit :notify_new_release, on: :create | ||||
| 
 | ||||
|   MAX_NUMBER_TO_DISPLAY = 3 | ||||
| 
 | ||||
|   def to_param | ||||
|     CGI.escape(tag) | ||||
|   end | ||||
|  |  | |||
|  | @ -0,0 +1 @@ | |||
| = _("These variables are configured in the parent group settings, and will be active in the current project in addition to the project variables.") | ||||
|  | @ -0,0 +1,5 @@ | |||
| %h5 | ||||
|   = _('Group variables (inherited)') | ||||
| 
 | ||||
| %p | ||||
|   = render "ci/group_variables/content" | ||||
|  | @ -0,0 +1,13 @@ | |||
| - variables = @project.group.self_and_ancestors.map(&:variables).flatten | ||||
| 
 | ||||
| .row | ||||
|   .col-lg-12 | ||||
|     .group-variable-list | ||||
|       = render 'ci/group_variables/variable_header' | ||||
|       - variables.each do |variable| | ||||
|         .group-variable-row.d-flex.w-100.border-bottom.pt-2.pb-2 | ||||
|           .table-section.section-40.append-right-10.key | ||||
|             = variable.key | ||||
|           .table-section.section-40.append-right-10 | ||||
|             %a.group-origin-link{ href: group_settings_ci_cd_path(variable.group) } | ||||
|               = variable.group.name | ||||
|  | @ -0,0 +1,5 @@ | |||
| .group-variable-keys.d-flex.w-100.align-items-center.pb-2.border-bottom | ||||
|   .bold.table-section.section-40.append-right-10 | ||||
|     = s_('Key') | ||||
|   .bold.table-section.section-40.append-right-10 | ||||
|     = s_('Origin') | ||||
|  | @ -24,3 +24,8 @@ | |||
|           = n_('Hide value', 'Hide values', @variables.size) | ||||
|         - else | ||||
|           = n_('Reveal value', 'Reveal values', @variables.size) | ||||
|     - if !@group && @project.group | ||||
|       .settings-header.border-top.prepend-top-20 | ||||
|         = render 'ci/group_variables/header' | ||||
|       .settings-content.pr-0 | ||||
|         = render 'ci/group_variables/index' | ||||
|  |  | |||
|  | @ -12,8 +12,20 @@ | |||
| 
 | ||||
|       - if @project || milestone.is_a?(GlobalMilestone) || milestone.group_milestone? | ||||
|         - if milestone.due_date || milestone.start_date | ||||
|           .milestone-range.append-bottom-5 | ||||
|           .text-tertiary.append-bottom-5 | ||||
|             = milestone_date_range(milestone) | ||||
|         - recent_releases, total_count, more_count = recent_releases_with_counts(milestone) | ||||
|         - unless total_count.zero? | ||||
|           .text-tertiary.append-bottom-5.milestone-release-links | ||||
|             = icon('rocket') | ||||
|             = n_('Release', 'Releases', total_count) | ||||
|             - recent_releases.each do |release| | ||||
|               = link_to release.name, project_releases_path(release.project, anchor: release.tag) | ||||
|               - unless release == recent_releases.last | ||||
|                 • | ||||
|             - if total_count > recent_releases.count | ||||
|               • | ||||
|               = link_to n_('%{count} more release', '%{count} more releases', more_count) % { count: more_count }, project_releases_path(milestone.project) | ||||
|         %div | ||||
|           = render('shared/milestone_expired', milestone: milestone) | ||||
|           - if milestone.group_milestone? | ||||
|  |  | |||
|  | @ -0,0 +1,5 @@ | |||
| --- | ||||
| title: Show inherited group variables in project view | ||||
| merge_request: 18759 | ||||
| author: | ||||
| type: added | ||||
|  | @ -0,0 +1,5 @@ | |||
| --- | ||||
| title: Add links to associated releases on the Milestones page | ||||
| merge_request: 16558 | ||||
| author: | ||||
| type: added | ||||
|  | @ -467,6 +467,13 @@ production: &base | |||
|       # enabled: true | ||||
|       # primary_api_url: http://localhost:5000/ # internal address to the primary registry, will be used by GitLab to directly communicate with primary registry API | ||||
| 
 | ||||
|   ## Feature Flag https://docs.gitlab.com/ee/user/project/operations/feature_flags.html | ||||
|   feature_flags: | ||||
|     unleash: | ||||
|       # enabled: false | ||||
|       # url: https://gitlab.com/api/v4/feature_flags/unleash/<project_id> | ||||
|       # app_name: gitlab.com # Environment name of your GitLab instance | ||||
|       # instance_id: INSTANCE_ID | ||||
| 
 | ||||
|   # | ||||
|   # 2. GitLab CI settings | ||||
|  |  | |||
|  | @ -308,6 +308,13 @@ Gitlab.ee do | |||
|   Settings.geo.registry_replication['enabled'] ||= false | ||||
| end | ||||
| 
 | ||||
| # | ||||
| # Unleash | ||||
| # | ||||
| Settings['feature_flags'] ||= Settingslogic.new({}) | ||||
| Settings.feature_flags['unleash'] ||= Settingslogic.new({}) | ||||
| Settings.feature_flags.unleash['enabled'] = false if Settings.feature_flags.unleash['enabled'].nil? | ||||
| 
 | ||||
| # | ||||
| # External merge request diffs | ||||
| # | ||||
|  |  | |||
|  | @ -357,7 +357,12 @@ Group-level variables can be added by: | |||
| 1. Inputing variable types, keys, and values in the **Variables** section. | ||||
|    Any variables of [subgroups](../../user/group/subgroups/index.md) will be inherited recursively. | ||||
| 
 | ||||
| Once you set them, they will be available for all subsequent pipelines. | ||||
| Once you set them, they will be available for all subsequent pipelines. Any group-level user defined variables can be viewed in projects by: | ||||
| 
 | ||||
| 1. Navigating to the project's **Settings > CI/CD** page. | ||||
| 1. Expanding the **Variables** section. | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| ## Priority of environment variables | ||||
| 
 | ||||
|  |  | |||
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 72 KiB | 
|  | @ -23,7 +23,7 @@ module API | |||
|       end | ||||
|       get ':id/registry/repositories' do | ||||
|         repositories = ContainerRepositoriesFinder.new( | ||||
|           id: user_group.id, container_type: :group | ||||
|           user: current_user, subject: user_group | ||||
|         ).execute | ||||
| 
 | ||||
|         present paginate(repositories), with: Entities::ContainerRegistry::Repository, tags: params[:tags] | ||||
|  |  | |||
|  | @ -24,7 +24,7 @@ module API | |||
|       end | ||||
|       get ':id/registry/repositories' do | ||||
|         repositories = ContainerRepositoriesFinder.new( | ||||
|           id: user_project.id, container_type: :project | ||||
|           user: current_user, subject: user_project | ||||
|         ).execute | ||||
| 
 | ||||
|         present paginate(repositories), with: Entities::ContainerRegistry::Repository, tags: params[:tags] | ||||
|  |  | |||
|  | @ -210,6 +210,11 @@ msgstr "" | |||
| msgid "%{count} more assignees" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "%{count} more release" | ||||
| msgid_plural "%{count} more releases" | ||||
| msgstr[0] "" | ||||
| msgstr[1] "" | ||||
| 
 | ||||
| msgid "%{count} of %{required} approvals from %{name}" | ||||
| msgstr "" | ||||
| 
 | ||||
|  | @ -8336,6 +8341,9 @@ msgstr "" | |||
| msgid "Group pipeline minutes were successfully reset." | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "Group variables (inherited)" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "Group was successfully updated." | ||||
| msgstr "" | ||||
| 
 | ||||
|  | @ -9537,6 +9545,9 @@ msgstr "" | |||
| msgid "June" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "Key" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "Key (PEM)" | ||||
| msgstr "" | ||||
| 
 | ||||
|  | @ -11601,6 +11612,9 @@ msgstr "" | |||
| msgid "Or you can choose one of the suggested colors below" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "Origin" | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "Other Labels" | ||||
| msgstr "" | ||||
| 
 | ||||
|  | @ -13794,7 +13808,9 @@ msgid "Related merge requests" | |||
| msgstr "" | ||||
| 
 | ||||
| msgid "Release" | ||||
| msgstr "" | ||||
| msgid_plural "Releases" | ||||
| msgstr[0] "" | ||||
| msgstr[1] "" | ||||
| 
 | ||||
| msgid "Release notes" | ||||
| msgstr "" | ||||
|  | @ -16933,6 +16949,9 @@ msgstr "" | |||
| msgid "These existing issues have a similar title. It might be better to comment there instead of creating another similar issue." | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "These variables are configured in the parent group settings, and will be active in the current project in addition to the project variables." | ||||
| msgstr "" | ||||
| 
 | ||||
| msgid "They can be managed using the %{link}." | ||||
| msgstr "" | ||||
| 
 | ||||
|  |  | |||
|  | @ -34,4 +34,31 @@ describe "User views milestones" do | |||
|         .and have_content(closed_issue.title) | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   context "with associated releases" do | ||||
|     set(:first_release) { create(:release, project: project, name: "The first release", milestones: [milestone], released_at: Time.zone.parse('2019-10-07')) } | ||||
| 
 | ||||
|     context "with a single associated release" do | ||||
|       it "shows the associated release" do | ||||
|         expect(page).to have_content("Release #{first_release.name}") | ||||
|         expect(page).to have_link(first_release.name, href: project_releases_path(project, anchor: first_release.tag)) | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     context "with lots of associated releases" do | ||||
|       set(:second_release) { create(:release, project: project, name: "The second release", milestones: [milestone], released_at: first_release.released_at + 1.day) } | ||||
|       set(:third_release) { create(:release, project: project, name: "The third release", milestones: [milestone], released_at: second_release.released_at + 1.day) } | ||||
|       set(:fourth_release) { create(:release, project: project, name: "The fourth release", milestones: [milestone], released_at: third_release.released_at + 1.day) } | ||||
|       set(:fifth_release) { create(:release, project: project, name: "The fifth release", milestones: [milestone], released_at: fourth_release.released_at + 1.day) } | ||||
| 
 | ||||
|       it "shows the associated releases and the truncation text" do | ||||
|         expect(page).to have_content("Releases #{fifth_release.name} • #{fourth_release.name} • #{third_release.name} • 2 more releases") | ||||
| 
 | ||||
|         expect(page).to have_link(fifth_release.name, href: project_releases_path(project, anchor: fifth_release.tag)) | ||||
|         expect(page).to have_link(fourth_release.name, href: project_releases_path(project, anchor: fourth_release.tag)) | ||||
|         expect(page).to have_link(third_release.name, href: project_releases_path(project, anchor: third_release.tag)) | ||||
|         expect(page).to have_link("2 more releases", href: project_releases_path(project)) | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -0,0 +1,60 @@ | |||
| # frozen_string_literal: true | ||||
| 
 | ||||
| require 'spec_helper' | ||||
| 
 | ||||
| describe 'Project group variables', :js do | ||||
|   let(:user) { create(:user) } | ||||
|   let(:group) { create(:group) } | ||||
|   let(:subgroup) { create(:group, parent: group) } | ||||
|   let(:subgroup_nested) { create(:group, parent: subgroup) } | ||||
|   let(:project) { create(:project, group: group) } | ||||
|   let(:project2) { create(:project, group: subgroup) } | ||||
|   let(:project3) { create(:project, group: subgroup_nested) } | ||||
|   let(:key1) { 'test_key' } | ||||
|   let(:key2) { 'test_key2' } | ||||
|   let(:key3) { 'test_key3' } | ||||
|   let!(:ci_variable) { create(:ci_group_variable, group: group, key: key1) } | ||||
|   let!(:ci_variable2) { create(:ci_group_variable, group: subgroup, key: key2) } | ||||
|   let!(:ci_variable3) { create(:ci_group_variable, group: subgroup_nested, key: key3) } | ||||
|   let(:project_path) { project_settings_ci_cd_path(project) } | ||||
|   let(:project2_path) { project_settings_ci_cd_path(project2) } | ||||
|   let(:project3_path) { project_settings_ci_cd_path(project3) } | ||||
| 
 | ||||
|   before do | ||||
|     sign_in(user) | ||||
|     project.add_maintainer(user) | ||||
|     group.add_owner(user) | ||||
|   end | ||||
| 
 | ||||
|   it 'project in group shows inherited vars from ancestor group' do | ||||
|     visit project_path | ||||
|     expect(page).to have_content(key1) | ||||
|     expect(page).to have_content(group.name) | ||||
|   end | ||||
| 
 | ||||
|   it 'project in subgroup shows inherited vars from all ancestor groups' do | ||||
|     visit project2_path | ||||
|     expect(page).to have_content(key1) | ||||
|     expect(page).to have_content(key2) | ||||
|     expect(page).to have_content(group.name) | ||||
|     expect(page).to have_content(subgroup.name) | ||||
|   end | ||||
| 
 | ||||
|   it 'project in nested subgroup shows inherited vars from all ancestor groups' do | ||||
|     visit project3_path | ||||
|     expect(page).to have_content(key1) | ||||
|     expect(page).to have_content(key2) | ||||
|     expect(page).to have_content(key3) | ||||
|     expect(page).to have_content(group.name) | ||||
|     expect(page).to have_content(subgroup.name) | ||||
|     expect(page).to have_content(subgroup_nested.name) | ||||
|   end | ||||
| 
 | ||||
|   it 'project origin keys link to ancestor groups ci_cd settings' do | ||||
|     visit project_path | ||||
|     find('.group-origin-link').click | ||||
|     page.within('.js-ci-variable-list-section .js-row:nth-child(2)') do | ||||
|       expect(find('.js-ci-variable-input-key').value).to eq(key1) | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  | @ -3,42 +3,50 @@ | |||
| require 'spec_helper' | ||||
| 
 | ||||
| describe ContainerRepositoriesFinder do | ||||
|   let_it_be(:reporter) { create(:user) } | ||||
|   let_it_be(:guest) { create(:user) } | ||||
| 
 | ||||
|   let(:group) { create(:group) } | ||||
|   let(:project) { create(:project, group: group) } | ||||
|   let(:project_repository) { create(:container_repository, project: project) } | ||||
| 
 | ||||
|   before do | ||||
|     group.add_reporter(reporter) | ||||
|     project.add_reporter(reporter) | ||||
|   end | ||||
| 
 | ||||
|   describe '#execute' do | ||||
|     let(:id) { nil } | ||||
|     context 'with authorized user' do | ||||
|       subject { described_class.new(user: reporter, subject: subject_object).execute } | ||||
| 
 | ||||
|     subject { described_class.new(id: id, container_type: container_type).execute } | ||||
| 
 | ||||
|     context 'when container_type is group' do | ||||
|       context 'when subject_type is group' do | ||||
|         let(:subject_object) { group } | ||||
|         let(:other_project) { create(:project, group: group) } | ||||
| 
 | ||||
|         let(:other_repository) do | ||||
|           create(:container_repository, name: 'test_repository2', project: other_project) | ||||
|         end | ||||
| 
 | ||||
|       let(:container_type) { :group } | ||||
|       let(:id) { group.id } | ||||
| 
 | ||||
|         it { is_expected.to match_array([project_repository, other_repository]) } | ||||
|       end | ||||
| 
 | ||||
|     context 'when container_type is project' do | ||||
|       let(:container_type) { :project } | ||||
|       let(:id) { project.id } | ||||
|       context 'when subject_type is project' do | ||||
|         let(:subject_object) { project } | ||||
| 
 | ||||
|         it { is_expected.to match_array([project_repository]) } | ||||
|       end | ||||
| 
 | ||||
|     context 'with invalid id' do | ||||
|       let(:container_type) { :project } | ||||
|       let(:id) { 123456789 } | ||||
|       context 'with invalid subject_type' do | ||||
|         let(:subject_object) { "invalid type" } | ||||
| 
 | ||||
|       it 'raises an error' do | ||||
|         expect { subject.execute }.to raise_error(ActiveRecord::RecordNotFound) | ||||
|         it { expect { subject }.to raise_exception('invalid subject_type') } | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     context 'with unauthorized user' do | ||||
|       subject { described_class.new(user: guest, subject: group).execute } | ||||
| 
 | ||||
|       it { is_expected.to be nil } | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -235,4 +235,36 @@ describe ContainerRepository do | |||
|       expect(repository).not_to be_persisted | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   describe '.for_group_and_its_subgroups' do | ||||
|     subject { described_class.for_group_and_its_subgroups(test_group) } | ||||
| 
 | ||||
|     context 'in a group' do | ||||
|       let(:test_group) { group } | ||||
| 
 | ||||
|       it { is_expected.to contain_exactly(repository) } | ||||
|     end | ||||
| 
 | ||||
|     context 'with a subgroup' do | ||||
|       let(:test_group) { create(:group) } | ||||
|       let(:another_project) { create(:project, path: 'test', group: test_group) } | ||||
| 
 | ||||
|       let(:another_repository) do | ||||
|         create(:container_repository, name: 'my_image', project: another_project) | ||||
|       end | ||||
| 
 | ||||
|       before do | ||||
|         group.parent = test_group | ||||
|         group.save | ||||
|       end | ||||
| 
 | ||||
|       it { is_expected.to contain_exactly(repository, another_repository) } | ||||
|     end | ||||
| 
 | ||||
|     context 'group without container_repositories' do | ||||
|       let(:test_group) { create(:group) } | ||||
| 
 | ||||
|       it { is_expected.to eq([]) } | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -3,10 +3,10 @@ | |||
| require 'spec_helper' | ||||
| 
 | ||||
| describe API::GroupContainerRepositories do | ||||
|   set(:group) { create(:group, :private) } | ||||
|   set(:project) { create(:project, :private, group: group) } | ||||
|   let(:reporter) { create(:user) } | ||||
|   let(:guest) { create(:user) } | ||||
|   let_it_be(:group) { create(:group, :private) } | ||||
|   let_it_be(:project) { create(:project, :private, group: group) } | ||||
|   let_it_be(:reporter) { create(:user) } | ||||
|   let_it_be(:guest) { create(:user) } | ||||
| 
 | ||||
|   let(:root_repository) { create(:container_repository, :root, project: project) } | ||||
|   let(:test_repository) { create(:container_repository, project: project) } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue