summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2022-02-22 12:14:09 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2022-02-22 12:14:09 +0000
commit59f160b0cf3ca52fc25f827e57d0dc1273a50521 (patch)
tree6c3d25e025f1dc60bc56fe8f49c133fa119e078b /app
parent932d504aaadc03b978eccad962a12be93f84be47 (diff)
downloadgitlab-ce-59f160b0cf3ca52fc25f827e57d0dc1273a50521.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/pages/projects/forks/new/components/app.vue43
-rw-r--r--app/assets/javascripts/pages/projects/forks/new/components/fork_form.vue25
-rw-r--r--app/assets/javascripts/pages/projects/forks/new/components/fork_groups_list.vue93
-rw-r--r--app/assets/javascripts/pages/projects/forks/new/components/fork_groups_list_item.vue148
-rw-r--r--app/assets/javascripts/pages/projects/forks/new/index.js79
-rw-r--r--app/assets/javascripts/security_configuration/constants.js2
-rw-r--r--app/controllers/projects/forks_controller.rb8
-rw-r--r--app/models/merge_request.rb19
-rw-r--r--app/serializers/fork_namespace_entity.rb8
-rw-r--r--app/services/merge_requests/create_service.rb10
-rw-r--r--app/services/projects/container_repository/third_party/delete_tags_service.rb8
-rw-r--r--app/services/system_notes/issuables_service.rb5
-rw-r--r--app/views/projects/forks/_fork_button.html.haml20
-rw-r--r--app/views/projects/forks/new.html.haml39
14 files changed, 87 insertions, 420 deletions
diff --git a/app/assets/javascripts/pages/projects/forks/new/components/app.vue b/app/assets/javascripts/pages/projects/forks/new/components/app.vue
index 7fb41c6e7b7..0995a2118b1 100644
--- a/app/assets/javascripts/pages/projects/forks/new/components/app.vue
+++ b/app/assets/javascripts/pages/projects/forks/new/components/app.vue
@@ -10,38 +10,6 @@ export default {
type: String,
required: true,
},
- endpoint: {
- type: String,
- required: true,
- },
- projectFullPath: {
- type: String,
- required: true,
- },
- projectId: {
- type: String,
- required: true,
- },
- projectName: {
- type: String,
- required: true,
- },
- projectPath: {
- type: String,
- required: true,
- },
- projectDescription: {
- type: String,
- required: true,
- },
- projectVisibility: {
- type: String,
- required: true,
- },
- restrictedVisibilityLevels: {
- type: Array,
- required: true,
- },
},
};
</script>
@@ -62,16 +30,7 @@ export default {
</p>
</div>
<div class="col-lg-9">
- <fork-form
- :endpoint="endpoint"
- :project-full-path="projectFullPath"
- :project-id="projectId"
- :project-name="projectName"
- :project-path="projectPath"
- :project-description="projectDescription"
- :project-visibility="projectVisibility"
- :restricted-visibility-levels="restrictedVisibilityLevels"
- />
+ <fork-form />
</div>
</div>
</template>
diff --git a/app/assets/javascripts/pages/projects/forks/new/components/fork_form.vue b/app/assets/javascripts/pages/projects/forks/new/components/fork_form.vue
index 25b62e6c971..701bf0c1e1d 100644
--- a/app/assets/javascripts/pages/projects/forks/new/components/fork_form.vue
+++ b/app/assets/javascripts/pages/projects/forks/new/components/fork_form.vue
@@ -72,40 +72,29 @@ export default {
visibilityHelpPath: {
default: '',
},
- },
- props: {
endpoint: {
- type: String,
- required: true,
+ default: '',
},
projectFullPath: {
- type: String,
- required: true,
+ default: '',
},
projectId: {
- type: String,
- required: true,
+ default: '',
},
projectName: {
- type: String,
- required: true,
+ default: '',
},
projectPath: {
- type: String,
- required: true,
+ default: '',
},
projectDescription: {
- type: String,
- required: false,
default: '',
},
projectVisibility: {
- type: String,
- required: true,
+ default: '',
},
restrictedVisibilityLevels: {
- type: Array,
- required: true,
+ default: [],
},
},
data() {
diff --git a/app/assets/javascripts/pages/projects/forks/new/components/fork_groups_list.vue b/app/assets/javascripts/pages/projects/forks/new/components/fork_groups_list.vue
deleted file mode 100644
index 10753de6cd0..00000000000
--- a/app/assets/javascripts/pages/projects/forks/new/components/fork_groups_list.vue
+++ /dev/null
@@ -1,93 +0,0 @@
-<script>
-import { GlTabs, GlTab, GlLoadingIcon, GlSearchBoxByType } from '@gitlab/ui';
-import createFlash from '~/flash';
-import axios from '~/lib/utils/axios_utils';
-import { __ } from '~/locale';
-import ForkGroupsListItem from './fork_groups_list_item.vue';
-
-export default {
- components: {
- GlTabs,
- GlTab,
- GlLoadingIcon,
- GlSearchBoxByType,
- ForkGroupsListItem,
- },
- props: {
- endpoint: {
- type: String,
- required: true,
- },
- },
- data() {
- return {
- namespaces: null,
- filter: '',
- };
- },
- computed: {
- filteredNamespaces() {
- return this.namespaces.filter((n) =>
- n.name.toLowerCase().includes(this.filter.toLowerCase()),
- );
- },
- },
-
- mounted() {
- this.loadGroups();
- },
-
- methods: {
- loadGroups() {
- axios
- .get(this.endpoint)
- .then((response) => {
- this.namespaces = response.data.namespaces;
- })
- .catch(() =>
- createFlash({
- message: __('There was a problem fetching groups.'),
- }),
- );
- },
- },
-
- i18n: {
- searchPlaceholder: __('Search by name'),
- },
-};
-</script>
-<template>
- <gl-tabs class="fork-groups">
- <gl-tab :title="__('Groups and subgroups')">
- <gl-loading-icon v-if="!namespaces" size="md" class="gl-mt-3" />
- <template v-else-if="namespaces.length === 0">
- <div class="gl-text-center">
- <div class="h5">{{ __('No available groups to fork the project.') }}</div>
- <p class="gl-mt-5">
- {{ __('You must have permission to create a project in a group before forking.') }}
- </p>
- </div>
- </template>
- <div v-else-if="filteredNamespaces.length === 0" class="gl-text-center gl-mt-3">
- {{ s__('GroupsTree|No groups matched your search') }}
- </div>
- <ul v-else class="groups-list group-list-tree">
- <fork-groups-list-item
- v-for="(namespace, index) in filteredNamespaces"
- :key="index"
- :group="namespace"
- />
- </ul>
- </gl-tab>
- <template #tabs-end>
- <gl-search-box-by-type
- v-if="namespaces && namespaces.length"
- v-model="filter"
- :placeholder="$options.i18n.searchPlaceholder"
- class="gl-align-self-center gl-ml-auto fork-filtered-search"
- data-qa-selector="fork_groups_list_search_field"
- />
- </template>
- </gl-tabs>
-</template>
diff --git a/app/assets/javascripts/pages/projects/forks/new/components/fork_groups_list_item.vue b/app/assets/javascripts/pages/projects/forks/new/components/fork_groups_list_item.vue
deleted file mode 100644
index d41488acf46..00000000000
--- a/app/assets/javascripts/pages/projects/forks/new/components/fork_groups_list_item.vue
+++ /dev/null
@@ -1,148 +0,0 @@
-<script>
-import {
- GlLink,
- GlButton,
- GlIcon,
- GlAvatar,
- GlTooltipDirective,
- GlTooltip,
- GlBadge,
- GlSafeHtmlDirective as SafeHtml,
-} from '@gitlab/ui';
-import { VISIBILITY_TYPE_ICON, GROUP_VISIBILITY_TYPE } from '~/groups/constants';
-import csrf from '~/lib/utils/csrf';
-import UserAccessRoleBadge from '~/vue_shared/components/user_access_role_badge.vue';
-
-export default {
- components: {
- GlIcon,
- GlAvatar,
- GlBadge,
- GlButton,
- GlTooltip,
- GlLink,
- UserAccessRoleBadge,
- },
- directives: {
- GlTooltip: GlTooltipDirective,
- SafeHtml,
- },
- props: {
- group: {
- type: Object,
- required: true,
- },
- },
- data() {
- return { namespaces: null, isForking: false };
- },
-
- computed: {
- rowClass() {
- return {
- 'has-description': this.group.description,
- 'being-removed': this.isGroupPendingRemoval,
- };
- },
- isGroupPendingRemoval() {
- return this.group.marked_for_deletion;
- },
- hasForkedProject() {
- return Boolean(this.group.forked_project_path);
- },
- visibilityIcon() {
- return VISIBILITY_TYPE_ICON[this.group.visibility];
- },
- visibilityTooltip() {
- return GROUP_VISIBILITY_TYPE[this.group.visibility];
- },
- isSelectButtonDisabled() {
- return !this.group.can_create_project;
- },
- },
-
- methods: {
- fork() {
- this.isForking = true;
- this.$refs.form.submit();
- },
- },
-
- csrf,
-};
-</script>
-<template>
- <li :class="rowClass" class="group-row">
- <div class="group-row-contents gl-display-flex gl-align-items-center gl-py-3 gl-pr-5">
- <div
- class="folder-toggle-wrap gl-mr-3 gl-display-flex gl-align-items-center gl-text-gray-500"
- >
- <gl-icon name="folder-o" />
- </div>
- <gl-link
- :href="group.relative_path"
- class="gl-display-none gl-flex-shrink-0 gl-sm-display-flex gl-mr-3"
- >
- <gl-avatar :size="32" shape="rect" :entity-name="group.name" :src="group.avatarUrl" />
- </gl-link>
- <div class="gl-min-w-0 gl-display-flex gl-flex-grow-1 gl-flex-shrink-1 gl-align-items-center">
- <div class="gl-min-w-0 gl-flex-grow-1 flex-shrink-1">
- <div class="title gl-display-flex gl-align-items-center gl-flex-wrap gl-mr-3">
- <gl-link :href="group.relative_path" class="gl-mt-3 gl-mr-3 gl-text-gray-900!">
- {{ group.full_name }}
- </gl-link>
- <gl-icon
- v-gl-tooltip.hover.bottom
- class="gl-display-inline-flex gl-mt-3 gl-mr-3 gl-text-gray-500"
- :name="visibilityIcon"
- :title="visibilityTooltip"
- />
- <gl-badge
- v-if="isGroupPendingRemoval"
- variant="warning"
- class="gl-display-none gl-sm-display-flex gl-mt-3 gl-mr-1"
- >{{ __('pending deletion') }}</gl-badge
- >
- <user-access-role-badge v-if="group.permission" class="gl-mt-3">
- {{ group.permission }}
- </user-access-role-badge>
- </div>
- <div v-if="group.description" class="description gl-line-height-20">
- <span v-safe-html="group.markdown_description"> </span>
- </div>
- </div>
- <div class="gl-display-flex gl-flex-shrink-0">
- <gl-button
- v-if="hasForkedProject"
- class="gl-h-7 gl-text-decoration-none!"
- :href="group.forked_project_path"
- >{{ __('Go to fork') }}</gl-button
- >
- <template v-else>
- <div ref="selectButtonWrapper">
- <form ref="form" method="POST" :action="group.fork_path">
- <input type="hidden" name="authenticity_token" :value="$options.csrf.token" />
- <gl-button
- type="submit"
- class="gl-h-7"
- :data-qa-name="group.full_name"
- category="secondary"
- variant="success"
- :disabled="isSelectButtonDisabled"
- :loading="isForking"
- @click="fork"
- >{{ __('Select') }}</gl-button
- >
- </form>
- </div>
- <gl-tooltip v-if="isSelectButtonDisabled" :target="() => $refs.selectButtonWrapper">
- {{
- __('You must have permission to create a project in a namespace before forking.')
- }}
- </gl-tooltip>
- </template>
- </div>
- </div>
- </div>
- </li>
-</template>
diff --git a/app/assets/javascripts/pages/projects/forks/new/index.js b/app/assets/javascripts/pages/projects/forks/new/index.js
index 1a171252048..cbf74f755e7 100644
--- a/app/assets/javascripts/pages/projects/forks/new/index.js
+++ b/app/assets/javascripts/pages/projects/forks/new/index.js
@@ -1,61 +1,42 @@
import Vue from 'vue';
import App from './components/app.vue';
-import ForkGroupsList from './components/fork_groups_list.vue';
const mountElement = document.getElementById('fork-groups-mount-element');
-if (gon.features.forkProjectForm) {
- const {
- forkIllustration,
- endpoint,
+const {
+ forkIllustration,
+ endpoint,
+ newGroupPath,
+ projectFullPath,
+ visibilityHelpPath,
+ projectId,
+ projectName,
+ projectPath,
+ projectDescription,
+ projectVisibility,
+ restrictedVisibilityLevels,
+} = mountElement.dataset;
+
+// eslint-disable-next-line no-new
+new Vue({
+ el: mountElement,
+ provide: {
newGroupPath,
- projectFullPath,
visibilityHelpPath,
+ endpoint,
+ projectFullPath,
projectId,
projectName,
projectPath,
projectDescription,
projectVisibility,
- restrictedVisibilityLevels,
- } = mountElement.dataset;
-
- // eslint-disable-next-line no-new
- new Vue({
- el: mountElement,
- provide: {
- newGroupPath,
- visibilityHelpPath,
- },
- render(h) {
- return h(App, {
- props: {
- forkIllustration,
- endpoint,
- newGroupPath,
- projectFullPath,
- visibilityHelpPath,
- projectId,
- projectName,
- projectPath,
- projectDescription,
- projectVisibility,
- restrictedVisibilityLevels: JSON.parse(restrictedVisibilityLevels),
- },
- });
- },
- });
-} else {
- const { endpoint } = mountElement.dataset;
-
- // eslint-disable-next-line no-new
- new Vue({
- el: mountElement,
- render(h) {
- return h(ForkGroupsList, {
- props: {
- endpoint,
- },
- });
- },
- });
-}
+ restrictedVisibilityLevels: JSON.parse(restrictedVisibilityLevels),
+ },
+ render(h) {
+ return h(App, {
+ props: {
+ forkIllustration,
+ },
+ });
+ },
+});
diff --git a/app/assets/javascripts/security_configuration/constants.js b/app/assets/javascripts/security_configuration/constants.js
index 86cc3a9c2f9..ccb4b0d884c 100644
--- a/app/assets/javascripts/security_configuration/constants.js
+++ b/app/assets/javascripts/security_configuration/constants.js
@@ -1,5 +1,5 @@
export const TRACK_TOGGLE_TRAINING_PROVIDER_ACTION = 'toggle_security_training_provider';
export const TRACK_TOGGLE_TRAINING_PROVIDER_LABEL = 'update_security_training_provider';
-
+export const TRACK_CLICK_TRAINING_LINK = 'click_security_training_link';
export const TRACK_PROVIDER_LEARN_MORE_CLICK_ACTION = 'click_link';
export const TRACK_PROVIDER_LEARN_MORE_CLICK_LABEL = 'security_training_provider';
diff --git a/app/controllers/projects/forks_controller.rb b/app/controllers/projects/forks_controller.rb
index 475c41eec9c..3208a5076e7 100644
--- a/app/controllers/projects/forks_controller.rb
+++ b/app/controllers/projects/forks_controller.rb
@@ -17,10 +17,6 @@ class Projects::ForksController < Projects::ApplicationController
feature_category :source_code_management
urgency :low, [:index]
- before_action do
- push_frontend_feature_flag(:fork_project_form, @project, default_enabled: :yaml)
- end
-
def index
@sort = forks_params[:sort]
@@ -54,9 +50,7 @@ class Projects::ForksController < Projects::ApplicationController
format.json do
namespaces = load_namespaces_with_associations - [project.namespace]
- namespaces = [current_user.namespace] + namespaces if
- Feature.enabled?(:fork_project_form, project, default_enabled: :yaml) &&
- can_fork_to?(current_user.namespace)
+ namespaces = [current_user.namespace] + namespaces if can_fork_to?(current_user.namespace)
render json: {
namespaces: ForkNamespaceSerializer.new.represent(
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 29540cbde2f..5baf286d860 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -1016,8 +1016,23 @@ class MergeRequest < ApplicationRecord
merge_request_diff.persisted? || create_merge_request_diff
end
- def create_merge_request_diff
+ def eager_fetch_ref!
+ return unless valid?
+
+ # has_internal_id normally attempts to allocate the iid in the
+ # before_create hook, but we need the iid to be available before
+ # that to fetch the ref into the target project.
+ track_target_project_iid!
+ ensure_target_project_iid!
+
fetch_ref!
+ # Prevent the after_create hook from fetching the source branch again
+ # Drop this field after rollout in https://gitlab.com/gitlab-org/gitlab/-/issues/353044.
+ @skip_fetch_ref = true
+ end
+
+ def create_merge_request_diff
+ fetch_ref! unless skip_fetch_ref
# n+1: https://gitlab.com/gitlab-org/gitlab/-/issues/19377
Gitlab::GitalyClient.allow_n_plus_1_calls do
@@ -1950,6 +1965,8 @@ class MergeRequest < ApplicationRecord
private
+ attr_accessor :skip_fetch_ref
+
def set_draft_status
self.draft = draft?
end
diff --git a/app/serializers/fork_namespace_entity.rb b/app/serializers/fork_namespace_entity.rb
index 2be37d23a05..997abb0f148 100644
--- a/app/serializers/fork_namespace_entity.rb
+++ b/app/serializers/fork_namespace_entity.rb
@@ -30,14 +30,6 @@ class ForkNamespaceEntity < Grape::Entity
markdown_description(namespace)
end
- expose :can_create_project do |namespace, options|
- if Feature.enabled?(:fork_project_form, options[:project], default_enabled: :yaml)
- true
- else
- options[:current_user].can?(:create_projects, namespace)
- end
- end
-
private
# rubocop: disable CodeReuse/ActiveRecord
diff --git a/app/services/merge_requests/create_service.rb b/app/services/merge_requests/create_service.rb
index c1292d924b2..66899e93299 100644
--- a/app/services/merge_requests/create_service.rb
+++ b/app/services/merge_requests/create_service.rb
@@ -31,6 +31,16 @@ module MergeRequests
private
+ def before_create(merge_request)
+ # If the fetching of the source branch occurs in an ActiveRecord
+ # callback (e.g. after_create), a database transaction will be
+ # open while the Gitaly RPC waits. To avoid an idle in transaction
+ # timeout, we do this before we attempt to save the merge request.
+ if Feature.enabled?(:merge_request_eager_fetch_ref, @project, default_enabled: :yaml)
+ merge_request.eager_fetch_ref!
+ end
+ end
+
def set_projects!
# @project is used to determine whether the user can set the merge request's
# assignee, milestone and labels. Whether they can depends on their
diff --git a/app/services/projects/container_repository/third_party/delete_tags_service.rb b/app/services/projects/container_repository/third_party/delete_tags_service.rb
index 404642acf72..4184c676fc3 100644
--- a/app/services/projects/container_repository/third_party/delete_tags_service.rb
+++ b/app/services/projects/container_repository/third_party/delete_tags_service.rb
@@ -41,14 +41,12 @@ module Projects
# update the manifests of the tags with the new dummy image
def replace_tag_manifests(dummy_manifest)
- deleted_tags = {}
-
- @tag_names.each do |name|
+ deleted_tags = @tag_names.map do |name|
digest = @container_repository.client.put_tag(@container_repository.path, name, dummy_manifest)
next unless digest
- deleted_tags[name] = digest
- end
+ [name, digest]
+ end.compact.to_h
# make sure the digests are the same (it should always be)
digests = deleted_tags.values.uniq
diff --git a/app/services/system_notes/issuables_service.rb b/app/services/system_notes/issuables_service.rb
index 09f36bb6501..91f9acf094b 100644
--- a/app/services/system_notes/issuables_service.rb
+++ b/app/services/system_notes/issuables_service.rb
@@ -160,6 +160,7 @@ module SystemNotes
body = "changed title from **#{marked_old_title}** to **#{marked_new_title}**"
issue_activity_counter.track_issue_title_changed_action(author: author) if noteable.is_a?(Issue)
+ work_item_activity_counter.track_work_item_title_changed_action(author: author) if noteable.is_a?(WorkItem)
create_note(NoteSummary.new(noteable, project, author, body, action: 'title'))
end
@@ -484,6 +485,10 @@ module SystemNotes
Gitlab::UsageDataCounters::IssueActivityUniqueCounter
end
+ def work_item_activity_counter
+ Gitlab::UsageDataCounters::WorkItemActivityUniqueCounter
+ end
+
def track_cross_reference_action
issue_activity_counter.track_issue_cross_referenced_action(author: author) if noteable.is_a?(Issue)
end
diff --git a/app/views/projects/forks/_fork_button.html.haml b/app/views/projects/forks/_fork_button.html.haml
deleted file mode 100644
index 84259890a44..00000000000
--- a/app/views/projects/forks/_fork_button.html.haml
+++ /dev/null
@@ -1,20 +0,0 @@
-- avatar = namespace_icon(namespace, 100)
-- can_create_project = current_user.can?(:create_projects, namespace)
-
-.bordered-box.fork-thumbnail.text-center.gl-m-3.gl-pb-5{ class: ("disabled" unless can_create_project) }
- - if /no_((\w*)_)*avatar/.match(avatar)
- = group_icon(namespace, class: "avatar rect-avatar s100 identicon mx-auto")
- - else
- .avatar-container.s100.mx-auto.gl-mt-5
- = image_tag(avatar, class: "avatar s100")
- %h5.gl-mt-3
- = namespace.human_name
- - if forked_project = namespace.find_fork_of(@project)
- = link_to _("Go to project"), project_path(forked_project), class: "btn gl-button btn-default"
- - else
- %div{ class: ('has-tooltip' unless can_create_project),
- title: (_('You have reached your project limit') unless can_create_project) }
- = link_to _("Select"), project_forks_path(@project, namespace_key: namespace.id),
- data: { qa_selector: 'fork_namespace_button', qa_name: namespace.human_name },
- method: "POST",
- class: ["btn gl-button btn-confirm", ("disabled" unless can_create_project)]
diff --git a/app/views/projects/forks/new.html.haml b/app/views/projects/forks/new.html.haml
index 8848fbae9cb..7243852e1f5 100644
--- a/app/views/projects/forks/new.html.haml
+++ b/app/views/projects/forks/new.html.haml
@@ -1,30 +1,13 @@
- page_title s_("ForkProject|Fork project")
-- if Feature.enabled?(:fork_project_form, @project, default_enabled: :yaml)
- #fork-groups-mount-element{ data: { fork_illustration: image_path('illustrations/project-create-new-sm.svg'),
- endpoint: new_project_fork_path(@project, format: :json),
- new_group_path: new_group_path,
- project_full_path: project_path(@project),
- visibility_help_path: help_page_path("public_access/public_access"),
- project_id: @project.id,
- project_name: @project.name,
- project_path: @project.path,
- project_description: @project.description,
- project_visibility: @project.visibility,
- restricted_visibility_levels: Gitlab::CurrentSettings.restricted_visibility_levels.to_json } }
-- else
- .row.gl-mt-3
- .col-lg-3
- %h4.gl-mt-0
- = s_("ForkProject|Fork project")
- %p
- = s_("ForkProject|A fork is a copy of a project.")
- %br
- = s_('ForkProject|Forking a repository allows you to make changes without affecting the original project.')
- .col-lg-9
- - if @own_namespace.present?
- .fork-thumbnail-container.js-fork-content
- %h5.gl-mt-0.gl-mb-0.gl-ml-3.gl-mr-3
- = s_("ForkProject|Select a namespace to fork the project")
- = render 'fork_button', namespace: @own_namespace
- #fork-groups-mount-element{ data: { endpoint: new_project_fork_path(@project, format: :json) } }
+#fork-groups-mount-element{ data: { fork_illustration: image_path('illustrations/project-create-new-sm.svg'),
+ endpoint: new_project_fork_path(@project, format: :json),
+ new_group_path: new_group_path,
+ project_full_path: project_path(@project),
+ visibility_help_path: help_page_path("public_access/public_access"),
+ project_id: @project.id,
+ project_name: @project.name,
+ project_path: @project.path,
+ project_description: @project.description,
+ project_visibility: @project.visibility,
+ restricted_visibility_levels: Gitlab::CurrentSettings.restricted_visibility_levels.to_json } }