Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-07-22 12:10:04 +00:00
parent 8e0dafbb66
commit cb8bd7d594
143 changed files with 1156 additions and 644 deletions

View File

@ -254,7 +254,6 @@ Gitlab/HTTParty:
Gitlab/Json: Gitlab/Json:
Enabled: true Enabled: true
Exclude: Exclude:
- 'db/**/*'
- 'qa/**/*' - 'qa/**/*'
- 'scripts/**/*' - 'scripts/**/*'
- 'tooling/rspec_flaky/**/*' - 'tooling/rspec_flaky/**/*'

View File

@ -90,45 +90,6 @@ Graphql/OldTypes:
- 'app/graphql/mutations/snippets/create.rb' - 'app/graphql/mutations/snippets/create.rb'
- 'app/graphql/mutations/snippets/update.rb' - 'app/graphql/mutations/snippets/update.rb'
- 'app/graphql/mutations/user_callouts/create.rb' - 'app/graphql/mutations/user_callouts/create.rb'
- 'app/graphql/resolvers/alert_management/alert_resolver.rb'
- 'app/graphql/resolvers/alert_management/alert_status_counts_resolver.rb'
- 'app/graphql/resolvers/blobs_resolver.rb'
- 'app/graphql/resolvers/ci/config_resolver.rb'
- 'app/graphql/resolvers/ci/runners_resolver.rb'
- 'app/graphql/resolvers/ci/template_resolver.rb'
- 'app/graphql/resolvers/concerns/group_issuable_resolver.rb'
- 'app/graphql/resolvers/concerns/issue_resolver_arguments.rb'
- 'app/graphql/resolvers/concerns/resolves_pipelines.rb'
- 'app/graphql/resolvers/container_repositories_resolver.rb'
- 'app/graphql/resolvers/design_management/design_resolver.rb'
- 'app/graphql/resolvers/design_management/version/design_at_version_resolver.rb'
- 'app/graphql/resolvers/design_management/version_in_collection_resolver.rb'
- 'app/graphql/resolvers/design_management/versions_resolver.rb'
- 'app/graphql/resolvers/environments_resolver.rb'
- 'app/graphql/resolvers/full_path_resolver.rb'
- 'app/graphql/resolvers/group_labels_resolver.rb'
- 'app/graphql/resolvers/group_milestones_resolver.rb'
- 'app/graphql/resolvers/labels_resolver.rb'
- 'app/graphql/resolvers/members_resolver.rb'
- 'app/graphql/resolvers/merge_request_resolver.rb'
- 'app/graphql/resolvers/merge_requests_resolver.rb'
- 'app/graphql/resolvers/metrics/dashboard_resolver.rb'
- 'app/graphql/resolvers/milestones_resolver.rb'
- 'app/graphql/resolvers/namespace_projects_resolver.rb'
- 'app/graphql/resolvers/packages_base_resolver.rb'
- 'app/graphql/resolvers/project_milestones_resolver.rb'
- 'app/graphql/resolvers/project_pipeline_resolver.rb'
- 'app/graphql/resolvers/projects/jira_projects_resolver.rb'
- 'app/graphql/resolvers/projects/services_resolver.rb'
- 'app/graphql/resolvers/projects_resolver.rb'
- 'app/graphql/resolvers/release_resolver.rb'
- 'app/graphql/resolvers/repository_branch_names_resolver.rb'
- 'app/graphql/resolvers/snippets_resolver.rb'
- 'app/graphql/resolvers/terraform/states_resolver.rb'
- 'app/graphql/resolvers/tree_resolver.rb'
- 'app/graphql/resolvers/user_resolver.rb'
- 'app/graphql/resolvers/user_starred_projects_resolver.rb'
- 'app/graphql/resolvers/users_resolver.rb'
- 'app/graphql/types/access_level_type.rb' - 'app/graphql/types/access_level_type.rb'
- 'app/graphql/types/admin/analytics/usage_trends/measurement_type.rb' - 'app/graphql/types/admin/analytics/usage_trends/measurement_type.rb'
- 'app/graphql/types/admin/sidekiq_queues/delete_jobs_response_type.rb' - 'app/graphql/types/admin/sidekiq_queues/delete_jobs_response_type.rb'

View File

@ -1 +1 @@
996a4adda765e8ced18c72eca0ebd27848afa3c9 818f3d85a2c8e6596376f1d2276aa22660203a6c

View File

@ -60,14 +60,14 @@ export default {
<gl-button <gl-button
:class="{ selected: !renderTreeList }" :class="{ selected: !renderTreeList }"
class="gl-w-half js-list-view" class="gl-w-half js-list-view"
@click="setRenderTreeList(false)" @click="setRenderTreeList({ renderTreeList: false })"
> >
{{ __('List view') }} {{ __('List view') }}
</gl-button> </gl-button>
<gl-button <gl-button
:class="{ selected: renderTreeList }" :class="{ selected: renderTreeList }"
class="gl-w-half js-tree-view" class="gl-w-half js-tree-view"
@click="setRenderTreeList(true)" @click="setRenderTreeList({ renderTreeList: true })"
> >
{{ __('Tree view') }} {{ __('Tree view') }}
</gl-button> </gl-button>

View File

@ -93,7 +93,7 @@ export default function initDiffsApp(store) {
const treeListStored = localStorage.getItem(TREE_LIST_STORAGE_KEY); const treeListStored = localStorage.getItem(TREE_LIST_STORAGE_KEY);
const renderTreeList = treeListStored !== null ? parseBoolean(treeListStored) : true; const renderTreeList = treeListStored !== null ? parseBoolean(treeListStored) : true;
this.setRenderTreeList(renderTreeList); this.setRenderTreeList({ renderTreeList, trackClick: false });
// NOTE: A "true" or "checked" value for `showWhitespace` is '0' not '1'. // NOTE: A "true" or "checked" value for `showWhitespace` is '0' not '1'.
// Check for cookie and save that setting for future use. // Check for cookie and save that setting for future use.
@ -104,6 +104,7 @@ export default function initDiffsApp(store) {
this.setShowWhitespace({ this.setShowWhitespace({
url: this.endpointUpdateUser, url: this.endpointUpdateUser,
showWhitespace: hideWhitespace !== '1', showWhitespace: hideWhitespace !== '1',
trackClick: false,
}); });
Cookies.remove(DIFF_WHITESPACE_COOKIE_NAME); Cookies.remove(DIFF_WHITESPACE_COOKIE_NAME);
} else { } else {
@ -111,6 +112,7 @@ export default function initDiffsApp(store) {
this.setShowWhitespace({ this.setShowWhitespace({
showWhitespace: this.showWhitespaceDefault, showWhitespace: this.showWhitespaceDefault,
updateDatabase: false, updateDatabase: false,
trackClick: false,
}); });
} }
}, },

View File

@ -560,12 +560,12 @@ export const closeDiffFileCommentForm = ({ commit }, fileHash) => {
commit(types.CLOSE_DIFF_FILE_COMMENT_FORM, fileHash); commit(types.CLOSE_DIFF_FILE_COMMENT_FORM, fileHash);
}; };
export const setRenderTreeList = ({ commit }, renderTreeList) => { export const setRenderTreeList = ({ commit }, { renderTreeList, trackClick = true }) => {
commit(types.SET_RENDER_TREE_LIST, renderTreeList); commit(types.SET_RENDER_TREE_LIST, renderTreeList);
localStorage.setItem(TREE_LIST_STORAGE_KEY, renderTreeList); localStorage.setItem(TREE_LIST_STORAGE_KEY, renderTreeList);
if (window.gon?.features?.diffSettingsUsageData) { if (window.gon?.features?.diffSettingsUsageData && trackClick) {
api.trackRedisHllUserEvent(TRACKING_CLICK_FILE_BROWSER_SETTING); api.trackRedisHllUserEvent(TRACKING_CLICK_FILE_BROWSER_SETTING);
if (renderTreeList) { if (renderTreeList) {
@ -578,7 +578,7 @@ export const setRenderTreeList = ({ commit }, renderTreeList) => {
export const setShowWhitespace = async ( export const setShowWhitespace = async (
{ state, commit }, { state, commit },
{ url, showWhitespace, updateDatabase = true }, { url, showWhitespace, updateDatabase = true, trackClick = true },
) => { ) => {
if (updateDatabase && Boolean(window.gon?.current_user_id)) { if (updateDatabase && Boolean(window.gon?.current_user_id)) {
await axios.put(url || state.endpointUpdateUser, { show_whitespace_in_diffs: showWhitespace }); await axios.put(url || state.endpointUpdateUser, { show_whitespace_in_diffs: showWhitespace });
@ -587,7 +587,7 @@ export const setShowWhitespace = async (
commit(types.SET_SHOW_WHITESPACE, showWhitespace); commit(types.SET_SHOW_WHITESPACE, showWhitespace);
notesEventHub.$emit('refetchDiffData'); notesEventHub.$emit('refetchDiffData');
if (window.gon?.features?.diffSettingsUsageData) { if (window.gon?.features?.diffSettingsUsageData && trackClick) {
api.trackRedisHllUserEvent(TRACKING_CLICK_WHITESPACE_SETTING); api.trackRedisHllUserEvent(TRACKING_CLICK_WHITESPACE_SETTING);
if (showWhitespace) { if (showWhitespace) {

View File

@ -1,6 +1,6 @@
<script> <script>
import { GlButton, GlTooltipDirective } from '@gitlab/ui'; import { GlButton, GlTooltipDirective } from '@gitlab/ui';
import { mapState } from 'vuex'; import { mapActions, mapState } from 'vuex';
export default { export default {
name: 'RemoveMemberButton', name: 'RemoveMemberButton',
@ -45,7 +45,7 @@ export default {
oncallSchedules: { oncallSchedules: {
type: Object, type: Object,
required: false, required: false,
default: () => {}, default: () => ({}),
}, },
}, },
computed: { computed: {
@ -54,30 +54,35 @@ export default {
return state[this.namespace].memberPath; return state[this.namespace].memberPath;
}, },
}), }),
computedMemberPath() { modalData() {
return this.memberPath.replace(':id', this.memberId); return {
}, isAccessRequest: this.isAccessRequest,
stringifiedSchedules() { isInvite: this.isInvite,
return JSON.stringify(this.oncallSchedules); memberPath: this.memberPath.replace(':id', this.memberId),
memberType: this.memberType,
message: this.message,
oncallSchedules: this.oncallSchedules,
};
}, },
}, },
methods: {
...mapActions({
showRemoveMemberModal(dispatch, payload) {
return dispatch(`${this.namespace}/showRemoveMemberModal`, payload);
},
}),
},
}; };
</script> </script>
<template> <template>
<gl-button <gl-button
v-gl-tooltip.hover v-gl-tooltip
class="js-remove-member-button"
variant="danger" variant="danger"
:title="title" :title="title"
:aria-label="title" :aria-label="title"
:icon="icon" :icon="icon"
:data-member-path="computedMemberPath"
:data-member-type="memberType"
:data-is-access-request="isAccessRequest"
:data-is-invite="isInvite"
:data-message="message"
:data-oncall-schedules="stringifiedSchedules"
data-qa-selector="delete_member_button" data-qa-selector="delete_member_button"
@click="showRemoveMemberModal(modalData)"
/> />
</template> </template>

View File

@ -1,7 +1,6 @@
<script> <script>
import { GlFormCheckbox, GlModal } from '@gitlab/ui'; import { GlFormCheckbox, GlModal } from '@gitlab/ui';
import * as Sentry from '@sentry/browser'; import { mapActions, mapState } from 'vuex';
import { parseBoolean } from '~/lib/utils/common_utils';
import csrf from '~/lib/utils/csrf'; import csrf from '~/lib/utils/csrf';
import { s__, __ } from '~/locale'; import { s__, __ } from '~/locale';
import OncallSchedulesList from '~/vue_shared/components/oncall_schedules_list.vue'; import OncallSchedulesList from '~/vue_shared/components/oncall_schedules_list.vue';
@ -16,20 +15,33 @@ export default {
GlModal, GlModal,
OncallSchedulesList, OncallSchedulesList,
}, },
data() { inject: ['namespace'],
return {
modalData: {},
};
},
computed: { computed: {
isAccessRequest() { ...mapState({
return parseBoolean(this.modalData.isAccessRequest); isAccessRequest(state) {
}, return state[this.namespace].removeMemberModalData.isAccessRequest;
isInvite() { },
return parseBoolean(this.modalData.isInvite); isInvite(state) {
}, return state[this.namespace].removeMemberModalData.isInvite;
},
memberPath(state) {
return state[this.namespace].removeMemberModalData.memberPath;
},
memberType(state) {
return state[this.namespace].removeMemberModalData.memberType;
},
message(state) {
return state[this.namespace].removeMemberModalData.message;
},
oncallSchedules(state) {
return state[this.namespace].removeMemberModalData.oncallSchedules ?? {};
},
removeMemberModalVisible(state) {
return state[this.namespace].removeMemberModalVisible;
},
}),
isGroupMember() { isGroupMember() {
return this.modalData.memberType === 'GroupMember'; return this.memberType === 'GroupMember';
}, },
actionText() { actionText() {
if (this.isAccessRequest) { if (this.isAccessRequest) {
@ -54,29 +66,13 @@ export default {
isPartOfOncallSchedules() { isPartOfOncallSchedules() {
return !this.isAccessRequest && this.oncallSchedules.schedules?.length; return !this.isAccessRequest && this.oncallSchedules.schedules?.length;
}, },
oncallSchedules() {
try {
return JSON.parse(this.modalData.oncallSchedules);
} catch (e) {
Sentry.captureException(e);
}
return {};
},
},
mounted() {
document.addEventListener('click', this.handleClick);
},
beforeDestroy() {
document.removeEventListener('click', this.handleClick);
}, },
methods: { methods: {
handleClick(event) { ...mapActions({
const removeButton = event.target.closest('.js-remove-member-button'); hideRemoveMemberModal(dispatch) {
if (removeButton) { return dispatch(`${this.namespace}/hideRemoveMemberModal`);
this.modalData = removeButton.dataset; },
this.$refs.modal.show(); }),
}
},
submitForm() { submitForm() {
this.$refs.form.submit(); this.$refs.form.submit();
}, },
@ -91,11 +87,13 @@ export default {
:action-cancel="$options.actionCancel" :action-cancel="$options.actionCancel"
:action-primary="actionPrimary" :action-primary="actionPrimary"
:title="actionText" :title="actionText"
:visible="removeMemberModalVisible"
data-qa-selector="remove_member_modal_content" data-qa-selector="remove_member_modal_content"
@primary="submitForm" @primary="submitForm"
@hide="hideRemoveMemberModal"
> >
<form ref="form" :action="modalData.memberPath" method="post"> <form ref="form" :action="memberPath" method="post">
<p data-testid="modal-message">{{ modalData.message }}</p> <p>{{ message }}</p>
<oncall-schedules-list <oncall-schedules-list
v-if="isPartOfOncallSchedules" v-if="isPartOfOncallSchedules"

View File

@ -7,6 +7,7 @@ import { mergeUrlParams } from '~/lib/utils/url_utility';
import initUserPopovers from '~/user_popovers'; import initUserPopovers from '~/user_popovers';
import { FIELDS, ACTIVE_TAB_QUERY_PARAM_NAME } from '../../constants'; import { FIELDS, ACTIVE_TAB_QUERY_PARAM_NAME } from '../../constants';
import RemoveGroupLinkModal from '../modals/remove_group_link_modal.vue'; import RemoveGroupLinkModal from '../modals/remove_group_link_modal.vue';
import RemoveMemberModal from '../modals/remove_member_modal.vue';
import CreatedAt from './created_at.vue'; import CreatedAt from './created_at.vue';
import ExpirationDatepicker from './expiration_datepicker.vue'; import ExpirationDatepicker from './expiration_datepicker.vue';
import ExpiresAt from './expires_at.vue'; import ExpiresAt from './expires_at.vue';
@ -29,6 +30,7 @@ export default {
MemberActionButtons, MemberActionButtons,
RoleDropdown, RoleDropdown,
RemoveGroupLinkModal, RemoveGroupLinkModal,
RemoveMemberModal,
ExpirationDatepicker, ExpirationDatepicker,
LdapOverrideConfirmationModal: () => LdapOverrideConfirmationModal: () =>
import('ee_component/members/components/ldap/ldap_override_confirmation_modal.vue'), import('ee_component/members/components/ldap/ldap_override_confirmation_modal.vue'),
@ -225,6 +227,7 @@ export default {
align="center" align="center"
/> />
<remove-group-link-modal /> <remove-group-link-modal />
<remove-member-modal />
<ldap-override-confirmation-modal /> <ldap-override-confirmation-modal />
</div> </div>
</template> </template>

View File

@ -25,6 +25,14 @@ export const hideRemoveGroupLinkModal = ({ commit }) => {
commit(types.HIDE_REMOVE_GROUP_LINK_MODAL); commit(types.HIDE_REMOVE_GROUP_LINK_MODAL);
}; };
export const showRemoveMemberModal = ({ commit }, modalData) => {
commit(types.SHOW_REMOVE_MEMBER_MODAL, modalData);
};
export const hideRemoveMemberModal = ({ commit }) => {
commit(types.HIDE_REMOVE_MEMBER_MODAL);
};
export const updateMemberExpiration = async ({ state, commit }, { memberId, expiresAt }) => { export const updateMemberExpiration = async ({ state, commit }, { memberId, expiresAt }) => {
try { try {
await axios.put( await axios.put(

View File

@ -8,3 +8,6 @@ export const HIDE_ERROR = 'HIDE_ERROR';
export const SHOW_REMOVE_GROUP_LINK_MODAL = 'SHOW_REMOVE_GROUP_LINK_MODAL'; export const SHOW_REMOVE_GROUP_LINK_MODAL = 'SHOW_REMOVE_GROUP_LINK_MODAL';
export const HIDE_REMOVE_GROUP_LINK_MODAL = 'HIDE_REMOVE_GROUP_LINK_MODAL'; export const HIDE_REMOVE_GROUP_LINK_MODAL = 'HIDE_REMOVE_GROUP_LINK_MODAL';
export const SHOW_REMOVE_MEMBER_MODAL = 'SHOW_REMOVE_MEMBER_MODAL';
export const HIDE_REMOVE_MEMBER_MODAL = 'HIDE_REMOVE_MEMBER_MODAL';

View File

@ -47,4 +47,11 @@ export default {
[types.HIDE_REMOVE_GROUP_LINK_MODAL](state) { [types.HIDE_REMOVE_GROUP_LINK_MODAL](state) {
state.removeGroupLinkModalVisible = false; state.removeGroupLinkModalVisible = false;
}, },
[types.SHOW_REMOVE_MEMBER_MODAL](state, modalData) {
state.removeMemberModalData = modalData;
state.removeMemberModalVisible = true;
},
[types.HIDE_REMOVE_MEMBER_MODAL](state) {
state.removeMemberModalVisible = false;
},
}; };

View File

@ -20,4 +20,6 @@ export default ({
errorMessage: '', errorMessage: '',
removeGroupLinkModalVisible: false, removeGroupLinkModalVisible: false,
groupLinkToRemove: null, groupLinkToRemove: null,
removeMemberModalData: {},
removeMemberModalVisible: false,
}); });

View File

@ -1,23 +1,3 @@
import Vue from 'vue';
import UsersSelect from '~/users_select'; import UsersSelect from '~/users_select';
import RemoveMemberModal from '~/vue_shared/components/remove_member_modal.vue';
function mountRemoveMemberModal() { new UsersSelect(); // eslint-disable-line no-new
const el = document.querySelector('.js-remove-member-modal');
if (!el) {
return false;
}
return new Vue({
el,
render(createComponent) {
return createComponent(RemoveMemberModal);
},
});
}
document.addEventListener('DOMContentLoaded', () => {
mountRemoveMemberModal();
new UsersSelect(); // eslint-disable-line no-new
});

View File

@ -1,23 +1,5 @@
import Vue from 'vue';
import NamespaceSelect from '~/namespace_select'; import NamespaceSelect from '~/namespace_select';
import ProjectsList from '~/projects_list'; import ProjectsList from '~/projects_list';
import RemoveMemberModal from '~/vue_shared/components/remove_member_modal.vue';
function mountRemoveMemberModal() {
const el = document.querySelector('.js-remove-member-modal');
if (!el) {
return false;
}
return new Vue({
el,
render(createComponent) {
return createComponent(RemoveMemberModal);
},
});
}
mountRemoveMemberModal();
new ProjectsList(); // eslint-disable-line no-new new ProjectsList(); // eslint-disable-line no-new

View File

@ -1,4 +1,3 @@
import Vue from 'vue';
import { groupMemberRequestFormatter } from '~/groups/members/utils'; import { groupMemberRequestFormatter } from '~/groups/members/utils';
import groupsSelect from '~/groups_select'; import groupsSelect from '~/groups_select';
import initInviteGroupTrigger from '~/invite_members/init_invite_group_trigger'; import initInviteGroupTrigger from '~/invite_members/init_invite_group_trigger';
@ -11,21 +10,6 @@ import { initMembersApp } from '~/members';
import { MEMBER_TYPES } from '~/members/constants'; import { MEMBER_TYPES } from '~/members/constants';
import { groupLinkRequestFormatter } from '~/members/utils'; import { groupLinkRequestFormatter } from '~/members/utils';
import UsersSelect from '~/users_select'; import UsersSelect from '~/users_select';
import RemoveMemberModal from '~/vue_shared/components/remove_member_modal.vue';
function mountRemoveMemberModal() {
const el = document.querySelector('.js-remove-member-modal');
if (!el) {
return false;
}
return new Vue({
el,
render(createComponent) {
return createComponent(RemoveMemberModal);
},
});
}
const SHARED_FIELDS = ['account', 'expires', 'maxRole', 'expiration', 'actions']; const SHARED_FIELDS = ['account', 'expires', 'maxRole', 'expiration', 'actions'];
@ -71,7 +55,6 @@ initMembersApp(document.querySelector('.js-group-members-list-app'), {
groupsSelect(); groupsSelect();
memberExpirationDate(); memberExpirationDate();
memberExpirationDate('.js-access-expiration-date-groups'); memberExpirationDate('.js-access-expiration-date-groups');
mountRemoveMemberModal();
initInviteMembersModal(); initInviteMembersModal();
initInviteMembersTrigger(); initInviteMembersTrigger();
initInviteGroupTrigger(); initInviteGroupTrigger();

View File

@ -1,4 +1,3 @@
import Vue from 'vue';
import groupsSelect from '~/groups_select'; import groupsSelect from '~/groups_select';
import initInviteGroupTrigger from '~/invite_members/init_invite_group_trigger'; import initInviteGroupTrigger from '~/invite_members/init_invite_group_trigger';
import initInviteMembersForm from '~/invite_members/init_invite_members_form'; import initInviteMembersForm from '~/invite_members/init_invite_members_form';
@ -11,26 +10,10 @@ import { MEMBER_TYPES } from '~/members/constants';
import { groupLinkRequestFormatter } from '~/members/utils'; import { groupLinkRequestFormatter } from '~/members/utils';
import { projectMemberRequestFormatter } from '~/projects/members/utils'; import { projectMemberRequestFormatter } from '~/projects/members/utils';
import UsersSelect from '~/users_select'; import UsersSelect from '~/users_select';
import RemoveMemberModal from '~/vue_shared/components/remove_member_modal.vue';
function mountRemoveMemberModal() {
const el = document.querySelector('.js-remove-member-modal');
if (!el) {
return false;
}
return new Vue({
el,
render(createComponent) {
return createComponent(RemoveMemberModal);
},
});
}
groupsSelect(); groupsSelect();
memberExpirationDate(); memberExpirationDate();
memberExpirationDate('.js-access-expiration-date-groups'); memberExpirationDate('.js-access-expiration-date-groups');
mountRemoveMemberModal();
initInviteMembersModal(); initInviteMembersModal();
initInviteMembersTrigger(); initInviteMembersTrigger();
initInviteGroupTrigger(); initInviteGroupTrigger();

View File

@ -111,7 +111,7 @@ export default {
> >
{{ __('Paused') }} {{ __('Paused') }}
<template #help> <template #help>
{{ __("Paused runners don't accept new jobs") }} {{ s__('Runners|Stop the runner from accepting new jobs.') }}
</template> </template>
</gl-form-checkbox> </gl-form-checkbox>
@ -123,14 +123,14 @@ export default {
> >
{{ __('Protected') }} {{ __('Protected') }}
<template #help> <template #help>
{{ __('This runner will only run on pipelines triggered on protected branches') }} {{ s__('Runners|Use the runner on pipelines for protected branches only.') }}
</template> </template>
</gl-form-checkbox> </gl-form-checkbox>
<gl-form-checkbox v-model="model.runUntagged" data-testid="runner-field-run-untagged"> <gl-form-checkbox v-model="model.runUntagged" data-testid="runner-field-run-untagged">
{{ __('Run untagged jobs') }} {{ __('Run untagged jobs') }}
<template #help> <template #help>
{{ __('Indicates whether this runner can pick jobs without tags') }} {{ s__('Runners|Use the runner for jobs without tags, in addition to tagged jobs.') }}
</template> </template>
</gl-form-checkbox> </gl-form-checkbox>
@ -141,7 +141,7 @@ export default {
> >
{{ __('Lock to current projects') }} {{ __('Lock to current projects') }}
<template #help> <template #help>
{{ __('When a runner is locked, it cannot be assigned to other projects') }} {{ s__('Runners|Use the runner for the currently assigned projects only.') }}
</template> </template>
</gl-form-checkbox> </gl-form-checkbox>

View File

@ -98,6 +98,10 @@ export const COVERAGE_FUZZING_DESCRIPTION = __(
export const COVERAGE_FUZZING_HELP_PATH = helpPagePath( export const COVERAGE_FUZZING_HELP_PATH = helpPagePath(
'user/application_security/coverage_fuzzing/index', 'user/application_security/coverage_fuzzing/index',
); );
export const COVERAGE_FUZZING_CONFIG_HELP_PATH = helpPagePath(
'user/application_security/coverage_fuzzing/index',
{ anchor: 'configuration' },
);
export const API_FUZZING_NAME = __('API Fuzzing'); export const API_FUZZING_NAME = __('API Fuzzing');
export const API_FUZZING_DESCRIPTION = __('Find bugs in your code with API fuzzing.'); export const API_FUZZING_DESCRIPTION = __('Find bugs in your code with API fuzzing.');
@ -262,6 +266,7 @@ export const securityFeatures = [
name: COVERAGE_FUZZING_NAME, name: COVERAGE_FUZZING_NAME,
description: COVERAGE_FUZZING_DESCRIPTION, description: COVERAGE_FUZZING_DESCRIPTION,
helpPath: COVERAGE_FUZZING_HELP_PATH, helpPath: COVERAGE_FUZZING_HELP_PATH,
configurationHelpPath: COVERAGE_FUZZING_CONFIG_HELP_PATH,
type: REPORT_TYPE_COVERAGE_FUZZING, type: REPORT_TYPE_COVERAGE_FUZZING,
}, },
]; ];

View File

@ -125,7 +125,12 @@ export default {
class="gl-mt-5" class="gl-mt-5"
/> />
<gl-button v-else icon="external-link" :href="feature.configurationHelpPath" class="gl-mt-5"> <gl-button
v-else-if="feature.configurationHelpPath"
icon="external-link"
:href="feature.configurationHelpPath"
class="gl-mt-5"
>
{{ $options.i18n.configurationGuide }} {{ $options.i18n.configurationGuide }}
</gl-button> </gl-button>
</template> </template>

View File

@ -10,10 +10,6 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap
before_action :apply_diff_view_cookie!, only: [:diffs, :diff_for_path] before_action :apply_diff_view_cookie!, only: [:diffs, :diff_for_path]
before_action :build_merge_request, except: [:create] before_action :build_merge_request, except: [:create]
before_action do
push_frontend_feature_flag(:mr_collapsed_approval_rules, @project)
end
def new def new
define_new_vars define_new_vars
end end

View File

@ -55,7 +55,6 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
end end
before_action do before_action do
push_frontend_feature_flag(:mr_collapsed_approval_rules, @project)
push_frontend_feature_flag(:show_relevant_approval_rule_approvers, @project, default_enabled: :yaml) push_frontend_feature_flag(:show_relevant_approval_rule_approvers, @project, default_enabled: :yaml)
end end

View File

@ -5,7 +5,7 @@ module Resolvers
class AlertResolver < BaseResolver class AlertResolver < BaseResolver
include LooksAhead include LooksAhead
argument :iid, GraphQL::STRING_TYPE, argument :iid, GraphQL::Types::String,
required: false, required: false,
description: 'IID of the alert. For example, "1".' description: 'IID of the alert. For example, "1".'
@ -23,11 +23,11 @@ module Resolvers
required: true, required: true,
default_value: 'operations' default_value: 'operations'
argument :search, GraphQL::STRING_TYPE, argument :search, GraphQL::Types::String,
description: 'Search query for title, description, service, or monitoring_tool.', description: 'Search query for title, description, service, or monitoring_tool.',
required: false required: false
argument :assignee_username, GraphQL::STRING_TYPE, argument :assignee_username, GraphQL::Types::String,
required: false, required: false,
description: 'Username of a user assigned to the issue.' description: 'Username of a user assigned to the issue.'

View File

@ -5,11 +5,11 @@ module Resolvers
class AlertStatusCountsResolver < BaseResolver class AlertStatusCountsResolver < BaseResolver
type Types::AlertManagement::AlertStatusCountsType, null: true type Types::AlertManagement::AlertStatusCountsType, null: true
argument :search, GraphQL::STRING_TYPE, argument :search, GraphQL::Types::String,
description: 'Search query for title, description, service, or monitoring_tool.', description: 'Search query for title, description, service, or monitoring_tool.',
required: false required: false
argument :assignee_username, GraphQL::STRING_TYPE, argument :assignee_username, GraphQL::Types::String,
required: false, required: false,
description: 'Username of a user assigned to the issue.' description: 'Username of a user assigned to the issue.'

View File

@ -10,10 +10,10 @@ module Resolvers
alias_method :repository, :object alias_method :repository, :object
argument :paths, [GraphQL::STRING_TYPE], argument :paths, [GraphQL::Types::String],
required: true, required: true,
description: 'Array of desired blob paths.' description: 'Array of desired blob paths.'
argument :ref, GraphQL::STRING_TYPE, argument :ref, GraphQL::Types::String,
required: false, required: false,
default_value: nil, default_value: nil,
description: 'The commit ref to get the blobs from. Default value is HEAD.' description: 'The commit ref to get the blobs from. Default value is HEAD.'

View File

@ -14,19 +14,19 @@ module Resolvers
authorize :read_pipeline authorize :read_pipeline
argument :project_path, GraphQL::ID_TYPE, argument :project_path, GraphQL::Types::ID,
required: true, required: true,
description: 'The project of the CI config.' description: 'The project of the CI config.'
argument :sha, GraphQL::STRING_TYPE, argument :sha, GraphQL::Types::String,
required: false, required: false,
description: "Sha for the pipeline." description: "Sha for the pipeline."
argument :content, GraphQL::STRING_TYPE, argument :content, GraphQL::Types::String,
required: true, required: true,
description: "Contents of `.gitlab-ci.yml`." description: "Contents of `.gitlab-ci.yml`."
argument :dry_run, GraphQL::BOOLEAN_TYPE, argument :dry_run, GraphQL::Types::Boolean,
required: false, required: false,
description: 'Run pipeline creation simulation, or only do static check.' description: 'Run pipeline creation simulation, or only do static check.'

View File

@ -9,12 +9,12 @@ module Resolvers
description 'Runner setup instructions.' description 'Runner setup instructions.'
argument :platform, argument :platform,
type: GraphQL::STRING_TYPE, type: GraphQL::Types::String,
required: true, required: true,
description: 'Platform to generate the instructions for.' description: 'Platform to generate the instructions for.'
argument :architecture, argument :architecture,
type: GraphQL::STRING_TYPE, type: GraphQL::Types::String,
required: true, required: true,
description: 'Architecture to generate the instructions for.' description: 'Architecture to generate the instructions for.'

View File

@ -15,11 +15,11 @@ module Resolvers
required: false, required: false,
description: 'Filter runners by type.' description: 'Filter runners by type.'
argument :tag_list, [GraphQL::STRING_TYPE], argument :tag_list, [GraphQL::Types::String],
required: false, required: false,
description: 'Filter by tags associated with the runner (comma-separated or array).' description: 'Filter by tags associated with the runner (comma-separated or array).'
argument :search, GraphQL::STRING_TYPE, argument :search, GraphQL::Types::String,
required: false, required: false,
description: 'Filter by full token or partial text in description field.' description: 'Filter by full token or partial text in description field.'

View File

@ -5,7 +5,7 @@ module Resolvers
class TemplateResolver < BaseResolver class TemplateResolver < BaseResolver
type Types::Ci::TemplateType, null: true type Types::Ci::TemplateType, null: true
argument :name, GraphQL::STRING_TYPE, required: true, argument :name, GraphQL::Types::String, required: true,
description: 'Name of the CI/CD template to search for. Template must be formatted as `Name.gitlab-ci.yml`.' description: 'Name of the CI/CD template to search for. Template must be formatted as `Name.gitlab-ci.yml`.'
alias_method :project, :object alias_method :project, :object

View File

@ -11,7 +11,7 @@ module Resolvers
alias_method :pipeline, :object alias_method :pipeline, :object
argument :build_ids, [GraphQL::ID_TYPE], argument :build_ids, [GraphQL::Types::ID],
required: true, required: true,
description: 'IDs of the builds used to run the test suite.' description: 'IDs of the builds used to run the test suite.'

View File

@ -5,7 +5,7 @@ module GroupIssuableResolver
class_methods do class_methods do
def include_subgroups(name_of_things) def include_subgroups(name_of_things)
argument :include_subgroups, GraphQL::BOOLEAN_TYPE, argument :include_subgroups, GraphQL::Types::Boolean,
required: false, required: false,
default_value: false, default_value: false,
description: "Include #{name_of_things} belonging to subgroups" description: "Include #{name_of_things} belonging to subgroups"

View File

@ -6,29 +6,29 @@ module IssueResolverArguments
prepended do prepended do
include LooksAhead include LooksAhead
argument :iid, GraphQL::STRING_TYPE, argument :iid, GraphQL::Types::String,
required: false, required: false,
description: 'IID of the issue. For example, "1".' description: 'IID of the issue. For example, "1".'
argument :iids, [GraphQL::STRING_TYPE], argument :iids, [GraphQL::Types::String],
required: false, required: false,
description: 'List of IIDs of issues. For example, `["1", "2"]`.' description: 'List of IIDs of issues. For example, `["1", "2"]`.'
argument :label_name, [GraphQL::STRING_TYPE, null: true], argument :label_name, [GraphQL::Types::String, null: true],
required: false, required: false,
description: 'Labels applied to this issue.' description: 'Labels applied to this issue.'
argument :milestone_title, [GraphQL::STRING_TYPE, null: true], argument :milestone_title, [GraphQL::Types::String, null: true],
required: false, required: false,
description: 'Milestone applied to this issue.' description: 'Milestone applied to this issue.'
argument :author_username, GraphQL::STRING_TYPE, argument :author_username, GraphQL::Types::String,
required: false, required: false,
description: 'Username of the author of the issue.' description: 'Username of the author of the issue.'
argument :assignee_username, GraphQL::STRING_TYPE, argument :assignee_username, GraphQL::Types::String,
required: false, required: false,
description: 'Username of a user assigned to the issue.', description: 'Username of a user assigned to the issue.',
deprecated: { reason: 'Use `assigneeUsernames`', milestone: '13.11' } deprecated: { reason: 'Use `assigneeUsernames`', milestone: '13.11' }
argument :assignee_usernames, [GraphQL::STRING_TYPE], argument :assignee_usernames, [GraphQL::Types::String],
required: false, required: false,
description: 'Usernames of users assigned to the issue.' description: 'Usernames of users assigned to the issue.'
argument :assignee_id, GraphQL::STRING_TYPE, argument :assignee_id, GraphQL::Types::String,
required: false, required: false,
description: 'ID of a user assigned to the issues, "none" and "any" values are supported.' description: 'ID of a user assigned to the issues, "none" and "any" values are supported.'
argument :created_before, Types::TimeType, argument :created_before, Types::TimeType,
@ -49,7 +49,7 @@ module IssueResolverArguments
argument :closed_after, Types::TimeType, argument :closed_after, Types::TimeType,
required: false, required: false,
description: 'Issues closed after this date.' description: 'Issues closed after this date.'
argument :search, GraphQL::STRING_TYPE, argument :search, GraphQL::Types::String,
required: false, required: false,
description: 'Search query for issue title or description.' description: 'Search query for issue title or description.'
argument :types, [Types::IssueTypeEnum], argument :types, [Types::IssueTypeEnum],

View File

@ -10,11 +10,11 @@ module ResolvesPipelines
required: false, required: false,
description: "Filter pipelines by their status." description: "Filter pipelines by their status."
argument :ref, argument :ref,
GraphQL::STRING_TYPE, GraphQL::Types::String,
required: false, required: false,
description: "Filter pipelines by the ref they are run for." description: "Filter pipelines by the ref they are run for."
argument :sha, argument :sha,
GraphQL::STRING_TYPE, GraphQL::Types::String,
required: false, required: false,
description: "Filter pipelines by the sha of the commit they are run for." description: "Filter pipelines by the sha of the commit they are run for."
end end

View File

@ -6,7 +6,7 @@ module Resolvers
type Types::ContainerRepositoryType, null: true type Types::ContainerRepositoryType, null: true
argument :name, GraphQL::STRING_TYPE, argument :name, GraphQL::Types::String,
required: false, required: false,
description: 'Filter the container repositories by their name.' description: 'Filter the container repositories by their name.'

View File

@ -11,7 +11,7 @@ module Resolvers
required: false, required: false,
description: 'Find a design by its ID.' description: 'Find a design by its ID.'
argument :filename, GraphQL::STRING_TYPE, argument :filename, GraphQL::Types::String,
required: false, required: false,
description: 'Find a design by its filename.' description: 'Find a design by its filename.'

View File

@ -11,7 +11,7 @@ module Resolvers
argument :ids, [DesignID], argument :ids, [DesignID],
required: false, required: false,
description: 'Filters designs by their ID.' description: 'Filters designs by their ID.'
argument :filenames, [GraphQL::STRING_TYPE], argument :filenames, [GraphQL::Types::String],
required: false, required: false,
description: 'Filters designs by their filename.' description: 'Filters designs by their filename.'
argument :at_version, VersionID, argument :at_version, VersionID,

View File

@ -23,7 +23,7 @@ module Resolvers
argument :design_id, DesignID, argument :design_id, DesignID,
required: false, required: false,
description: 'The ID of a specific design.' description: 'The ID of a specific design.'
argument :filename, GraphQL::STRING_TYPE, argument :filename, GraphQL::Types::String,
required: false, required: false,
description: 'The filename of a specific design.' description: 'The filename of a specific design.'

View File

@ -17,7 +17,7 @@ module Resolvers
required: false, required: false,
description: 'Filters designs by their ID.' description: 'Filters designs by their ID.'
argument :filenames, argument :filenames,
[GraphQL::STRING_TYPE], [GraphQL::Types::String],
required: false, required: false,
description: 'Filters designs by their filename.' description: 'Filters designs by their filename.'

View File

@ -15,7 +15,7 @@ module Resolvers
VersionID = ::Types::GlobalIDType[::DesignManagement::Version] VersionID = ::Types::GlobalIDType[::DesignManagement::Version]
argument :sha, GraphQL::STRING_TYPE, argument :sha, GraphQL::Types::String,
required: false, required: false,
description: "The SHA256 of a specific version." description: "The SHA256 of a specific version."
argument :id, VersionID, argument :id, VersionID,

View File

@ -11,7 +11,7 @@ module Resolvers
extras [:parent] extras [:parent]
argument :earlier_or_equal_to_sha, GraphQL::STRING_TYPE, argument :earlier_or_equal_to_sha, GraphQL::Types::String,
as: :sha, as: :sha,
required: false, required: false,
description: 'The SHA256 of the most recent acceptable version.' description: 'The SHA256 of the most recent acceptable version.'

View File

@ -2,11 +2,11 @@
module Resolvers module Resolvers
class EchoResolver < BaseResolver class EchoResolver < BaseResolver
type ::GraphQL::STRING_TYPE, null: false type ::GraphQL::Types::String, null: false
description 'Testing endpoint to validate the API with' description 'Testing endpoint to validate the API with'
argument :text, argument :text,
type: GraphQL::STRING_TYPE, type: GraphQL::Types::String,
required: true, required: true,
description: 'Text to echo back.' description: 'Text to echo back.'

View File

@ -2,15 +2,15 @@
module Resolvers module Resolvers
class EnvironmentsResolver < BaseResolver class EnvironmentsResolver < BaseResolver
argument :name, GraphQL::STRING_TYPE, argument :name, GraphQL::Types::String,
required: false, required: false,
description: 'Name of the environment.' description: 'Name of the environment.'
argument :search, GraphQL::STRING_TYPE, argument :search, GraphQL::Types::String,
required: false, required: false,
description: 'Search query for environment name.' description: 'Search query for environment name.'
argument :states, [GraphQL::STRING_TYPE], argument :states, [GraphQL::Types::String],
required: false, required: false,
description: 'States of environments that should be included in result.' description: 'States of environments that should be included in result.'

View File

@ -6,12 +6,12 @@ module Resolvers
type Types::ErrorTracking::SentryErrorType.connection_type, null: true type Types::ErrorTracking::SentryErrorType.connection_type, null: true
extension Gitlab::Graphql::Extensions::ExternallyPaginatedArrayExtension extension Gitlab::Graphql::Extensions::ExternallyPaginatedArrayExtension
argument :search_term, ::GraphQL::STRING_TYPE, argument :search_term, ::GraphQL::Types::String,
description: 'Search query for the Sentry error details.', description: 'Search query for the Sentry error details.',
required: false required: false
# TODO: convert to Enum # TODO: convert to Enum
argument :sort, ::GraphQL::STRING_TYPE, argument :sort, ::GraphQL::Types::String,
description: 'Attribute to sort on. Options are frequency, first_seen, last_seen. last_seen is default.', description: 'Attribute to sort on. Options are frequency, first_seen, last_seen. last_seen is default.',
required: false required: false

View File

@ -5,7 +5,7 @@ module Resolvers
extend ActiveSupport::Concern extend ActiveSupport::Concern
prepended do prepended do
argument :full_path, GraphQL::ID_TYPE, argument :full_path, GraphQL::Types::ID,
required: true, required: true,
description: 'The full path of the project, group or namespace, e.g., `gitlab-org/gitlab-foss`.' description: 'The full path of the project, group or namespace, e.g., `gitlab-org/gitlab-foss`.'
end end

View File

@ -4,12 +4,12 @@ module Resolvers
class GroupLabelsResolver < LabelsResolver class GroupLabelsResolver < LabelsResolver
type Types::LabelType.connection_type, null: true type Types::LabelType.connection_type, null: true
argument :include_descendant_groups, GraphQL::BOOLEAN_TYPE, argument :include_descendant_groups, GraphQL::Types::Boolean,
required: false, required: false,
description: 'Include labels from descendant groups.', description: 'Include labels from descendant groups.',
default_value: false default_value: false
argument :only_group_labels, GraphQL::BOOLEAN_TYPE, argument :only_group_labels, GraphQL::Types::Boolean,
required: false, required: false,
description: 'Include only group level labels.', description: 'Include only group level labels.',
default_value: false default_value: false

View File

@ -2,10 +2,10 @@
module Resolvers module Resolvers
class GroupMilestonesResolver < MilestonesResolver class GroupMilestonesResolver < MilestonesResolver
argument :include_descendants, GraphQL::BOOLEAN_TYPE, argument :include_descendants, GraphQL::Types::Boolean,
required: false, required: false,
description: 'Include milestones from all subgroups and subprojects.' description: 'Include milestones from all subgroups and subprojects.'
argument :include_ancestors, GraphQL::BOOLEAN_TYPE, argument :include_ancestors, GraphQL::Types::Boolean,
required: false, required: false,
description: 'Include milestones from all parent groups.' description: 'Include milestones from all parent groups.'

View File

@ -8,11 +8,11 @@ module Resolvers
type Types::LabelType.connection_type, null: true type Types::LabelType.connection_type, null: true
argument :search_term, GraphQL::STRING_TYPE, argument :search_term, GraphQL::Types::String,
required: false, required: false,
description: 'A search term to find labels with.' description: 'A search term to find labels with.'
argument :include_ancestor_groups, GraphQL::BOOLEAN_TYPE, argument :include_ancestor_groups, GraphQL::Types::Boolean,
required: false, required: false,
description: 'Include labels from ancestor groups.', description: 'Include labels from ancestor groups.',
default_value: false default_value: false

View File

@ -7,7 +7,7 @@ module Resolvers
type Types::MemberInterface.connection_type, null: true type Types::MemberInterface.connection_type, null: true
argument :search, GraphQL::STRING_TYPE, argument :search, GraphQL::Types::String,
required: false, required: false,
description: 'Search query.' description: 'Search query.'

View File

@ -8,7 +8,7 @@ module Resolvers
type ::Types::MergeRequestType, null: true type ::Types::MergeRequestType, null: true
argument :iid, GraphQL::STRING_TYPE, argument :iid, GraphQL::Types::String,
required: true, required: true,
as: :iids, as: :iids,
description: 'IID of the merge request, for example `1`.' description: 'IID of the merge request, for example `1`.'

View File

@ -10,28 +10,28 @@ module Resolvers
alias_method :project, :object alias_method :project, :object
def self.accept_assignee def self.accept_assignee
argument :assignee_username, GraphQL::STRING_TYPE, argument :assignee_username, GraphQL::Types::String,
required: false, required: false,
description: 'Username of the assignee.' description: 'Username of the assignee.'
end end
def self.accept_author def self.accept_author
argument :author_username, GraphQL::STRING_TYPE, argument :author_username, GraphQL::Types::String,
required: false, required: false,
description: 'Username of the author.' description: 'Username of the author.'
end end
def self.accept_reviewer def self.accept_reviewer
argument :reviewer_username, GraphQL::STRING_TYPE, argument :reviewer_username, GraphQL::Types::String,
required: false, required: false,
description: 'Username of the reviewer.' description: 'Username of the reviewer.'
end end
argument :iids, [GraphQL::STRING_TYPE], argument :iids, [GraphQL::Types::String],
required: false, required: false,
description: 'Array of IIDs of merge requests, for example `[1, 2]`.' description: 'Array of IIDs of merge requests, for example `[1, 2]`.'
argument :source_branches, [GraphQL::STRING_TYPE], argument :source_branches, [GraphQL::Types::String],
required: false, required: false,
as: :source_branch, as: :source_branch,
description: <<~DESC description: <<~DESC
@ -39,7 +39,7 @@ module Resolvers
All resolved merge requests will have one of these branches as their source. All resolved merge requests will have one of these branches as their source.
DESC DESC
argument :target_branches, [GraphQL::STRING_TYPE], argument :target_branches, [GraphQL::Types::String],
required: false, required: false,
as: :target_branch, as: :target_branch,
description: <<~DESC description: <<~DESC
@ -51,7 +51,7 @@ module Resolvers
required: false, required: false,
description: 'A merge request state. If provided, all resolved merge requests will have this state.' description: 'A merge request state. If provided, all resolved merge requests will have this state.'
argument :labels, [GraphQL::STRING_TYPE], argument :labels, [GraphQL::Types::String],
required: false, required: false,
as: :label_name, as: :label_name,
description: 'Array of label names. All resolved merge requests will have all of these labels.' description: 'Array of label names. All resolved merge requests will have all of these labels.'
@ -61,7 +61,7 @@ module Resolvers
argument :merged_before, Types::TimeType, argument :merged_before, Types::TimeType,
required: false, required: false,
description: 'Merge requests merged before this date.' description: 'Merge requests merged before this date.'
argument :milestone_title, GraphQL::STRING_TYPE, argument :milestone_title, GraphQL::Types::String,
required: false, required: false,
description: 'Title of the milestone.' description: 'Title of the milestone.'
argument :sort, Types::MergeRequestSortEnum, argument :sort, Types::MergeRequestSortEnum,
@ -70,11 +70,11 @@ module Resolvers
default_value: :created_desc default_value: :created_desc
negated do negated do
argument :labels, [GraphQL::STRING_TYPE], argument :labels, [GraphQL::Types::String],
required: false, required: false,
as: :label_name, as: :label_name,
description: 'Array of label names. All resolved merge requests will not have these labels.' description: 'Array of label names. All resolved merge requests will not have these labels.'
argument :milestone_title, GraphQL::STRING_TYPE, argument :milestone_title, GraphQL::Types::String,
required: false, required: false,
description: 'Title of the milestone.' description: 'Title of the milestone.'
end end

View File

@ -6,7 +6,7 @@ module Resolvers
type Types::Metrics::DashboardType, null: true type Types::Metrics::DashboardType, null: true
calls_gitaly! calls_gitaly!
argument :path, GraphQL::STRING_TYPE, argument :path, GraphQL::Types::String,
required: true, required: true,
description: <<~MD description: <<~MD
Path to a file which defines a metrics dashboard eg: `"config/prometheus/common_metrics.yml"`. Path to a file which defines a metrics dashboard eg: `"config/prometheus/common_metrics.yml"`.

View File

@ -5,7 +5,7 @@ module Resolvers
include Gitlab::Graphql::Authorize::AuthorizeResource include Gitlab::Graphql::Authorize::AuthorizeResource
include TimeFrameArguments include TimeFrameArguments
argument :ids, [GraphQL::ID_TYPE], argument :ids, [GraphQL::Types::ID],
required: false, required: false,
description: 'Array of global milestone IDs, e.g., `"gid://gitlab/Milestone/1"`.' description: 'Array of global milestone IDs, e.g., `"gid://gitlab/Milestone/1"`.'
@ -13,11 +13,11 @@ module Resolvers
required: false, required: false,
description: 'Filter milestones by state.' description: 'Filter milestones by state.'
argument :title, GraphQL::STRING_TYPE, argument :title, GraphQL::Types::String,
required: false, required: false,
description: 'The title of the milestone.' description: 'The title of the milestone.'
argument :search_title, GraphQL::STRING_TYPE, argument :search_title, GraphQL::Types::String,
required: false, required: false,
description: 'A search string for the title.' description: 'A search string for the title.'

View File

@ -2,12 +2,12 @@
module Resolvers module Resolvers
class NamespaceProjectsResolver < BaseResolver class NamespaceProjectsResolver < BaseResolver
argument :include_subgroups, GraphQL::BOOLEAN_TYPE, argument :include_subgroups, GraphQL::Types::Boolean,
required: false, required: false,
default_value: false, default_value: false,
description: 'Include also subgroup projects.' description: 'Include also subgroup projects.'
argument :search, GraphQL::STRING_TYPE, argument :search, GraphQL::Types::String,
required: false, required: false,
default_value: nil, default_value: nil,
description: 'Search project with most similar names or paths.' description: 'Search project with most similar names or paths.'
@ -17,7 +17,7 @@ module Resolvers
default_value: nil, default_value: nil,
description: 'Sort projects by this criteria.' description: 'Sort projects by this criteria.'
argument :ids, [GraphQL::ID_TYPE], argument :ids, [GraphQL::Types::ID],
required: false, required: false,
default_value: nil, default_value: nil,
description: 'Filter projects by IDs.' description: 'Filter projects by IDs.'

View File

@ -9,7 +9,7 @@ module Resolvers
required: false, required: false,
default_value: :created_desc default_value: :created_desc
argument :package_name, GraphQL::STRING_TYPE, argument :package_name, GraphQL::Types::String,
description: 'Search a package by name.', description: 'Search a package by name.',
required: false, required: false,
default_value: nil default_value: nil
@ -24,7 +24,7 @@ module Resolvers
required: false, required: false,
default_value: nil default_value: nil
argument :include_versionless, GraphQL::BOOLEAN_TYPE, argument :include_versionless, GraphQL::Types::Boolean,
description: 'Include versionless packages.', description: 'Include versionless packages.',
required: false, required: false,
default_value: false default_value: false

View File

@ -3,7 +3,7 @@
module Resolvers module Resolvers
class ProjectMilestonesResolver < MilestonesResolver class ProjectMilestonesResolver < MilestonesResolver
argument :include_ancestors, GraphQL::BOOLEAN_TYPE, argument :include_ancestors, GraphQL::Types::Boolean,
required: false, required: false,
description: "Also return milestones in the project's parent group and its ancestors." description: "Also return milestones in the project's parent group and its ancestors."

View File

@ -6,11 +6,11 @@ module Resolvers
alias_method :project, :object alias_method :project, :object
argument :iid, GraphQL::ID_TYPE, argument :iid, GraphQL::Types::ID,
required: false, required: false,
description: 'IID of the Pipeline. For example, "1".' description: 'IID of the Pipeline. For example, "1".'
argument :sha, GraphQL::STRING_TYPE, argument :sha, GraphQL::Types::String,
required: false, required: false,
description: 'SHA of the Pipeline. For example, "dyd0f15ay83993f5ab66k927w28673882x99100b".' description: 'SHA of the Pipeline. For example, "dyd0f15ay83993f5ab66k927w28673882x99100b".'

View File

@ -9,7 +9,7 @@ module Resolvers
authorize :admin_project authorize :admin_project
argument :name, argument :name,
GraphQL::STRING_TYPE, GraphQL::Types::String,
required: false, required: false,
description: 'Project name or key.' description: 'Project name or key.'

View File

@ -10,7 +10,7 @@ module Resolvers
authorizes_object! authorizes_object!
argument :active, argument :active,
GraphQL::BOOLEAN_TYPE, GraphQL::Types::Boolean,
required: false, required: false,
description: 'Indicates if the integration is active.' description: 'Indicates if the integration is active.'
argument :type, argument :type,

View File

@ -4,27 +4,27 @@ module Resolvers
class ProjectsResolver < BaseResolver class ProjectsResolver < BaseResolver
type Types::ProjectType, null: true type Types::ProjectType, null: true
argument :membership, GraphQL::BOOLEAN_TYPE, argument :membership, GraphQL::Types::Boolean,
required: false, required: false,
description: 'Limit projects that the current user is a member of.' description: 'Limit projects that the current user is a member of.'
argument :search, GraphQL::STRING_TYPE, argument :search, GraphQL::Types::String,
required: false, required: false,
description: 'Search query for project name, path, or description.' description: 'Search query for project name, path, or description.'
argument :ids, [GraphQL::ID_TYPE], argument :ids, [GraphQL::Types::ID],
required: false, required: false,
description: 'Filter projects by IDs.' description: 'Filter projects by IDs.'
argument :search_namespaces, GraphQL::BOOLEAN_TYPE, argument :search_namespaces, GraphQL::Types::Boolean,
required: false, required: false,
description: 'Include namespace in project search.' description: 'Include namespace in project search.'
argument :sort, GraphQL::STRING_TYPE, argument :sort, GraphQL::Types::String,
required: false, required: false,
description: 'Sort order of results.' description: 'Sort order of results.'
argument :topics, type: [GraphQL::STRING_TYPE], argument :topics, type: [GraphQL::Types::String],
required: false, required: false,
description: 'Filters projects by topics.' description: 'Filters projects by topics.'

View File

@ -4,7 +4,7 @@ module Resolvers
class ReleaseResolver < BaseResolver class ReleaseResolver < BaseResolver
type Types::ReleaseType, null: true type Types::ReleaseType, null: true
argument :tag_name, GraphQL::STRING_TYPE, argument :tag_name, GraphQL::Types::String,
required: true, required: true,
description: 'The name of the tag associated to the release.' description: 'The name of the tag associated to the release.'

View File

@ -2,19 +2,19 @@
module Resolvers module Resolvers
class RepositoryBranchNamesResolver < BaseResolver class RepositoryBranchNamesResolver < BaseResolver
type ::GraphQL::STRING_TYPE, null: false type ::GraphQL::Types::String, null: false
calls_gitaly! calls_gitaly!
argument :search_pattern, GraphQL::STRING_TYPE, argument :search_pattern, GraphQL::Types::String,
required: true, required: true,
description: 'The pattern to search for branch names by.' description: 'The pattern to search for branch names by.'
argument :offset, GraphQL::INT_TYPE, argument :offset, GraphQL::Types::Int,
required: true, required: true,
description: 'The number of branch names to skip.' description: 'The number of branch names to skip.'
argument :limit, GraphQL::INT_TYPE, argument :limit, GraphQL::Types::Int,
required: true, required: true,
description: 'The number of branch names to return.' description: 'The number of branch names to return.'

View File

@ -12,7 +12,7 @@ module Resolvers
alias_method :snippet, :object alias_method :snippet, :object
argument :paths, [GraphQL::STRING_TYPE], argument :paths, [GraphQL::Types::String],
required: false, required: false,
description: 'Paths of the blobs.' description: 'Paths of the blobs.'

View File

@ -22,7 +22,7 @@ module Resolvers
description: 'The type of snippet.' description: 'The type of snippet.'
argument :explore, argument :explore,
GraphQL::BOOLEAN_TYPE, GraphQL::Types::Boolean,
required: false, required: false,
description: 'Explore personal snippets.' description: 'Explore personal snippets.'

View File

@ -8,7 +8,7 @@ module Resolvers
alias_method :project, :object alias_method :project, :object
when_single do when_single do
argument :name, GraphQL::STRING_TYPE, argument :name, GraphQL::Types::String,
required: true, required: true,
description: 'Name of the Terraform state.' description: 'Name of the Terraform state.'
end end

View File

@ -10,15 +10,15 @@ module Resolvers
required: false, required: false,
description: 'The action to be filtered.' description: 'The action to be filtered.'
argument :author_id, [GraphQL::ID_TYPE], argument :author_id, [GraphQL::Types::ID],
required: false, required: false,
description: 'The ID of an author.' description: 'The ID of an author.'
argument :project_id, [GraphQL::ID_TYPE], argument :project_id, [GraphQL::Types::ID],
required: false, required: false,
description: 'The ID of a project.' description: 'The ID of a project.'
argument :group_id, [GraphQL::ID_TYPE], argument :group_id, [GraphQL::Types::ID],
required: false, required: false,
description: 'The ID of a group.' description: 'The ID of a group.'

View File

@ -6,15 +6,15 @@ module Resolvers
calls_gitaly! calls_gitaly!
argument :path, GraphQL::STRING_TYPE, argument :path, GraphQL::Types::String,
required: false, required: false,
default_value: '', default_value: '',
description: 'The path to get the tree for. Default value is the root of the repository.' description: 'The path to get the tree for. Default value is the root of the repository.'
argument :ref, GraphQL::STRING_TYPE, argument :ref, GraphQL::Types::String,
required: false, required: false,
default_value: :head, default_value: :head,
description: 'The commit ref to get the tree for. Default value is HEAD.' description: 'The commit ref to get the tree for. Default value is HEAD.'
argument :recursive, GraphQL::BOOLEAN_TYPE, argument :recursive, GraphQL::Types::Boolean,
required: false, required: false,
default_value: false, default_value: false,
description: 'Used to get a recursive tree. Default is false.' description: 'Used to get a recursive tree. Default is false.'

View File

@ -4,7 +4,7 @@ module Resolvers
class UserDiscussionsCountResolver < BaseResolver class UserDiscussionsCountResolver < BaseResolver
include Gitlab::Graphql::Authorize::AuthorizeResource include Gitlab::Graphql::Authorize::AuthorizeResource
type GraphQL::INT_TYPE, null: true type GraphQL::Types::Int, null: true
def resolve def resolve
authorize!(object) authorize!(object)

View File

@ -5,7 +5,7 @@ module Resolvers
include ResolvesProject include ResolvesProject
argument :project_path, argument :project_path,
type: GraphQL::STRING_TYPE, type: GraphQL::Types::String,
required: false, required: false,
description: <<~DESC description: <<~DESC
The full-path of the project the authored merge requests should be in. The full-path of the project the authored merge requests should be in.

View File

@ -4,7 +4,7 @@ module Resolvers
class UserNotesCountResolver < BaseResolver class UserNotesCountResolver < BaseResolver
include Gitlab::Graphql::Authorize::AuthorizeResource include Gitlab::Graphql::Authorize::AuthorizeResource
type GraphQL::INT_TYPE, null: true type GraphQL::Types::Int, null: true
def resolve def resolve
authorize!(object) authorize!(object)

View File

@ -10,7 +10,7 @@ module Resolvers
required: false, required: false,
description: 'ID of the User.' description: 'ID of the User.'
argument :username, GraphQL::STRING_TYPE, argument :username, GraphQL::Types::String,
required: false, required: false,
description: 'Username of the User.' description: 'Username of the User.'

View File

@ -4,7 +4,7 @@ module Resolvers
class UserStarredProjectsResolver < BaseResolver class UserStarredProjectsResolver < BaseResolver
type Types::ProjectType.connection_type, null: true type Types::ProjectType.connection_type, null: true
argument :search, GraphQL::STRING_TYPE, argument :search, GraphQL::Types::String,
required: false, required: false,
description: 'Search query.' description: 'Search query.'

View File

@ -3,7 +3,7 @@
module Resolvers module Resolvers
module Users module Users
class GroupCountResolver < BaseResolver class GroupCountResolver < BaseResolver
type GraphQL::INT_TYPE, null: true type GraphQL::Types::Int, null: true
alias_method :user, :object alias_method :user, :object

View File

@ -7,11 +7,11 @@ module Resolvers
type Types::UserType.connection_type, null: true type Types::UserType.connection_type, null: true
description 'Find Users' description 'Find Users'
argument :ids, [GraphQL::ID_TYPE], argument :ids, [GraphQL::Types::ID],
required: false, required: false,
description: 'List of user Global IDs.' description: 'List of user Global IDs.'
argument :usernames, [GraphQL::STRING_TYPE], required: false, argument :usernames, [GraphQL::Types::String], required: false,
description: 'List of usernames.' description: 'List of usernames.'
argument :sort, Types::SortEnum, argument :sort, Types::SortEnum,
@ -19,11 +19,11 @@ module Resolvers
required: false, required: false,
default_value: :created_desc default_value: :created_desc
argument :search, GraphQL::STRING_TYPE, argument :search, GraphQL::Types::String,
required: false, required: false,
description: "Query to search users by name, username, or primary email." description: "Query to search users by name, username, or primary email."
argument :admins, GraphQL::BOOLEAN_TYPE, argument :admins, GraphQL::Types::Boolean,
required: false, required: false,
default_value: false, default_value: false,
description: 'Return only admin users.' description: 'Return only admin users.'

View File

@ -413,6 +413,12 @@ module ApplicationHelper
end end
end end
def gitlab_ui_form_for(record, *args, &block)
options = args.extract_options!
form_for(record, *(args << options.merge({ builder: ::Gitlab::FormBuilders::GitlabUiFormBuilder })), &block)
end
private private
def appearance def appearance

View File

@ -5,20 +5,42 @@ module Packages
class GenerateDistributionKeyService class GenerateDistributionKeyService
include Gitlab::Utils::StrongMemoize include Gitlab::Utils::StrongMemoize
def initialize(current_user:, params: {}) def initialize(params: {})
@current_user = current_user
@params = params @params = params
end end
def execute def execute
raise ArgumentError, 'Please provide a user' unless current_user.is_a?(User) using_pinentry do |ctx|
# Generate key
ctx.generate_key generate_key_params
generate_key key = ctx.keys.first # rubocop:disable Gitlab/KeysFirstAndValuesFirst
fingerprint = key.fingerprint
# Export private key
data = GPGME::Data.new
ctx.export_keys fingerprint, data, GPGME::EXPORT_MODE_SECRET
data.seek 0
private_key = data.read
# Export public key
data = GPGME::Data.new
ctx.export_keys fingerprint, data
data.seek 0
public_key = data.read
{
private_key: private_key,
public_key: public_key,
passphrase: passphrase,
fingerprint: fingerprint
}
end
end end
private private
attr_reader :current_user, :params attr_reader :params
def passphrase def passphrase
strong_memoize(:passphrase) do strong_memoize(:passphrase) do
@ -72,35 +94,6 @@ module Packages
}.map { |k, v| "#{k}: #{v}\n" }.join + }.map { |k, v| "#{k}: #{v}\n" }.join +
'</GnupgKeyParms>' '</GnupgKeyParms>'
end end
def generate_key
using_pinentry do |ctx|
# Generate key
ctx.generate_key generate_key_params
key = ctx.keys.first # rubocop:disable Gitlab/KeysFirstAndValuesFirst
fingerprint = key.fingerprint
# Export private key
data = GPGME::Data.new
ctx.export_keys fingerprint, data, GPGME::EXPORT_MODE_SECRET
data.seek 0
private_key = data.read
# Export public key
data = GPGME::Data.new
ctx.export_keys fingerprint, data
data.seek 0
public_key = data.read
{
private_key: private_key,
public_key: public_key,
passphrase: passphrase,
fingerprint: fingerprint
}
end
end
end end
end end
end end

View File

@ -0,0 +1,38 @@
# frozen_string_literal: true
module Packages
module Debian
class SignDistributionService
include Gitlab::Utils::StrongMemoize
def initialize(distribution, content, params: {})
@distribution = distribution
@content = content
@params = params
end
def execute
raise ArgumentError, 'distribution key is missing' unless @distribution.key
sig_mode = GPGME::GPGME_SIG_MODE_CLEAR
sig_mode = GPGME::GPGME_SIG_MODE_DETACH if @params[:detach]
Gitlab::Gpg.using_tmp_keychain do
GPGME::Ctx.new(
armor: true,
offline: true,
pinentry_mode: GPGME::PINENTRY_MODE_LOOPBACK,
password: @distribution.key.passphrase
) do |ctx|
ctx.import(GPGME::Data.from_str(@distribution.key.public_key))
ctx.import(GPGME::Data.from_str(@distribution.key.private_key))
signature = GPGME::Data.new
ctx.sign(GPGME::Data.from_str(@content), signature, sig_mode)
signature.to_s
end
end
end
end
end
end

View File

@ -118,6 +118,7 @@
- if Gitlab::CurrentSettings.version_check_enabled - if Gitlab::CurrentSettings.version_check_enabled
.float-right .float-right
= version_status_badge = version_status_badge
= link_to(sprite_icon('question'), "https://gitlab.com/gitlab-org/gitlab/-/blob/master/CHANGELOG.md", class: 'gl-ml-2', target: '_blank', rel: 'noopener noreferrer')
%p %p
= link_to _('GitLab'), general_admin_application_settings_path = link_to _('GitLab'), general_admin_application_settings_path
%span.float-right %span.float-right

View File

@ -4,7 +4,6 @@
- page_title @group.name, _("Groups") - page_title @group.name, _("Groups")
- current_user_is_group_owner = @group && @group.has_owner?(current_user) - current_user_is_group_owner = @group && @group.has_owner?(current_user)
.js-remove-member-modal
%h3.page-title %h3.page-title
= _('Group: %{group_name}') % { group_name: @group.full_name } = _('Group: %{group_name}') % { group_name: @group.full_name }

View File

@ -5,7 +5,6 @@
- @content_class = "admin-projects" - @content_class = "admin-projects"
- current_user_is_group_owner = @group && @group.has_owner?(current_user) - current_user_is_group_owner = @group && @group.has_owner?(current_user)
.js-remove-member-modal
%h3.page-title %h3.page-title
= _('Project: %{name}') % { name: @project.full_name } = _('Project: %{name}') % { name: @project.full_name }
= link_to edit_project_path(@project), class: "btn btn-default gl-button float-right" do = link_to edit_project_path(@project), class: "btn btn-default gl-button float-right" do

View File

@ -2,7 +2,6 @@
- page_title _('Group members') - page_title _('Group members')
- groups_select_tag_data = group_select_data(@group).merge({ skip_groups: @skip_groups }) - groups_select_tag_data = group_select_data(@group).merge({ skip_groups: @skip_groups })
.js-remove-member-modal
.row.gl-mt-3 .row.gl-mt-3
.col-lg-12 .col-lg-12
.gl-display-flex.gl-flex-wrap .gl-display-flex.gl-flex-wrap

View File

@ -1,4 +1,4 @@
= form_for @group, html: { multipart: true, class: 'gl-show-field-errors js-general-permissions-form' }, authenticity_token: true do |f| = gitlab_ui_form_for @group, html: { multipart: true, class: 'gl-show-field-errors js-general-permissions-form' }, authenticity_token: true do |f|
%input{ type: 'hidden', name: 'update_section', value: 'js-permissions-settings' } %input{ type: 'hidden', name: 'update_section', value: 'js-permissions-settings' }
= form_errors(@group) = form_errors(@group)
@ -9,12 +9,10 @@
- if @group.root? - if @group.root?
.form-group.gl-mb-3 .form-group.gl-mb-3
.gl-form-checkbox.custom-control.custom-checkbox = f.gitlab_ui_checkbox_component :prevent_sharing_groups_outside_hierarchy,
= f.check_box :prevent_sharing_groups_outside_hierarchy, disabled: !can_change_prevent_sharing_groups_outside_hierarchy?(@group), class: 'custom-control-input' s_('GroupSettings|Prevent members from sending invitations to groups outside of %{group} and its subgroups.').html_safe % { group: link_to_group(@group) },
= f.label :prevent_sharing_groups_outside_hierarchy, class: 'custom-control-label' do help_text: prevent_sharing_groups_outside_hierarchy_help_text(@group),
%span checkbox_options: { disabled: !can_change_prevent_sharing_groups_outside_hierarchy?(@group) }
= s_('GroupSettings|Prevent members from sending invitations to groups outside of %{group} and its subgroups.').html_safe % { group: link_to_group(@group) }
%p.js-descr.help-text= prevent_sharing_groups_outside_hierarchy_help_text(@group)
.form-group.gl-mb-3 .form-group.gl-mb-3
.gl-form-checkbox.custom-control.custom-checkbox .gl-form-checkbox.custom-control.custom-checkbox

View File

@ -32,6 +32,7 @@
- if page_canonical_link - if page_canonical_link
%link{ rel: 'canonical', href: page_canonical_link } %link{ rel: 'canonical', href: page_canonical_link }
= preload_link_tag(path_to_stylesheet('application_utilities'))
= yield :prefetch_asset_tags = yield :prefetch_asset_tags
= favicon_link_tag favicon, id: 'favicon', data: { original_href: favicon }, type: 'image/png' = favicon_link_tag favicon, id: 'favicon', data: { original_href: favicon }, type: 'image/png'
@ -39,12 +40,10 @@
= render 'layouts/startup_css', { startup_filename: local_assigns.fetch(:startup_filename, nil) } = render 'layouts/startup_css', { startup_filename: local_assigns.fetch(:startup_filename, nil) }
- if user_application_theme == 'gl-dark' - if user_application_theme == 'gl-dark'
= stylesheet_link_tag_defer "application_dark" = stylesheet_link_tag_defer "application_dark"
= yield :page_specific_styles
= stylesheet_link_tag_defer "application_utilities_dark"
- else - else
= stylesheet_link_tag_defer "application" = stylesheet_link_tag_defer "application"
= yield :page_specific_styles = yield :page_specific_styles
= stylesheet_link_tag_defer "application_utilities" = stylesheet_link_tag_defer 'application_utilities'
= stylesheet_link_tag "disable_animations", media: "all" if Rails.env.test? || Gitlab.config.gitlab['disable_animations'] = stylesheet_link_tag "disable_animations", media: "all" if Rails.env.test? || Gitlab.config.gitlab['disable_animations']
= stylesheet_link_tag "test_environment", media: "all" if Rails.env.test? = stylesheet_link_tag "test_environment", media: "all" if Rails.env.test?

View File

@ -54,6 +54,8 @@
.settings-content .settings-content
= render 'shared/badges/badge_settings' = render 'shared/badges/badge_settings'
= render_if_exists 'compliance_management/compliance_framework/project_settings', expanded: expanded
= render_if_exists 'projects/settings/default_issue_template' = render_if_exists 'projects/settings/default_issue_template'
= render 'projects/service_desk_settings' = render 'projects/service_desk_settings'

View File

@ -1,7 +1,6 @@
- add_page_specific_style 'page_bundles/members' - add_page_specific_style 'page_bundles/members'
- page_title _("Members") - page_title _("Members")
.js-remove-member-modal
.row.gl-mt-3 .row.gl-mt-3
.col-lg-12 .col-lg-12
- if can_invite_members_for_project?(@project) || can_invite_group_for_project?(@project) - if can_invite_members_for_project?(@project) || can_invite_group_for_project?(@project)

View File

@ -19,8 +19,6 @@
= f.text_field :topics, value: @project.topic_list.join(', '), maxlength: 2000, class: "form-control gl-form-input" = f.text_field :topics, value: @project.topic_list.join(', '), maxlength: 2000, class: "form-control gl-form-input"
%p.form-text.text-muted= _('Separate topics with commas.') %p.form-text.text-muted= _('Separate topics with commas.')
= render_if_exists 'compliance_management/compliance_framework/project_settings', f: f
.row .row
.form-group.col-md-9 .form-group.col-md-9
= f.label :description, _('Project description (optional)'), class: 'label-bold' = f.label :description, _('Project description (optional)'), class: 'label-bold'

View File

@ -31,8 +31,6 @@
= render 'shared/issuable/form/metadata', issuable: issuable, form: form, project: project, presenter: presenter = render 'shared/issuable/form/metadata', issuable: issuable, form: form, project: project, presenter: presenter
= render_if_exists 'shared/issuable/approvals', issuable: issuable, presenter: presenter, form: form
= render 'shared/issuable/form/merge_params', issuable: issuable, project: project = render 'shared/issuable/form/merge_params', issuable: issuable, project: project
= render 'shared/issuable/form/contribution', issuable: issuable, form: form = render 'shared/issuable/form/contribution', issuable: issuable, form: form

View File

@ -8,5 +8,4 @@
= hidden_field_tag "#{issuable.to_ability_name}[reviewer_ids][]", 0, id: nil, data: { meta: '' } = hidden_field_tag "#{issuable.to_ability_name}[reviewer_ids][]", 0, id: nil, data: { meta: '' }
= dropdown_tag(users_dropdown_label(issuable.reviewers), options: reviewers_dropdown_options(issuable.to_ability_name, issuable.iid, issuable.target_branch)) = dropdown_tag(users_dropdown_label(issuable.reviewers), options: reviewers_dropdown_options(issuable.to_ability_name, issuable.iid, issuable.target_branch))
- if Feature.enabled?(:mr_collapsed_approval_rules, @project) = render_if_exists 'shared/issuable/approver_suggestion', issuable: issuable, presenter: presenter
= render_if_exists 'shared/issuable/approver_suggestion', issuable: issuable, presenter: presenter

View File

@ -1,8 +0,0 @@
---
name: mr_collapsed_approval_rules
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/47475
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/284052
milestone: '13.6'
type: development
group: group::source code
default_enabled: false

View File

@ -70,7 +70,7 @@ class MigrateK8sServiceIntegration < ActiveRecord::Migration[5.1]
private private
def parsed_properties def parsed_properties
@parsed_properties ||= JSON.parse(self.properties) @parsed_properties ||= JSON.parse(self.properties) # rubocop:disable Gitlab/Json
end end
end end

View File

@ -159,6 +159,27 @@ This machine's Geo node name matches a database record ... no
doc/administration/geo/replication/troubleshooting.md#can-geo-detect-the-current-node-correctly doc/administration/geo/replication/troubleshooting.md#can-geo-detect-the-current-node-correctly
``` ```
### Message: `WARNING: oldest xmin is far in the past` and `pg_wal` size growing
If a replication slot is inactive,
the `pg_wal` logs corresponding to the slot are reserved forever
(or until the slot is active again). This causes continuous disk usage growth
and the following messages appear repeatedly in the
[PostgreSQL logs](../../logs.md#postgresql-logs):
```plaintext
WARNING: oldest xmin is far in the past
HINT: Close open transactions soon to avoid wraparound problems.
You might also need to commit or roll back old prepared transactions, or drop stale replication slots.
```
To fix this, do the following:
1. [Connect to the primary database](https://docs.gitlab.com/omnibus/settings/database.html#connecting-to-the-bundled-postgresql-database).
1. Run `SELECT * FROM pg_replication_slots;`.
1. Note the `slot_name` that reports `active` as `f` (false).
1. Follow [all these steps to remove that Geo site](remove_geo_site.md).
## Fixing errors found when running the Geo check Rake task ## Fixing errors found when running the Geo check Rake task
When running this Rake task, you may see errors if the nodes are not properly configured: When running this Rake task, you may see errors if the nodes are not properly configured:
@ -325,7 +346,8 @@ log data to build up in `pg_xlog`. Removing the unused slots can reduce the amou
Slots where `active` is `f` are not active. Slots where `active` is `f` are not active.
- When this slot should be active, because you have a **secondary** node configured using that slot, - When this slot should be active, because you have a **secondary** node configured using that slot,
log in to that **secondary** node and check the PostgreSQL logs why the replication is not running. log in to that **secondary** node and check the [PostgreSQL logs](../../logs.md#postgresql-logs)
to view why the replication is not running.
- If you are no longer using the slot (for example, you no longer have Geo enabled), you can remove it with in the - If you are no longer using the slot (for example, you no longer have Geo enabled), you can remove it with in the
PostgreSQL console session: PostgreSQL console session:
@ -521,7 +543,7 @@ to start again from scratch, there are a few steps that can help you:
gitlab-ctl stop geo-logcursor gitlab-ctl stop geo-logcursor
``` ```
You can watch Sidekiq logs to know when Sidekiq jobs processing have finished: You can watch the [Sidekiq logs](../../logs.md#sidekiq-logs) to know when Sidekiq jobs processing has finished:
```shell ```shell
gitlab-ctl tail sidekiq gitlab-ctl tail sidekiq

View File

@ -0,0 +1,74 @@
---
stage: none
group: unassigned
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# HAML
[HAML](https://haml.info/) is the [Ruby on Rails](https://rubyonrails.org/) template language that GitLab uses.
## GitLab UI form builder
[GitLab UI](https://gitlab-org.gitlab.io/gitlab-ui/) is a Vue component library that conforms
to the [Pajamas design system](https://design.gitlab.com/). Most of these components
rely on JavaScript and therefore can only be used in Vue.
However, some of the simpler components (checkboxes, radio buttons, form inputs) can be
used in HAML by applying the correct CSS classes to the elements. A custom
[Ruby on Rails form builder](https://gitlab.com/gitlab-org/gitlab/-/blob/7c108df101e86d8a27d69df2b5b1ff1fc24133c5/lib/gitlab/form_builders/gitlab_ui_form_builder.rb) exists to help use GitLab UI components in HAML.
### Use the GitLab UI form builder
To use the GitLab UI form builder:
1. Change `form_for` to `gitlab_ui_form_for`.
1. Change `f.check_box` to `f.gitlab_ui_checkbox_component`.
1. Remove `f.label` and instead pass the label as the second argument in `f.gitlab_ui_checkbox_component`.
For example:
- Before:
```haml
= gitlab_ui_form_for @group do |f|
.form-group.gl-mb-3
.gl-form-checkbox.custom-control.custom-checkbox
= f.check_box :prevent_sharing_groups_outside_hierarchy, disabled: !can_change_prevent_sharing_groups_outside_hierarchy?(@group), class: 'custom-control-input'
= f.label :prevent_sharing_groups_outside_hierarchy, class: 'custom-control-label' do
%span
= s_('GroupSettings|Prevent members from sending invitations to groups outside of %{group} and its subgroups.').html_safe % { group: link_to_group(@group) }
%p.help-text= prevent_sharing_groups_outside_hierarchy_help_text(@group)
```
- After:
```haml
= gitlab_ui_form_for @group do |f|
.form-group.gl-mb-3
= f.gitlab_ui_checkbox_component :prevent_sharing_groups_outside_hierarchy,
s_('GroupSettings|Prevent members from sending invitations to groups outside of %{group} and its subgroups.').html_safe % { group: link_to_group(@group) },
help_text: prevent_sharing_groups_outside_hierarchy_help_text(@group),
checkbox_options: { disabled: !can_change_prevent_sharing_groups_outside_hierarchy?(@group) }
```
### Available components
When using the GitLab UI form builder, the following components are available for use in HAML.
NOTE:
Currently only `gitlab_ui_checkbox_component` is available but more components are planned.
#### gitlab_ui_checkbox_component
[GitLab UI Docs](https://gitlab-org.gitlab.io/gitlab-ui/?path=/story/base-form-form-checkbox--default)
| Argument | Description | Type | Required (default value) |
|---|---|---|---|
| `method` | Attribute on the object passed to `gitlab_ui_form_for`. | `Symbol` | `true` |
| `label` | Checkbox label. | `String` | `true` |
| `help_text` | Help text displayed below the checkbox. | `String` | `false` (`nil`) |
| `checkbox_options` | Options that are passed to [Rails `check_box` method](https://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html#method-i-check_box). | `Hash` | `false` (`{}`) |
| `checked_value` | Value when checkbox is checked. | `String` | `false` (`'1'`) |
| `unchecked_value` | Value when checkbox is unchecked. | `String` | `false` (`'0'`) |
| `label_options` | Options that are passed to [Rails `label` method](https://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html#method-i-label). | `Hash` | `false` (`{}`) |

View File

@ -81,6 +81,10 @@ Vue specific [design patterns and practices](vue.md).
How to use [GraphQL](graphql.md). How to use [GraphQL](graphql.md).
## HAML
How to use [HAML](haml.md).
## Icons and Illustrations ## Icons and Illustrations
How we use SVG for our [Icons and Illustrations](icons.md). How we use SVG for our [Icons and Illustrations](icons.md).

View File

@ -466,6 +466,24 @@ If we expect an increase of **less than 5%**, then no further action is needed.
Otherwise, please ping `@gitlab-org/scalability` on the merge request and ask Otherwise, please ping `@gitlab-org/scalability` on the merge request and ask
for a review. for a review.
## Job size
GitLab stores Sidekiq jobs and their arguments in Redis. To avoid
excessive memory usage, we compress the arguments of Sidekiq jobs
if their original size is bigger than 100KB.
After compression, if their size still exceeds 5MB, it raises an
[`ExceedLimitError`](https://gitlab.com/gitlab-org/gitlab/-/blob/f3dd89e5e510ea04b43ffdcb58587d8f78a8d77c/lib/gitlab/sidekiq_middleware/size_limiter/exceed_limit_error.rb#L8)
error when scheduling the job.
If this happens, rely on other means of making the data
available in Sidekiq. There are possible workarounds such as:
- Rebuild the data in Sidekiq with data loaded from the database or
elsewhere.
- Store the data in [object storage](file_storage.md#object-storage)
before scheduling the job, and retrieve it inside the job.
## Job data consistency strategies ## Job data consistency strategies
In GitLab 13.11 and earlier, Sidekiq workers would always send database queries to the primary In GitLab 13.11 and earlier, Sidekiq workers would always send database queries to the primary

View File

@ -1050,9 +1050,10 @@ is being discussed in [issue #17517](https://gitlab.com/gitlab-org/gitlab/-/issu
## Alternative backup strategies ## Alternative backup strategies
If your GitLab server contains a lot of Git repository data, you may find the If your GitLab instance contains a lot of Git repository data, you may find the
GitLab backup script to be too slow. In this case you can consider using GitLab backup script to be too slow. If your GitLab instance has a lot of forked
file system snapshots as part of your backup strategy. projects, the regular backup task also duplicates the Git data for all of them.
In these cases, consider using file system snapshots as part of your backup strategy.
Example: Amazon EBS Example: Amazon EBS
@ -1074,6 +1075,71 @@ VM snapshots of the entire GitLab server. It's not uncommon however for a VM
snapshot to require you to power down the server, which limits this solution's snapshot to require you to power down the server, which limits this solution's
practical use. practical use.
### Back up repository data separately
First, ensure you back up existing GitLab data while [skipping repositories](#excluding-specific-directories-from-the-backup):
```shell
# for Omnibus GitLab package installations
sudo gitlab-backup create SKIP=repositories
# for installations from source:
sudo -u git -H bundle exec rake gitlab:backup:create SKIP=repositories RAILS_ENV=production
```
For manually backing up the Git repository data on disk, there are multiple possible strategies:
- Use snapshots, such as the previous examples of Amazon EBS drive snapshots, or LVM snapshots + rsync.
- Use [GitLab Geo](../administration/geo/index.md) and rely on the repository data on a Geo secondary site.
- [Prevent writes and copy the Git repository data](#prevent-writes-and-copy-the-git-repository-data).
- [Create an online backup by marking repositories as read-only (experimental)](#online-backup-through-marking-repositories-as-read-only-experimental).
#### Prevent writes and copy the Git repository data
Git repositories must be copied in a consistent way. They should not be copied during concurrent write
operations, as this can lead to inconsistencies or corruption issues. For more details,
[issue 270422](https://gitlab.com/gitlab-org/gitlab/-/issues/270422 "Provide documentation on preferred method of migrating Gitaly servers")
has a longer discussion explaining the potential problems.
To prevent writes to the Git repository data, there are two possible approaches:
- Use [maintenance mode](../administration/maintenance_mode/index.md) to place GitLab in a read-only state.
- Create explicit downtime by stopping all Gitaly services before backing up the repositories:
```shell
sudo gitlab-ctl stop gitaly
# execute git data copy step
sudo gitlab-ctl start gitaly
```
You can copy Git repository data using any method, as long as writes are prevented on the data being copied
(to prevent inconsistencies and corruption issues). In order of preference and safety, the recommended methods are:
1. Use `rsync` with archive-mode, delete, and checksum options, for example:
```shell
rsync -aR --delete --checksum source destination # be extra safe with the order as it will delete existing data if inverted
```
1. Use a [`tar` pipe to copy the entire repository's directory to another server or location](../administration/operations/moving_repositories.md#tar-pipe-to-another-server).
1. Use `sftp`, `scp`, `cp`, or any other copying method.
#### Online backup through marking repositories as read-only (experimental)
One way of backing up repositories without requiring instance-wide downtime
is to programmatically mark projects as read-only while copying the underlying data.
There are a few possible downsides to this:
- Repositories are read-only for a period of time that scales with the size of the repository.
- Backups take a longer time to complete due to marking each project as read-only, potentially leading to inconsistencies. For example,
a possible date discrepancy between the last data available for the first project that gets backed up compared to
the last project that gets backed up.
- Fork networks should be entirely read-only while the projects inside get backed up to prevent potential changes to the pool repository.
There is an **experimental** script that attempts to automate this process in [Snippet 2149205](https://gitlab.com/-/snippets/2149205).
## Backup and restore for installations using PgBouncer ## Backup and restore for installations using PgBouncer
Do NOT backup or restore GitLab through a PgBouncer connection. These Do NOT backup or restore GitLab through a PgBouncer connection. These

View File

@ -0,0 +1,139 @@
---
stage: Configure
group: Configure
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# Infrastructure as Code with Terraform and GitLab **(FREE)**
## Motivation
The Terraform integration features in GitLab enable your GitOps / Infrastructure-as-Code (IaC)
workflows to tie into GitLab authentication and authorization. These features focus on
lowering the barrier to entry for teams to adopt Terraform, collaborate effectively in
GitLab, and support Terraform best practices.
## Quick Start
Use the following `.gitlab-ci.yml` to set up a basic Terraform project integration
for GitLab versions 14.0 and later:
```yaml
include:
- template: Terraform.gitlab-ci.yml
variables:
# If not using GitLab's HTTP backend, remove this line and specify TF_HTTP_* variables
TF_STATE_NAME: default
TF_CACHE_KEY: default
# If your terraform files are in a subdirectory, set TF_ROOT accordingly
# TF_ROOT: terraform/production
```
This template includes some opinionated decisions, which you can override:
- Including the latest [GitLab Terraform Image](https://gitlab.com/gitlab-org/terraform-images).
- Using the [GitLab managed Terraform State](#gitlab-managed-terraform-state) as
the Terraform state storage backend.
- Creating [four pipeline stages](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Terraform.gitlab-ci.yml):
`init`, `validate`, `build`, and `deploy`. These stages
[run the Terraform commands](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Terraform/Base.gitlab-ci.yml)
`init`, `validate`, `plan`, `plan-json`, and `apply`. The `apply` command only runs on the default branch.
This video from January 2021 walks you through all the GitLab Terraform integration features:
<div class="video-fallback">
See the video: <a href="https://www.youtube.com/watch?v=iGXjUrkkzDI">Terraform with GitLab</a>.
</div>
<figure class="video-container">
<iframe src="https://www.youtube.com/embed/iGXjUrkkzDI" frameborder="0" allowfullscreen="true"> </iframe>
</figure>
## GitLab Managed Terraform state
[Terraform remote backends](https://www.terraform.io/docs/language/settings/backends/index.html)
enable you to store the state file in a remote, shared store. GitLab uses the
[Terraform HTTP backend](https://www.terraform.io/docs/language/settings/backends/http.html)
to securely store the state files in local storage (the default) or
[the remote store of your choice](../../../administration/terraform_state.md).
The GitLab managed Terraform state backend can store your Terraform state easily and
securely. It spares you from setting up additional remote resources like
Amazon S3 or Google Cloud Storage. Its features include:
- Supporting encryption of the state file both in transit and at rest.
- Locking and unlocking state.
- Remote Terraform plan and apply execution.
Read more on setting up and [using GitLab Managed Terraform states](../terraform_state.md)
WARNING:
Like any other job artifact, Terraform plan data is [viewable by anyone with Guest access](../../permissions.md) to the repository.
Neither Terraform nor GitLab encrypts the plan file by default. If your Terraform plan
includes sensitive data such as passwords, access tokens, or certificates, GitLab strongly
recommends encrypting plan output or modifying the project visibility settings.
## Terraform module registry
GitLab can be used as a [Terraform module registry](../../packages/terraform_module_registry/index.md)
to create and publish Terraform modules to a private registry specific to your
top-level namespace.
## Terraform integration in Merge Requests
Collaborating around Infrastructure as Code (IaC) changes requires both code changes
and expected infrastructure changes to be checked and approved. GitLab provides a
solution to help collaboration around Terraform code changes and their expected
effects using the Merge Request pages. This way users don't have to build custom
tools or rely on 3rd party solutions to streamline their IaC workflows.
Read more on setting up and [using the merge request integrations](../mr_integration.md).
## The GitLab Terraform provider
WARNING:
The GitLab Terraform provider is released separately from GitLab.
We are working on migrating the GitLab Terraform provider for GitLab.com.
You can use the [GitLab Terraform provider](https://github.com/gitlabhq/terraform-provider-gitlab)
to manage various aspects of GitLab using Terraform. The provider is an open source project,
owned by GitLab, where everyone can contribute.
The [documentation of the provider](https://registry.terraform.io/providers/gitlabhq/gitlab/latest/docs)
is available as part of the official Terraform provider documentations.
## Create a new cluster through IaC
Learn how to [create a new cluster on Google Kubernetes Engine (GKE)](../clusters/connect/new_gke_cluster.md).
## Troubleshooting
### `gitlab_group_share_group` resources not detected when subgroup state is refreshed
The GitLab Terraform provider can fail to detect existing `gitlab_group_share_group` resources
due to the issue ["User with permissions cannot retrieve `share_with_groups` from the API"](https://gitlab.com/gitlab-org/gitlab/-/issues/328428).
This results in an error when running `terraform apply` because Terraform attempts to recreate an
existing resource.
For example, consider the following group/subgroup configuration:
```plaintext
parent-group
├── subgroup-A
└── subgroup-B
```
Where:
- User `user-1` creates `parent-group`, `subgroup-A`, and `subgroup-B`.
- `subgroup-A` is shared with `subgroup-B`.
- User `terraform-user` is member of `parent-group` with inherited `owner` access to both subgroups.
When the Terraform state is refreshed, the API query `GET /groups/:subgroup-A_id` issued by the provider does not return the
details of `subgroup-B` in the `shared_with_groups` array. This leads to the error.
To workaround this issue, make sure to apply one of the following conditions:
1. The `terraform-user` creates all subgroup resources.
1. Grant Maintainer or Owner role to the `terraform-user` user on `subgroup-B`.
1. The `terraform-user` inherited access to `subgroup-B` and `subgroup-B` contains at least one project.

View File

@ -4,136 +4,11 @@ group: Configure
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
--- ---
# Infrastructure as code with Terraform and GitLab **(FREE)** # Infrastructure management **(FREE)**
## Motivation GitLab provides you with great solutions to help you manage your
infrastrucure:
The Terraform integration features in GitLab enable your GitOps / Infrastructure-as-Code (IaC) - [Infrastructure as Code and GitOps](iac/index.md)
workflows to tie into GitLab authentication and authorization. These features focus on - [Kubernetes clusters](../project/clusters/index.md)
lowering the barrier to entry for teams to adopt Terraform, collaborate effectively in - [Runbooks](../project/clusters/runbooks/index.md)
GitLab, and support Terraform best practices.
## Quick Start
Use the following `.gitlab-ci.yml` to set up a basic Terraform project integration
for GitLab versions 14.0 and later:
```yaml
include:
- template: Terraform.gitlab-ci.yml
variables:
# If not using GitLab's HTTP backend, remove this line and specify TF_HTTP_* variables
TF_STATE_NAME: default
TF_CACHE_KEY: default
# If your terraform files are in a subdirectory, set TF_ROOT accordingly
# TF_ROOT: terraform/production
```
This template includes some opinionated decisions, which you can override:
- Including the latest [GitLab Terraform Image](https://gitlab.com/gitlab-org/terraform-images).
- Using the [GitLab managed Terraform State](#gitlab-managed-terraform-state) as
the Terraform state storage backend.
- Creating [four pipeline stages](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Terraform.gitlab-ci.yml):
`init`, `validate`, `build`, and `deploy`. These stages
[run the Terraform commands](https://gitlab.com/gitlab-org/gitlab/-/blob/master/lib/gitlab/ci/templates/Terraform/Base.gitlab-ci.yml)
`init`, `validate`, `plan`, `plan-json`, and `apply`. The `apply` command only runs on the default branch.
This video from January 2021 walks you through all the GitLab Terraform integration features:
<div class="video-fallback">
See the video: <a href="https://www.youtube.com/watch?v=iGXjUrkkzDI">Terraform with GitLab</a>.
</div>
<figure class="video-container">
<iframe src="https://www.youtube.com/embed/iGXjUrkkzDI" frameborder="0" allowfullscreen="true"> </iframe>
</figure>
## GitLab Managed Terraform state
[Terraform remote backends](https://www.terraform.io/docs/language/settings/backends/index.html)
enable you to store the state file in a remote, shared store. GitLab uses the
[Terraform HTTP backend](https://www.terraform.io/docs/language/settings/backends/http.html)
to securely store the state files in local storage (the default) or
[the remote store of your choice](../../administration/terraform_state.md).
The GitLab managed Terraform state backend can store your Terraform state easily and
securely. It spares you from setting up additional remote resources like
Amazon S3 or Google Cloud Storage. Its features include:
- Supporting encryption of the state file both in transit and at rest.
- Locking and unlocking state.
- Remote Terraform plan and apply execution.
Read more on setting up and [using GitLab Managed Terraform states](terraform_state.md)
WARNING:
Like any other job artifact, Terraform plan data is [viewable by anyone with Guest access](../permissions.md) to the repository.
Neither Terraform nor GitLab encrypts the plan file by default. If your Terraform plan
includes sensitive data such as passwords, access tokens, or certificates, GitLab strongly
recommends encrypting plan output or modifying the project visibility settings.
## Terraform module registry
GitLab can be used as a [Terraform module registry](../packages/terraform_module_registry/index.md)
to create and publish Terraform modules to a private registry specific to your
top-level namespace.
## Terraform integration in Merge Requests
Collaborating around Infrastructure as Code (IaC) changes requires both code changes
and expected infrastructure changes to be checked and approved. GitLab provides a
solution to help collaboration around Terraform code changes and their expected
effects using the Merge Request pages. This way users don't have to build custom
tools or rely on 3rd party solutions to streamline their IaC workflows.
Read more on setting up and [using the merge request integrations](mr_integration.md).
## The GitLab Terraform provider
WARNING:
The GitLab Terraform provider is released separately from GitLab.
We are working on migrating the GitLab Terraform provider for GitLab.com.
You can use the [GitLab Terraform provider](https://github.com/gitlabhq/terraform-provider-gitlab)
to manage various aspects of GitLab using Terraform. The provider is an open source project,
owned by GitLab, where everyone can contribute.
The [documentation of the provider](https://registry.terraform.io/providers/gitlabhq/gitlab/latest/docs)
is available as part of the official Terraform provider documentations.
## Create a new cluster through IaC
Learn how to [create a new cluster on Google Kubernetes Engine (GKE)](clusters/connect/new_gke_cluster.md).
## Troubleshooting
### `gitlab_group_share_group` resources not detected when subgroup state is refreshed
The GitLab Terraform provider can fail to detect existing `gitlab_group_share_group` resources
due to the issue ["User with permissions cannot retrieve `share_with_groups` from the API"](https://gitlab.com/gitlab-org/gitlab/-/issues/328428).
This results in an error when running `terraform apply` because Terraform attempts to recreate an
existing resource.
For example, consider the following group/subgroup configuration:
```plaintext
parent-group
├── subgroup-A
└── subgroup-B
```
Where:
- User `user-1` creates `parent-group`, `subgroup-A`, and `subgroup-B`.
- `subgroup-A` is shared with `subgroup-B`.
- User `terraform-user` is member of `parent-group` with inherited `owner` access to both subgroups.
When the Terraform state is refreshed, the API query `GET /groups/:subgroup-A_id` issued by the provider does not return the
details of `subgroup-B` in the `shared_with_groups` array. This leads to the error.
To workaround this issue, make sure to apply one of the following conditions:
1. The `terraform-user` creates all subgroup resources.
1. Grant Maintainer or Owner role to the `terraform-user` user on `subgroup-B`.
1. The `terraform-user` inherited access to `subgroup-B` and `subgroup-B` contains at least one project.

View File

@ -18,7 +18,7 @@ modifies, or destroys.
## Setup ## Setup
NOTE: NOTE:
GitLab ships with a [pre-built CI template](index.md#quick-start) that uses GitLab Managed Terraform state and integrates Terraform changes into merge requests. We recommend customizing the pre-built image and relying on the `gitlab-terraform` helper provided within for a quick setup. GitLab ships with a [pre-built CI template](iac/index.md#quick-start) that uses GitLab Managed Terraform state and integrates Terraform changes into merge requests. We recommend customizing the pre-built image and relying on the `gitlab-terraform` helper provided within for a quick setup.
To manually configure a GitLab Terraform Report artifact requires the following steps: To manually configure a GitLab Terraform Report artifact requires the following steps:

View File

@ -23,7 +23,7 @@ module Gitlab
new(nodes).tsort new(nodes).tsort
rescue TSort::Cyclic rescue TSort::Cyclic
raise ValidationError, 'The pipeline has circular dependencies.' raise ValidationError, 'The pipeline has circular dependencies'
rescue MissingNodeError rescue MissingNodeError
end end

View File

@ -0,0 +1,55 @@
# frozen_string_literal: true
module Gitlab
module FormBuilders
class GitlabUiFormBuilder < ActionView::Helpers::FormBuilder
def gitlab_ui_checkbox_component(
method,
label,
help_text: nil,
checkbox_options: {},
checked_value: '1',
unchecked_value: '0',
label_options: {}
)
@template.content_tag(
:div,
class: 'gl-form-checkbox custom-control custom-checkbox'
) do
@template.check_box(
@object_name,
method,
format_options(checkbox_options, ['custom-control-input']),
checked_value,
unchecked_value
) +
@template.label(
@object_name, method, format_options(label_options, ['custom-control-label'])
) do
if help_text
@template.content_tag(
:span,
label
) +
@template.content_tag(
:p,
help_text,
class: 'help-text'
)
else
label
end
end
end
end
private
def format_options(options, classes)
classes << options[:class]
objectify_options(options.merge({ class: classes.flatten.compact }))
end
end
end
end

View File

@ -27,7 +27,7 @@ module Gitlab
end end
def read_hash def read_hash
ActiveSupport::JSON.decode(IO.read(@path)) Gitlab::Json.parse(IO.read(@path))
rescue StandardError => e rescue StandardError => e
Gitlab::ErrorTracking.log_exception(e) Gitlab::ErrorTracking.log_exception(e)
raise Gitlab::ImportExport::Error, 'Incorrect JSON format' raise Gitlab::ImportExport::Error, 'Incorrect JSON format'

Some files were not shown because too many files have changed in this diff Show More