Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
f67f18d949
commit
4f288fdc92
73 changed files with 2433 additions and 514 deletions
|
@ -53,7 +53,7 @@ export default {
|
|||
@click="updateViewer"
|
||||
/>
|
||||
</div>
|
||||
<div class="prepend-top-5 ide-review-sub-header">
|
||||
<div class="gl-mt-2 ide-review-sub-header">
|
||||
<template v-if="showLatestChangesText">
|
||||
{{ __('Latest changes') }}
|
||||
</template>
|
||||
|
|
|
@ -339,7 +339,6 @@ export default {
|
|||
<h5
|
||||
ref="graphTitle"
|
||||
class="prometheus-graph-title gl-font-lg font-weight-bold text-truncate gl-mr-3"
|
||||
tabindex="0"
|
||||
>
|
||||
{{ title }}
|
||||
</h5>
|
||||
|
|
|
@ -52,7 +52,7 @@ export default {
|
|||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="showPanels" ref="graph-group" class="card prometheus-panel" tabindex="0">
|
||||
<div v-if="showPanels" ref="graph-group" class="card prometheus-panel">
|
||||
<div class="card-header d-flex align-items-center">
|
||||
<h4 class="flex-grow-1">{{ name }}</h4>
|
||||
<a
|
||||
|
|
|
@ -36,7 +36,7 @@ export default {
|
|||
};
|
||||
</script>
|
||||
<template>
|
||||
<div class="report-block-list-issue-description prepend-top-5 gl-mb-2">
|
||||
<div class="report-block-list-issue-description gl-mt-2 gl-mb-2">
|
||||
<div ref="accessibility-issue-description" class="report-block-list-issue-description-text">
|
||||
<div v-if="isNew" ref="accessibility-issue-is-new-badge" class="badge badge-danger gl-mr-2">
|
||||
{{ s__('AccessibilityReport|New') }}
|
||||
|
|
|
@ -91,6 +91,11 @@ export default {
|
|||
required: false,
|
||||
default: undefined,
|
||||
},
|
||||
shouldEmitToggleEvent: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
|
@ -157,6 +162,9 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
toggleCollapsed() {
|
||||
if (this.shouldEmitToggleEvent) {
|
||||
this.$emit('toggleEvent');
|
||||
}
|
||||
this.isCollapsed = !this.isCollapsed;
|
||||
},
|
||||
},
|
||||
|
|
|
@ -25,7 +25,7 @@ export default {
|
|||
};
|
||||
</script>
|
||||
<template>
|
||||
<div class="report-block-list-issue-description prepend-top-5 gl-mb-2">
|
||||
<div class="report-block-list-issue-description gl-mt-2 gl-mb-2">
|
||||
<div class="report-block-list-issue-description-text" data-testid="test-issue-body-description">
|
||||
<button
|
||||
type="button"
|
||||
|
|
|
@ -88,7 +88,7 @@ export default {
|
|||
>
|
||||
</span>
|
||||
</strong>
|
||||
<span class="diff-changed-file-path prepend-top-5">
|
||||
<span class="diff-changed-file-path gl-mt-2">
|
||||
<span
|
||||
v-for="(char, charIndex) in pathWithEllipsis.split('')"
|
||||
:key="charIndex + char"
|
||||
|
|
|
@ -11,30 +11,30 @@
|
|||
// like a table or typography then make changes in the framework/ directory.
|
||||
// If you need to add unique style that should affect only one page - use pages/
|
||||
// directory.
|
||||
@import "@gitlab/at.js/dist/css/jquery.atwho";
|
||||
@import "dropzone/dist/basic";
|
||||
@import "select2/select2";
|
||||
@import '@gitlab/at.js/dist/css/jquery.atwho';
|
||||
@import 'dropzone/dist/basic';
|
||||
@import 'select2/select2';
|
||||
|
||||
// GitLab UI framework
|
||||
@import "framework";
|
||||
@import 'framework';
|
||||
|
||||
// Font icons
|
||||
@import "font-awesome";
|
||||
@import 'font-awesome';
|
||||
|
||||
// Page specific styles (issues, projects etc):
|
||||
@import "pages/**/*";
|
||||
@import 'pages/**/*';
|
||||
|
||||
// Component specific styles, will be moved to gitlab-ui
|
||||
@import "components/**/*";
|
||||
@import 'components/**/*';
|
||||
|
||||
// Vendors specific styles
|
||||
@import "vendors/**/*";
|
||||
@import 'vendors/**/*';
|
||||
|
||||
// Styles for JS behaviors.
|
||||
@import "behaviors";
|
||||
@import 'behaviors';
|
||||
|
||||
// EE-only stylesheets
|
||||
@import "application_ee";
|
||||
@import 'application_ee';
|
||||
|
||||
// CSS util classes
|
||||
/**
|
||||
|
@ -42,12 +42,12 @@
|
|||
Please check https://unpkg.com/browse/@gitlab/ui/src/scss/utilities.scss
|
||||
to see the available utility classes.
|
||||
**/
|
||||
@import "utilities";
|
||||
@import 'utilities';
|
||||
|
||||
// Gitlab UI util classes
|
||||
@import "@gitlab/ui/src/scss/utilities";
|
||||
@import '@gitlab/ui/src/scss/utilities';
|
||||
|
||||
/* print styles */
|
||||
@media print {
|
||||
@import "print";
|
||||
@import 'print';
|
||||
}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
@import "./themes/dark";
|
||||
@import './themes/dark';
|
||||
|
||||
@import "./application";
|
||||
@import './application';
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
.js-toggler-container {
|
||||
.turn-on { display: block; }
|
||||
.turn-off { display: none; }
|
||||
|
||||
&.on {
|
||||
.turn-on { display: none; }
|
||||
.turn-off { display: block; }
|
||||
|
@ -23,6 +24,6 @@
|
|||
}
|
||||
|
||||
// Hide element if Vue is still working on rendering it fully.
|
||||
[v-cloak="true"] {
|
||||
[v-cloak='true'] {
|
||||
display: none !important;
|
||||
}
|
||||
|
|
13
app/assets/stylesheets/bootstrap_migration.scss
vendored
13
app/assets/stylesheets/bootstrap_migration.scss
vendored
|
@ -23,7 +23,7 @@ body,
|
|||
// Override default font size used in non-csslab UI
|
||||
// Use rem to keep default font-size at 14px on body so 1rem still
|
||||
// fits 8px grid, but also allow users to change browser font size
|
||||
font-size: .875rem;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
legend {
|
||||
|
@ -32,11 +32,12 @@ legend {
|
|||
}
|
||||
|
||||
button,
|
||||
html [type="button"],
|
||||
[type="reset"],
|
||||
[type="submit"],
|
||||
[role="button"] {
|
||||
html [type='button'],
|
||||
[type='reset'],
|
||||
[type='submit'],
|
||||
[role='button'] {
|
||||
// Override bootstrap reboot
|
||||
/* stylelint-disable-next-line property-no-vendor-prefix */
|
||||
-webkit-appearance: inherit;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
@ -77,7 +78,7 @@ h5,
|
|||
font-size: $gl-font-size;
|
||||
}
|
||||
|
||||
input[type="file"] {
|
||||
input[type='file'] {
|
||||
// Bootstrap 4 file input height is taller by default
|
||||
// which makes them look ugly
|
||||
line-height: 1;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
* {
|
||||
/* stylelint-disable property-no-vendor-prefix */
|
||||
-o-transition: none !important;
|
||||
-moz-transition: none !important;
|
||||
-ms-transition: none !important;
|
||||
|
@ -9,6 +10,7 @@
|
|||
-o-animation: none !important;
|
||||
-ms-animation: none !important;
|
||||
animation: none !important;
|
||||
/* stylelint-enable property-no-vendor-prefix */
|
||||
}
|
||||
|
||||
// Disable sticky changes bar for tests
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -15,9 +15,9 @@ $header-color: #456;
|
|||
body {
|
||||
color: $body-color;
|
||||
text-align: center;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
margin: auto;
|
||||
font-size: .875rem;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
h1 {
|
||||
|
@ -105,7 +105,6 @@ a {
|
|||
}
|
||||
|
||||
@include media-breakpoint-up(sm) {
|
||||
|
||||
li {
|
||||
display: inline-block;
|
||||
padding-bottom: 0;
|
||||
|
|
|
@ -68,6 +68,6 @@
|
|||
@import 'framework/read_more';
|
||||
@import 'framework/flex_grid';
|
||||
@import 'framework/system_messages';
|
||||
@import "framework/spinner";
|
||||
@import 'framework/spinner';
|
||||
@import 'framework/card';
|
||||
@import 'framework/editor-lite';
|
||||
|
|
|
@ -396,7 +396,6 @@ img.emoji {
|
|||
🚨 Do not use these classes — they are deprecated and being removed. 🚨
|
||||
See https://gitlab.com/gitlab-org/gitlab/-/issues/217418 for more details.
|
||||
**/
|
||||
.prepend-top-5 { margin-top: 5px; }
|
||||
.prepend-top-10 { margin-top: 10px; }
|
||||
.prepend-top-15 { margin-top: 15px; }
|
||||
.prepend-top-20 { margin-top: 20px; }
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* https://github.com/MozMorris/tomorrow-pygments */
|
||||
|
||||
@import "../common";
|
||||
@import '../common';
|
||||
|
||||
/*
|
||||
* Dark syntax colors
|
||||
|
@ -223,11 +223,20 @@ $dark-il: #de935f;
|
|||
.cs { color: $dark-cs; } /* Comment.Special */
|
||||
.gd { color: $dark-gd; } /* Generic.Deleted */
|
||||
.ge { font-style: italic; } /* Generic.Emph */
|
||||
.gh { color: $dark-gh; font-weight: $gl-font-weight-bold; } /* Generic.Heading */
|
||||
.gh { /* Generic.Heading */
|
||||
color: $dark-gh;
|
||||
font-weight: $gl-font-weight-bold;
|
||||
}
|
||||
.gi { color: $dark-gi; } /* Generic.Inserted */
|
||||
.gp { color: $dark-gp; font-weight: $gl-font-weight-bold; } /* Generic.Prompt */
|
||||
.gp { /* Generic.Prompt */
|
||||
color: $dark-gp;
|
||||
font-weight: $gl-font-weight-bold;
|
||||
}
|
||||
.gs { font-weight: $gl-font-weight-bold; } /* Generic.Strong */
|
||||
.gu { color: $dark-gu; font-weight: $gl-font-weight-bold; } /* Generic.Subheading */
|
||||
.gu { /* Generic.Subheading */
|
||||
color: $dark-gu;
|
||||
font-weight: $gl-font-weight-bold;
|
||||
}
|
||||
.kc { color: $dark-kc; } /* Keyword.Constant */
|
||||
.kd { color: $dark-kd; } /* Keyword.Declaration */
|
||||
.kn { color: $dark-kn; } /* Keyword.Namespace */
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* https://github.com/richleland/pygments-css/blob/master/monokai.css */
|
||||
|
||||
@import "../common";
|
||||
@import '../common';
|
||||
|
||||
/*
|
||||
* Monokai Colors
|
||||
|
@ -211,7 +211,10 @@ $monokai-gi: #a6e22e;
|
|||
|
||||
.hll { background-color: $monokai-hll; }
|
||||
.c { color: $monokai-c; } /* Comment */
|
||||
.err { color: $monokai-err-color; background-color: $monokai-err-bg; } /* Error */
|
||||
.err { /* Error */
|
||||
color: $monokai-err-color;
|
||||
background-color: $monokai-err-bg;
|
||||
}
|
||||
.k { color: $monokai-k; } /* Keyword */
|
||||
.l { color: $monokai-l; } /* Literal */
|
||||
.n { color: $monokai-n; } /* Name */
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* None Syntax Colors
|
||||
*/
|
||||
|
||||
@import "../common";
|
||||
@import '../common';
|
||||
|
||||
@mixin match-line {
|
||||
color: $black-transparent;
|
||||
|
@ -10,7 +10,7 @@
|
|||
}
|
||||
|
||||
.code.none {
|
||||
// Line numbers
|
||||
// Line numbers
|
||||
.line-numbers,
|
||||
.diff-line-num {
|
||||
background-color: $gray-light;
|
||||
|
@ -44,7 +44,6 @@
|
|||
$none-expanded-bg: #e0e0e0;
|
||||
|
||||
.line_holder {
|
||||
|
||||
&.match .line_content,
|
||||
.new-nonewline.line_content,
|
||||
.old-nonewline.line_content {
|
||||
|
@ -149,12 +148,12 @@
|
|||
background-color: $white-normal;
|
||||
}
|
||||
|
||||
// Search result highlight
|
||||
// Search result highlight
|
||||
span.highlight_word {
|
||||
background-color: $white-normal;
|
||||
}
|
||||
|
||||
// Links to URLs, emails, or dependencies
|
||||
// Links to URLs, emails, or dependencies
|
||||
.line a {
|
||||
color: $gl-text-color;
|
||||
text-decoration: underline;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* https://gist.github.com/qguv/7936275 */
|
||||
|
||||
@import "../common";
|
||||
@import '../common';
|
||||
|
||||
/*
|
||||
* Solarized dark colors
|
||||
|
@ -244,13 +244,19 @@ $solarized-dark-il: #2aa198;
|
|||
.c1 { color: $solarized-dark-c1; } /* Comment.Single */
|
||||
.cs { color: $solarized-dark-cs; } /* Comment.Special */
|
||||
.gd { color: $solarized-dark-gd; } /* Generic.Deleted */
|
||||
.ge { color: $solarized-dark-ge; font-style: italic; } /* Generic.Emph */
|
||||
.ge { /* Generic.Emph */
|
||||
color: $solarized-dark-ge;
|
||||
font-style: italic;
|
||||
}
|
||||
.gr { color: $solarized-dark-gr; } /* Generic.Error */
|
||||
.gh { color: $solarized-dark-gh; } /* Generic.Heading */
|
||||
.gi { color: $solarized-dark-gi; } /* Generic.Inserted */
|
||||
.go { color: $solarized-dark-go; } /* Generic.Output */
|
||||
.gp { color: $solarized-dark-gp; } /* Generic.Prompt */
|
||||
.gs { color: $solarized-dark-gs; font-weight: $gl-font-weight-bold; } /* Generic.Strong */
|
||||
.gs { /* Generic.Strong */
|
||||
color: $solarized-dark-gs;
|
||||
font-weight: $gl-font-weight-bold;
|
||||
}
|
||||
.gu { color: $solarized-dark-gu; } /* Generic.Subheading */
|
||||
.gt { color: $solarized-dark-gt; } /* Generic.Traceback */
|
||||
.kc { color: $solarized-dark-kc; } /* Keyword.Constant */
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/* https://gist.github.com/qguv/7936275 */
|
||||
|
||||
@import "../common";
|
||||
@import '../common';
|
||||
|
||||
/*
|
||||
* Solarized light syntax colors
|
||||
|
@ -252,13 +252,19 @@ $solarized-light-il: #2aa198;
|
|||
.c1 { color: $solarized-light-c1; } /* Comment.Single */
|
||||
.cs { color: $solarized-light-cs; } /* Comment.Special */
|
||||
.gd { color: $solarized-light-gd; } /* Generic.Deleted */
|
||||
.ge { color: $solarized-light-ge; font-style: italic; } /* Generic.Emph */
|
||||
.ge { /* Generic.Emph */
|
||||
color: $solarized-light-ge;
|
||||
font-style: italic;
|
||||
}
|
||||
.gr { color: $solarized-light-gr; } /* Generic.Error */
|
||||
.gh { color: $solarized-light-gh; } /* Generic.Heading */
|
||||
.gi { color: $solarized-light-gi; } /* Generic.Inserted */
|
||||
.go { color: $solarized-light-go; } /* Generic.Output */
|
||||
.gp { color: $solarized-light-gp; } /* Generic.Prompt */
|
||||
.gs { color: $solarized-light-gs; font-weight: $gl-font-weight-bold; } /* Generic.Strong */
|
||||
.gs { /* Generic.Strong */
|
||||
color: $solarized-light-gs;
|
||||
font-weight: $gl-font-weight-bold;
|
||||
}
|
||||
.gu { color: $solarized-light-gu; } /* Generic.Subheading */
|
||||
.gt { color: $solarized-light-gt; } /* Generic.Traceback */
|
||||
.kc { color: $solarized-light-kc; } /* Keyword.Constant */
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
.code.white {
|
||||
@import "../white_base";
|
||||
@import '../white_base';
|
||||
}
|
||||
|
|
|
@ -6,12 +6,12 @@
|
|||
// stylelint-disable color-hex-length
|
||||
|
||||
$mailer-font: 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
$mailer-text-color: #333333;
|
||||
$mailer-text-color: #333;
|
||||
$mailer-bg-color: #fafafa;
|
||||
$mailer-link-color: #3777b0;
|
||||
$mailer-link-muted-color: #333333;
|
||||
$mailer-link-muted-color: #333;
|
||||
$mailer-line-cell-bg-color: #6b4fbb;
|
||||
$mailer-wrapper-cell-bg-color: #ffffff;
|
||||
$mailer-wrapper-cell-bg-color: #fff;
|
||||
$mailer-wrapper-cell-border-color: #ededed;
|
||||
$mailer-header-footer-text-color: #5c5c5c;
|
||||
|
||||
|
|
|
@ -47,4 +47,4 @@
|
|||
|
||||
--ide-animation-gradient-1: var(--ide-file-row-btn-hover-background);
|
||||
--ide-animation-gradient-2: var(--ide-dropdown-hover-background);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
@import "framework/variables";
|
||||
@import 'framework/variables';
|
||||
|
||||
.gitlab-embed-snippets {
|
||||
@import "highlight/embedded";
|
||||
@import "framework/images";
|
||||
@import 'highlight/embedded';
|
||||
@import 'framework/images';
|
||||
|
||||
$border-style: 1px solid $border-color;
|
||||
|
||||
|
@ -15,6 +15,7 @@
|
|||
|
||||
.gl-snippet-icon {
|
||||
display: inline-block;
|
||||
/* stylelint-disable-next-line function-url-quotes */
|
||||
background: url(asset_path('ext_snippet_icons/ext_snippet_icons.png')) no-repeat;
|
||||
overflow: hidden;
|
||||
text-align: left;
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
|
||||
@for $i from 1 through 12 {
|
||||
#{'.tab-width-#{$i}'} {
|
||||
/* stylelint-disable-next-line property-no-vendor-prefix */
|
||||
-moz-tab-size: $i;
|
||||
tab-size: $i;
|
||||
}
|
||||
|
|
|
@ -7,14 +7,19 @@ module Projects
|
|||
RequestError = Class.new(StandardError)
|
||||
|
||||
class IssuesFinder
|
||||
attr_reader :issues, :total_count
|
||||
attr_reader :issues, :total_count, :per_page
|
||||
|
||||
class << self
|
||||
def valid_params
|
||||
@valid_params ||= %i[page per_page search state]
|
||||
end
|
||||
end
|
||||
|
||||
def initialize(project, params = {})
|
||||
@project = project
|
||||
@jira_service = project.jira_service
|
||||
@page = params[:page].presence || 1
|
||||
@params = params
|
||||
@params = @params.reverse_merge(map_sort_values(@params.delete(:sort)))
|
||||
@params = params.merge(map_sort_values(params[:sort]))
|
||||
set_pagination
|
||||
end
|
||||
|
||||
def execute
|
||||
|
@ -35,7 +40,9 @@ module Projects
|
|||
# rubocop: disable CodeReuse/ServiceClass
|
||||
def fetch_issues(project_key)
|
||||
jql = ::Jira::JqlBuilderService.new(project_key, params).execute
|
||||
response = ::Jira::Requests::Issues::ListService.new(jira_service, { jql: jql, page: page }).execute
|
||||
response = ::Jira::Requests::Issues::ListService
|
||||
.new(jira_service, { jql: jql, page: page, per_page: per_page })
|
||||
.execute
|
||||
|
||||
if response.success?
|
||||
@total_count = response.payload[:total_count]
|
||||
|
@ -54,6 +61,11 @@ module Projects
|
|||
{ sort: ::Jira::JqlBuilderService::DEFAULT_SORT, sort_direction: ::Jira::JqlBuilderService::DEFAULT_SORT_DIRECTION }
|
||||
end
|
||||
end
|
||||
|
||||
def set_pagination
|
||||
@page = (params[:page].presence || 1).to_i
|
||||
@per_page = (params[:per_page].presence || ::Jira::Requests::Issues::ListService::PER_PAGE).to_i
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -32,7 +32,8 @@ module SystemNoteHelper
|
|||
'designs_modified' => 'doc-image',
|
||||
'designs_removed' => 'doc-image',
|
||||
'designs_discussion_added' => 'doc-image',
|
||||
'status' => 'status'
|
||||
'status' => 'status',
|
||||
'alert_issue_added' => 'issues'
|
||||
}.freeze
|
||||
|
||||
def system_note_icon_name(note)
|
||||
|
|
|
@ -19,7 +19,7 @@ class SystemNoteMetadata < ApplicationRecord
|
|||
title time_tracking branch milestone discussion task moved
|
||||
opened closed merged duplicate locked unlocked outdated
|
||||
tag due_date pinned_embed cherry_pick health_status approved unapproved
|
||||
status
|
||||
status alert_issue_added
|
||||
].freeze
|
||||
|
||||
validates :note, presence: true
|
||||
|
|
|
@ -21,6 +21,8 @@ module AlertManagement
|
|||
return error(result.message, issue) if result.error?
|
||||
return error(object_errors(alert), issue) unless associate_alert_with_issue(issue)
|
||||
|
||||
SystemNoteService.new_alert_issue(alert, issue, user)
|
||||
|
||||
result
|
||||
end
|
||||
|
||||
|
|
|
@ -296,6 +296,10 @@ module SystemNoteService
|
|||
::SystemNotes::AlertManagementService.new(noteable: alert, project: alert.project, author: author).change_alert_status(alert)
|
||||
end
|
||||
|
||||
def new_alert_issue(alert, issue, author)
|
||||
::SystemNotes::AlertManagementService.new(noteable: alert, project: alert.project, author: author).new_alert_issue(alert, issue)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def merge_requests_service(noteable, project, author)
|
||||
|
|
|
@ -17,5 +17,21 @@ module SystemNotes
|
|||
|
||||
create_note(NoteSummary.new(noteable, project, author, body, action: 'status'))
|
||||
end
|
||||
|
||||
# Called when an issue is created based on an AlertManagement::Alert
|
||||
#
|
||||
# alert - AlertManagement::Alert object.
|
||||
# issue - Issue object.
|
||||
#
|
||||
# Example Note text:
|
||||
#
|
||||
# "created issue #17 for this alert"
|
||||
#
|
||||
# Returns the created Note object
|
||||
def new_alert_issue(alert, issue)
|
||||
body = "created issue #{issue.to_reference(project)} for this alert"
|
||||
|
||||
create_note(NoteSummary.new(noteable, project, author, body, action: 'alert_issue_added'))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -54,7 +54,7 @@
|
|||
= _('Newly registered users will by default be external')
|
||||
.prepend-top-10
|
||||
= _('Internal users')
|
||||
= f.text_field :user_default_internal_regex, placeholder: _('Regex pattern'), class: 'form-control prepend-top-5'
|
||||
= f.text_field :user_default_internal_regex, placeholder: _('Regex pattern'), class: 'form-control gl-mt-2'
|
||||
.help-block
|
||||
= _('Specify an e-mail address regex pattern to identify default internal users.')
|
||||
= link_to _('More information'), help_page_path('user/permissions', anchor: 'setting-new-users-to-external'),
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
#promote-label-modal
|
||||
= render 'shared/labels/nav', labels_or_filters: labels_or_filters, can_admin_label: can_admin_label
|
||||
|
||||
.labels-container.prepend-top-5
|
||||
.labels-container.gl-mt-2
|
||||
- if @labels.any?
|
||||
.text-muted
|
||||
= _('Labels can be applied to %{features}. Group labels are available for any project within the group.') % { features: issuable_types.to_sentence }
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
= link_to avatar_icon_for_user(@user, 400), target: '_blank', rel: 'noopener noreferrer' do
|
||||
= image_tag avatar_icon_for_user(@user, 160), alt: '', class: 'avatar s160'
|
||||
%h5.gl-mt-0= s_("Profiles|Upload new avatar")
|
||||
.prepend-top-5.append-bottom-10
|
||||
.gl-mt-2.append-bottom-10
|
||||
%button.btn.js-choose-user-avatar-button{ type: 'button' }= s_("Profiles|Choose file...")
|
||||
%span.avatar-file-name.gl-ml-3.js-avatar-filename= s_("Profiles|No file chosen")
|
||||
= f.file_field_without_bootstrap :avatar, class: 'js-user-avatar-input hidden', accept: 'image/*'
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
- else
|
||||
%strong.diff-changed-blank-file-name
|
||||
= s_('Diffs|No file name available')
|
||||
%span.diff-changed-file-path.prepend-top-5= diff_file_path_text(diff_file)
|
||||
%span.diff-changed-file-path.gl-mt-2= diff_file_path_text(diff_file)
|
||||
%span.diff-changed-stats
|
||||
%span.cgreen<
|
||||
+#{diff_file.added_lines}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
= link_to _('Submit as spam'), mark_as_spam_project_snippet_path(@project, @snippet), method: :post, class: 'btn btn-grouped btn-spam', title: _('Submit as spam')
|
||||
- if can?(current_user, :create_snippet, @project) || can?(current_user, :update_snippet, @snippet)
|
||||
.d-block.d-sm-none.dropdown
|
||||
%button.btn.btn-default.btn-block.gl-mb-0.prepend-top-5{ data: { toggle: "dropdown" } }
|
||||
%button.btn.btn-default.btn-block.gl-mb-0.gl-mt-2{ data: { toggle: "dropdown" } }
|
||||
= _('Options')
|
||||
= icon('caret-down')
|
||||
.dropdown-menu.dropdown-menu-full-width
|
||||
|
|
|
@ -8,11 +8,11 @@
|
|||
= text_field_tag 'deploy-token-user', deploy_token.username, readonly: true, class: 'deploy-token-field form-control js-select-on-focus qa-deploy-token-user'
|
||||
.input-group-append
|
||||
= clipboard_button(text: deploy_token.username, title: s_('DeployTokens|Copy username'), placement: 'left')
|
||||
%span.deploy-token-help-block.prepend-top-5.text-success= s_("DeployTokens|Use this username as a login.")
|
||||
%span.deploy-token-help-block.gl-mt-2.text-success= s_("DeployTokens|Use this username as a login.")
|
||||
|
||||
.form-group
|
||||
.input-group
|
||||
= text_field_tag 'deploy-token', deploy_token.token, readonly: true, class: 'deploy-token-field form-control js-select-on-focus qa-deploy-token'
|
||||
.input-group-append
|
||||
= clipboard_button(text: deploy_token.token, title: s_('DeployTokens|Copy deploy token'), placement: 'left')
|
||||
%span.deploy-token-help-block.prepend-top-5.text-danger= s_("DeployTokens|Use this token as a password. Make sure you save it - you won't be able to access it again.")
|
||||
%span.deploy-token-help-block.gl-mt-2.text-danger= s_("DeployTokens|Use this token as a password. Make sure you save it - you won't be able to access it again.")
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
%label.col-form-label.col-sm-2
|
||||
= _('Contribution')
|
||||
.col-sm-10
|
||||
.form-check.prepend-top-5
|
||||
.form-check.gl-mt-2
|
||||
= form.check_box :allow_collaboration, disabled: !issuable.can_allow_collaboration?(current_user), class: 'form-check-input'
|
||||
= form.label :allow_collaboration, class: 'form-check-label' do
|
||||
= _('Allow commits from members who can merge to the target branch.')
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
= f.label :start_date, _('Start Date')
|
||||
.col-sm-10
|
||||
= f.text_field :start_date, class: "datepicker form-control", data: { qa_selector: "start_date_field" }, placeholder: _('Select start date'), autocomplete: 'off'
|
||||
%a.inline.float-right.prepend-top-5.js-clear-start-date{ href: "#" }= _('Clear start date')
|
||||
%a.inline.float-right.gl-mt-2.js-clear-start-date{ href: "#" }= _('Clear start date')
|
||||
.form-group.row
|
||||
.col-form-label.col-sm-2
|
||||
= f.label :due_date, _('Due Date')
|
||||
.col-sm-10
|
||||
= f.text_field :due_date, class: "datepicker form-control", data: { qa_selector: "due_date_field" }, placeholder: _('Select due date'), autocomplete: 'off'
|
||||
%a.inline.float-right.prepend-top-5.js-clear-due-date{ href: "#" }= _('Clear due date')
|
||||
%a.inline.float-right.gl-mt-2.js-clear-due-date{ href: "#" }= _('Clear due date')
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
= _('SSL Verification:')
|
||||
= hook.enable_ssl_verification ? _('enabled') : _('disabled')
|
||||
|
||||
.col-md-4.col-lg-5.text-right-md.prepend-top-5
|
||||
.col-md-4.col-lg-5.text-right-md.gl-mt-2
|
||||
%span>= render 'shared/web_hooks/test_button', hook: hook, button_class: 'btn-sm gl-mr-3'
|
||||
%span>= link_to _('Edit'), edit_hook_path(hook), class: 'btn btn-sm gl-mr-3'
|
||||
= link_to _('Delete'), destroy_hook_path(hook), data: { confirm: _('Are you sure?') }, method: :delete, class: 'btn btn-sm'
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
.col-sm-12= f.label :title, class: 'control-label-full-width'
|
||||
.col-sm-12
|
||||
= f.text_field :title, class: 'form-control qa-wiki-title-textbox', value: @page.title, required: true, autofocus: !@page.persisted?, placeholder: s_('Wiki|Page title')
|
||||
%span.d-inline-block.mw-100.prepend-top-5
|
||||
%span.d-inline-block.mw-100.gl-mt-2
|
||||
= icon('lightbulb-o')
|
||||
- if @page.persisted?
|
||||
= s_("WikiEditPageTip|Tip: You can move this page by adding the path to the beginning of the title.")
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
- if @snippet.submittable_as_spam_by?(current_user)
|
||||
= link_to _('Submit as spam'), mark_as_spam_snippet_path(@snippet), method: :post, class: 'btn btn-grouped btn-spam', title: _('Submit as spam')
|
||||
.d-block.d-sm-none.dropdown
|
||||
%button.btn.btn-default.btn-block.gl-mb-0.prepend-top-5{ data: { toggle: "dropdown" } }
|
||||
%button.btn.btn-default.btn-block.gl-mb-0.gl-mt-2{ data: { toggle: "dropdown" } }
|
||||
= _("Options")
|
||||
= icon('caret-down')
|
||||
.dropdown-menu.dropdown-menu-full-width
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Remove dashboard panels' tabindex where is not needed
|
||||
merge_request: 36168
|
||||
author:
|
||||
type: fixed
|
5
changelogs/unreleased/sy-alert-issue-system-notes.yml
Normal file
5
changelogs/unreleased/sy-alert-issue-system-notes.yml
Normal file
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
title: Add system note for alert when creating issue
|
||||
merge_request: 36370
|
||||
author:
|
||||
type: added
|
|
@ -13,7 +13,7 @@ This API is deprecated and [scheduled for removal in GitLab 14.0](https://gitlab
|
|||
|
||||
The API for creating, updating, reading and deleting Feature Flag Specs.
|
||||
Automation engineers benefit from this API by being able to modify Feature Flag Specs without accessing user interface.
|
||||
To manage the [Feature Flag](../user/project/operations/feature_flags.md) resources via public API, please refer to the [Feature Flags API](feature_flags.md) document.
|
||||
To manage the [Feature Flag](../operations/feature_flags.md) resources via public API, please refer to the [Feature Flags API](feature_flags.md) document.
|
||||
|
||||
Users with Developer or higher [permissions](../user/permissions.md) can access Feature Flag Specs API.
|
||||
|
||||
|
@ -166,7 +166,7 @@ POST /projects/:id/feature_flags/:name/scopes
|
|||
| `name` | string | yes | The name of the feature flag. |
|
||||
| `environment_scope` | string | yes | The [environment spec](../ci/environments/index.md#scoping-environments-with-specs) of the feature flag. |
|
||||
| `active` | boolean | yes | Whether the spec is active. |
|
||||
| `strategies` | JSON | yes | The [strategies](../user/project/operations/feature_flags.md#feature-flag-strategies) of the feature flag spec. |
|
||||
| `strategies` | JSON | yes | The [strategies](../operations/feature_flags.md#feature-flag-strategies) of the feature flag spec. |
|
||||
|
||||
```shell
|
||||
curl "https://gitlab.example.com/api/v4/projects/1/feature_flags/new_live_trace/scopes" \
|
||||
|
@ -249,7 +249,7 @@ PUT /projects/:id/feature_flags/:name/scopes/:environment_scope
|
|||
| `name` | string | yes | The name of the feature flag. |
|
||||
| `environment_scope` | string | yes | The URL-encoded [environment spec](../ci/environments/index.md#scoping-environments-with-specs) of the feature flag. |
|
||||
| `active` | boolean | yes | Whether the spec is active. |
|
||||
| `strategies` | JSON | yes | The [strategies](../user/project/operations/feature_flags.md#feature-flag-strategies) of the feature flag spec. |
|
||||
| `strategies` | JSON | yes | The [strategies](../operations/feature_flags.md#feature-flag-strategies) of the feature flag spec. |
|
||||
|
||||
```shell
|
||||
curl "https://gitlab.example.com/api/v4/projects/1/feature_flags/new_live_trace/scopes/production" \
|
||||
|
|
|
@ -9,10 +9,10 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/9566) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.5.
|
||||
|
||||
NOTE: **Note**
|
||||
This API is behind a [feature flag](../user/project/operations/feature_flags.md#enable-or-disable-feature-flag-strategies).
|
||||
This API is behind a [feature flag](../operations/feature_flags.md#enable-or-disable-feature-flag-strategies).
|
||||
If this flag is not enabled in your environment, you can use the [legacy feature flags API](feature_flags_legacy.md).
|
||||
|
||||
API for accessing resources of [GitLab Feature Flags](../user/project/operations/feature_flags.md).
|
||||
API for accessing resources of [GitLab Feature Flags](../operations/feature_flags.md).
|
||||
|
||||
Users with Developer or higher [permissions](../user/permissions.md) can access Feature Flag API.
|
||||
|
||||
|
@ -146,7 +146,7 @@ POST /projects/:id/feature_flags
|
|||
| `name` | string | yes | The name of the feature flag. |
|
||||
| `version` | string | yes | The version of the feature flag. Must be `new_version_flag`. Omit or set to `legacy_flag` to create a [Legacy Feature Flag](feature_flags_legacy.md). |
|
||||
| `description` | string | no | The description of the feature flag. |
|
||||
| `strategies` | JSON | no | The feature flag [strategies](../user/project/operations/feature_flags.md#feature-flag-strategies). |
|
||||
| `strategies` | JSON | no | The feature flag [strategies](../operations/feature_flags.md#feature-flag-strategies). |
|
||||
| `strategies:name` | JSON | no | The strategy name. |
|
||||
| `strategies:parameters` | JSON | no | The strategy parameters. |
|
||||
| `strategies:scopes` | JSON | no | The scopes for the strategy. |
|
||||
|
@ -204,7 +204,7 @@ PUT /projects/:id/feature_flags/:name
|
|||
| `id` | integer/string | yes | The ID or [URL-encoded path of the project](README.md#namespaced-path-encoding). |
|
||||
| `name` | string | yes | The name of the feature flag. |
|
||||
| `description` | string | no | The description of the feature flag. |
|
||||
| `strategies` | JSON | no | The feature flag [strategies](../user/project/operations/feature_flags.md#feature-flag-strategies). |
|
||||
| `strategies` | JSON | no | The feature flag [strategies](../operations/feature_flags.md#feature-flag-strategies). |
|
||||
| `strategies:id` | JSON | no | The feature flag strategy id. |
|
||||
| `strategies:name` | JSON | no | The strategy name. |
|
||||
| `strategies:parameters` | JSON | no | The strategy parameters. |
|
||||
|
|
|
@ -11,7 +11,7 @@ info: To determine the technical writer assigned to the Stage/Group associated w
|
|||
CAUTION: **Deprecation**
|
||||
This API is deprecated and [scheduled for removal in GitLab 14.0](https://gitlab.com/gitlab-org/gitlab/-/issues/213369). Use [this API](feature_flags.md) instead.
|
||||
|
||||
API for accessing resources of [GitLab Feature Flags](../user/project/operations/feature_flags.md).
|
||||
API for accessing resources of [GitLab Feature Flags](../operations/feature_flags.md).
|
||||
|
||||
Users with Developer or higher [permissions](../user/permissions.md) can access Feature Flag API.
|
||||
|
||||
|
@ -166,7 +166,7 @@ POST /projects/:id/feature_flags
|
|||
| `scopes` | JSON | no | The feature flag specs of the feature flag. |
|
||||
| `scopes:environment_scope` | string | no | The environment spec. |
|
||||
| `scopes:active` | boolean | no | Whether the spec is active. |
|
||||
| `scopes:strategies` | JSON | no | The [strategies](../user/project/operations/feature_flags.md#feature-flag-strategies) of the feature flag spec. |
|
||||
| `scopes:strategies` | JSON | no | The [strategies](../operations/feature_flags.md#feature-flag-strategies) of the feature flag spec. |
|
||||
|
||||
```shell
|
||||
curl https://gitlab.example.com/api/v4/projects/1/feature_flags \
|
||||
|
|
|
@ -139,7 +139,7 @@ Its feature set is listed on the table below according to DevOps stages.
|
|||
| [Building Docker images](docker/using_docker_build.md) | Maintain Docker-based projects using GitLab CI/CD. |
|
||||
| [Canary Deployments](../user/project/canary_deployments.md) **(PREMIUM)** | Ship features to only a portion of your pods and let a percentage of your user base to visit the temporarily deployed feature. |
|
||||
| [Deploy Boards](../user/project/deploy_boards.md) **(PREMIUM)** | Check the current health and status of each CI/CD environment running on Kubernetes. |
|
||||
| [Feature Flags](../user/project/operations/feature_flags.md) **(PREMIUM)** | Deploy your features behind Feature Flags. |
|
||||
| [Feature Flags](../operations/feature_flags.md) **(PREMIUM)** | Deploy your features behind Feature Flags. |
|
||||
| [GitLab Pages](../user/project/pages/index.md) | Deploy static websites. |
|
||||
| [GitLab Releases](../user/project/releases/index.md) | Add release notes to Git tags. |
|
||||
| [Review Apps](review_apps/index.md) | Configure GitLab CI/CD to preview code changes. |
|
||||
|
|
|
@ -202,7 +202,7 @@ according to each stage (Verify, Package, Release).
|
|||
- Continuous Delivery, manually click to deploy your app to production.
|
||||
- Deploy static websites with [GitLab Pages](../../user/project/pages/index.md).
|
||||
- Ship features to only a portion of your pods and let a percentage of your user base to visit the temporarily deployed feature with [Canary Deployments](../../user/project/canary_deployments.md). **(PREMIUM)**
|
||||
- Deploy your features behind [Feature Flags](../../user/project/operations/feature_flags.md). **(PREMIUM)**
|
||||
- Deploy your features behind [Feature Flags](../../operations/feature_flags.md). **(PREMIUM)**
|
||||
- Add release notes to any Git tag with [GitLab Releases](../../user/project/releases/index.md).
|
||||
- View of the current health and status of each CI environment running on Kubernetes with [Deploy Boards](../../user/project/deploy_boards.md). **(PREMIUM)**
|
||||
- Deploy your application to a production environment in a Kubernetes cluster with [Auto Deploy](../../topics/autodevops/stages.md#auto-deploy).
|
||||
|
|
|
@ -770,7 +770,7 @@ Relative linking enables crosslinks to work:
|
|||
|
||||
- in Review Apps, local previews, and `/help`.
|
||||
- when working on the docs locally, so you can verify that they work as early as possible in the process.
|
||||
- within the GitLab UI when browsing doc files in their respective repositories. For example, the links displayed at <https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/README.md>.
|
||||
- within the GitLab UI when browsing doc files in their respective repositories. For example, the links displayed at `https://gitlab.com/gitlab-org/gitlab/-/blob/master/doc/README.md`.
|
||||
|
||||
To link to internal documentation:
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Feature flags in development of GitLab
|
||||
|
||||
[Feature Flags](../../user/project/operations/feature_flags.md)
|
||||
[Feature Flags](../../operations/feature_flags.md)
|
||||
can be used to gradually roll out changes, be
|
||||
it a new feature, or a performance improvement. By using feature flags, we can
|
||||
comfortably measure the impact of our changes, while still being able to easily
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
This document only covers feature flags used in the development of GitLab
|
||||
itself. Feature flags in deployed user applications can be found at
|
||||
[Feature Flags feature documentation](../../user/project/operations/feature_flags.md).
|
||||
[Feature Flags feature documentation](../../operations/feature_flags.md).
|
||||
|
||||
## Feature flags in GitLab development
|
||||
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
---
|
||||
redirect_to: '../administration/operations/index.md'
|
||||
---
|
||||
|
||||
This document was moved to [another location](../administration/operations/index.md).
|
289
doc/operations/feature_flags.md
Normal file
289
doc/operations/feature_flags.md
Normal file
|
@ -0,0 +1,289 @@
|
|||
---
|
||||
stage: Release
|
||||
group: Progressive Delivery
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
|
||||
---
|
||||
|
||||
# Feature Flags **(PREMIUM)**
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/7433) in GitLab 11.4.
|
||||
|
||||
With Feature Flags, you can deploy your application's new features to production in smaller batches.
|
||||
You can toggle a feature on and off to subsets of users, helping you achieve Continuous Delivery.
|
||||
Feature flags help reduce risk, allowing you to do controlled testing, and separate feature
|
||||
delivery from customer launch.
|
||||
|
||||
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
|
||||
For an example of feature flags in action, see [GitLab for Deploys, Feature Flags, and Error Tracking](https://www.youtube.com/embed/5tw2p6lwXxo).
|
||||
|
||||
## How it works
|
||||
|
||||
GitLab uses [Unleash](https://github.com/Unleash/unleash), a feature
|
||||
toggle service.
|
||||
|
||||
By enabling or disabling a flag in GitLab, your application
|
||||
can determine which features to enable or disable.
|
||||
|
||||
You can create feature flags in GitLab and use the API from your application
|
||||
to get the list of feature flags and their statuses. The application must be configured to communicate
|
||||
with GitLab, so it's up to developers to use a compatible client library and
|
||||
[integrate the feature flags in your app](#integrate-feature-flags-with-your-application).
|
||||
|
||||
## Create a feature flag
|
||||
|
||||
To create and enable a feature flag:
|
||||
|
||||
1. Navigate to your project's **Operations > Feature Flags**.
|
||||
1. Click the **New feature flag** button.
|
||||
1. Enter a name that starts with a letter and contains only lowercase letters, digits, underscores (`_`),
|
||||
or dashes (`-`), and does not end with a dash (`-`) or underscore (`_`).
|
||||
1. Enter a description (optional, 255 characters max).
|
||||
1. Enter details about how the flag should be applied:
|
||||
- In GitLab 13.0 and earlier, add **Environment specs**. For each environment,
|
||||
include the **Status** (default enabled) and [**Rollout strategy**](#rollout-strategy-legacy)
|
||||
(defaults to **All users**).
|
||||
- In GitLab 13.1 and later, add Feature Flag [**Strategies**](#feature-flag-strategies).
|
||||
For each strategy, include the **Type** (defaults to [**All users**](#all-users))
|
||||
and **Environments** (defaults to all environments).
|
||||
1. Click **Create feature flag**.
|
||||
|
||||
You can change these settings by clicking the **{pencil}** (edit) button
|
||||
next to any feature flag in the list.
|
||||
|
||||
## Rollout strategy (legacy)
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/8240) in GitLab 12.2.
|
||||
|
||||
In GitLab 13.0 and earlier, the **Rollout strategy** setting affects which users will experience
|
||||
the feature as enabled. Choose the percentage of users that the feature will be enabled
|
||||
for. The rollout strategy will have no effect if the environment spec is disabled.
|
||||
|
||||
It can be set to:
|
||||
|
||||
- All users
|
||||
- [Percent rollout (logged in users)](#percent-rollout-logged-in-users)
|
||||
- Optionally, you can click the **Include additional user IDs** checkbox and add a list
|
||||
of specific users IDs to enable the feature for.
|
||||
- [User IDs](#user-ids)
|
||||
|
||||
## Feature flag strategies
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/35555) in GitLab 13.0.
|
||||
> - It's deployed behind a feature flag, disabled by default.
|
||||
> - It's enabled on GitLab.com.
|
||||
> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-feature-flag-strategies). **(CORE ONLY)**
|
||||
|
||||
You can apply a feature flag strategy across multiple environments, without defining
|
||||
the strategy multiple times.
|
||||
|
||||
GitLab Feature Flags use [Unleash](https://unleash.github.io) as the feature flag
|
||||
engine. In Unleash, there are [strategies](https://unleash.github.io/docs/activation_strategy)
|
||||
for granular feature flag controls. GitLab Feature Flags can have multiple strategies,
|
||||
and the supported strategies are:
|
||||
|
||||
- [All users](#all-users)
|
||||
- [Percent rollout (logged in users)](#percent-rollout-logged-in-users)
|
||||
- [User IDs](#user-ids)
|
||||
- [List](#list)
|
||||
|
||||
Strategies can be added to feature flags when [creating a feature flag](#create-a-feature-flag),
|
||||
or by editing an existing feature flag after creation by navigating to **Operations > Feature Flags**
|
||||
and clicking **{pencil}** (edit).
|
||||
|
||||
### All users
|
||||
|
||||
Enables the feature for all users. It uses the [`default`](https://unleash.github.io/docs/activation_strategy#default)
|
||||
Unleash activation strategy.
|
||||
|
||||
### Percent rollout (logged in users)
|
||||
|
||||
Enables the feature for a percentage of authenticated users. It uses the
|
||||
[`gradualRolloutUserId`](https://unleash.github.io/docs/activation_strategy#gradualrolloutuserid)
|
||||
Unleash activation strategy.
|
||||
|
||||
For example, set a value of 15% to enable the feature for 15% of authenticated users.
|
||||
|
||||
The rollout percentage can be from 0% to 100%.
|
||||
|
||||
CAUTION: **Caution:**
|
||||
If this strategy is selected, then the Unleash client **must** be given a user
|
||||
ID for the feature to be enabled. See the [Ruby example](#ruby-application-example) below.
|
||||
|
||||
### User IDs
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/8240) in GitLab 12.2. [Updated](https://gitlab.com/gitlab-org/gitlab/-/issues/34363) to be defined per environment in GitLab 12.6.
|
||||
|
||||
Enables the feature for a list of target users. It is implemented
|
||||
using the Unleash [`userWithId`](https://unleash.github.io/docs/activation_strategy#userwithid)
|
||||
activation strategy.
|
||||
|
||||
Enter user IDs as a comma-separated list of values. For example,
|
||||
`user@example.com, user2@example.com`, or `username1,username2,username3`, and so on.
|
||||
|
||||
CAUTION: **Caution:**
|
||||
The Unleash client **must** be given a user ID for the feature to be enabled for
|
||||
target users. See the [Ruby example](#ruby-application-example) below.
|
||||
|
||||
### List
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/35930) in GitLab 13.1.
|
||||
|
||||
Enables the feature for lists of users created with the [Feature Flag User List API](../api/feature_flag_user_lists.md).
|
||||
Similar to [User IDs](#user-ids), it uses the Unleash [`userWithId`](https://unleash.github.io/docs/activation_strategy#userwithid)
|
||||
activation strategy.
|
||||
|
||||
### Enable or disable feature flag strategies
|
||||
|
||||
This feature is under development, but is ready for testing. It's
|
||||
deployed behind a feature flag that is **disabled by default**.
|
||||
[GitLab administrators with access to the GitLab Rails console](../administration/feature_flags.md)
|
||||
can enable it for your instance.
|
||||
|
||||
To enable it:
|
||||
|
||||
```ruby
|
||||
Feature.enable(:feature_flags_new_version)
|
||||
```
|
||||
|
||||
To disable it:
|
||||
|
||||
```ruby
|
||||
Feature.disable(:feature_flags_new_version)
|
||||
```
|
||||
|
||||
## Disable a feature flag for a specific environment
|
||||
|
||||
In [GitLab 13.0 and earlier](https://gitlab.com/gitlab-org/gitlab/-/issues/8621),
|
||||
to disable a feature flag for a specific environment:
|
||||
|
||||
1. Navigate to your project's **Operations > Feature Flags**.
|
||||
1. For the feature flag you want to disable, click the Pencil icon.
|
||||
1. To disable the flag:
|
||||
- In GitLab 13.0 and earlier: Slide the Status toggle for the environment. Or, to delete the
|
||||
environment spec, on the right, click the **Remove (X)** icon.
|
||||
- In GitLab 13.1 and later: For each strategy it applies to, under **Environments**, delete the environment.
|
||||
|
||||
1. Click **Save changes**.
|
||||
|
||||
## Disable a feature flag for all environments
|
||||
|
||||
To disable a feature flag for all environments:
|
||||
|
||||
1. Navigate to your project's **Operations > Feature Flags**.
|
||||
1. For the feature flag you want to disable, slide the Status toggle to **Disabled**.
|
||||
|
||||
The feature flag is displayed on the **Disabled** tab.
|
||||
|
||||
## Integrate feature flags with your application
|
||||
|
||||
To use feature flags with your application, get access credentials from GitLab.
|
||||
Then prepare your application with a client library.
|
||||
|
||||
### Get access credentials
|
||||
|
||||
To get the access credentials that your application needs to communicate with GitLab:
|
||||
|
||||
1. Navigate to your project's **Operations > Feature Flags**.
|
||||
1. Click the **Configure** button to view the following:
|
||||
- **API URL**: URL where the client (application) connects to get a list of feature flags.
|
||||
- **Instance ID**: Unique token that authorizes the retrieval of the feature flags.
|
||||
- **Application name**: The name of the running environment. For instance,
|
||||
if the application runs for a production server, the application name would be
|
||||
`production` or similar. This value is used for the environment spec evaluation.
|
||||
|
||||
NOTE: **Note:**
|
||||
The meaning of these fields might change over time. For example, we are not sure
|
||||
if **Instance ID** will be single token or multiple tokens, assigned to the
|
||||
**Environment**. Also, **Application name** could describe the version of
|
||||
application instead of the running environment.
|
||||
|
||||
### Choose a client library
|
||||
|
||||
GitLab implements a single backend that is compatible with Unleash clients.
|
||||
|
||||
With the Unleash client, developers can define, in the application code, the default values for flags.
|
||||
Each feature flag evaluation can express the desired outcome if the flag isn't present in the
|
||||
provided configuration file.
|
||||
|
||||
Unleash currently [offers many SDKs for various languages and frameworks](https://github.com/Unleash/unleash#client-implementations).
|
||||
|
||||
### Feature flags API information
|
||||
|
||||
For API content, see:
|
||||
|
||||
- [Feature Flags API](../api/feature_flags.md)
|
||||
- [Feature Flag Specs API](../api/feature_flag_specs.md) (Deprecated and [scheduled for removal in GitLab 14.0](https://gitlab.com/gitlab-org/gitlab/-/issues/213369).)
|
||||
- [Feature Flag User Lists API](../api/feature_flag_user_lists.md)
|
||||
- [Legacy Feature Flags API](../api/feature_flags_legacy.md)
|
||||
|
||||
### Golang application example
|
||||
|
||||
Here's an example of how to integrate feature flags in a Golang application:
|
||||
|
||||
```golang
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/Unleash/unleash-client-go"
|
||||
)
|
||||
|
||||
type metricsInterface struct {
|
||||
}
|
||||
|
||||
func init() {
|
||||
unleash.Initialize(
|
||||
unleash.WithUrl("https://gitlab.com/api/v4/feature_flags/unleash/42"),
|
||||
unleash.WithInstanceId("29QmjsW6KngPR5JNPMWx"),
|
||||
unleash.WithAppName("production"),
|
||||
unleash.WithListener(&metricsInterface{}),
|
||||
)
|
||||
}
|
||||
|
||||
func helloServer(w http.ResponseWriter, req *http.Request) {
|
||||
if unleash.IsEnabled("my_feature_name") {
|
||||
io.WriteString(w, "Feature enabled\n")
|
||||
} else {
|
||||
io.WriteString(w, "hello, world!\n")
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
http.HandleFunc("/", helloServer)
|
||||
log.Fatal(http.ListenAndServe(":8080", nil))
|
||||
}
|
||||
```
|
||||
|
||||
### Ruby application example
|
||||
|
||||
Here's an example of how to integrate feature flags in a Ruby application.
|
||||
|
||||
The Unleash client is given a user ID for use with a **Percent rollout (logged in users)** rollout strategy or a list of **Target Users**.
|
||||
|
||||
```ruby
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
require 'unleash'
|
||||
require 'unleash/context'
|
||||
|
||||
unleash = Unleash::Client.new({
|
||||
url: 'http://gitlab.com/api/v4/feature_flags/unleash/42',
|
||||
app_name: 'production',
|
||||
instance_id: '29QmjsW6KngPR5JNPMWx'
|
||||
})
|
||||
|
||||
unleash_context = Unleash::Context.new
|
||||
# Replace "123" with the id of an authenticated user.
|
||||
# Note that the context's user id must be a string:
|
||||
# https://unleash.github.io/docs/unleash_context
|
||||
unleash_context.user_id = "123"
|
||||
|
||||
if unleash.is_enabled?("my_feature_name", unleash_context)
|
||||
puts "Feature enabled"
|
||||
else
|
||||
puts "hello, world!"
|
||||
end
|
||||
```
|
13
doc/operations/index.md
Normal file
13
doc/operations/index.md
Normal file
|
@ -0,0 +1,13 @@
|
|||
# Project operations
|
||||
|
||||
GitLab provides a variety of tools to help operate and maintain
|
||||
your applications:
|
||||
|
||||
- Collect [Prometheus metrics](../user/project/integrations/prometheus_library/index.md).
|
||||
- Deploy to different [environments](../ci/environments/index.md).
|
||||
- Connect your project to a [Kubernetes cluster](../user/project/clusters/index.md).
|
||||
- Manage your infrastructure with [Infrastructure as Code](../user/infrastructure/index.md) approaches.
|
||||
- Discover and view errors generated by your applications with [Error Tracking](../user/project/operations/error_tracking.md).
|
||||
- Create, toggle, and remove [Feature Flags](feature_flags.md). **(PREMIUM)**
|
||||
- [Trace](../user/project/operations/tracing.md) the performance and health of a deployed application. **(ULTIMATE)**
|
||||
- Change the [settings of the Monitoring Dashboard](../user/project/operations/dashboard_settings.md).
|
|
@ -118,23 +118,17 @@ The sort option and order is saved and used wherever you browse epics, including
|
|||
|
||||
## Make an epic confidential
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/213068) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.0 behind a feature flag, disabled by default.
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/213068) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.0 behind a feature flag, disabled by default.
|
||||
> - [Became enabled by default](https://gitlab.com/gitlab-org/gitlab/-/issues/224513) in [GitLab Premium](https://about.gitlab.com/pricing/) 13.2.
|
||||
|
||||
When you're creating an epic, you can make it confidential by selecting the **Make this epic
|
||||
confidential** checkbox.
|
||||
|
||||
### Enable confidential epics **(PREMIUM ONLY)**
|
||||
### Disable confidential epics **(PREMIUM ONLY)**
|
||||
|
||||
The confidential epics feature is under development and not ready for production use. It's deployed
|
||||
behind a feature flag that is **disabled by default**.
|
||||
The confidential epics feature is deployed behind a feature flag that is **enabled by default**.
|
||||
[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md)
|
||||
can enable it for your self-managed instance.
|
||||
|
||||
To enable it:
|
||||
|
||||
```ruby
|
||||
Feature.enable(:confidential_epics)
|
||||
```
|
||||
can disable it for your self-managed instance.
|
||||
|
||||
To disable it:
|
||||
|
||||
|
|
|
@ -78,7 +78,7 @@ When you create a project in GitLab, you'll have access to a large number of
|
|||
timeout (defines the maximum amount of time in minutes that a job is able run), custom path for `.gitlab-ci.yml`, test coverage parsing, pipeline's visibility, and much more
|
||||
- [Kubernetes cluster integration](clusters/index.md): Connecting your GitLab project
|
||||
with a Kubernetes cluster
|
||||
- [Feature Flags](operations/feature_flags.md): Feature flags allow you to ship a project in
|
||||
- [Feature Flags](../../operations/feature_flags.md): Feature flags allow you to ship a project in
|
||||
different flavors by dynamically toggling certain functionality **(PREMIUM)**
|
||||
- [GitLab Pages](pages/index.md): Build, test, and deploy your static
|
||||
website with GitLab Pages
|
||||
|
|
|
@ -1,289 +1,5 @@
|
|||
---
|
||||
stage: Release
|
||||
group: Progressive Delivery
|
||||
info: To determine the technical writer assigned to the Stage/Group associated with this page, see https://about.gitlab.com/handbook/engineering/ux/technical-writing/#designated-technical-writers
|
||||
redirect_to: '../../../operations/feature_flags.md'
|
||||
---
|
||||
|
||||
# Feature Flags **(PREMIUM)**
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/7433) in GitLab 11.4.
|
||||
|
||||
With Feature Flags, you can deploy your application's new features to production in smaller batches.
|
||||
You can toggle a feature on and off to subsets of users, helping you achieve Continuous Delivery.
|
||||
Feature flags help reduce risk, allowing you to do controlled testing, and separate feature
|
||||
delivery from customer launch.
|
||||
|
||||
<i class="fa fa-youtube-play youtube" aria-hidden="true"></i>
|
||||
For an example of feature flags in action, see [GitLab for Deploys, Feature Flags, and Error Tracking](https://www.youtube.com/embed/5tw2p6lwXxo).
|
||||
|
||||
## How it works
|
||||
|
||||
GitLab uses [Unleash](https://github.com/Unleash/unleash), a feature
|
||||
toggle service.
|
||||
|
||||
By enabling or disabling a flag in GitLab, your application
|
||||
can determine which features to enable or disable.
|
||||
|
||||
You can create feature flags in GitLab and use the API from your application
|
||||
to get the list of feature flags and their statuses. The application must be configured to communicate
|
||||
with GitLab, so it's up to developers to use a compatible client library and
|
||||
[integrate the feature flags in your app](#integrate-feature-flags-with-your-application).
|
||||
|
||||
## Create a feature flag
|
||||
|
||||
To create and enable a feature flag:
|
||||
|
||||
1. Navigate to your project's **Operations > Feature Flags**.
|
||||
1. Click the **New feature flag** button.
|
||||
1. Enter a name that starts with a letter and contains only lowercase letters, digits, underscores (`_`),
|
||||
or dashes (`-`), and does not end with a dash (`-`) or underscore (`_`).
|
||||
1. Enter a description (optional, 255 characters max).
|
||||
1. Enter details about how the flag should be applied:
|
||||
- In GitLab 13.0 and earlier, add **Environment specs**. For each environment,
|
||||
include the **Status** (default enabled) and [**Rollout strategy**](#rollout-strategy-legacy)
|
||||
(defaults to **All users**).
|
||||
- In GitLab 13.1 and later, add Feature Flag [**Strategies**](#feature-flag-strategies).
|
||||
For each strategy, include the **Type** (defaults to [**All users**](#all-users))
|
||||
and **Environments** (defaults to all environments).
|
||||
1. Click **Create feature flag**.
|
||||
|
||||
You can change these settings by clicking the **{pencil}** (edit) button
|
||||
next to any feature flag in the list.
|
||||
|
||||
## Rollout strategy (legacy)
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/8240) in GitLab 12.2.
|
||||
|
||||
In GitLab 13.0 and earlier, the **Rollout strategy** setting affects which users will experience
|
||||
the feature as enabled. Choose the percentage of users that the feature will be enabled
|
||||
for. The rollout strategy will have no effect if the environment spec is disabled.
|
||||
|
||||
It can be set to:
|
||||
|
||||
- All users
|
||||
- [Percent rollout (logged in users)](#percent-rollout-logged-in-users)
|
||||
- Optionally, you can click the **Include additional user IDs** checkbox and add a list
|
||||
of specific users IDs to enable the feature for.
|
||||
- [User IDs](#user-ids)
|
||||
|
||||
## Feature flag strategies
|
||||
|
||||
> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/35555) in GitLab 13.0.
|
||||
> - It's deployed behind a feature flag, disabled by default.
|
||||
> - It's enabled on GitLab.com.
|
||||
> - To use it in GitLab self-managed instances, ask a GitLab administrator to [enable it](#enable-or-disable-feature-flag-strategies). **(CORE ONLY)**
|
||||
|
||||
You can apply a feature flag strategy across multiple environments, without defining
|
||||
the strategy multiple times.
|
||||
|
||||
GitLab Feature Flags use [Unleash](https://unleash.github.io) as the feature flag
|
||||
engine. In Unleash, there are [strategies](https://unleash.github.io/docs/activation_strategy)
|
||||
for granular feature flag controls. GitLab Feature Flags can have multiple strategies,
|
||||
and the supported strategies are:
|
||||
|
||||
- [All users](#all-users)
|
||||
- [Percent rollout (logged in users)](#percent-rollout-logged-in-users)
|
||||
- [User IDs](#user-ids)
|
||||
- [List](#list)
|
||||
|
||||
Strategies can be added to feature flags when [creating a feature flag](#create-a-feature-flag),
|
||||
or by editing an existing feature flag after creation by navigating to **Operations > Feature Flags**
|
||||
and clicking **{pencil}** (edit).
|
||||
|
||||
### All users
|
||||
|
||||
Enables the feature for all users. It uses the [`default`](https://unleash.github.io/docs/activation_strategy#default)
|
||||
Unleash activation strategy.
|
||||
|
||||
### Percent rollout (logged in users)
|
||||
|
||||
Enables the feature for a percentage of authenticated users. It uses the
|
||||
[`gradualRolloutUserId`](https://unleash.github.io/docs/activation_strategy#gradualrolloutuserid)
|
||||
Unleash activation strategy.
|
||||
|
||||
For example, set a value of 15% to enable the feature for 15% of authenticated users.
|
||||
|
||||
The rollout percentage can be from 0% to 100%.
|
||||
|
||||
CAUTION: **Caution:**
|
||||
If this strategy is selected, then the Unleash client **must** be given a user
|
||||
ID for the feature to be enabled. See the [Ruby example](#ruby-application-example) below.
|
||||
|
||||
### User IDs
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/8240) in GitLab 12.2. [Updated](https://gitlab.com/gitlab-org/gitlab/-/issues/34363) to be defined per environment in GitLab 12.6.
|
||||
|
||||
Enables the feature for a list of target users. It is implemented
|
||||
using the Unleash [`userWithId`](https://unleash.github.io/docs/activation_strategy#userwithid)
|
||||
activation strategy.
|
||||
|
||||
Enter user IDs as a comma-separated list of values. For example,
|
||||
`user@example.com, user2@example.com`, or `username1,username2,username3`, and so on.
|
||||
|
||||
CAUTION: **Caution:**
|
||||
The Unleash client **must** be given a user ID for the feature to be enabled for
|
||||
target users. See the [Ruby example](#ruby-application-example) below.
|
||||
|
||||
### List
|
||||
|
||||
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/35930) in GitLab 13.1.
|
||||
|
||||
Enables the feature for lists of users created with the [Feature Flag User List API](../../../api/feature_flag_user_lists.md).
|
||||
Similar to [User IDs](#user-ids), it uses the Unleash [`userWithId`](https://unleash.github.io/docs/activation_strategy#userwithid)
|
||||
activation strategy.
|
||||
|
||||
### Enable or disable feature flag strategies
|
||||
|
||||
This feature is under development, but is ready for testing. It's
|
||||
deployed behind a feature flag that is **disabled by default**.
|
||||
[GitLab administrators with access to the GitLab Rails console](../../../administration/feature_flags.md)
|
||||
can enable it for your instance.
|
||||
|
||||
To enable it:
|
||||
|
||||
```ruby
|
||||
Feature.enable(:feature_flags_new_version)
|
||||
```
|
||||
|
||||
To disable it:
|
||||
|
||||
```ruby
|
||||
Feature.disable(:feature_flags_new_version)
|
||||
```
|
||||
|
||||
## Disable a feature flag for a specific environment
|
||||
|
||||
In [GitLab 13.0 and earlier](https://gitlab.com/gitlab-org/gitlab/-/issues/8621),
|
||||
to disable a feature flag for a specific environment:
|
||||
|
||||
1. Navigate to your project's **Operations > Feature Flags**.
|
||||
1. For the feature flag you want to disable, click the Pencil icon.
|
||||
1. To disable the flag:
|
||||
- In GitLab 13.0 and earlier: Slide the Status toggle for the environment. Or, to delete the
|
||||
environment spec, on the right, click the **Remove (X)** icon.
|
||||
- In GitLab 13.1 and later: For each strategy it applies to, under **Environments**, delete the environment.
|
||||
|
||||
1. Click **Save changes**.
|
||||
|
||||
## Disable a feature flag for all environments
|
||||
|
||||
To disable a feature flag for all environments:
|
||||
|
||||
1. Navigate to your project's **Operations > Feature Flags**.
|
||||
1. For the feature flag you want to disable, slide the Status toggle to **Disabled**.
|
||||
|
||||
The feature flag is displayed on the **Disabled** tab.
|
||||
|
||||
## Integrate feature flags with your application
|
||||
|
||||
To use feature flags with your application, get access credentials from GitLab.
|
||||
Then prepare your application with a client library.
|
||||
|
||||
### Get access credentials
|
||||
|
||||
To get the access credentials that your application needs to communicate with GitLab:
|
||||
|
||||
1. Navigate to your project's **Operations > Feature Flags**.
|
||||
1. Click the **Configure** button to view the following:
|
||||
- **API URL**: URL where the client (application) connects to get a list of feature flags.
|
||||
- **Instance ID**: Unique token that authorizes the retrieval of the feature flags.
|
||||
- **Application name**: The name of the running environment. For instance,
|
||||
if the application runs for a production server, the application name would be
|
||||
`production` or similar. This value is used for the environment spec evaluation.
|
||||
|
||||
NOTE: **Note:**
|
||||
The meaning of these fields might change over time. For example, we are not sure
|
||||
if **Instance ID** will be single token or multiple tokens, assigned to the
|
||||
**Environment**. Also, **Application name** could describe the version of
|
||||
application instead of the running environment.
|
||||
|
||||
### Choose a client library
|
||||
|
||||
GitLab implements a single backend that is compatible with Unleash clients.
|
||||
|
||||
With the Unleash client, developers can define, in the application code, the default values for flags.
|
||||
Each feature flag evaluation can express the desired outcome if the flag isn't present in the
|
||||
provided configuration file.
|
||||
|
||||
Unleash currently [offers many SDKs for various languages and frameworks](https://github.com/Unleash/unleash#client-implementations).
|
||||
|
||||
### Feature flags API information
|
||||
|
||||
For API content, see:
|
||||
|
||||
- [Feature Flags API](../../../api/feature_flags.md)
|
||||
- [Feature Flag Specs API](../../../api/feature_flag_specs.md) (Deprecated and [scheduled for removal in GitLab 14.0](https://gitlab.com/gitlab-org/gitlab/-/issues/213369).)
|
||||
- [Feature Flag User Lists API](../../../api/feature_flag_user_lists.md)
|
||||
- [Legacy Feature Flags API](../../../api/feature_flags_legacy.md)
|
||||
|
||||
### Golang application example
|
||||
|
||||
Here's an example of how to integrate feature flags in a Golang application:
|
||||
|
||||
```golang
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/Unleash/unleash-client-go"
|
||||
)
|
||||
|
||||
type metricsInterface struct {
|
||||
}
|
||||
|
||||
func init() {
|
||||
unleash.Initialize(
|
||||
unleash.WithUrl("https://gitlab.com/api/v4/feature_flags/unleash/42"),
|
||||
unleash.WithInstanceId("29QmjsW6KngPR5JNPMWx"),
|
||||
unleash.WithAppName("production"),
|
||||
unleash.WithListener(&metricsInterface{}),
|
||||
)
|
||||
}
|
||||
|
||||
func helloServer(w http.ResponseWriter, req *http.Request) {
|
||||
if unleash.IsEnabled("my_feature_name") {
|
||||
io.WriteString(w, "Feature enabled\n")
|
||||
} else {
|
||||
io.WriteString(w, "hello, world!\n")
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
http.HandleFunc("/", helloServer)
|
||||
log.Fatal(http.ListenAndServe(":8080", nil))
|
||||
}
|
||||
```
|
||||
|
||||
### Ruby application example
|
||||
|
||||
Here's an example of how to integrate feature flags in a Ruby application.
|
||||
|
||||
The Unleash client is given a user ID for use with a **Percent rollout (logged in users)** rollout strategy or a list of **Target Users**.
|
||||
|
||||
```ruby
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
require 'unleash'
|
||||
require 'unleash/context'
|
||||
|
||||
unleash = Unleash::Client.new({
|
||||
url: 'http://gitlab.com/api/v4/feature_flags/unleash/42',
|
||||
app_name: 'production',
|
||||
instance_id: '29QmjsW6KngPR5JNPMWx'
|
||||
})
|
||||
|
||||
unleash_context = Unleash::Context.new
|
||||
# Replace "123" with the id of an authenticated user.
|
||||
# Note that the context's user id must be a string:
|
||||
# https://unleash.github.io/docs/unleash_context
|
||||
unleash_context.user_id = "123"
|
||||
|
||||
if unleash.is_enabled?("my_feature_name", unleash_context)
|
||||
puts "Feature enabled"
|
||||
else
|
||||
puts "hello, world!"
|
||||
end
|
||||
```
|
||||
This document was moved to [another location](../../../operations/feature_flags.md).
|
||||
|
|
|
@ -1,13 +1,5 @@
|
|||
# Project operations
|
||||
---
|
||||
redirect_to: '../../../operations/index.md'
|
||||
---
|
||||
|
||||
GitLab provides a variety of tools to help operate and maintain
|
||||
your applications:
|
||||
|
||||
- Collect [Prometheus metrics](../integrations/prometheus_library/index.md).
|
||||
- Deploy to different [environments](../../../ci/environments/index.md).
|
||||
- Connect your project to a [Kubernetes cluster](../clusters/index.md).
|
||||
- Manage your infrastructure with [Infrastructure as Code](../../infrastructure/index.md) approaches.
|
||||
- Discover and view errors generated by your applications with [Error Tracking](error_tracking.md).
|
||||
- Create, toggle, and remove [Feature Flags](feature_flags.md). **(PREMIUM)**
|
||||
- [Trace](tracing.md) the performance and health of a deployed application. **(ULTIMATE)**
|
||||
- Change the [settings of the Monitoring Dashboard](dashboard_settings.md).
|
||||
This document was moved to [another location](../../../operations/index.md).
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
"prettier-staged-save": "node ./scripts/frontend/prettier.js save",
|
||||
"prettier-all": "node ./scripts/frontend/prettier.js check-all",
|
||||
"prettier-all-save": "node ./scripts/frontend/prettier.js save-all",
|
||||
"stylelint": "yarn stylelint-file app/assets/stylesheets/**/*.* ee/app/assets/stylesheets/**/*.* !**/vendors/**",
|
||||
"stylelint": "yarn stylelint-file 'app/assets/stylesheets/**/*.*' 'ee/app/assets/stylesheets/**/*.*' '!**/vendors/**'",
|
||||
"stylelint-file": "BROWSERSLIST_IGNORE_OLD_DATA=true node node_modules/stylelint/bin/stylelint.js",
|
||||
"stylelint-create-utility-map": "node scripts/frontend/stylelint/stylelint-utility-map.js",
|
||||
"test": "node scripts/frontend/test",
|
||||
|
|
|
@ -41,7 +41,7 @@ fi
|
|||
|
||||
# Do not use 'README.md', instead use 'index.md'
|
||||
# Number of 'README.md's as of 2020-05-28
|
||||
NUMBER_READMES=45
|
||||
NUMBER_READMES=44
|
||||
FIND_READMES=$(find doc/ -name "README.md" | wc -l)
|
||||
echo '=> Checking for new README.md files...'
|
||||
echo
|
||||
|
|
|
@ -99,6 +99,18 @@ RSpec.describe Projects::Integrations::Jira::IssuesFinder do
|
|||
subject
|
||||
end
|
||||
end
|
||||
|
||||
context 'when pagination params used' do
|
||||
let(:params) { { page: '10', per_page: '20' } }
|
||||
|
||||
it 'passes them to JqlBuilderService ' do
|
||||
expect(::Jira::JqlBuilderService).to receive(:new)
|
||||
.with(jira_service.project_key, include({ page: '10', per_page: '20' }))
|
||||
.and_call_original
|
||||
|
||||
subject
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -136,10 +136,6 @@ describe('Dashboard Panel', () => {
|
|||
expect(wrapper.find(MonitorEmptyChart).exists()).toBe(true);
|
||||
expect(wrapper.find(MonitorEmptyChart).isVueInstance()).toBe(true);
|
||||
});
|
||||
|
||||
it('does not contain a tabindex attribute', () => {
|
||||
expect(wrapper.find(MonitorEmptyChart).contains('[tabindex]')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('When graphData is null', () => {
|
||||
|
|
|
@ -42,14 +42,10 @@ describe('Graph group component', () => {
|
|||
});
|
||||
});
|
||||
|
||||
it('should contain a tabindex', () => {
|
||||
expect(findGroup().contains('[tabindex]')).toBe(true);
|
||||
});
|
||||
|
||||
it('should contain a tab index for the collapse button', () => {
|
||||
const groupToggle = findToggleButton();
|
||||
|
||||
expect(groupToggle.contains('[tabindex]')).toBe(true);
|
||||
expect(groupToggle.is('[tabindex]')).toBe(true);
|
||||
});
|
||||
|
||||
it('should show the open the group when collapseGroup is set to true', () => {
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
import Vue from 'vue';
|
||||
import { shallowMount } from '@vue/test-utils';
|
||||
import mountComponent, { mountComponentWithSlots } from 'helpers/vue_mount_component_helper';
|
||||
import reportSection from '~/reports/components/report_section.vue';
|
||||
|
||||
describe('Report section', () => {
|
||||
let vm;
|
||||
let wrapper;
|
||||
const ReportSection = Vue.extend(reportSection);
|
||||
|
||||
const resolvedIssues = [
|
||||
|
@ -16,22 +18,41 @@ describe('Report section', () => {
|
|||
},
|
||||
];
|
||||
|
||||
const defaultProps = {
|
||||
component: '',
|
||||
status: 'SUCCESS',
|
||||
loadingText: 'Loading codeclimate report',
|
||||
errorText: 'foo',
|
||||
successText: 'Code quality improved on 1 point and degraded on 1 point',
|
||||
resolvedIssues,
|
||||
hasIssues: false,
|
||||
alwaysOpen: false,
|
||||
};
|
||||
|
||||
const createComponent = props => {
|
||||
wrapper = shallowMount(reportSection, {
|
||||
propsData: {
|
||||
...defaultProps,
|
||||
...props,
|
||||
},
|
||||
});
|
||||
return wrapper;
|
||||
};
|
||||
|
||||
afterEach(() => {
|
||||
vm.$destroy();
|
||||
if (vm) {
|
||||
vm.$destroy();
|
||||
vm = null;
|
||||
}
|
||||
if (wrapper) {
|
||||
wrapper.destroy();
|
||||
wrapper = null;
|
||||
}
|
||||
});
|
||||
|
||||
describe('computed', () => {
|
||||
beforeEach(() => {
|
||||
vm = mountComponent(ReportSection, {
|
||||
component: '',
|
||||
status: 'SUCCESS',
|
||||
loadingText: 'Loading codeclimate report',
|
||||
errorText: 'foo',
|
||||
successText: 'Code quality improved on 1 point and degraded on 1 point',
|
||||
resolvedIssues,
|
||||
hasIssues: false,
|
||||
alwaysOpen: false,
|
||||
});
|
||||
vm = mountComponent(ReportSection, defaultProps);
|
||||
});
|
||||
|
||||
describe('isCollapsible', () => {
|
||||
|
@ -105,12 +126,7 @@ describe('Report section', () => {
|
|||
describe('with success status', () => {
|
||||
beforeEach(() => {
|
||||
vm = mountComponent(ReportSection, {
|
||||
component: '',
|
||||
status: 'SUCCESS',
|
||||
loadingText: 'Loading codeclimate report',
|
||||
errorText: 'foo',
|
||||
successText: 'Code quality improved on 1 point and degraded on 1 point',
|
||||
resolvedIssues,
|
||||
...defaultProps,
|
||||
hasIssues: true,
|
||||
});
|
||||
});
|
||||
|
@ -160,6 +176,50 @@ describe('Report section', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('snowplow events', () => {
|
||||
it('does emit an event on issue toggle if the shouldEmitToggleEvent prop does exist', done => {
|
||||
createComponent({ hasIssues: true, shouldEmitToggleEvent: true });
|
||||
|
||||
expect(wrapper.emitted().toggleEvent).toBeUndefined();
|
||||
|
||||
wrapper.vm.$el.querySelector('button').click();
|
||||
return wrapper.vm
|
||||
.$nextTick()
|
||||
.then(() => {
|
||||
expect(wrapper.emitted().toggleEvent).toHaveLength(1);
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('does not emit an event on issue toggle if the shouldEmitToggleEvent prop does not exist', done => {
|
||||
createComponent({ hasIssues: true });
|
||||
|
||||
expect(wrapper.emitted().toggleEvent).toBeUndefined();
|
||||
|
||||
wrapper.vm.$el.querySelector('button').click();
|
||||
return wrapper.vm
|
||||
.$nextTick()
|
||||
.then(() => {
|
||||
expect(wrapper.emitted().toggleEvent).toBeUndefined();
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it('does not emit an event if always-open is set to true', done => {
|
||||
createComponent({ alwaysOpen: true, hasIssues: true, shouldEmitToggleEvent: true });
|
||||
|
||||
wrapper.vm
|
||||
.$nextTick()
|
||||
.then(() => {
|
||||
expect(wrapper.emitted().toggleEvent).toBeUndefined();
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.fail);
|
||||
});
|
||||
});
|
||||
|
||||
describe('with failed request', () => {
|
||||
it('should render error indicator', () => {
|
||||
vm = mountComponent(ReportSection, {
|
||||
|
@ -199,7 +259,7 @@ describe('Report section', () => {
|
|||
});
|
||||
|
||||
describe('Success and Error slots', () => {
|
||||
const createComponent = status => {
|
||||
const createComponentWithSlots = status => {
|
||||
vm = mountComponentWithSlots(ReportSection, {
|
||||
props: {
|
||||
status,
|
||||
|
@ -214,7 +274,7 @@ describe('Report section', () => {
|
|||
};
|
||||
|
||||
it('only renders success slot when status is "SUCCESS"', () => {
|
||||
createComponent('SUCCESS');
|
||||
createComponentWithSlots('SUCCESS');
|
||||
|
||||
expect(vm.$el.textContent.trim()).toContain('This is a success');
|
||||
expect(vm.$el.textContent.trim()).not.toContain('This is an error');
|
||||
|
@ -222,7 +282,7 @@ describe('Report section', () => {
|
|||
});
|
||||
|
||||
it('only renders error slot when status is "ERROR"', () => {
|
||||
createComponent('ERROR');
|
||||
createComponentWithSlots('ERROR');
|
||||
|
||||
expect(vm.$el.textContent.trim()).toContain('This is an error');
|
||||
expect(vm.$el.textContent.trim()).not.toContain('This is a success');
|
||||
|
@ -230,7 +290,7 @@ describe('Report section', () => {
|
|||
});
|
||||
|
||||
it('only renders loading slot when status is "LOADING"', () => {
|
||||
createComponent('LOADING');
|
||||
createComponentWithSlots('LOADING');
|
||||
|
||||
expect(vm.$el.textContent.trim()).toContain('This is loading');
|
||||
expect(vm.$el.textContent.trim()).not.toContain('This is an error');
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
const buildMockTextNode = literal => {
|
||||
return {
|
||||
firstChild: null,
|
||||
literal,
|
||||
type: 'text',
|
||||
};
|
||||
};
|
||||
|
||||
const buildMockListNode = literal => {
|
||||
return {
|
||||
firstChild: {
|
||||
firstChild: {
|
||||
firstChild: buildMockTextNode(literal),
|
||||
type: 'paragraph',
|
||||
},
|
||||
type: 'item',
|
||||
},
|
||||
type: 'list',
|
||||
};
|
||||
};
|
||||
|
||||
export const buildMockParagraphNode = literal => {
|
||||
return {
|
||||
firstChild: buildMockTextNode(literal),
|
||||
type: 'paragraph',
|
||||
};
|
||||
};
|
||||
|
||||
export const kramdownListNode = buildMockListNode('TOC');
|
||||
export const normalListNode = buildMockListNode('Just another bullet point');
|
||||
|
||||
export const kramdownTextNode = buildMockTextNode('{:toc}');
|
||||
export const identifierTextNode = buildMockTextNode('[Some text]: https://link.com');
|
||||
export const embeddedRubyTextNode = buildMockTextNode('<%= partial("some/path") %>');
|
||||
export const normalTextNode = buildMockTextNode('This is just normal text.');
|
||||
export const normalParagraphNode = buildMockParagraphNode(
|
||||
'This is just normal paragraph. It has multiple sentences.',
|
||||
);
|
||||
|
||||
const uneditableOpenToken = {
|
||||
type: 'openTag',
|
||||
tagName: 'div',
|
||||
attributes: { contenteditable: false },
|
||||
classNames: [
|
||||
'gl-px-4 gl-py-2 gl-opacity-5 gl-bg-gray-100 gl-user-select-none gl-cursor-not-allowed',
|
||||
],
|
||||
};
|
||||
|
||||
export const uneditableCloseToken = { type: 'closeTag', tagName: 'div' };
|
||||
export const originToken = {
|
||||
type: 'text',
|
||||
content: '{:.no_toc .hidden-md .hidden-lg}',
|
||||
};
|
||||
export const uneditableOpenTokens = [uneditableOpenToken, originToken];
|
||||
export const uneditableCloseTokens = [originToken, uneditableCloseToken];
|
||||
export const uneditableTokens = [...uneditableOpenTokens, uneditableCloseToken];
|
|
@ -8,21 +8,7 @@ export const buildMockTextNode = literal => {
|
|||
};
|
||||
};
|
||||
|
||||
export const buildMockListNode = literal => {
|
||||
return {
|
||||
firstChild: {
|
||||
firstChild: {
|
||||
firstChild: buildMockTextNode(literal),
|
||||
type: 'paragraph',
|
||||
},
|
||||
type: 'item',
|
||||
},
|
||||
type: 'list',
|
||||
};
|
||||
};
|
||||
|
||||
export const normalTextNode = buildMockTextNode('This is just normal text.');
|
||||
export const normalListNode = buildMockListNode('Just another bullet point');
|
||||
|
||||
// Token spec helpers
|
||||
|
||||
|
|
|
@ -4,8 +4,18 @@ import {
|
|||
buildUneditableCloseToken,
|
||||
} from '~/vue_shared/components/rich_content_editor/services/renderers/build_uneditable_token';
|
||||
|
||||
import { buildMockParagraphNode, normalParagraphNode } from '../../mock_data';
|
||||
import { buildMockTextNode } from './mock_data';
|
||||
|
||||
const buildMockParagraphNode = literal => {
|
||||
return {
|
||||
firstChild: buildMockTextNode(literal),
|
||||
type: 'paragraph',
|
||||
};
|
||||
};
|
||||
|
||||
const normalParagraphNode = buildMockParagraphNode(
|
||||
'This is just normal paragraph. It has multiple sentences.',
|
||||
);
|
||||
const identifierParagraphNode = buildMockParagraphNode(
|
||||
`[another-identifier]: https://example.com "This example has a title" [identifier]: http://example1.com [this link]: http://example2.com`,
|
||||
);
|
||||
|
|
|
@ -4,8 +4,22 @@ import {
|
|||
buildUneditableCloseToken,
|
||||
} from '~/vue_shared/components/rich_content_editor/services/renderers/build_uneditable_token';
|
||||
|
||||
import { buildMockListNode, normalListNode } from './mock_data';
|
||||
import { buildMockTextNode } from './mock_data';
|
||||
|
||||
const buildMockListNode = literal => {
|
||||
return {
|
||||
firstChild: {
|
||||
firstChild: {
|
||||
firstChild: buildMockTextNode(literal),
|
||||
type: 'paragraph',
|
||||
},
|
||||
type: 'item',
|
||||
},
|
||||
type: 'list',
|
||||
};
|
||||
};
|
||||
|
||||
const normalListNode = buildMockListNode('Just another bullet point');
|
||||
const kramdownListNode = buildMockListNode('TOC');
|
||||
|
||||
describe('Render Kramdown List renderer', () => {
|
||||
|
|
|
@ -46,6 +46,10 @@ RSpec.describe AlertManagement::CreateAlertIssueService do
|
|||
|
||||
expect(alert.reload.issue_id).to eq(created_issue.id)
|
||||
end
|
||||
|
||||
it 'creates a system note' do
|
||||
expect { execute }.to change { alert.reload.notes.count }.by(1)
|
||||
end
|
||||
end
|
||||
|
||||
shared_examples 'setting an issue attributes' do
|
||||
|
|
|
@ -693,4 +693,16 @@ RSpec.describe SystemNoteService do
|
|||
described_class.change_alert_status(alert, author)
|
||||
end
|
||||
end
|
||||
|
||||
describe '.new_alert_issue' do
|
||||
let(:alert) { build(:alert_management_alert, :with_issue) }
|
||||
|
||||
it 'calls AlertManagementService' do
|
||||
expect_next_instance_of(SystemNotes::AlertManagementService) do |service|
|
||||
expect(service).to receive(:new_alert_issue).with(alert, alert.issue)
|
||||
end
|
||||
|
||||
described_class.new_alert_issue(alert, alert.issue, author)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -5,8 +5,7 @@ require 'spec_helper'
|
|||
RSpec.describe ::SystemNotes::AlertManagementService do
|
||||
let_it_be(:author) { create(:user) }
|
||||
let_it_be(:project) { create(:project, :repository) }
|
||||
|
||||
let(:noteable) { create(:alert_management_alert, :acknowledged, project: project) }
|
||||
let_it_be(:noteable) { create(:alert_management_alert, :with_issue, :acknowledged, project: project) }
|
||||
|
||||
describe '#change_alert_status' do
|
||||
subject { described_class.new(noteable: noteable, project: project, author: author).change_alert_status(noteable) }
|
||||
|
@ -19,4 +18,18 @@ RSpec.describe ::SystemNotes::AlertManagementService do
|
|||
expect(subject.note).to eq("changed the status to **Acknowledged**")
|
||||
end
|
||||
end
|
||||
|
||||
describe '#new_alert_issue' do
|
||||
let_it_be(:issue) { noteable.issue }
|
||||
|
||||
subject { described_class.new(noteable: noteable, project: project, author: author).new_alert_issue(noteable, issue) }
|
||||
|
||||
it_behaves_like 'a system note' do
|
||||
let(:action) { 'alert_issue_added' }
|
||||
end
|
||||
|
||||
it 'has the appropriate message' do
|
||||
expect(subject.note).to eq("created issue #{issue.to_reference(project)} for this alert")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue