Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-01-21 15:09:05 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2021-01-21 15:09:05 +0300
commitcbf0385d17af276030868aa260af02f3f90ee683 (patch)
treef2953cb7d17d319ee917c67937d79f341c3be3d0 /app
parentea037b91577f1b645267df9e034f6da3e389626c (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/groups/members/constants.js4
-rw-r--r--app/assets/javascripts/groups/members/utils.js47
-rw-r--r--app/assets/javascripts/ide/components/commit_sidebar/form.vue79
-rw-r--r--app/assets/javascripts/ide/components/ide.vue15
-rw-r--r--app/assets/javascripts/ide/constants.js7
-rw-r--r--app/assets/javascripts/ide/stores/getters.js3
-rw-r--r--app/assets/javascripts/members/components/app.vue (renamed from app/assets/javascripts/groups/members/components/app.vue)8
-rw-r--r--app/assets/javascripts/members/constants.js5
-rw-r--r--app/assets/javascripts/members/index.js (renamed from app/assets/javascripts/groups/members/index.js)6
-rw-r--r--app/assets/javascripts/members/utils.js46
-rw-r--r--app/assets/javascripts/pages/groups/group_members/index.js20
-rw-r--r--app/assets/javascripts/performance_bar/performance_bar_log.js2
-rw-r--r--app/assets/stylesheets/page_bundles/oncall_schedules.scss6
-rw-r--r--app/helpers/groups/group_members_helper.rb10
-rw-r--r--app/models/clusters/applications/runner.rb2
-rw-r--r--app/services/projects/cleanup_service.rb2
-rw-r--r--app/services/repositories/housekeeping_service.rb2
-rw-r--r--app/views/groups/group_members/index.html.haml2
-rw-r--r--app/views/layouts/welcome.html.haml2
-rw-r--r--app/views/projects/_merge_request_merge_options_settings.html.haml2
-rw-r--r--app/workers/all_queues.yml8
-rw-r--r--app/workers/git_garbage_collect_worker.rb118
-rw-r--r--app/workers/projects/git_garbage_collect_worker.rb132
23 files changed, 310 insertions, 218 deletions
diff --git a/app/assets/javascripts/groups/members/constants.js b/app/assets/javascripts/groups/members/constants.js
index 6d71b666d7a..3315712891d 100644
--- a/app/assets/javascripts/groups/members/constants.js
+++ b/app/assets/javascripts/groups/members/constants.js
@@ -1,5 +1 @@
export const GROUP_MEMBER_BASE_PROPERTY_NAME = 'group_member';
-export const GROUP_MEMBER_ACCESS_LEVEL_PROPERTY_NAME = 'access_level';
-
-export const GROUP_LINK_BASE_PROPERTY_NAME = 'group_link';
-export const GROUP_LINK_ACCESS_LEVEL_PROPERTY_NAME = 'group_access';
diff --git a/app/assets/javascripts/groups/members/utils.js b/app/assets/javascripts/groups/members/utils.js
index 4fcf348b69f..71918bfe9f0 100644
--- a/app/assets/javascripts/groups/members/utils.js
+++ b/app/assets/javascripts/groups/members/utils.js
@@ -1,45 +1,8 @@
-import { isUndefined } from 'lodash';
-import { convertObjectPropsToCamelCase, parseBoolean } from '~/lib/utils/common_utils';
-import {
- GROUP_MEMBER_BASE_PROPERTY_NAME,
- GROUP_MEMBER_ACCESS_LEVEL_PROPERTY_NAME,
- GROUP_LINK_BASE_PROPERTY_NAME,
- GROUP_LINK_ACCESS_LEVEL_PROPERTY_NAME,
-} from './constants';
-
-export const parseDataAttributes = (el) => {
- const { members, groupId, memberPath, canManageMembers } = el.dataset;
-
- return {
- members: convertObjectPropsToCamelCase(JSON.parse(members), { deep: true }),
- sourceId: parseInt(groupId, 10),
- memberPath,
- canManageMembers: parseBoolean(canManageMembers),
- };
-};
-
-const baseRequestFormatter = (basePropertyName, accessLevelPropertyName) => ({
- accessLevel,
- ...otherProperties
-}) => {
- const accessLevelProperty = !isUndefined(accessLevel)
- ? { [accessLevelPropertyName]: accessLevel }
- : {};
+import { baseRequestFormatter } from '~/members/utils';
+import { MEMBER_ACCESS_LEVEL_PROPERTY_NAME } from '~/members/constants';
+import { GROUP_MEMBER_BASE_PROPERTY_NAME } from './constants';
- return {
- [basePropertyName]: {
- ...accessLevelProperty,
- ...otherProperties,
- },
- };
-};
-
-export const memberRequestFormatter = baseRequestFormatter(
+export const groupMemberRequestFormatter = baseRequestFormatter(
GROUP_MEMBER_BASE_PROPERTY_NAME,
- GROUP_MEMBER_ACCESS_LEVEL_PROPERTY_NAME,
-);
-
-export const groupLinkRequestFormatter = baseRequestFormatter(
- GROUP_LINK_BASE_PROPERTY_NAME,
- GROUP_LINK_ACCESS_LEVEL_PROPERTY_NAME,
+ MEMBER_ACCESS_LEVEL_PROPERTY_NAME,
);
diff --git a/app/assets/javascripts/ide/components/commit_sidebar/form.vue b/app/assets/javascripts/ide/components/commit_sidebar/form.vue
index fb4785a6f8c..0d24ab1f01e 100644
--- a/app/assets/javascripts/ide/components/commit_sidebar/form.vue
+++ b/app/assets/javascripts/ide/components/commit_sidebar/form.vue
@@ -1,13 +1,17 @@
<script>
import { mapState, mapActions, mapGetters } from 'vuex';
-import { GlModal, GlSafeHtmlDirective, GlButton } from '@gitlab/ui';
-import { n__ } from '~/locale';
+import { GlModal, GlSafeHtmlDirective, GlButton, GlTooltipDirective } from '@gitlab/ui';
+import { n__, s__ } from '~/locale';
import CommitMessageField from './message_field.vue';
import Actions from './actions.vue';
import SuccessMessage from './success_message.vue';
import { leftSidebarViews, MAX_WINDOW_HEIGHT_COMPACT } from '../../constants';
import { createUnexpectedCommitError } from '../../lib/errors';
+const MSG_CANNOT_PUSH_CODE = s__(
+ 'WebIDE|You need permission to edit files directly in this project.',
+);
+
export default {
components: {
Actions,
@@ -18,6 +22,7 @@ export default {
},
directives: {
SafeHtml: GlSafeHtmlDirective,
+ GlTooltip: GlTooltipDirective,
},
data() {
return {
@@ -30,8 +35,18 @@ export default {
computed: {
...mapState(['changedFiles', 'stagedFiles', 'currentActivityView', 'lastCommitMsg']),
...mapState('commit', ['commitMessage', 'submitCommitLoading', 'commitError']),
- ...mapGetters(['someUncommittedChanges']),
+ ...mapGetters(['someUncommittedChanges', 'canPushCode']),
...mapGetters('commit', ['discardDraftButtonDisabled', 'preBuiltCommitMessage']),
+ commitButtonDisabled() {
+ return !this.canPushCode || !this.someUncommittedChanges;
+ },
+ commitButtonTooltip() {
+ if (!this.canPushCode) {
+ return MSG_CANNOT_PUSH_CODE;
+ }
+
+ return '';
+ },
overviewText() {
return n__('%d changed file', '%d changed files', this.stagedFiles.length);
},
@@ -69,6 +84,12 @@ export default {
'updateCommitAction',
]),
commit() {
+ // Even though the submit button will be disabled, we need to disable the submission
+ // since hitting enter on the branch name text input also submits the form.
+ if (!this.canPushCode) {
+ return false;
+ }
+
return this.commitChanges();
},
handleCompactState() {
@@ -109,6 +130,8 @@ export default {
this.componentHeight = null;
},
},
+ // Expose for tests
+ MSG_CANNOT_PUSH_CODE,
};
</script>
@@ -130,17 +153,22 @@ export default {
@after-enter="afterEndTransition"
>
<div v-if="isCompact" ref="compactEl" class="commit-form-compact">
- <gl-button
- :disabled="!someUncommittedChanges"
- category="primary"
- variant="info"
- block
- class="qa-begin-commit-button"
- data-testid="begin-commit-button"
- @click="beginCommit"
+ <div
+ v-gl-tooltip="{ title: commitButtonTooltip }"
+ data-testid="begin-commit-button-tooltip"
>
- {{ __('Commit…') }}
- </gl-button>
+ <gl-button
+ :disabled="commitButtonDisabled"
+ category="primary"
+ variant="info"
+ block
+ class="qa-begin-commit-button"
+ data-testid="begin-commit-button"
+ @click="beginCommit"
+ >
+ {{ __('Commit…') }}
+ </gl-button>
+ </div>
<p class="text-center bold">{{ overviewText }}</p>
</div>
<form v-else ref="formEl" @submit.prevent.stop="commit">
@@ -153,16 +181,23 @@ export default {
/>
<div class="clearfix gl-mt-5">
<actions />
- <gl-button
- :loading="submitCommitLoading"
- class="float-left qa-commit-button"
- data-testid="commit-button"
- category="primary"
- variant="success"
- @click="commit"
+ <div
+ v-gl-tooltip="{ title: commitButtonTooltip }"
+ class="float-left"
+ data-testid="commit-button-tooltip"
>
- {{ __('Commit') }}
- </gl-button>
+ <gl-button
+ :disabled="commitButtonDisabled"
+ :loading="submitCommitLoading"
+ data-testid="commit-button"
+ class="qa-commit-button"
+ category="primary"
+ variant="success"
+ @click="commit"
+ >
+ {{ __('Commit') }}
+ </gl-button>
+ </div>
<gl-button
v-if="!discardDraftButtonDisabled"
class="float-right"
diff --git a/app/assets/javascripts/ide/components/ide.vue b/app/assets/javascripts/ide/components/ide.vue
index aac899fde0d..644c79f2aab 100644
--- a/app/assets/javascripts/ide/components/ide.vue
+++ b/app/assets/javascripts/ide/components/ide.vue
@@ -1,7 +1,7 @@
<script>
import { mapActions, mapGetters, mapState } from 'vuex';
-import { GlButton, GlLoadingIcon } from '@gitlab/ui';
-import { __ } from '~/locale';
+import { GlAlert, GlButton, GlLoadingIcon } from '@gitlab/ui';
+import { __, s__ } from '~/locale';
import {
WEBIDE_MARK_APP_START,
WEBIDE_MARK_FILE_FINISH,
@@ -26,10 +26,15 @@ eventHub.$on(WEBIDE_MEASURE_FILE_AFTER_INTERACTION, () =>
),
);
+const MSG_CANNOT_PUSH_CODE = s__(
+ 'WebIDE|You need permission to edit files directly in this project. Fork this project to make your changes and submit a merge request.',
+);
+
export default {
components: {
IdeSidebar,
RepoEditor,
+ GlAlert,
GlButton,
GlLoadingIcon,
ErrorMessage: () => import(/* webpackChunkName: 'ide_runtime' */ './error_message.vue'),
@@ -59,12 +64,14 @@ export default {
'loading',
]),
...mapGetters([
+ 'canPushCode',
'activeFile',
'someUncommittedChanges',
'isCommitModeActive',
'allBlobs',
'emptyRepo',
'currentTree',
+ 'hasCurrentProject',
'editorTheme',
'getUrlForPath',
]),
@@ -110,6 +117,7 @@ export default {
this.loadDeferred = true;
},
},
+ MSG_CANNOT_PUSH_CODE,
};
</script>
@@ -118,6 +126,9 @@ export default {
class="ide position-relative d-flex flex-column align-items-stretch"
:class="{ [`theme-${themeName}`]: themeName }"
>
+ <gl-alert v-if="!canPushCode" :dismissible="false">{{
+ $options.MSG_CANNOT_PUSH_CODE
+ }}</gl-alert>
<error-message v-if="errorMessage" :message="errorMessage" />
<div class="ide-view flex-grow d-flex">
<template v-if="loadDeferred">
diff --git a/app/assets/javascripts/ide/constants.js b/app/assets/javascripts/ide/constants.js
index e5618466395..6bd74b143e2 100644
--- a/app/assets/javascripts/ide/constants.js
+++ b/app/assets/javascripts/ide/constants.js
@@ -16,6 +16,13 @@ export const PERMISSION_CREATE_MR = 'createMergeRequestIn';
export const PERMISSION_READ_MR = 'readMergeRequest';
export const PERMISSION_PUSH_CODE = 'pushCode';
+// The default permission object to use when the project data isn't available yet.
+// This helps us encapsulate checks like `canPushCode` without requiring an
+// additional check like `currentProject && canPushCode`.
+export const DEFAULT_PERMISSIONS = {
+ [PERMISSION_PUSH_CODE]: true,
+};
+
export const viewerTypes = {
mr: 'mrdiff',
edit: 'editor',
diff --git a/app/assets/javascripts/ide/stores/getters.js b/app/assets/javascripts/ide/stores/getters.js
index 59e8d37a92a..697fa02bcb4 100644
--- a/app/assets/javascripts/ide/stores/getters.js
+++ b/app/assets/javascripts/ide/stores/getters.js
@@ -2,6 +2,7 @@ import { getChangesCountForFiles, filePathMatches } from './utils';
import {
leftSidebarViews,
packageJsonPath,
+ DEFAULT_PERMISSIONS,
PERMISSION_READ_MR,
PERMISSION_CREATE_MR,
PERMISSION_PUSH_CODE,
@@ -150,7 +151,7 @@ export const getDiffInfo = (state, getters) => (path) => {
};
export const findProjectPermissions = (state, getters) => (projectId) =>
- getters.findProject(projectId)?.userPermissions || {};
+ getters.findProject(projectId)?.userPermissions || DEFAULT_PERMISSIONS;
export const canReadMergeRequests = (state, getters) =>
Boolean(getters.findProjectPermissions(state.currentProjectId)[PERMISSION_READ_MR]);
diff --git a/app/assets/javascripts/groups/members/components/app.vue b/app/assets/javascripts/members/components/app.vue
index 34a2c67fa9f..c08c36d1b30 100644
--- a/app/assets/javascripts/groups/members/components/app.vue
+++ b/app/assets/javascripts/members/components/app.vue
@@ -1,13 +1,13 @@
<script>
import { mapState, mapMutations } from 'vuex';
import { GlAlert } from '@gitlab/ui';
-import MembersTable from '~/members/components/table/members_table.vue';
-import FilterSortContainer from '~/members/components/filter_sort/filter_sort_container.vue';
+import MembersTable from './table/members_table.vue';
+import FilterSortContainer from './filter_sort/filter_sort_container.vue';
+import { HIDE_ERROR } from '../store/mutation_types';
import { scrollToElement } from '~/lib/utils/common_utils';
-import { HIDE_ERROR } from '~/members/store/mutation_types';
export default {
- name: 'GroupMembersApp',
+ name: 'MembersApp',
components: { MembersTable, FilterSortContainer, GlAlert },
computed: {
...mapState(['showError', 'errorMessage']),
diff --git a/app/assets/javascripts/members/constants.js b/app/assets/javascripts/members/constants.js
index 77cb150bff6..f68a8814fee 100644
--- a/app/assets/javascripts/members/constants.js
+++ b/app/assets/javascripts/members/constants.js
@@ -98,3 +98,8 @@ export const REMOVE_GROUP_LINK_MODAL_ID = 'remove-group-link-modal-id';
export const SEARCH_TOKEN_TYPE = 'filtered-search-term';
export const SORT_PARAM = 'sort';
+
+export const MEMBER_ACCESS_LEVEL_PROPERTY_NAME = 'access_level';
+
+export const GROUP_LINK_BASE_PROPERTY_NAME = 'group_link';
+export const GROUP_LINK_ACCESS_LEVEL_PROPERTY_NAME = 'group_access';
diff --git a/app/assets/javascripts/groups/members/index.js b/app/assets/javascripts/members/index.js
index 3ec874b8d36..bd80bb2485b 100644
--- a/app/assets/javascripts/groups/members/index.js
+++ b/app/assets/javascripts/members/index.js
@@ -1,11 +1,11 @@
import Vue from 'vue';
import Vuex from 'vuex';
import { GlToast } from '@gitlab/ui';
-import { parseDataAttributes } from 'ee_else_ce/groups/members/utils';
+import { parseDataAttributes } from 'ee_else_ce/members/utils';
import App from './components/app.vue';
-import membersStore from '~/members/store';
+import membersStore from './store';
-export const initGroupMembersApp = (
+export const initMembersApp = (
el,
{
tableFields = [],
diff --git a/app/assets/javascripts/members/utils.js b/app/assets/javascripts/members/utils.js
index 780b5a9df57..723b371ccb0 100644
--- a/app/assets/javascripts/members/utils.js
+++ b/app/assets/javascripts/members/utils.js
@@ -1,7 +1,17 @@
+import { isUndefined } from 'lodash';
import { __ } from '~/locale';
-import { getParameterByName } from '~/lib/utils/common_utils';
+import {
+ getParameterByName,
+ convertObjectPropsToCamelCase,
+ parseBoolean,
+} from '~/lib/utils/common_utils';
import { setUrlParams } from '~/lib/utils/url_utility';
-import { FIELDS, DEFAULT_SORT } from './constants';
+import {
+ FIELDS,
+ DEFAULT_SORT,
+ GROUP_LINK_BASE_PROPERTY_NAME,
+ GROUP_LINK_ACCESS_LEVEL_PROPERTY_NAME,
+} from './constants';
export const generateBadges = (member, isCurrentUser) => [
{
@@ -95,3 +105,35 @@ export const buildSortHref = ({
// Defined in `ee/app/assets/javascripts/vue_shared/components/members/utils.js`
export const canOverride = () => false;
+
+export const parseDataAttributes = (el) => {
+ const { members, sourceId, memberPath, canManageMembers } = el.dataset;
+
+ return {
+ members: convertObjectPropsToCamelCase(JSON.parse(members), { deep: true }),
+ sourceId: parseInt(sourceId, 10),
+ memberPath,
+ canManageMembers: parseBoolean(canManageMembers),
+ };
+};
+
+export const baseRequestFormatter = (basePropertyName, accessLevelPropertyName) => ({
+ accessLevel,
+ ...otherProperties
+}) => {
+ const accessLevelProperty = !isUndefined(accessLevel)
+ ? { [accessLevelPropertyName]: accessLevel }
+ : {};
+
+ return {
+ [basePropertyName]: {
+ ...accessLevelProperty,
+ ...otherProperties,
+ },
+ };
+};
+
+export const groupLinkRequestFormatter = baseRequestFormatter(
+ GROUP_LINK_BASE_PROPERTY_NAME,
+ GROUP_LINK_ACCESS_LEVEL_PROPERTY_NAME,
+);
diff --git a/app/assets/javascripts/pages/groups/group_members/index.js b/app/assets/javascripts/pages/groups/group_members/index.js
index 5346e3720e8..516311dd841 100644
--- a/app/assets/javascripts/pages/groups/group_members/index.js
+++ b/app/assets/javascripts/pages/groups/group_members/index.js
@@ -3,10 +3,11 @@ import memberExpirationDate from '~/member_expiration_date';
import UsersSelect from '~/users_select';
import groupsSelect from '~/groups_select';
import RemoveMemberModal from '~/vue_shared/components/remove_member_modal.vue';
-import { initGroupMembersApp } from '~/groups/members';
import initInviteMembersModal from '~/invite_members/init_invite_members_modal';
import initInviteMembersTrigger from '~/invite_members/init_invite_members_trigger';
-import { memberRequestFormatter, groupLinkRequestFormatter } from '~/groups/members/utils';
+import { initMembersApp } from '~/members/index';
+import { groupMemberRequestFormatter } from '~/groups/members/utils';
+import { groupLinkRequestFormatter } from '~/members/utils';
import { s__ } from '~/locale';
function mountRemoveMemberModal() {
@@ -25,11 +26,11 @@ function mountRemoveMemberModal() {
const SHARED_FIELDS = ['account', 'expires', 'maxRole', 'expiration', 'actions'];
-initGroupMembersApp(document.querySelector('.js-group-members-list'), {
+initMembersApp(document.querySelector('.js-group-members-list'), {
tableFields: SHARED_FIELDS.concat(['source', 'granted']),
tableAttrs: { tr: { 'data-qa-selector': 'member_row' } },
tableSortableFields: ['account', 'granted', 'maxRole', 'lastSignIn'],
- requestFormatter: memberRequestFormatter,
+ requestFormatter: groupMemberRequestFormatter,
filteredSearchBar: {
show: true,
tokens: ['two_factor', 'with_inherited_permissions'],
@@ -38,7 +39,8 @@ initGroupMembersApp(document.querySelector('.js-group-members-list'), {
recentSearchesStorageKey: 'group_members',
},
});
-initGroupMembersApp(document.querySelector('.js-group-linked-list'), {
+
+initMembersApp(document.querySelector('.js-group-group-links-list'), {
tableFields: SHARED_FIELDS.concat('granted'),
tableAttrs: {
table: { 'data-qa-selector': 'groups_list' },
@@ -46,9 +48,9 @@ initGroupMembersApp(document.querySelector('.js-group-linked-list'), {
},
requestFormatter: groupLinkRequestFormatter,
});
-initGroupMembersApp(document.querySelector('.js-group-invited-members-list'), {
+initMembersApp(document.querySelector('.js-group-invited-members-list'), {
tableFields: SHARED_FIELDS.concat('invited'),
- requestFormatter: memberRequestFormatter,
+ requestFormatter: groupMemberRequestFormatter,
filteredSearchBar: {
show: true,
tokens: [],
@@ -57,9 +59,9 @@ initGroupMembersApp(document.querySelector('.js-group-invited-members-list'), {
recentSearchesStorageKey: 'group_invited_members',
},
});
-initGroupMembersApp(document.querySelector('.js-group-access-requests-list'), {
+initMembersApp(document.querySelector('.js-group-access-requests-list'), {
tableFields: SHARED_FIELDS.concat('requested'),
- requestFormatter: memberRequestFormatter,
+ requestFormatter: groupMemberRequestFormatter,
});
groupsSelect();
diff --git a/app/assets/javascripts/performance_bar/performance_bar_log.js b/app/assets/javascripts/performance_bar/performance_bar_log.js
index c61b0cb32e8..aad99e2604e 100644
--- a/app/assets/javascripts/performance_bar/performance_bar_log.js
+++ b/app/assets/javascripts/performance_bar/performance_bar_log.js
@@ -43,7 +43,7 @@ const logUserTimingMetrics = () => {
const initPerformanceBarLog = () => {
console.log(
`%c ${String.fromCodePoint(0x1f98a)} GitLab performance bar`,
- 'width:100%;background-color: #292961; color: #FFFFFF; font-size:24px; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto; padding: 10px;display:block;padding-right: 100px;',
+ 'width:100%; background-color: #292961; color: #FFFFFF; padding: 10px; display:block;',
);
initVitalsLog();
diff --git a/app/assets/stylesheets/page_bundles/oncall_schedules.scss b/app/assets/stylesheets/page_bundles/oncall_schedules.scss
index 1b190024457..a1d1ff0c42f 100644
--- a/app/assets/stylesheets/page_bundles/oncall_schedules.scss
+++ b/app/assets/stylesheets/page_bundles/oncall_schedules.scss
@@ -37,12 +37,6 @@
&.gl-modal .modal-md {
max-width: 640px;
}
-
- // TODO: move to gitlab/ui utilities
- // https://gitlab.com/gitlab-org/gitlab/-/issues/297502
- .gl-w-fit-content {
- width: fit-content;
- }
}
//// Copied from roadmaps.scss - adapted for on-call schedules
diff --git a/app/helpers/groups/group_members_helper.rb b/app/helpers/groups/group_members_helper.rb
index a4159ed6b19..76f31dc97cb 100644
--- a/app/helpers/groups/group_members_helper.rb
+++ b/app/helpers/groups/group_members_helper.rb
@@ -13,7 +13,7 @@ module Groups::GroupMembersHelper
render 'shared/members/invite_member', submit_url: group_group_members_path(group), access_levels: group.access_level_roles, default_access_level: default_access_level
end
- def linked_groups_data_json(group_links)
+ def group_group_links_data_json(group_links)
GroupGroupLinkSerializer.new.represent(group_links, { current_user: current_user }).to_json
end
@@ -26,16 +26,16 @@ module Groups::GroupMembersHelper
{
members: members_data_json(group, members),
member_path: group_group_member_path(group, ':id'),
- group_id: group.id,
+ source_id: group.id,
can_manage_members: can?(current_user, :admin_group_member, group).to_s
}
end
- def linked_groups_list_data_attributes(group)
+ def group_group_links_list_data_attributes(group)
{
- members: linked_groups_data_json(group.shared_with_group_links),
+ members: group_group_links_data_json(group.shared_with_group_links),
member_path: group_group_link_path(group, ':id'),
- group_id: group.id
+ source_id: group.id
}
end
end
diff --git a/app/models/clusters/applications/runner.rb b/app/models/clusters/applications/runner.rb
index 56acac53e0b..f87eccecf9f 100644
--- a/app/models/clusters/applications/runner.rb
+++ b/app/models/clusters/applications/runner.rb
@@ -3,7 +3,7 @@
module Clusters
module Applications
class Runner < ApplicationRecord
- VERSION = '0.24.0'
+ VERSION = '0.25.0'
self.table_name = 'clusters_applications_runners'
diff --git a/app/services/projects/cleanup_service.rb b/app/services/projects/cleanup_service.rb
index 6e3b320afbe..7bcaee75813 100644
--- a/app/services/projects/cleanup_service.rb
+++ b/app/services/projects/cleanup_service.rb
@@ -40,7 +40,7 @@ module Projects
apply_bfg_object_map!
# Remove older objects that are no longer referenced
- GitGarbageCollectWorker.new.perform(project.id, :prune, "project_cleanup:gc:#{project.id}")
+ Projects::GitGarbageCollectWorker.new.perform(project.id, :prune, "project_cleanup:gc:#{project.id}")
# The cache may now be inaccurate, and holding onto it could prevent
# bugs assuming the presence of some object from manifesting for some
diff --git a/app/services/repositories/housekeeping_service.rb b/app/services/repositories/housekeeping_service.rb
index 6a2fa95d25f..e97c295e18e 100644
--- a/app/services/repositories/housekeeping_service.rb
+++ b/app/services/repositories/housekeeping_service.rb
@@ -45,7 +45,7 @@ module Repositories
private
def execute_gitlab_shell_gc(lease_uuid)
- GitGarbageCollectWorker.perform_async(@resource.id, task, lease_key, lease_uuid)
+ Projects::GitGarbageCollectWorker.perform_async(@resource.id, task, lease_key, lease_uuid)
ensure
if pushes_since_gc >= gc_period
Gitlab::Metrics.measure(:reset_pushes_since_gc) do
diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml
index abdb4553cdd..a5257ff20bc 100644
--- a/app/views/groups/group_members/index.html.haml
+++ b/app/views/groups/group_members/index.html.haml
@@ -66,7 +66,7 @@
= paginate @members, theme: 'gitlab', params: { invited_members_page: nil, search_invited: nil }
- if @group.shared_with_group_links.any?
#tab-groups.tab-pane
- .js-group-linked-list{ data: linked_groups_list_data_attributes(@group) }
+ .js-group-group-links-list{ data: group_group_links_list_data_attributes(@group) }
.loading
.spinner.spinner-md
- if show_invited_members
diff --git a/app/views/layouts/welcome.html.haml b/app/views/layouts/welcome.html.haml
index 48921e9ff89..30ba7f7f230 100644
--- a/app/views/layouts/welcome.html.haml
+++ b/app/views/layouts/welcome.html.haml
@@ -1,7 +1,7 @@
!!! 5
%html.subscriptions-layout-html{ lang: 'en' }
= render 'layouts/head'
- %body.ui-indigo.d-flex.vh-100.gl-bg-gray-10
+ %body.ui-indigo.gl-display-flex.vh-100
= render "layouts/header/logo_with_title"
= render "layouts/broadcast"
.container.d-flex.flex-grow-1.m-0
diff --git a/app/views/projects/_merge_request_merge_options_settings.html.haml b/app/views/projects/_merge_request_merge_options_settings.html.haml
index 8951f2ed22f..80dabeceeb0 100644
--- a/app/views/projects/_merge_request_merge_options_settings.html.haml
+++ b/app/views/projects/_merge_request_merge_options_settings.html.haml
@@ -1,6 +1,6 @@
- form = local_assigns.fetch(:form)
-.form-group
+.form-group#project-merge-options{ data: { project_full_path: @project.full_path } }
%b= s_('ProjectSettings|Merge options')
%p.text-secondary= s_('ProjectSettings|Additional merge request capabilities that influence how and when merges will be performed')
= render_if_exists 'projects/merge_pipelines_settings', form: form
diff --git a/app/workers/all_queues.yml b/app/workers/all_queues.yml
index 4c4a314a1e6..8590e1bc6f7 100644
--- a/app/workers/all_queues.yml
+++ b/app/workers/all_queues.yml
@@ -1999,6 +1999,14 @@
:weight: 1
:idempotent: true
:tags: []
+- :name: projects_git_garbage_collect
+ :feature_category: :gitaly
+ :has_external_dependencies:
+ :urgency: :low
+ :resource_boundary: :unknown
+ :weight: 1
+ :idempotent:
+ :tags: []
- :name: prometheus_create_default_alerts
:feature_category: :incident_management
:has_external_dependencies:
diff --git a/app/workers/git_garbage_collect_worker.rb b/app/workers/git_garbage_collect_worker.rb
index e1dcb16bafb..a2aab23db7b 100644
--- a/app/workers/git_garbage_collect_worker.rb
+++ b/app/workers/git_garbage_collect_worker.rb
@@ -1,5 +1,11 @@
# frozen_string_literal: true
+# According to our docs, we can only remove workers on major releases
+# https://docs.gitlab.com/ee/development/sidekiq_style_guide.html#removing-workers.
+#
+# We need to still maintain this until 14.0 but with the current functionality.
+#
+# In https://gitlab.com/gitlab-org/gitlab/-/issues/299290 we track that removal.
class GitGarbageCollectWorker # rubocop:disable Scalability/IdempotentWorker
include ApplicationWorker
@@ -7,117 +13,7 @@ class GitGarbageCollectWorker # rubocop:disable Scalability/IdempotentWorker
feature_category :gitaly
loggable_arguments 1, 2, 3
- # Timeout set to 24h
- LEASE_TIMEOUT = 86400
-
def perform(project_id, task = :gc, lease_key = nil, lease_uuid = nil)
- lease_key ||= "git_gc:#{task}:#{project_id}"
- project = Project.find(project_id)
- active_uuid = get_lease_uuid(lease_key)
-
- if active_uuid
- return unless active_uuid == lease_uuid
-
- renew_lease(lease_key, active_uuid)
- else
- lease_uuid = try_obtain_lease(lease_key)
-
- return unless lease_uuid
- end
-
- task = task.to_sym
-
- if gc?(task)
- ::Projects::GitDeduplicationService.new(project).execute
- cleanup_orphan_lfs_file_references(project)
- end
-
- gitaly_call(task, project)
-
- # Refresh the branch cache in case garbage collection caused a ref lookup to fail
- flush_ref_caches(project) if gc?(task)
-
- update_repository_statistics(project) if task != :pack_refs
-
- # In case pack files are deleted, release libgit2 cache and open file
- # descriptors ASAP instead of waiting for Ruby garbage collection
- project.cleanup
- ensure
- cancel_lease(lease_key, lease_uuid) if lease_key.present? && lease_uuid.present?
- end
-
- private
-
- def gc?(task)
- task == :gc || task == :prune
- end
-
- def try_obtain_lease(key)
- ::Gitlab::ExclusiveLease.new(key, timeout: LEASE_TIMEOUT).try_obtain
- end
-
- def renew_lease(key, uuid)
- ::Gitlab::ExclusiveLease.new(key, uuid: uuid, timeout: LEASE_TIMEOUT).renew
- end
-
- def cancel_lease(key, uuid)
- ::Gitlab::ExclusiveLease.cancel(key, uuid)
- end
-
- def get_lease_uuid(key)
- ::Gitlab::ExclusiveLease.get_uuid(key)
- end
-
- def gitaly_call(task, project)
- repository = project.repository.raw_repository
-
- client = if task == :pack_refs
- Gitlab::GitalyClient::RefService.new(repository)
- else
- Gitlab::GitalyClient::RepositoryService.new(repository)
- end
-
- case task
- when :prune, :gc
- client.garbage_collect(bitmaps_enabled?, prune: task == :prune)
- when :full_repack
- client.repack_full(bitmaps_enabled?)
- when :incremental_repack
- client.repack_incremental
- when :pack_refs
- client.pack_refs
- end
- rescue GRPC::NotFound => e
- Gitlab::GitLogger.error("#{__method__} failed:\nRepository not found")
- raise Gitlab::Git::Repository::NoRepository.new(e)
- rescue GRPC::BadStatus => e
- Gitlab::GitLogger.error("#{__method__} failed:\n#{e}")
- raise Gitlab::Git::CommandError.new(e)
- end
-
- def cleanup_orphan_lfs_file_references(project)
- return if Gitlab::Database.read_only? # GitGarbageCollectWorker may be run on a Geo secondary
-
- ::Gitlab::Cleanup::OrphanLfsFileReferences.new(project, dry_run: false, logger: logger).run!
- rescue => err
- Gitlab::GitLogger.warn(message: "Cleaning up orphan LFS objects files failed", error: err.message)
- Gitlab::ErrorTracking.track_and_raise_for_dev_exception(err)
- end
-
- def flush_ref_caches(project)
- project.repository.expire_branches_cache
- project.repository.branch_names
- project.repository.has_visible_content?
- end
-
- def update_repository_statistics(project)
- project.repository.expire_statistics_caches
- return if Gitlab::Database.read_only? # GitGarbageCollectWorker may be run on a Geo secondary
-
- Projects::UpdateStatisticsService.new(project, nil, statistics: [:repository_size, :lfs_objects_size]).execute
- end
-
- def bitmaps_enabled?
- Gitlab::CurrentSettings.housekeeping_bitmaps_enabled
+ ::Projects::GitGarbageCollectWorker.new.perform(project_id, task, lease_key, lease_uuid)
end
end
diff --git a/app/workers/projects/git_garbage_collect_worker.rb b/app/workers/projects/git_garbage_collect_worker.rb
new file mode 100644
index 00000000000..aba99ce35d0
--- /dev/null
+++ b/app/workers/projects/git_garbage_collect_worker.rb
@@ -0,0 +1,132 @@
+# frozen_string_literal: true
+
+module Projects
+ class GitGarbageCollectWorker # rubocop:disable Scalability/IdempotentWorker
+ include ApplicationWorker
+
+ sidekiq_options retry: false
+ feature_category :gitaly
+ loggable_arguments 1, 2, 3
+
+ # Timeout set to 24h
+ LEASE_TIMEOUT = 86400
+
+ def perform(project_id, task = :gc, lease_key = nil, lease_uuid = nil)
+ lease_key ||= "git_gc:#{task}:#{project_id}"
+ project = find_project(project_id)
+ active_uuid = get_lease_uuid(lease_key)
+
+ if active_uuid
+ return unless active_uuid == lease_uuid
+
+ renew_lease(lease_key, active_uuid)
+ else
+ lease_uuid = try_obtain_lease(lease_key)
+
+ return unless lease_uuid
+ end
+
+ task = task.to_sym
+
+ if gc?(task)
+ ::Projects::GitDeduplicationService.new(project).execute
+ cleanup_orphan_lfs_file_references(project)
+ end
+
+ gitaly_call(task, project)
+
+ # Refresh the branch cache in case garbage collection caused a ref lookup to fail
+ flush_ref_caches(project) if gc?(task)
+
+ update_repository_statistics(project) if task != :pack_refs
+
+ # In case pack files are deleted, release libgit2 cache and open file
+ # descriptors ASAP instead of waiting for Ruby garbage collection
+ project.cleanup
+ ensure
+ cancel_lease(lease_key, lease_uuid) if lease_key.present? && lease_uuid.present?
+ end
+
+ private
+
+ def find_project(project_id)
+ Project.find(project_id)
+ end
+
+ def gc?(task)
+ task == :gc || task == :prune
+ end
+
+ def try_obtain_lease(key)
+ ::Gitlab::ExclusiveLease.new(key, timeout: LEASE_TIMEOUT).try_obtain
+ end
+
+ def renew_lease(key, uuid)
+ ::Gitlab::ExclusiveLease.new(key, uuid: uuid, timeout: LEASE_TIMEOUT).renew
+ end
+
+ def cancel_lease(key, uuid)
+ ::Gitlab::ExclusiveLease.cancel(key, uuid)
+ end
+
+ def get_lease_uuid(key)
+ ::Gitlab::ExclusiveLease.get_uuid(key)
+ end
+
+ def gitaly_call(task, project)
+ repository = project.repository.raw_repository
+ client = get_gitaly_client(task, repository)
+
+ case task
+ when :prune, :gc
+ client.garbage_collect(bitmaps_enabled?, prune: task == :prune)
+ when :full_repack
+ client.repack_full(bitmaps_enabled?)
+ when :incremental_repack
+ client.repack_incremental
+ when :pack_refs
+ client.pack_refs
+ end
+ rescue GRPC::NotFound => e
+ Gitlab::GitLogger.error("#{__method__} failed:\nRepository not found")
+ raise Gitlab::Git::Repository::NoRepository.new(e)
+ rescue GRPC::BadStatus => e
+ Gitlab::GitLogger.error("#{__method__} failed:\n#{e}")
+ raise Gitlab::Git::CommandError.new(e)
+ end
+
+ def get_gitaly_client(task, repository)
+ if task == :pack_refs
+ Gitlab::GitalyClient::RefService
+ else
+ Gitlab::GitalyClient::RepositoryService
+ end.new(repository)
+ end
+
+ def cleanup_orphan_lfs_file_references(project)
+ return if Gitlab::Database.read_only? # GitGarbageCollectWorker may be run on a Geo secondary
+
+ ::Gitlab::Cleanup::OrphanLfsFileReferences.new(project, dry_run: false, logger: logger).run!
+ rescue => err
+ Gitlab::GitLogger.warn(message: "Cleaning up orphan LFS objects files failed", error: err.message)
+ Gitlab::ErrorTracking.track_and_raise_for_dev_exception(err)
+ end
+
+ def flush_ref_caches(project)
+ project.repository.expire_branches_cache
+ project.repository.branch_names
+ project.repository.has_visible_content?
+ end
+
+ def update_repository_statistics(project)
+ project.repository.expire_statistics_caches
+ return if Gitlab::Database.read_only? # GitGarbageCollectWorker may be run on a Geo secondary
+
+ Projects::UpdateStatisticsService.new(project, nil, statistics: [:repository_size, :lfs_objects_size]).execute
+ end
+
+ def bitmaps_enabled?
+ Gitlab::CurrentSettings.housekeeping_bitmaps_enabled
+ end
+ end
+end