diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-05-31 06:09:17 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-05-31 06:09:17 +0000 |
commit | a9f214d42107c847fcc8fb2a5dff78ba17363e41 (patch) | |
tree | 5cffa8c1d0b21df5084c9958eb2b1b8049834fb2 | |
parent | 226c5810c9f0171ba0daceb1cd3d72b43860647a (diff) | |
download | gitlab-ce-a9f214d42107c847fcc8fb2a5dff78ba17363e41.tar.gz |
Add latest changes from gitlab-org/gitlab@master
18 files changed, 198 insertions, 22 deletions
diff --git a/app/assets/javascripts/issues/list/components/issues_list_app.vue b/app/assets/javascripts/issues/list/components/issues_list_app.vue index e5c557ce769..4e5d0c9ac57 100644 --- a/app/assets/javascripts/issues/list/components/issues_list_app.vue +++ b/app/assets/javascripts/issues/list/components/issues_list_app.vue @@ -117,6 +117,7 @@ export default { 'autocompleteAwardEmojisPath', 'calendarPath', 'canBulkUpdate', + 'canCreateProjects', 'canReadCrmContact', 'canReadCrmOrganization', 'emptyStateSvgPath', @@ -136,6 +137,7 @@ export default { 'isSignedIn', 'jiraIntegrationPath', 'newIssuePath', + 'newProjectPath', 'releasesPath', 'rssPath', 'showNewIssueLink', @@ -844,12 +846,17 @@ export default { </issuable-list> <template v-else-if="isSignedIn"> - <gl-empty-state - :description="$options.i18n.noIssuesSignedInDescription" - :title="$options.i18n.noIssuesSignedInTitle" - :svg-path="emptyStateSvgPath" - > + <gl-empty-state :title="$options.i18n.noIssuesSignedInTitle" :svg-path="emptyStateSvgPath"> + <template #description> + <p>{{ $options.i18n.noIssuesSignedInDescription }}</p> + <p v-if="canCreateProjects"> + <strong>{{ $options.i18n.noGroupIssuesSignedInDescription }}</strong> + </p> + </template> <template #actions> + <gl-button v-if="canCreateProjects" :href="newProjectPath" variant="confirm"> + {{ $options.i18n.newProjectLabel }} + </gl-button> <gl-button v-if="showNewIssueLink" :href="newIssuePath" variant="confirm"> {{ $options.i18n.newIssueLabel }} </gl-button> diff --git a/app/assets/javascripts/issues/list/constants.js b/app/assets/javascripts/issues/list/constants.js index c7098d048ad..74f801f685c 100644 --- a/app/assets/javascripts/issues/list/constants.js +++ b/app/assets/javascripts/issues/list/constants.js @@ -29,7 +29,11 @@ export const i18n = { jiraIntegrationSecondaryMessage: s__('JiraService|This feature requires a Premium plan.'), jiraIntegrationTitle: s__('JiraService|Using Jira for issue tracking?'), newIssueLabel: __('New issue'), + newProjectLabel: __('New project'), noClosedIssuesTitle: __('There are no closed issues'), + noGroupIssuesSignedInDescription: __( + 'Issues exist in projects, so to create an issue, first create a project.', + ), noOpenIssuesDescription: __('To keep this project going, create a new issue'), noOpenIssuesTitle: __('There are no open issues'), noIssuesSignedInDescription: __( diff --git a/app/assets/javascripts/issues/list/index.js b/app/assets/javascripts/issues/list/index.js index 7cfd8badf48..c89a6385bc4 100644 --- a/app/assets/javascripts/issues/list/index.js +++ b/app/assets/javascripts/issues/list/index.js @@ -80,6 +80,7 @@ export function mountIssuesListApp() { autocompleteAwardEmojisPath, calendarPath, canBulkUpdate, + canCreateProjects, canEdit, canImportIssues, canReadCrmContact, @@ -109,6 +110,7 @@ export function mountIssuesListApp() { markdownHelpPath, maxAttachmentSize, newIssuePath, + newProjectPath, projectImportJiraPath, quickActionsHelpPath, releasesPath, @@ -133,6 +135,7 @@ export function mountIssuesListApp() { autocompleteAwardEmojisPath, calendarPath, canBulkUpdate: parseBoolean(canBulkUpdate), + canCreateProjects: parseBoolean(canCreateProjects), canReadCrmContact: parseBoolean(canReadCrmContact), canReadCrmOrganization: parseBoolean(canReadCrmOrganization), emptyStateSvgPath, @@ -153,6 +156,7 @@ export function mountIssuesListApp() { isSignedIn: parseBoolean(isSignedIn), jiraIntegrationPath, newIssuePath, + newProjectPath, releasesPath, rssPath, showNewIssueLink: parseBoolean(showNewIssueLink), diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index 98afab554d2..bf0fa2c3e77 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -238,10 +238,12 @@ module IssuesHelper def group_issues_list_data(group, current_user) common_issues_list_data(group, current_user).merge( + can_create_projects: can?(current_user, :create_projects, group).to_s, can_read_crm_contact: can?(current_user, :read_crm_contact, group).to_s, can_read_crm_organization: can?(current_user, :read_crm_organization, group).to_s, has_any_issues: @has_issues.to_s, - has_any_projects: @has_projects.to_s + has_any_projects: @has_projects.to_s, + new_project_path: new_project_path(namespace_id: group.id) ) end diff --git a/app/serializers/diff_file_entity.rb b/app/serializers/diff_file_entity.rb index ef856ee0116..9f8628fe849 100644 --- a/app/serializers/diff_file_entity.rb +++ b/app/serializers/diff_file_entity.rb @@ -56,8 +56,7 @@ class DiffFileEntity < DiffFileBaseEntity # Used for inline diffs expose :highlighted_diff_lines, using: DiffLineEntity, if: -> (diff_file, options) { inline_diff_view?(options) && diff_file.text? } do |diff_file| - file = conflict_file(options, diff_file) || diff_file - file.diff_lines_for_serializer + highlighted_diff_lines_for(diff_file, options) end expose :is_fully_expanded do |diff_file| @@ -89,6 +88,15 @@ class DiffFileEntity < DiffFileBaseEntity # If nothing is present, inline will be the default. options.fetch(:diff_view, :inline).to_sym end + + def highlighted_diff_lines_for(diff_file, options) + file = conflict_file(options, diff_file) || diff_file + + file.diff_lines_for_serializer + rescue Gitlab::Git::Conflict::Parser::UnmergeableFile + # Fallback to diff_file as it means that conflict lines can't be parsed due to limit + diff_file.diff_lines_for_serializer + end end DiffFileEntity.prepend_mod diff --git a/app/services/auto_merge/base_service.rb b/app/services/auto_merge/base_service.rb index 4ed4368d3b7..2a32f0c74ac 100644 --- a/app/services/auto_merge/base_service.rb +++ b/app/services/auto_merge/base_service.rb @@ -55,7 +55,10 @@ module AutoMerge def available_for?(merge_request) strong_memoize("available_for_#{merge_request.id}") do merge_request.can_be_merged_by?(current_user) && - merge_request.mergeable_state?(skip_ci_check: true) && + merge_request.open? && + !merge_request.broken? && + !merge_request.draft? && + merge_request.mergeable_discussions_state? && yield end end diff --git a/db/post_migrate/20220525131557_cleanup_backfill_integrations_enable_ssl_verification.rb b/db/post_migrate/20220525131557_cleanup_backfill_integrations_enable_ssl_verification.rb new file mode 100644 index 00000000000..3bd4b21c6fd --- /dev/null +++ b/db/post_migrate/20220525131557_cleanup_backfill_integrations_enable_ssl_verification.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class CleanupBackfillIntegrationsEnableSslVerification < Gitlab::Database::Migration[2.0] + disable_ddl_transaction! + + MIGRATION = 'BackfillIntegrationsEnableSslVerification' + + def up + finalize_background_migration(MIGRATION) + end + + def down + # no-op + end +end diff --git a/db/post_migrate/20220525131624_drop_temporary_index_for_backfill_integrations_enable_ssl_verification.rb b/db/post_migrate/20220525131624_drop_temporary_index_for_backfill_integrations_enable_ssl_verification.rb new file mode 100644 index 00000000000..95abac4b7ac --- /dev/null +++ b/db/post_migrate/20220525131624_drop_temporary_index_for_backfill_integrations_enable_ssl_verification.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +class DropTemporaryIndexForBackfillIntegrationsEnableSslVerification < Gitlab::Database::Migration[2.0] + disable_ddl_transaction! + + INDEX_NAME = 'tmp_index_integrations_on_id_where_type_droneci_or_teamcity' + INDEX_CONDITION = "type_new IN ('Integrations::DroneCi', 'Integrations::Teamcity') " \ + "AND encrypted_properties IS NOT NULL" + + def up + remove_concurrent_index_by_name :integrations, INDEX_NAME + end + + def down + # this index is used in 20220209121435_backfill_integrations_enable_ssl_verification + add_concurrent_index :integrations, :id, where: INDEX_CONDITION, name: INDEX_NAME + end +end diff --git a/db/schema_migrations/20220525131557 b/db/schema_migrations/20220525131557 new file mode 100644 index 00000000000..c1b29410ef0 --- /dev/null +++ b/db/schema_migrations/20220525131557 @@ -0,0 +1 @@ +fe0e9acc39c2408853ea3fc35574c553172ad381a5b6f243578f44ed77dc75f8
\ No newline at end of file diff --git a/db/schema_migrations/20220525131624 b/db/schema_migrations/20220525131624 new file mode 100644 index 00000000000..71a3d6d26d2 --- /dev/null +++ b/db/schema_migrations/20220525131624 @@ -0,0 +1 @@ +f34c6e7b75d375342f5c88a9c7b98e15031a6dcdadf7e7dad862ef5f32a54e68
\ No newline at end of file diff --git a/db/structure.sql b/db/structure.sql index d610885ba57..f72dc4d0593 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -29975,8 +29975,6 @@ CREATE INDEX tmp_index_for_null_project_namespace_id ON projects USING btree (id CREATE INDEX tmp_index_for_project_namespace_id_migration_on_routes ON routes USING btree (id) WHERE ((namespace_id IS NULL) AND ((source_type)::text = 'Project'::text)); -CREATE INDEX tmp_index_integrations_on_id_where_type_droneci_or_teamcity ON integrations USING btree (id) WHERE ((type_new = ANY (ARRAY['Integrations::DroneCi'::text, 'Integrations::Teamcity'::text])) AND (encrypted_properties IS NOT NULL)); - CREATE INDEX tmp_index_issues_on_issue_type_and_id ON issues USING btree (issue_type, id); CREATE INDEX tmp_index_members_on_state ON members USING btree (state) WHERE (state = 2); diff --git a/doc/administration/audit_event_streaming.md b/doc/administration/audit_event_streaming.md index 457820c75df..0f4b3b31e8a 100644 --- a/doc/administration/audit_event_streaming.md +++ b/doc/administration/audit_event_streaming.md @@ -175,6 +175,8 @@ To configure streaming audit events for Git operations, see [Add a new event str ### Headers +> `X-Gitlab-Audit-Event-Type` [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/86881) in GitLab 15.0. + Headers are formatted as follows: ```plaintext @@ -182,6 +184,7 @@ POST /logs HTTP/1.1 Host: <DESTINATION_HOST> Content-Type: application/x-www-form-urlencoded X-Gitlab-Event-Streaming-Token: <DESTINATION_TOKEN> +X-Gitlab-Audit-Event-Type: repository_git_operation ``` ### Example payloads for SSH events @@ -212,7 +215,8 @@ Fetch: "target_details": "example-project", "created_at": "2022-02-23T06:21:05.283Z", "target_type": "Project", - "target_id": 29 + "target_id": 29, + "event_type": "repository_git_operation" } ``` @@ -242,7 +246,8 @@ Push: "target_details": "example-project", "created_at": "2022-02-23T06:23:08.746Z", "target_type": "Project", - "target_id": 29 + "target_id": 29, + "event_type": "repository_git_operation" } ``` @@ -274,7 +279,8 @@ Fetch: "target_details": "example-project", "created_at": "2022-02-23T06:25:43.938Z", "target_type": "Project", - "target_id": 29 + "target_id": 29, + "event_type": "repository_git_operation" } ``` @@ -304,7 +310,8 @@ Push: "target_details": "example-project", "created_at": "2022-02-23T06:26:29.294Z", "target_type": "Project", - "target_id": 29 + "target_id": 29, + "event_type": "repository_git_operation" } ``` @@ -333,7 +340,8 @@ Fetch: "target_details": "example-group/example-project", "created_at": "2022-02-23T06:27:17.873Z", "target_type": "Project", - "target_id": 29 + "target_id": 29, + "event_type": "repository_git_operation" } ``` @@ -352,6 +360,7 @@ POST /logs HTTP/1.1 Host: <DESTINATION_HOST> Content-Type: application/x-www-form-urlencoded X-Gitlab-Event-Streaming-Token: <DESTINATION_TOKEN> +X-Gitlab-Audit-Event-Type: audit_operation ``` ### Example payload @@ -377,6 +386,7 @@ X-Gitlab-Event-Streaming-Token: <DESTINATION_TOKEN> "target_details": "merge request title", "created_at": "2022-03-09T06:53:11.181Z", "target_type": "MergeRequest", - "target_id": 20 + "target_id": 20, + "event_type": "audit_operation" } ``` diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 2fe2430c42f..e190a867668 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -21324,6 +21324,9 @@ msgstr "" msgid "Issues closed" msgstr "" +msgid "Issues exist in projects, so to create an issue, first create a project." +msgstr "" + msgid "Issues must match this scope to appear in this list." msgstr "" diff --git a/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_spec.rb b/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_spec.rb index f4ed9f28dac..2beea45a81d 100644 --- a/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_spec.rb +++ b/qa/qa/specs/features/browser_ui/3_create/merge_request/create_merge_request_spec.rb @@ -1,7 +1,10 @@ # frozen_string_literal: true module QA - RSpec.describe 'Create' do + RSpec.describe 'Create', quarantine: { + issue: 'https://gitlab.com/gitlab-org/gitlab/-/issues/363807', + type: :stale + } do describe 'Create a new merge request' do let(:project) do Resource::Project.fabricate_via_api! do |project| diff --git a/spec/frontend/issues/list/components/issues_list_app_spec.js b/spec/frontend/issues/list/components/issues_list_app_spec.js index 14aac5ea562..934178cda0e 100644 --- a/spec/frontend/issues/list/components/issues_list_app_spec.js +++ b/spec/frontend/issues/list/components/issues_list_app_spec.js @@ -67,6 +67,7 @@ describe('CE IssuesListApp component', () => { autocompleteAwardEmojisPath: 'autocomplete/award/emojis/path', calendarPath: 'calendar/path', canBulkUpdate: false, + canCreateProjects: false, canReadCrmContact: false, canReadCrmOrganization: false, emptyStateSvgPath: 'empty-state.svg', @@ -88,6 +89,7 @@ describe('CE IssuesListApp component', () => { isSignedIn: true, jiraIntegrationPath: 'jira/integration/path', newIssuePath: 'new/issue/path', + newProjectPath: 'new/project/path', releasesPath: 'releases/path', rssPath: 'rss/path', showNewIssueLink: true, @@ -118,6 +120,7 @@ describe('CE IssuesListApp component', () => { issuesQueryResponse = jest.fn().mockResolvedValue(defaultQueryResponse), issuesCountsQueryResponse = jest.fn().mockResolvedValue(getIssuesCountsQueryResponse), sortPreferenceMutationResponse = jest.fn().mockResolvedValue(setSortPreferenceMutationResponse), + stubs = {}, mountFn = shallowMount, } = {}) => { const requestHandlers = [ @@ -136,6 +139,7 @@ describe('CE IssuesListApp component', () => { data() { return data; }, + stubs, }); }; @@ -521,10 +525,12 @@ describe('CE IssuesListApp component', () => { it('shows empty state', () => { expect(findGlEmptyState().props()).toMatchObject({ - description: IssuesListApp.i18n.noIssuesSignedInDescription, title: IssuesListApp.i18n.noIssuesSignedInTitle, svgPath: defaultProvide.emptyStateSvgPath, }); + expect(findGlEmptyState().text()).toContain( + IssuesListApp.i18n.noIssuesSignedInDescription, + ); }); it('shows "New issue" and import/export buttons', () => { @@ -538,11 +544,11 @@ describe('CE IssuesListApp component', () => { it('shows Jira integration information', () => { const paragraphs = wrapper.findAll('p'); - expect(paragraphs.at(1).text()).toContain(IssuesListApp.i18n.jiraIntegrationTitle); - expect(paragraphs.at(2).text()).toContain( + expect(paragraphs.at(2).text()).toContain(IssuesListApp.i18n.jiraIntegrationTitle); + expect(paragraphs.at(3).text()).toContain( 'Enable the Jira integration to view your Jira issues in GitLab.', ); - expect(paragraphs.at(3).text()).toContain( + expect(paragraphs.at(4).text()).toContain( IssuesListApp.i18n.jiraIntegrationSecondaryMessage, ); expect(findGlLink().text()).toBe('Enable the Jira integration'); @@ -550,6 +556,29 @@ describe('CE IssuesListApp component', () => { }); }); + describe('when user is logged in and can create projects', () => { + beforeEach(() => { + wrapper = mountComponent({ + provide: { canCreateProjects: true, hasAnyIssues: false, isSignedIn: true }, + stubs: { GlEmptyState }, + }); + }); + + it('shows empty state with additional description about creating projects', () => { + expect(findGlEmptyState().text()).toContain( + IssuesListApp.i18n.noIssuesSignedInDescription, + ); + expect(findGlEmptyState().text()).toContain( + IssuesListApp.i18n.noGroupIssuesSignedInDescription, + ); + }); + + it('shows "New project" button', () => { + expect(findGlButton().text()).toBe(IssuesListApp.i18n.newProjectLabel); + expect(findGlButton().attributes('href')).toBe(defaultProvide.newProjectPath); + }); + }); + describe('when user is logged out', () => { beforeEach(() => { wrapper = mountComponent({ diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb index dd5e162b444..64d596595bb 100644 --- a/spec/helpers/issues_helper_spec.rb +++ b/spec/helpers/issues_helper_spec.rb @@ -355,12 +355,14 @@ RSpec.describe IssuesHelper do expected = { autocomplete_award_emojis_path: autocomplete_award_emojis_path, calendar_path: '#', + can_create_projects: 'true', empty_state_svg_path: '#', full_path: group.full_path, has_any_issues: false.to_s, has_any_projects: true.to_s, is_signed_in: current_user.present?.to_s, jira_integration_path: help_page_url('integration/jira/issues', anchor: 'view-jira-issues'), + new_project_path: new_project_path(namespace_id: group.id), rss_path: '#', sign_in_path: new_user_session_path } diff --git a/spec/migrations/cleanup_backfill_integrations_enable_ssl_verification_spec.rb b/spec/migrations/cleanup_backfill_integrations_enable_ssl_verification_spec.rb new file mode 100644 index 00000000000..1517405b358 --- /dev/null +++ b/spec/migrations/cleanup_backfill_integrations_enable_ssl_verification_spec.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +require 'spec_helper' +require_migration! + +RSpec.describe CleanupBackfillIntegrationsEnableSslVerification, :migration do + let(:job_class_name) { 'BackfillIntegrationsEnableSslVerification' } + + before do + # Jobs enqueued in Sidekiq. + Sidekiq::Testing.disable! do + BackgroundMigrationWorker.perform_in(10, job_class_name, [1, 2]) + BackgroundMigrationWorker.perform_in(20, job_class_name, [3, 4]) + end + + # Jobs tracked in the database. + Gitlab::Database::BackgroundMigrationJob.create!( + class_name: job_class_name, + arguments: [5, 6], + status: Gitlab::Database::BackgroundMigrationJob.statuses['pending'] + ) + Gitlab::Database::BackgroundMigrationJob.create!( + class_name: job_class_name, + arguments: [7, 8], + status: Gitlab::Database::BackgroundMigrationJob.statuses['succeeded'] + ) + + migrate! + end + + it_behaves_like( + 'finalized tracked background migration', + Gitlab::BackgroundMigration::BackfillIntegrationsEnableSslVerification + ) +end diff --git a/spec/serializers/diff_file_entity_spec.rb b/spec/serializers/diff_file_entity_spec.rb index ebfb21c4311..48099cb1fdf 100644 --- a/spec/serializers/diff_file_entity_spec.rb +++ b/spec/serializers/diff_file_entity_spec.rb @@ -91,5 +91,38 @@ RSpec.describe DiffFileEntity do end end + describe '#highlighted_diff_lines' do + context 'file without a conflict' do + let(:options) { { conflicts: {} } } + + it 'calls diff_lines_for_serializer on diff_file' do + # #diff_lines_for_serializer gets called in #fully_expanded? as well so we expect twice + expect(diff_file).to receive(:diff_lines_for_serializer).twice.and_return([]) + expect(subject[:highlighted_diff_lines]).to eq([]) + end + end + + context 'file with a conflict' do + let(:conflict_file) { instance_double(Gitlab::Conflict::File, conflict_type: :both_modified) } + let(:options) { { conflicts: { diff_file.new_path => conflict_file } } } + + it 'calls diff_lines_for_serializer on matching conflict file' do + expect(conflict_file).to receive(:diff_lines_for_serializer).and_return([]) + expect(subject[:highlighted_diff_lines]).to eq([]) + end + + context 'when Gitlab::Git::Conflict::Parser::UnmergeableFile gets raised' do + before do + allow(conflict_file).to receive(:diff_lines_for_serializer).and_raise(Gitlab::Git::Conflict::Parser::UnmergeableFile) + end + + it 'falls back to diff_file diff_lines_for_serializer' do + expect(diff_file).to receive(:diff_lines_for_serializer).and_return([]) + expect(subject[:highlighted_diff_lines]).to eq([]) + end + end + end + end + it_behaves_like 'diff file with conflict_type' end |