Add latest changes from gitlab-org/gitlab@master

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

View File

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

View File

@ -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'

View File

@ -1 +1 @@
996a4adda765e8ced18c72eca0ebd27848afa3c9
818f3d85a2c8e6596376f1d2276aa22660203a6c

View File

@ -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>

View File

@ -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,
});
}
},

View File

@ -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) {

View File

@ -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>

View File

@ -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"

View File

@ -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>

View File

@ -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(

View File

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

View File

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

View File

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

View File

@ -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

View File

@ -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

View File

@ -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();

View File

@ -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();

View File

@ -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>

View File

@ -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,
},
];

View File

@ -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>

View File

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

View File

@ -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

View File

@ -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.'

View File

@ -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.'

View File

@ -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.'

View File

@ -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.'

View File

@ -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.'

View File

@ -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.'

View File

@ -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

View File

@ -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.'

View File

@ -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"

View File

@ -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],

View File

@ -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

View File

@ -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.'

View File

@ -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.'

View File

@ -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,

View File

@ -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.'

View File

@ -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.'

View File

@ -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,

View File

@ -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.'

View File

@ -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.'

View File

@ -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.'

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.'

View File

@ -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

View File

@ -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.'

View File

@ -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`.'

View File

@ -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

View File

@ -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"`.

View File

@ -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.'

View File

@ -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.'

View File

@ -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

View File

@ -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."

View File

@ -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".'

View File

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

View File

@ -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,

View File

@ -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.'

View File

@ -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.'

View File

@ -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.'

View File

@ -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.'

View File

@ -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.'

View File

@ -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

View File

@ -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.'

View File

@ -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.'

View File

@ -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)

View File

@ -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.

View File

@ -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)

View File

@ -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.'

View File

@ -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.'

View File

@ -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

View File

@ -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.'

View File

@ -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

View File

@ -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

View File

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

View File

@ -118,6 +118,7 @@
- if Gitlab::CurrentSettings.version_check_enabled
.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

View File

@ -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 }

View File

@ -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

View File

@ -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

View File

@ -1,4 +1,4 @@
= form_for @group, html: { multipart: true, class: 'gl-show-field-errors js-general-permissions-form' }, authenticity_token: true do |f|
= gitlab_ui_form_for @group, html: { multipart: true, class: 'gl-show-field-errors js-general-permissions-form' }, authenticity_token: true do |f|
%input{ type: 'hidden', name: 'update_section', value: 'js-permissions-settings' }
= 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

View File

@ -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?

View File

@ -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'

View File

@ -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)

View File

@ -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'

View File

@ -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

View File

@ -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

View File

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

View File

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

View File

@ -159,6 +159,27 @@ This machine's Geo node name matches a database record ... no
doc/administration/geo/replication/troubleshooting.md#can-geo-detect-the-current-node-correctly
```
### 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

View File

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

View File

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

View File

@ -466,6 +466,24 @@ If we expect an increase of **less than 5%**, then no further action is needed.
Otherwise, please ping `@gitlab-org/scalability` on the merge request and ask
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

View File

@ -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

View File

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

View File

@ -4,136 +4,11 @@ group: Configure
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#assignments
---
# 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)

View File

@ -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:

View File

@ -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

View File

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

View File

@ -27,7 +27,7 @@ module Gitlab
end
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