Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
1e9d859394
commit
e5731d5194
|
@ -375,7 +375,6 @@ RSpec/LeakyConstantDeclaration:
|
|||
- 'spec/lib/gitlab/quick_actions/dsl_spec.rb'
|
||||
- 'spec/lib/gitlab/sidekiq_middleware/client_metrics_spec.rb'
|
||||
- 'spec/lib/gitlab/sidekiq_middleware/server_metrics_spec.rb'
|
||||
- 'spec/lib/gitlab/sidekiq_middleware_spec.rb'
|
||||
- 'spec/lib/gitlab/view/presenter/factory_spec.rb'
|
||||
- 'spec/lib/marginalia_spec.rb'
|
||||
- 'spec/lib/omni_auth/strategies/jwt_spec.rb'
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
<script>
|
||||
import { GlDatepicker } from '@gitlab/ui';
|
||||
|
||||
export default {
|
||||
name: 'ExpiresAtField',
|
||||
components: { GlDatepicker },
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<gl-datepicker :target="null" :min-date="new Date()">
|
||||
<slot></slot>
|
||||
</gl-datepicker>
|
||||
</template>
|
|
@ -0,0 +1,12 @@
|
|||
import Vue from 'vue';
|
||||
import ExpiresAtField from './components/expires_at_field.vue';
|
||||
|
||||
const initExpiresAtField = () => {
|
||||
// eslint-disable-next-line no-new
|
||||
new Vue({
|
||||
el: document.querySelector('.js-access-tokens-expires-at'),
|
||||
components: { ExpiresAtField },
|
||||
});
|
||||
};
|
||||
|
||||
export default initExpiresAtField;
|
|
@ -1,12 +1,25 @@
|
|||
<script>
|
||||
import * as Sentry from '@sentry/browser';
|
||||
import { GlAlert, GlIcon, GlLoadingIcon, GlSprintf, GlTabs, GlTab, GlButton } from '@gitlab/ui';
|
||||
import {
|
||||
GlAlert,
|
||||
GlIcon,
|
||||
GlLoadingIcon,
|
||||
GlDropdown,
|
||||
GlDropdownItem,
|
||||
GlSprintf,
|
||||
GlTabs,
|
||||
GlTab,
|
||||
GlButton,
|
||||
} from '@gitlab/ui';
|
||||
import createFlash from '~/flash';
|
||||
import { capitalizeFirstCharacter } from '~/lib/utils/text_utility';
|
||||
import { s__ } from '~/locale';
|
||||
import query from '../graphql/queries/details.query.graphql';
|
||||
import { fetchPolicies } from '~/lib/graphql';
|
||||
import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
|
||||
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
|
||||
import { ALERTS_SEVERITY_LABELS } from '../constants';
|
||||
import updateAlertStatus from '../graphql/mutations/update_alert_status.graphql';
|
||||
|
||||
export default {
|
||||
statuses: {
|
||||
|
@ -29,6 +42,8 @@ export default {
|
|||
GlIcon,
|
||||
GlLoadingIcon,
|
||||
GlSprintf,
|
||||
GlDropdown,
|
||||
GlDropdownItem,
|
||||
GlTab,
|
||||
GlTabs,
|
||||
GlButton,
|
||||
|
@ -85,9 +100,28 @@ export default {
|
|||
},
|
||||
},
|
||||
methods: {
|
||||
capitalizeFirstCharacter,
|
||||
dismissError() {
|
||||
this.isErrorDismissed = true;
|
||||
},
|
||||
updateAlertStatus(status) {
|
||||
this.$apollo
|
||||
.mutate({
|
||||
mutation: updateAlertStatus,
|
||||
variables: {
|
||||
iid: this.alertId,
|
||||
status: status.toUpperCase(),
|
||||
projectPath: this.projectPath,
|
||||
},
|
||||
})
|
||||
.catch(() => {
|
||||
createFlash(
|
||||
s__(
|
||||
'AlertManagement|There was an error while updating the status of the alert. Please try again.',
|
||||
),
|
||||
);
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -97,7 +131,7 @@ export default {
|
|||
{{ $options.i18n.errorMsg }}
|
||||
</gl-alert>
|
||||
<div v-if="loading"><gl-loading-icon size="lg" class="gl-mt-5" /></div>
|
||||
<div v-if="alert" class="alert-management-details">
|
||||
<div v-if="alert" class="alert-management-details gl-relative">
|
||||
<div
|
||||
class="gl-display-flex gl-justify-content-space-between gl-align-items-center gl-px-1 gl-py-6 gl-border-b-1 gl-border-b-gray-200 gl-border-b-solid"
|
||||
>
|
||||
|
@ -137,6 +171,28 @@ export default {
|
|||
>
|
||||
<h2 data-testid="title">{{ alert.title }}</h2>
|
||||
</div>
|
||||
<gl-dropdown
|
||||
:text="capitalizeFirstCharacter(alert.status.toLowerCase())"
|
||||
class="gl-absolute gl-right-0"
|
||||
right
|
||||
>
|
||||
<gl-dropdown-item
|
||||
v-for="(label, field) in $options.statuses"
|
||||
:key="field"
|
||||
data-testid="statusDropdownItem"
|
||||
class="gl-vertical-align-middle"
|
||||
@click="updateAlertStatus(label)"
|
||||
>
|
||||
<span class="d-flex">
|
||||
<gl-icon
|
||||
class="flex-shrink-0 append-right-4"
|
||||
:class="{ invisible: label.toUpperCase() !== alert.status }"
|
||||
name="mobile-issue-close"
|
||||
/>
|
||||
{{ label }}
|
||||
</span>
|
||||
</gl-dropdown-item>
|
||||
</gl-dropdown>
|
||||
<gl-tabs v-if="alert" data-testid="alertDetailsTabs">
|
||||
<gl-tab data-testid="overviewTab" :title="$options.i18n.overviewTitle">
|
||||
<ul class="pl-4 mb-n1">
|
||||
|
|
|
@ -10,7 +10,6 @@ import {
|
|||
GlDropdownItem,
|
||||
GlTabs,
|
||||
GlTab,
|
||||
GlBadge,
|
||||
} from '@gitlab/ui';
|
||||
import createFlash from '~/flash';
|
||||
import { s__ } from '~/locale';
|
||||
|
@ -87,7 +86,6 @@ export default {
|
|||
GlIcon,
|
||||
GlTabs,
|
||||
GlTab,
|
||||
GlBadge,
|
||||
},
|
||||
mixins: [glFeatureFlagsMixin()],
|
||||
props: {
|
||||
|
@ -118,7 +116,7 @@ export default {
|
|||
variables() {
|
||||
return {
|
||||
projectPath: this.projectPath,
|
||||
status: this.statusFilter,
|
||||
statuses: this.statusFilter,
|
||||
};
|
||||
},
|
||||
update(data) {
|
||||
|
@ -135,7 +133,7 @@ export default {
|
|||
errored: false,
|
||||
isAlertDismissed: false,
|
||||
isErrorAlertDismissed: false,
|
||||
statusFilter: this.$options.statusTabs[0].status,
|
||||
statusFilter: this.$options.statusTabs[4].filters,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
@ -151,7 +149,7 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
filterALertsByStatus(tabIndex) {
|
||||
this.statusFilter = this.$options.statusTabs[tabIndex].status;
|
||||
this.statusFilter = this.$options.statusTabs[tabIndex].filters;
|
||||
},
|
||||
capitalizeFirstCharacter,
|
||||
updateAlertStatus(status, iid) {
|
||||
|
@ -190,9 +188,6 @@ export default {
|
|||
<gl-tab v-for="tab in $options.statusTabs" :key="tab.status">
|
||||
<template slot="title">
|
||||
<span>{{ tab.title }}</span>
|
||||
<gl-badge v-if="alerts" size="sm" class="gl-tab-counter-badge">
|
||||
{{ alerts.length }}
|
||||
</gl-badge>
|
||||
</template>
|
||||
</gl-tab>
|
||||
</gl-tabs>
|
||||
|
|
|
@ -10,32 +10,37 @@ export const ALERTS_SEVERITY_LABELS = {
|
|||
};
|
||||
|
||||
export const ALERTS_STATUS = {
|
||||
OPEN: 'open',
|
||||
TRIGGERED: 'triggered',
|
||||
ACKNOWLEDGED: 'acknowledged',
|
||||
RESOLVED: 'resolved',
|
||||
ALL: 'all',
|
||||
OPEN: 'OPEN',
|
||||
TRIGGERED: 'TRIGGERED',
|
||||
ACKNOWLEDGED: 'ACKNOWLEDGED',
|
||||
RESOLVED: 'RESOLVED',
|
||||
ALL: 'ALL',
|
||||
};
|
||||
|
||||
export const ALERTS_STATUS_TABS = [
|
||||
{
|
||||
title: s__('AlertManagement|Open'),
|
||||
status: ALERTS_STATUS.OPEN,
|
||||
filters: [ALERTS_STATUS.TRIGGERED, ALERTS_STATUS.ACKNOWLEDGED],
|
||||
},
|
||||
{
|
||||
title: s__('AlertManagement|Triggered'),
|
||||
status: ALERTS_STATUS.TRIGGERED,
|
||||
filters: [ALERTS_STATUS.TRIGGERED],
|
||||
},
|
||||
{
|
||||
title: s__('AlertManagement|Acknowledged'),
|
||||
status: ALERTS_STATUS.ACKNOWLEDGED,
|
||||
filters: [ALERTS_STATUS.ACKNOWLEDGED],
|
||||
},
|
||||
{
|
||||
title: s__('AlertManagement|Resolved'),
|
||||
status: ALERTS_STATUS.RESOLVED,
|
||||
filters: [ALERTS_STATUS.RESOLVED],
|
||||
},
|
||||
{
|
||||
title: s__('AlertManagement|All alerts'),
|
||||
status: ALERTS_STATUS.ALL,
|
||||
filters: [ALERTS_STATUS.TRIGGERED, ALERTS_STATUS.ACKNOWLEDGED, ALERTS_STATUS.RESOLVED],
|
||||
},
|
||||
];
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import Vue from 'vue';
|
||||
import VueApollo from 'vue-apollo';
|
||||
import createDefaultClient from '~/lib/graphql';
|
||||
import { defaultDataIdFromObject } from 'apollo-cache-inmemory';
|
||||
import AlertDetails from './components/alert_details.vue';
|
||||
|
||||
Vue.use(VueApollo);
|
||||
|
@ -10,7 +11,20 @@ export default selector => {
|
|||
const { alertId, projectPath, newIssuePath } = domEl.dataset;
|
||||
|
||||
const apolloProvider = new VueApollo({
|
||||
defaultClient: createDefaultClient(),
|
||||
defaultClient: createDefaultClient(
|
||||
{},
|
||||
{
|
||||
cacheConfig: {
|
||||
dataIdFromObject: object => {
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
if (object.__typename === 'AlertManagementAlert') {
|
||||
return object.iid;
|
||||
}
|
||||
return defaultDataIdFromObject(object);
|
||||
},
|
||||
},
|
||||
},
|
||||
),
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-new
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#import "../fragments/listItem.fragment.graphql"
|
||||
|
||||
query getAlerts($projectPath: ID!) {
|
||||
query getAlerts($projectPath: ID!, $statuses: [AlertManagementStatus!]) {
|
||||
project(fullPath: $projectPath) {
|
||||
alertManagementAlerts {
|
||||
alertManagementAlerts(statuses: $statuses) {
|
||||
nodes {
|
||||
...AlertListItem
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import flash from '~/flash';
|
|||
import { __, sprintf, s__ } from '~/locale';
|
||||
import { GlModal } from '@gitlab/ui';
|
||||
import { modalTypes } from '../../constants';
|
||||
import { trimPathComponents } from '../../utils';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -51,6 +52,8 @@ export default {
|
|||
methods: {
|
||||
...mapActions(['createTempEntry', 'renameEntry']),
|
||||
submitForm() {
|
||||
this.entryName = trimPathComponents(this.entryName);
|
||||
|
||||
if (this.modalType === modalTypes.rename) {
|
||||
if (this.entries[this.entryName] && !this.entries[this.entryName].deleted) {
|
||||
flash(
|
||||
|
|
|
@ -69,6 +69,12 @@ export const createPathWithExt = p => {
|
|||
return `${p.substring(1, p.lastIndexOf('.') + 1 || p.length)}${ext || '.js'}`;
|
||||
};
|
||||
|
||||
export const trimPathComponents = path =>
|
||||
path
|
||||
.split('/')
|
||||
.map(s => s.trim())
|
||||
.join('/');
|
||||
|
||||
export function registerLanguages(def, ...defs) {
|
||||
if (defs.length) defs.forEach(lang => registerLanguages(lang));
|
||||
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
import DueDateSelectors from '~/due_date_select';
|
||||
import initExpiresAtField from '~/access_tokens';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => new DueDateSelectors());
|
||||
document.addEventListener('DOMContentLoaded', initExpiresAtField);
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
import DueDateSelectors from '~/due_date_select';
|
||||
import initExpiresAtField from '~/access_tokens';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => new DueDateSelectors());
|
||||
document.addEventListener('DOMContentLoaded', initExpiresAtField);
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
import initExpiresAtField from '~/access_tokens';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', initExpiresAtField);
|
|
@ -17,7 +17,7 @@
|
|||
min-height: 68px;
|
||||
|
||||
&:last-child {
|
||||
background-color: $gray-normal;
|
||||
background-color: $gray-10;
|
||||
|
||||
&::before {
|
||||
content: none !important;
|
||||
|
|
|
@ -19,8 +19,6 @@ module Types
|
|||
markdown_field :description_html, null: true
|
||||
field :name, GraphQL::STRING_TYPE, null: true,
|
||||
description: 'Name of the release'
|
||||
field :evidence_sha, GraphQL::STRING_TYPE, null: true,
|
||||
description: "SHA of the release's evidence"
|
||||
field :created_at, Types::TimeType, null: true,
|
||||
description: 'Timestamp of when the release was created'
|
||||
field :released_at, Types::TimeType, null: true,
|
||||
|
|
|
@ -674,6 +674,7 @@ module ProjectsHelper
|
|||
services#edit
|
||||
hooks#index
|
||||
hooks#edit
|
||||
access_tokens#index
|
||||
hook_logs#show
|
||||
repository#show
|
||||
ci_cd#show
|
||||
|
|
|
@ -53,7 +53,6 @@ module Timebox
|
|||
|
||||
scope :of_projects, ->(ids) { where(project_id: ids) }
|
||||
scope :of_groups, ->(ids) { where(group_id: ids) }
|
||||
scope :active, -> { with_state(:active) }
|
||||
scope :closed, -> { with_state(:closed) }
|
||||
scope :for_projects, -> { where(group: nil).includes(:project) }
|
||||
scope :with_title, -> (title) { where(title: title) }
|
||||
|
|
|
@ -84,6 +84,7 @@ class Issue < ApplicationRecord
|
|||
|
||||
scope :preload_associated_models, -> { preload(:assignees, :labels, project: :namespace) }
|
||||
scope :with_api_entity_associations, -> { preload(:timelogs, :assignees, :author, :notes, :labels, project: [:route, { namespace: :route }] ) }
|
||||
scope :with_label_attributes, ->(label_attributes) { joins(:labels).where(labels: label_attributes) }
|
||||
|
||||
scope :public_only, -> { where(confidential: false) }
|
||||
scope :confidential_only, -> { where(confidential: true) }
|
||||
|
|
|
@ -5,9 +5,12 @@ class Iteration < ApplicationRecord
|
|||
|
||||
self.table_name = 'sprints'
|
||||
|
||||
STATE_ID_MAP = {
|
||||
active: 1,
|
||||
closed: 2
|
||||
attr_accessor :skip_future_date_validation
|
||||
|
||||
STATE_ENUM_MAP = {
|
||||
upcoming: 1,
|
||||
started: 2,
|
||||
closed: 3
|
||||
}.with_indifferent_access.freeze
|
||||
|
||||
include AtomicInternalId
|
||||
|
@ -21,16 +24,77 @@ class Iteration < ApplicationRecord
|
|||
has_internal_id :iid, scope: :project, init: ->(s) { s&.project&.iterations&.maximum(:iid) }
|
||||
has_internal_id :iid, scope: :group, init: ->(s) { s&.group&.iterations&.maximum(:iid) }
|
||||
|
||||
state_machine :state, initial: :active do
|
||||
validates :start_date, presence: true
|
||||
validates :due_date, presence: true
|
||||
|
||||
validate :dates_do_not_overlap, if: :start_or_due_dates_changed?
|
||||
validate :future_date, if: :start_or_due_dates_changed?, unless: :skip_future_date_validation
|
||||
|
||||
scope :upcoming, -> { with_state(:upcoming) }
|
||||
scope :started, -> { with_state(:started) }
|
||||
|
||||
state_machine :state_enum, initial: :upcoming do
|
||||
event :start do
|
||||
transition upcoming: :started
|
||||
end
|
||||
|
||||
event :close do
|
||||
transition active: :closed
|
||||
transition [:upcoming, :started] => :closed
|
||||
end
|
||||
|
||||
event :activate do
|
||||
transition closed: :active
|
||||
state :upcoming, value: Iteration::STATE_ENUM_MAP[:upcoming]
|
||||
state :started, value: Iteration::STATE_ENUM_MAP[:started]
|
||||
state :closed, value: Iteration::STATE_ENUM_MAP[:closed]
|
||||
end
|
||||
|
||||
# Alias to state machine .with_state_enum method
|
||||
# This needs to be defined after the state machine block to avoid errors
|
||||
class << self
|
||||
alias_method :with_state, :with_state_enum
|
||||
alias_method :with_states, :with_state_enums
|
||||
|
||||
def filter_by_state(iterations, state)
|
||||
case state
|
||||
when 'closed' then iterations.closed
|
||||
when 'started' then iterations.started
|
||||
when 'opened' then iterations.started.or(iterations.upcoming)
|
||||
when 'all' then iterations
|
||||
else iterations.upcoming
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def state
|
||||
STATE_ENUM_MAP.key(state_enum)
|
||||
end
|
||||
|
||||
def state=(value)
|
||||
self.state_enum = STATE_ENUM_MAP[value]
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def start_or_due_dates_changed?
|
||||
start_date_changed? || due_date_changed?
|
||||
end
|
||||
|
||||
# ensure dates do not overlap with other Iterations in the same group/project
|
||||
def dates_do_not_overlap
|
||||
return unless resource_parent.iterations.within_timeframe(start_date, due_date).exists?
|
||||
|
||||
errors.add(:base, s_("Iteration|Dates cannot overlap with other existing Iterations"))
|
||||
end
|
||||
|
||||
# ensure dates are in the future
|
||||
def future_date
|
||||
if start_date_changed?
|
||||
errors.add(:start_date, s_("Iteration|cannot be in the past")) if start_date < Date.today
|
||||
errors.add(:start_date, s_("Iteration|cannot be more than 500 years in the future")) if start_date > 500.years.from_now
|
||||
end
|
||||
|
||||
state :active, value: Iteration::STATE_ID_MAP[:active]
|
||||
state :closed, value: Iteration::STATE_ID_MAP[:closed]
|
||||
if due_date_changed?
|
||||
errors.add(:due_date, s_("Iteration|cannot be in the past")) if due_date < Date.today
|
||||
errors.add(:due_date, s_("Iteration|cannot be more than 500 years in the future")) if due_date > 500.years.from_now
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -18,6 +18,7 @@ class Milestone < ApplicationRecord
|
|||
|
||||
has_many :events, as: :target, dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
|
||||
|
||||
scope :active, -> { with_state(:active) }
|
||||
scope :started, -> { active.where('milestones.start_date <= CURRENT_DATE') }
|
||||
scope :not_started, -> { active.where('milestones.start_date > CURRENT_DATE') }
|
||||
scope :not_upcoming, -> do
|
||||
|
|
|
@ -52,4 +52,7 @@ class ProjectRepositoryStorageMove < ApplicationRecord
|
|||
state :finished, value: 4
|
||||
state :failed, value: 5
|
||||
end
|
||||
|
||||
scope :order_created_at_desc, -> { order(created_at: :desc) }
|
||||
scope :with_projects, -> { includes(project: :route) }
|
||||
end
|
||||
|
|
|
@ -81,14 +81,6 @@ class Release < ApplicationRecord
|
|||
self.milestones.map {|m| m.title }.sort.join(", ")
|
||||
end
|
||||
|
||||
def evidence_sha
|
||||
evidences.first&.summary_sha
|
||||
end
|
||||
|
||||
def evidence_summary
|
||||
evidences.first&.summary || {}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def actual_sha
|
||||
|
|
|
@ -43,13 +43,6 @@ class ReleasePresenter < Gitlab::View::Presenter::Delegated
|
|||
edit_project_release_url(project, release)
|
||||
end
|
||||
|
||||
def evidence_file_path
|
||||
evidence = release.evidences.first
|
||||
return unless evidence
|
||||
|
||||
project_evidence_url(project, release, evidence, format: :json)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def can_download_code?
|
||||
|
|
|
@ -15,7 +15,7 @@ class FileUploader < GitlabUploader
|
|||
prepend ObjectStorage::Extension::RecordsUploads
|
||||
|
||||
MARKDOWN_PATTERN = %r{\!?\[.*?\]\(/uploads/(?<secret>[0-9a-f]{32})/(?<file>.*?)\)}.freeze
|
||||
DYNAMIC_PATH_PATTERN = %r{.*/(?<secret>\h{10,32})/(?<identifier>.*)}.freeze
|
||||
DYNAMIC_PATH_PATTERN = %r{.*(?<secret>\b(\h{10}|\h{32}))\/(?<identifier>.*)}.freeze
|
||||
VALID_SECRET_PATTERN = %r{\A\h{10,32}\z}.freeze
|
||||
|
||||
InvalidSecret = Class.new(StandardError)
|
||||
|
|
|
@ -1,15 +1,29 @@
|
|||
- add_to_breadcrumbs "Users", admin_users_path
|
||||
- add_to_breadcrumbs 'Users', admin_users_path
|
||||
- breadcrumb_title @user.name
|
||||
- page_title "Impersonation Tokens", @user.name, "Users"
|
||||
- page_title _('Impersonation Tokens'), @user.name, _('Users')
|
||||
- type = _('impersonation token')
|
||||
- type_plural = _('impersonation tokens')
|
||||
|
||||
= render 'admin/users/head'
|
||||
|
||||
.row.prepend-top-default
|
||||
.col-lg-12
|
||||
- if @new_impersonation_token
|
||||
= render "shared/personal_access_tokens_created_container", new_token_value: @new_impersonation_token,
|
||||
container_title: 'Your New Impersonation Token',
|
||||
clipboard_button_title: _('Copy impersonation token')
|
||||
= render 'shared/access_tokens/created_container',
|
||||
type: type,
|
||||
new_token_value: @new_impersonation_token
|
||||
|
||||
= render "shared/personal_access_tokens_form", path: admin_user_impersonation_tokens_path, impersonation: true, token: @impersonation_token, scopes: @scopes
|
||||
= render 'shared/access_tokens/form',
|
||||
type: type,
|
||||
title: _('Add an impersonation token'),
|
||||
path: admin_user_impersonation_tokens_path,
|
||||
impersonation: true,
|
||||
token: @impersonation_token,
|
||||
scopes: @scopes
|
||||
|
||||
= render "shared/personal_access_tokens_table", impersonation: true, active_tokens: @active_impersonation_tokens, inactive_tokens: @inactive_impersonation_tokens
|
||||
= render 'shared/access_tokens/table',
|
||||
type: type,
|
||||
type_plural: type_plural,
|
||||
impersonation: true,
|
||||
active_tokens: @active_impersonation_tokens,
|
||||
revoke_route_helper: ->(token) { revoke_admin_user_impersonation_token_path(token.user, token) }
|
||||
|
|
|
@ -363,6 +363,11 @@
|
|||
= link_to project_hooks_path(@project), title: _('Webhooks'), data: { qa_selector: 'webhooks_settings_link' } do
|
||||
%span
|
||||
= _('Webhooks')
|
||||
- if project_access_token_available?(@project)
|
||||
= nav_link(controller: [:access_tokens]) do
|
||||
= link_to project_settings_access_tokens_path(@project), title: _('Access Tokens'), data: { qa_selector: 'access_tokens_settings_link' } do
|
||||
%span
|
||||
= _('Access Tokens')
|
||||
= nav_link(controller: :repository) do
|
||||
= link_to project_settings_repository_path(@project), title: _('Repository') do
|
||||
%span
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
- breadcrumb_title s_('AccessTokens|Access Tokens')
|
||||
- page_title s_('AccessTokens|Personal Access Tokens')
|
||||
- @content_class = "limit-container-width" unless fluid_layout
|
||||
- type = _('personal access token')
|
||||
- type_plural = _('personal access tokens')
|
||||
- @content_class = 'limit-container-width' unless fluid_layout
|
||||
|
||||
.row.prepend-top-default
|
||||
.col-lg-4.profile-settings-sidebar
|
||||
|
@ -14,11 +16,21 @@
|
|||
|
||||
.col-lg-8
|
||||
- if @new_personal_access_token
|
||||
= render "shared/personal_access_tokens_created_container", new_token_value: @new_personal_access_token
|
||||
= render 'shared/access_tokens/created_container',
|
||||
type: type,
|
||||
new_token_value: @new_personal_access_token
|
||||
|
||||
= render "shared/personal_access_tokens_form", path: profile_personal_access_tokens_path, impersonation: false, token: @personal_access_token, scopes: @scopes
|
||||
= render 'shared/access_tokens/form',
|
||||
type: type,
|
||||
path: profile_personal_access_tokens_path,
|
||||
token: @personal_access_token,
|
||||
scopes: @scopes
|
||||
|
||||
= render "shared/personal_access_tokens_table", impersonation: false, active_tokens: @active_personal_access_tokens, inactive_tokens: @inactive_personal_access_tokens
|
||||
= render 'shared/access_tokens/table',
|
||||
type: type,
|
||||
type_plural: type_plural,
|
||||
active_tokens: @active_personal_access_tokens,
|
||||
revoke_route_helper: ->(token) { revoke_profile_personal_access_token_path(token) }
|
||||
|
||||
%hr
|
||||
.row.prepend-top-default
|
||||
|
@ -30,7 +42,7 @@
|
|||
%p
|
||||
= s_('AccessTokens|It cannot be used to access any other data.')
|
||||
.col-lg-8.feed-token-reset
|
||||
= label_tag :feed_token, s_('AccessTokens|Feed token'), class: "label-bold"
|
||||
= label_tag :feed_token, s_('AccessTokens|Feed token'), class: 'label-bold'
|
||||
= text_field_tag :feed_token, current_user.feed_token, class: 'form-control js-select-on-focus', readonly: true
|
||||
%p.form-text.text-muted
|
||||
- reset_link = link_to s_('AccessTokens|reset it'), [:reset, :feed_token, :profile], method: :put, data: { confirm: s_('AccessTokens|Are you sure? Any RSS or calendar URLs currently in use will stop working.') }
|
||||
|
@ -48,7 +60,7 @@
|
|||
%p
|
||||
= s_('AccessTokens|It cannot be used to access any other data.')
|
||||
.col-lg-8.incoming-email-token-reset
|
||||
= label_tag :incoming_email_token, s_('AccessTokens|Incoming email token'), class: "label-bold"
|
||||
= label_tag :incoming_email_token, s_('AccessTokens|Incoming email token'), class: 'label-bold'
|
||||
= text_field_tag :incoming_email_token, current_user.incoming_email_token, class: 'form-control js-select-on-focus', readonly: true
|
||||
%p.form-text.text-muted
|
||||
- reset_link = link_to s_('AccessTokens|reset it'), [:reset, :incoming_email_token, :profile], method: :put, data: { confirm: s_('AccessTokens|Are you sure? Any issue email addresses currently in use will stop working.') }
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
- breadcrumb_title s_('AccessTokens|Access Tokens')
|
||||
- page_title _('Project Access Tokens')
|
||||
- type = _('project access token')
|
||||
- type_plural = _('project access tokens')
|
||||
- @content_class = 'limit-container-width' unless fluid_layout
|
||||
|
||||
.row.prepend-top-default
|
||||
.col-lg-4.profile-settings-sidebar
|
||||
%h4.prepend-top-0
|
||||
= page_title
|
||||
%p
|
||||
= _('You can generate an access token scoped to this project for each application to use the GitLab API.')
|
||||
%p
|
||||
= _('You can also use project access tokens to authenticate against Git over HTTP.')
|
||||
|
||||
.col-lg-8
|
||||
- if @new_project_access_token
|
||||
= render 'shared/access_tokens/created_container',
|
||||
type: type,
|
||||
new_token_value: @new_project_access_token
|
||||
|
||||
= render 'shared/access_tokens/form',
|
||||
type: type,
|
||||
path: project_settings_access_tokens_path(@project),
|
||||
token: @project_access_token,
|
||||
scopes: @scopes,
|
||||
prefix: :project_access_token
|
||||
|
||||
= render 'shared/access_tokens/table',
|
||||
active_tokens: @active_project_access_tokens,
|
||||
type: type,
|
||||
type_plural: type_plural,
|
||||
revoke_route_helper: ->(token) { revoke_namespace_project_settings_access_token_path(id: token) },
|
||||
no_active_tokens_message: _('This project has no active access tokens.')
|
|
@ -13,7 +13,7 @@
|
|||
%ul.label-links
|
||||
- if show_label_issues_link
|
||||
%li.label-link-item.inline
|
||||
= link_to_label(label) { 'Issues' }
|
||||
= link_to_label(label) { _('Issues') }
|
||||
- if show_label_merge_requests_link
|
||||
·
|
||||
%li.label-link-item.inline
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
%ul.nav-links.mobile-separator.nav.nav-tabs
|
||||
%li{ class: milestone_class_for_state(params[:state], 'opened', true) }>
|
||||
= link_to milestones_filter_path(state: 'opened') do
|
||||
Open
|
||||
= _('Open')
|
||||
%span.badge.badge-pill= counts[:opened]
|
||||
%li{ class: milestone_class_for_state(params[:state], 'closed') }>
|
||||
= link_to milestones_filter_path(state: 'closed', sort: 'due_date_desc') do
|
||||
Closed
|
||||
= _('Closed')
|
||||
%span.badge.badge-pill= counts[:closed]
|
||||
%li{ class: milestone_class_for_state(params[:state], 'all') }>
|
||||
= link_to milestones_filter_path(state: 'all', sort: 'due_date_desc') do
|
||||
All
|
||||
= _('All')
|
||||
%span.badge.badge-pill= counts[:all]
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
- container_title = local_assigns.fetch(:container_title, _('Your New Personal Access Token'))
|
||||
- clipboard_button_title = local_assigns.fetch(:clipboard_button_title, _('Copy personal access token'))
|
||||
|
||||
.created-personal-access-token-container
|
||||
%h5.prepend-top-0
|
||||
= container_title
|
||||
.form-group
|
||||
.input-group
|
||||
= text_field_tag 'created-personal-access-token', new_token_value, readonly: true, class: "qa-created-personal-access-token form-control js-select-on-focus", 'aria-describedby' => "created-token-help-block"
|
||||
%span.input-group-append
|
||||
= clipboard_button(text: new_token_value, title: clipboard_button_title, placement: "left", class: "input-group-text btn-default btn-clipboard")
|
||||
%span#created-token-help-block.form-text.text-muted.text-danger
|
||||
= _("Make sure you save it - you won't be able to access it again.")
|
||||
|
||||
%hr
|
|
@ -1,31 +0,0 @@
|
|||
- type = impersonation ? s_('Profiles|impersonation') : s_('Profiles|personal access')
|
||||
|
||||
%h5.prepend-top-0
|
||||
= _('Add a %{type} token') % { type: type }
|
||||
%p.profile-settings-content
|
||||
= _("Pick a name for the application, and we'll give you a unique %{type} token.") % { type: type }
|
||||
|
||||
= form_for token, url: path, method: :post, html: { class: 'js-requires-input' } do |f|
|
||||
|
||||
= form_errors(token)
|
||||
|
||||
.row
|
||||
.form-group.col-md-6
|
||||
= f.label :name, _('Name'), class: 'label-bold'
|
||||
= f.text_field :name, class: "form-control", required: true, data: { qa_selector: 'personal_access_token_name_field' }
|
||||
|
||||
.row
|
||||
.form-group.col-md-6
|
||||
= f.label :expires_at, _('Expires at'), class: 'label-bold'
|
||||
.input-icon-wrapper
|
||||
|
||||
= render_if_exists 'personal_access_tokens/callout_max_personal_access_token_lifetime'
|
||||
|
||||
= f.text_field :expires_at, class: "datepicker form-control", placeholder: 'YYYY-MM-DD', data: { qa_selector: 'expiry_date_field' }
|
||||
|
||||
.form-group
|
||||
= f.label :scopes, _('Scopes'), class: 'label-bold'
|
||||
= render 'shared/tokens/scopes_form', prefix: 'personal_access_token', token: token, scopes: scopes
|
||||
|
||||
.prepend-top-default
|
||||
= f.submit _('Create %{type} token') % { type: type }, class: "btn btn-success", data: { qa_selector: 'create_token_button' }
|
|
@ -1,8 +1,8 @@
|
|||
- if cookies[:hide_project_limit_message].blank? && !current_user.hide_project_limit && !current_user.can_create_project? && current_user.projects_limit > 0
|
||||
.project-limit-message.alert.alert-warning.d-none.d-sm-block
|
||||
You won't be able to create new projects because you have reached your project limit.
|
||||
= _("You won't be able to create new projects because you have reached your project limit.")
|
||||
|
||||
.float-right
|
||||
= link_to "Don't show again", profile_path(user: {hide_project_limit: true}), method: :put, class: 'alert-link'
|
||||
= link_to _("Don't show again"), profile_path(user: {hide_project_limit: true}), method: :put, class: 'alert-link'
|
||||
|
|
||||
= link_to 'Remind later', '#', class: 'hide-project-limit-message alert-link'
|
||||
= link_to _('Remind later'), '#', class: 'hide-project-limit-message alert-link'
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
.created-personal-access-token-container
|
||||
%h5.prepend-top-0
|
||||
= _('Your new %{type}') % { type: type }
|
||||
.form-group
|
||||
.input-group
|
||||
= text_field_tag 'created-personal-access-token', new_token_value, readonly: true, class: 'qa-created-access-token form-control js-select-on-focus', 'aria-describedby' => 'created-token-help-block'
|
||||
%span.input-group-append
|
||||
= clipboard_button(text: new_token_value, title: _('Copy %{type}') % { type: type }, placement: 'left', class: 'input-group-text btn-default btn-clipboard')
|
||||
%span#created-token-help-block.form-text.text-muted.text-danger
|
||||
= _("Make sure you save it - you won't be able to access it again.")
|
||||
|
||||
%hr
|
|
@ -0,0 +1,34 @@
|
|||
- title = local_assigns.fetch(:title, _('Add a %{type}') % { type: type })
|
||||
- prefix = local_assigns.fetch(:prefix, :personal_access_token)
|
||||
|
||||
%h5.prepend-top-0
|
||||
= title
|
||||
%p.profile-settings-content
|
||||
= _("Enter the name of your application, and we'll return a unique %{type}.") % { type: type }
|
||||
|
||||
= form_for token, as: prefix, url: path, method: :post, html: { class: 'js-requires-input' } do |f|
|
||||
|
||||
= form_errors(token)
|
||||
|
||||
.row
|
||||
.form-group.col-md-6
|
||||
= f.label :name, _('Name'), class: 'label-bold'
|
||||
= f.text_field :name, class: 'form-control', required: true, data: { qa_selector: 'access_token_name_field' }
|
||||
|
||||
.row
|
||||
.form-group.col-md-6
|
||||
= f.label :expires_at, _('Expires at'), class: 'label-bold'
|
||||
.input-icon-wrapper
|
||||
|
||||
= render_if_exists 'personal_access_tokens/callout_max_personal_access_token_lifetime'
|
||||
|
||||
.js-access-tokens-expires-at
|
||||
%expires-at-field
|
||||
= f.text_field :expires_at, class: 'datepicker form-control gl-datepicker-input', placeholder: 'YYYY-MM-DD', autocomplete: 'off', inputmode: 'none', data: { qa_selector: 'expiry_date_field' }
|
||||
|
||||
.form-group
|
||||
= f.label :scopes, _('Scopes'), class: 'label-bold'
|
||||
= render 'shared/tokens/scopes_form', prefix: prefix, token: token, scopes: scopes
|
||||
|
||||
.prepend-top-default
|
||||
= f.submit _('Create %{type}') % { type: type }, class: 'btn btn-success', data: { qa_selector: 'create_token_button' }
|
|
@ -1,8 +1,10 @@
|
|||
- type = impersonation ? s_('Profiles|Impersonation') : s_('Profiles|Personal Access')
|
||||
- no_active_tokens_message = local_assigns.fetch(:no_active_tokens_message, _('This user has no active %{type}.') % { type: type_plural })
|
||||
- impersonation = local_assigns.fetch(:impersonation, false)
|
||||
|
||||
%hr
|
||||
|
||||
%h5
|
||||
= _('Active %{type} Tokens (%{token_length})') % { type: type, token_length: active_tokens.length }
|
||||
= _('Active %{type} (%{token_length})') % { type: type_plural, token_length: active_tokens.length }
|
||||
- if impersonation
|
||||
%p.profile-settings-content
|
||||
= _("To see all the user's personal access tokens you must impersonate them first.")
|
||||
|
@ -28,9 +30,8 @@
|
|||
In #{distance_of_time_in_words_to_now(token.expires_at)}
|
||||
- else
|
||||
%span.token-never-expires-label= _('Never')
|
||||
%td= token.scopes.present? ? token.scopes.join(", ") : _('<no scopes selected>')
|
||||
- path = impersonation ? revoke_admin_user_impersonation_token_path(token.user, token) : revoke_profile_personal_access_token_path(token)
|
||||
%td= link_to _('Revoke'), path, method: :put, class: "btn btn-danger float-right qa-revoke-button", data: { confirm: _('Are you sure you want to revoke this %{type} Token? This action cannot be undone.') % { type: type } }
|
||||
%td= token.scopes.present? ? token.scopes.join(', ') : _('<no scopes selected>')
|
||||
%td= link_to _('Revoke'), revoke_route_helper.call(token), method: :put, class: 'btn btn-danger float-right qa-revoke-button', data: { confirm: _('Are you sure you want to revoke this %{type}? This action cannot be undone.') % { type: type } }
|
||||
- else
|
||||
.settings-message.text-center
|
||||
= _('This user has no active %{type} Tokens.') % { type: type }
|
||||
= no_active_tokens_message
|
|
@ -8,14 +8,14 @@
|
|||
|
||||
- if is_current_user
|
||||
- if can_update
|
||||
= link_to "Close #{display_issuable_type}", close_issuable_path(issuable), method: button_method,
|
||||
class: "d-none d-sm-none d-md-block btn btn-grouped btn-close js-btn-issue-action #{issuable_button_visibility(issuable, true)} #{(add_blocked_class ? 'btn-issue-blocked' : '')}", title: "Close #{display_issuable_type}", data: { qa_selector: 'close_issue_button' }
|
||||
= link_to _("Close %{display_issuable_type}") % { display_issuable_type: display_issuable_type }, close_issuable_path(issuable), method: button_method,
|
||||
class: "d-none d-sm-none d-md-block btn btn-grouped btn-close js-btn-issue-action #{issuable_button_visibility(issuable, true)} #{(add_blocked_class ? 'btn-issue-blocked' : '')}", title: _("Close %{display_issuable_type}") % { display_issuable_type: display_issuable_type }, data: { qa_selector: 'close_issue_button' }
|
||||
- if can_reopen
|
||||
= link_to "Reopen #{display_issuable_type}", reopen_issuable_path(issuable), method: button_method,
|
||||
class: "d-none d-sm-none d-md-block btn btn-grouped btn-reopen js-btn-issue-action #{issuable_button_visibility(issuable, false)}", title: "Reopen #{display_issuable_type}", data: { qa_selector: 'reopen_issue_button' }
|
||||
= link_to _("Reopen %{display_issuable_type}") % { display_issuable_type: display_issuable_type }, reopen_issuable_path(issuable), method: button_method,
|
||||
class: "d-none d-sm-none d-md-block btn btn-grouped btn-reopen js-btn-issue-action #{issuable_button_visibility(issuable, false)}", title: _("Reopen %{display_issuable_type}") % { display_issuable_type: display_issuable_type }, data: { qa_selector: 'reopen_issue_button' }
|
||||
- else
|
||||
- if can_update && !are_close_and_open_buttons_hidden
|
||||
= render 'shared/issuable/close_reopen_report_toggle', issuable: issuable, warn_before_close: add_blocked_class
|
||||
- else
|
||||
= link_to 'Report abuse', new_abuse_report_path(user_id: issuable.author.id, ref_url: issuable_url(issuable)),
|
||||
class: 'd-none d-sm-none d-md-block btn btn-grouped btn-close-color', title: 'Report abuse'
|
||||
= link_to _('Report abuse'), new_abuse_report_path(user_id: issuable.author.id, ref_url: issuable_url(issuable)),
|
||||
class: 'd-none d-sm-none d-md-block btn btn-grouped btn-close-color', title: _('Report abuse')
|
||||
|
|
|
@ -14,39 +14,37 @@
|
|||
method: button_method, class: "#{button_class} btn-#{button_action} #{(add_blocked_class ? 'btn-issue-blocked' : '')}", title: "#{display_button_action} #{display_issuable_type}", data: { qa_selector: 'close_issue_button' }
|
||||
|
||||
= button_tag type: 'button', class: "#{toggle_class} btn-#{button_action}-color",
|
||||
data: { 'dropdown-trigger' => '#issuable-close-menu' }, 'aria-label' => 'Toggle dropdown' do
|
||||
data: { 'dropdown-trigger' => '#issuable-close-menu' }, 'aria-label' => _('Toggle dropdown') do
|
||||
= icon('caret-down', class: 'toggle-icon icon')
|
||||
|
||||
%ul#issuable-close-menu.js-issuable-close-menu.dropdown-menu{ data: { dropdown: true } }
|
||||
%li.close-item{ class: "#{issuable_button_visibility(issuable, true) || 'droplab-item-selected'}",
|
||||
data: { text: "Close #{display_issuable_type}", url: close_issuable_path(issuable),
|
||||
data: { text: _("Close %{display_issuable_type}") % { display_issuable_type: display_issuable_type }, url: close_issuable_path(issuable),
|
||||
button_class: "#{button_class} btn-close", toggle_class: "#{toggle_class} btn-close-color", method: button_method } }
|
||||
%button.btn.btn-transparent
|
||||
= icon('check', class: 'icon')
|
||||
.description
|
||||
%strong.title
|
||||
Close
|
||||
= _('Close')
|
||||
= display_issuable_type
|
||||
|
||||
%li.reopen-item{ class: "#{issuable_button_visibility(issuable, false) || 'droplab-item-selected'}",
|
||||
data: { text: "Reopen #{display_issuable_type}", url: reopen_issuable_path(issuable),
|
||||
data: { text: _("Reopen %{display_issuable_type}") % { display_issuable_type: display_issuable_type }, url: reopen_issuable_path(issuable),
|
||||
button_class: "#{button_class} btn-reopen", toggle_class: "#{toggle_class} btn-reopen-color", method: button_method } }
|
||||
%button.btn.btn-transparent
|
||||
= icon('check', class: 'icon')
|
||||
.description
|
||||
%strong.title
|
||||
Reopen
|
||||
= _('Reopen')
|
||||
= display_issuable_type
|
||||
|
||||
%li.divider.droplab-item-ignore
|
||||
|
||||
%li.report-item{ data: { text: 'Report abuse', url: new_abuse_report_path(user_id: issuable.author.id, ref_url: issuable_url(issuable)),
|
||||
%li.report-item{ data: { text: _('Report abuse'), url: new_abuse_report_path(user_id: issuable.author.id, ref_url: issuable_url(issuable)),
|
||||
button_class: "#{button_class} btn-close-color", toggle_class: "#{toggle_class} btn-close-color", method: '' } }
|
||||
%button.btn.btn-transparent
|
||||
= icon('check', class: 'icon')
|
||||
.description
|
||||
%strong.title Report abuse
|
||||
%strong.title = _('Report abuse')
|
||||
%p.text
|
||||
Report
|
||||
= display_issuable_type.pluralize
|
||||
that are abusive, inappropriate or spam.
|
||||
= _('Report %{display_issuable_type} that are abusive, inappropriate or spam.') % { display_issuable_type: display_issuable_type.pluralize }
|
||||
|
|
|
@ -67,7 +67,7 @@
|
|||
class: 'btn btn-default align-self-center mr-sm-2',
|
||||
title: _('Resend invite')
|
||||
|
||||
- if user != current_user && member.can_update?
|
||||
- if user != current_user && member.can_update? && !user&.project_bot?
|
||||
= form_for member, remote: true, html: { class: "js-edit-member-form form-group #{'d-sm-flex' unless force_mobile_view}" } do |f|
|
||||
= f.hidden_field :access_level
|
||||
.member-form-control.dropdown{ class: [("mr-sm-2 d-sm-inline-block" unless force_mobile_view)] }
|
||||
|
@ -117,7 +117,7 @@
|
|||
method: :delete,
|
||||
data: { confirm: leave_confirmation_message(member.source) },
|
||||
class: "btn btn-remove align-self-center m-0 #{'ml-sm-2' unless force_mobile_view}"
|
||||
- else
|
||||
- elsif !user&.project_bot?
|
||||
= link_to member,
|
||||
method: :delete,
|
||||
data: { confirm: remove_member_message(member), qa_selector: 'delete_member_button' },
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Remove JenkinsDeprecatedService
|
||||
merge_request: 31607
|
||||
author: tnwx
|
||||
type: removed
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
title: Deploy tokens can be used in the API with Basic Auth Headers enabling NuGet
|
||||
and PyPI to be used with deploy tokens
|
||||
merge_request: 31035
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Remove deprecated Release Evidence endpoints
|
||||
merge_request: 30975
|
||||
author:
|
||||
type: removed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add incident_labeled_issues to usage ping
|
||||
merge_request: 31406
|
||||
author:
|
||||
type: added
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Trim whitespace in directory names in the Web IDE
|
||||
merge_request: 31305
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix incorrect regex used in FileUploader#extract_dynamic_path
|
||||
merge_request: 32271
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Externalize i18n strings from ./app/views/shared/issuable/_close_reopen_report_toggle.html.haml
|
||||
merge_request: 32168
|
||||
author: Gilang Gumilar
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Externalize i18n strings from ./app/views/shared/issuable/_close_reopen_button.html.haml
|
||||
merge_request: 32172
|
||||
author: Gilang Gumilar
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Externalize i18n strings from ./app/views/shared/_label_row.html.haml
|
||||
merge_request: 32124
|
||||
author: Gilang Gumilar
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Externalize i18n strings from ./app/views/shared/_milestones_filter.html.haml
|
||||
merge_request: 32120
|
||||
author: Gilang Gumilar
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Externalize i18n strings from ./app/views/shared/_project_limit.html.haml
|
||||
merge_request: 32110
|
||||
author: Gilang Gumilar
|
||||
type: changed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Update android template
|
||||
merge_request: 32096
|
||||
author:
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Update error tracking table background colour to correct gray
|
||||
merge_request: 32133
|
||||
author:
|
||||
type: other
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Fix leaky constant issue in sidekiq middleware spec
|
||||
merge_request: 32101
|
||||
author: Rajendra Kadam
|
||||
type: fixed
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Read only storage move API
|
||||
merge_request: 31285
|
||||
author:
|
||||
type: added
|
|
@ -10,7 +10,7 @@ development:
|
|||
# host: localhost
|
||||
# port: 26380 # point to sentinel, not to redis port
|
||||
# -
|
||||
# host: slave2
|
||||
# host: replica2
|
||||
# port: 26380 # point to sentinel, not to redis port
|
||||
test:
|
||||
url: redis://localhost:6379/10
|
||||
|
@ -31,8 +31,8 @@ production:
|
|||
# url: redis://master:6380
|
||||
# sentinels:
|
||||
# -
|
||||
# host: slave1
|
||||
# host: replica1
|
||||
# port: 26380 # point to sentinel, not to redis port
|
||||
# -
|
||||
# host: slave2
|
||||
# host: replica2
|
||||
# port: 26380 # point to sentinel, not to redis port
|
||||
|
|
|
@ -10,7 +10,7 @@ development:
|
|||
# host: localhost
|
||||
# port: 26381 # point to sentinel, not to redis port
|
||||
# -
|
||||
# host: slave2
|
||||
# host: replica2
|
||||
# port: 26381 # point to sentinel, not to redis port
|
||||
test:
|
||||
url: redis://localhost:6379/11
|
||||
|
@ -31,8 +31,8 @@ production:
|
|||
# url: redis://master:6381
|
||||
# sentinels:
|
||||
# -
|
||||
# host: slave1
|
||||
# host: replica1
|
||||
# port: 26381 # point to sentinel, not to redis port
|
||||
# -
|
||||
# host: slave2
|
||||
# host: replica2
|
||||
# port: 26381 # point to sentinel, not to redis port
|
||||
|
|
|
@ -10,7 +10,7 @@ development:
|
|||
# host: localhost
|
||||
# port: 26382 # point to sentinel, not to redis port
|
||||
# -
|
||||
# host: slave2
|
||||
# host: replica2
|
||||
# port: 26382 # point to sentinel, not to redis port
|
||||
test:
|
||||
url: redis://localhost:6379/12
|
||||
|
@ -31,8 +31,8 @@ production:
|
|||
# url: redis://master:6382
|
||||
# sentinels:
|
||||
# -
|
||||
# host: slave1
|
||||
# host: replica1
|
||||
# port: 26382 # point to sentinel, not to redis port
|
||||
# -
|
||||
# host: slave2
|
||||
# host: replica2
|
||||
# port: 26382 # point to sentinel, not to redis port
|
||||
|
|
|
@ -8,7 +8,7 @@ development:
|
|||
# host: localhost
|
||||
# port: 26380 # point to sentinel, not to redis port
|
||||
# -
|
||||
# host: slave2
|
||||
# host: replica2
|
||||
# port: 26381 # point to sentinel, not to redis port
|
||||
test:
|
||||
url: redis://localhost:6379
|
||||
|
@ -27,8 +27,8 @@ production:
|
|||
# url: redis://master:6379
|
||||
# sentinels:
|
||||
# -
|
||||
# host: slave1
|
||||
# host: replica1
|
||||
# port: 26379 # point to sentinel, not to redis port
|
||||
# -
|
||||
# host: slave2
|
||||
# host: replica2
|
||||
# port: 26379 # point to sentinel, not to redis port
|
||||
|
|
|
@ -484,7 +484,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
|
|||
# Legacy routes.
|
||||
# Introduced in 12.0.
|
||||
# Should be removed with https://gitlab.com/gitlab-org/gitlab/issues/28848.
|
||||
Gitlab::Routing.redirect_legacy_paths(self, :mirror,
|
||||
Gitlab::Routing.redirect_legacy_paths(self, :mirror, :tags,
|
||||
:cycle_analytics, :mattermost, :variables, :triggers,
|
||||
:environments, :protected_environments, :error_tracking, :alert_management,
|
||||
:serverless, :clusters, :audit_events, :wikis, :merge_requests,
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class SprintRenameStateToStateEnum < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
rename_column_concurrently :sprints, :state, :state_enum
|
||||
end
|
||||
|
||||
def down
|
||||
undo_rename_column_concurrently :sprints, :state, :state_enum
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class RemoveDeprecatedJenkinsServiceRecords < ActiveRecord::Migration[6.0]
|
||||
DOWNTIME = false
|
||||
|
||||
def up
|
||||
execute <<~SQL.strip
|
||||
DELETE FROM services WHERE type = 'JenkinsDeprecatedService';
|
||||
SQL
|
||||
end
|
||||
|
||||
def down
|
||||
# no-op
|
||||
|
||||
# The records were removed by `up`
|
||||
end
|
||||
end
|
|
@ -0,0 +1,15 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class SprintMakeStateEnumNotNullAndDefault < ActiveRecord::Migration[6.0]
|
||||
DOWNTIME = false
|
||||
|
||||
def up
|
||||
change_column_default :sprints, :state_enum, from: 0, to: 1
|
||||
change_column_null :sprints, :state_enum, false, 1
|
||||
end
|
||||
|
||||
def down
|
||||
change_column_null :sprints, :state_enum, true
|
||||
change_column_default :sprints, :state_enum, from: 1, to: nil
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class CleanupSprintsStateRename < ActiveRecord::Migration[6.0]
|
||||
include Gitlab::Database::MigrationHelpers
|
||||
|
||||
DOWNTIME = false
|
||||
|
||||
disable_ddl_transaction!
|
||||
|
||||
def up
|
||||
cleanup_concurrent_column_rename :sprints, :state, :state_enum
|
||||
end
|
||||
|
||||
def down
|
||||
undo_cleanup_concurrent_column_rename :sprints, :state, :state_enum
|
||||
end
|
||||
end
|
|
@ -0,0 +1,17 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class EnsureDeprecatedJenkinsServiceRecordsRemoval < ActiveRecord::Migration[6.0]
|
||||
DOWNTIME = false
|
||||
|
||||
def up
|
||||
execute <<~SQL.strip
|
||||
DELETE FROM services WHERE type = 'JenkinsDeprecatedService';
|
||||
SQL
|
||||
end
|
||||
|
||||
def down
|
||||
# no-op
|
||||
|
||||
# The records were removed by `up`
|
||||
end
|
||||
end
|
|
@ -6242,11 +6242,11 @@ CREATE TABLE public.sprints (
|
|||
group_id bigint,
|
||||
iid integer NOT NULL,
|
||||
cached_markdown_version integer,
|
||||
state smallint,
|
||||
title text NOT NULL,
|
||||
title_html text,
|
||||
description text,
|
||||
description_html text,
|
||||
state_enum smallint DEFAULT 1 NOT NULL,
|
||||
CONSTRAINT sprints_must_belong_to_project_or_group CHECK ((((project_id <> NULL::bigint) AND (group_id IS NULL)) OR ((group_id <> NULL::bigint) AND (project_id IS NULL)))),
|
||||
CONSTRAINT sprints_title CHECK ((char_length(title) <= 255))
|
||||
);
|
||||
|
@ -13789,6 +13789,8 @@ COPY "schema_migrations" (version) FROM STDIN;
|
|||
20200424101920
|
||||
20200424135319
|
||||
20200427064130
|
||||
20200429001827
|
||||
20200429002150
|
||||
20200429015603
|
||||
20200429181335
|
||||
20200429181955
|
||||
|
@ -13808,10 +13810,13 @@ COPY "schema_migrations" (version) FROM STDIN;
|
|||
20200511121549
|
||||
20200511121610
|
||||
20200511121620
|
||||
20200511130129
|
||||
20200511130130
|
||||
20200511145545
|
||||
20200511162057
|
||||
20200511162115
|
||||
20200512085150
|
||||
20200512164334
|
||||
20200513234502
|
||||
20200513235347
|
||||
20200513235532
|
||||
|
|
|
@ -98,7 +98,7 @@ Starting with 8.14, Redis Sentinel is no longer experimental.
|
|||
If you've used it with versions `< 8.14` before, please check the updated
|
||||
documentation here.
|
||||
|
||||
High Availability with [Redis](https://redis.io/) is possible using a **Master** x **Slave**
|
||||
High Availability with [Redis](https://redis.io/) is possible using a **Master** x **Replica**
|
||||
topology with a [Redis Sentinel](https://redis.io/topics/sentinel) service to watch and automatically
|
||||
start the failover procedure.
|
||||
|
||||
|
@ -130,12 +130,12 @@ make sure you read this Overview section to better understand how the components
|
|||
are tied together.
|
||||
|
||||
You need at least `3` independent machines: physical, or VMs running into
|
||||
distinct physical machines. It is essential that all master and slaves Redis
|
||||
distinct physical machines. It is essential that all master and replica Redis
|
||||
instances run in different machines. If you fail to provision the machines in
|
||||
that specific way, any issue with the shared environment can bring your entire
|
||||
setup down.
|
||||
|
||||
It is OK to run a Sentinel alongside of a master or slave Redis instance.
|
||||
It is OK to run a Sentinel alongside of a master or replica Redis instance.
|
||||
There should be no more than one Sentinel on the same machine though.
|
||||
|
||||
You also need to take into consideration the underlying network topology,
|
||||
|
@ -156,16 +156,16 @@ components below.
|
|||
High Availability with Redis requires a few things:
|
||||
|
||||
- Multiple Redis instances
|
||||
- Run Redis in a **Master** x **Slave** topology
|
||||
- Run Redis in a **Master** x **Replica** topology
|
||||
- Multiple Sentinel instances
|
||||
- Application support and visibility to all Sentinel and Redis instances
|
||||
|
||||
Redis Sentinel can handle the most important tasks in an HA environment and that's
|
||||
to help keep servers online with minimal to no downtime. Redis Sentinel:
|
||||
|
||||
- Monitors **Master** and **Slaves** instances to see if they are available
|
||||
- Promotes a **Slave** to **Master** when the **Master** fails
|
||||
- Demotes a **Master** to **Slave** when the failed **Master** comes back online
|
||||
- Monitors **Master** and **Replicas** instances to see if they are available
|
||||
- Promotes a **Replica** to **Master** when the **Master** fails
|
||||
- Demotes a **Master** to **Replica** when the failed **Master** comes back online
|
||||
(to prevent data-partitioning)
|
||||
- Can be queried by the application to always connect to the current **Master**
|
||||
server
|
||||
|
@ -185,8 +185,8 @@ For a minimal setup, you will install the Omnibus GitLab package in `3`
|
|||
**independent** machines, both with **Redis** and **Sentinel**:
|
||||
|
||||
- Redis Master + Sentinel
|
||||
- Redis Slave + Sentinel
|
||||
- Redis Slave + Sentinel
|
||||
- Redis Replica + Sentinel
|
||||
- Redis Replica + Sentinel
|
||||
|
||||
If you are not sure or don't understand why and where the amount of nodes come
|
||||
from, read [Redis setup overview](#redis-setup-overview) and
|
||||
|
@ -197,14 +197,14 @@ the Omnibus GitLab package in `5` **independent** machines, both with
|
|||
**Redis** and **Sentinel**:
|
||||
|
||||
- Redis Master + Sentinel
|
||||
- Redis Slave + Sentinel
|
||||
- Redis Slave + Sentinel
|
||||
- Redis Slave + Sentinel
|
||||
- Redis Slave + Sentinel
|
||||
- Redis Replica + Sentinel
|
||||
- Redis Replica + Sentinel
|
||||
- Redis Replica + Sentinel
|
||||
- Redis Replica + Sentinel
|
||||
|
||||
### Redis setup overview
|
||||
|
||||
You must have at least `3` Redis servers: `1` Master, `2` Slaves, and they
|
||||
You must have at least `3` Redis servers: `1` Master, `2` Replicas, and they
|
||||
need to each be on independent machines (see explanation above).
|
||||
|
||||
You can have additional Redis nodes, that will help survive a situation
|
||||
|
@ -221,7 +221,7 @@ nodes to be provisioned. See [Sentinel setup overview](#sentinel-setup-overview)
|
|||
documentation for more information.
|
||||
|
||||
All Redis nodes should be configured the same way and with similar server specs, as
|
||||
in a failover situation, any **Slave** can be promoted as the new **Master** by
|
||||
in a failover situation, any **Replica** can be promoted as the new **Master** by
|
||||
the Sentinel servers.
|
||||
|
||||
The replication requires authentication, so you need to define a password to
|
||||
|
@ -241,9 +241,9 @@ need to be available and reachable, so that they can elect the Sentinel **leader
|
|||
who will take all the decisions to restore the service availability by:
|
||||
|
||||
- Promoting a new **Master**
|
||||
- Reconfiguring the other **Slaves** and make them point to the new **Master**
|
||||
- Reconfiguring the other **Replicas** and make them point to the new **Master**
|
||||
- Announce the new **Master** to every other Sentinel peer
|
||||
- Reconfigure the old **Master** and demote to **Slave** when it comes back online
|
||||
- Reconfigure the old **Master** and demote to **Replica** when it comes back online
|
||||
|
||||
You must have at least `3` Redis Sentinel servers, and they need to
|
||||
be each in an independent machine (that are believed to fail independently),
|
||||
|
@ -280,18 +280,18 @@ the official documentation:
|
|||
already tried against the same master by a given Sentinel, is two
|
||||
times the failover timeout.
|
||||
|
||||
- The time needed for a slave replicating to a wrong master according
|
||||
- The time needed for a replica replicating to a wrong master according
|
||||
to a Sentinel current configuration, to be forced to replicate
|
||||
with the right master, is exactly the failover timeout (counting since
|
||||
the moment a Sentinel detected the misconfiguration).
|
||||
|
||||
- The time needed to cancel a failover that is already in progress but
|
||||
did not produced any configuration change (SLAVEOF NO ONE yet not
|
||||
acknowledged by the promoted slave).
|
||||
did not produced any configuration change (REPLICAOF NO ONE yet not
|
||||
acknowledged by the promoted replica).
|
||||
|
||||
- The maximum time a failover in progress waits for all the slaves to be
|
||||
reconfigured as slaves of the new master. However even after this time
|
||||
the slaves will be reconfigured by the Sentinels anyway, but not with
|
||||
- The maximum time a failover in progress waits for all the replicas to be
|
||||
reconfigured as replicas of the new master. However even after this time
|
||||
the replicas will be reconfigured by the Sentinels anyway, but not with
|
||||
the exact parallel-syncs progression as specified.
|
||||
|
||||
### Available configuration setups
|
||||
|
@ -306,12 +306,12 @@ Pick the one that suits your needs.
|
|||
documentation.
|
||||
- [Omnibus GitLab **Community Edition** (CE) package](https://about.gitlab.com/install/?version=ce): Redis is bundled, so you
|
||||
can use the package with only the Redis service enabled as described in steps
|
||||
1 and 2 of this document (works for both master and slave setups). To install
|
||||
1 and 2 of this document (works for both master and replica setups). To install
|
||||
and configure Sentinel, jump directly to the Sentinel section in the
|
||||
[Redis HA installation from source](redis_source.md#step-3-configuring-the-redis-sentinel-instances) documentation.
|
||||
- [Omnibus GitLab **Enterprise Edition** (EE) package](https://about.gitlab.com/install/?version=ee): Both Redis and Sentinel
|
||||
are bundled in the package, so you can use the EE package to set up the whole
|
||||
Redis HA infrastructure (master, slave and Sentinel) which is described in
|
||||
Redis HA infrastructure (master, replica and Sentinel) which is described in
|
||||
this document.
|
||||
- If you have installed GitLab using the Omnibus GitLab packages (CE or EE),
|
||||
but you want to use your own external Redis server, follow steps 1-3 in the
|
||||
|
@ -328,9 +328,9 @@ This is the section where we install and set up the new Redis instances.
|
|||
> - We assume that you have installed GitLab and all HA components from scratch. If you
|
||||
> already have it installed and running, read how to
|
||||
> [switch from a single-machine installation to Redis HA](#switching-from-an-existing-single-machine-installation-to-redis-ha).
|
||||
> - Redis nodes (both master and slaves) will need the same password defined in
|
||||
> - Redis nodes (both master and replica) will need the same password defined in
|
||||
> `redis['password']`. At any time during a failover the Sentinels can
|
||||
> reconfigure a node and change its status from master to slave and vice versa.
|
||||
> reconfigure a node and change its status from master to replica and vice versa.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
|
@ -392,9 +392,9 @@ The prerequisites for a HA Redis setup are the following:
|
|||
> `roles ['redis_sentinel_role', 'redis_master_role']`. Read more about high
|
||||
> availability roles at <https://docs.gitlab.com/omnibus/roles/>.
|
||||
|
||||
### Step 2. Configuring the slave Redis instances
|
||||
### Step 2. Configuring the replica Redis instances
|
||||
|
||||
1. SSH into the **slave** Redis server.
|
||||
1. SSH into the **replica** Redis server.
|
||||
1. [Download/install](https://about.gitlab.com/install/) the Omnibus GitLab
|
||||
package you want using **steps 1 and 2** from the GitLab downloads page.
|
||||
- Make sure you select the correct Omnibus package, with the same version
|
||||
|
@ -404,8 +404,8 @@ The prerequisites for a HA Redis setup are the following:
|
|||
1. Edit `/etc/gitlab/gitlab.rb` and add the contents:
|
||||
|
||||
```ruby
|
||||
# Specify server role as 'redis_slave_role'
|
||||
roles ['redis_slave_role']
|
||||
# Specify server role as 'redis_replica_role'
|
||||
roles ['redis_replica_role']
|
||||
|
||||
# IP address pointing to a local IP that the other machines can reach to.
|
||||
# You can also set bind to '0.0.0.0' which listen in all interfaces.
|
||||
|
@ -435,10 +435,10 @@ The prerequisites for a HA Redis setup are the following:
|
|||
```
|
||||
|
||||
1. [Reconfigure Omnibus GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
|
||||
1. Go through the steps again for all the other slave nodes.
|
||||
1. Go through the steps again for all the other replica nodes.
|
||||
|
||||
> Note: You can specify multiple roles like sentinel and Redis as:
|
||||
> `roles ['redis_sentinel_role', 'redis_slave_role']`. Read more about high
|
||||
> `roles ['redis_sentinel_role', 'redis_replica_role']`. Read more about high
|
||||
> availability roles at <https://docs.gitlab.com/omnibus/roles/>.
|
||||
|
||||
---
|
||||
|
@ -542,18 +542,18 @@ multiple machines with the Sentinel daemon.
|
|||
## already tried against the same master by a given Sentinel, is two
|
||||
## times the failover timeout.
|
||||
##
|
||||
## - The time needed for a slave replicating to a wrong master according
|
||||
## - The time needed for a replica replicating to a wrong master according
|
||||
## to a Sentinel current configuration, to be forced to replicate
|
||||
## with the right master, is exactly the failover timeout (counting since
|
||||
## the moment a Sentinel detected the misconfiguration).
|
||||
##
|
||||
## - The time needed to cancel a failover that is already in progress but
|
||||
## did not produced any configuration change (SLAVEOF NO ONE yet not
|
||||
## acknowledged by the promoted slave).
|
||||
## did not produced any configuration change (REPLICAOF NO ONE yet not
|
||||
## acknowledged by the promoted replica).
|
||||
##
|
||||
## - The maximum time a failover in progress waits for all the slaves to be
|
||||
## reconfigured as slaves of the new master. However even after this time
|
||||
## the slaves will be reconfigured by the Sentinels anyway, but not with
|
||||
## - The maximum time a failover in progress waits for all the replica to be
|
||||
## reconfigured as replicas of the new master. However even after this time
|
||||
## the replicas will be reconfigured by the Sentinels anyway, but not with
|
||||
## the exact parallel-syncs progression as specified.
|
||||
# sentinel['failover_timeout'] = 60000
|
||||
```
|
||||
|
@ -612,7 +612,7 @@ replicate from this machine first, before de-activating the Redis instance
|
|||
inside it.
|
||||
|
||||
Your single-machine install will be the initial **Master**, and the `3` others
|
||||
should be configured as **Slave** pointing to this machine.
|
||||
should be configured as **Replica** pointing to this machine.
|
||||
|
||||
After replication catches up, you will need to stop services in the
|
||||
single-machine install, to rotate the **Master** to one of the new nodes.
|
||||
|
@ -627,7 +627,7 @@ redis['enable'] = false
|
|||
|
||||
If you fail to replicate first, you may loose data (unprocessed background jobs).
|
||||
|
||||
## Example of a minimal configuration with 1 master, 2 slaves and 3 Sentinels
|
||||
## Example of a minimal configuration with 1 master, 2 replicas and 3 Sentinels
|
||||
|
||||
>**Note:**
|
||||
Redis Sentinel is bundled with Omnibus GitLab Enterprise Edition only. For
|
||||
|
@ -649,8 +649,8 @@ discussed in [Redis setup overview](#redis-setup-overview) and
|
|||
Here is a list and description of each **machine** and the assigned **IP**:
|
||||
|
||||
- `10.0.0.1`: Redis Master + Sentinel 1
|
||||
- `10.0.0.2`: Redis Slave 1 + Sentinel 2
|
||||
- `10.0.0.3`: Redis Slave 2 + Sentinel 3
|
||||
- `10.0.0.2`: Redis Replica 1 + Sentinel 2
|
||||
- `10.0.0.3`: Redis Replica 2 + Sentinel 3
|
||||
- `10.0.0.4`: GitLab application
|
||||
|
||||
Please note that after the initial configuration, if a failover is initiated
|
||||
|
@ -684,12 +684,12 @@ sentinel['quorum'] = 2
|
|||
|
||||
[Reconfigure Omnibus GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
|
||||
|
||||
### Example configuration for Redis slave 1 and Sentinel 2
|
||||
### Example configuration for Redis replica 1 and Sentinel 2
|
||||
|
||||
In `/etc/gitlab/gitlab.rb`:
|
||||
|
||||
```ruby
|
||||
roles ['redis_sentinel_role', 'redis_slave_role']
|
||||
roles ['redis_sentinel_role', 'redis_replica_role']
|
||||
redis['bind'] = '10.0.0.2'
|
||||
redis['port'] = 6379
|
||||
redis['password'] = 'redis-password-goes-here'
|
||||
|
@ -706,12 +706,12 @@ sentinel['quorum'] = 2
|
|||
|
||||
[Reconfigure Omnibus GitLab](../restart_gitlab.md#omnibus-gitlab-reconfigure) for the changes to take effect.
|
||||
|
||||
### Example configuration for Redis slave 2 and Sentinel 3
|
||||
### Example configuration for Redis replica 2 and Sentinel 3
|
||||
|
||||
In `/etc/gitlab/gitlab.rb`:
|
||||
|
||||
```ruby
|
||||
roles ['redis_sentinel_role', 'redis_slave_role']
|
||||
roles ['redis_sentinel_role', 'redis_replica_role']
|
||||
redis['bind'] = '10.0.0.3'
|
||||
redis['port'] = 6379
|
||||
redis['password'] = 'redis-password-goes-here'
|
||||
|
@ -847,11 +847,11 @@ mailroom['enable'] = false
|
|||
|
||||
-------
|
||||
|
||||
## Redis master/slave Role
|
||||
## Redis master/replica Role
|
||||
redis_master_role['enable'] = true # enable only one of them
|
||||
redis_slave_role['enable'] = true # enable only one of them
|
||||
redis_replica_role['enable'] = true # enable only one of them
|
||||
|
||||
# When Redis Master or Slave role are enabled, the following services are
|
||||
# When Redis Master or Replica role are enabled, the following services are
|
||||
# enabled/disabled. Note that if Redis and Sentinel roles are combined, both
|
||||
# services will be enabled.
|
||||
|
||||
|
@ -863,7 +863,7 @@ postgresql['enable'] = false
|
|||
gitlab_rails['enable'] = false
|
||||
mailroom['enable'] = false
|
||||
|
||||
# For Redis Slave role, also change this setting from default 'true' to 'false':
|
||||
# For Redis Replica role, also change this setting from default 'true' to 'false':
|
||||
redis['master'] = false
|
||||
```
|
||||
|
||||
|
@ -894,13 +894,13 @@ You can check if everything is correct by connecting to each server using
|
|||
```
|
||||
|
||||
When connected to a `master` Redis, you will see the number of connected
|
||||
`slaves`, and a list of each with connection details:
|
||||
`replicas`, and a list of each with connection details:
|
||||
|
||||
```plaintext
|
||||
# Replication
|
||||
role:master
|
||||
connected_slaves:1
|
||||
slave0:ip=10.133.5.21,port=6379,state=online,offset=208037514,lag=1
|
||||
connected_replicas:1
|
||||
replica0:ip=10.133.5.21,port=6379,state=online,offset=208037514,lag=1
|
||||
master_repl_offset:208037658
|
||||
repl_backlog_active:1
|
||||
repl_backlog_size:1048576
|
||||
|
@ -908,21 +908,21 @@ repl_backlog_first_byte_offset:206989083
|
|||
repl_backlog_histlen:1048576
|
||||
```
|
||||
|
||||
When it's a `slave`, you will see details of the master connection and if
|
||||
When it's a `replica`, you will see details of the master connection and if
|
||||
its `up` or `down`:
|
||||
|
||||
```plaintext
|
||||
# Replication
|
||||
role:slave
|
||||
role:replica
|
||||
master_host:10.133.1.58
|
||||
master_port:6379
|
||||
master_link_status:up
|
||||
master_last_io_seconds_ago:1
|
||||
master_sync_in_progress:0
|
||||
slave_repl_offset:208096498
|
||||
slave_priority:100
|
||||
slave_read_only:1
|
||||
connected_slaves:0
|
||||
replica_repl_offset:208096498
|
||||
replica_priority:100
|
||||
replica_read_only:1
|
||||
connected_replicas:0
|
||||
master_repl_offset:0
|
||||
repl_backlog_active:0
|
||||
repl_backlog_size:1048576
|
||||
|
|
|
@ -40,7 +40,7 @@ This is the section where we install and set up the new Redis instances.
|
|||
- Since Redis 3.2, you must define a password to receive external connections
|
||||
(`requirepass`).
|
||||
- If you are using Redis with Sentinel, you will also need to define the same
|
||||
password for the slave password definition (`masterauth`) in the same instance.
|
||||
password for the replica password definition (`masterauth`) in the same instance.
|
||||
|
||||
In addition, read the prerequisites as described in the
|
||||
[Omnibus Redis HA document](redis.md#prerequisites) since they provide some
|
||||
|
@ -72,9 +72,9 @@ Assuming that the Redis master instance IP is `10.0.0.1`:
|
|||
|
||||
1. Restart the Redis service for the changes to take effect.
|
||||
|
||||
### Step 2. Configuring the slave Redis instances
|
||||
### Step 2. Configuring the replica Redis instances
|
||||
|
||||
Assuming that the Redis slave instance IP is `10.0.0.2`:
|
||||
Assuming that the Redis replica instance IP is `10.0.0.2`:
|
||||
|
||||
1. [Install Redis](../../install/installation.md#7-redis).
|
||||
1. Edit `/etc/redis/redis.conf`:
|
||||
|
@ -95,12 +95,12 @@ Assuming that the Redis slave instance IP is `10.0.0.2`:
|
|||
requirepass redis-password-goes-here
|
||||
masterauth redis-password-goes-here
|
||||
|
||||
## Define `slaveof` pointing to the Redis master instance with IP and port.
|
||||
slaveof 10.0.0.1 6379
|
||||
## Define `replicaof` pointing to the Redis master instance with IP and port.
|
||||
replicaof 10.0.0.1 6379
|
||||
```
|
||||
|
||||
1. Restart the Redis service for the changes to take effect.
|
||||
1. Go through the steps again for all the other slave nodes.
|
||||
1. Go through the steps again for all the other replica nodes.
|
||||
|
||||
### Step 3. Configuring the Redis Sentinel instances
|
||||
|
||||
|
@ -131,7 +131,7 @@ master with IP `10.0.0.1` (some settings might overlap with the master):
|
|||
masterauth redis-password-goes-here
|
||||
|
||||
## Define with `sentinel auth-pass` the same shared password you have
|
||||
## defined for both Redis master and slaves instances.
|
||||
## defined for both Redis master and replicas instances.
|
||||
sentinel auth-pass gitlab-redis redis-password-goes-here
|
||||
|
||||
## Define with `sentinel monitor` the IP and port of the Redis
|
||||
|
@ -149,18 +149,18 @@ master with IP `10.0.0.1` (some settings might overlap with the master):
|
|||
## already tried against the same master by a given Sentinel, is two
|
||||
## times the failover timeout.
|
||||
##
|
||||
## * The time needed for a slave replicating to a wrong master according
|
||||
## * The time needed for a replica replicating to a wrong master according
|
||||
## to a Sentinel current configuration, to be forced to replicate
|
||||
## with the right master, is exactly the failover timeout (counting since
|
||||
## the moment a Sentinel detected the misconfiguration).
|
||||
##
|
||||
## * The time needed to cancel a failover that is already in progress but
|
||||
## did not produced any configuration change (SLAVEOF NO ONE yet not
|
||||
## acknowledged by the promoted slave).
|
||||
## did not produced any configuration change (REPLICAOF NO ONE yet not
|
||||
## acknowledged by the promoted replica).
|
||||
##
|
||||
## * The maximum time a failover in progress waits for all the slaves to be
|
||||
## reconfigured as slaves of the new master. However even after this time
|
||||
## the slaves will be reconfigured by the Sentinels anyway, but not with
|
||||
## * The maximum time a failover in progress waits for all the replicas to be
|
||||
## reconfigured as replicas of the new master. However even after this time
|
||||
## the replicas will be reconfigured by the Sentinels anyway, but not with
|
||||
## the exact parallel-syncs progression as specified.
|
||||
sentinel failover_timeout 30000
|
||||
```
|
||||
|
@ -203,7 +203,7 @@ setup:
|
|||
|
||||
1. [Restart GitLab](../restart_gitlab.md#installations-from-source) for the changes to take effect.
|
||||
|
||||
## Example of minimal configuration with 1 master, 2 slaves and 3 Sentinels
|
||||
## Example of minimal configuration with 1 master, 2 replicas and 3 Sentinels
|
||||
|
||||
In this example we consider that all servers have an internal network
|
||||
interface with IPs in the `10.0.0.x` range, and that they can connect
|
||||
|
@ -215,13 +215,13 @@ outside ([Internet](https://gitlab.com/gitlab-org/gitlab-foss/uploads/c4cc8cd353
|
|||
|
||||
For this example, **Sentinel 1** will be configured in the same machine as the
|
||||
**Redis Master**, **Sentinel 2** and **Sentinel 3** in the same machines as the
|
||||
**Slave 1** and **Slave 2** respectively.
|
||||
**Replica 1** and **Replica 2** respectively.
|
||||
|
||||
Here is a list and description of each **machine** and the assigned **IP**:
|
||||
|
||||
- `10.0.0.1`: Redis Master + Sentinel 1
|
||||
- `10.0.0.2`: Redis Slave 1 + Sentinel 2
|
||||
- `10.0.0.3`: Redis Slave 2 + Sentinel 3
|
||||
- `10.0.0.2`: Redis Replica 1 + Sentinel 2
|
||||
- `10.0.0.3`: Redis Replica 2 + Sentinel 3
|
||||
- `10.0.0.4`: GitLab application
|
||||
|
||||
Please note that after the initial configuration, if a failover is initiated
|
||||
|
@ -257,7 +257,7 @@ or a failover promotes a different **Master** node.
|
|||
|
||||
1. Restart the Redis service for the changes to take effect.
|
||||
|
||||
### Example configuration for Redis slave 1 and Sentinel 2
|
||||
### Example configuration for Redis replica 1 and Sentinel 2
|
||||
|
||||
1. In `/etc/redis/redis.conf`:
|
||||
|
||||
|
@ -266,7 +266,7 @@ or a failover promotes a different **Master** node.
|
|||
port 6379
|
||||
requirepass redis-password-goes-here
|
||||
masterauth redis-password-goes-here
|
||||
slaveof 10.0.0.1 6379
|
||||
replicaof 10.0.0.1 6379
|
||||
```
|
||||
|
||||
1. In `/etc/redis/sentinel.conf`:
|
||||
|
@ -282,7 +282,7 @@ or a failover promotes a different **Master** node.
|
|||
|
||||
1. Restart the Redis service for the changes to take effect.
|
||||
|
||||
### Example configuration for Redis slave 2 and Sentinel 3
|
||||
### Example configuration for Redis replica 2 and Sentinel 3
|
||||
|
||||
1. In `/etc/redis/redis.conf`:
|
||||
|
||||
|
@ -291,7 +291,7 @@ or a failover promotes a different **Master** node.
|
|||
port 6379
|
||||
requirepass redis-password-goes-here
|
||||
masterauth redis-password-goes-here
|
||||
slaveof 10.0.0.1 6379
|
||||
replicaof 10.0.0.1 6379
|
||||
```
|
||||
|
||||
1. In `/etc/redis/sentinel.conf`:
|
||||
|
|
|
@ -139,6 +139,7 @@ The following API resources are available outside of project and group contexts
|
|||
| [Notification settings](notification_settings.md) | `/notification_settings` (also available for groups and projects) |
|
||||
| [Pages domains](pages_domains.md) | `/pages/domains` (also available for projects) |
|
||||
| [Projects](projects.md) | `/users/:id/projects` (also available for projects) |
|
||||
| [Project Repository Storage Moves](project_repository_storage_moves.md) | `/project_repository_storage_moves` |
|
||||
| [Runners](runners.md) | `/runners` (also available for projects) |
|
||||
| [Search](search.md) | `/search` (also available for groups and projects) |
|
||||
| [Settings](settings.md) **(CORE ONLY)** | `/application/settings` |
|
||||
|
|
|
@ -1115,6 +1115,61 @@ type CreateImageDiffNotePayload {
|
|||
note: Note
|
||||
}
|
||||
|
||||
"""
|
||||
Autogenerated input type of CreateIteration
|
||||
"""
|
||||
input CreateIterationInput {
|
||||
"""
|
||||
A unique identifier for the client performing the mutation.
|
||||
"""
|
||||
clientMutationId: String
|
||||
|
||||
"""
|
||||
The description of the iteration
|
||||
"""
|
||||
description: String
|
||||
|
||||
"""
|
||||
The end date of the iteration
|
||||
"""
|
||||
dueDate: String
|
||||
|
||||
"""
|
||||
The target group for the iteration
|
||||
"""
|
||||
groupPath: ID!
|
||||
|
||||
"""
|
||||
The start date of the iteration
|
||||
"""
|
||||
startDate: String
|
||||
|
||||
"""
|
||||
The title of the iteration
|
||||
"""
|
||||
title: String
|
||||
}
|
||||
|
||||
"""
|
||||
Autogenerated return type of CreateIteration
|
||||
"""
|
||||
type CreateIterationPayload {
|
||||
"""
|
||||
A unique identifier for the client performing the mutation.
|
||||
"""
|
||||
clientMutationId: String
|
||||
|
||||
"""
|
||||
Errors encountered during execution of the mutation.
|
||||
"""
|
||||
errors: [String!]!
|
||||
|
||||
"""
|
||||
The created iteration
|
||||
"""
|
||||
iteration: Iteration
|
||||
}
|
||||
|
||||
"""
|
||||
Autogenerated input type of CreateNote
|
||||
"""
|
||||
|
@ -3186,6 +3241,11 @@ type EpicIssue implements Noteable {
|
|||
"""
|
||||
iid: ID!
|
||||
|
||||
"""
|
||||
Iteration of the issue
|
||||
"""
|
||||
iteration: Iteration
|
||||
|
||||
"""
|
||||
Labels of the issue
|
||||
"""
|
||||
|
@ -4075,6 +4135,53 @@ type Group {
|
|||
updatedBefore: Time
|
||||
): IssueConnection
|
||||
|
||||
"""
|
||||
Find iterations
|
||||
"""
|
||||
iterations(
|
||||
"""
|
||||
Returns the elements in the list that come after the specified cursor.
|
||||
"""
|
||||
after: String
|
||||
|
||||
"""
|
||||
Returns the elements in the list that come before the specified cursor.
|
||||
"""
|
||||
before: String
|
||||
|
||||
"""
|
||||
List items within a time frame where items.end_date is between startDate and
|
||||
endDate parameters (startDate parameter must be present)
|
||||
"""
|
||||
endDate: Time
|
||||
|
||||
"""
|
||||
Returns the first _n_ elements from the list.
|
||||
"""
|
||||
first: Int
|
||||
|
||||
"""
|
||||
Returns the last _n_ elements from the list.
|
||||
"""
|
||||
last: Int
|
||||
|
||||
"""
|
||||
List items within a time frame where items.start_date is between startDate
|
||||
and endDate parameters (endDate parameter must be present)
|
||||
"""
|
||||
startDate: Time
|
||||
|
||||
"""
|
||||
Filter iterations by state
|
||||
"""
|
||||
state: IterationState
|
||||
|
||||
"""
|
||||
Fuzzy search by title
|
||||
"""
|
||||
title: String
|
||||
): IterationConnection
|
||||
|
||||
"""
|
||||
Indicates if Large File Storage (LFS) is enabled for namespace
|
||||
"""
|
||||
|
@ -4536,6 +4643,11 @@ type Issue implements Noteable {
|
|||
"""
|
||||
iid: ID!
|
||||
|
||||
"""
|
||||
Iteration of the issue
|
||||
"""
|
||||
iteration: Iteration
|
||||
|
||||
"""
|
||||
Labels of the issue
|
||||
"""
|
||||
|
@ -4872,6 +4984,51 @@ type IssueSetDueDatePayload {
|
|||
issue: Issue
|
||||
}
|
||||
|
||||
"""
|
||||
Autogenerated input type of IssueSetIteration
|
||||
"""
|
||||
input IssueSetIterationInput {
|
||||
"""
|
||||
A unique identifier for the client performing the mutation.
|
||||
"""
|
||||
clientMutationId: String
|
||||
|
||||
"""
|
||||
The iid of the issue to mutate
|
||||
"""
|
||||
iid: String!
|
||||
|
||||
"""
|
||||
The iteration to assign to the issue.
|
||||
"""
|
||||
iterationId: ID
|
||||
|
||||
"""
|
||||
The project the issue to mutate is in
|
||||
"""
|
||||
projectPath: ID!
|
||||
}
|
||||
|
||||
"""
|
||||
Autogenerated return type of IssueSetIteration
|
||||
"""
|
||||
type IssueSetIterationPayload {
|
||||
"""
|
||||
A unique identifier for the client performing the mutation.
|
||||
"""
|
||||
clientMutationId: String
|
||||
|
||||
"""
|
||||
Errors encountered during execution of the mutation.
|
||||
"""
|
||||
errors: [String!]!
|
||||
|
||||
"""
|
||||
The issue after mutation
|
||||
"""
|
||||
issue: Issue
|
||||
}
|
||||
|
||||
"""
|
||||
Autogenerated input type of IssueSetWeight
|
||||
"""
|
||||
|
@ -5006,6 +5163,107 @@ enum IssueState {
|
|||
opened
|
||||
}
|
||||
|
||||
"""
|
||||
Represents an iteration object.
|
||||
"""
|
||||
type Iteration {
|
||||
"""
|
||||
Timestamp of iteration creation
|
||||
"""
|
||||
createdAt: Time!
|
||||
|
||||
"""
|
||||
Description of the iteration
|
||||
"""
|
||||
description: String
|
||||
|
||||
"""
|
||||
Timestamp of the iteration due date
|
||||
"""
|
||||
dueDate: Time
|
||||
|
||||
"""
|
||||
ID of the iteration
|
||||
"""
|
||||
id: ID!
|
||||
|
||||
"""
|
||||
Timestamp of the iteration start date
|
||||
"""
|
||||
startDate: Time
|
||||
|
||||
"""
|
||||
State of the iteration
|
||||
"""
|
||||
state: IterationState!
|
||||
|
||||
"""
|
||||
Title of the iteration
|
||||
"""
|
||||
title: String!
|
||||
|
||||
"""
|
||||
Timestamp of last iteration update
|
||||
"""
|
||||
updatedAt: Time!
|
||||
|
||||
"""
|
||||
Web path of the iteration
|
||||
"""
|
||||
webPath: String!
|
||||
|
||||
"""
|
||||
Web URL of the iteration
|
||||
"""
|
||||
webUrl: String!
|
||||
}
|
||||
|
||||
"""
|
||||
The connection type for Iteration.
|
||||
"""
|
||||
type IterationConnection {
|
||||
"""
|
||||
A list of edges.
|
||||
"""
|
||||
edges: [IterationEdge]
|
||||
|
||||
"""
|
||||
A list of nodes.
|
||||
"""
|
||||
nodes: [Iteration]
|
||||
|
||||
"""
|
||||
Information to aid in pagination.
|
||||
"""
|
||||
pageInfo: PageInfo!
|
||||
}
|
||||
|
||||
"""
|
||||
An edge in a connection.
|
||||
"""
|
||||
type IterationEdge {
|
||||
"""
|
||||
A cursor for use in pagination.
|
||||
"""
|
||||
cursor: String!
|
||||
|
||||
"""
|
||||
The item at the end of the edge.
|
||||
"""
|
||||
node: Iteration
|
||||
}
|
||||
|
||||
"""
|
||||
State of a GitLab iteration
|
||||
"""
|
||||
enum IterationState {
|
||||
all
|
||||
closed
|
||||
opened
|
||||
started
|
||||
upcoming
|
||||
}
|
||||
|
||||
"""
|
||||
Represents untyped JSON
|
||||
"""
|
||||
|
@ -6253,6 +6511,7 @@ type Mutation {
|
|||
createDiffNote(input: CreateDiffNoteInput!): CreateDiffNotePayload
|
||||
createEpic(input: CreateEpicInput!): CreateEpicPayload
|
||||
createImageDiffNote(input: CreateImageDiffNoteInput!): CreateImageDiffNotePayload
|
||||
createIteration(input: CreateIterationInput!): CreateIterationPayload
|
||||
createNote(input: CreateNoteInput!): CreateNotePayload
|
||||
createRequirement(input: CreateRequirementInput!): CreateRequirementPayload
|
||||
createSnippet(input: CreateSnippetInput!): CreateSnippetPayload
|
||||
|
@ -6266,6 +6525,7 @@ type Mutation {
|
|||
epicTreeReorder(input: EpicTreeReorderInput!): EpicTreeReorderPayload
|
||||
issueSetConfidential(input: IssueSetConfidentialInput!): IssueSetConfidentialPayload
|
||||
issueSetDueDate(input: IssueSetDueDateInput!): IssueSetDueDatePayload
|
||||
issueSetIteration(input: IssueSetIterationInput!): IssueSetIterationPayload
|
||||
issueSetWeight(input: IssueSetWeightInput!): IssueSetWeightPayload
|
||||
jiraImportStart(input: JiraImportStartInput!): JiraImportStartPayload
|
||||
markAsSpamSnippet(input: MarkAsSpamSnippetInput!): MarkAsSpamSnippetPayload
|
||||
|
@ -8376,11 +8636,6 @@ type Release {
|
|||
"""
|
||||
descriptionHtml: String
|
||||
|
||||
"""
|
||||
SHA of the release's evidence
|
||||
"""
|
||||
evidenceSha: String
|
||||
|
||||
"""
|
||||
Milestones associated to the release
|
||||
"""
|
||||
|
@ -9246,7 +9501,6 @@ enum ServiceType {
|
|||
HANGOUTS_CHAT_SERVICE
|
||||
HIPCHAT_SERVICE
|
||||
IRKER_SERVICE
|
||||
JENKINS_DEPRECATED_SERVICE
|
||||
JENKINS_SERVICE
|
||||
JIRA_SERVICE
|
||||
MATTERMOST_SERVICE
|
||||
|
|
|
@ -3016,6 +3016,148 @@
|
|||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "INPUT_OBJECT",
|
||||
"name": "CreateIterationInput",
|
||||
"description": "Autogenerated input type of CreateIteration",
|
||||
"fields": null,
|
||||
"inputFields": [
|
||||
{
|
||||
"name": "groupPath",
|
||||
"description": "The target group for the iteration",
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "ID",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "title",
|
||||
"description": "The title of the iteration",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "description",
|
||||
"description": "The description of the iteration",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "startDate",
|
||||
"description": "The start date of the iteration",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "dueDate",
|
||||
"description": "The end date of the iteration",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "clientMutationId",
|
||||
"description": "A unique identifier for the client performing the mutation.",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
}
|
||||
],
|
||||
"interfaces": null,
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "CreateIterationPayload",
|
||||
"description": "Autogenerated return type of CreateIteration",
|
||||
"fields": [
|
||||
{
|
||||
"name": "clientMutationId",
|
||||
"description": "A unique identifier for the client performing the mutation.",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "errors",
|
||||
"description": "Errors encountered during execution of the mutation.",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "LIST",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "iteration",
|
||||
"description": "The created iteration",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "OBJECT",
|
||||
"name": "Iteration",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"inputFields": null,
|
||||
"interfaces": [
|
||||
|
||||
],
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "INPUT_OBJECT",
|
||||
"name": "CreateNoteInput",
|
||||
|
@ -9010,6 +9152,20 @@
|
|||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "iteration",
|
||||
"description": "Iteration of the issue",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "OBJECT",
|
||||
"name": "Iteration",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "labels",
|
||||
"description": "Labels of the issue",
|
||||
|
@ -11377,6 +11533,99 @@
|
|||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "iterations",
|
||||
"description": "Find iterations",
|
||||
"args": [
|
||||
{
|
||||
"name": "startDate",
|
||||
"description": "List items within a time frame where items.start_date is between startDate and endDate parameters (endDate parameter must be present)",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Time",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "endDate",
|
||||
"description": "List items within a time frame where items.end_date is between startDate and endDate parameters (startDate parameter must be present)",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Time",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "state",
|
||||
"description": "Filter iterations by state",
|
||||
"type": {
|
||||
"kind": "ENUM",
|
||||
"name": "IterationState",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "title",
|
||||
"description": "Fuzzy search by title",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "after",
|
||||
"description": "Returns the elements in the list that come after the specified cursor.",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "before",
|
||||
"description": "Returns the elements in the list that come before the specified cursor.",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "first",
|
||||
"description": "Returns the first _n_ elements from the list.",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Int",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "last",
|
||||
"description": "Returns the last _n_ elements from the list.",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Int",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"kind": "OBJECT",
|
||||
"name": "IterationConnection",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "lfsEnabled",
|
||||
"description": "Indicates if Large File Storage (LFS) is enabled for namespace",
|
||||
|
@ -12608,6 +12857,20 @@
|
|||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "iteration",
|
||||
"description": "Iteration of the issue",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "OBJECT",
|
||||
"name": "Iteration",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "labels",
|
||||
"description": "Labels of the issue",
|
||||
|
@ -13611,6 +13874,132 @@
|
|||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "INPUT_OBJECT",
|
||||
"name": "IssueSetIterationInput",
|
||||
"description": "Autogenerated input type of IssueSetIteration",
|
||||
"fields": null,
|
||||
"inputFields": [
|
||||
{
|
||||
"name": "projectPath",
|
||||
"description": "The project the issue to mutate is in",
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "ID",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "iid",
|
||||
"description": "The iid of the issue to mutate",
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "iterationId",
|
||||
"description": "The iteration to assign to the issue.\n",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "ID",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
},
|
||||
{
|
||||
"name": "clientMutationId",
|
||||
"description": "A unique identifier for the client performing the mutation.",
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"defaultValue": null
|
||||
}
|
||||
],
|
||||
"interfaces": null,
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "IssueSetIterationPayload",
|
||||
"description": "Autogenerated return type of IssueSetIteration",
|
||||
"fields": [
|
||||
{
|
||||
"name": "clientMutationId",
|
||||
"description": "A unique identifier for the client performing the mutation.",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "errors",
|
||||
"description": "Errors encountered during execution of the mutation.",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "LIST",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "issue",
|
||||
"description": "The issue after mutation",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "OBJECT",
|
||||
"name": "Issue",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"inputFields": null,
|
||||
"interfaces": [
|
||||
|
||||
],
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "INPUT_OBJECT",
|
||||
"name": "IssueSetWeightInput",
|
||||
|
@ -13871,6 +14260,340 @@
|
|||
],
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "Iteration",
|
||||
"description": "Represents an iteration object.",
|
||||
"fields": [
|
||||
{
|
||||
"name": "createdAt",
|
||||
"description": "Timestamp of iteration creation",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Time",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "description",
|
||||
"description": "Description of the iteration",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "dueDate",
|
||||
"description": "Timestamp of the iteration due date",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Time",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "id",
|
||||
"description": "ID of the iteration",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "ID",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "startDate",
|
||||
"description": "Timestamp of the iteration start date",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Time",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "state",
|
||||
"description": "State of the iteration",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "ENUM",
|
||||
"name": "IterationState",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "title",
|
||||
"description": "Title of the iteration",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "updatedAt",
|
||||
"description": "Timestamp of last iteration update",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "Time",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "webPath",
|
||||
"description": "Web path of the iteration",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "webUrl",
|
||||
"description": "Web URL of the iteration",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"inputFields": null,
|
||||
"interfaces": [
|
||||
|
||||
],
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "IterationConnection",
|
||||
"description": "The connection type for Iteration.",
|
||||
"fields": [
|
||||
{
|
||||
"name": "edges",
|
||||
"description": "A list of edges.",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "LIST",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "OBJECT",
|
||||
"name": "IterationEdge",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "nodes",
|
||||
"description": "A list of nodes.",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "LIST",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "OBJECT",
|
||||
"name": "Iteration",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "pageInfo",
|
||||
"description": "Information to aid in pagination.",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "OBJECT",
|
||||
"name": "PageInfo",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"inputFields": null,
|
||||
"interfaces": [
|
||||
|
||||
],
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "IterationEdge",
|
||||
"description": "An edge in a connection.",
|
||||
"fields": [
|
||||
{
|
||||
"name": "cursor",
|
||||
"description": "A cursor for use in pagination.",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "node",
|
||||
"description": "The item at the end of the edge.",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "OBJECT",
|
||||
"name": "Iteration",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"inputFields": null,
|
||||
"interfaces": [
|
||||
|
||||
],
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "ENUM",
|
||||
"name": "IterationState",
|
||||
"description": "State of a GitLab iteration",
|
||||
"fields": null,
|
||||
"inputFields": null,
|
||||
"interfaces": null,
|
||||
"enumValues": [
|
||||
{
|
||||
"name": "upcoming",
|
||||
"description": null,
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "started",
|
||||
"description": null,
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "opened",
|
||||
"description": null,
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "closed",
|
||||
"description": null,
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "all",
|
||||
"description": null,
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "SCALAR",
|
||||
"name": "JSON",
|
||||
|
@ -17779,6 +18502,33 @@
|
|||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "createIteration",
|
||||
"description": null,
|
||||
"args": [
|
||||
{
|
||||
"name": "input",
|
||||
"description": null,
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "INPUT_OBJECT",
|
||||
"name": "CreateIterationInput",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"defaultValue": null
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"kind": "OBJECT",
|
||||
"name": "CreateIterationPayload",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "createNote",
|
||||
"description": null,
|
||||
|
@ -18130,6 +18880,33 @@
|
|||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "issueSetIteration",
|
||||
"description": null,
|
||||
"args": [
|
||||
{
|
||||
"name": "input",
|
||||
"description": null,
|
||||
"type": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "INPUT_OBJECT",
|
||||
"name": "IssueSetIterationInput",
|
||||
"ofType": null
|
||||
}
|
||||
},
|
||||
"defaultValue": null
|
||||
}
|
||||
],
|
||||
"type": {
|
||||
"kind": "OBJECT",
|
||||
"name": "IssueSetIterationPayload",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "issueSetWeight",
|
||||
"description": null,
|
||||
|
@ -24597,20 +25374,6 @@
|
|||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "evidenceSha",
|
||||
"description": "SHA of the release's evidence",
|
||||
"args": [
|
||||
|
||||
],
|
||||
"type": {
|
||||
"kind": "SCALAR",
|
||||
"name": "String",
|
||||
"ofType": null
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "milestones",
|
||||
"description": "Milestones associated to the release",
|
||||
|
@ -27478,12 +28241,6 @@
|
|||
"description": null,
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "JENKINS_DEPRECATED_SERVICE",
|
||||
"description": null,
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"possibleTypes": null
|
||||
|
|
|
@ -204,6 +204,16 @@ Autogenerated return type of CreateImageDiffNote
|
|||
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
|
||||
| `note` | Note | The note after mutation |
|
||||
|
||||
## CreateIterationPayload
|
||||
|
||||
Autogenerated return type of CreateIteration
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | ---- | ---------- |
|
||||
| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
|
||||
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
|
||||
| `iteration` | Iteration | The created iteration |
|
||||
|
||||
## CreateNotePayload
|
||||
|
||||
Autogenerated return type of CreateNote
|
||||
|
@ -521,6 +531,7 @@ Relationship between an epic and an issue
|
|||
| `healthStatus` | HealthStatus | Current health status. Returns null if `save_issuable_health_status` feature flag is disabled. |
|
||||
| `id` | ID | Global ID of the epic-issue relation |
|
||||
| `iid` | ID! | Internal ID of the issue |
|
||||
| `iteration` | Iteration | Iteration of the issue |
|
||||
| `milestone` | Milestone | Milestone of the issue |
|
||||
| `reference` | String! | Internal reference of the issue. Returned in shortened format by default |
|
||||
| `relationPath` | String | URI path of the epic-issue relation |
|
||||
|
@ -660,6 +671,7 @@ Autogenerated return type of EpicTreeReorder
|
|||
| `epic` | Epic | Epic to which this issue belongs |
|
||||
| `healthStatus` | HealthStatus | Current health status. Returns null if `save_issuable_health_status` feature flag is disabled. |
|
||||
| `iid` | ID! | Internal ID of the issue |
|
||||
| `iteration` | Iteration | Iteration of the issue |
|
||||
| `milestone` | Milestone | Milestone of the issue |
|
||||
| `reference` | String! | Internal reference of the issue. Returned in shortened format by default |
|
||||
| `relativePosition` | Int | Relative position of the issue (used for positioning in epic tree and issue boards) |
|
||||
|
@ -713,6 +725,16 @@ Autogenerated return type of IssueSetDueDate
|
|||
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
|
||||
| `issue` | Issue | The issue after mutation |
|
||||
|
||||
## IssueSetIterationPayload
|
||||
|
||||
Autogenerated return type of IssueSetIteration
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | ---- | ---------- |
|
||||
| `clientMutationId` | String | A unique identifier for the client performing the mutation. |
|
||||
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
|
||||
| `issue` | Issue | The issue after mutation |
|
||||
|
||||
## IssueSetWeightPayload
|
||||
|
||||
Autogenerated return type of IssueSetWeight
|
||||
|
@ -723,6 +745,23 @@ Autogenerated return type of IssueSetWeight
|
|||
| `errors` | String! => Array | Errors encountered during execution of the mutation. |
|
||||
| `issue` | Issue | The issue after mutation |
|
||||
|
||||
## Iteration
|
||||
|
||||
Represents an iteration object.
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | ---- | ---------- |
|
||||
| `createdAt` | Time! | Timestamp of iteration creation |
|
||||
| `description` | String | Description of the iteration |
|
||||
| `dueDate` | Time | Timestamp of the iteration due date |
|
||||
| `id` | ID! | ID of the iteration |
|
||||
| `startDate` | Time | Timestamp of the iteration start date |
|
||||
| `state` | IterationState! | State of the iteration |
|
||||
| `title` | String! | Title of the iteration |
|
||||
| `updatedAt` | Time! | Timestamp of last iteration update |
|
||||
| `webPath` | String! | Web path of the iteration |
|
||||
| `webUrl` | String! | Web URL of the iteration |
|
||||
|
||||
## JiraImport
|
||||
|
||||
| Name | Type | Description |
|
||||
|
@ -1166,7 +1205,6 @@ Information about pagination in a connection.
|
|||
| `createdAt` | Time | Timestamp of when the release was created |
|
||||
| `description` | String | Description (also known as "release notes") of the release |
|
||||
| `descriptionHtml` | String | The GitLab Flavored Markdown rendering of `description` |
|
||||
| `evidenceSha` | String | SHA of the release's evidence |
|
||||
| `name` | String | Name of the release |
|
||||
| `releasedAt` | Time | Timestamp of when the release was released |
|
||||
| `tagName` | String! | Name of the tag associated with the release |
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
# Project repository storage move API
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/31285) in GitLab 13.0.
|
||||
|
||||
Project repository storage can be moved. To retrieve project repository storage moves using the API, you must [authenticate yourself](README.md#authentication) as an administrator.
|
||||
|
||||
## Retrieve all project repository storage moves
|
||||
|
||||
```text
|
||||
GET /project_repository_storage_moves
|
||||
```
|
||||
|
||||
By default, `GET` requests return 20 results at a time because the API results
|
||||
are [paginated](README.md#pagination).
|
||||
|
||||
Example request:
|
||||
|
||||
```shell
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" 'https://primary.example.com/api/v4/project_repository_storage_moves'
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": 1,
|
||||
"created_at": "2020-05-07T04:27:17.234Z",
|
||||
"state": "scheduled",
|
||||
"source_storage_name": "default",
|
||||
"destination_storage_name": "storage2",
|
||||
"project": {
|
||||
"id": 1,
|
||||
"description": null,
|
||||
"name": "project1",
|
||||
"name_with_namespace": "John Doe2 / project1",
|
||||
"path": "project1",
|
||||
"path_with_namespace": "namespace1/project1",
|
||||
"created_at": "2020-05-07T04:27:17.016Z"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
## Get a single project repository storage move
|
||||
|
||||
```text
|
||||
GET /project_repository_storage_moves/:id
|
||||
```
|
||||
|
||||
Parameters:
|
||||
|
||||
| Attribute | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `id` | integer | yes | The ID of the project repository storage move |
|
||||
|
||||
Example request:
|
||||
|
||||
```shell
|
||||
curl --header "PRIVATE-TOKEN: <your_access_token>" 'https://primary.example.com/api/v4/project_repository_storage_moves/1'
|
||||
```
|
||||
|
||||
Example response:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"created_at": "2020-05-07T04:27:17.234Z",
|
||||
"state": "scheduled",
|
||||
"source_storage_name": "default",
|
||||
"destination_storage_name": "storage2",
|
||||
"project": {
|
||||
"id": 1,
|
||||
"description": null,
|
||||
"name": "project1",
|
||||
"name_with_namespace": "John Doe2 / project1",
|
||||
"path": "project1",
|
||||
"path_with_namespace": "namespace1/project1",
|
||||
"created_at": "2020-05-07T04:27:17.016Z"
|
||||
}
|
||||
```
|
|
@ -1303,6 +1303,9 @@ GET /projects/:id/services/jenkins
|
|||
|
||||
A continuous integration and build server
|
||||
|
||||
NOTE: **Note:**
|
||||
This service was [removed in v13.0](https://gitlab.com/gitlab-org/gitlab/-/issues/1600)
|
||||
|
||||
### Create/Edit Jenkins CI (Deprecated) service
|
||||
|
||||
Set Jenkins CI (Deprecated) service for a project.
|
||||
|
|
|
@ -214,7 +214,7 @@ Redis process.
|
|||
Single-core: Like PgBouncer, a single Redis process can only use one
|
||||
core. It does not support multi-threading.
|
||||
|
||||
Dumb secondaries: Redis secondaries (aka slaves) don't actually
|
||||
Dumb secondaries: Redis secondaries (aka replicas) don't actually
|
||||
handle any load. Unlike PostgreSQL secondaries, they don't even serve
|
||||
read queries. They simply replicate data from the primary and take over
|
||||
only when the primary fails.
|
||||
|
@ -231,8 +231,8 @@ election to determine a new leader.
|
|||
No leader: A Redis cluster can get into a mode where there are no
|
||||
primaries. For example, this can happen if Redis nodes are misconfigured
|
||||
to follow the wrong node. Sometimes this requires forcing one node to
|
||||
become a primary via the [`SLAVEOF NO ONE`
|
||||
command](https://redis.io/commands/slaveof).
|
||||
become a primary via the [`REPLICAOF NO ONE`
|
||||
command](https://redis.io/commands/replicaof).
|
||||
|
||||
### Sidekiq
|
||||
|
||||
|
|
|
@ -48,15 +48,18 @@ to avoid getting this error, you need to remove all instances of the
|
|||
**Omnibus Installation**
|
||||
|
||||
```shell
|
||||
sudo gitlab-rails runner "Service.where(type: ['JenkinsService', 'JenkinsDeprecatedService', 'GithubService']).delete_all"
|
||||
sudo gitlab-rails runner "Service.where(type: ['JenkinsService', 'GithubService']).delete_all"
|
||||
```
|
||||
|
||||
**Source Installation**
|
||||
|
||||
```shell
|
||||
bundle exec rails runner "Service.where(type: ['JenkinsService', 'JenkinsDeprecatedService', 'GithubService']).delete_all" production
|
||||
bundle exec rails runner "Service.where(type: ['JenkinsService', 'GithubService']).delete_all" production
|
||||
```
|
||||
|
||||
NOTE: **Note:**
|
||||
If you are running `GitLab =< v13.0` you need to also remove `JenkinsDeprecatedService` records.
|
||||
|
||||
### Variables environment scopes
|
||||
|
||||
If you're using this feature and there are variables sharing the same
|
||||
|
|
|
@ -79,7 +79,7 @@ As we'll be using [Amazon S3 object storage](#amazon-s3-object-storage), our EC2
|
|||
"Effect": "Allow",
|
||||
"Action": [
|
||||
"s3:AbortMultipartUpload",
|
||||
"s3::CompleteMultipartUpload",
|
||||
"s3:CompleteMultipartUpload",
|
||||
"s3:ListBucket",
|
||||
"s3:PutObject",
|
||||
"s3:GetObject",
|
||||
|
|
|
@ -152,8 +152,8 @@ The following Elasticsearch settings are available:
|
|||
| `AWS Access Key` | The AWS access key. |
|
||||
| `AWS Secret Access Key` | The AWS secret access key. |
|
||||
| `Maximum field length` | See [the explanation in instance limits.](../administration/instance_limits.md#maximum-field-length). |
|
||||
| `Maximum bulk request size (MiB)` | Repository indexing uses the Elasticsearch bulk request API. This setting determines the maximum size of an individual bulk request during these operations. |
|
||||
| `Bulk request concurrency` | Each repository indexing operation may submit bulk requests in parallel. This increases indexing performance, but fills the Elasticsearch bulk requests queue faster. |
|
||||
| `Maximum bulk request size (MiB)` | The Maximum Bulk Request size is used by GitLab's Golang-based indexer processes and indicates how much data it ought to collect (and store in memory) in a given indexing process before submitting the payload to Elasticsearch’s Bulk API. This setting works in tandem with the Bulk request concurrency setting (see below) and needs to accomodate the resource constraints of both the Elasticsearch host(s) and the host(s) running GitLab's Golang-based indexer either from the `gitlab-rake` command or the Sidekiq tasks. |
|
||||
| `Bulk request concurrency` | The Bulk request concurrency indicates how many of GitLab's Golang-based indexer processes (or threads) can run in parallel to collect data to subsequently submit to Elasticsearch’s Bulk API. This increases indexing performance, but fills the Elasticsearch bulk requests queue faster. This setting works in tandem with the Maximum bulk request size setting (see above) and needs to accomodate the resource constraints of both the Elasticsearch host(s) and the host(s) running GitLab's Golang-based indexer either from the `gitlab-rake` command or the Sidekiq tasks. |
|
||||
|
||||
### Limiting namespaces and projects
|
||||
|
||||
|
|
|
@ -6,6 +6,9 @@ was deprecated in favor of the
|
|||
[GitLab Plugin](https://wiki.jenkins.io/display/JENKINS/GitLab+Plugin).
|
||||
Please use documentation for the new [Jenkins CI service](jenkins.md).
|
||||
|
||||
NOTE: **Note:**
|
||||
This service was [removed in v13.0](https://gitlab.com/gitlab-org/gitlab/-/issues/1600)
|
||||
|
||||
Integration includes:
|
||||
|
||||
- Trigger Jenkins build after push to repo
|
||||
|
|
|
@ -257,7 +257,6 @@ but commented out to help encourage others to add to it in the future. -->
|
|||
|projects_hipchat_active|counts||
|
||||
|projects_irker_active|counts||
|
||||
|projects_jenkins_active|counts||
|
||||
|projects_jenkins_deprecated_active|counts||
|
||||
|projects_jira_active -|counts||
|
||||
|projects_mattermost_active|counts||
|
||||
|projects_mattermost_slash_commands_active|counts||
|
||||
|
@ -313,7 +312,9 @@ but commented out to help encourage others to add to it in the future. -->
|
|||
|epics|counts||
|
||||
|feature_flags|counts||
|
||||
|geo_nodes|counts||
|
||||
|incident_issues|counts||
|
||||
|incident_issues|counts|monitor|Issues created by the alert bot|
|
||||
|alert_bot_incident_issues|counts|monitor|Issues created by the alert bot|
|
||||
|incident_labeled_issues|counts|monitor|Issues with the incident label|
|
||||
|ldap_group_links|counts||
|
||||
|ldap_keys|counts||
|
||||
|ldap_users|counts||
|
||||
|
|
|
@ -68,7 +68,9 @@ You should then be able to see the **Packages & Registries** section on the left
|
|||
You will need the following:
|
||||
|
||||
- Your GitLab username.
|
||||
- A personal access token. You can generate a [personal access token](../../../user/profile/personal_access_tokens.md) with the scope set to `api` for repository authentication.
|
||||
- A personal access token or deploy token. For repository authentication:
|
||||
- You can generate a [personal access token](../../../user/profile/personal_access_tokens.md) with the scope set to `api`.
|
||||
- You can generate a [deploy token](./../../project/deploy_tokens/index.md) with the scope set to `read_package_registry`, `write_package_registry`, or both.
|
||||
- A suitable name for your source.
|
||||
- Your project ID which can be found on the home page of your project.
|
||||
|
||||
|
@ -83,7 +85,7 @@ You can now add a new source to NuGet with:
|
|||
To add the GitLab NuGet Repository as a source with `nuget`:
|
||||
|
||||
```shell
|
||||
nuget source Add -Name <source_name> -Source "https://gitlab-instance.example.com/api/v4/projects/<your_project_id>/packages/nuget/index.json" -UserName <gitlab_username> -Password <gitlab_personal_access_token>
|
||||
nuget source Add -Name <source_name> -Source "https://gitlab-instance.example.com/api/v4/projects/<your_project_id>/packages/nuget/index.json" -UserName <gitlab_username or deploy_token_username> -Password <gitlab_personal_access_token or deploy_token>
|
||||
```
|
||||
|
||||
Where:
|
||||
|
@ -107,8 +109,8 @@ nuget source Add -Name "GitLab" -Source "https//gitlab.example/api/v4/projects/1
|
|||
- **Location**: `https://gitlab.com/api/v4/projects/<your_project_id>/packages/nuget/index.json`
|
||||
- Replace `<your_project_id>` with your project ID.
|
||||
- If you have a self-managed GitLab installation, replace `gitlab.com` with your domain name.
|
||||
- **Username**: Your GitLab username
|
||||
- **Password**: Your personal access token
|
||||
- **Username**: Your GitLab username or deploy token username
|
||||
- **Password**: Your personal access token or deploy token
|
||||
|
||||
![Visual Studio Adding a NuGet source](img/visual_studio_adding_nuget_source.png)
|
||||
|
||||
|
@ -131,8 +133,8 @@ To add the GitLab NuGet Repository as a source for .NET, create a file named `nu
|
|||
</packageSources>
|
||||
<packageSourceCredentials>
|
||||
<gitlab>
|
||||
<add key="Username" value="<gitlab_username>" />
|
||||
<add key="ClearTextPassword" value="<gitlab_personal_access_token>" />
|
||||
<add key="Username" value="<gitlab_username or deploy_token_username>" />
|
||||
<add key="ClearTextPassword" value="<gitlab_personal_access_token or deploy_token>" />
|
||||
</gitlab>
|
||||
</packageSourceCredentials>
|
||||
</configuration>
|
||||
|
|
|
@ -150,6 +150,8 @@ Package Registry**. Before we do so, we next need to set up authentication.
|
|||
|
||||
## Adding the GitLab PyPi Repository as a source
|
||||
|
||||
### Authenticating with a personal access token
|
||||
|
||||
You will need the following:
|
||||
|
||||
- A personal access token. You can generate a [personal access token](../../../user/profile/personal_access_tokens.md) with the scope set to `api` for repository authentication.
|
||||
|
@ -169,6 +171,27 @@ username = __token__
|
|||
password = <your personal access token>
|
||||
```
|
||||
|
||||
### Authenticating with a deploy token
|
||||
|
||||
You will need the following:
|
||||
|
||||
- A deploy token. You can generate a [deploy token](./../../project/deploy_tokens/index.md) with the `read_package_registry` and/or `write_package_registry` scopes for repository authentication.
|
||||
- A suitable name for your source.
|
||||
- Your project ID which can be found on the home page of your project.
|
||||
|
||||
Edit your `~/.pypirc` file and add the following:
|
||||
|
||||
```ini
|
||||
[distutils]
|
||||
index-servers =
|
||||
gitlab
|
||||
|
||||
[gitlab]
|
||||
repository = https://gitlab.com/api/v4/projects/<project_id>/packages/pypi
|
||||
username = <deploy token username>
|
||||
password = <deploy token>
|
||||
```
|
||||
|
||||
## Uploading packages
|
||||
|
||||
When uploading packages, note that:
|
||||
|
|
|
@ -180,6 +180,7 @@ module API
|
|||
mount ::API::ProjectImport
|
||||
mount ::API::ProjectHooks
|
||||
mount ::API::ProjectMilestones
|
||||
mount ::API::ProjectRepositoryStorageMoves
|
||||
mount ::API::Projects
|
||||
mount ::API::ProjectSnapshots
|
||||
mount ::API::ProjectSnippets
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module API
|
||||
module Entities
|
||||
class ProjectRepositoryStorageMove < Grape::Entity
|
||||
expose :id
|
||||
expose :created_at
|
||||
expose :human_state_name, as: :state
|
||||
expose :source_storage_name
|
||||
expose :destination_storage_name
|
||||
expose :project, using: Entities::ProjectIdentity
|
||||
end
|
||||
end
|
||||
end
|
|
@ -21,7 +21,6 @@ module API
|
|||
expose :milestones, using: Entities::MilestoneWithStats, if: -> (release, _) { release.milestones.present? && can_read_milestone? }
|
||||
expose :commit_path, expose_nil: false
|
||||
expose :tag_path, expose_nil: false
|
||||
expose :evidence_sha, expose_nil: false, if: ->(_, _) { can_download_code? }
|
||||
|
||||
expose :assets do
|
||||
expose :assets_count, as: :count do |release, _|
|
||||
|
@ -32,7 +31,6 @@ module API
|
|||
expose :links, using: Entities::Releases::Link do |release, options|
|
||||
release.links.sorted
|
||||
end
|
||||
expose :evidence_file_path, expose_nil: false, if: ->(_, _) { can_download_code? }
|
||||
end
|
||||
expose :evidences, using: Entities::Releases::Evidence, expose_nil: false, if: ->(_, _) { can_download_code? }
|
||||
expose :_links do
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module API
|
||||
class ProjectRepositoryStorageMoves < Grape::API
|
||||
include PaginationParams
|
||||
|
||||
before { authenticated_as_admin! }
|
||||
|
||||
resource :project_repository_storage_moves do
|
||||
desc 'Get a list of all project repository storage moves' do
|
||||
detail 'This feature was introduced in GitLab 13.0.'
|
||||
success Entities::ProjectRepositoryStorageMove
|
||||
end
|
||||
params do
|
||||
use :pagination
|
||||
end
|
||||
get do
|
||||
storage_moves = ProjectRepositoryStorageMove.with_projects.order_created_at_desc
|
||||
|
||||
present paginate(storage_moves), with: Entities::ProjectRepositoryStorageMove, current_user: current_user
|
||||
end
|
||||
|
||||
desc 'Get a project repository storage move' do
|
||||
detail 'This feature was introduced in GitLab 13.0.'
|
||||
success Entities::ProjectRepositoryStorageMove
|
||||
end
|
||||
get ':id' do
|
||||
storage_move = ProjectRepositoryStorageMove.find(params[:id])
|
||||
|
||||
present storage_move, with: Entities::ProjectRepositoryStorageMove, current_user: current_user
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -104,11 +104,17 @@ module Gitlab
|
|||
|
||||
# This returns a deploy token, not a user since a deploy token does not
|
||||
# belong to a user.
|
||||
#
|
||||
# deploy tokens are accepted with deploy token headers and basic auth headers
|
||||
def deploy_token_from_request
|
||||
return unless route_authentication_setting[:deploy_token_allowed]
|
||||
|
||||
token = current_request.env[DEPLOY_TOKEN_HEADER].presence || parsed_oauth_token
|
||||
|
||||
if has_basic_credentials?(current_request)
|
||||
_, token = user_name_and_password(current_request)
|
||||
end
|
||||
|
||||
deploy_token = DeployToken.active.find_by_token(token)
|
||||
@current_authenticated_deploy_token = deploy_token # rubocop:disable Gitlab/ModuleWithInstanceVariables
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ license_management:
|
|||
refs:
|
||||
- branches
|
||||
variables:
|
||||
- $GITLAB_FEATURES =~ /\blicense_management\b/
|
||||
- $GITLAB_FEATURES =~ /\blicense_scanning\b/
|
||||
except:
|
||||
refs:
|
||||
- master
|
||||
|
|
|
@ -40,6 +40,8 @@ module Gitlab
|
|||
end
|
||||
|
||||
if order_list.count > 2
|
||||
# Keep in mind an order clause for primary key is added if one is not present
|
||||
# lib/gitlab/graphql/pagination/keyset/connection.rb:97
|
||||
raise ArgumentError.new('A maximum of 2 ordering fields are allowed')
|
||||
end
|
||||
|
||||
|
|
|
@ -63,6 +63,8 @@ module Gitlab
|
|||
# rubocop: disable Metrics/AbcSize
|
||||
# rubocop: disable CodeReuse/ActiveRecord
|
||||
def system_usage_data
|
||||
alert_bot_incident_count = count(::Issue.authored(::User.alert_bot))
|
||||
|
||||
{
|
||||
counts: {
|
||||
assignee_lists: count(List.assignee),
|
||||
|
@ -112,7 +114,9 @@ module Gitlab
|
|||
issues_with_associated_zoom_link: count(ZoomMeeting.added_to_issue),
|
||||
issues_using_zoom_quick_actions: distinct_count(ZoomMeeting, :issue_id),
|
||||
issues_with_embedded_grafana_charts_approx: grafana_embed_usage_data,
|
||||
incident_issues: count(::Issue.authored(::User.alert_bot)),
|
||||
incident_issues: alert_bot_incident_count,
|
||||
alert_bot_incident_issues: alert_bot_incident_count,
|
||||
incident_labeled_issues: count(::Issue.with_label_attributes(IncidentManagement::CreateIssueService::INCIDENT_LABEL)),
|
||||
keys: count(Key),
|
||||
label_lists: count(List.label),
|
||||
lfs_objects: count(LfsObject),
|
||||
|
|
|
@ -1120,7 +1120,7 @@ msgstr ""
|
|||
msgid "Active"
|
||||
msgstr ""
|
||||
|
||||
msgid "Active %{type} Tokens (%{token_length})"
|
||||
msgid "Active %{type} (%{token_length})"
|
||||
msgstr ""
|
||||
|
||||
msgid "Active Sessions"
|
||||
|
@ -1173,7 +1173,7 @@ msgstr ""
|
|||
msgid "Add Zoom meeting"
|
||||
msgstr ""
|
||||
|
||||
msgid "Add a %{type} token"
|
||||
msgid "Add a %{type}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Add a GPG key"
|
||||
|
@ -1224,6 +1224,9 @@ msgstr ""
|
|||
msgid "Add an existing issue"
|
||||
msgstr ""
|
||||
|
||||
msgid "Add an impersonation token"
|
||||
msgstr ""
|
||||
|
||||
msgid "Add an issue"
|
||||
msgstr ""
|
||||
|
||||
|
@ -2659,7 +2662,7 @@ msgstr ""
|
|||
msgid "Are you sure you want to reset the health check token?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Are you sure you want to revoke this %{type} Token? This action cannot be undone."
|
||||
msgid "Are you sure you want to revoke this %{type}? This action cannot be undone."
|
||||
msgstr ""
|
||||
|
||||
msgid "Are you sure you want to revoke this nickname?"
|
||||
|
@ -4358,6 +4361,9 @@ msgstr ""
|
|||
msgid "Close"
|
||||
msgstr ""
|
||||
|
||||
msgid "Close %{display_issuable_type}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Close %{tabname}"
|
||||
msgstr ""
|
||||
|
||||
|
@ -6042,6 +6048,9 @@ msgstr ""
|
|||
msgid "Copy %{proxy_url}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Copy %{type}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Copy Account ID to clipboard"
|
||||
msgstr ""
|
||||
|
||||
|
@ -6087,9 +6096,6 @@ msgstr ""
|
|||
msgid "Copy file path"
|
||||
msgstr ""
|
||||
|
||||
msgid "Copy impersonation token"
|
||||
msgstr ""
|
||||
|
||||
msgid "Copy key"
|
||||
msgstr ""
|
||||
|
||||
|
@ -6105,9 +6111,6 @@ msgstr ""
|
|||
msgid "Copy link to chart"
|
||||
msgstr ""
|
||||
|
||||
msgid "Copy personal access token"
|
||||
msgstr ""
|
||||
|
||||
msgid "Copy reference"
|
||||
msgstr ""
|
||||
|
||||
|
@ -6204,7 +6207,7 @@ msgstr ""
|
|||
msgid "Create %{environment}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Create %{type} token"
|
||||
msgid "Create %{type}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Create New Directory"
|
||||
|
@ -8147,6 +8150,9 @@ msgstr ""
|
|||
msgid "Enter the merge request title"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enter the name of your application, and we'll return a unique %{type}."
|
||||
msgstr ""
|
||||
|
||||
msgid "Enter your password to approve"
|
||||
msgstr ""
|
||||
|
||||
|
@ -11337,6 +11343,9 @@ msgstr ""
|
|||
msgid "ImageViewerDimensions|W"
|
||||
msgstr ""
|
||||
|
||||
msgid "Impersonation Tokens"
|
||||
msgstr ""
|
||||
|
||||
msgid "Impersonation has been disabled"
|
||||
msgstr ""
|
||||
|
||||
|
@ -11954,6 +11963,18 @@ msgstr ""
|
|||
msgid "It's you"
|
||||
msgstr ""
|
||||
|
||||
msgid "Iterations"
|
||||
msgstr ""
|
||||
|
||||
msgid "Iteration|Dates cannot overlap with other existing Iterations"
|
||||
msgstr ""
|
||||
|
||||
msgid "Iteration|cannot be in the past"
|
||||
msgstr ""
|
||||
|
||||
msgid "Iteration|cannot be more than 500 years in the future"
|
||||
msgstr ""
|
||||
|
||||
msgid "Jaeger URL"
|
||||
msgstr ""
|
||||
|
||||
|
@ -13904,6 +13925,9 @@ msgid_plural "New Issues"
|
|||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "New Iteration"
|
||||
msgstr ""
|
||||
|
||||
msgid "New Jira import"
|
||||
msgstr ""
|
||||
|
||||
|
@ -15181,9 +15205,6 @@ msgstr ""
|
|||
msgid "Pick a name"
|
||||
msgstr ""
|
||||
|
||||
msgid "Pick a name for the application, and we'll give you a unique %{type} token."
|
||||
msgstr ""
|
||||
|
||||
msgid "Pin code"
|
||||
msgstr ""
|
||||
|
||||
|
@ -15982,9 +16003,6 @@ msgstr ""
|
|||
msgid "Profiles|Give your individual key a title"
|
||||
msgstr ""
|
||||
|
||||
msgid "Profiles|Impersonation"
|
||||
msgstr ""
|
||||
|
||||
msgid "Profiles|Include private contributions on my profile"
|
||||
msgstr ""
|
||||
|
||||
|
@ -16030,9 +16048,6 @@ msgstr ""
|
|||
msgid "Profiles|Path"
|
||||
msgstr ""
|
||||
|
||||
msgid "Profiles|Personal Access"
|
||||
msgstr ""
|
||||
|
||||
msgid "Profiles|Position and size your new avatar"
|
||||
msgstr ""
|
||||
|
||||
|
@ -16177,12 +16192,6 @@ msgstr ""
|
|||
msgid "Profiles|e.g. My MacBook key"
|
||||
msgstr ""
|
||||
|
||||
msgid "Profiles|impersonation"
|
||||
msgstr ""
|
||||
|
||||
msgid "Profiles|personal access"
|
||||
msgstr ""
|
||||
|
||||
msgid "Profiles|username"
|
||||
msgstr ""
|
||||
|
||||
|
@ -16234,6 +16243,9 @@ msgstr ""
|
|||
msgid "Project '%{project_name}' will be deleted on %{date}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Project Access Tokens"
|
||||
msgstr ""
|
||||
|
||||
msgid "Project Audit Events"
|
||||
msgstr ""
|
||||
|
||||
|
@ -17711,6 +17723,9 @@ msgstr ""
|
|||
msgid "Reopen"
|
||||
msgstr ""
|
||||
|
||||
msgid "Reopen %{display_issuable_type}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Reopen epic"
|
||||
msgstr ""
|
||||
|
||||
|
@ -17759,9 +17774,15 @@ msgstr ""
|
|||
msgid "Repo by URL"
|
||||
msgstr ""
|
||||
|
||||
msgid "Report %{display_issuable_type} that are abusive, inappropriate or spam."
|
||||
msgstr ""
|
||||
|
||||
msgid "Report Type: %{report_type}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Report abuse"
|
||||
msgstr ""
|
||||
|
||||
msgid "Report abuse to admin"
|
||||
msgstr ""
|
||||
|
||||
|
@ -21938,6 +21959,9 @@ msgstr ""
|
|||
msgid "This project does not have billing enabled. To create a cluster, <a href=%{linkToBilling} target=\"_blank\" rel=\"noopener noreferrer\">enable billing <i class=\"fa fa-external-link\" aria-hidden=\"true\"></i></a> and try again."
|
||||
msgstr ""
|
||||
|
||||
msgid "This project has no active access tokens."
|
||||
msgstr ""
|
||||
|
||||
msgid "This project is archived and cannot be commented on."
|
||||
msgstr ""
|
||||
|
||||
|
@ -21983,7 +22007,7 @@ msgstr ""
|
|||
msgid "This user cannot be unlocked manually from GitLab"
|
||||
msgstr ""
|
||||
|
||||
msgid "This user has no active %{type} Tokens."
|
||||
msgid "This user has no active %{type}."
|
||||
msgstr ""
|
||||
|
||||
msgid "This user has no identities"
|
||||
|
@ -22488,6 +22512,9 @@ msgstr ""
|
|||
msgid "Toggle commit list"
|
||||
msgstr ""
|
||||
|
||||
msgid "Toggle dropdown"
|
||||
msgstr ""
|
||||
|
||||
msgid "Toggle emoji award"
|
||||
msgstr ""
|
||||
|
||||
|
@ -24447,6 +24474,9 @@ msgstr ""
|
|||
msgid "You can also upload existing files from your computer using the instructions below."
|
||||
msgstr ""
|
||||
|
||||
msgid "You can also use project access tokens to authenticate against Git over HTTP."
|
||||
msgstr ""
|
||||
|
||||
msgid "You can always edit this later"
|
||||
msgstr ""
|
||||
|
||||
|
@ -24474,6 +24504,9 @@ msgstr ""
|
|||
msgid "You can filter by 'days to merge' by clicking on the columns in the chart."
|
||||
msgstr ""
|
||||
|
||||
msgid "You can generate an access token scoped to this project for each application to use the GitLab API."
|
||||
msgstr ""
|
||||
|
||||
msgid "You can get started by cloning the repository or start adding files to it with one of the following options."
|
||||
msgstr ""
|
||||
|
||||
|
@ -24753,6 +24786,9 @@ msgstr ""
|
|||
msgid "You will receive notifications only for comments in which you were @mentioned"
|
||||
msgstr ""
|
||||
|
||||
msgid "You won't be able to create new projects because you have reached your project limit."
|
||||
msgstr ""
|
||||
|
||||
msgid "You won't be able to pull or push project code via %{protocol} until you %{set_password_link} on your account"
|
||||
msgstr ""
|
||||
|
||||
|
@ -24846,9 +24882,6 @@ msgstr ""
|
|||
msgid "Your Groups"
|
||||
msgstr ""
|
||||
|
||||
msgid "Your New Personal Access Token"
|
||||
msgstr ""
|
||||
|
||||
msgid "Your Personal Access Tokens will expire in %{days_to_expire} days or less"
|
||||
msgstr ""
|
||||
|
||||
|
@ -24957,6 +24990,9 @@ msgstr ""
|
|||
msgid "Your name"
|
||||
msgstr ""
|
||||
|
||||
msgid "Your new %{type}"
|
||||
msgstr ""
|
||||
|
||||
msgid "Your new SCIM token"
|
||||
msgstr ""
|
||||
|
||||
|
@ -25514,6 +25550,12 @@ msgstr ""
|
|||
msgid "image diff"
|
||||
msgstr ""
|
||||
|
||||
msgid "impersonation token"
|
||||
msgstr ""
|
||||
|
||||
msgid "impersonation tokens"
|
||||
msgstr ""
|
||||
|
||||
msgid "import flow"
|
||||
msgstr ""
|
||||
|
||||
|
@ -26053,6 +26095,12 @@ msgstr ""
|
|||
msgid "per day"
|
||||
msgstr ""
|
||||
|
||||
msgid "personal access token"
|
||||
msgstr ""
|
||||
|
||||
msgid "personal access tokens"
|
||||
msgstr ""
|
||||
|
||||
msgid "pipeline"
|
||||
msgstr ""
|
||||
|
||||
|
@ -26081,6 +26129,12 @@ msgid_plural "projects"
|
|||
msgstr[0] ""
|
||||
msgstr[1] ""
|
||||
|
||||
msgid "project access token"
|
||||
msgstr ""
|
||||
|
||||
msgid "project access tokens"
|
||||
msgstr ""
|
||||
|
||||
msgid "project avatar"
|
||||
msgstr ""
|
||||
|
||||
|
|
|
@ -6,9 +6,9 @@ module QA
|
|||
module Page
|
||||
module Profile
|
||||
class PersonalAccessTokens < Page::Base
|
||||
view 'app/views/shared/_personal_access_tokens_form.html.haml' do
|
||||
view 'app/views/shared/access_tokens/_form.html.haml' do
|
||||
element :expiry_date_field
|
||||
element :personal_access_token_name_field
|
||||
element :access_token_name_field
|
||||
element :create_token_button
|
||||
end
|
||||
|
||||
|
@ -16,15 +16,15 @@ module QA
|
|||
element :api_radio, 'qa-#{scope}-radio' # rubocop:disable QA/ElementWithPattern, Lint/InterpolationCheck
|
||||
end
|
||||
|
||||
view 'app/views/shared/_personal_access_tokens_created_container.html.haml' do
|
||||
element :created_personal_access_token
|
||||
view 'app/views/shared/access_tokens/_created_container.html.haml' do
|
||||
element :created_access_token
|
||||
end
|
||||
view 'app/views/shared/_personal_access_tokens_table.html.haml' do
|
||||
view 'app/views/shared/access_tokens/_table.html.haml' do
|
||||
element :revoke_button
|
||||
end
|
||||
|
||||
def fill_token_name(name)
|
||||
fill_element(:personal_access_token_name_field, name)
|
||||
fill_element(:access_token_name_field, name)
|
||||
end
|
||||
|
||||
def check_api
|
||||
|
@ -36,7 +36,7 @@ module QA
|
|||
end
|
||||
|
||||
def created_access_token
|
||||
find_element(:created_personal_access_token, wait: 30).value
|
||||
find_element(:created_access_token, wait: 30).value
|
||||
end
|
||||
|
||||
def fill_expiry_date(date)
|
||||
|
|
|
@ -1,8 +1,14 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
FactoryBot.define do
|
||||
sequence(:sequential_date) do |n|
|
||||
n.days.from_now
|
||||
end
|
||||
|
||||
factory :iteration do
|
||||
title
|
||||
start_date { generate(:sequential_date) }
|
||||
due_date { generate(:sequential_date) }
|
||||
|
||||
transient do
|
||||
project { nil }
|
||||
|
@ -12,17 +18,22 @@ FactoryBot.define do
|
|||
resource_parent { nil }
|
||||
end
|
||||
|
||||
trait :active do
|
||||
state { Iteration::STATE_ID_MAP[:active] }
|
||||
trait :upcoming do
|
||||
state_enum { Iteration::STATE_ENUM_MAP[:upcoming] }
|
||||
end
|
||||
|
||||
trait :started do
|
||||
state_enum { Iteration::STATE_ENUM_MAP[:started] }
|
||||
end
|
||||
|
||||
trait :closed do
|
||||
state { Iteration::STATE_ID_MAP[:closed] }
|
||||
state_enum { Iteration::STATE_ENUM_MAP[:closed] }
|
||||
end
|
||||
|
||||
trait :with_dates do
|
||||
start_date { Date.new(2000, 1, 1) }
|
||||
due_date { Date.new(2000, 1, 30) }
|
||||
trait(:skip_future_date_validation) do
|
||||
after(:stub, :build) do |iteration|
|
||||
iteration.skip_future_date_validation = true
|
||||
end
|
||||
end
|
||||
|
||||
after(:build, :stub) do |iteration, evaluator|
|
||||
|
@ -42,7 +53,8 @@ FactoryBot.define do
|
|||
end
|
||||
end
|
||||
|
||||
factory :active_iteration, traits: [:active]
|
||||
factory :upcoming_iteration, traits: [:upcoming]
|
||||
factory :started_iteration, traits: [:started]
|
||||
factory :closed_iteration, traits: [:closed]
|
||||
end
|
||||
end
|
||||
|
|
|
@ -40,6 +40,16 @@ FactoryBot.define do
|
|||
create_list(:zoom_meeting, 2, project: projects[0], issue: projects[0].issues[2], issue_status: :removed)
|
||||
create(:sentry_issue, issue: projects[0].issues[0])
|
||||
|
||||
# Incident Labeled Issues
|
||||
incident_label_attrs = IncidentManagement::CreateIssueService::INCIDENT_LABEL
|
||||
incident_label = create(:label, project: projects[0], **incident_label_attrs)
|
||||
create(:labeled_issue, project: projects[0], labels: [incident_label])
|
||||
incident_group = create(:group)
|
||||
incident_label_scoped_to_project = create(:label, project: projects[1], **incident_label_attrs)
|
||||
incident_label_scoped_to_group = create(:group_label, group: incident_group, **incident_label_attrs)
|
||||
create(:labeled_issue, project: projects[1], labels: [incident_label_scoped_to_project])
|
||||
create(:labeled_issue, project: projects[1], labels: [incident_label_scoped_to_group])
|
||||
|
||||
# Enabled clusters
|
||||
gcp_cluster = create(:cluster_provider_gcp, :created).cluster
|
||||
create(:cluster_provider_aws, :created)
|
||||
|
|
|
@ -70,7 +70,7 @@ describe 'Admin > Users > Impersonation Tokens', :js do
|
|||
accept_confirm { click_on "Revoke" }
|
||||
|
||||
expect(page).to have_selector(".settings-message")
|
||||
expect(no_personal_access_tokens_message).to have_text("This user has no active Impersonation Tokens.")
|
||||
expect(no_personal_access_tokens_message).to have_text("This user has no active impersonation tokens.")
|
||||
end
|
||||
|
||||
it "removes expired tokens from 'active' section" do
|
||||
|
@ -79,7 +79,7 @@ describe 'Admin > Users > Impersonation Tokens', :js do
|
|||
visit admin_user_impersonation_tokens_path(user_id: user.username)
|
||||
|
||||
expect(page).to have_selector(".settings-message")
|
||||
expect(no_personal_access_tokens_message).to have_text("This user has no active Impersonation Tokens.")
|
||||
expect(no_personal_access_tokens_message).to have_text("This user has no active impersonation tokens.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -86,7 +86,7 @@ describe 'Profile > Personal Access Tokens', :js do
|
|||
accept_confirm { click_on "Revoke" }
|
||||
|
||||
expect(page).to have_selector(".settings-message")
|
||||
expect(no_personal_access_tokens_message).to have_text("This user has no active Personal Access Tokens.")
|
||||
expect(no_personal_access_tokens_message).to have_text("This user has no active personal access tokens.")
|
||||
end
|
||||
|
||||
it "removes expired tokens from 'active' section" do
|
||||
|
@ -94,7 +94,7 @@ describe 'Profile > Personal Access Tokens', :js do
|
|||
visit profile_personal_access_tokens_path
|
||||
|
||||
expect(page).to have_selector(".settings-message")
|
||||
expect(no_personal_access_tokens_message).to have_text("This user has no active Personal Access Tokens.")
|
||||
expect(no_personal_access_tokens_message).to have_text("This user has no active personal access tokens.")
|
||||
end
|
||||
|
||||
context "when revocation fails" do
|
||||
|
|
|
@ -34,7 +34,7 @@ describe 'Project members list' do
|
|||
expect(second_row).to be_blank
|
||||
end
|
||||
|
||||
it 'update user acess level', :js do
|
||||
it 'update user access level', :js do
|
||||
project.add_developer(user2)
|
||||
|
||||
visit_members_page
|
||||
|
@ -86,6 +86,23 @@ describe 'Project members list' do
|
|||
end
|
||||
end
|
||||
|
||||
context 'project bots' do
|
||||
let(:project_bot) { create(:user, :project_bot, name: 'project_bot') }
|
||||
|
||||
before do
|
||||
project.add_maintainer(project_bot)
|
||||
end
|
||||
|
||||
it 'does not show form used to change roles and "Expiration date" or the remove user button' do
|
||||
project_member = project.project_members.find_by(user_id: project_bot.id)
|
||||
|
||||
visit_members_page
|
||||
|
||||
expect(page).not_to have_selector("#edit_project_member_#{project_member.id}")
|
||||
expect(page).not_to have_selector("#project_member_#{project_member.id} .btn-remove")
|
||||
end
|
||||
end
|
||||
|
||||
def add_user(id, role)
|
||||
page.within ".invite-users-form" do
|
||||
select2(id, from: "#user_ids", multiple: true)
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
describe 'Project > Settings > Access Tokens', :js do
|
||||
let_it_be(:user) { create(:user) }
|
||||
let_it_be(:bot_user) { create(:user, :project_bot) }
|
||||
let_it_be(:project) { create(:project) }
|
||||
|
||||
before_all do
|
||||
project.add_maintainer(user)
|
||||
end
|
||||
|
||||
before do
|
||||
sign_in(user)
|
||||
end
|
||||
|
||||
def create_project_access_token
|
||||
project.add_maintainer(bot_user)
|
||||
|
||||
create(:personal_access_token, user: bot_user)
|
||||
end
|
||||
|
||||
def active_project_access_tokens
|
||||
find('.table.active-tokens')
|
||||
end
|
||||
|
||||
def no_project_access_tokens_message
|
||||
find('.settings-message')
|
||||
end
|
||||
|
||||
def created_project_access_token
|
||||
find('#created-personal-access-token').value
|
||||
end
|
||||
|
||||
describe 'token creation' do
|
||||
it 'allows creation of a project access token' do
|
||||
name = 'My project access token'
|
||||
|
||||
visit project_settings_access_tokens_path(project)
|
||||
fill_in 'Name', with: name
|
||||
|
||||
# Set date to 1st of next month
|
||||
find_field('Expires at').click
|
||||
find('.pika-next').click
|
||||
click_on '1'
|
||||
|
||||
# Scopes
|
||||
check 'api'
|
||||
check 'read_api'
|
||||
|
||||
click_on 'Create project access token'
|
||||
|
||||
expect(active_project_access_tokens).to have_text(name)
|
||||
expect(active_project_access_tokens).to have_text('In')
|
||||
expect(active_project_access_tokens).to have_text('api')
|
||||
expect(active_project_access_tokens).to have_text('read_api')
|
||||
expect(created_project_access_token).not_to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
describe 'active tokens' do
|
||||
let!(:project_access_token) { create_project_access_token }
|
||||
|
||||
it 'shows active project access tokens' do
|
||||
visit project_settings_access_tokens_path(project)
|
||||
|
||||
expect(active_project_access_tokens).to have_text(project_access_token.name)
|
||||
end
|
||||
end
|
||||
|
||||
describe 'inactive tokens' do
|
||||
let!(:project_access_token) { create_project_access_token }
|
||||
|
||||
no_active_tokens_text = 'This project has no active access tokens.'
|
||||
|
||||
it 'allows revocation of an active token' do
|
||||
visit project_settings_access_tokens_path(project)
|
||||
accept_confirm { click_on 'Revoke' }
|
||||
|
||||
expect(page).to have_selector('.settings-message')
|
||||
expect(no_project_access_tokens_message).to have_text(no_active_tokens_text)
|
||||
end
|
||||
|
||||
it 'removes expired tokens from active section' do
|
||||
project_access_token.update(expires_at: 5.days.ago)
|
||||
visit project_settings_access_tokens_path(project)
|
||||
|
||||
expect(page).to have_selector('.settings-message')
|
||||
expect(no_project_access_tokens_message).to have_text(no_active_tokens_text)
|
||||
end
|
||||
end
|
||||
end
|
20
spec/fixtures/api/schemas/public_api/v4/project_repository_storage_move.json
vendored
Normal file
20
spec/fixtures/api/schemas/public_api/v4/project_repository_storage_move.json
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"id",
|
||||
"created_at",
|
||||
"state",
|
||||
"source_storage_name",
|
||||
"destination_storage_name",
|
||||
"project"
|
||||
],
|
||||
"properties" : {
|
||||
"id": { "type": "integer" },
|
||||
"created_at": { "type": "date" },
|
||||
"state": { "type": "string" },
|
||||
"source_storage_name": { "type": "string" },
|
||||
"destination_storage_name": { "type": "string" },
|
||||
"project": { "type": "object" }
|
||||
},
|
||||
"additionalProperties": false
|
||||
}
|
6
spec/fixtures/api/schemas/public_api/v4/project_repository_storage_moves.json
vendored
Normal file
6
spec/fixtures/api/schemas/public_api/v4/project_repository_storage_moves.json
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "./project_repository_storage_move.json"
|
||||
}
|
||||
}
|
|
@ -7,7 +7,7 @@ development:
|
|||
host: localhost
|
||||
port: 26380 # point to sentinel, not to redis port
|
||||
-
|
||||
host: slave2
|
||||
host: replica2
|
||||
port: 26380 # point to sentinel, not to redis port
|
||||
test:
|
||||
url: redis://:mynewpassword@localhost:6380/10
|
||||
|
@ -16,14 +16,14 @@ test:
|
|||
host: localhost
|
||||
port: 26380 # point to sentinel, not to redis port
|
||||
-
|
||||
host: slave2
|
||||
host: replica2
|
||||
port: 26380 # point to sentinel, not to redis port
|
||||
production:
|
||||
url: redis://:mynewpassword@localhost:6380/10
|
||||
sentinels:
|
||||
-
|
||||
host: slave1
|
||||
host: replica1
|
||||
port: 26380 # point to sentinel, not to redis port
|
||||
-
|
||||
host: slave2
|
||||
host: replica2
|
||||
port: 26380 # point to sentinel, not to redis port
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue