summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.yml8
-rw-r--r--Gemfile7
-rw-r--r--Gemfile.lock4
-rw-r--r--app/assets/images/none-scheme-preview.pngbin0 -> 5971 bytes
-rw-r--r--app/assets/javascripts/monitoring/utils/multiple_time_series.js32
-rw-r--r--app/assets/javascripts/notes/components/noteable_discussion.vue8
-rw-r--r--app/assets/javascripts/notes/components/noteable_note.vue25
-rw-r--r--app/assets/stylesheets/application.scss1
-rw-r--r--app/assets/stylesheets/highlight/none.scss242
-rw-r--r--app/assets/stylesheets/pages/merge_conflicts.scss24
-rw-r--r--app/assets/stylesheets/pages/notes.scss10
-rw-r--r--app/controllers/concerns/service_params.rb6
-rw-r--r--app/controllers/projects/jobs_controller.rb2
-rw-r--r--app/finders/concerns/finder_with_cross_project_access.rb30
-rw-r--r--app/finders/events_finder.rb45
-rw-r--r--app/models/prometheus_metric.rb7
-rw-r--r--app/services/groups/update_service.rb2
-rw-r--r--app/services/issuable/common_system_notes_service.rb21
-rw-r--r--app/services/issuable_base_service.rb6
-rw-r--r--app/services/merge_requests/update_service.rb9
-rw-r--r--app/services/users/update_service.rb10
-rw-r--r--app/views/projects/_home_panel.html.haml4
-rw-r--r--changelogs/unreleased/51485-new-issue-labels-note.yml5
-rw-r--r--changelogs/unreleased/53954-resolved-non-diff-discussions-on-merge-requests-no-longer-show-who-resolved-them-and-when-at-a-glance.yml5
-rw-r--r--changelogs/unreleased/add-new-nginx-metrics.yml5
-rw-r--r--changelogs/unreleased/blackst0ne-improve-encoding-helper-spec.yml5
-rw-r--r--changelogs/unreleased/bvl-hide-confidential-events-take2.yml5
-rw-r--r--changelogs/unreleased/none-syntax-highlighting.yml5
-rw-r--r--changelogs/unreleased/sh-carrierwave-patch-google-acl.yml5
-rw-r--r--config/initializers/carrierwave_patch.rb13
-rw-r--r--config/initializers/sidekiq.rb4
-rw-r--r--config/prometheus/common_metrics.yml48
-rw-r--r--db/importers/common_metrics_importer.rb4
-rw-r--r--db/migrate/20181006004100_import_common_metrics_nginx_vts.rb15
-rw-r--r--doc/administration/uploads.md52
-rw-r--r--doc/api/suggestions.md36
-rw-r--r--doc/ci/yaml/README.md2
-rw-r--r--doc/topics/autodevops/index.md2
-rw-r--r--doc/user/discussions/img/insert_suggestion.pngbin0 -> 19913 bytes
-rw-r--r--doc/user/discussions/img/make_suggestion.pngbin0 -> 28447 bytes
-rw-r--r--doc/user/discussions/img/suggestion.pngbin0 -> 39775 bytes
-rw-r--r--doc/user/discussions/index.md45
-rw-r--r--doc/user/group/clusters/index.md8
-rw-r--r--doc/user/markdown.md77
-rw-r--r--doc/user/permissions.md1
-rw-r--r--doc/user/project/cycle_analytics.md3
-rw-r--r--doc/user/project/merge_requests/index.md10
-rw-r--r--lib/api/events.rb42
-rw-r--r--lib/gitlab/color_schemes.rb3
-rw-r--r--lib/gitlab/safe_request_store.rb8
-rw-r--r--locale/gitlab.pot3
-rw-r--r--qa/qa/specs/features/browser_ui/2_plan/issue/issue_suggestions_spec.rb35
-rwxr-xr-xscripts/review_apps/review-apps.sh4
-rwxr-xr-xscripts/trigger-build8
-rw-r--r--spec/finders/concerns/finder_with_cross_project_access_spec.rb16
-rw-r--r--spec/finders/events_finder_spec.rb17
-rw-r--r--spec/finders/user_recent_events_finder_spec.rb3
-rw-r--r--spec/lib/gitlab/encoding_helper_spec.rb2
-rw-r--r--spec/lib/gitlab/gpg_spec.rb2
-rw-r--r--spec/lib/gitlab/safe_request_store_spec.rb12
-rw-r--r--spec/models/concerns/chronic_duration_attribute_spec.rb4
-rw-r--r--spec/models/merge_request_spec.rb2
-rw-r--r--spec/requests/api/events_spec.rb62
-rw-r--r--spec/requests/api/redacted_events_spec.rb68
-rw-r--r--spec/services/issuable/common_system_notes_service_spec.rb47
-rw-r--r--spec/support/shared_examples/common_system_notes_examples.rb4
66 files changed, 946 insertions, 254 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 2c3d18d21be..3d12f4142ba 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -231,10 +231,16 @@ package-and-qa:
<<: *single-script-job
variables:
<<: *single-script-job-variables
+ API_TOKEN: "${GITLAB_BOT_MULTI_PROJECT_PIPELINE_POLLING_TOKEN}"
SCRIPT_NAME: trigger-build
retry: 0
script:
- gem install gitlab --no-document
+ - apk add --update openssl curl jq
+ - wget $CI_PROJECT_URL/raw/$CI_COMMIT_SHA/scripts/review_apps/review-apps.sh
+ - chmod 755 review-apps.sh
+ - source ./review-apps.sh
+ - wait_for_job_to_be_done "gitlab:assets:compile"
- ./$SCRIPT_NAME omnibus
when: manual
only:
@@ -600,7 +606,7 @@ gitlab:setup-mysql:
# Frontend-related jobs
gitlab:assets:compile:
- <<: *dedicated-no-docs-and-no-qa-pull-cache-job
+ <<: *dedicated-no-docs-pull-cache-job
image: dev.gitlab.org:5005/gitlab/gitlab-build-images:ruby-2.5.3-git-2.18-chrome-69.0-node-8.x-yarn-1.12-graphicsmagick-1.3.29-docker-18.06.1
dependencies: []
services:
diff --git a/Gemfile b/Gemfile
index ad7ba1122fd..6ada5eede27 100644
--- a/Gemfile
+++ b/Gemfile
@@ -89,8 +89,9 @@ gem 'kaminari', '~> 1.0'
gem 'hamlit', '~> 2.8.8'
# Files attachments
-# Locked until https://github.com/carrierwaveuploader/carrierwave/pull/2332/files is merged.
-# config/initializers/carrierwave_patch.rb can be removed once that change is released.
+# Locked until https://github.com/carrierwaveuploader/carrierwave/pull/2332 and
+# https://github.com/carrierwaveuploader/carrierwave/pull/2356 are merged.
+# config/initializers/carrierwave_patch.rb can be removed once both changes are released.
gem 'carrierwave', '= 1.2.3'
gem 'mini_magick'
@@ -164,6 +165,7 @@ gem 'acts-as-taggable-on', '~> 5.0'
gem 'sidekiq', '~> 5.2.1'
gem 'sidekiq-cron', '~> 0.6.0'
gem 'redis-namespace', '~> 1.6.0'
+gem 'gitlab-sidekiq-fetcher', '~> 0.1.0', require: 'sidekiq-reliable-fetch'
# Cron Parser
gem 'rufus-scheduler', '~> 3.4'
@@ -294,7 +296,6 @@ gem 'peek-mysql2', '~> 1.2.0', group: :mysql
gem 'peek-pg', '~> 1.3.0', group: :postgres
gem 'peek-rblineprof', '~> 0.2.0'
gem 'peek-redis', '~> 1.2.0'
-gem 'gitlab-sidekiq-fetcher', require: 'sidekiq-reliable-fetch'
# Metrics
group :metrics do
diff --git a/Gemfile.lock b/Gemfile.lock
index 312ad95cd1e..6d21089c82c 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -280,7 +280,7 @@ GEM
gitlab-default_value_for (3.1.1)
activerecord (>= 3.2.0, < 6.0)
gitlab-markup (1.6.5)
- gitlab-sidekiq-fetcher (0.3.0)
+ gitlab-sidekiq-fetcher (0.1.0)
sidekiq (~> 5)
gitlab-styles (2.4.1)
rubocop (~> 0.54.0)
@@ -1012,7 +1012,7 @@ DEPENDENCIES
github-markup (~> 1.7.0)
gitlab-default_value_for (~> 3.1.1)
gitlab-markup (~> 1.6.5)
- gitlab-sidekiq-fetcher
+ gitlab-sidekiq-fetcher (~> 0.1.0)
gitlab-styles (~> 2.4)
gitlab_omniauth-ldap (~> 2.0.4)
gon (~> 6.2)
diff --git a/app/assets/images/none-scheme-preview.png b/app/assets/images/none-scheme-preview.png
new file mode 100644
index 00000000000..2eb6bf96671
--- /dev/null
+++ b/app/assets/images/none-scheme-preview.png
Binary files differ
diff --git a/app/assets/javascripts/monitoring/utils/multiple_time_series.js b/app/assets/javascripts/monitoring/utils/multiple_time_series.js
index bb24a1acdb3..50ba14dfb2e 100644
--- a/app/assets/javascripts/monitoring/utils/multiple_time_series.js
+++ b/app/assets/javascripts/monitoring/utils/multiple_time_series.js
@@ -92,7 +92,11 @@ function queryTimeSeries(query, graphDrawData, lineStyle) {
if (seriesCustomizationData) {
metricTag = seriesCustomizationData.value || timeSeriesMetricLabel;
[lineColor, areaColor] = pickColor(seriesCustomizationData.color);
- shouldRenderLegend = false;
+ if (timeSeriesParsed.length > 0) {
+ shouldRenderLegend = false;
+ } else {
+ shouldRenderLegend = true;
+ }
} else {
metricTag = timeSeriesMetricLabel || query.label || `series ${timeSeriesNumber + 1}`;
[lineColor, areaColor] = pickColor();
@@ -101,19 +105,6 @@ function queryTimeSeries(query, graphDrawData, lineStyle) {
}
}
- if (!shouldRenderLegend) {
- if (!timeSeriesParsed[0].tracksLegend) {
- timeSeriesParsed[0].tracksLegend = [];
- }
- timeSeriesParsed[0].tracksLegend.push({
- max: maximumValue,
- average: accum / timeSeries.values.length,
- lineStyle,
- lineColor,
- metricTag,
- });
- }
-
const values = datesWithoutGaps.map(time => ({
time,
value: findByDate(timeSeries.values, time),
@@ -135,6 +126,19 @@ function queryTimeSeries(query, graphDrawData, lineStyle) {
shouldRenderLegend,
renderCanary,
});
+
+ if (!shouldRenderLegend) {
+ if (!timeSeriesParsed[0].tracksLegend) {
+ timeSeriesParsed[0].tracksLegend = [];
+ }
+ timeSeriesParsed[0].tracksLegend.push({
+ max: maximumValue,
+ average: accum / timeSeries.values.length,
+ lineStyle,
+ lineColor,
+ metricTag,
+ });
+ }
});
return timeSeriesParsed;
diff --git a/app/assets/javascripts/notes/components/noteable_discussion.vue b/app/assets/javascripts/notes/components/noteable_discussion.vue
index 07c938a0021..d9dd08a7a6b 100644
--- a/app/assets/javascripts/notes/components/noteable_discussion.vue
+++ b/app/assets/javascripts/notes/components/noteable_discussion.vue
@@ -378,6 +378,14 @@ Please check your network connection and try again.`;
:help-page-path="helpPagePath"
@handleDeleteNote="deleteNoteHandler"
>
+ <note-edited-text
+ v-if="discussion.resolved"
+ slot="discussion-resolved-text"
+ :edited-at="discussion.resolved_at"
+ :edited-by="discussion.resolved_by"
+ :action-text="resolvedText"
+ class-name="discussion-headline-light js-discussion-headline discussion-resolved-text"
+ />
<slot slot="avatar-badge" name="avatar-badge"></slot>
</component>
<toggle-replies-widget
diff --git a/app/assets/javascripts/notes/components/noteable_note.vue b/app/assets/javascripts/notes/components/noteable_note.vue
index 57e9c40bd61..4c02588127e 100644
--- a/app/assets/javascripts/notes/components/noteable_note.vue
+++ b/app/assets/javascripts/notes/components/noteable_note.vue
@@ -195,7 +195,7 @@ export default {
:img-alt="author.name"
:img-size="40"
>
- <slot slot="avatar-badge" name="avatar-badge"> </slot>
+ <slot slot="avatar-badge" name="avatar-badge"></slot>
</user-avatar-link>
</div>
<div class="timeline-content">
@@ -227,16 +227,19 @@ export default {
@handleResolve="resolveHandler"
/>
</div>
- <note-body
- ref="noteBody"
- :note="note"
- :line="line"
- :can-edit="note.current_user.can_edit"
- :is-editing="isEditing"
- :help-page-path="helpPagePath"
- @handleFormUpdate="formUpdateHandler"
- @cancelForm="formCancelHandler"
- />
+ <div class="timeline-discussion-body">
+ <slot name="discussion-resolved-text"></slot>
+ <note-body
+ ref="noteBody"
+ :note="note"
+ :line="line"
+ :can-edit="note.current_user.can_edit"
+ :is-editing="isEditing"
+ :help-page-path="helpPagePath"
+ @handleFormUpdate="formUpdateHandler"
+ @cancelForm="formCancelHandler"
+ />
+ </div>
</div>
</timeline-entry-item>
</template>
diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss
index 985fac11c87..bdf20866197 100644
--- a/app/assets/stylesheets/application.scss
+++ b/app/assets/stylesheets/application.scss
@@ -47,6 +47,7 @@
@import "highlight/solarized_dark";
@import "highlight/solarized_light";
@import "highlight/white";
+@import "highlight/none";
/*
* Styles for JS behaviors.
diff --git a/app/assets/stylesheets/highlight/none.scss b/app/assets/stylesheets/highlight/none.scss
new file mode 100644
index 00000000000..7d692a87e33
--- /dev/null
+++ b/app/assets/stylesheets/highlight/none.scss
@@ -0,0 +1,242 @@
+/*
+* None Syntax Colors
+*/
+
+
+
+@mixin matchLine {
+ color: $black-transparent;
+ background-color: $white-normal;
+}
+
+.code.none {
+ // Line numbers
+ .line-numbers,
+ .diff-line-num {
+ background-color: $gray-light;
+ }
+
+ .diff-line-num,
+ .diff-line-num a {
+ color: $black-transparent;
+ }
+
+ // Code itself
+ pre.code,
+ .diff-line-num {
+ border-color: $white-normal;
+ }
+
+ &,
+ pre.code,
+ .line_holder .line_content {
+ background-color: $white-light;
+ color: $gl-text-color;
+ }
+
+// Diff line
+
+ $none-over-bg: #ded7fc;
+ $none-expanded-border: #e0e0e0;
+ $none-expanded-bg: #f7f7f7;
+
+ .line_holder {
+
+ &.match .line_content,
+ .new-nonewline.line_content,
+ .old-nonewline.line_content {
+ @include matchLine;
+ }
+
+ .diff-line-num {
+ &.old {
+ background-color: $line-number-old;
+ border-color: $line-removed-dark;
+
+ a {
+ color: scale-color($line-number-old, $red: -30%, $green: -30%, $blue: -30%);
+ }
+ }
+
+ &.new {
+ background-color: $line-number-new;
+ border-color: $line-added-dark;
+
+ a {
+ color: scale-color($line-number-new, $red: -30%, $green: -30%, $blue: -30%);
+ }
+ }
+
+ &.is-over,
+ &.hll:not(.empty-cell).is-over {
+ background-color: $none-over-bg;
+ border-color: darken($none-over-bg, 5%);
+
+ a {
+ color: darken($none-over-bg, 15%);
+ }
+ }
+
+ &.hll:not(.empty-cell) {
+ background-color: $line-number-select;
+ border-color: $line-select-yellow-dark;
+ }
+ }
+
+ &:not(.diff-expanded) + .diff-expanded,
+ &.diff-expanded + .line_holder:not(.diff-expanded) {
+ > .diff-line-num,
+ > .line_content {
+ border-top: 1px solid $none-expanded-border;
+ }
+ }
+
+ &.diff-expanded {
+ > .diff-line-num,
+ > .line_content {
+ background: $none-expanded-bg;
+ border-color: $none-expanded-bg;
+ }
+ }
+
+ .line_content {
+ &.old {
+ background-color: $line-removed;
+
+ &::before {
+ color: scale-color($line-number-old, $red: -30%, $green: -30%, $blue: -30%);
+ }
+
+ span.idiff {
+ background-color: $line-removed-dark;
+ }
+ }
+
+ &.new {
+ background-color: $line-added;
+
+ &::before {
+ color: scale-color($line-number-new, $red: -30%, $green: -30%, $blue: -30%);
+ }
+
+ span.idiff {
+ background-color: $line-added-dark;
+ }
+ }
+
+ &.match {
+ @include matchLine;
+ }
+
+ &.hll:not(.empty-cell) {
+ background-color: $line-select-yellow;
+ }
+ }
+ }
+
+ // highlight line via anchor
+ pre .hll {
+ background-color: $white-normal;
+ }
+
+ // Search result highlight
+ span.highlight_word {
+ background-color: $white-normal;
+ }
+
+ // Links to URLs, emails, or dependencies
+ .line a {
+ color: $gl-text-color;
+ text-decoration: underline;
+ }
+
+ .hll { background-color: $white-light; }
+
+ .gd {
+ color: $gl-text-color;
+ background-color: $white-light;
+
+ .x {
+ color: $gl-text-color;
+ background-color: $white-light;
+ }
+ }
+
+ .gi {
+ color: $gl-text-color;
+ background-color: $white-light;
+
+ .x {
+ color: $gl-text-color;
+ background-color: $white-light;
+ }
+ }
+
+ .c { color: $gl-text-color; } /* Comment */
+ .err { color: $gl-text-color; } /* Error */
+ .g { color: $gl-text-color; } /* Generic */
+ .k { color: $gl-text-color; } /* Keyword */
+ .l { color: $gl-text-color; } /* Literal */
+ .n { color: $gl-text-color; } /* Name */
+ .o { color: $gl-text-color; } /* Operator */
+ .x { color: $gl-text-color; } /* Other */
+ .p { color: $gl-text-color; } /* Punctuation */
+ .cm { color: $gl-text-color; } /* Comment.Multiline */
+ .cp { color: $gl-text-color; } /* Comment.Preproc */
+ .c1 { color: $gl-text-color; } /* Comment.Single */
+ .cs { color: $gl-text-color; } /* Comment.Special */
+ .ge { color: $gl-text-color; } /* Generic.Emph */
+ .gr { color: $gl-text-color; } /* Generic.Error */
+ .gh { color: $gl-text-color; } /* Generic.Heading */
+ .go { color: $gl-text-color; } /* Generic.Output */
+ .gp { color: $gl-text-color; } /* Generic.Prompt */
+ .gs { color: $gl-text-color; } /* Generic.Strong */
+ .gu { color: $gl-text-color; } /* Generic.Subheading */
+ .gt { color: $gl-text-color; } /* Generic.Traceback */
+ .kc { color: $gl-text-color; } /* Keyword.Constant */
+ .kd { color: $gl-text-color; } /* Keyword.Declaration */
+ .kn { color: $gl-text-color; } /* Keyword.Namespace */
+ .kp { color: $gl-text-color; } /* Keyword.Pseudo */
+ .kr { color: $gl-text-color; } /* Keyword.Reserved */
+ .kt { color: $gl-text-color; } /* Keyword.Type */
+ .ld { color: $gl-text-color; } /* Literal.Date */
+ .m { color: $gl-text-color; } /* Literal.Number */
+ .s { color: $gl-text-color; } /* Literal.String */
+ .na { color: $gl-text-color; } /* Name.Attribute */
+ .nb { color: $gl-text-color; } /* Name.Builtin */
+ .nc { color: $gl-text-color; } /* Name.Class */
+ .no { color: $gl-text-color; } /* Name.Constant */
+ .nd { color: $gl-text-color; } /* Name.Decorator */
+ .ni { color: $gl-text-color; } /* Name.Entity */
+ .ne { color: $gl-text-color; } /* Name.Exception */
+ .nf { color: $gl-text-color; } /* Name.Function */
+ .nl { color: $gl-text-color; } /* Name.Label */
+ .nn { color: $gl-text-color; } /* Name.Namespace */
+ .nx { color: $gl-text-color; } /* Name.Other */
+ .py { color: $gl-text-color; } /* Name.Property */
+ .nt { color: $gl-text-color; } /* Name.Tag */
+ .nv { color: $gl-text-color; } /* Name.Variable */
+ .ow { color: $gl-text-color; } /* Operator.Word */
+ .w { color: $gl-text-color; } /* Text.Whitespace */
+ .mf { color: $gl-text-color; } /* Literal.Number.Float */
+ .mh { color: $gl-text-color; } /* Literal.Number.Hex */
+ .mi { color: $gl-text-color; } /* Literal.Number.Integer */
+ .mo { color: $gl-text-color; } /* Literal.Number.Oct */
+ .sb { color: $gl-text-color; } /* Literal.String.Backtick */
+ .sc { color: $gl-text-color; } /* Literal.String.Char */
+ .sd { color: $gl-text-color; } /* Literal.String.Doc */
+ .s2 { color: $gl-text-color; } /* Literal.String.Double */
+ .se { color: $gl-text-color; } /* Literal.String.Escape */
+ .sh { color: $gl-text-color; } /* Literal.String.Heredoc */
+ .si { color: $gl-text-color; } /* Literal.String.Interpol */
+ .sx { color: $gl-text-color; } /* Literal.String.Other */
+ .sr { color: $gl-text-color; } /* Literal.String.Regex */
+ .s1 { color: $gl-text-color; } /* Literal.String.Single */
+ .ss { color: $gl-text-color; } /* Literal.String.Symbol */
+ .bp { color: $gl-text-color; } /* Name.Builtin.Pseudo */
+ .vc { color: $gl-text-color; } /* Name.Variable.Class */
+ .vg { color: $gl-text-color; } /* Name.Variable.Global */
+ .vi { color: $gl-text-color; } /* Name.Variable.Instance */
+ .il { color: $gl-text-color; } /* Literal.Number.Integer.Long */
+
+}
diff --git a/app/assets/stylesheets/pages/merge_conflicts.scss b/app/assets/stylesheets/pages/merge_conflicts.scss
index d26659701e1..e0f7d075fc7 100644
--- a/app/assets/stylesheets/pages/merge_conflicts.scss
+++ b/app/assets/stylesheets/pages/merge_conflicts.scss
@@ -93,8 +93,28 @@ $colors: (
solarized-dark-line-origin-chosen : rgba(#2878c9, .35),
solarized-dark-button-origin-chosen : #0082cc,
- solarized-dark-header-not-chosen : rgba(#839496, .25),
- solarized-dark-line-not-chosen : rgba(#839496, .15)
+ solarized_dark_header_not_chosen : rgba(#839496, .25),
+ solarized_dark_line_not_chosen : rgba(#839496, .15),
+
+ none_header_head_neutral : $gray-normal,
+ none_line_head_neutral : $gray-normal,
+ none_button_head_neutral : $gray-normal,
+
+ none_header_head_chosen : $gray-darker,
+ none_line_head_chosen : $gray-darker,
+ none_button_head_chosen : $gray-darker,
+
+ none_header_origin_neutral : $gray-normal,
+ none_line_origin_neutral : $gray-normal,
+ none_button_origin_neutral : $gray-normal,
+
+ none_header_origin_chosen : $gray-darker,
+ none_line_origin_chosen : $gray-darker,
+ none_button_origin_chosen : $gray-darker,
+
+ none_header_not_chosen : $gray-light,
+ none_line_not_chosen : $gray-light
+
);
// scss-lint:enable ColorVariable
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index 2adfa0d312e..a5b1eff3e1d 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -152,6 +152,16 @@ $note-form-margin-left: 72px;
display: block;
position: relative;
+ .timeline-discussion-body {
+ margin-top: -8px;
+ overflow-x: auto;
+ overflow-y: hidden;
+
+ .discussion-resolved-text {
+ margin-bottom: 8px;
+ }
+ }
+
.diff-content {
overflow: visible;
padding: 0;
diff --git a/app/controllers/concerns/service_params.rb b/app/controllers/concerns/service_params.rb
index 8bd93a349ef..c6ae4fe15bf 100644
--- a/app/controllers/concerns/service_params.rb
+++ b/app/controllers/concerns/service_params.rb
@@ -70,7 +70,7 @@ module ServiceParams
def service_params
dynamic_params = @service.event_channel_names + @service.event_names # rubocop:disable Gitlab/ModuleWithInstanceVariables
- service_params = params.permit(:id, service: ALLOWED_PARAMS_CE + dynamic_params)
+ service_params = params.permit(:id, service: allowed_service_params + dynamic_params)
if service_params[:service].is_a?(Hash)
FILTER_BLANK_PARAMS.each do |param|
@@ -80,4 +80,8 @@ module ServiceParams
service_params
end
+
+ def allowed_service_params
+ ALLOWED_PARAMS_CE
+ end
end
diff --git a/app/controllers/projects/jobs_controller.rb b/app/controllers/projects/jobs_controller.rb
index c58b30eace7..bfbbcba883f 100644
--- a/app/controllers/projects/jobs_controller.rb
+++ b/app/controllers/projects/jobs_controller.rb
@@ -9,7 +9,7 @@ class Projects::JobsController < Projects::ApplicationController
before_action :authorize_update_build!,
except: [:index, :show, :status, :raw, :trace, :cancel_all, :erase]
before_action :authorize_erase_build!, only: [:erase]
- before_action :authorize_use_build_terminal!, only: [:terminal, :terminal_workhorse_authorize]
+ before_action :authorize_use_build_terminal!, only: [:terminal, :terminal_websocket_authorize]
before_action :verify_api_request!, only: :terminal_websocket_authorize
layout 'project'
diff --git a/app/finders/concerns/finder_with_cross_project_access.rb b/app/finders/concerns/finder_with_cross_project_access.rb
index 220f62bcc7f..06ebb286086 100644
--- a/app/finders/concerns/finder_with_cross_project_access.rb
+++ b/app/finders/concerns/finder_with_cross_project_access.rb
@@ -5,7 +5,8 @@
#
# This module depends on the finder implementing the following methods:
#
-# - `#execute` should return an `ActiveRecord::Relation`
+# - `#execute` should return an `ActiveRecord::Relation` or the `model` needs to
+# be defined in the call to `requires_cross_project_access`.
# - `#current_user` the user that requires access (or nil)
module FinderWithCrossProjectAccess
extend ActiveSupport::Concern
@@ -13,20 +14,35 @@ module FinderWithCrossProjectAccess
prepended do
extend Gitlab::CrossProjectAccess::ClassMethods
+
+ cattr_accessor :finder_model
+
+ def self.requires_cross_project_access(*args)
+ super
+
+ self.finder_model = extract_model_from_arguments(args)
+ end
+
+ private
+
+ def self.extract_model_from_arguments(args)
+ args.detect { |argument| argument.is_a?(Hash) && argument[:model] }
+ &.fetch(:model)
+ end
end
override :execute
def execute(*args)
check = Gitlab::CrossProjectAccess.find_check(self)
- original = super
+ original = -> { super }
- return original unless check
- return original if should_skip_cross_project_check || can_read_cross_project?
+ return original.call unless check
+ return original.call if should_skip_cross_project_check || can_read_cross_project?
if check.should_run?(self)
- original.model.none
+ finder_model&.none || original.call.model.none
else
- original
+ original.call
end
end
@@ -48,8 +64,6 @@ module FinderWithCrossProjectAccess
skip_cross_project_check { super }
end
- private
-
attr_accessor :should_skip_cross_project_check
def skip_cross_project_check
diff --git a/app/finders/events_finder.rb b/app/finders/events_finder.rb
index 8df01f1dad9..234b7090fd9 100644
--- a/app/finders/events_finder.rb
+++ b/app/finders/events_finder.rb
@@ -3,22 +3,27 @@
class EventsFinder
prepend FinderMethods
prepend FinderWithCrossProjectAccess
+
+ MAX_PER_PAGE = 100
+
attr_reader :source, :params, :current_user
- requires_cross_project_access unless: -> { source.is_a?(Project) }
+ requires_cross_project_access unless: -> { source.is_a?(Project) }, model: Event
# Used to filter Events
#
# Arguments:
# source - which user or project to looks for events on
# current_user - only return events for projects visible to this user
- # WARNING: does not consider project feature visibility!
# params:
# action: string
# target_type: string
# before: datetime
# after: datetime
- #
+ # per_page: integer (max. 100)
+ # page: integer
+ # with_associations: boolean
+ # sort: 'asc' or 'desc'
def initialize(params = {})
@source = params.delete(:source)
@current_user = params.delete(:current_user)
@@ -33,15 +38,18 @@ class EventsFinder
events = by_target_type(events)
events = by_created_at_before(events)
events = by_created_at_after(events)
+ events = sort(events)
+
+ events = events.with_associations if params[:with_associations]
- events
+ paginated_filtered_by_user_visibility(events)
end
private
# rubocop: disable CodeReuse/ActiveRecord
def by_current_user_access(events)
- events.merge(ProjectsFinder.new(current_user: current_user).execute) # rubocop: disable CodeReuse/Finder
+ events.merge(Project.public_or_visible_to_user(current_user))
.joins(:project)
end
# rubocop: enable CodeReuse/ActiveRecord
@@ -77,4 +85,31 @@ class EventsFinder
events.where('events.created_at > ?', params[:after].end_of_day)
end
# rubocop: enable CodeReuse/ActiveRecord
+
+ def sort(events)
+ return events unless params[:sort]
+
+ if params[:sort] == 'asc'
+ events.order_id_asc
+ else
+ events.order_id_desc
+ end
+ end
+
+ def paginated_filtered_by_user_visibility(events)
+ limited_events = events.page(page).per(per_page)
+ visible_events = limited_events.select { |event| event.visible_to_user?(current_user) }
+
+ Kaminari.paginate_array(visible_events, total_count: events.count)
+ end
+
+ def per_page
+ return MAX_PER_PAGE unless params[:per_page]
+
+ [params[:per_page], MAX_PER_PAGE].min
+ end
+
+ def page
+ params[:page] || 1
+ end
end
diff --git a/app/models/prometheus_metric.rb b/app/models/prometheus_metric.rb
index ce2db9cb44c..defbade1ed6 100644
--- a/app/models/prometheus_metric.rb
+++ b/app/models/prometheus_metric.rb
@@ -5,11 +5,12 @@ class PrometheusMetric < ActiveRecord::Base
enum group: {
# built-in groups
- nginx_ingress: -1,
+ nginx_ingress_vts: -1,
ha_proxy: -2,
aws_elb: -3,
nginx: -4,
kubernetes: -5,
+ nginx_ingress: -6,
# custom/user groups
business: 0,
@@ -30,6 +31,7 @@ class PrometheusMetric < ActiveRecord::Base
GROUP_TITLES = {
# built-in groups
+ nginx_ingress_vts: _('Response metrics (NGINX Ingress VTS)'),
nginx_ingress: _('Response metrics (NGINX Ingress)'),
ha_proxy: _('Response metrics (HA Proxy)'),
aws_elb: _('Response metrics (AWS ELB)'),
@@ -43,7 +45,8 @@ class PrometheusMetric < ActiveRecord::Base
}.freeze
REQUIRED_METRICS = {
- nginx_ingress: %w(nginx_upstream_responses_total nginx_upstream_response_msecs_avg),
+ nginx_ingress_vts: %w(nginx_upstream_responses_total nginx_upstream_response_msecs_avg),
+ nginx_ingress: %w(nginx_ingress_controller_requests nginx_ingress_controller_ingress_upstream_latency_seconds_sum),
ha_proxy: %w(haproxy_frontend_http_requests_total haproxy_frontend_http_responses_total),
aws_elb: %w(aws_elb_request_count_sum aws_elb_latency_average aws_elb_httpcode_backend_5_xx_sum),
nginx: %w(nginx_server_requests nginx_server_requestMsec),
diff --git a/app/services/groups/update_service.rb b/app/services/groups/update_service.rb
index 0bf0e967dcc..31d3c844ad5 100644
--- a/app/services/groups/update_service.rb
+++ b/app/services/groups/update_service.rb
@@ -36,7 +36,7 @@ module Groups
end
def reject_parent_id!
- params.except!(:parent_id)
+ params.delete(:parent_id)
end
def valid_share_with_group_lock_change?
diff --git a/app/services/issuable/common_system_notes_service.rb b/app/services/issuable/common_system_notes_service.rb
index 765de9c66b0..885e14bba8f 100644
--- a/app/services/issuable/common_system_notes_service.rb
+++ b/app/services/issuable/common_system_notes_service.rb
@@ -4,20 +4,23 @@ module Issuable
class CommonSystemNotesService < ::BaseService
attr_reader :issuable
- def execute(issuable, old_labels)
+ def execute(issuable, old_labels: [], is_update: true)
@issuable = issuable
- if issuable.previous_changes.include?('title')
- create_title_change_note(issuable.previous_changes['title'].first)
- end
+ if is_update
+ if issuable.previous_changes.include?('title')
+ create_title_change_note(issuable.previous_changes['title'].first)
+ end
- handle_description_change_note
+ handle_description_change_note
+
+ handle_time_tracking_note if issuable.is_a?(TimeTrackable)
+ create_discussion_lock_note if issuable.previous_changes.include?('discussion_locked')
+ end
- handle_time_tracking_note if issuable.is_a?(TimeTrackable)
- create_labels_note(old_labels) if issuable.labels != old_labels
- create_discussion_lock_note if issuable.previous_changes.include?('discussion_locked')
- create_milestone_note if issuable.previous_changes.include?('milestone_id')
create_due_date_note if issuable.previous_changes.include?('due_date')
+ create_milestone_note if issuable.previous_changes.include?('milestone_id')
+ create_labels_note(old_labels) if issuable.labels != old_labels
end
private
diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb
index e32e262ac31..c7e7bb55e4b 100644
--- a/app/services/issuable_base_service.rb
+++ b/app/services/issuable_base_service.rb
@@ -152,6 +152,10 @@ class IssuableBaseService < BaseService
before_create(issuable)
if issuable.save
+ ActiveRecord::Base.no_touching do
+ Issuable::CommonSystemNotesService.new(project, current_user).execute(issuable, is_update: false)
+ end
+
after_create(issuable)
execute_hooks(issuable)
invalidate_cache_counts(issuable, users: issuable.assignees)
@@ -207,7 +211,7 @@ class IssuableBaseService < BaseService
if issuable.with_transaction_returning_status { issuable.save }
# We do not touch as it will affect a update on updated_at field
ActiveRecord::Base.no_touching do
- Issuable::CommonSystemNotesService.new(project, current_user).execute(issuable, old_associations[:labels])
+ Issuable::CommonSystemNotesService.new(project, current_user).execute(issuable, old_labels: old_associations[:labels])
end
handle_changes(issuable, old_associations: old_associations)
diff --git a/app/services/merge_requests/update_service.rb b/app/services/merge_requests/update_service.rb
index aacaf10d09c..33d8299c8b6 100644
--- a/app/services/merge_requests/update_service.rb
+++ b/app/services/merge_requests/update_service.rb
@@ -5,14 +5,15 @@ module MergeRequests
def execute(merge_request)
# We don't allow change of source/target projects and source branch
# after merge request was created
- params.except!(:source_project_id)
- params.except!(:target_project_id)
- params.except!(:source_branch)
+ params.delete(:source_project_id)
+ params.delete(:target_project_id)
+ params.delete(:source_branch)
merge_from_quick_action(merge_request) if params[:merge]
if merge_request.closed_without_fork?
- params.except!(:target_branch, :force_remove_source_branch)
+ params.delete(:target_branch)
+ params.delete(:force_remove_source_branch)
end
if params[:force_remove_source_branch].present?
diff --git a/app/services/users/update_service.rb b/app/services/users/update_service.rb
index a897e4bd56a..af4fe1aebb9 100644
--- a/app/services/users/update_service.rb
+++ b/app/services/users/update_service.rb
@@ -16,7 +16,7 @@ module Users
user_exists = @user.persisted?
- assign_attributes(&block)
+ assign_attributes
if @user.save(validate: validate) && update_status
notify_success(user_exists)
@@ -48,9 +48,11 @@ module Users
success
end
- def assign_attributes(&block)
- if @user.user_synced_attributes_metadata
- params.except!(*@user.user_synced_attributes_metadata.read_only_attributes)
+ def assign_attributes
+ if (metadata = @user.user_synced_attributes_metadata)
+ read_only = metadata.read_only_attributes
+
+ params.reject! { |key, _| read_only.include?(key.to_sym) }
end
@user.assign_attributes(params) if params.any?
diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml
index e191b009db2..82b2ab64a5d 100644
--- a/app/views/projects/_home_panel.html.haml
+++ b/app/views/projects/_home_panel.html.haml
@@ -2,7 +2,7 @@
- show_auto_devops_callout = show_auto_devops_callout?(@project)
.project-home-panel{ class: ("empty-project" if empty_repo) }
.project-header.row.append-bottom-8
- .project-title-row.col-md-12.col-lg-7.d-flex
+ .project-title-row.col-md-12.col-lg-6.d-flex
.avatar-container.project-avatar.float-none
= project_icon(@project, alt: @project.name, class: 'avatar avatar-tile s64', width: 64, height: 64)
.d-flex.flex-column.flex-wrap.align-items-baseline
@@ -25,7 +25,7 @@
- if @project.has_extra_tags?
= _("+ %{count} more") % { count: @project.count_of_extra_tags_not_shown }
- .project-repo-buttons.col-md-12.col-lg-5.d-inline-flex.flex-wrap.justify-content-lg-end
+ .project-repo-buttons.col-md-12.col-lg-6.d-inline-flex.flex-wrap.justify-content-lg-end
- if current_user
.d-inline-flex
= render 'projects/buttons/notifications', notification_setting: @notification_setting, btn_class: 'btn-xs'
diff --git a/changelogs/unreleased/51485-new-issue-labels-note.yml b/changelogs/unreleased/51485-new-issue-labels-note.yml
new file mode 100644
index 00000000000..a312d379ce2
--- /dev/null
+++ b/changelogs/unreleased/51485-new-issue-labels-note.yml
@@ -0,0 +1,5 @@
+---
+title: Create system notes on issue / MR creation when labels, milestone, or due date is set
+merge_request: 23859
+author:
+type: added
diff --git a/changelogs/unreleased/53954-resolved-non-diff-discussions-on-merge-requests-no-longer-show-who-resolved-them-and-when-at-a-glance.yml b/changelogs/unreleased/53954-resolved-non-diff-discussions-on-merge-requests-no-longer-show-who-resolved-them-and-when-at-a-glance.yml
new file mode 100644
index 00000000000..0632c1992c7
--- /dev/null
+++ b/changelogs/unreleased/53954-resolved-non-diff-discussions-on-merge-requests-no-longer-show-who-resolved-them-and-when-at-a-glance.yml
@@ -0,0 +1,5 @@
+---
+title: Show message on non-diff discussions
+merge_request:
+author:
+type: changed
diff --git a/changelogs/unreleased/add-new-nginx-metrics.yml b/changelogs/unreleased/add-new-nginx-metrics.yml
new file mode 100644
index 00000000000..57221056d6e
--- /dev/null
+++ b/changelogs/unreleased/add-new-nginx-metrics.yml
@@ -0,0 +1,5 @@
+---
+title: Add NGINX 0.16.0 and above metrics
+merge_request: 22133
+author:
+type: added
diff --git a/changelogs/unreleased/blackst0ne-improve-encoding-helper-spec.yml b/changelogs/unreleased/blackst0ne-improve-encoding-helper-spec.yml
new file mode 100644
index 00000000000..09480499b87
--- /dev/null
+++ b/changelogs/unreleased/blackst0ne-improve-encoding-helper-spec.yml
@@ -0,0 +1,5 @@
+---
+title: Update specs to exclude possible false positive pass
+merge_request: 23893
+author: "@blackst0ne"
+type: other
diff --git a/changelogs/unreleased/bvl-hide-confidential-events-take2.yml b/changelogs/unreleased/bvl-hide-confidential-events-take2.yml
new file mode 100644
index 00000000000..a5abd496a9d
--- /dev/null
+++ b/changelogs/unreleased/bvl-hide-confidential-events-take2.yml
@@ -0,0 +1,5 @@
+---
+title: Hide confidential events in the API
+merge_request: 23746
+author:
+type: other
diff --git a/changelogs/unreleased/none-syntax-highlighting.yml b/changelogs/unreleased/none-syntax-highlighting.yml
new file mode 100644
index 00000000000..b373aac7c02
--- /dev/null
+++ b/changelogs/unreleased/none-syntax-highlighting.yml
@@ -0,0 +1,5 @@
+---
+title: Add no-color theme for syntax highlighting.
+merge_request: !20170
+author: khm
+type: added
diff --git a/changelogs/unreleased/sh-carrierwave-patch-google-acl.yml b/changelogs/unreleased/sh-carrierwave-patch-google-acl.yml
new file mode 100644
index 00000000000..206253a100c
--- /dev/null
+++ b/changelogs/unreleased/sh-carrierwave-patch-google-acl.yml
@@ -0,0 +1,5 @@
+---
+title: Fix object storage not working properly with Google S3 compatibility
+merge_request: 23858
+author:
+type: fixed
diff --git a/config/initializers/carrierwave_patch.rb b/config/initializers/carrierwave_patch.rb
index 35ffff03abe..c361784491d 100644
--- a/config/initializers/carrierwave_patch.rb
+++ b/config/initializers/carrierwave_patch.rb
@@ -23,6 +23,19 @@ module CarrierWave
end
end
end
+
+ # Fix for https://github.com/carrierwaveuploader/carrierwave/pull/2356
+ def acl_header
+ if fog_provider == 'AWS'
+ { 'x-amz-acl' => @uploader.fog_public ? 'public-read' : 'private' }
+ else
+ {}
+ end
+ end
+
+ def fog_provider
+ @uploader.fog_credentials[:provider].to_s
+ end
end
end
end
diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb
index 6aba6c7c21d..be4183f39be 100644
--- a/config/initializers/sidekiq.rb
+++ b/config/initializers/sidekiq.rb
@@ -42,7 +42,9 @@ Sidekiq.configure_server do |config|
end
if Feature::FlipperFeature.table_exists? && Feature.enabled?(:gitlab_sidekiq_reliable_fetcher)
- Sidekiq::ReliableFetcher.setup_reliable_fetch!(config)
+ # By default we're going to use Semi Reliable Fetch
+ config.options[:semi_reliable_fetch] = Feature.enabled?(:gitlab_sidekiq_enable_semi_reliable_fetcher, default_enabled: true)
+ Sidekiq::ReliableFetch.setup_reliable_fetch!(config)
end
# Sidekiq-cron: load recurring jobs from gitlab.yml
diff --git a/config/prometheus/common_metrics.yml b/config/prometheus/common_metrics.yml
index 52023a2e3cb..9bdaf1575e9 100644
--- a/config/prometheus/common_metrics.yml
+++ b/config/prometheus/common_metrics.yml
@@ -1,4 +1,5 @@
-- group: Response metrics (NGINX Ingress)
+ # NGINX Ingress metrics for pre-0.16.0 versions
+- group: Response metrics (NGINX Ingress VTS)
priority: 10
metrics:
- title: "Throughput"
@@ -40,6 +41,51 @@
query_range: 'sum(rate(nginx_upstream_responses_total{status_code="5xx", upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[2m])) / sum(rate(nginx_upstream_responses_total{upstream=~"%{kube_namespace}-%{ci_environment_slug}-.*"}[2m])) * 100'
label: 5xx Errors
unit: "%"
+# NGINX Ingress metrics for post-0.16.0 versions
+- group: Response metrics (NGINX Ingress)
+ priority: 10
+ metrics:
+ - title: "Throughput"
+ y_label: "Requests / Sec"
+ required_metrics:
+ - nginx_ingress_controller_requests
+ weight: 1
+ queries:
+ - id: response_metrics_nginx_ingress_16_throughput_status_code
+ query_range: 'sum(label_replace(rate(nginx_ingress_controller_requests{namespace="%{kube_namespace}",ingress=~".*%{ci_environment_slug}.*"}[2m]), "status_code", "${1}xx", "status", "(.)..")) by (status_code)'
+ unit: req / sec
+ label: Status Code
+ series:
+ - label: status_code
+ when:
+ - value: 2xx
+ color: green
+ - value: 3xx
+ color: blue
+ - value: 4xx
+ color: orange
+ - value: 5xx
+ color: red
+ - title: "Latency"
+ y_label: "Latency (ms)"
+ required_metrics:
+ - nginx_ingress_controller_ingress_upstream_latency_seconds_sum
+ weight: 1
+ queries:
+ - id: response_metrics_nginx_ingress_16_latency_pod_average
+ query_range: 'sum(rate(nginx_ingress_controller_ingress_upstream_latency_seconds_sum{namespace="%{kube_namespace}",ingress=~".*%{ci_environment_slug}.*"}[2m])) / sum(rate(nginx_ingress_controller_ingress_upstream_latency_seconds_count{namespace="%{kube_namespace}",ingress=~".*%{ci_environment_slug}.*"}[2m])) * 1000'
+ label: Pod average
+ unit: ms
+ - title: "HTTP Error Rate"
+ y_label: "HTTP Errors"
+ required_metrics:
+ - nginx_ingress_controller_requests
+ weight: 1
+ queries:
+ - id: response_metrics_nginx_ingress_16_http_error_rate
+ query_range: 'sum(rate(nginx_ingress_controller_requests{status=~"5.*",namespace="%{kube_namespace}",ingress=~".*%{ci_environment_slug}.*"}[2m])) / sum(rate(nginx_ingress_controller_requests{namespace="%{kube_namespace}",ingress=~".*%{ci_environment_slug}.*"}[2m])) * 100'
+ label: 5xx Errors
+ unit: "%"
- group: Response metrics (HA Proxy)
priority: 10
metrics:
diff --git a/db/importers/common_metrics_importer.rb b/db/importers/common_metrics_importer.rb
index 6302394d7a6..deadd653ae9 100644
--- a/db/importers/common_metrics_importer.rb
+++ b/db/importers/common_metrics_importer.rb
@@ -4,11 +4,12 @@ module Importers
class PrometheusMetric < ActiveRecord::Base
enum group: {
# built-in groups
- nginx_ingress: -1,
+ nginx_ingress_vts: -1,
ha_proxy: -2,
aws_elb: -3,
nginx: -4,
kubernetes: -5,
+ nginx_ingress: -6,
# custom groups
business: 0,
@@ -22,6 +23,7 @@ module Importers
business: _('Business metrics (Custom)'),
response: _('Response metrics (Custom)'),
system: _('System metrics (Custom)'),
+ nginx_ingress_vts: _('Response metrics (NGINX Ingress VTS)'),
nginx_ingress: _('Response metrics (NGINX Ingress)'),
ha_proxy: _('Response metrics (HA Proxy)'),
aws_elb: _('Response metrics (AWS ELB)'),
diff --git a/db/migrate/20181006004100_import_common_metrics_nginx_vts.rb b/db/migrate/20181006004100_import_common_metrics_nginx_vts.rb
new file mode 100644
index 00000000000..98fafed7912
--- /dev/null
+++ b/db/migrate/20181006004100_import_common_metrics_nginx_vts.rb
@@ -0,0 +1,15 @@
+class ImportCommonMetricsNginxVts < ActiveRecord::Migration
+ include Gitlab::Database::MigrationHelpers
+
+ require Rails.root.join('db/importers/common_metrics_importer.rb')
+
+ DOWNTIME = false
+
+ def up
+ Importers::CommonMetricsImporter.new.execute
+ end
+
+ def down
+ # no-op
+ end
+end
diff --git a/doc/administration/uploads.md b/doc/administration/uploads.md
index f85a1f791f9..476ae8e8a76 100644
--- a/doc/administration/uploads.md
+++ b/doc/administration/uploads.md
@@ -120,30 +120,7 @@ _The uploads are stored by default in
```
1. Save the file and [reconfigure GitLab][] for the changes to take effect.
-1. Migrate any existing local uploads to the object storage:
-
- > **Notes:**
- > These task complies with the `BATCH` environment variable to process uploads in batch (200 by default). All of the processing will be done in a background worker and requires **no downtime**.
-
- ```bash
- # gitlab-rake gitlab:uploads:migrate[uploader_class, model_class, mount_point]
-
- # Avatars
- gitlab-rake "gitlab:uploads:migrate[AvatarUploader, Project, :avatar]"
- gitlab-rake "gitlab:uploads:migrate[AvatarUploader, Group, :avatar]"
- gitlab-rake "gitlab:uploads:migrate[AvatarUploader, User, :avatar]"
-
- # Attachments
- gitlab-rake "gitlab:uploads:migrate[AttachmentUploader, Note, :attachment]"
- gitlab-rake "gitlab:uploads:migrate[AttachmentUploader, Appearance, :logo]"
- gitlab-rake "gitlab:uploads:migrate[AttachmentUploader, Appearance, :header_logo]"
-
- # Markdown
- gitlab-rake "gitlab:uploads:migrate[FileUploader, Project]"
- gitlab-rake "gitlab:uploads:migrate[PersonalFileUploader, Snippet]"
- gitlab-rake "gitlab:uploads:migrate[NamespaceFileUploader, Snippet]"
- gitlab-rake "gitlab:uploads:migrate[FileUploader, MergeRequest]"
- ```
+1. Migrate any existing local uploads to the object storage using [`gitlab:uploads:migrate` rake task](raketasks/uploads/migrate.md).
---
@@ -168,32 +145,7 @@ _The uploads are stored by default in
```
1. Save the file and [restart GitLab][] for the changes to take effect.
-1. Migrate any existing local uploads to the object storage:
-
- > **Notes:**
- > - These task comply with the `BATCH` environment variable to process uploads in batch (200 by default). All of the processing will be done in a background worker and requires **no downtime**.
- > - To migrate in production use `RAILS_ENV=production` environment variable.
-
- ```bash
- # sudo -u git -H bundle exec rake gitlab:uploads:migrate
-
- # Avatars
- sudo -u git -H bundle exec rake "gitlab:uploads:migrate[AvatarUploader, Project, :avatar]"
- sudo -u git -H bundle exec rake "gitlab:uploads:migrate[AvatarUploader, Group, :avatar]"
- sudo -u git -H bundle exec rake "gitlab:uploads:migrate[AvatarUploader, User, :avatar]"
-
- # Attachments
- sudo -u git -H bundle exec rake "gitlab:uploads:migrate[AttachmentUploader, Note, :attachment]"
- sudo -u git -H bundle exec rake "gitlab:uploads:migrate[AttachmentUploader, Appearance, :logo]"
- sudo -u git -H bundle exec rake "gitlab:uploads:migrate[AttachmentUploader, Appearance, :header_logo]"
-
- # Markdown
- sudo -u git -H bundle exec rake "gitlab:uploads:migrate[FileUploader, Project]"
- sudo -u git -H bundle exec rake "gitlab:uploads:migrate[PersonalFileUploader, Snippet]"
- sudo -u git -H bundle exec rake "gitlab:uploads:migrate[NamespaceFileUploader, Snippet]"
- sudo -u git -H bundle exec rake "gitlab:uploads:migrate[FileUploader, MergeRequest]"
-
- ```
+1. Migrate any existing local uploads to the object storage using [`gitlab:uploads:migrate` rake task](raketasks/uploads/migrate.md).
[reconfigure gitlab]: restart_gitlab.md#omnibus-gitlab-reconfigure "How to reconfigure Omnibus GitLab"
[restart gitlab]: restart_gitlab.md#installations-from-source "How to restart GitLab"
diff --git a/doc/api/suggestions.md b/doc/api/suggestions.md
new file mode 100644
index 00000000000..9d76ef0c4bf
--- /dev/null
+++ b/doc/api/suggestions.md
@@ -0,0 +1,36 @@
+# Suggest Changes API
+
+Every API call to suggestions must be authenticated.
+
+## Applying suggestions
+
+Applies a suggested patch in a merge request. Users must be
+at least [Developer](../user/permissions.md) to perform such action.
+
+```
+PUT /suggestions/:id/apply
+```
+
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `id` | integer/string | yes | The ID of a suggestion |
+
+```bash
+curl --request PUT --header "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v4/suggestions/5/apply
+```
+
+Example response:
+
+```json
+ {
+ "id": 36,
+ "from_original_line": 10,
+ "to_original_line": 10,
+ "from_line": 10,
+ "to_line": 10,
+ "appliable": false,
+ "applied": true,
+ "from_content": " \"--talk-name=org.freedesktop.\",\n",
+ "to_content": " \"--talk-name=org.free.\",\n \"--talk-name=org.desktop.\",\n"
+ }
+```
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index acfcd05624d..4df5d8fb54a 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -203,7 +203,7 @@ used for time of the job. The configuration of this feature is covered in
jobs, including deploy jobs, but after the restoration of [artifacts](#artifacts).
This can be an array or a multi-line string.
-`after_script` is used to define the command that will be run after for all
+`after_script` is used to define the command that will be run after all
jobs, including failed ones. This has to be an array or a multi-line string.
The `before_script` and the main `script` are concatenated and run in a single context/container.
diff --git a/doc/topics/autodevops/index.md b/doc/topics/autodevops/index.md
index 7885cffd107..b41f401e14c 100644
--- a/doc/topics/autodevops/index.md
+++ b/doc/topics/autodevops/index.md
@@ -567,7 +567,7 @@ To view the metrics, open the
While Auto DevOps provides great defaults to get you started, you can customize
almost everything to fit your needs; from custom [buildpacks](#custom-buildpacks),
to [`Dockerfile`s](#custom-dockerfile), [Helm charts](#custom-helm-chart), or
-even copying the complete [CI/CD configuration](#customizing-gitlab-ci-yml)
+even copying the complete [CI/CD configuration](#customizing-gitlab-ciyml)
into your project to enable staging and canary deployments, and more.
### Custom buildpacks
diff --git a/doc/user/discussions/img/insert_suggestion.png b/doc/user/discussions/img/insert_suggestion.png
new file mode 100644
index 00000000000..4bf293b8297
--- /dev/null
+++ b/doc/user/discussions/img/insert_suggestion.png
Binary files differ
diff --git a/doc/user/discussions/img/make_suggestion.png b/doc/user/discussions/img/make_suggestion.png
new file mode 100644
index 00000000000..20acc1417da
--- /dev/null
+++ b/doc/user/discussions/img/make_suggestion.png
Binary files differ
diff --git a/doc/user/discussions/img/suggestion.png b/doc/user/discussions/img/suggestion.png
new file mode 100644
index 00000000000..68a67e6ae5e
--- /dev/null
+++ b/doc/user/discussions/img/suggestion.png
Binary files differ
diff --git a/doc/user/discussions/index.md b/doc/user/discussions/index.md
index 0f89d261ff6..9379d047fca 100644
--- a/doc/user/discussions/index.md
+++ b/doc/user/discussions/index.md
@@ -293,6 +293,51 @@ Once you select one of the filters in a given issue or MR, GitLab will save
your preference, so that it will persist when you visit the same page again
from any device you're logged into.
+## Suggest Changes
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/18008) in GitLab 11.6.
+
+As a reviewer, you're able to suggest code changes with a simple
+markdown syntax in Merge Request Diff discussions. Then, the
+Merge Request author (or other users with appropriate
+[permission](../permissions.md)) is able to apply these
+suggestions with a click, which will generate a commit in
+the Merge Request authored by the user that applied them.
+
+1. Choose a line of code to be changed, add a new comment, then click
+on the **Insert suggestion** icon in the toolbar:
+
+ ![Add a new comment](img/insert_suggestion.png)
+
+ > **Note:**
+ The suggestion will only affect the commented line. Multi-line
+ suggestions are currently not supported. Will be introduced by
+ [#53310](https://gitlab.com/gitlab-org/gitlab-ce/issues/53310).
+
+1. In the comment, add your suggestion to the pre-populated code block:
+
+ ![Add a suggestion into a code block tagged properly](img/make_suggestion.png)
+
+1. Click **Comment**.
+
+ The suggestions in the comment can be applied by the merge request author
+ directly from the merge request:
+
+ ![Apply suggestions](img/suggestion.png)
+
+ > **Note:**
+ Discussions are _not_ automatically resolved. Will be introduced by
+ [#54405](https://gitlab.com/gitlab-org/gitlab-ce/issues/54405).
+
+Once the author applies a suggestion, it will be marked with the **Applied** label,
+and GitLab will create a new commit with the message `Apply suggestion to <file-name>`
+and push the suggested change directly into the codebase in the merge request's branch.
+[Developer permission](../permissions.md) is required to do so.
+
+> **Note:**
+Custom commit messages will be introduced by
+[#54404](https://gitlab.com/gitlab-org/gitlab-ce/issues/54404).
+
[ce-5022]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/5022
[ce-7125]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7125
[ce-7527]: https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/7527
diff --git a/doc/user/group/clusters/index.md b/doc/user/group/clusters/index.md
index adc43921d47..8e03d116b81 100644
--- a/doc/user/group/clusters/index.md
+++ b/doc/user/group/clusters/index.md
@@ -124,3 +124,11 @@ The result will then be:
- The Project cluster will be used for the `test` job.
- The Staging cluster will be used for the `deploy to staging` job.
- The Production cluster will be used for the `deploy to production` job.
+
+## Unavailable features
+
+The following features are not currently available for group-level clusters:
+
+1. Terminals (see [related issue](https://gitlab.com/gitlab-org/gitlab-ce/issues/55487)).
+1. Pod logs (see [related issue](https://gitlab.com/gitlab-org/gitlab-ce/issues/55488)).
+1. Deployment boards (see [related issue](https://gitlab.com/gitlab-org/gitlab-ce/issues/55488)).
diff --git a/doc/user/markdown.md b/doc/user/markdown.md
index debebd4c081..893658290e5 100644
--- a/doc/user/markdown.md
+++ b/doc/user/markdown.md
@@ -5,11 +5,11 @@ It is not valid for the [GitLab documentation website](https://docs.gitlab.com)
nor [GitLab's main website](https://about.gitlab.com), as they both use
[Kramdown](https://kramdown.gettalong.org) as their markdown engine.
The documentation website uses an extended Kramdown gem, [GitLab Kramdown](https://gitlab.com/gitlab-org/gitlab_kramdown).
-Consult the [GitLab Kramdown Guide](https://about.gitlab.com/handbook/product/technical-writing/markdown-guide/) for a complete Kramdown reference._
+Consult the [GitLab Kramdown Guide](https://about.gitlab.com/handbook/product/technical-writing/markdown-guide/) for a complete Kramdown reference.
## GitLab Flavored Markdown (GFM)
-GitLab uses "GitLab Flavored Markdown" (GFM). It extends the [CommonMark specification][commonmark-spec] (which is based on standard Markdown) in a few significant ways to add some useful functionality. It was inspired by [GitHub Flavored Markdown](https://help.github.com/articles/basic-writing-and-formatting-syntax/).
+GitLab uses "GitLab Flavored Markdown" (GFM). It extends the [CommonMark specification][commonmark-spec] (which is based on standard Markdown) in a few significant ways to add additional useful functionality. It was inspired by [GitHub Flavored Markdown](https://help.github.com/articles/basic-writing-and-formatting-syntax/).
You can use GFM in the following areas:
@@ -26,8 +26,7 @@ dependency to do so. Please see the [`github-markup` gem readme](https://github.
> **Notes:**
>
-> For the best result, we encourage you to check this document out as [rendered
-> by GitLab itself](markdown.md).
+> We encourage you to view this document as [rendered by GitLab itself](markdown.md).
>
> As of 11.1, GitLab uses the [CommonMark Ruby Library][commonmarker] for Markdown
processing of all new issues, merge requests, comments, and other Markdown content
@@ -142,7 +141,7 @@ GFM will autolink almost any URL you copy and paste into your text:
* <a href="irc://irc.freenode.net/gitlab">irc://irc.freenode.net/gitlab</a>
* http://localhost:3000
-### Multiline Blockquote
+### Multiline blockquote
> If this is not rendered correctly, see
https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#multiline-blockquote
@@ -172,7 +171,7 @@ you can quote that without having to manually prepend `>` to every line!
<p>you can quote that without having to manually prepend <code>&gt;</code> to every line!</p>
</blockquote>
-### Code and Syntax Highlighting
+### Code and syntax highlighting
> If this is not rendered correctly, see
https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#code-and-syntax-highlighting
@@ -242,7 +241,7 @@ s = "There is no highlighting for this."
But let's throw in a <b>tag</b>.
```
-### Inline Diff
+### Inline diff
> If this is not rendered correctly, see
https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#inline-diff
@@ -309,7 +308,7 @@ On Linux, you can download [Noto Color Emoji](https://www.google.com/get/noto/he
Ubuntu 18.04 (like many modern Linux distros) has this font installed by default.
-### Special GitLab References
+### Special GitLab references
GFM recognizes special references.
@@ -363,7 +362,7 @@ It also has a shorthand version to reference other projects from the same namesp
| `project@9ba12248...b19a04f5` | commit range comparison |
| `project~"Some label"` | issues with given label |
-### Task Lists
+### Task lists
> If this is not rendered correctly, see
https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#task-lists
@@ -505,6 +504,66 @@ Becomes:
For details see the [Mermaid official page][mermaid].
+### Front matter
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/23331)
+ in GitLab 11.6.
+
+Front matter is metadata included at the beginning of a markdown document, preceding
+its content. This data can be used by static site generators such as [Jekyll](https://jekyllrb.com/docs/front-matter/) and [Hugo](https://gohugo.io/content-management/front-matter/),
+and many other applications.
+
+In GitLab, front matter is only used in Markdown files and wiki pages, not the other places where Markdown formatting is supported.
+When you view a Markdown file rendered by GitLab, any front matter is displayed as-is, in a box at the top of the document, before the rendered HTML content.
+To view an example, you can toggle between the source and rendered version of a [GitLab documentation file](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/README.md).
+
+The following delimeters are supported:
+
+- YAML (`---`):
+
+ ```
+ ---
+ title: About Front Matter
+ example:
+ language: yaml
+ ---
+ ```
+
+- TOML (`+++`):
+
+ ```
+ +++
+ title = "About Front Matter"
+ [example]
+ language = "toml"
+ +++
+ ```
+
+- JSON (`;;;`):
+
+ ```
+ ;;;
+ {
+ "title": "About Front Matter"
+ "example": {
+ "language": "json"
+ }
+ }
+ ;;;
+ ```
+
+Other languages are supported by adding a specifier to any of the existing
+delimiters. For example:
+
+```
+---php
+$title = "About Front Matter";
+$example = array(
+ 'language' => "php",
+);
+---
+```
+
## Standard Markdown
### Headers
diff --git a/doc/user/permissions.md b/doc/user/permissions.md
index c4a2d5f66e5..ed00f86f9de 100644
--- a/doc/user/permissions.md
+++ b/doc/user/permissions.md
@@ -79,6 +79,7 @@ The following table depicts the various user permission levels in a project.
| View approved/blacklisted licenses **[ULTIMATE]** | | | ✓ | ✓ | ✓ |
| Use security dashboard **[ULTIMATE]** | | | ✓ | ✓ | ✓ |
| Dismiss vulnerability **[ULTIMATE]** | | | ✓ | ✓ | ✓ |
+| Apply code change suggestions | | | ✓ | ✓ | ✓ |
| Use environment terminals | | | | ✓ | ✓ |
| Add new team members | | | | ✓ | ✓ |
| Push to protected branches | | | | ✓ | ✓ |
diff --git a/doc/user/project/cycle_analytics.md b/doc/user/project/cycle_analytics.md
index ea843054f8e..b98221bea61 100644
--- a/doc/user/project/cycle_analytics.md
+++ b/doc/user/project/cycle_analytics.md
@@ -146,7 +146,8 @@ A few notes:
The current permissions on the Cycle Analytics dashboard are:
- Public projects - anyone can access
-- Private/internal projects - any member (guest level and above) can access
+- Internal projects - any authenticated user can access
+- Private projects - any member Guest and above can access
You can [read more about permissions][permissions] in general.
diff --git a/doc/user/project/merge_requests/index.md b/doc/user/project/merge_requests/index.md
index 85d8d804133..d4f8cf929f6 100644
--- a/doc/user/project/merge_requests/index.md
+++ b/doc/user/project/merge_requests/index.md
@@ -150,6 +150,16 @@ in a Merge Request. To do so, click the **...** button in the gutter of the Merg
![Comment on any diff file line](img/comment-on-any-diff-line.png)
+## Suggest changes
+
+> [Introduced](https://gitlab.com/gitlab-org/gitlab-ce/issues/18008) in GitLab 11.6.
+
+As a reviewer, you can add suggestions to change the content in
+merge request discussions, and users with appropriate [permission](../../permissions.md)
+can easily apply them to the codebase directly from the UI. Read
+through the documentation on [Suggest changes](../../discussions/index.md#suggest-changes)
+to learn more.
+
## Resolve conflicts
When a merge request has conflicts, GitLab may provide the option to resolve
diff --git a/lib/api/events.rb b/lib/api/events.rb
index 44dae57770d..b98aa9f31e1 100644
--- a/lib/api/events.rb
+++ b/lib/api/events.rb
@@ -18,29 +18,15 @@ module API
desc: 'Return events sorted in ascending and descending order'
end
- RedactedEvent = OpenStruct.new(target_title: 'Confidential event').freeze
-
- def redact_events(events)
- events.map do |event|
- if event.visible_to_user?(current_user)
- event
- else
- RedactedEvent
- end
- end
- end
-
- # rubocop: disable CodeReuse/ActiveRecord
- def present_events(events, redact: true)
- events = events.reorder(created_at: params[:sort])
- .with_associations
-
+ def present_events(events)
events = paginate(events)
- events = redact_events(events) if redact
present events, with: Entities::Event
end
- # rubocop: enable CodeReuse/ActiveRecord
+
+ def find_events(source)
+ EventsFinder.new(params.merge(source: source, current_user: current_user, with_associations: true)).execute
+ end
end
resource :events do
@@ -55,16 +41,14 @@ module API
use :event_filter_params
use :sort_params
end
- # rubocop: disable CodeReuse/ActiveRecord
+
get do
authenticate!
- events = EventsFinder.new(params.merge(source: current_user, current_user: current_user)).execute.preload(:author, :target)
+ events = find_events(current_user)
- # Since we're viewing our own events, redaction is unnecessary
- present_events(events, redact: false)
+ present_events(events)
end
- # rubocop: enable CodeReuse/ActiveRecord
end
params do
@@ -82,16 +66,15 @@ module API
use :event_filter_params
use :sort_params
end
- # rubocop: disable CodeReuse/ActiveRecord
+
get ':id/events' do
user = find_user(params[:id])
not_found!('User') unless user
- events = EventsFinder.new(params.merge(source: user, current_user: current_user)).execute.preload(:author, :target)
+ events = find_events(user)
present_events(events)
end
- # rubocop: enable CodeReuse/ActiveRecord
end
params do
@@ -106,13 +89,12 @@ module API
use :event_filter_params
use :sort_params
end
- # rubocop: disable CodeReuse/ActiveRecord
+
get ":id/events" do
- events = EventsFinder.new(params.merge(source: user_project, current_user: current_user)).execute.preload(:author, :target)
+ events = find_events(user_project)
present_events(events)
end
- # rubocop: enable CodeReuse/ActiveRecord
end
end
end
diff --git a/lib/gitlab/color_schemes.rb b/lib/gitlab/color_schemes.rb
index a5e4065cf09..881e5dbc923 100644
--- a/lib/gitlab/color_schemes.rb
+++ b/lib/gitlab/color_schemes.rb
@@ -12,7 +12,8 @@ module Gitlab
Scheme.new(2, 'Dark', 'dark'),
Scheme.new(3, 'Solarized Light', 'solarized-light'),
Scheme.new(4, 'Solarized Dark', 'solarized-dark'),
- Scheme.new(5, 'Monokai', 'monokai')
+ Scheme.new(5, 'Monokai', 'monokai'),
+ Scheme.new(6, 'None', 'none')
].freeze
# Convenience method to get a space-separated String of all the color scheme
diff --git a/lib/gitlab/safe_request_store.rb b/lib/gitlab/safe_request_store.rb
index 4e82353adb6..d146913bdb3 100644
--- a/lib/gitlab/safe_request_store.rb
+++ b/lib/gitlab/safe_request_store.rb
@@ -19,5 +19,13 @@ module Gitlab
NULL_STORE
end
end
+
+ # This method accept an options hash to be compatible with
+ # ActiveSupport::Cache::Store#write method. The options are
+ # not passed to the underlying cache implementation because
+ # RequestStore#write accepts only a key, and value params.
+ def self.write(key, value, options = nil)
+ store.write(key, value)
+ end
end
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index dcd5e3c1409..8a7db438add 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -5647,6 +5647,9 @@ msgstr ""
msgid "Response metrics (HA Proxy)"
msgstr ""
+msgid "Response metrics (NGINX Ingress VTS)"
+msgstr ""
+
msgid "Response metrics (NGINX Ingress)"
msgstr ""
diff --git a/qa/qa/specs/features/browser_ui/2_plan/issue/issue_suggestions_spec.rb b/qa/qa/specs/features/browser_ui/2_plan/issue/issue_suggestions_spec.rb
new file mode 100644
index 00000000000..7e8b42e286f
--- /dev/null
+++ b/qa/qa/specs/features/browser_ui/2_plan/issue/issue_suggestions_spec.rb
@@ -0,0 +1,35 @@
+# frozen_string_literal: true
+
+module QA
+ context 'Plan' do
+ describe 'issue suggestions' do
+ let(:issue_title) { 'Issue Lists are awesome' }
+
+ it 'user sees issue suggestions when creating a new issue' do
+ Runtime::Browser.visit(:gitlab, Page::Main::Login)
+ Page::Main::Login.perform(&:sign_in_using_credentials)
+
+ project = Resource::Project.fabricate! do |resource|
+ resource.name = 'project-for-issue-suggestions'
+ resource.description = 'project for issue suggestions'
+ end
+
+ Resource::Issue.fabricate! do |issue|
+ issue.title = issue_title
+ issue.project = project
+ end
+
+ project.visit!
+
+ Page::Project::Show.perform(&:go_to_new_issue)
+ Page::Project::Issue::New.perform do |new_issue_page|
+ new_issue_page.add_title("issue")
+ expect(new_issue_page).to have_content(issue_title)
+
+ new_issue_page.add_title("Issue Board")
+ expect(new_issue_page).not_to have_content(issue_title)
+ end
+ end
+ end
+ end
+end
diff --git a/scripts/review_apps/review-apps.sh b/scripts/review_apps/review-apps.sh
index b50bf2161cb..8f9414cc1ed 100755
--- a/scripts/review_apps/review-apps.sh
+++ b/scripts/review_apps/review-apps.sh
@@ -292,7 +292,7 @@ function get_job_id() {
local job_id=$(curl --silent --show-error --header "PRIVATE-TOKEN: ${API_TOKEN}" "${url}" | jq "map(select(.name == \"${job_name}\")) | map(.id) | last")
[[ "${job_id}" == "null" && "${page}" -lt "$max_page" ]] || break
- ((page++))
+ let "page++"
done
if [[ "${job_id}" == "" ]]; then
@@ -334,7 +334,7 @@ function wait_for_job_to_be_done() {
[[ "${job_status}" == "pending" || "${job_status}" == "running" ]] || break
printf "."
- ((elapsed_seconds+=$interval))
+ let "elapsed_seconds+=interval"
sleep ${interval}
done
diff --git a/scripts/trigger-build b/scripts/trigger-build
index 14af3281106..4032ba853e6 100755
--- a/scripts/trigger-build
+++ b/scripts/trigger-build
@@ -68,7 +68,7 @@ module Trigger
def base_variables
{
- 'GITLAB_REF_SLUG' => ref_slug,
+ 'GITLAB_REF_SLUG' => ENV['CI_COMMIT_REF_SLUG'],
'TRIGGERED_USER' => ENV['TRIGGERED_USER'] || ENV['GITLAB_USER_NAME'],
'TRIGGER_SOURCE' => ENV['CI_JOB_URL'],
'TOP_UPSTREAM_SOURCE_PROJECT' => ENV['CI_PROJECT_PATH'],
@@ -77,12 +77,6 @@ module Trigger
}
end
- def ref_slug
- return 'master' if ENV['CI_COMMIT_REF_SLUG'] =~ %r{(\Aqa[/-]|-qa\z)}
-
- ENV['CI_COMMIT_REF_SLUG']
- end
-
# Read version files from all components
def version_file_variables
Dir.glob("*_VERSION").each_with_object({}) do |version_file, params|
diff --git a/spec/finders/concerns/finder_with_cross_project_access_spec.rb b/spec/finders/concerns/finder_with_cross_project_access_spec.rb
index 1ff65a8101b..f29acb521a8 100644
--- a/spec/finders/concerns/finder_with_cross_project_access_spec.rb
+++ b/spec/finders/concerns/finder_with_cross_project_access_spec.rb
@@ -115,4 +115,20 @@ describe FinderWithCrossProjectAccess do
expect(finder.execute).to include(result)
end
end
+
+ context 'when specifying a model' do
+ let(:finder_class) do
+ Class.new do
+ prepend FinderWithCrossProjectAccess
+
+ requires_cross_project_access model: Project
+ end
+ end
+
+ context '.finder_model' do
+ it 'is set correctly' do
+ expect(finder_class.finder_model).to eq(Project)
+ end
+ end
+ end
end
diff --git a/spec/finders/events_finder_spec.rb b/spec/finders/events_finder_spec.rb
index 62968e83292..3bce46cc4d1 100644
--- a/spec/finders/events_finder_spec.rb
+++ b/spec/finders/events_finder_spec.rb
@@ -14,6 +14,10 @@ describe EventsFinder do
let!(:closed_issue_event2) { create(:event, project: project1, author: user, target: closed_issue, action: Event::CLOSED, created_at: Date.new(2016, 2, 2)) }
let!(:opened_merge_request_event2) { create(:event, project: project2, author: user, target: opened_merge_request, action: Event::CREATED, created_at: Date.new(2017, 2, 2)) }
+ let(:public_project) { create(:project, :public, creator_id: user.id, namespace: user.namespace) }
+ let(:confidential_issue) { create(:closed_issue, confidential: true, project: public_project, author: user) }
+ let!(:confidential_event) { create(:event, project: public_project, author: user, target: confidential_issue, action: Event::CLOSED) }
+
context 'when targeting a user' do
it 'returns events between specified dates filtered on action and type' do
events = described_class.new(source: user, current_user: user, action: 'created', target_type: 'merge_request', after: Date.new(2017, 1, 1), before: Date.new(2017, 2, 1)).execute
@@ -27,6 +31,19 @@ describe EventsFinder do
expect(events).not_to include(opened_merge_request_event)
end
+ it 'does not include events on confidential issues the user does not have access to' do
+ events = described_class.new(source: user, current_user: other_user).execute
+
+ expect(events).not_to include(confidential_event)
+ end
+
+ it 'includes confidential events user has access to' do
+ public_project.add_developer(other_user)
+ events = described_class.new(source: user, current_user: other_user).execute
+
+ expect(events).to include(confidential_event)
+ end
+
it 'returns nothing when the current user cannot read cross project' do
expect(Ability).to receive(:allowed?).with(user, :read_cross_project) { false }
diff --git a/spec/finders/user_recent_events_finder_spec.rb b/spec/finders/user_recent_events_finder_spec.rb
index c5fcd68eb4c..5ebceeb7586 100644
--- a/spec/finders/user_recent_events_finder_spec.rb
+++ b/spec/finders/user_recent_events_finder_spec.rb
@@ -29,8 +29,9 @@ describe UserRecentEventsFinder do
end
it 'does not include the events if the user cannot read cross project' do
- expect(Ability).to receive(:allowed?).and_call_original
+ allow(Ability).to receive(:allowed?).and_call_original
expect(Ability).to receive(:allowed?).with(current_user, :read_cross_project) { false }
+
expect(finder.execute).to be_empty
end
end
diff --git a/spec/lib/gitlab/encoding_helper_spec.rb b/spec/lib/gitlab/encoding_helper_spec.rb
index a5bf2f2b3df..429816efec3 100644
--- a/spec/lib/gitlab/encoding_helper_spec.rb
+++ b/spec/lib/gitlab/encoding_helper_spec.rb
@@ -124,7 +124,7 @@ describe Gitlab::EncodingHelper do
end
it 'returns empty string on conversion errors' do
- expect { ext_class.encode_utf8('') }.not_to raise_error(ArgumentError)
+ expect { ext_class.encode_utf8('') }.not_to raise_error
end
context 'with strings that can be forcefully encoded into utf8' do
diff --git a/spec/lib/gitlab/gpg_spec.rb b/spec/lib/gitlab/gpg_spec.rb
index 39d09c49989..48bbd7f854c 100644
--- a/spec/lib/gitlab/gpg_spec.rb
+++ b/spec/lib/gitlab/gpg_spec.rb
@@ -137,7 +137,7 @@ describe Gitlab::Gpg do
described_class.using_tmp_keychain do
end
end
- end.not_to raise_error(ThreadError)
+ end.not_to raise_error
end
end
end
diff --git a/spec/lib/gitlab/safe_request_store_spec.rb b/spec/lib/gitlab/safe_request_store_spec.rb
index c797171dbe2..bae87e43615 100644
--- a/spec/lib/gitlab/safe_request_store_spec.rb
+++ b/spec/lib/gitlab/safe_request_store_spec.rb
@@ -78,6 +78,12 @@ describe Gitlab::SafeRequestStore do
described_class.write('foo', true)
end.to change { described_class.read('foo') }.from(nil).to(true)
end
+
+ it 'does not pass the options hash to the underlying store implementation' do
+ expect(described_class.store).to receive(:write).with('foo', true)
+
+ described_class.write('foo', true, expires_in: 15.seconds)
+ end
end
context 'when RequestStore is NOT active' do
@@ -86,6 +92,12 @@ describe Gitlab::SafeRequestStore do
described_class.write('foo', true)
end.not_to change { described_class.read('foo') }.from(nil)
end
+
+ it 'does not pass the options hash to the underlying store implementation' do
+ expect(described_class.store).to receive(:write).with('foo', true)
+
+ described_class.write('foo', true, expires_in: 15.seconds)
+ end
end
end
diff --git a/spec/models/concerns/chronic_duration_attribute_spec.rb b/spec/models/concerns/chronic_duration_attribute_spec.rb
index b14b773b653..51221e07ca3 100644
--- a/spec/models/concerns/chronic_duration_attribute_spec.rb
+++ b/spec/models/concerns/chronic_duration_attribute_spec.rb
@@ -43,7 +43,7 @@ shared_examples 'ChronicDurationAttribute writer' do
end
it "doesn't raise exception" do
- expect { subject.send("#{virtual_field}=", '-10m') }.not_to raise_error(ChronicDuration::DurationParseError)
+ expect { subject.send("#{virtual_field}=", '-10m') }.not_to raise_error
end
it "doesn't change value" do
@@ -87,7 +87,7 @@ shared_examples 'ChronicDurationAttribute writer' do
end
it "doesn't raise exception" do
- expect { subject.send("#{virtual_field}=", nil) }.not_to raise_error(NoMethodError)
+ expect { subject.send("#{virtual_field}=", nil) }.not_to raise_error
end
end
end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index bf4117fbcaf..7fd5307b906 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -1358,7 +1358,7 @@ describe MergeRequest do
it 'does not raises a NameError exception' do
allow_any_instance_of(service_class_name.constantize).to receive(:execute).and_return(nil)
- expect { subject }.not_to raise_error(NameError)
+ expect { subject }.not_to raise_error
end
end
end
diff --git a/spec/requests/api/events_spec.rb b/spec/requests/api/events_spec.rb
index 573eebe314d..c4e3ac14441 100644
--- a/spec/requests/api/events_spec.rb
+++ b/spec/requests/api/events_spec.rb
@@ -182,6 +182,68 @@ describe API::Events do
end
end
+ context 'with inaccessible events' do
+ let(:public_project) { create(:project, :public, creator_id: user.id, namespace: user.namespace) }
+ let(:confidential_issue) { create(:closed_issue, confidential: true, project: public_project, author: user) }
+ let!(:confidential_event) { create(:event, project: public_project, author: user, target: confidential_issue, action: Event::CLOSED) }
+ let(:public_issue) { create(:closed_issue, project: public_project, author: user) }
+ let!(:public_event) { create(:event, project: public_project, author: user, target: public_issue, action: Event::CLOSED) }
+
+ it 'returns only accessible events' do
+ get api("/projects/#{public_project.id}/events", non_member)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response.size).to eq(1)
+ end
+
+ it 'returns all events when the user has access' do
+ get api("/projects/#{public_project.id}/events", user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response.size).to eq(2)
+ end
+ end
+
+ context 'pagination' do
+ let(:public_project) { create(:project, :public) }
+
+ before do
+ create(:event,
+ project: public_project,
+ target: create(:issue, project: public_project, title: 'Issue 1'),
+ action: Event::CLOSED,
+ created_at: Date.parse('2018-12-10'))
+ create(:event,
+ project: public_project,
+ target: create(:issue, confidential: true, project: public_project, title: 'Confidential event'),
+ action: Event::CLOSED,
+ created_at: Date.parse('2018-12-11'))
+ create(:event,
+ project: public_project,
+ target: create(:issue, project: public_project, title: 'Issue 2'),
+ action: Event::CLOSED,
+ created_at: Date.parse('2018-12-12'))
+ end
+
+ it 'correctly returns the second page without inaccessible events' do
+ get api("/projects/#{public_project.id}/events", user), per_page: 2, page: 2
+
+ titles = json_response.map { |event| event['target_title'] }
+
+ expect(titles.first).to eq('Issue 1')
+ expect(titles).not_to include('Confidential event')
+ end
+
+ it 'correctly returns the first page without inaccessible events' do
+ get api("/projects/#{public_project.id}/events", user), per_page: 2, page: 1
+
+ titles = json_response.map { |event| event['target_title'] }
+
+ expect(titles.first).to eq('Issue 2')
+ expect(titles).not_to include('Confidential event')
+ end
+ end
+
context 'when not permitted to read' do
it 'returns 404' do
get api("/projects/#{private_project.id}/events", non_member)
diff --git a/spec/requests/api/redacted_events_spec.rb b/spec/requests/api/redacted_events_spec.rb
deleted file mode 100644
index 086dd3df9ba..00000000000
--- a/spec/requests/api/redacted_events_spec.rb
+++ /dev/null
@@ -1,68 +0,0 @@
-require 'spec_helper'
-
-describe 'Redacted events in API::Events' do
- shared_examples 'private events are redacted' do
- it 'redacts events the user does not have access to' do
- expect_any_instance_of(Event).to receive(:visible_to_user?).and_call_original
-
- get api(path), user
-
- expect(response).to have_gitlab_http_status(200)
- expect(json_response).to contain_exactly(
- 'project_id' => nil,
- 'action_name' => nil,
- 'target_id' => nil,
- 'target_iid' => nil,
- 'target_type' => nil,
- 'author_id' => nil,
- 'target_title' => 'Confidential event',
- 'created_at' => nil,
- 'author_username' => nil
- )
- end
- end
-
- describe '/users/:id/events' do
- let(:project) { create(:project, :public) }
- let(:path) { "/users/#{project.owner.id}/events" }
- let(:issue) { create(:issue, :confidential, project: project) }
-
- before do
- EventCreateService.new.open_issue(issue, issue.author)
- end
-
- context 'unauthenticated user views another user with private events' do
- let(:user) { nil }
-
- include_examples 'private events are redacted'
- end
-
- context 'authenticated user without access views another user with private events' do
- let(:user) { create(:user) }
-
- include_examples 'private events are redacted'
- end
- end
-
- describe '/projects/:id/events' do
- let(:project) { create(:project, :public) }
- let(:path) { "/projects/#{project.id}/events" }
- let(:issue) { create(:issue, :confidential, project: project) }
-
- before do
- EventCreateService.new.open_issue(issue, issue.author)
- end
-
- context 'unauthenticated user views public project' do
- let(:user) { nil }
-
- include_examples 'private events are redacted'
- end
-
- context 'authenticated user without access views public project' do
- let(:user) { create(:user) }
-
- include_examples 'private events are redacted'
- end
- end
-end
diff --git a/spec/services/issuable/common_system_notes_service_spec.rb b/spec/services/issuable/common_system_notes_service_spec.rb
index fa1a421d528..fa5d5ebac5c 100644
--- a/spec/services/issuable/common_system_notes_service_spec.rb
+++ b/spec/services/issuable/common_system_notes_service_spec.rb
@@ -5,7 +5,7 @@ describe Issuable::CommonSystemNotesService do
let(:project) { create(:project) }
let(:issuable) { create(:issue) }
- describe '#execute' do
+ context 'on issuable update' do
it_behaves_like 'system note creation', { title: 'New title' }, 'changed title'
it_behaves_like 'system note creation', { description: 'New description' }, 'changed the description'
it_behaves_like 'system note creation', { discussion_locked: true }, 'locked this issue'
@@ -20,7 +20,7 @@ describe Issuable::CommonSystemNotesService do
end
it 'creates a resource label event' do
- described_class.new(project, user).execute(issuable, [])
+ described_class.new(project, user).execute(issuable, old_labels: [])
event = issuable.reload.resource_label_events.last
expect(event).not_to be_nil
@@ -68,4 +68,47 @@ describe Issuable::CommonSystemNotesService do
end
end
end
+
+ context 'on issuable create' do
+ let(:issuable) { build(:issue) }
+
+ subject { described_class.new(project, user).execute(issuable, old_labels: [], is_update: false) }
+
+ it 'does not create system note for title and description' do
+ issuable.save
+
+ expect { subject }.not_to change { issuable.notes.count }
+ end
+
+ it 'creates a resource label event for labels added' do
+ label = create(:label, project: project)
+
+ issuable.labels << label
+ issuable.save
+
+ expect { subject }.to change { issuable.resource_label_events.count }.from(0).to(1)
+
+ event = issuable.reload.resource_label_events.last
+
+ expect(event).not_to be_nil
+ expect(event.label_id).to eq label.id
+ expect(event.user_id).to eq user.id
+ end
+
+ it 'creates a system note for milestone set' do
+ issuable.milestone = create(:milestone, project: project)
+ issuable.save
+
+ expect { subject }.to change { issuable.notes.count }.from(0).to(1)
+ expect(issuable.notes.last.note).to match('changed milestone')
+ end
+
+ it 'creates a system note for due_date set' do
+ issuable.due_date = Date.today
+ issuable.save
+
+ expect { subject }.to change { issuable.notes.count }.from(0).to(1)
+ expect(issuable.notes.last.note).to match('changed due date')
+ end
+ end
end
diff --git a/spec/support/shared_examples/common_system_notes_examples.rb b/spec/support/shared_examples/common_system_notes_examples.rb
index 96ef30b7513..da5a4f3e319 100644
--- a/spec/support/shared_examples/common_system_notes_examples.rb
+++ b/spec/support/shared_examples/common_system_notes_examples.rb
@@ -1,5 +1,5 @@
shared_examples 'system note creation' do |update_params, note_text|
- subject { described_class.new(project, user).execute(issuable, [])}
+ subject { described_class.new(project, user).execute(issuable, old_labels: []) }
before do
issuable.assign_attributes(update_params)
@@ -16,7 +16,7 @@ shared_examples 'system note creation' do |update_params, note_text|
end
shared_examples 'WIP notes creation' do |wip_action|
- subject { described_class.new(project, user).execute(issuable, []) }
+ subject { described_class.new(project, user).execute(issuable, old_labels: []) }
it 'creates WIP toggle and title change notes' do
expect { subject }.to change { Note.count }.from(0).to(2)