Commit 89b770bb authored by 🤖 GitLab Bot 🤖's avatar 🤖 GitLab Bot 🤖
Browse files

Add latest changes from gitlab-org/gitlab@master

parent 3bc30c28
......@@ -519,3 +519,5 @@ gem 'webauthn', '~> 2.3'
# IPAddress utilities
gem 'ipaddress', '~> 0.8.3'
gem 'parslet', '~> 1.8'
......@@ -1464,6 +1464,7 @@ DEPENDENCIES
omniauth_openid_connect (~> 0.3.5)
org-ruby (~> 0.9.12)
parallel (~> 1.19)
parslet (~> 1.8)
peek (~> 1.1)
pg (~> 1.1)
pg_query (~> 1.3.0)
......
......@@ -34,12 +34,12 @@ export default {
};
</script>
<template>
<p class="build-detail-row">
<span v-if="hasTitle" class="font-weight-bold">{{ title }}:</span> {{ value }}
<span v-if="hasHelpURL" class="help-button float-right">
<gl-link :href="helpUrl" target="_blank" rel="noopener noreferrer nofollow">
<gl-icon name="question-o" />
</gl-link>
</span>
<p class="gl-display-flex gl-justify-content-space-between gl-mb-2">
<span v-if="hasTitle"
><b>{{ title }}:</b> {{ value }}</span
>
<gl-link v-if="hasHelpURL" :href="helpUrl" target="_blank">
<gl-icon name="question-o" />
</gl-link>
</p>
</template>
......@@ -20,18 +20,26 @@ export default {
},
},
data() {
const [chart] = getParameterValues('chart') || charts;
const tab = charts.indexOf(chart);
return {
chart,
selectedTab: tab >= 0 ? tab : 0,
selectedTab: 0,
};
},
created() {
this.selectTab();
window.addEventListener('popstate', this.selectTab);
},
methods: {
selectTab() {
const [chart] = getParameterValues('chart') || charts;
const tab = charts.indexOf(chart);
this.selectedTab = tab >= 0 ? tab : 0;
},
onTabChange(index) {
this.selectedTab = index;
const path = mergeUrlParams({ chart: charts[index] }, window.location.pathname);
updateHistory({ url: path });
if (index !== this.selectedTab) {
this.selectedTab = index;
const path = mergeUrlParams({ chart: charts[index] }, window.location.pathname);
updateHistory({ url: path, title: window.title });
}
},
},
};
......
......@@ -982,7 +982,14 @@ $mr-widget-min-height: 69px;
}
.mini-pipeline-graph-dropdown-toggle,
.stage-cell .mini-pipeline-graph-dropdown-toggle svg {
.stage-cell .mini-pipeline-graph-dropdown-toggle svg,
// As the `mini-pipeline-item` mixin specificity is lower
// than the toggle of dropdown with 'variant="link"' we add
// classes ".gl-button.btn-link" to make it more specific.
// Once FF ci_mini_pipeline_gl_dropdown is removed, the `mini-pipeline-item`
// itself could increase its specificity to simplify this selector
button.gl-button.btn-link.mini-pipeline-graph-gl-dropdown-toggle,
.stage-cell button.gl-button.btn-link.mini-pipeline-graph-gl-dropdown-toggle svg {
height: $ci-action-icon-size-lg;
width: $ci-action-icon-size-lg;
}
......
......@@ -24,10 +24,8 @@ def show
end
def names
templates = @template_type.dropdown_names(project)
respond_to do |format|
format.json { render json: templates }
format.json { render json: TemplateFinder.all_template_names_array(project, params[:template_type].to_s.pluralize) }
end
end
......
......@@ -28,6 +28,10 @@ def execute
end
end
def template_names
::Gitlab::Template::BaseTemplate.template_names_by_category(vendored_licenses)
end
private
def vendored_licenses
......
......@@ -21,6 +21,18 @@ def build(type, project, params = {})
new(type, project, params)
end
end
def all_template_names(project, type)
return {} if !VENDORED_TEMPLATES.key?(type.to_s) && type.to_s != 'licenses'
build(type, project).template_names
end
# This is issues and merge requests description templates only.
# This will be removed once we introduce group level inherited templates
def all_template_names_array(project, type)
all_template_names(project, type).values.flatten.uniq
end
end
attr_reader :type, :project, :params
......@@ -43,6 +55,10 @@ def execute
vendored_templates.all(project)
end
end
def template_names
vendored_templates.template_names(project)
end
end
TemplateFinder.prepend_if_ee('::EE::TemplateFinder')
......@@ -29,6 +29,12 @@ def resolve(project_path:, content:, dry_run: false)
.new(project: project, current_user: context[:current_user])
.validate(content, dry_run: dry_run)
response(result).merge(merged_yaml: result.merged_yaml)
end
private
def response(result)
if result.errors.empty?
{
status: :valid,
......@@ -43,8 +49,6 @@ def resolve(project_path:, content:, dry_run: false)
end
end
private
def make_jobs(config_jobs)
config_jobs.map do |job|
{
......
......@@ -194,40 +194,28 @@ def ref_project
@ref_project ||= @target_project || @project
end
def template_dropdown_names(items)
grouped = items.group_by(&:category)
categories = grouped.keys
categories.each_with_object({}) do |category, hash|
hash[category] = grouped[category].map do |item|
{ name: item.name, id: item.key }
end
end
end
private :template_dropdown_names
def licenses_for_select(project)
@licenses_for_select ||= template_dropdown_names(TemplateFinder.build(:licenses, project).execute)
@licenses_for_select ||= TemplateFinder.all_template_names(project, :licenses)
end
def gitignore_names(project)
@gitignore_names ||= template_dropdown_names(TemplateFinder.build(:gitignores, project).execute)
@gitignore_names ||= TemplateFinder.all_template_names(project, :gitignores)
end
def gitlab_ci_ymls(project)
@gitlab_ci_ymls ||= template_dropdown_names(TemplateFinder.build(:gitlab_ci_ymls, project).execute)
@gitlab_ci_ymls ||= TemplateFinder.all_template_names(project, :gitlab_ci_ymls)
end
def gitlab_ci_syntax_ymls(project)
@gitlab_ci_syntax_ymls ||= template_dropdown_names(TemplateFinder.build(:gitlab_ci_syntax_ymls, project).execute)
@gitlab_ci_syntax_ymls ||= TemplateFinder.all_template_names(project, :gitlab_ci_syntax_ymls)
end
def metrics_dashboard_ymls(project)
@metrics_dashboard_ymls ||= template_dropdown_names(TemplateFinder.build(:metrics_dashboard_ymls, project).execute)
@metrics_dashboard_ymls ||= TemplateFinder.all_template_names(project, :metrics_dashboard_ymls)
end
def dockerfile_names(project)
@dockerfile_names ||= template_dropdown_names(TemplateFinder.build(:dockerfiles, project).execute)
@dockerfile_names ||= TemplateFinder.all_template_names(project, :dockerfiles)
end
def blob_editor_paths(project)
......
# frozen_string_literal: true
module IssuablesDescriptionTemplatesHelper
include Gitlab::Utils::StrongMemoize
include GitlabRoutingHelper
def template_dropdown_tag(issuable, &block)
title = selected_template(issuable) || "Choose a template"
options = {
toggle_class: 'js-issuable-selector',
title: title,
filter: true,
placeholder: 'Filter',
footer_content: true,
data: {
data: issuable_templates(ref_project, issuable.to_ability_name),
field_name: 'issuable_template',
selected: selected_template(issuable),
project_id: ref_project.id,
project_path: ref_project.path,
namespace_path: ref_project.namespace.full_path
}
}
dropdown_tag(title, options: options) do
capture(&block)
end
end
def issuable_templates(project, issuable_type)
@template_types ||= {}
@template_types[project.id] ||= {}
@template_types[project.id][issuable_type] ||= TemplateFinder.all_template_names_array(project, issuable_type.pluralize)
end
def issuable_templates_names(issuable)
issuable_templates(ref_project, issuable.to_ability_name).map { |template| template[:name] }
end
def selected_template(issuable)
params[:issuable_template] if issuable_templates(ref_project, issuable.to_ability_name).any? { |template| template[:name] == params[:issuable_template] }
end
def template_names_path(parent, issuable)
return '' unless parent.is_a?(Project)
project_template_names_path(parent, template_type: issuable.to_ability_name)
end
end
......@@ -2,6 +2,7 @@
module IssuablesHelper
include GitlabRoutingHelper
include IssuablesDescriptionTemplatesHelper
def sidebar_gutter_toggle_icon
content_tag(:span, class: 'js-sidebar-toggle-container', data: { is_expanded: !sidebar_gutter_collapsed? }) do
......@@ -75,28 +76,6 @@ def serialize_issuable(issuable, opts = {})
.to_json
end
def template_dropdown_tag(issuable, &block)
title = selected_template(issuable) || "Choose a template"
options = {
toggle_class: 'js-issuable-selector',
title: title,
filter: true,
placeholder: 'Filter',
footer_content: true,
data: {
data: issuable_templates(issuable),
field_name: 'issuable_template',
selected: selected_template(issuable),
project_path: ref_project.path,
namespace_path: ref_project.namespace.full_path
}
}
dropdown_tag(title, options: options) do
capture(&block)
end
end
def users_dropdown_label(selected_users)
case selected_users.length
when 0
......@@ -282,6 +261,7 @@ def path_data(parent)
{
projectPath: ref_project.path,
projectId: ref_project.id,
projectNamespace: ref_project.namespace.full_path
}
end
......@@ -358,24 +338,6 @@ def sidebar_gutter_collapsed?
cookies[:collapsed_gutter] == 'true'
end
def issuable_templates(issuable)
@issuable_templates ||=
case issuable
when Issue
ref_project.repository.issue_template_names
when MergeRequest
ref_project.repository.merge_request_template_names
end
end
def issuable_templates_names(issuable)
issuable_templates(issuable).map { |template| template[:name] }
end
def selected_template(issuable)
params[:issuable_template] if issuable_templates(issuable).any? { |template| template[:name] == params[:issuable_template] }
end
def issuable_todo_button_data(issuable, is_collapsed)
{
todo_text: _('Add a to do'),
......@@ -413,12 +375,6 @@ def labels_path
end
end
def template_names_path(parent, issuable)
return '' unless parent.is_a?(Project)
project_template_names_path(parent, template_type: issuable.class.name.underscore)
end
def issuable_sidebar_options(issuable)
{
endpoint: "#{issuable[:issuable_json_path]}?serializer=sidebar_extras",
......
......@@ -117,7 +117,7 @@ class Project < ApplicationRecord
use_fast_destroy :build_trace_chunks
after_destroy -> { run_after_commit { remove_pages } }
after_destroy -> { run_after_commit { legacy_remove_pages } }
after_destroy :remove_exports
after_validation :check_pending_delete
......@@ -1788,16 +1788,16 @@ def remove_private_deploy_keys
.delete_all
end
# TODO: what to do here when not using Legacy Storage? Do we still need to rename and delay removal?
# TODO: remove this method https://gitlab.com/gitlab-org/gitlab/-/issues/320775
# rubocop: disable CodeReuse/ServiceClass
def remove_pages
def legacy_remove_pages
return unless Feature.enabled?(:pages_update_legacy_storage, default_enabled: true)
# Projects with a missing namespace cannot have their pages removed
return unless namespace
mark_pages_as_not_deployed unless destroyed?
DestroyPagesDeploymentsWorker.perform_async(id)
# 1. We rename pages to temporary directory
# 2. We wait 5 minutes, due to NFS caching
# 3. We asynchronously remove pages with force
......
# frozen_string_literal: true
# Accessible as Project#external_issue_tracker
class JiraService < IssueTrackerService
extend ::Gitlab::Utils::Override
include Gitlab::Routing
......@@ -162,7 +163,7 @@ def find_issue(issue_key)
jira_request { client.Issue.find(issue_key) }
end
def close_issue(entity, external_issue)
def close_issue(entity, external_issue, current_user)
issue = find_issue(external_issue.iid)
return if issue.nil? || has_resolution?(issue) || !jira_issue_transition_id.present?
......@@ -179,6 +180,7 @@ def close_issue(entity, external_issue)
# if it is closed, so we don't have one comment for every commit.
issue = find_issue(issue.key) if transition_issue(issue)
add_issue_solved_comment(issue, commit_id, commit_url) if has_resolution?(issue)
log_usage(:close_issue, current_user)
end
def create_cross_reference_note(mentioned, noteable, author)
......@@ -214,7 +216,7 @@ def create_cross_reference_note(mentioned, noteable, author)
}
}
add_comment(data, jira_issue)
add_comment(data, jira_issue).tap { log_usage(:cross_reference, author) }
end
def valid_connection?
......@@ -275,6 +277,12 @@ def transition_issue(issue)
end
end
def log_usage(action, user)
key = "i_ecosystem_jira_service_#{action}"
Gitlab::UsageDataCounters::HLLRedisCounter.track_event(key, values: user.id)
end
def add_issue_solved_comment(issue, commit_id, commit_url)
link_title = "Solved by commit #{commit_id}."
comment = "Issue solved with [#{commit_id}|#{commit_url}]."
......
......@@ -43,7 +43,7 @@ class Repository
changelog license_blob license_key gitignore
gitlab_ci_yml branch_names tag_names branch_count
tag_count avatar exists? root_ref merged_branch_names
has_visible_content? issue_template_names merge_request_template_names
has_visible_content? issue_template_names_by_category merge_request_template_names_by_category
user_defined_metrics_dashboard_paths xcode_project? has_ambiguous_refs?).freeze
# Methods that use cache_method but only memoize the value
......@@ -60,8 +60,8 @@ class Repository
gitignore: :gitignore,
gitlab_ci: :gitlab_ci_yml,
avatar: :avatar,
issue_template: :issue_template_names,
merge_request_template: :merge_request_template_names,
issue_template: :issue_template_names_by_category,
merge_request_template: :merge_request_template_names_by_category,
metrics_dashboard: :user_defined_metrics_dashboard_paths,
xcode_config: :xcode_project?
}.freeze
......@@ -572,15 +572,16 @@ def avatar
end
cache_method :avatar
def issue_template_names
Gitlab::Template::IssueTemplate.dropdown_names(project)
# store issue_template_names as hash
def issue_template_names_by_category
Gitlab::Template::IssueTemplate.repository_template_names(project)
end
cache_method :issue_template_names, fallback: []
cache_method :issue_template_names_by_category, fallback: {}
def merge_request_template_names
Gitlab::Template::MergeRequestTemplate.dropdown_names(project)
def merge_request_template_names_by_category
Gitlab::Template::MergeRequestTemplate.repository_template_names(project)
end
cache_method :merge_request_template_names, fallback: []
cache_method :merge_request_template_names_by_category, fallback: {}
def user_defined_metrics_dashboard_paths
Gitlab::Metrics::Dashboard::RepoDashboardFinder.list_dashboards(project)
......
......@@ -55,7 +55,7 @@ def close_issue(issue, closed_via: nil, notifications: true, system_note: true)
def close_external_issue(issue, closed_via)
return unless project.external_issue_tracker&.support_close_issue?
project.external_issue_tracker.close_issue(closed_via, issue)
project.external_issue_tracker.close_issue(closed_via, issue, current_user)
todo_service.close_issue(issue, current_user)
end
......
......@@ -27,7 +27,11 @@ def execute
end
note_saved = note.with_transaction_returning_status do
!only_commands && note.save
break false if only_commands
note.save.tap do
update_discussions(note)
end
end
when_saved(note) if note_saved
......@@ -54,12 +58,17 @@ def quick_actions_service
@quick_actions_service ||= QuickActionsService.new(project, current_user)
end
def when_saved(note)
def update_discussions(note)
# Ensure that individual notes that are promoted into discussions are
# updated in a transaction with the note creation to avoid inconsistencies:
# https://gitlab.com/gitlab-org/gitlab/-/issues/301237
if note.part_of_discussion? && note.discussion.can_convert_to_discussion?
note.discussion.convert_to_discussion!.save
note.clear_memoization(:discussion)
end
end
def when_saved(note)
todo_service.new_note(note, current_user)
clear_noteable_diffs_cache(note)
Suggestions::CreateService.new(note).execute
......
......@@ -3,7 +3,13 @@
module Pages
class DeleteService < BaseService
def execute
PagesRemoveWorker.perform_async(project.id)
project.mark_pages_as_not_deployed # prevents domain from updating config when deleted
project.pages_domains.delete_all
DestroyPagesDeploymentsWorker.perform_async(project.id)
# TODO: remove this call https://gitlab.com/gitlab-org/gitlab/-/issues/320775
PagesRemoveWorker.perform_async(project.id) if Feature.enabled?(:pages_update_legacy_storage, default_enabled: true)
end
end
end
- page_title _("Applications")
%h3.page-title
System OAuth applications
= _('System OAuth applications')
%p.light
System OAuth applications don't belong to any user and can only be managed by admins
= _('System OAuth applications don\'t belong to any user and can only be managed by admins')
%hr
%p= link_to 'New application', new_admin_application_path, class: 'gl-button btn btn-success'
%table.table
%thead
%tr
%th Name
%th Callback URL
%th Clients
%th Trusted
%th Confidential
%th
%th
%tbody.oauth-applications
- @applications.each do |application|
%tr{ :id => "application_#{application.id}" }
%td= link_to application.name, admin_application_path(application)
%td= application.redirect_uri
%td= @application_counts[application.id].to_i
%td= application.trusted? ? 'Y': 'N'
%td= application.confidential? ? 'Y': 'N'
%td= link_to 'Edit', edit_admin_application_path(application), class: 'gl-button btn btn-link'
%td= render 'delete_form', application: application
%p= link_to _('New application'), new_admin_application_path, class: 'gl-button btn btn-success'
.table-responsive
%table.table
%thead
%tr
%th
= _('Name')
%th
= _('Callback URL')
%th
= _('Clients')
%th
= _('Trusted')
%th