Commit 9517d0eb authored by 🤖 GitLab Bot 🤖's avatar 🤖 GitLab Bot 🤖

Add latest changes from gitlab-org/gitlab@master

parent 3454d4cb
......@@ -38,7 +38,7 @@ review-docs-cleanup:
script:
- ./scripts/trigger-build docs cleanup
docs lint:
docs-lint markdown:
extends:
- .default-retry
- .docs:rules:docs-lint
......@@ -47,6 +47,15 @@ docs lint:
needs: []
script:
- scripts/lint-doc.sh
docs-lint links:
extends:
- .default-retry
- .docs:rules:docs-lint
image: "registry.gitlab.com/gitlab-org/gitlab-docs/lint:ruby-2.7.2-alpine-3.12-vale-2.4.3-markdownlint-0.24.0"
stage: test
needs: []
script:
# Prepare docs for build
# The path must be 'ee/' because we have hardcoded links relying on it
# https://gitlab.com/gitlab-org/gitlab-docs/-/blob/887850752fc0e72856da6632db132f005ba77f16/content/index.erb#L44-63
......
0926b9f8ff2215874eef0e22f750a74da5fe4321
194a9f58926793ade53152de90b474d66804e21e
<script>
import { GlTooltipDirective } from '@gitlab/ui';
import GlModal from '~/vue_shared/components/gl_modal.vue';
import { GlTooltipDirective, GlModal } from '@gitlab/ui';
import { s__, sprintf } from '~/locale';
import eventHub from '../event_hub';
export default {
id: 'delete-environment-modal',
name: 'DeleteEnvironmentModal',
components: {
GlModal,
},
directives: {
GlTooltip: GlTooltipDirective,
},
props: {
environment: {
type: Object,
required: true,
},
},
computed: {
primaryProps() {
return {
text: s__('Environments|Delete environment'),
attributes: [{ variant: 'danger' }],
};
},
cancelProps() {
return {
text: s__('Cancel'),
};
},
confirmDeleteMessage() {
return sprintf(
s__(
......@@ -35,8 +41,12 @@ export default {
false,
);
},
modalTitle() {
return sprintf(s__(`Environments|Delete '%{environmentName}'?`), {
environmentName: this.environment.name,
});
},
},
methods: {
onSubmit() {
eventHub.$emit('deleteEnvironment', this.environment);
......@@ -47,20 +57,12 @@ export default {
<template>
<gl-modal
:id="$options.id"
:footer-primary-button-text="s__('Environments|Delete environment')"
footer-primary-button-variant="danger"
@submit="onSubmit"
:modal-id="$options.id"
:action-primary="primaryProps"
:action-cancel="cancelProps"
:title="modalTitle"
@primary="onSubmit"
>
<template #header>
<h4 class="modal-title d-flex mw-100">
{{ __('Delete') }}
<span v-gl-tooltip :title="environment.name" class="text-truncate mx-1 flex-fill">
{{ environment.name }}?
</span>
</h4>
</template>
<p>{{ confirmDeleteMessage }}</p>
</gl-modal>
</template>
......@@ -4,7 +4,7 @@
* Used in the environments table.
*/
import { GlTooltipDirective, GlButton } from '@gitlab/ui';
import { GlTooltipDirective, GlButton, GlModalDirective } from '@gitlab/ui';
import { s__ } from '~/locale';
import eventHub from '../event_hub';
......@@ -14,6 +14,7 @@ export default {
},
directives: {
GlTooltip: GlTooltipDirective,
GlModalDirective,
},
props: {
environment: {
......@@ -54,6 +55,7 @@ export default {
<template>
<gl-button
v-gl-tooltip="{ id: $options.deleteEnvironmentTooltipId }"
v-gl-modal-directive="'delete-environment-modal'"
:loading="isLoading"
:title="title"
:aria-label="title"
......@@ -61,8 +63,6 @@ export default {
variant="danger"
category="primary"
icon="remove"
data-toggle="modal"
data-target="#delete-environment-modal"
@click="onClick"
/>
</template>
......@@ -3,7 +3,7 @@ import { escape } from 'lodash';
import { __, sprintf } from './locale';
import axios from './lib/utils/axios_utils';
import { deprecatedCreateFlash as flash } from './flash';
import { parseBoolean } from './lib/utils/common_utils';
import { parseBoolean, spriteIcon } from './lib/utils/common_utils';
class ImporterStatus {
constructor({ jobsUrl, importUrl, ciCdOnly }) {
......@@ -108,7 +108,7 @@ class ImporterStatus {
switch (job.import_status) {
case 'finished':
jobItem.removeClass('table-active').addClass('table-success');
statusField.html(`<span><i class="fa fa-check"></i> ${__('Done')}</span>`);
statusField.html(`<span>${spriteIcon('check', 's16')} ${__('Done')}</span>`);
break;
case 'scheduled':
statusField.html(`${spinner} ${__('Scheduled')}`);
......
const ComponentPerformancePlugin = {
install(Vue, options) {
Vue.mixin({
beforeCreate() {
/** Make sure the component you want to measure has `name` option defined
* and it matches the one you pass as the plugin option. Example:
*
* my_component.vue:
*
* ```
* export default {
* name: 'MyComponent'
* ...
* }
* ```
*
* index.js (where you initialize your Vue app containing <my-component>):
*
* ```
* Vue.use(PerformancePlugin, {
* components: [
* 'MyComponent',
* ]
* });
* ```
*/
const componentName = this.$options.name;
if (options?.components?.indexOf(componentName) !== -1) {
const tagName = `<${componentName}>`;
if (!performance.getEntriesByName(`${tagName}-start`).length) {
performance.mark(`${tagName}-start`);
}
}
},
mounted() {
const componentName = this.$options.name;
if (options?.components?.indexOf(componentName) !== -1) {
this.$nextTick(() => {
window.requestAnimationFrame(() => {
const tagName = `<${componentName}>`;
if (!performance.getEntriesByName(`${tagName}-end`).length) {
performance.mark(`${tagName}-end`);
performance.measure(`${tagName}`, `${tagName}-start`);
}
});
});
}
},
});
},
};
export default ComponentPerformancePlugin;
......@@ -179,7 +179,7 @@ export default {
<span class="input-group-prepend">
<button
ref="toggleEmojiMenuButton"
v-gl-tooltip.bottom
v-gl-tooltip.bottom.hover
:title="s__('SetStatusModal|Add status emoji')"
:aria-label="s__('SetStatusModal|Add status emoji')"
name="button"
......
......@@ -92,10 +92,6 @@
content: '\f0d7';
}
.fa-check::before {
content: '\f00c';
}
.fa-warning::before,
.fa-exclamation-triangle::before {
content: '\f071';
......
......@@ -12,6 +12,8 @@ def initialize(project, package_name)
end
def execute
return Packages::Package.none unless project
packages
end
......
......@@ -118,6 +118,12 @@ class IssueType < BaseObject
field :severity, Types::IssuableSeverityEnum, null: true,
description: 'Severity level of the incident'
field :moved, GraphQL::BOOLEAN_TYPE, method: :moved?, null: true,
description: 'Indicates if issue got moved from other project'
field :moved_to, Types::IssueType, null: true,
description: 'Updated Issue after it got moved to another project'
def user_notes_count
BatchLoader::GraphQL.for(object.id).batch(key: :issue_user_notes_count) do |ids, loader, args|
counts = Note.count_for_collection(ids, 'Issue').index_by(&:noteable_id)
......@@ -150,6 +156,10 @@ def milestone
Gitlab::Graphql::Loaders::BatchModelLoader.new(Milestone, object.milestone_id).find
end
def moved_to
Gitlab::Graphql::Loaders::BatchModelLoader.new(Issue, object.moved_to_id).find
end
def discussion_locked
!!object.discussion_locked
end
......
......@@ -311,6 +311,8 @@ def public_merge_status
includes(:metrics)
end
scope :with_jira_issue_keys, -> { where('title ~ :regex OR merge_requests.description ~ :regex', regex: Gitlab::Regex.jira_issue_key_regex.source) }
after_save :keep_around_commit, unless: :importing?
alias_attribute :project, :target_project
......
......@@ -602,7 +602,7 @@ def self.eager_load_namespace_and_owner
# Returns a collection of projects that is either public or visible to the
# logged in user.
def self.public_or_visible_to_user(user = nil, min_access_level = nil)
min_access_level = nil if user&.admin?
min_access_level = nil if user&.can_read_all_resources?
return public_to_user unless user
......@@ -628,7 +628,7 @@ def self.public_or_visible_to_user(user = nil, min_access_level = nil)
def self.with_feature_available_for_user(feature, user)
visible = [ProjectFeature::ENABLED, ProjectFeature::PUBLIC]
if user&.admin?
if user&.can_read_all_resources?
with_feature_enabled(feature)
elsif user
min_access_level = ProjectFeature.required_minimum_access_level(feature)
......
......@@ -72,6 +72,10 @@ def requires_ldap_check?
def try_obtain_ldap_lease
nil
end
def can_read_all_resources?
false
end
end
PolicyActor.prepend_if_ee('EE::PolicyActor')
......@@ -3,4 +3,5 @@
class MoveToProjectEntity < Grape::Entity
expose :id
expose :name_with_namespace
expose :full_path
end
......@@ -3,6 +3,8 @@
module JiraConnectSubscriptions
class CreateService < ::JiraConnectSubscriptions::BaseService
include Gitlab::Utils::StrongMemoize
MERGE_REQUEST_SYNC_BATCH_SIZE = 20
MERGE_REQUEST_SYNC_BATCH_delay = 1.minute.freeze
def execute
unless namespace && can?(current_user, :create_jira_connect_subscription, namespace)
......@@ -18,6 +20,8 @@ def create_subscription
subscription = JiraConnectSubscription.new(installation: jira_connect_installation, namespace: namespace)
if subscription.save
schedule_sync_project_jobs
success
else
error(subscription.errors.full_messages.join(', '), 422)
......@@ -29,5 +33,18 @@ def namespace
Namespace.find_by_full_path(params[:namespace_path])
end
end
def schedule_sync_project_jobs
return unless Feature.enabled?(:jira_connect_full_namespace_sync)
namespace.all_projects.each_batch(of: MERGE_REQUEST_SYNC_BATCH_SIZE) do |projects, index|
JiraConnect::SyncProjectWorker.bulk_perform_in_with_contexts(
index * MERGE_REQUEST_SYNC_BATCH_delay,
projects,
arguments_proc: -> (project) { [project.id, Atlassian::JiraConnect::Client.generate_update_sequence_id] },
context_proc: -> (project) { { project: project } }
)
end
end
end
end
- return unless can?(current_user, :remove_project, project)
- confirm_phrase = s_('DeleteProject|Delete %{name}') % { name: project.full_name }
.sub-section
%h4.danger-title= _('Delete project')
......@@ -7,4 +6,4 @@
%strong= _('Deleting the project will delete its repository and all related resources including issues, merge requests etc.')
%p
%strong= _('Deleted projects cannot be restored!')
#js-project-delete-button{ data: { form_path: project_path(project), confirm_phrase: confirm_phrase } }
#js-project-delete-button{ data: { form_path: project_path(project), confirm_phrase: project.path } }
......@@ -827,6 +827,14 @@
:weight: 1
:idempotent:
:tags: []
- :name: jira_connect:jira_connect_sync_project
:feature_category: :integrations
:has_external_dependencies: true
:urgency: :low
:resource_boundary: :unknown
:weight: 1
:idempotent: true
:tags: []
- :name: jira_importer:jira_import_advance_stage
:feature_category: :importers
:has_external_dependencies:
......
# frozen_string_literal: true
module JiraConnect
class SyncProjectWorker
include ApplicationWorker
queue_namespace :jira_connect
feature_category :integrations
idempotent!
worker_has_external_dependencies!
MERGE_REQUEST_LIMIT = 400
def perform(project_id, update_sequence_id)
project = Project.find_by_id(project_id)
return if project.nil?
JiraConnect::SyncService.new(project).execute(merge_requests: merge_requests_to_sync(project), update_sequence_id: update_sequence_id)
end
private
# rubocop: disable CodeReuse/ActiveRecord
def merge_requests_to_sync(project)
project.merge_requests.with_jira_issue_keys.preload(:author).limit(MERGE_REQUEST_LIMIT).order(id: :desc)
end
# rubocop: enable CodeReuse/ActiveRecord
end
end
---
title: NPM project level API
merge_request: 46867
author:
type: added
---
title: Expose moved and movedTo attributes in Issues query
merge_request: 46447
author:
type: added
---
title: Make delete repo prompts consistent
merge_request: 47117
author:
type: fixed
---
title: Migrate services specs to consider admin mode
merge_request: 45988
author: Diego Louzán
type: other
---
title: Fix status emoji tooltip trigger
merge_request: 47378
author:
type: fixed
---
title: Jira Connect automatically synchronizes up to 400 existing merge requests per project when a namespace is connected.
merge_request: 43880
author:
type: added
---
title: Replace fa-check icon in importer status
merge_request: 47373
author:
type: changed
---
name: jira_connect_full_namespace_sync
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/43880
rollout_issue_url:
type: development
group: group::ecosystem
default_enabled: false
# frozen_string_literal: true
class AddMergeRequestJiraReferenceIndexes < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
DESCRIPTION_INDEX_NAME = 'index_merge_requests_on_target_project_id_iid_jira_description'
TITLE_INDEX_NAME = 'index_merge_requests_on_target_project_id_and_iid_jira_title'
JIRA_KEY_REGEX = '[A-Z][A-Z_0-9]+-\d+'
disable_ddl_transaction!
def up
add_concurrent_index(
:merge_requests,
[:target_project_id, :iid],
name: TITLE_INDEX_NAME,
using: :btree,
where: "(merge_requests.title)::text ~ '#{JIRA_KEY_REGEX}'::text"
)
add_concurrent_index(
:merge_requests,
[:target_project_id, :iid],
name: DESCRIPTION_INDEX_NAME,
using: :btree,
where: "(merge_requests.description)::text ~ '#{JIRA_KEY_REGEX}'::text"
)
end
def down
remove_concurrent_index_by_name(
:merge_requests,
TITLE_INDEX_NAME
)
remove_concurrent_index_by_name(
:merge_requests,
DESCRIPTION_INDEX_NAME
)
end
end
a2dc0d31af6834adf6634f6051d7d451fc48d31492d96efe57547c3e9d61a64d
\ No newline at end of file
......@@ -21150,8 +21150,12 @@ CREATE UNIQUE INDEX index_merge_requests_on_target_project_id_and_iid ON merge_r
CREATE INDEX index_merge_requests_on_target_project_id_and_iid_and_state_id ON merge_requests USING btree (target_project_id, iid, state_id);
CREATE INDEX index_merge_requests_on_target_project_id_and_iid_jira_title ON merge_requests USING btree (target_project_id, iid) WHERE ((title)::text ~ '[A-Z][A-Z_0-9]+-\d+'::text);
CREATE INDEX index_merge_requests_on_target_project_id_and_target_branch ON merge_requests USING btree (target_project_id, target_branch) WHERE ((state_id = 1) AND (merge_when_pipeline_succeeds = true));
CREATE INDEX index_merge_requests_on_target_project_id_iid_jira_description ON merge_requests USING btree (target_project_id, iid) WHERE (description ~ '[A-Z][A-Z_0-9]+-\d+'::text);
CREATE INDEX index_merge_requests_on_title ON merge_requests USING btree (title);
CREATE INDEX index_merge_requests_on_title_trigram ON merge_requests USING gin (title gin_trgm_ops);
......
......@@ -260,9 +260,9 @@ The following documentation relates to the DevOps **Verify** stage:
### Package
GitLab Packages allows organizations to utilize GitLab as a private repository
GitLab Packages allows organizations to use GitLab as a private repository
for a variety of common package managers. Users are able to build and publish
packages, which can be easily consumed as a dependency in downstream projects.
packages, which can be consumed as a dependency in downstream projects.
The following documentation relates to the DevOps **Package** stage:
......
......@@ -7645,6 +7645,16 @@ type EpicIssue implements CurrentUserTodos & Noteable {
"""
milestone: Milestone
"""
Indicates if issue got moved from other project
"""
moved: Boolean
"""
Updated Issue after it got moved to another project
"""
movedTo: Issue
"""
All notes on this noteable
"""
......@@ -10169,6 +10179,16 @@ type Issue implements CurrentUserTodos & Noteable {
"""