Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
8e0dafbb66
commit
cb8bd7d594
|
@ -254,7 +254,6 @@ Gitlab/HTTParty:
|
|||
Gitlab/Json:
|
||||
Enabled: true
|
||||
Exclude:
|
||||
- 'db/**/*'
|
||||
- 'qa/**/*'
|
||||
- 'scripts/**/*'
|
||||
- 'tooling/rspec_flaky/**/*'
|
||||
|
|
|
@ -90,45 +90,6 @@ Graphql/OldTypes:
|
|||
- 'app/graphql/mutations/snippets/create.rb'
|
||||
- 'app/graphql/mutations/snippets/update.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/admin/analytics/usage_trends/measurement_type.rb'
|
||||
- 'app/graphql/types/admin/sidekiq_queues/delete_jobs_response_type.rb'
|
||||
|
|
|
@ -1 +1 @@
|
|||
996a4adda765e8ced18c72eca0ebd27848afa3c9
|
||||
818f3d85a2c8e6596376f1d2276aa22660203a6c
|
||||
|
|
|
@ -60,14 +60,14 @@ export default {
|
|||
<gl-button
|
||||
:class="{ selected: !renderTreeList }"
|
||||
class="gl-w-half js-list-view"
|
||||
@click="setRenderTreeList(false)"
|
||||
@click="setRenderTreeList({ renderTreeList: false })"
|
||||
>
|
||||
{{ __('List view') }}
|
||||
</gl-button>
|
||||
<gl-button
|
||||
:class="{ selected: renderTreeList }"
|
||||
class="gl-w-half js-tree-view"
|
||||
@click="setRenderTreeList(true)"
|
||||
@click="setRenderTreeList({ renderTreeList: true })"
|
||||
>
|
||||
{{ __('Tree view') }}
|
||||
</gl-button>
|
||||
|
|
|
@ -93,7 +93,7 @@ export default function initDiffsApp(store) {
|
|||
const treeListStored = localStorage.getItem(TREE_LIST_STORAGE_KEY);
|
||||
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'.
|
||||
// Check for cookie and save that setting for future use.
|
||||
|
@ -104,6 +104,7 @@ export default function initDiffsApp(store) {
|
|||
this.setShowWhitespace({
|
||||
url: this.endpointUpdateUser,
|
||||
showWhitespace: hideWhitespace !== '1',
|
||||
trackClick: false,
|
||||
});
|
||||
Cookies.remove(DIFF_WHITESPACE_COOKIE_NAME);
|
||||
} else {
|
||||
|
@ -111,6 +112,7 @@ export default function initDiffsApp(store) {
|
|||
this.setShowWhitespace({
|
||||
showWhitespace: this.showWhitespaceDefault,
|
||||
updateDatabase: false,
|
||||
trackClick: false,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
|
|
@ -560,12 +560,12 @@ export const closeDiffFileCommentForm = ({ commit }, 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);
|
||||
|
||||
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);
|
||||
|
||||
if (renderTreeList) {
|
||||
|
@ -578,7 +578,7 @@ export const setRenderTreeList = ({ commit }, renderTreeList) => {
|
|||
|
||||
export const setShowWhitespace = async (
|
||||
{ state, commit },
|
||||
{ url, showWhitespace, updateDatabase = true },
|
||||
{ url, showWhitespace, updateDatabase = true, trackClick = true },
|
||||
) => {
|
||||
if (updateDatabase && Boolean(window.gon?.current_user_id)) {
|
||||
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);
|
||||
notesEventHub.$emit('refetchDiffData');
|
||||
|
||||
if (window.gon?.features?.diffSettingsUsageData) {
|
||||
if (window.gon?.features?.diffSettingsUsageData && trackClick) {
|
||||
api.trackRedisHllUserEvent(TRACKING_CLICK_WHITESPACE_SETTING);
|
||||
|
||||
if (showWhitespace) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
import { GlButton, GlTooltipDirective } from '@gitlab/ui';
|
||||
import { mapState } from 'vuex';
|
||||
import { mapActions, mapState } from 'vuex';
|
||||
|
||||
export default {
|
||||
name: 'RemoveMemberButton',
|
||||
|
@ -45,7 +45,7 @@ export default {
|
|||
oncallSchedules: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: () => {},
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
|
@ -54,30 +54,35 @@ export default {
|
|||
return state[this.namespace].memberPath;
|
||||
},
|
||||
}),
|
||||
computedMemberPath() {
|
||||
return this.memberPath.replace(':id', this.memberId);
|
||||
},
|
||||
stringifiedSchedules() {
|
||||
return JSON.stringify(this.oncallSchedules);
|
||||
modalData() {
|
||||
return {
|
||||
isAccessRequest: this.isAccessRequest,
|
||||
isInvite: this.isInvite,
|
||||
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>
|
||||
|
||||
<template>
|
||||
<gl-button
|
||||
v-gl-tooltip.hover
|
||||
class="js-remove-member-button"
|
||||
v-gl-tooltip
|
||||
variant="danger"
|
||||
:title="title"
|
||||
:aria-label="title"
|
||||
: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"
|
||||
@click="showRemoveMemberModal(modalData)"
|
||||
/>
|
||||
</template>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<script>
|
||||
import { GlFormCheckbox, GlModal } from '@gitlab/ui';
|
||||
import * as Sentry from '@sentry/browser';
|
||||
import { parseBoolean } from '~/lib/utils/common_utils';
|
||||
import { mapActions, mapState } from 'vuex';
|
||||
import csrf from '~/lib/utils/csrf';
|
||||
import { s__, __ } from '~/locale';
|
||||
import OncallSchedulesList from '~/vue_shared/components/oncall_schedules_list.vue';
|
||||
|
@ -16,20 +15,33 @@ export default {
|
|||
GlModal,
|
||||
OncallSchedulesList,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
modalData: {},
|
||||
};
|
||||
},
|
||||
inject: ['namespace'],
|
||||
computed: {
|
||||
isAccessRequest() {
|
||||
return parseBoolean(this.modalData.isAccessRequest);
|
||||
},
|
||||
isInvite() {
|
||||
return parseBoolean(this.modalData.isInvite);
|
||||
},
|
||||
...mapState({
|
||||
isAccessRequest(state) {
|
||||
return state[this.namespace].removeMemberModalData.isAccessRequest;
|
||||
},
|
||||
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() {
|
||||
return this.modalData.memberType === 'GroupMember';
|
||||
return this.memberType === 'GroupMember';
|
||||
},
|
||||
actionText() {
|
||||
if (this.isAccessRequest) {
|
||||
|
@ -54,29 +66,13 @@ export default {
|
|||
isPartOfOncallSchedules() {
|
||||
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: {
|
||||
handleClick(event) {
|
||||
const removeButton = event.target.closest('.js-remove-member-button');
|
||||
if (removeButton) {
|
||||
this.modalData = removeButton.dataset;
|
||||
this.$refs.modal.show();
|
||||
}
|
||||
},
|
||||
...mapActions({
|
||||
hideRemoveMemberModal(dispatch) {
|
||||
return dispatch(`${this.namespace}/hideRemoveMemberModal`);
|
||||
},
|
||||
}),
|
||||
submitForm() {
|
||||
this.$refs.form.submit();
|
||||
},
|
||||
|
@ -91,11 +87,13 @@ export default {
|
|||
:action-cancel="$options.actionCancel"
|
||||
:action-primary="actionPrimary"
|
||||
:title="actionText"
|
||||
:visible="removeMemberModalVisible"
|
||||
data-qa-selector="remove_member_modal_content"
|
||||
@primary="submitForm"
|
||||
@hide="hideRemoveMemberModal"
|
||||
>
|
||||
<form ref="form" :action="modalData.memberPath" method="post">
|
||||
<p data-testid="modal-message">{{ modalData.message }}</p>
|
||||
<form ref="form" :action="memberPath" method="post">
|
||||
<p>{{ message }}</p>
|
||||
|
||||
<oncall-schedules-list
|
||||
v-if="isPartOfOncallSchedules"
|
|
@ -7,6 +7,7 @@ import { mergeUrlParams } from '~/lib/utils/url_utility';
|
|||
import initUserPopovers from '~/user_popovers';
|
||||
import { FIELDS, ACTIVE_TAB_QUERY_PARAM_NAME } from '../../constants';
|
||||
import RemoveGroupLinkModal from '../modals/remove_group_link_modal.vue';
|
||||
import RemoveMemberModal from '../modals/remove_member_modal.vue';
|
||||
import CreatedAt from './created_at.vue';
|
||||
import ExpirationDatepicker from './expiration_datepicker.vue';
|
||||
import ExpiresAt from './expires_at.vue';
|
||||
|
@ -29,6 +30,7 @@ export default {
|
|||
MemberActionButtons,
|
||||
RoleDropdown,
|
||||
RemoveGroupLinkModal,
|
||||
RemoveMemberModal,
|
||||
ExpirationDatepicker,
|
||||
LdapOverrideConfirmationModal: () =>
|
||||
import('ee_component/members/components/ldap/ldap_override_confirmation_modal.vue'),
|
||||
|
@ -225,6 +227,7 @@ export default {
|
|||
align="center"
|
||||
/>
|
||||
<remove-group-link-modal />
|
||||
<remove-member-modal />
|
||||
<ldap-override-confirmation-modal />
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -25,6 +25,14 @@ export const hideRemoveGroupLinkModal = ({ commit }) => {
|
|||
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 }) => {
|
||||
try {
|
||||
await axios.put(
|
||||
|
|
|
@ -8,3 +8,6 @@ export const HIDE_ERROR = 'HIDE_ERROR';
|
|||
|
||||
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 SHOW_REMOVE_MEMBER_MODAL = 'SHOW_REMOVE_MEMBER_MODAL';
|
||||
export const HIDE_REMOVE_MEMBER_MODAL = 'HIDE_REMOVE_MEMBER_MODAL';
|
||||
|
|
|
@ -47,4 +47,11 @@ export default {
|
|||
[types.HIDE_REMOVE_GROUP_LINK_MODAL](state) {
|
||||
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;
|
||||
},
|
||||
};
|
||||
|
|
|
@ -20,4 +20,6 @@ export default ({
|
|||
errorMessage: '',
|
||||
removeGroupLinkModalVisible: false,
|
||||
groupLinkToRemove: null,
|
||||
removeMemberModalData: {},
|
||||
removeMemberModalVisible: false,
|
||||
});
|
||||
|
|
|
@ -1,23 +1,3 @@
|
|||
import Vue from 'vue';
|
||||
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);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
mountRemoveMemberModal();
|
||||
|
||||
new UsersSelect(); // eslint-disable-line no-new
|
||||
});
|
||||
new UsersSelect(); // eslint-disable-line no-new
|
||||
|
|
|
@ -1,23 +1,5 @@
|
|||
import Vue from 'vue';
|
||||
import NamespaceSelect from '~/namespace_select';
|
||||
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
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import Vue from 'vue';
|
||||
import { groupMemberRequestFormatter } from '~/groups/members/utils';
|
||||
import groupsSelect from '~/groups_select';
|
||||
import initInviteGroupTrigger from '~/invite_members/init_invite_group_trigger';
|
||||
|
@ -11,21 +10,6 @@ import { initMembersApp } from '~/members';
|
|||
import { MEMBER_TYPES } from '~/members/constants';
|
||||
import { groupLinkRequestFormatter } from '~/members/utils';
|
||||
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'];
|
||||
|
||||
|
@ -71,7 +55,6 @@ initMembersApp(document.querySelector('.js-group-members-list-app'), {
|
|||
groupsSelect();
|
||||
memberExpirationDate();
|
||||
memberExpirationDate('.js-access-expiration-date-groups');
|
||||
mountRemoveMemberModal();
|
||||
initInviteMembersModal();
|
||||
initInviteMembersTrigger();
|
||||
initInviteGroupTrigger();
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import Vue from 'vue';
|
||||
import groupsSelect from '~/groups_select';
|
||||
import initInviteGroupTrigger from '~/invite_members/init_invite_group_trigger';
|
||||
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 { projectMemberRequestFormatter } from '~/projects/members/utils';
|
||||
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();
|
||||
memberExpirationDate();
|
||||
memberExpirationDate('.js-access-expiration-date-groups');
|
||||
mountRemoveMemberModal();
|
||||
initInviteMembersModal();
|
||||
initInviteMembersTrigger();
|
||||
initInviteGroupTrigger();
|
||||
|
|
|
@ -111,7 +111,7 @@ export default {
|
|||
>
|
||||
{{ __('Paused') }}
|
||||
<template #help>
|
||||
{{ __("Paused runners don't accept new jobs") }}
|
||||
{{ s__('Runners|Stop the runner from accepting new jobs.') }}
|
||||
</template>
|
||||
</gl-form-checkbox>
|
||||
|
||||
|
@ -123,14 +123,14 @@ export default {
|
|||
>
|
||||
{{ __('Protected') }}
|
||||
<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>
|
||||
</gl-form-checkbox>
|
||||
|
||||
<gl-form-checkbox v-model="model.runUntagged" data-testid="runner-field-run-untagged">
|
||||
{{ __('Run untagged jobs') }}
|
||||
<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>
|
||||
</gl-form-checkbox>
|
||||
|
||||
|
@ -141,7 +141,7 @@ export default {
|
|||
>
|
||||
{{ __('Lock to current projects') }}
|
||||
<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>
|
||||
</gl-form-checkbox>
|
||||
|
||||
|
|
|
@ -98,6 +98,10 @@ export const COVERAGE_FUZZING_DESCRIPTION = __(
|
|||
export const COVERAGE_FUZZING_HELP_PATH = helpPagePath(
|
||||
'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_DESCRIPTION = __('Find bugs in your code with API fuzzing.');
|
||||
|
@ -262,6 +266,7 @@ export const securityFeatures = [
|
|||
name: COVERAGE_FUZZING_NAME,
|
||||
description: COVERAGE_FUZZING_DESCRIPTION,
|
||||
helpPath: COVERAGE_FUZZING_HELP_PATH,
|
||||
configurationHelpPath: COVERAGE_FUZZING_CONFIG_HELP_PATH,
|
||||
type: REPORT_TYPE_COVERAGE_FUZZING,
|
||||
},
|
||||
];
|
||||
|
|
|
@ -125,7 +125,12 @@ export default {
|
|||
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 }}
|
||||
</gl-button>
|
||||
</template>
|
||||
|
|
|
@ -10,10 +10,6 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap
|
|||
before_action :apply_diff_view_cookie!, only: [:diffs, :diff_for_path]
|
||||
before_action :build_merge_request, except: [:create]
|
||||
|
||||
before_action do
|
||||
push_frontend_feature_flag(:mr_collapsed_approval_rules, @project)
|
||||
end
|
||||
|
||||
def new
|
||||
define_new_vars
|
||||
end
|
||||
|
|
|
@ -55,7 +55,6 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
|
|||
end
|
||||
|
||||
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)
|
||||
end
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ module Resolvers
|
|||
class AlertResolver < BaseResolver
|
||||
include LooksAhead
|
||||
|
||||
argument :iid, GraphQL::STRING_TYPE,
|
||||
argument :iid, GraphQL::Types::String,
|
||||
required: false,
|
||||
description: 'IID of the alert. For example, "1".'
|
||||
|
||||
|
@ -23,11 +23,11 @@ module Resolvers
|
|||
required: true,
|
||||
default_value: 'operations'
|
||||
|
||||
argument :search, GraphQL::STRING_TYPE,
|
||||
argument :search, GraphQL::Types::String,
|
||||
description: 'Search query for title, description, service, or monitoring_tool.',
|
||||
required: false
|
||||
|
||||
argument :assignee_username, GraphQL::STRING_TYPE,
|
||||
argument :assignee_username, GraphQL::Types::String,
|
||||
required: false,
|
||||
description: 'Username of a user assigned to the issue.'
|
||||
|
||||
|
|
|
@ -5,11 +5,11 @@ module Resolvers
|
|||
class AlertStatusCountsResolver < BaseResolver
|
||||
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.',
|
||||
required: false
|
||||
|
||||
argument :assignee_username, GraphQL::STRING_TYPE,
|
||||
argument :assignee_username, GraphQL::Types::String,
|
||||
required: false,
|
||||
description: 'Username of a user assigned to the issue.'
|
||||
|
||||
|
|
|
@ -10,10 +10,10 @@ module Resolvers
|
|||
|
||||
alias_method :repository, :object
|
||||
|
||||
argument :paths, [GraphQL::STRING_TYPE],
|
||||
argument :paths, [GraphQL::Types::String],
|
||||
required: true,
|
||||
description: 'Array of desired blob paths.'
|
||||
argument :ref, GraphQL::STRING_TYPE,
|
||||
argument :ref, GraphQL::Types::String,
|
||||
required: false,
|
||||
default_value: nil,
|
||||
description: 'The commit ref to get the blobs from. Default value is HEAD.'
|
||||
|
|
|
@ -14,19 +14,19 @@ module Resolvers
|
|||
|
||||
authorize :read_pipeline
|
||||
|
||||
argument :project_path, GraphQL::ID_TYPE,
|
||||
argument :project_path, GraphQL::Types::ID,
|
||||
required: true,
|
||||
description: 'The project of the CI config.'
|
||||
|
||||
argument :sha, GraphQL::STRING_TYPE,
|
||||
argument :sha, GraphQL::Types::String,
|
||||
required: false,
|
||||
description: "Sha for the pipeline."
|
||||
|
||||
argument :content, GraphQL::STRING_TYPE,
|
||||
argument :content, GraphQL::Types::String,
|
||||
required: true,
|
||||
description: "Contents of `.gitlab-ci.yml`."
|
||||
|
||||
argument :dry_run, GraphQL::BOOLEAN_TYPE,
|
||||
argument :dry_run, GraphQL::Types::Boolean,
|
||||
required: false,
|
||||
description: 'Run pipeline creation simulation, or only do static check.'
|
||||
|
||||
|
|
|
@ -9,12 +9,12 @@ module Resolvers
|
|||
description 'Runner setup instructions.'
|
||||
|
||||
argument :platform,
|
||||
type: GraphQL::STRING_TYPE,
|
||||
type: GraphQL::Types::String,
|
||||
required: true,
|
||||
description: 'Platform to generate the instructions for.'
|
||||
|
||||
argument :architecture,
|
||||
type: GraphQL::STRING_TYPE,
|
||||
type: GraphQL::Types::String,
|
||||
required: true,
|
||||
description: 'Architecture to generate the instructions for.'
|
||||
|
||||
|
|
|
@ -15,11 +15,11 @@ module Resolvers
|
|||
required: false,
|
||||
description: 'Filter runners by type.'
|
||||
|
||||
argument :tag_list, [GraphQL::STRING_TYPE],
|
||||
argument :tag_list, [GraphQL::Types::String],
|
||||
required: false,
|
||||
description: 'Filter by tags associated with the runner (comma-separated or array).'
|
||||
|
||||
argument :search, GraphQL::STRING_TYPE,
|
||||
argument :search, GraphQL::Types::String,
|
||||
required: false,
|
||||
description: 'Filter by full token or partial text in description field.'
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ module Resolvers
|
|||
class TemplateResolver < BaseResolver
|
||||
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`.'
|
||||
|
||||
alias_method :project, :object
|
||||
|
|
|
@ -11,7 +11,7 @@ module Resolvers
|
|||
|
||||
alias_method :pipeline, :object
|
||||
|
||||
argument :build_ids, [GraphQL::ID_TYPE],
|
||||
argument :build_ids, [GraphQL::Types::ID],
|
||||
required: true,
|
||||
description: 'IDs of the builds used to run the test suite.'
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ module GroupIssuableResolver
|
|||
|
||||
class_methods do
|
||||
def include_subgroups(name_of_things)
|
||||
argument :include_subgroups, GraphQL::BOOLEAN_TYPE,
|
||||
argument :include_subgroups, GraphQL::Types::Boolean,
|
||||
required: false,
|
||||
default_value: false,
|
||||
description: "Include #{name_of_things} belonging to subgroups"
|
||||
|
|
|
@ -6,29 +6,29 @@ module IssueResolverArguments
|
|||
prepended do
|
||||
include LooksAhead
|
||||
|
||||
argument :iid, GraphQL::STRING_TYPE,
|
||||
argument :iid, GraphQL::Types::String,
|
||||
required: false,
|
||||
description: 'IID of the issue. For example, "1".'
|
||||
argument :iids, [GraphQL::STRING_TYPE],
|
||||
argument :iids, [GraphQL::Types::String],
|
||||
required: false,
|
||||
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,
|
||||
description: 'Labels applied to this issue.'
|
||||
argument :milestone_title, [GraphQL::STRING_TYPE, null: true],
|
||||
argument :milestone_title, [GraphQL::Types::String, null: true],
|
||||
required: false,
|
||||
description: 'Milestone applied to this issue.'
|
||||
argument :author_username, GraphQL::STRING_TYPE,
|
||||
argument :author_username, GraphQL::Types::String,
|
||||
required: false,
|
||||
description: 'Username of the author of the issue.'
|
||||
argument :assignee_username, GraphQL::STRING_TYPE,
|
||||
argument :assignee_username, GraphQL::Types::String,
|
||||
required: false,
|
||||
description: 'Username of a user assigned to the issue.',
|
||||
deprecated: { reason: 'Use `assigneeUsernames`', milestone: '13.11' }
|
||||
argument :assignee_usernames, [GraphQL::STRING_TYPE],
|
||||
argument :assignee_usernames, [GraphQL::Types::String],
|
||||
required: false,
|
||||
description: 'Usernames of users assigned to the issue.'
|
||||
argument :assignee_id, GraphQL::STRING_TYPE,
|
||||
argument :assignee_id, GraphQL::Types::String,
|
||||
required: false,
|
||||
description: 'ID of a user assigned to the issues, "none" and "any" values are supported.'
|
||||
argument :created_before, Types::TimeType,
|
||||
|
@ -49,7 +49,7 @@ module IssueResolverArguments
|
|||
argument :closed_after, Types::TimeType,
|
||||
required: false,
|
||||
description: 'Issues closed after this date.'
|
||||
argument :search, GraphQL::STRING_TYPE,
|
||||
argument :search, GraphQL::Types::String,
|
||||
required: false,
|
||||
description: 'Search query for issue title or description.'
|
||||
argument :types, [Types::IssueTypeEnum],
|
||||
|
|
|
@ -10,11 +10,11 @@ module ResolvesPipelines
|
|||
required: false,
|
||||
description: "Filter pipelines by their status."
|
||||
argument :ref,
|
||||
GraphQL::STRING_TYPE,
|
||||
GraphQL::Types::String,
|
||||
required: false,
|
||||
description: "Filter pipelines by the ref they are run for."
|
||||
argument :sha,
|
||||
GraphQL::STRING_TYPE,
|
||||
GraphQL::Types::String,
|
||||
required: false,
|
||||
description: "Filter pipelines by the sha of the commit they are run for."
|
||||
end
|
||||
|
|
|
@ -6,7 +6,7 @@ module Resolvers
|
|||
|
||||
type Types::ContainerRepositoryType, null: true
|
||||
|
||||
argument :name, GraphQL::STRING_TYPE,
|
||||
argument :name, GraphQL::Types::String,
|
||||
required: false,
|
||||
description: 'Filter the container repositories by their name.'
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ module Resolvers
|
|||
required: false,
|
||||
description: 'Find a design by its ID.'
|
||||
|
||||
argument :filename, GraphQL::STRING_TYPE,
|
||||
argument :filename, GraphQL::Types::String,
|
||||
required: false,
|
||||
description: 'Find a design by its filename.'
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ module Resolvers
|
|||
argument :ids, [DesignID],
|
||||
required: false,
|
||||
description: 'Filters designs by their ID.'
|
||||
argument :filenames, [GraphQL::STRING_TYPE],
|
||||
argument :filenames, [GraphQL::Types::String],
|
||||
required: false,
|
||||
description: 'Filters designs by their filename.'
|
||||
argument :at_version, VersionID,
|
||||
|
|
|
@ -23,7 +23,7 @@ module Resolvers
|
|||
argument :design_id, DesignID,
|
||||
required: false,
|
||||
description: 'The ID of a specific design.'
|
||||
argument :filename, GraphQL::STRING_TYPE,
|
||||
argument :filename, GraphQL::Types::String,
|
||||
required: false,
|
||||
description: 'The filename of a specific design.'
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ module Resolvers
|
|||
required: false,
|
||||
description: 'Filters designs by their ID.'
|
||||
argument :filenames,
|
||||
[GraphQL::STRING_TYPE],
|
||||
[GraphQL::Types::String],
|
||||
required: false,
|
||||
description: 'Filters designs by their filename.'
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@ module Resolvers
|
|||
|
||||
VersionID = ::Types::GlobalIDType[::DesignManagement::Version]
|
||||
|
||||
argument :sha, GraphQL::STRING_TYPE,
|
||||
argument :sha, GraphQL::Types::String,
|
||||
required: false,
|
||||
description: "The SHA256 of a specific version."
|
||||
argument :id, VersionID,
|
||||
|
|
|
@ -11,7 +11,7 @@ module Resolvers
|
|||
|
||||
extras [:parent]
|
||||
|
||||
argument :earlier_or_equal_to_sha, GraphQL::STRING_TYPE,
|
||||
argument :earlier_or_equal_to_sha, GraphQL::Types::String,
|
||||
as: :sha,
|
||||
required: false,
|
||||
description: 'The SHA256 of the most recent acceptable version.'
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
|
||||
module Resolvers
|
||||
class EchoResolver < BaseResolver
|
||||
type ::GraphQL::STRING_TYPE, null: false
|
||||
type ::GraphQL::Types::String, null: false
|
||||
description 'Testing endpoint to validate the API with'
|
||||
|
||||
argument :text,
|
||||
type: GraphQL::STRING_TYPE,
|
||||
type: GraphQL::Types::String,
|
||||
required: true,
|
||||
description: 'Text to echo back.'
|
||||
|
||||
|
|
|
@ -2,15 +2,15 @@
|
|||
|
||||
module Resolvers
|
||||
class EnvironmentsResolver < BaseResolver
|
||||
argument :name, GraphQL::STRING_TYPE,
|
||||
argument :name, GraphQL::Types::String,
|
||||
required: false,
|
||||
description: 'Name of the environment.'
|
||||
|
||||
argument :search, GraphQL::STRING_TYPE,
|
||||
argument :search, GraphQL::Types::String,
|
||||
required: false,
|
||||
description: 'Search query for environment name.'
|
||||
|
||||
argument :states, [GraphQL::STRING_TYPE],
|
||||
argument :states, [GraphQL::Types::String],
|
||||
required: false,
|
||||
description: 'States of environments that should be included in result.'
|
||||
|
||||
|
|
|
@ -6,12 +6,12 @@ module Resolvers
|
|||
type Types::ErrorTracking::SentryErrorType.connection_type, null: true
|
||||
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.',
|
||||
required: false
|
||||
|
||||
# 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.',
|
||||
required: false
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ module Resolvers
|
|||
extend ActiveSupport::Concern
|
||||
|
||||
prepended do
|
||||
argument :full_path, GraphQL::ID_TYPE,
|
||||
argument :full_path, GraphQL::Types::ID,
|
||||
required: true,
|
||||
description: 'The full path of the project, group or namespace, e.g., `gitlab-org/gitlab-foss`.'
|
||||
end
|
||||
|
|
|
@ -4,12 +4,12 @@ module Resolvers
|
|||
class GroupLabelsResolver < LabelsResolver
|
||||
type Types::LabelType.connection_type, null: true
|
||||
|
||||
argument :include_descendant_groups, GraphQL::BOOLEAN_TYPE,
|
||||
argument :include_descendant_groups, GraphQL::Types::Boolean,
|
||||
required: false,
|
||||
description: 'Include labels from descendant groups.',
|
||||
default_value: false
|
||||
|
||||
argument :only_group_labels, GraphQL::BOOLEAN_TYPE,
|
||||
argument :only_group_labels, GraphQL::Types::Boolean,
|
||||
required: false,
|
||||
description: 'Include only group level labels.',
|
||||
default_value: false
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
|
||||
module Resolvers
|
||||
class GroupMilestonesResolver < MilestonesResolver
|
||||
argument :include_descendants, GraphQL::BOOLEAN_TYPE,
|
||||
argument :include_descendants, GraphQL::Types::Boolean,
|
||||
required: false,
|
||||
description: 'Include milestones from all subgroups and subprojects.'
|
||||
argument :include_ancestors, GraphQL::BOOLEAN_TYPE,
|
||||
argument :include_ancestors, GraphQL::Types::Boolean,
|
||||
required: false,
|
||||
description: 'Include milestones from all parent groups.'
|
||||
|
||||
|
|
|
@ -8,11 +8,11 @@ module Resolvers
|
|||
|
||||
type Types::LabelType.connection_type, null: true
|
||||
|
||||
argument :search_term, GraphQL::STRING_TYPE,
|
||||
argument :search_term, GraphQL::Types::String,
|
||||
required: false,
|
||||
description: 'A search term to find labels with.'
|
||||
|
||||
argument :include_ancestor_groups, GraphQL::BOOLEAN_TYPE,
|
||||
argument :include_ancestor_groups, GraphQL::Types::Boolean,
|
||||
required: false,
|
||||
description: 'Include labels from ancestor groups.',
|
||||
default_value: false
|
||||
|
|
|
@ -7,7 +7,7 @@ module Resolvers
|
|||
|
||||
type Types::MemberInterface.connection_type, null: true
|
||||
|
||||
argument :search, GraphQL::STRING_TYPE,
|
||||
argument :search, GraphQL::Types::String,
|
||||
required: false,
|
||||
description: 'Search query.'
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ module Resolvers
|
|||
|
||||
type ::Types::MergeRequestType, null: true
|
||||
|
||||
argument :iid, GraphQL::STRING_TYPE,
|
||||
argument :iid, GraphQL::Types::String,
|
||||
required: true,
|
||||
as: :iids,
|
||||
description: 'IID of the merge request, for example `1`.'
|
||||
|
|
|
@ -10,28 +10,28 @@ module Resolvers
|
|||
alias_method :project, :object
|
||||
|
||||
def self.accept_assignee
|
||||
argument :assignee_username, GraphQL::STRING_TYPE,
|
||||
argument :assignee_username, GraphQL::Types::String,
|
||||
required: false,
|
||||
description: 'Username of the assignee.'
|
||||
end
|
||||
|
||||
def self.accept_author
|
||||
argument :author_username, GraphQL::STRING_TYPE,
|
||||
argument :author_username, GraphQL::Types::String,
|
||||
required: false,
|
||||
description: 'Username of the author.'
|
||||
end
|
||||
|
||||
def self.accept_reviewer
|
||||
argument :reviewer_username, GraphQL::STRING_TYPE,
|
||||
argument :reviewer_username, GraphQL::Types::String,
|
||||
required: false,
|
||||
description: 'Username of the reviewer.'
|
||||
end
|
||||
|
||||
argument :iids, [GraphQL::STRING_TYPE],
|
||||
argument :iids, [GraphQL::Types::String],
|
||||
required: false,
|
||||
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,
|
||||
as: :source_branch,
|
||||
description: <<~DESC
|
||||
|
@ -39,7 +39,7 @@ module Resolvers
|
|||
All resolved merge requests will have one of these branches as their source.
|
||||
DESC
|
||||
|
||||
argument :target_branches, [GraphQL::STRING_TYPE],
|
||||
argument :target_branches, [GraphQL::Types::String],
|
||||
required: false,
|
||||
as: :target_branch,
|
||||
description: <<~DESC
|
||||
|
@ -51,7 +51,7 @@ module Resolvers
|
|||
required: false,
|
||||
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,
|
||||
as: :label_name,
|
||||
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,
|
||||
required: false,
|
||||
description: 'Merge requests merged before this date.'
|
||||
argument :milestone_title, GraphQL::STRING_TYPE,
|
||||
argument :milestone_title, GraphQL::Types::String,
|
||||
required: false,
|
||||
description: 'Title of the milestone.'
|
||||
argument :sort, Types::MergeRequestSortEnum,
|
||||
|
@ -70,11 +70,11 @@ module Resolvers
|
|||
default_value: :created_desc
|
||||
|
||||
negated do
|
||||
argument :labels, [GraphQL::STRING_TYPE],
|
||||
argument :labels, [GraphQL::Types::String],
|
||||
required: false,
|
||||
as: :label_name,
|
||||
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,
|
||||
description: 'Title of the milestone.'
|
||||
end
|
||||
|
|
|
@ -6,7 +6,7 @@ module Resolvers
|
|||
type Types::Metrics::DashboardType, null: true
|
||||
calls_gitaly!
|
||||
|
||||
argument :path, GraphQL::STRING_TYPE,
|
||||
argument :path, GraphQL::Types::String,
|
||||
required: true,
|
||||
description: <<~MD
|
||||
Path to a file which defines a metrics dashboard eg: `"config/prometheus/common_metrics.yml"`.
|
||||
|
|
|
@ -5,7 +5,7 @@ module Resolvers
|
|||
include Gitlab::Graphql::Authorize::AuthorizeResource
|
||||
include TimeFrameArguments
|
||||
|
||||
argument :ids, [GraphQL::ID_TYPE],
|
||||
argument :ids, [GraphQL::Types::ID],
|
||||
required: false,
|
||||
description: 'Array of global milestone IDs, e.g., `"gid://gitlab/Milestone/1"`.'
|
||||
|
||||
|
@ -13,11 +13,11 @@ module Resolvers
|
|||
required: false,
|
||||
description: 'Filter milestones by state.'
|
||||
|
||||
argument :title, GraphQL::STRING_TYPE,
|
||||
argument :title, GraphQL::Types::String,
|
||||
required: false,
|
||||
description: 'The title of the milestone.'
|
||||
|
||||
argument :search_title, GraphQL::STRING_TYPE,
|
||||
argument :search_title, GraphQL::Types::String,
|
||||
required: false,
|
||||
description: 'A search string for the title.'
|
||||
|
||||
|
|
|
@ -2,12 +2,12 @@
|
|||
|
||||
module Resolvers
|
||||
class NamespaceProjectsResolver < BaseResolver
|
||||
argument :include_subgroups, GraphQL::BOOLEAN_TYPE,
|
||||
argument :include_subgroups, GraphQL::Types::Boolean,
|
||||
required: false,
|
||||
default_value: false,
|
||||
description: 'Include also subgroup projects.'
|
||||
|
||||
argument :search, GraphQL::STRING_TYPE,
|
||||
argument :search, GraphQL::Types::String,
|
||||
required: false,
|
||||
default_value: nil,
|
||||
description: 'Search project with most similar names or paths.'
|
||||
|
@ -17,7 +17,7 @@ module Resolvers
|
|||
default_value: nil,
|
||||
description: 'Sort projects by this criteria.'
|
||||
|
||||
argument :ids, [GraphQL::ID_TYPE],
|
||||
argument :ids, [GraphQL::Types::ID],
|
||||
required: false,
|
||||
default_value: nil,
|
||||
description: 'Filter projects by IDs.'
|
||||
|
|
|
@ -9,7 +9,7 @@ module Resolvers
|
|||
required: false,
|
||||
default_value: :created_desc
|
||||
|
||||
argument :package_name, GraphQL::STRING_TYPE,
|
||||
argument :package_name, GraphQL::Types::String,
|
||||
description: 'Search a package by name.',
|
||||
required: false,
|
||||
default_value: nil
|
||||
|
@ -24,7 +24,7 @@ module Resolvers
|
|||
required: false,
|
||||
default_value: nil
|
||||
|
||||
argument :include_versionless, GraphQL::BOOLEAN_TYPE,
|
||||
argument :include_versionless, GraphQL::Types::Boolean,
|
||||
description: 'Include versionless packages.',
|
||||
required: false,
|
||||
default_value: false
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
module Resolvers
|
||||
class ProjectMilestonesResolver < MilestonesResolver
|
||||
argument :include_ancestors, GraphQL::BOOLEAN_TYPE,
|
||||
argument :include_ancestors, GraphQL::Types::Boolean,
|
||||
required: false,
|
||||
description: "Also return milestones in the project's parent group and its ancestors."
|
||||
|
||||
|
|
|
@ -6,11 +6,11 @@ module Resolvers
|
|||
|
||||
alias_method :project, :object
|
||||
|
||||
argument :iid, GraphQL::ID_TYPE,
|
||||
argument :iid, GraphQL::Types::ID,
|
||||
required: false,
|
||||
description: 'IID of the Pipeline. For example, "1".'
|
||||
|
||||
argument :sha, GraphQL::STRING_TYPE,
|
||||
argument :sha, GraphQL::Types::String,
|
||||
required: false,
|
||||
description: 'SHA of the Pipeline. For example, "dyd0f15ay83993f5ab66k927w28673882x99100b".'
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ module Resolvers
|
|||
authorize :admin_project
|
||||
|
||||
argument :name,
|
||||
GraphQL::STRING_TYPE,
|
||||
GraphQL::Types::String,
|
||||
required: false,
|
||||
description: 'Project name or key.'
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ module Resolvers
|
|||
authorizes_object!
|
||||
|
||||
argument :active,
|
||||
GraphQL::BOOLEAN_TYPE,
|
||||
GraphQL::Types::Boolean,
|
||||
required: false,
|
||||
description: 'Indicates if the integration is active.'
|
||||
argument :type,
|
||||
|
|
|
@ -4,27 +4,27 @@ module Resolvers
|
|||
class ProjectsResolver < BaseResolver
|
||||
type Types::ProjectType, null: true
|
||||
|
||||
argument :membership, GraphQL::BOOLEAN_TYPE,
|
||||
argument :membership, GraphQL::Types::Boolean,
|
||||
required: false,
|
||||
description: 'Limit projects that the current user is a member of.'
|
||||
|
||||
argument :search, GraphQL::STRING_TYPE,
|
||||
argument :search, GraphQL::Types::String,
|
||||
required: false,
|
||||
description: 'Search query for project name, path, or description.'
|
||||
|
||||
argument :ids, [GraphQL::ID_TYPE],
|
||||
argument :ids, [GraphQL::Types::ID],
|
||||
required: false,
|
||||
description: 'Filter projects by IDs.'
|
||||
|
||||
argument :search_namespaces, GraphQL::BOOLEAN_TYPE,
|
||||
argument :search_namespaces, GraphQL::Types::Boolean,
|
||||
required: false,
|
||||
description: 'Include namespace in project search.'
|
||||
|
||||
argument :sort, GraphQL::STRING_TYPE,
|
||||
argument :sort, GraphQL::Types::String,
|
||||
required: false,
|
||||
description: 'Sort order of results.'
|
||||
|
||||
argument :topics, type: [GraphQL::STRING_TYPE],
|
||||
argument :topics, type: [GraphQL::Types::String],
|
||||
required: false,
|
||||
description: 'Filters projects by topics.'
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ module Resolvers
|
|||
class ReleaseResolver < BaseResolver
|
||||
type Types::ReleaseType, null: true
|
||||
|
||||
argument :tag_name, GraphQL::STRING_TYPE,
|
||||
argument :tag_name, GraphQL::Types::String,
|
||||
required: true,
|
||||
description: 'The name of the tag associated to the release.'
|
||||
|
||||
|
|
|
@ -2,19 +2,19 @@
|
|||
|
||||
module Resolvers
|
||||
class RepositoryBranchNamesResolver < BaseResolver
|
||||
type ::GraphQL::STRING_TYPE, null: false
|
||||
type ::GraphQL::Types::String, null: false
|
||||
|
||||
calls_gitaly!
|
||||
|
||||
argument :search_pattern, GraphQL::STRING_TYPE,
|
||||
argument :search_pattern, GraphQL::Types::String,
|
||||
required: true,
|
||||
description: 'The pattern to search for branch names by.'
|
||||
|
||||
argument :offset, GraphQL::INT_TYPE,
|
||||
argument :offset, GraphQL::Types::Int,
|
||||
required: true,
|
||||
description: 'The number of branch names to skip.'
|
||||
|
||||
argument :limit, GraphQL::INT_TYPE,
|
||||
argument :limit, GraphQL::Types::Int,
|
||||
required: true,
|
||||
description: 'The number of branch names to return.'
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ module Resolvers
|
|||
|
||||
alias_method :snippet, :object
|
||||
|
||||
argument :paths, [GraphQL::STRING_TYPE],
|
||||
argument :paths, [GraphQL::Types::String],
|
||||
required: false,
|
||||
description: 'Paths of the blobs.'
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ module Resolvers
|
|||
description: 'The type of snippet.'
|
||||
|
||||
argument :explore,
|
||||
GraphQL::BOOLEAN_TYPE,
|
||||
GraphQL::Types::Boolean,
|
||||
required: false,
|
||||
description: 'Explore personal snippets.'
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ module Resolvers
|
|||
alias_method :project, :object
|
||||
|
||||
when_single do
|
||||
argument :name, GraphQL::STRING_TYPE,
|
||||
argument :name, GraphQL::Types::String,
|
||||
required: true,
|
||||
description: 'Name of the Terraform state.'
|
||||
end
|
||||
|
|
|
@ -10,15 +10,15 @@ module Resolvers
|
|||
required: false,
|
||||
description: 'The action to be filtered.'
|
||||
|
||||
argument :author_id, [GraphQL::ID_TYPE],
|
||||
argument :author_id, [GraphQL::Types::ID],
|
||||
required: false,
|
||||
description: 'The ID of an author.'
|
||||
|
||||
argument :project_id, [GraphQL::ID_TYPE],
|
||||
argument :project_id, [GraphQL::Types::ID],
|
||||
required: false,
|
||||
description: 'The ID of a project.'
|
||||
|
||||
argument :group_id, [GraphQL::ID_TYPE],
|
||||
argument :group_id, [GraphQL::Types::ID],
|
||||
required: false,
|
||||
description: 'The ID of a group.'
|
||||
|
||||
|
|
|
@ -6,15 +6,15 @@ module Resolvers
|
|||
|
||||
calls_gitaly!
|
||||
|
||||
argument :path, GraphQL::STRING_TYPE,
|
||||
argument :path, GraphQL::Types::String,
|
||||
required: false,
|
||||
default_value: '',
|
||||
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,
|
||||
default_value: :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,
|
||||
default_value: false,
|
||||
description: 'Used to get a recursive tree. Default is false.'
|
||||
|
|
|
@ -4,7 +4,7 @@ module Resolvers
|
|||
class UserDiscussionsCountResolver < BaseResolver
|
||||
include Gitlab::Graphql::Authorize::AuthorizeResource
|
||||
|
||||
type GraphQL::INT_TYPE, null: true
|
||||
type GraphQL::Types::Int, null: true
|
||||
|
||||
def resolve
|
||||
authorize!(object)
|
||||
|
|
|
@ -5,7 +5,7 @@ module Resolvers
|
|||
include ResolvesProject
|
||||
|
||||
argument :project_path,
|
||||
type: GraphQL::STRING_TYPE,
|
||||
type: GraphQL::Types::String,
|
||||
required: false,
|
||||
description: <<~DESC
|
||||
The full-path of the project the authored merge requests should be in.
|
||||
|
|
|
@ -4,7 +4,7 @@ module Resolvers
|
|||
class UserNotesCountResolver < BaseResolver
|
||||
include Gitlab::Graphql::Authorize::AuthorizeResource
|
||||
|
||||
type GraphQL::INT_TYPE, null: true
|
||||
type GraphQL::Types::Int, null: true
|
||||
|
||||
def resolve
|
||||
authorize!(object)
|
||||
|
|
|
@ -10,7 +10,7 @@ module Resolvers
|
|||
required: false,
|
||||
description: 'ID of the User.'
|
||||
|
||||
argument :username, GraphQL::STRING_TYPE,
|
||||
argument :username, GraphQL::Types::String,
|
||||
required: false,
|
||||
description: 'Username of the User.'
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ module Resolvers
|
|||
class UserStarredProjectsResolver < BaseResolver
|
||||
type Types::ProjectType.connection_type, null: true
|
||||
|
||||
argument :search, GraphQL::STRING_TYPE,
|
||||
argument :search, GraphQL::Types::String,
|
||||
required: false,
|
||||
description: 'Search query.'
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
module Resolvers
|
||||
module Users
|
||||
class GroupCountResolver < BaseResolver
|
||||
type GraphQL::INT_TYPE, null: true
|
||||
type GraphQL::Types::Int, null: true
|
||||
|
||||
alias_method :user, :object
|
||||
|
||||
|
|
|
@ -7,11 +7,11 @@ module Resolvers
|
|||
type Types::UserType.connection_type, null: true
|
||||
description 'Find Users'
|
||||
|
||||
argument :ids, [GraphQL::ID_TYPE],
|
||||
argument :ids, [GraphQL::Types::ID],
|
||||
required: false,
|
||||
description: 'List of user Global IDs.'
|
||||
|
||||
argument :usernames, [GraphQL::STRING_TYPE], required: false,
|
||||
argument :usernames, [GraphQL::Types::String], required: false,
|
||||
description: 'List of usernames.'
|
||||
|
||||
argument :sort, Types::SortEnum,
|
||||
|
@ -19,11 +19,11 @@ module Resolvers
|
|||
required: false,
|
||||
default_value: :created_desc
|
||||
|
||||
argument :search, GraphQL::STRING_TYPE,
|
||||
argument :search, GraphQL::Types::String,
|
||||
required: false,
|
||||
description: "Query to search users by name, username, or primary email."
|
||||
|
||||
argument :admins, GraphQL::BOOLEAN_TYPE,
|
||||
argument :admins, GraphQL::Types::Boolean,
|
||||
required: false,
|
||||
default_value: false,
|
||||
description: 'Return only admin users.'
|
||||
|
|
|
@ -413,6 +413,12 @@ module ApplicationHelper
|
|||
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
|
||||
|
||||
def appearance
|
||||
|
|
|
@ -5,20 +5,42 @@ module Packages
|
|||
class GenerateDistributionKeyService
|
||||
include Gitlab::Utils::StrongMemoize
|
||||
|
||||
def initialize(current_user:, params: {})
|
||||
@current_user = current_user
|
||||
def initialize(params: {})
|
||||
@params = params
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
private
|
||||
|
||||
attr_reader :current_user, :params
|
||||
attr_reader :params
|
||||
|
||||
def passphrase
|
||||
strong_memoize(:passphrase) do
|
||||
|
@ -72,35 +94,6 @@ module Packages
|
|||
}.map { |k, v| "#{k}: #{v}\n" }.join +
|
||||
'</GnupgKeyParms>'
|
||||
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
|
||||
|
|
|
@ -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
|
|
@ -118,6 +118,7 @@
|
|||
- if Gitlab::CurrentSettings.version_check_enabled
|
||||
.float-right
|
||||
= 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
|
||||
= link_to _('GitLab'), general_admin_application_settings_path
|
||||
%span.float-right
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
- page_title @group.name, _("Groups")
|
||||
- current_user_is_group_owner = @group && @group.has_owner?(current_user)
|
||||
|
||||
.js-remove-member-modal
|
||||
%h3.page-title
|
||||
= _('Group: %{group_name}') % { group_name: @group.full_name }
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
- @content_class = "admin-projects"
|
||||
- current_user_is_group_owner = @group && @group.has_owner?(current_user)
|
||||
|
||||
.js-remove-member-modal
|
||||
%h3.page-title
|
||||
= _('Project: %{name}') % { name: @project.full_name }
|
||||
= link_to edit_project_path(@project), class: "btn btn-default gl-button float-right" do
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
- page_title _('Group members')
|
||||
- groups_select_tag_data = group_select_data(@group).merge({ skip_groups: @skip_groups })
|
||||
|
||||
.js-remove-member-modal
|
||||
.row.gl-mt-3
|
||||
.col-lg-12
|
||||
.gl-display-flex.gl-flex-wrap
|
||||
|
|
|
@ -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' }
|
||||
= form_errors(@group)
|
||||
|
||||
|
@ -9,12 +9,10 @@
|
|||
|
||||
- if @group.root?
|
||||
.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.js-descr.help-text= prevent_sharing_groups_outside_hierarchy_help_text(@group)
|
||||
= 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) }
|
||||
|
||||
.form-group.gl-mb-3
|
||||
.gl-form-checkbox.custom-control.custom-checkbox
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
- if page_canonical_link
|
||||
%link{ rel: 'canonical', href: page_canonical_link }
|
||||
|
||||
= preload_link_tag(path_to_stylesheet('application_utilities'))
|
||||
= yield :prefetch_asset_tags
|
||||
|
||||
= 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) }
|
||||
- if user_application_theme == 'gl-dark'
|
||||
= stylesheet_link_tag_defer "application_dark"
|
||||
= yield :page_specific_styles
|
||||
= stylesheet_link_tag_defer "application_utilities_dark"
|
||||
- else
|
||||
= stylesheet_link_tag_defer "application"
|
||||
= yield :page_specific_styles
|
||||
= stylesheet_link_tag_defer "application_utilities"
|
||||
= yield :page_specific_styles
|
||||
= 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 "test_environment", media: "all" if Rails.env.test?
|
||||
|
||||
|
|
|
@ -54,6 +54,8 @@
|
|||
.settings-content
|
||||
= 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 'projects/service_desk_settings'
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
- add_page_specific_style 'page_bundles/members'
|
||||
- page_title _("Members")
|
||||
|
||||
.js-remove-member-modal
|
||||
.row.gl-mt-3
|
||||
.col-lg-12
|
||||
- if can_invite_members_for_project?(@project) || can_invite_group_for_project?(@project)
|
||||
|
|
|
@ -19,8 +19,6 @@
|
|||
= 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.')
|
||||
|
||||
= render_if_exists 'compliance_management/compliance_framework/project_settings', f: f
|
||||
|
||||
.row
|
||||
.form-group.col-md-9
|
||||
= f.label :description, _('Project description (optional)'), class: 'label-bold'
|
||||
|
|
|
@ -31,8 +31,6 @@
|
|||
|
||||
= 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/contribution', issuable: issuable, form: form
|
||||
|
|
|
@ -8,5 +8,4 @@
|
|||
= 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))
|
||||
- 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
|
||||
|
|
|
@ -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
|
|
@ -70,7 +70,7 @@ class MigrateK8sServiceIntegration < ActiveRecord::Migration[5.1]
|
|||
private
|
||||
|
||||
def parsed_properties
|
||||
@parsed_properties ||= JSON.parse(self.properties)
|
||||
@parsed_properties ||= JSON.parse(self.properties) # rubocop:disable Gitlab/Json
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -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
|
||||
```
|
||||
|
||||
### 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
|
||||
|
||||
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.
|
||||
|
||||
- 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
|
||||
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
|
||||
```
|
||||
|
||||
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
|
||||
gitlab-ctl tail sidekiq
|
||||
|
|
|
@ -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` (`{}`) |
|
|
@ -81,6 +81,10 @@ Vue specific [design patterns and practices](vue.md).
|
|||
|
||||
How to use [GraphQL](graphql.md).
|
||||
|
||||
## HAML
|
||||
|
||||
How to use [HAML](haml.md).
|
||||
|
||||
## Icons and Illustrations
|
||||
|
||||
How we use SVG for our [Icons and Illustrations](icons.md).
|
||||
|
|
|
@ -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
|
||||
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
|
||||
|
||||
In GitLab 13.11 and earlier, Sidekiq workers would always send database queries to the primary
|
||||
|
|
|
@ -1050,9 +1050,10 @@ is being discussed in [issue #17517](https://gitlab.com/gitlab-org/gitlab/-/issu
|
|||
|
||||
## Alternative backup strategies
|
||||
|
||||
If your GitLab server 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
|
||||
file system snapshots as part of your backup strategy.
|
||||
If your GitLab instance contains a lot of Git repository data, you may find the
|
||||
GitLab backup script to be too slow. If your GitLab instance has a lot of forked
|
||||
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
|
||||
|
||||
|
@ -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
|
||||
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
|
||||
|
||||
Do NOT backup or restore GitLab through a PgBouncer connection. These
|
||||
|
|
|
@ -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.
|
|
@ -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
|
||||
---
|
||||
|
||||
# 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)
|
||||
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.
|
||||
- [Infrastructure as Code and GitOps](iac/index.md)
|
||||
- [Kubernetes clusters](../project/clusters/index.md)
|
||||
- [Runbooks](../project/clusters/runbooks/index.md)
|
||||
|
|
|
@ -18,7 +18,7 @@ modifies, or destroys.
|
|||
## Setup
|
||||
|
||||
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:
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ module Gitlab
|
|||
|
||||
new(nodes).tsort
|
||||
rescue TSort::Cyclic
|
||||
raise ValidationError, 'The pipeline has circular dependencies.'
|
||||
raise ValidationError, 'The pipeline has circular dependencies'
|
||||
rescue MissingNodeError
|
||||
end
|
||||
|
||||
|
|
|
@ -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
|
|
@ -27,7 +27,7 @@ module Gitlab
|
|||
end
|
||||
|
||||
def read_hash
|
||||
ActiveSupport::JSON.decode(IO.read(@path))
|
||||
Gitlab::Json.parse(IO.read(@path))
|
||||
rescue StandardError => e
|
||||
Gitlab::ErrorTracking.log_exception(e)
|
||||
raise Gitlab::ImportExport::Error, 'Incorrect JSON format'
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue